57 |
57 |
}
|
58 |
58 |
}
|
59 |
59 |
|
60 |
|
static int vhost_client_sync_dirty_bitmap(CPUPhysMemoryClient *client,
|
61 |
|
target_phys_addr_t start_addr,
|
62 |
|
target_phys_addr_t end_addr)
|
|
60 |
static int vhost_sync_dirty_bitmap(struct vhost_dev *dev,
|
|
61 |
target_phys_addr_t start_addr,
|
|
62 |
target_phys_addr_t end_addr)
|
63 |
63 |
{
|
64 |
|
struct vhost_dev *dev = container_of(client, struct vhost_dev, client);
|
65 |
64 |
int i;
|
|
65 |
|
66 |
66 |
if (!dev->log_enabled || !dev->started) {
|
67 |
67 |
return 0;
|
68 |
68 |
}
|
... | ... | |
81 |
81 |
return 0;
|
82 |
82 |
}
|
83 |
83 |
|
|
84 |
static void vhost_log_sync(MemoryListener *listener,
|
|
85 |
MemoryRegionSection *section)
|
|
86 |
{
|
|
87 |
struct vhost_dev *dev = container_of(listener, struct vhost_dev,
|
|
88 |
memory_listener);
|
|
89 |
target_phys_addr_t start_addr = section->offset_within_address_space;
|
|
90 |
target_phys_addr_t end_addr = start_addr + section->size;
|
|
91 |
|
|
92 |
vhost_sync_dirty_bitmap(dev, start_addr, end_addr);
|
|
93 |
}
|
|
94 |
|
84 |
95 |
/* Assign/unassign. Keep an unsorted array of non-overlapping
|
85 |
96 |
* memory regions in dev->mem. */
|
86 |
97 |
static void vhost_dev_unassign_memory(struct vhost_dev *dev,
|
... | ... | |
259 |
270 |
log_base = (uint64_t)(unsigned long)log;
|
260 |
271 |
r = ioctl(dev->control, VHOST_SET_LOG_BASE, &log_base);
|
261 |
272 |
assert(r >= 0);
|
262 |
|
vhost_client_sync_dirty_bitmap(&dev->client, 0,
|
263 |
|
(target_phys_addr_t)~0x0ull);
|
|
273 |
vhost_sync_dirty_bitmap(dev, 0, (target_phys_addr_t)~0x0ull);
|
264 |
274 |
if (dev->log) {
|
265 |
275 |
g_free(dev->log);
|
266 |
276 |
}
|
... | ... | |
335 |
345 |
return uaddr != reg->userspace_addr + start_addr - reg->guest_phys_addr;
|
336 |
346 |
}
|
337 |
347 |
|
338 |
|
static void vhost_client_set_memory(CPUPhysMemoryClient *client,
|
339 |
|
target_phys_addr_t start_addr,
|
340 |
|
ram_addr_t size,
|
341 |
|
ram_addr_t phys_offset,
|
342 |
|
bool log_dirty)
|
|
348 |
static void vhost_set_memory(MemoryListener *listener,
|
|
349 |
MemoryRegionSection *section,
|
|
350 |
bool add)
|
343 |
351 |
{
|
344 |
|
struct vhost_dev *dev = container_of(client, struct vhost_dev, client);
|
345 |
|
ram_addr_t flags = phys_offset & ~TARGET_PAGE_MASK;
|
|
352 |
struct vhost_dev *dev = container_of(listener, struct vhost_dev,
|
|
353 |
memory_listener);
|
|
354 |
target_phys_addr_t start_addr = section->offset_within_address_space;
|
|
355 |
ram_addr_t size = section->size;
|
|
356 |
bool log_dirty = memory_region_is_logging(section->mr);
|
346 |
357 |
int s = offsetof(struct vhost_memory, regions) +
|
347 |
358 |
(dev->mem->nregions + 1) * sizeof dev->mem->regions[0];
|
348 |
359 |
uint64_t log_size;
|
349 |
360 |
int r;
|
|
361 |
void *ram;
|
|
362 |
|
|
363 |
if (!memory_region_is_ram(section->mr)) {
|
|
364 |
return;
|
|
365 |
}
|
350 |
366 |
|
351 |
367 |
dev->mem = g_realloc(dev->mem, s);
|
352 |
368 |
|
353 |
369 |
if (log_dirty) {
|
354 |
|
flags = IO_MEM_UNASSIGNED;
|
|
370 |
add = false;
|
355 |
371 |
}
|
356 |
372 |
|
357 |
373 |
assert(size);
|
358 |
374 |
|
359 |
375 |
/* Optimize no-change case. At least cirrus_vga does this a lot at this time. */
|
360 |
|
if (flags == IO_MEM_RAM) {
|
361 |
|
if (!vhost_dev_cmp_memory(dev, start_addr, size,
|
362 |
|
(uintptr_t)qemu_get_ram_ptr(phys_offset))) {
|
|
376 |
ram = memory_region_get_ram_ptr(section->mr);
|
|
377 |
if (add) {
|
|
378 |
if (!vhost_dev_cmp_memory(dev, start_addr, size, (uintptr_t)ram)) {
|
363 |
379 |
/* Region exists with same address. Nothing to do. */
|
364 |
380 |
return;
|
365 |
381 |
}
|
... | ... | |
371 |
387 |
}
|
372 |
388 |
|
373 |
389 |
vhost_dev_unassign_memory(dev, start_addr, size);
|
374 |
|
if (flags == IO_MEM_RAM) {
|
|
390 |
if (add) {
|
375 |
391 |
/* Add given mapping, merging adjacent regions if any */
|
376 |
|
vhost_dev_assign_memory(dev, start_addr, size,
|
377 |
|
(uintptr_t)qemu_get_ram_ptr(phys_offset));
|
|
392 |
vhost_dev_assign_memory(dev, start_addr, size, (uintptr_t)ram);
|
378 |
393 |
} else {
|
379 |
394 |
/* Remove old mapping for this memory, if any. */
|
380 |
395 |
vhost_dev_unassign_memory(dev, start_addr, size);
|
... | ... | |
410 |
425 |
}
|
411 |
426 |
}
|
412 |
427 |
|
|
428 |
static void vhost_region_add(MemoryListener *listener,
|
|
429 |
MemoryRegionSection *section)
|
|
430 |
{
|
|
431 |
vhost_set_memory(listener, section, true);
|
|
432 |
}
|
|
433 |
|
|
434 |
static void vhost_region_del(MemoryListener *listener,
|
|
435 |
MemoryRegionSection *section)
|
|
436 |
{
|
|
437 |
vhost_set_memory(listener, section, false);
|
|
438 |
}
|
|
439 |
|
413 |
440 |
static int vhost_virtqueue_set_addr(struct vhost_dev *dev,
|
414 |
441 |
struct vhost_virtqueue *vq,
|
415 |
442 |
unsigned idx, bool enable_log)
|
... | ... | |
467 |
494 |
return r;
|
468 |
495 |
}
|
469 |
496 |
|
470 |
|
static int vhost_client_migration_log(CPUPhysMemoryClient *client,
|
471 |
|
int enable)
|
|
497 |
static int vhost_migration_log(MemoryListener *listener, int enable)
|
472 |
498 |
{
|
473 |
|
struct vhost_dev *dev = container_of(client, struct vhost_dev, client);
|
|
499 |
struct vhost_dev *dev = container_of(listener, struct vhost_dev,
|
|
500 |
memory_listener);
|
474 |
501 |
int r;
|
475 |
502 |
if (!!enable == dev->log_enabled) {
|
476 |
503 |
return 0;
|
... | ... | |
500 |
527 |
return 0;
|
501 |
528 |
}
|
502 |
529 |
|
|
530 |
static void vhost_log_global_start(MemoryListener *listener)
|
|
531 |
{
|
|
532 |
int r;
|
|
533 |
|
|
534 |
r = vhost_migration_log(listener, true);
|
|
535 |
if (r < 0) {
|
|
536 |
abort();
|
|
537 |
}
|
|
538 |
}
|
|
539 |
|
|
540 |
static void vhost_log_global_stop(MemoryListener *listener)
|
|
541 |
{
|
|
542 |
int r;
|
|
543 |
|
|
544 |
r = vhost_migration_log(listener, false);
|
|
545 |
if (r < 0) {
|
|
546 |
abort();
|
|
547 |
}
|
|
548 |
}
|
|
549 |
|
|
550 |
static void vhost_log_start(MemoryListener *listener,
|
|
551 |
MemoryRegionSection *section)
|
|
552 |
{
|
|
553 |
/* FIXME: implement */
|
|
554 |
}
|
|
555 |
|
|
556 |
static void vhost_log_stop(MemoryListener *listener,
|
|
557 |
MemoryRegionSection *section)
|
|
558 |
{
|
|
559 |
/* FIXME: implement */
|
|
560 |
}
|
|
561 |
|
503 |
562 |
static int vhost_virtqueue_init(struct vhost_dev *dev,
|
504 |
563 |
struct VirtIODevice *vdev,
|
505 |
564 |
struct vhost_virtqueue *vq,
|
... | ... | |
645 |
704 |
}
|
646 |
705 |
hdev->features = features;
|
647 |
706 |
|
648 |
|
hdev->client.set_memory = vhost_client_set_memory;
|
649 |
|
hdev->client.sync_dirty_bitmap = vhost_client_sync_dirty_bitmap;
|
650 |
|
hdev->client.migration_log = vhost_client_migration_log;
|
651 |
|
hdev->client.log_start = NULL;
|
652 |
|
hdev->client.log_stop = NULL;
|
|
707 |
hdev->memory_listener = (MemoryListener) {
|
|
708 |
.region_add = vhost_region_add,
|
|
709 |
.region_del = vhost_region_del,
|
|
710 |
.log_start = vhost_log_start,
|
|
711 |
.log_stop = vhost_log_stop,
|
|
712 |
.log_sync = vhost_log_sync,
|
|
713 |
.log_global_start = vhost_log_global_start,
|
|
714 |
.log_global_stop = vhost_log_global_stop,
|
|
715 |
};
|
653 |
716 |
hdev->mem = g_malloc0(offsetof(struct vhost_memory, regions));
|
654 |
717 |
hdev->log = NULL;
|
655 |
718 |
hdev->log_size = 0;
|
656 |
719 |
hdev->log_enabled = false;
|
657 |
720 |
hdev->started = false;
|
658 |
|
cpu_register_phys_memory_client(&hdev->client);
|
|
721 |
memory_listener_register(&hdev->memory_listener);
|
659 |
722 |
hdev->force = force;
|
660 |
723 |
return 0;
|
661 |
724 |
fail:
|
... | ... | |
666 |
729 |
|
667 |
730 |
void vhost_dev_cleanup(struct vhost_dev *hdev)
|
668 |
731 |
{
|
669 |
|
cpu_unregister_phys_memory_client(&hdev->client);
|
|
732 |
memory_listener_unregister(&hdev->memory_listener);
|
670 |
733 |
g_free(hdev->mem);
|
671 |
734 |
close(hdev->control);
|
672 |
735 |
}
|
... | ... | |
808 |
871 |
hdev->vqs + i,
|
809 |
872 |
i);
|
810 |
873 |
}
|
811 |
|
vhost_client_sync_dirty_bitmap(&hdev->client, 0,
|
812 |
|
(target_phys_addr_t)~0x0ull);
|
|
874 |
vhost_sync_dirty_bitmap(hdev, 0, (target_phys_addr_t)~0x0ull);
|
813 |
875 |
r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, false);
|
814 |
876 |
if (r < 0) {
|
815 |
877 |
fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", r);
|