dev->num_pages记录了Number of pages host wants Guest to give up。virtio_notify_config会给guest发送cofiguration change interrupt。 guest cofiguration change interrupt的handler是virtballoon_changed。
/* Legacy balloon config space is LE, unlike all other devices. */ if (!virtio_has_feature(vb->vdev, VIRTIO_F_VERSION_1)) num_pages = le32_to_cpu((__force __le32)num_pages);
structvirtio_balloon_config { /* Number of pages host wants Guest to give up. */ __u32 num_pages; /* Number of pages we've actually got in balloon. */ __u32 actual; };
最终guest看到的num_pages寄存器的值就是dev->num_pages,即为Number of pages host wants Guest to give up。
towards_target中vb->num_pages的含义为Number of balloon pages guest has told the Host it’s not using. 在我们考虑的场景中,vb->num_pages为0(初始值),此时towards_target返回的值为20 * (2MB/4KB)= 10K。因此update_balloon_size_func中的diff变量大于0,此时会调用fill_balloon函数。
for (;;) { size_t offset = 0; uint32_t pfn; elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); if (!elem) { return; }
while (iov_to_buf(elem->out_sg, elem->out_num, offset, &pfn, 4) == 4) { ram_addr_t pa; ram_addr_t addr; int p = virtio_ldl_p(vdev, &pfn);
pa = (ram_addr_t) p << VIRTIO_BALLOON_PFN_SHIFT; offset += 4;
section = memory_region_find(get_system_memory(), pa, 1); if (!int128_nz(section.size) || !memory_region_is_ram(section.mr)) continue;
trace_virtio_balloon_handle_output(memory_region_name(section.mr), pa); /* Using memory_region_get_ram_ptr is bending the rules a bit, but should be OK because we only want a single page. */ addr = section.offset_within_region; balloon_page(memory_region_get_ram_ptr(section.mr) + addr, !!(vq == s->dvq)); memory_region_unref(section.mr); }
update_balloon_size_func在调用完fill_balloon来回收内存后,guest driver会调用update_balloon_size来通知QEMU Number of pages it’s actually got in balloon。
1 2 3 4 5 6 7 8 9 10 11
staticvoidupdate_balloon_size(struct virtio_balloon *vb) { u32 actual = vb->num_pages;
/* Legacy balloon config space is LE, unlike all other devices. */ if (!virtio_has_feature(vb->vdev, VIRTIO_F_VERSION_1)) actual = (__force u32)cpu_to_le32(actual);