Commit feebcaea authored by Jason Wang's avatar Jason Wang Committed by Michael S. Tsirkin
Browse files

vhost: factor out setting vring addr and num



Factoring vring address and num setting which needs special care for
accelerating vq metadata accessing.

Signed-off-by: default avatarJason Wang <jasowang@redhat.com>
Signed-off-by: default avatarMichael S. Tsirkin <mst@redhat.com>
parent 4942e825
Loading
Loading
Loading
Loading
+103 −74
Original line number Original line Diff line number Diff line
@@ -1486,6 +1486,104 @@ err:
	return -EFAULT;
	return -EFAULT;
}
}


static long vhost_vring_set_num(struct vhost_dev *d,
				struct vhost_virtqueue *vq,
				void __user *argp)
{
	struct vhost_vring_state s;

	/* Resizing ring with an active backend?
	 * You don't want to do that. */
	if (vq->private_data)
		return -EBUSY;

	if (copy_from_user(&s, argp, sizeof s))
		return -EFAULT;

	if (!s.num || s.num > 0xffff || (s.num & (s.num - 1)))
		return -EINVAL;
	vq->num = s.num;

	return 0;
}

static long vhost_vring_set_addr(struct vhost_dev *d,
				 struct vhost_virtqueue *vq,
				 void __user *argp)
{
	struct vhost_vring_addr a;

	if (copy_from_user(&a, argp, sizeof a))
		return -EFAULT;
	if (a.flags & ~(0x1 << VHOST_VRING_F_LOG))
		return -EOPNOTSUPP;

	/* For 32bit, verify that the top 32bits of the user
	   data are set to zero. */
	if ((u64)(unsigned long)a.desc_user_addr != a.desc_user_addr ||
	    (u64)(unsigned long)a.used_user_addr != a.used_user_addr ||
	    (u64)(unsigned long)a.avail_user_addr != a.avail_user_addr)
		return -EFAULT;

	/* Make sure it's safe to cast pointers to vring types. */
	BUILD_BUG_ON(__alignof__ *vq->avail > VRING_AVAIL_ALIGN_SIZE);
	BUILD_BUG_ON(__alignof__ *vq->used > VRING_USED_ALIGN_SIZE);
	if ((a.avail_user_addr & (VRING_AVAIL_ALIGN_SIZE - 1)) ||
	    (a.used_user_addr & (VRING_USED_ALIGN_SIZE - 1)) ||
	    (a.log_guest_addr & (VRING_USED_ALIGN_SIZE - 1)))
		return -EINVAL;

	/* We only verify access here if backend is configured.
	 * If it is not, we don't as size might not have been setup.
	 * We will verify when backend is configured. */
	if (vq->private_data) {
		if (!vq_access_ok(vq, vq->num,
			(void __user *)(unsigned long)a.desc_user_addr,
			(void __user *)(unsigned long)a.avail_user_addr,
			(void __user *)(unsigned long)a.used_user_addr))
			return -EINVAL;

		/* Also validate log access for used ring if enabled. */
		if ((a.flags & (0x1 << VHOST_VRING_F_LOG)) &&
			!log_access_ok(vq->log_base, a.log_guest_addr,
				sizeof *vq->used +
				vq->num * sizeof *vq->used->ring))
			return -EINVAL;
	}

	vq->log_used = !!(a.flags & (0x1 << VHOST_VRING_F_LOG));
	vq->desc = (void __user *)(unsigned long)a.desc_user_addr;
	vq->avail = (void __user *)(unsigned long)a.avail_user_addr;
	vq->log_addr = a.log_guest_addr;
	vq->used = (void __user *)(unsigned long)a.used_user_addr;

	return 0;
}

static long vhost_vring_set_num_addr(struct vhost_dev *d,
				     struct vhost_virtqueue *vq,
				     unsigned int ioctl,
				     void __user *argp)
{
	long r;

	mutex_lock(&vq->mutex);

	switch (ioctl) {
	case VHOST_SET_VRING_NUM:
		r = vhost_vring_set_num(d, vq, argp);
		break;
	case VHOST_SET_VRING_ADDR:
		r = vhost_vring_set_addr(d, vq, argp);
		break;
	default:
		BUG();
	}

	mutex_unlock(&vq->mutex);

	return r;
}
long vhost_vring_ioctl(struct vhost_dev *d, unsigned int ioctl, void __user *argp)
long vhost_vring_ioctl(struct vhost_dev *d, unsigned int ioctl, void __user *argp)
{
{
	struct file *eventfp, *filep = NULL;
	struct file *eventfp, *filep = NULL;
@@ -1495,7 +1593,6 @@ long vhost_vring_ioctl(struct vhost_dev *d, unsigned int ioctl, void __user *arg
	struct vhost_virtqueue *vq;
	struct vhost_virtqueue *vq;
	struct vhost_vring_state s;
	struct vhost_vring_state s;
	struct vhost_vring_file f;
	struct vhost_vring_file f;
	struct vhost_vring_addr a;
	u32 idx;
	u32 idx;
	long r;
	long r;


@@ -1508,26 +1605,14 @@ long vhost_vring_ioctl(struct vhost_dev *d, unsigned int ioctl, void __user *arg
	idx = array_index_nospec(idx, d->nvqs);
	idx = array_index_nospec(idx, d->nvqs);
	vq = d->vqs[idx];
	vq = d->vqs[idx];


	if (ioctl == VHOST_SET_VRING_NUM ||
	    ioctl == VHOST_SET_VRING_ADDR) {
		return vhost_vring_set_num_addr(d, vq, ioctl, argp);
	}

	mutex_lock(&vq->mutex);
	mutex_lock(&vq->mutex);


	switch (ioctl) {
	switch (ioctl) {
	case VHOST_SET_VRING_NUM:
		/* Resizing ring with an active backend?
		 * You don't want to do that. */
		if (vq->private_data) {
			r = -EBUSY;
			break;
		}
		if (copy_from_user(&s, argp, sizeof s)) {
			r = -EFAULT;
			break;
		}
		if (!s.num || s.num > 0xffff || (s.num & (s.num - 1))) {
			r = -EINVAL;
			break;
		}
		vq->num = s.num;
		break;
	case VHOST_SET_VRING_BASE:
	case VHOST_SET_VRING_BASE:
		/* Moving base with an active backend?
		/* Moving base with an active backend?
		 * You don't want to do that. */
		 * You don't want to do that. */
@@ -1553,62 +1638,6 @@ long vhost_vring_ioctl(struct vhost_dev *d, unsigned int ioctl, void __user *arg
		if (copy_to_user(argp, &s, sizeof s))
		if (copy_to_user(argp, &s, sizeof s))
			r = -EFAULT;
			r = -EFAULT;
		break;
		break;
	case VHOST_SET_VRING_ADDR:
		if (copy_from_user(&a, argp, sizeof a)) {
			r = -EFAULT;
			break;
		}
		if (a.flags & ~(0x1 << VHOST_VRING_F_LOG)) {
			r = -EOPNOTSUPP;
			break;
		}
		/* For 32bit, verify that the top 32bits of the user
		   data are set to zero. */
		if ((u64)(unsigned long)a.desc_user_addr != a.desc_user_addr ||
		    (u64)(unsigned long)a.used_user_addr != a.used_user_addr ||
		    (u64)(unsigned long)a.avail_user_addr != a.avail_user_addr) {
			r = -EFAULT;
			break;
		}

		/* Make sure it's safe to cast pointers to vring types. */
		BUILD_BUG_ON(__alignof__ *vq->avail > VRING_AVAIL_ALIGN_SIZE);
		BUILD_BUG_ON(__alignof__ *vq->used > VRING_USED_ALIGN_SIZE);
		if ((a.avail_user_addr & (VRING_AVAIL_ALIGN_SIZE - 1)) ||
		    (a.used_user_addr & (VRING_USED_ALIGN_SIZE - 1)) ||
		    (a.log_guest_addr & (VRING_USED_ALIGN_SIZE - 1))) {
			r = -EINVAL;
			break;
		}

		/* We only verify access here if backend is configured.
		 * If it is not, we don't as size might not have been setup.
		 * We will verify when backend is configured. */
		if (vq->private_data) {
			if (!vq_access_ok(vq, vq->num,
				(void __user *)(unsigned long)a.desc_user_addr,
				(void __user *)(unsigned long)a.avail_user_addr,
				(void __user *)(unsigned long)a.used_user_addr)) {
				r = -EINVAL;
				break;
			}

			/* Also validate log access for used ring if enabled. */
			if ((a.flags & (0x1 << VHOST_VRING_F_LOG)) &&
			    !log_access_ok(vq->log_base, a.log_guest_addr,
					   sizeof *vq->used +
					   vq->num * sizeof *vq->used->ring)) {
				r = -EINVAL;
				break;
			}
		}

		vq->log_used = !!(a.flags & (0x1 << VHOST_VRING_F_LOG));
		vq->desc = (void __user *)(unsigned long)a.desc_user_addr;
		vq->avail = (void __user *)(unsigned long)a.avail_user_addr;
		vq->log_addr = a.log_guest_addr;
		vq->used = (void __user *)(unsigned long)a.used_user_addr;
		break;
	case VHOST_SET_VRING_KICK:
	case VHOST_SET_VRING_KICK:
		if (copy_from_user(&f, argp, sizeof f)) {
		if (copy_from_user(&f, argp, sizeof f)) {
			r = -EFAULT;
			r = -EFAULT;