staging: vchiq: convert compat bulk transfer
Split out the ioctl implementation for VCHIQ_IOC_QUEUE_BULK_TRANSMIT into a separate function so it can be shared with the compat implementation. Here, the input data is converted separately in the compat handler, while the output data is passed as a __user pointer to thec vchiq_queue_bulk_transfer->mode word that is compatible. Signed-off-by: Arnd Bergmann <arnd@arndb.de> Link: https://lore.kernel.org/r/20200918095441.1446041-5-arnd@arndb.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Родитель
f618affa77
Коммит
a4367cd2b2
|
@ -938,6 +938,95 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int vchiq_irq_queue_bulk_tx_rx(struct vchiq_instance *instance,
|
||||
struct vchiq_queue_bulk_transfer *args,
|
||||
enum vchiq_bulk_dir dir,
|
||||
enum vchiq_bulk_mode __user *mode)
|
||||
{
|
||||
struct vchiq_service *service;
|
||||
struct bulk_waiter_node *waiter = NULL;
|
||||
int status = 0;
|
||||
int ret;
|
||||
|
||||
service = find_service_for_instance(instance, args->handle);
|
||||
if (!service)
|
||||
return -EINVAL;
|
||||
|
||||
if (args->mode == VCHIQ_BULK_MODE_BLOCKING) {
|
||||
waiter = kzalloc(sizeof(struct bulk_waiter_node),
|
||||
GFP_KERNEL);
|
||||
if (!waiter) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
args->userdata = &waiter->bulk_waiter;
|
||||
} else if (args->mode == VCHIQ_BULK_MODE_WAITING) {
|
||||
mutex_lock(&instance->bulk_waiter_list_mutex);
|
||||
list_for_each_entry(waiter, &instance->bulk_waiter_list,
|
||||
list) {
|
||||
if (waiter->pid == current->pid) {
|
||||
list_del(&waiter->list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&instance->bulk_waiter_list_mutex);
|
||||
if (!waiter) {
|
||||
vchiq_log_error(vchiq_arm_log_level,
|
||||
"no bulk_waiter found for pid %d",
|
||||
current->pid);
|
||||
ret = -ESRCH;
|
||||
goto out;
|
||||
}
|
||||
vchiq_log_info(vchiq_arm_log_level,
|
||||
"found bulk_waiter %pK for pid %d", waiter,
|
||||
current->pid);
|
||||
args->userdata = &waiter->bulk_waiter;
|
||||
}
|
||||
|
||||
status = vchiq_bulk_transfer(args->handle, args->data, args->size,
|
||||
args->userdata, args->mode, dir);
|
||||
|
||||
if (!waiter) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) ||
|
||||
!waiter->bulk_waiter.bulk) {
|
||||
if (waiter->bulk_waiter.bulk) {
|
||||
/* Cancel the signal when the transfer
|
||||
** completes. */
|
||||
spin_lock(&bulk_waiter_spinlock);
|
||||
waiter->bulk_waiter.bulk->userdata = NULL;
|
||||
spin_unlock(&bulk_waiter_spinlock);
|
||||
}
|
||||
kfree(waiter);
|
||||
ret = 0;
|
||||
} else {
|
||||
const enum vchiq_bulk_mode mode_waiting =
|
||||
VCHIQ_BULK_MODE_WAITING;
|
||||
waiter->pid = current->pid;
|
||||
mutex_lock(&instance->bulk_waiter_list_mutex);
|
||||
list_add(&waiter->list, &instance->bulk_waiter_list);
|
||||
mutex_unlock(&instance->bulk_waiter_list_mutex);
|
||||
vchiq_log_info(vchiq_arm_log_level,
|
||||
"saved bulk_waiter %pK for pid %d",
|
||||
waiter, current->pid);
|
||||
|
||||
ret = put_user(mode_waiting, mode);
|
||||
}
|
||||
out:
|
||||
unlock_service(service);
|
||||
if (ret)
|
||||
return ret;
|
||||
else if (status == VCHIQ_ERROR)
|
||||
return -EIO;
|
||||
else if (status == VCHIQ_RETRY)
|
||||
return -EINTR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* vchiq_ioctl
|
||||
|
@ -1118,90 +1207,20 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|||
case VCHIQ_IOC_QUEUE_BULK_TRANSMIT:
|
||||
case VCHIQ_IOC_QUEUE_BULK_RECEIVE: {
|
||||
struct vchiq_queue_bulk_transfer args;
|
||||
struct bulk_waiter_node *waiter = NULL;
|
||||
struct vchiq_queue_bulk_transfer __user *argp;
|
||||
|
||||
enum vchiq_bulk_dir dir =
|
||||
(cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT) ?
|
||||
VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE;
|
||||
|
||||
if (copy_from_user(&args, (const void __user *)arg,
|
||||
sizeof(args))) {
|
||||
argp = (void __user *)arg;
|
||||
if (copy_from_user(&args, argp, sizeof(args))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
service = find_service_for_instance(instance, args.handle);
|
||||
if (!service) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (args.mode == VCHIQ_BULK_MODE_BLOCKING) {
|
||||
waiter = kzalloc(sizeof(struct bulk_waiter_node),
|
||||
GFP_KERNEL);
|
||||
if (!waiter) {
|
||||
ret = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
args.userdata = &waiter->bulk_waiter;
|
||||
} else if (args.mode == VCHIQ_BULK_MODE_WAITING) {
|
||||
mutex_lock(&instance->bulk_waiter_list_mutex);
|
||||
list_for_each_entry(waiter, &instance->bulk_waiter_list,
|
||||
list) {
|
||||
if (waiter->pid == current->pid) {
|
||||
list_del(&waiter->list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&instance->bulk_waiter_list_mutex);
|
||||
if (!waiter) {
|
||||
vchiq_log_error(vchiq_arm_log_level,
|
||||
"no bulk_waiter found for pid %d",
|
||||
current->pid);
|
||||
ret = -ESRCH;
|
||||
break;
|
||||
}
|
||||
vchiq_log_info(vchiq_arm_log_level,
|
||||
"found bulk_waiter %pK for pid %d", waiter,
|
||||
current->pid);
|
||||
args.userdata = &waiter->bulk_waiter;
|
||||
}
|
||||
|
||||
status = vchiq_bulk_transfer(args.handle, args.data, args.size,
|
||||
args.userdata, args.mode, dir);
|
||||
|
||||
if (!waiter)
|
||||
break;
|
||||
|
||||
if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) ||
|
||||
!waiter->bulk_waiter.bulk) {
|
||||
if (waiter->bulk_waiter.bulk) {
|
||||
/* Cancel the signal when the transfer
|
||||
** completes. */
|
||||
spin_lock(&bulk_waiter_spinlock);
|
||||
waiter->bulk_waiter.bulk->userdata = NULL;
|
||||
spin_unlock(&bulk_waiter_spinlock);
|
||||
}
|
||||
kfree(waiter);
|
||||
} else {
|
||||
const enum vchiq_bulk_mode mode_waiting =
|
||||
VCHIQ_BULK_MODE_WAITING;
|
||||
waiter->pid = current->pid;
|
||||
mutex_lock(&instance->bulk_waiter_list_mutex);
|
||||
list_add(&waiter->list, &instance->bulk_waiter_list);
|
||||
mutex_unlock(&instance->bulk_waiter_list_mutex);
|
||||
vchiq_log_info(vchiq_arm_log_level,
|
||||
"saved bulk_waiter %pK for pid %d",
|
||||
waiter, current->pid);
|
||||
|
||||
if (copy_to_user((void __user *)
|
||||
&(((struct vchiq_queue_bulk_transfer __user *)
|
||||
arg)->mode),
|
||||
(const void *)&mode_waiting,
|
||||
sizeof(mode_waiting)))
|
||||
ret = -EFAULT;
|
||||
}
|
||||
ret = vchiq_irq_queue_bulk_tx_rx(instance, &args,
|
||||
dir, &argp->mode);
|
||||
} break;
|
||||
|
||||
case VCHIQ_IOC_AWAIT_COMPLETION: {
|
||||
|
@ -1620,47 +1639,26 @@ struct vchiq_queue_bulk_transfer32 {
|
|||
static long
|
||||
vchiq_compat_ioctl_queue_bulk(struct file *file,
|
||||
unsigned int cmd,
|
||||
unsigned long arg)
|
||||
struct vchiq_queue_bulk_transfer32 __user *argp)
|
||||
{
|
||||
struct vchiq_queue_bulk_transfer __user *args;
|
||||
struct vchiq_queue_bulk_transfer32 args32;
|
||||
struct vchiq_queue_bulk_transfer32 __user *ptrargs32 =
|
||||
(struct vchiq_queue_bulk_transfer32 __user *)arg;
|
||||
long ret;
|
||||
struct vchiq_queue_bulk_transfer args;
|
||||
enum vchiq_bulk_dir dir = (cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT) ?
|
||||
VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE;
|
||||
|
||||
args = compat_alloc_user_space(sizeof(*args));
|
||||
if (!args)
|
||||
if (copy_from_user(&args32, argp, sizeof(args32)))
|
||||
return -EFAULT;
|
||||
|
||||
if (copy_from_user(&args32, ptrargs32, sizeof(args32)))
|
||||
return -EFAULT;
|
||||
args = (struct vchiq_queue_bulk_transfer) {
|
||||
.handle = args32.handle,
|
||||
.data = compat_ptr(args32.data),
|
||||
.size = args32.size,
|
||||
.userdata = compat_ptr(args32.userdata),
|
||||
.mode = args32.mode,
|
||||
};
|
||||
|
||||
if (put_user(args32.handle, &args->handle) ||
|
||||
put_user(compat_ptr(args32.data), &args->data) ||
|
||||
put_user(args32.size, &args->size) ||
|
||||
put_user(compat_ptr(args32.userdata), &args->userdata) ||
|
||||
put_user(args32.mode, &args->mode))
|
||||
return -EFAULT;
|
||||
|
||||
if (cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT32)
|
||||
cmd = VCHIQ_IOC_QUEUE_BULK_TRANSMIT;
|
||||
else
|
||||
cmd = VCHIQ_IOC_QUEUE_BULK_RECEIVE;
|
||||
|
||||
ret = vchiq_ioctl(file, cmd, (unsigned long)args);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (get_user(args32.mode, &args->mode))
|
||||
return -EFAULT;
|
||||
|
||||
if (copy_to_user(&ptrargs32->mode,
|
||||
&args32.mode,
|
||||
sizeof(args32.mode)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
return vchiq_irq_queue_bulk_tx_rx(file->private_data, &args,
|
||||
dir, &argp->mode);
|
||||
}
|
||||
|
||||
struct vchiq_completion_data32 {
|
||||
|
@ -1893,7 +1891,7 @@ vchiq_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|||
return vchiq_compat_ioctl_queue_message(file, cmd, argp);
|
||||
case VCHIQ_IOC_QUEUE_BULK_TRANSMIT32:
|
||||
case VCHIQ_IOC_QUEUE_BULK_RECEIVE32:
|
||||
return vchiq_compat_ioctl_queue_bulk(file, cmd, arg);
|
||||
return vchiq_compat_ioctl_queue_bulk(file, cmd, argp);
|
||||
case VCHIQ_IOC_AWAIT_COMPLETION32:
|
||||
return vchiq_compat_ioctl_await_completion(file, cmd, arg);
|
||||
case VCHIQ_IOC_DEQUEUE_MESSAGE32:
|
||||
|
|
Загрузка…
Ссылка в новой задаче