static long vduse_dev_ioctl()

in vdpa_user/vduse_dev.c [919:1097]


static long vduse_dev_ioctl(struct file *file, unsigned int cmd,
			    unsigned long arg)
{
	struct vduse_dev *dev = file->private_data;
	void __user *argp = (void __user *)arg;
	int ret;

	if (unlikely(dev->broken))
		return -EPERM;

	switch (cmd) {
	case VDUSE_IOTLB_GET_FD: {
		struct vduse_iotlb_entry entry;
		struct vhost_iotlb_map *map;
		struct vdpa_map_file *map_file;
		struct vduse_iova_domain *domain = dev->domain;
		struct file *f = NULL;

		ret = -EFAULT;
		if (copy_from_user(&entry, argp, sizeof(entry)))
			break;

		ret = -EINVAL;
		if (entry.start > entry.last)
			break;

		spin_lock(&domain->iotlb_lock);
		map = vhost_iotlb_itree_first(domain->iotlb,
					      entry.start, entry.last);
		if (map) {
			map_file = (struct vdpa_map_file *)map->opaque;
			f = get_file(map_file->file);
			entry.offset = map_file->offset;
			entry.start = map->start;
			entry.last = map->last;
			entry.perm = map->perm;
		}
		spin_unlock(&domain->iotlb_lock);
		ret = -EINVAL;
		if (!f)
			break;

		ret = -EFAULT;
		if (copy_to_user(argp, &entry, sizeof(entry))) {
			fput(f);
			break;
		}
		ret = receive_fd(f, perm_to_file_flags(entry.perm));
		fput(f);
		break;
	}
	case VDUSE_DEV_GET_FEATURES:
		/*
		 * Just mirror what driver wrote here.
		 * The driver is expected to check FEATURE_OK later.
		 */
		ret = put_user(dev->driver_features, (u64 __user *)argp);
		break;
	case VDUSE_DEV_SET_CONFIG: {
		struct vduse_config_data config;
		unsigned long size = offsetof(struct vduse_config_data,
					      buffer);

		ret = -EFAULT;
		if (copy_from_user(&config, argp, size))
			break;

		ret = -EINVAL;
		if (config.offset > dev->config_size ||
		    config.length == 0 ||
		    config.length > dev->config_size - config.offset)
			break;

		ret = -EFAULT;
		if (copy_from_user(dev->config + config.offset, argp + size,
				   config.length))
			break;

		ret = 0;
		break;
	}
	case VDUSE_DEV_INJECT_CONFIG_IRQ:
		ret = vduse_dev_queue_irq_work(dev, &dev->inject);
		break;
	case VDUSE_VQ_SETUP: {
		struct vduse_vq_config config;
		u32 index;

		ret = -EFAULT;
		if (copy_from_user(&config, argp, sizeof(config)))
			break;

		ret = -EINVAL;
		if (config.index >= dev->vq_num)
			break;

		if (!is_mem_zero((const char *)config.reserved,
				 sizeof(config.reserved)))
			break;

		index = array_index_nospec(config.index, dev->vq_num);
		dev->vqs[index].num_max = config.max_size;
		ret = 0;
		break;
	}
	case VDUSE_VQ_GET_INFO: {
		struct vduse_vq_info vq_info;
		struct vduse_virtqueue *vq;
		u32 index;

		ret = -EFAULT;
		if (copy_from_user(&vq_info, argp, sizeof(vq_info)))
			break;

		ret = -EINVAL;
		if (vq_info.index >= dev->vq_num)
			break;

		index = array_index_nospec(vq_info.index, dev->vq_num);
		vq = &dev->vqs[index];
		vq_info.desc_addr = vq->desc_addr;
		vq_info.driver_addr = vq->driver_addr;
		vq_info.device_addr = vq->device_addr;
		vq_info.num = vq->num;

		if (dev->driver_features & BIT_ULL(VIRTIO_F_RING_PACKED)) {
			vq_info.packed.last_avail_counter =
				vq->state.packed.last_avail_counter;
			vq_info.packed.last_avail_idx =
				vq->state.packed.last_avail_idx;
			vq_info.packed.last_used_counter =
				vq->state.packed.last_used_counter;
			vq_info.packed.last_used_idx =
				vq->state.packed.last_used_idx;
		} else
			vq_info.split.avail_index =
				vq->state.split.avail_index;

		vq_info.ready = vq->ready;

		ret = -EFAULT;
		if (copy_to_user(argp, &vq_info, sizeof(vq_info)))
			break;

		ret = 0;
		break;
	}
	case VDUSE_VQ_SETUP_KICKFD: {
		struct vduse_vq_eventfd eventfd;

		ret = -EFAULT;
		if (copy_from_user(&eventfd, argp, sizeof(eventfd)))
			break;

		ret = vduse_kickfd_setup(dev, &eventfd);
		break;
	}
	case VDUSE_VQ_INJECT_IRQ: {
		u32 index;

		ret = -EFAULT;
		if (get_user(index, (u32 __user *)argp))
			break;

		ret = -EINVAL;
		if (index >= dev->vq_num)
			break;

		index = array_index_nospec(index, dev->vq_num);
		ret = vduse_dev_queue_irq_work(dev, &dev->vqs[index].inject);
		break;
	}
	default:
		ret = -ENOIOCTLCMD;
		break;
	}

	return ret;
}