static long vhost_set_memory()

in vhost.c [1419:1484]


static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m)
{
	struct vhost_memory mem, *newmem;
	struct vhost_memory_region *region;
	struct vhost_iotlb *newumem, *oldumem;
	unsigned long size = offsetof(struct vhost_memory, regions);
	int i;

	if (copy_from_user(&mem, m, size))
		return -EFAULT;
	if (mem.padding)
		return -EOPNOTSUPP;
	if (mem.nregions > max_mem_regions)
		return -E2BIG;
	newmem = kvzalloc(struct_size(newmem, regions, mem.nregions),
			GFP_KERNEL);
	if (!newmem)
		return -ENOMEM;

	memcpy(newmem, &mem, size);
	if (copy_from_user(newmem->regions, m->regions,
			   flex_array_size(newmem, regions, mem.nregions))) {
		kvfree(newmem);
		return -EFAULT;
	}

	newumem = iotlb_alloc();
	if (!newumem) {
		kvfree(newmem);
		return -ENOMEM;
	}

	for (region = newmem->regions;
	     region < newmem->regions + mem.nregions;
	     region++) {
		if (vhost_iotlb_add_range(newumem,
					  region->guest_phys_addr,
					  region->guest_phys_addr +
					  region->memory_size - 1,
					  region->userspace_addr,
					  VHOST_MAP_RW))
			goto err;
	}

	if (!memory_access_ok(d, newumem, 0))
		goto err;

	oldumem = d->umem;
	d->umem = newumem;

	/* All memory accesses are done under some VQ mutex. */
	for (i = 0; i < d->nvqs; ++i) {
		mutex_lock(&d->vqs[i]->mutex);
		d->vqs[i]->umem = newumem;
		mutex_unlock(&d->vqs[i]->mutex);
	}

	kvfree(newmem);
	vhost_iotlb_free(oldumem);
	return 0;

err:
	vhost_iotlb_free(newumem);
	kvfree(newmem);
	return -EFAULT;
}