in vdpa_user/vduse_dev.c [1371:1440]
static long vduse_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int ret;
void __user *argp = (void __user *)arg;
struct vduse_control *control = file->private_data;
mutex_lock(&vduse_lock);
switch (cmd) {
case VDUSE_GET_API_VERSION:
ret = put_user(control->api_version, (u64 __user *)argp);
break;
case VDUSE_SET_API_VERSION: {
u64 api_version;
ret = -EFAULT;
if (get_user(api_version, (u64 __user *)argp))
break;
ret = -EINVAL;
if (api_version > VDUSE_API_VERSION)
break;
ret = 0;
control->api_version = api_version;
break;
}
case VDUSE_CREATE_DEV: {
struct vduse_dev_config config;
unsigned long size = offsetof(struct vduse_dev_config, config);
void *buf;
ret = -EFAULT;
if (copy_from_user(&config, argp, size))
break;
ret = -EINVAL;
if (vduse_validate_config(&config) == false)
break;
buf = vmemdup_user(argp + size, config.config_size);
if (IS_ERR(buf)) {
ret = PTR_ERR(buf);
break;
}
config.name[VDUSE_NAME_MAX - 1] = '\0';
ret = vduse_create_dev(&config, buf, control->api_version);
if (ret)
kvfree(buf);
break;
}
case VDUSE_DESTROY_DEV: {
char name[VDUSE_NAME_MAX];
ret = -EFAULT;
if (copy_from_user(name, argp, VDUSE_NAME_MAX))
break;
name[VDUSE_NAME_MAX - 1] = '\0';
ret = vduse_destroy_dev(name);
break;
}
default:
ret = -EINVAL;
break;
}
mutex_unlock(&vduse_lock);
return ret;
}