Commit a4367cd2 authored by Arnd Bergmann's avatar Arnd Bergmann Committed by Greg Kroah-Hartman

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: default avatarArnd Bergmann <arnd@arndb.de>
Link: https://lore.kernel.org/r/20200918095441.1446041-5-arnd@arndb.deSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent f618affa
......@@ -938,6 +938,95 @@ static int vchiq_ioc_dequeue_message(struct vchiq_instance *instance,
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;
args = compat_alloc_user_space(sizeof(*args));
if (!args)
return -EFAULT;
if (copy_from_user(&args32, ptrargs32, sizeof(args32)))
return -EFAULT;
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);
struct vchiq_queue_bulk_transfer args;
enum vchiq_bulk_dir dir = (cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT) ?
VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE;
if (ret < 0)
return ret;
if (get_user(args32.mode, &args->mode))
if (copy_from_user(&args32, argp, sizeof(args32)))
return -EFAULT;
if (copy_to_user(&ptrargs32->mode,
&args32.mode,
sizeof(args32.mode)))
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,
};
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:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment