kvm ioeventfd

컴퓨터공부/가상화기술 2018.08.11 23:10 Posted by 아는 개발자

* 개인 공부용으로 정리한 것이라 부정확한 내용이 있을 수 있으니 참고용으로만 사용하길 바랍니다.


IOEVENTFD


eventfd를 응용해서 guest에 interrupt를 보낼 수 있는 기능을 만든 irqfd것과 비슷하게 ioeventfd도 eventfd를 이용해서 guest에게 mmio 기능을 전달 할 수 있는 매커니즘을 만들었다. 초기화 작업도 irqfd와 거의 비슷하다.


1. QEMU에서 kvm으로 mmio 값 전달.


QEMU에서 장치가 사용할 주소 값과 flag, eventfd 값을 세팅하고 kvm에 ioctl 을 날린다. 이때 전달인자 fd는 MemoryRegionIoeventfd 라는 구조체의 EventNotifier 값을 뽑아낸 것이다. 구조체 이름이 매우 와닿지 않는다.

// qemu/accel/kvm-all.c
static int kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint16_t val, 
                      bool assign, uint32_t size, bool datamatch)
{
    struct kvm_ioeventfd kick = {
        .datamatch = datamatch 
                       ? adjust_ioeventfd_endianness(val, size) : 0, 
        .addr = addr,
        .flags = KVM_IOEVENTFD_FLAG_PIO,
        .len = size,
        .fd = fd,
    };   
    r = kvm_vm_ioctl(kvm_state, KVM_IOEVENTFD, &kick);

2. 받은 값을 이용해 _ioeventfd 자료구조 생성 및 초기화


_ioevent라는 자료구조를 선언하고 qemu 에서 전달한 값을 세팅한다. args는 QEMU에서 전달받은 인자 값이고 eventfd는 MemoryRegionIoeventfd 의 EventNotifer의 파일 디스크립터 값이다. VirtQueue와 비슷하게 선언된 것 같다.

// virt/kvm/eventfd.c
static int kvm_assign_ioeventfd_idx(struct kvm *kvm,
                enum kvm_bus bus_idx,
                struct kvm_ioeventfd *args)
{
    // HK: Define ioeventfd data structure
    INIT_LIST_HEAD(&p->list);
    p->addr    = args->addr;
    p->bus_idx = bus_idx; // HK: bus_idx is defined by ioeventfd flag
    p->length  = args->len;
    p->eventfd = eventfd;
}

3. IO Device 초기화


초기화한 _ioeventfd에 ioeventfd 작업인 write와 destructor 함수를 세팅한다.. kvm ioeventfd 기반에서 사용할 수 있는 함수들을 설정하는 작업으로 보면 될 것 같다. ioeventfd_write 함수는 쓰려는 값이 range 안에 있는지 확인하고 있으면 eventfd_signal을 보낸다. 값을 업데이트해서 QEMU에 알림을주려는 용도다. 

// virt/kvm/eventfd.c
static int kvm_assign_ioeventfd_idx(struct kvm *kvm,
                enum kvm_bus bus_idx,
                struct kvm_ioeventfd *args)
{
    // HK: Set ioeventfd operation for this device
    kvm_iodevice_init(&p->dev, &ioeventfd_ops);

static const struct kvm_io_device_ops ioeventfd_ops = {
    .write      = ioeventfd_write,
    .destructor = ioeventfd_destructor,
};

/* MMIO/PIO writes trigger an event if the addr/val match */
static int
ioeventfd_write(struct kvm_vcpu *vcpu, struct kvm_io_device *this,
      gpa_t addr, int len, const void *val)
{
    struct _ioeventfd *p = to_ioeventfd(this);

    if (!ioeventfd_in_range(p, addr, len, val))
        return -EOPNOTSUPP;
            
    eventfd_signal(p->eventfd, 1);
    return 0;
}

4. KVM IO bus 등록


mmio로 사용할 주소와 bus index, mmio 동작 작업을 kvm io bus에 등록한다. 버스에서는 등록된 mmio 장치의 주소 값을 보고 event를 날려주는것 같다. bus쪽은 공부가 좀더 필요한 부분이다.

    ret = kvm_io_bus_register_dev(kvm, bus_idx, p->addr, p->length,
                      &p->dev);
    if (ret < 0) 
        goto unlock_fail;
    
    kvm->buses[bus_idx]->ioeventfd_count++;


'컴퓨터공부 > 가상화기술' 카테고리의 다른 글

Virtio Block 성능 벤치마크  (0) 2018.12.23
QEMU를 이용해 커널 이미지 바꿔서 부팅해보기  (0) 2018.12.20
kvm ioeventfd  (0) 2018.08.11
kvm irqfd  (0) 2018.08.11
vhost  (0) 2018.07.08
virtio  (0) 2018.07.08