/* Called after unassign, so no regions overlap the given range. */ static void vhost_dev_assign_memory(struct vhost_dev *dev, uint64_t start_addr, uint64_t size, uint64_t uaddr) { int from, to; struct vhost_memory_region *merged = NULL; for (from = 0, to = 0; from < dev->mem->nregions; ++from, ++to) { struct vhost_memory_region *reg = dev->mem->regions + to; uint64_t prlast, urlast; uint64_t pmlast, umlast; uint64_t s, e, u; /* clone old region */ if (to != from) { memcpy(reg, dev->mem->regions + from, sizeof *reg); } prlast = range_get_last(reg->guest_phys_addr, reg->memory_size); pmlast = range_get_last(start_addr, size); urlast = range_get_last(reg->userspace_addr, reg->memory_size); umlast = range_get_last(uaddr, size); /* check for overlapping regions: should never happen. */ assert(prlast < start_addr || pmlast < reg->guest_phys_addr); /* Not an adjacent or overlapping region - do not merge. */ if ((prlast + 1 != start_addr || urlast + 1 != uaddr) && (pmlast + 1 != reg->guest_phys_addr || umlast + 1 != reg->userspace_addr)) { continue; } if (merged) { --to; assert(to >= 0); } else { merged = reg; } u = MIN(uaddr, reg->userspace_addr); s = MIN(start_addr, reg->guest_phys_addr); e = MAX(pmlast, prlast); uaddr = merged->userspace_addr = u; start_addr = merged->guest_phys_addr = s; size = merged->memory_size = e - s + 1; assert(merged->memory_size); } if (!merged) { struct vhost_memory_region *reg = dev->mem->regions + to; memset(reg, 0, sizeof *reg); reg->memory_size = size; assert(reg->memory_size); reg->guest_phys_addr = start_addr; reg->userspace_addr = uaddr; ++to; } assert(to <= dev->mem->nregions + 1); dev->mem->nregions = to; }
static int vhost_client_sync_dirty_bitmap(CPUPhysMemoryClient *client, target_phys_addr_t start_addr, target_phys_addr_t end_addr) { struct vhost_dev *dev = container_of(client, struct vhost_dev, client); int i; if (!dev->log_enabled || !dev->started) { return 0; } for (i = 0; i < dev->mem->nregions; ++i) { struct vhost_memory_region *reg = dev->mem->regions + i; vhost_dev_sync_region(dev, start_addr, end_addr, reg->guest_phys_addr, range_get_last(reg->guest_phys_addr, reg->memory_size)); } for (i = 0; i < dev->nvqs; ++i) { struct vhost_virtqueue *vq = dev->vqs + i; vhost_dev_sync_region(dev, start_addr, end_addr, vq->used_phys, range_get_last(vq->used_phys, vq->used_size)); } return 0; }
static guint64 get_log_size(TestServer *s) { guint64 log_size = 0; int i; for (i = 0; i < s->memory.nregions; ++i) { VhostUserMemoryRegion *reg = &s->memory.regions[i]; guint64 last = range_get_last(reg->guest_phys_addr, reg->memory_size); log_size = MAX(log_size, last / (8 * VHOST_LOG_PAGE) + 1); } return log_size; }
static bool vhost_dev_cmp_memory(struct vhost_dev *dev, uint64_t start_addr, uint64_t size, uint64_t uaddr) { struct vhost_memory_region *reg = vhost_dev_find_reg(dev, start_addr, size); uint64_t reglast; uint64_t memlast; if (!reg) { return true; } reglast = range_get_last(reg->guest_phys_addr, reg->memory_size); memlast = range_get_last(start_addr, size); /* Need to extend region? */ if (start_addr < reg->guest_phys_addr || memlast > reglast) { return true; } /* userspace_addr changed? */ return uaddr != reg->userspace_addr + start_addr - reg->guest_phys_addr; }
static int vhost_sync_dirty_bitmap(struct vhost_dev *dev, MemoryRegionSection *section, target_phys_addr_t start_addr, target_phys_addr_t end_addr) { int i; if (!dev->log_enabled || !dev->started) { return 0; } for (i = 0; i < dev->mem->nregions; ++i) { struct vhost_memory_region *reg = dev->mem->regions + i; vhost_dev_sync_region(dev, section, start_addr, end_addr, reg->guest_phys_addr, range_get_last(reg->guest_phys_addr, reg->memory_size)); } for (i = 0; i < dev->nvqs; ++i) { struct vhost_virtqueue *vq = dev->vqs + i; vhost_dev_sync_region(dev, section, start_addr, end_addr, vq->used_phys, range_get_last(vq->used_phys, vq->used_size)); } return 0; }
static uint64_t vhost_get_log_size(struct vhost_dev *dev) { uint64_t log_size = 0; int i; for (i = 0; i < dev->mem->nregions; ++i) { struct vhost_memory_region *reg = dev->mem->regions + i; uint64_t last = range_get_last(reg->guest_phys_addr, reg->memory_size); log_size = MAX(log_size, last / VHOST_LOG_CHUNK + 1); } for (i = 0; i < dev->nvqs; ++i) { struct vhost_virtqueue *vq = dev->vqs + i; uint64_t last = vq->used_phys + vq->used_size - 1; log_size = MAX(log_size, last / VHOST_LOG_CHUNK + 1); } return log_size; }
/* Assign/unassign. Keep an unsorted array of non-overlapping * memory regions in dev->mem. */ static void vhost_dev_unassign_memory(struct vhost_dev *dev, uint64_t start_addr, uint64_t size) { int from, to, n = dev->mem->nregions; /* Track overlapping/split regions for sanity checking. */ int overlap_start = 0, overlap_end = 0, overlap_middle = 0, split = 0; for (from = 0, to = 0; from < n; ++from, ++to) { struct vhost_memory_region *reg = dev->mem->regions + to; uint64_t reglast; uint64_t memlast; uint64_t change; /* clone old region */ if (to != from) { memcpy(reg, dev->mem->regions + from, sizeof *reg); } /* No overlap is simple */ if (!ranges_overlap(reg->guest_phys_addr, reg->memory_size, start_addr, size)) { continue; } /* Split only happens if supplied region * is in the middle of an existing one. Thus it can not * overlap with any other existing region. */ assert(!split); reglast = range_get_last(reg->guest_phys_addr, reg->memory_size); memlast = range_get_last(start_addr, size); /* Remove whole region */ if (start_addr <= reg->guest_phys_addr && memlast >= reglast) { --dev->mem->nregions; --to; assert(to >= 0); ++overlap_middle; continue; } /* Shrink region */ if (memlast >= reglast) { reg->memory_size = start_addr - reg->guest_phys_addr; assert(reg->memory_size); assert(!overlap_end); ++overlap_end; continue; } /* Shift region */ if (start_addr <= reg->guest_phys_addr) { change = memlast + 1 - reg->guest_phys_addr; reg->memory_size -= change; reg->guest_phys_addr += change; reg->userspace_addr += change; assert(reg->memory_size); assert(!overlap_start); ++overlap_start; continue; } /* This only happens if supplied region * is in the middle of an existing one. Thus it can not * overlap with any other existing region. */ assert(!overlap_start); assert(!overlap_end); assert(!overlap_middle); /* Split region: shrink first part, shift second part. */ memcpy(dev->mem->regions + n, reg, sizeof *reg); reg->memory_size = start_addr - reg->guest_phys_addr; assert(reg->memory_size); change = memlast + 1 - reg->guest_phys_addr; reg = dev->mem->regions + n; reg->memory_size -= change; assert(reg->memory_size); reg->guest_phys_addr += change; reg->userspace_addr += change; /* Never add more than 1 region */ assert(dev->mem->nregions == n); ++dev->mem->nregions; ++split; } }