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:
parent
f618affa77
commit
a4367cd2b2
@ -938,6 +938,95 @@ out:
|
|||||||
return ret;
|
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
|
* 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_TRANSMIT:
|
||||||
case VCHIQ_IOC_QUEUE_BULK_RECEIVE: {
|
case VCHIQ_IOC_QUEUE_BULK_RECEIVE: {
|
||||||
struct vchiq_queue_bulk_transfer args;
|
struct vchiq_queue_bulk_transfer args;
|
||||||
struct bulk_waiter_node *waiter = NULL;
|
struct vchiq_queue_bulk_transfer __user *argp;
|
||||||
|
|
||||||
enum vchiq_bulk_dir dir =
|
enum vchiq_bulk_dir dir =
|
||||||
(cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT) ?
|
(cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT) ?
|
||||||
VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE;
|
VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE;
|
||||||
|
|
||||||
if (copy_from_user(&args, (const void __user *)arg,
|
argp = (void __user *)arg;
|
||||||
sizeof(args))) {
|
if (copy_from_user(&args, argp, sizeof(args))) {
|
||||||
ret = -EFAULT;
|
ret = -EFAULT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
service = find_service_for_instance(instance, args.handle);
|
ret = vchiq_irq_queue_bulk_tx_rx(instance, &args,
|
||||||
if (!service) {
|
dir, &argp->mode);
|
||||||
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;
|
|
||||||
}
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case VCHIQ_IOC_AWAIT_COMPLETION: {
|
case VCHIQ_IOC_AWAIT_COMPLETION: {
|
||||||
@ -1620,47 +1639,26 @@ struct vchiq_queue_bulk_transfer32 {
|
|||||||
static long
|
static long
|
||||||
vchiq_compat_ioctl_queue_bulk(struct file *file,
|
vchiq_compat_ioctl_queue_bulk(struct file *file,
|
||||||
unsigned int cmd,
|
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 args32;
|
||||||
struct vchiq_queue_bulk_transfer32 __user *ptrargs32 =
|
struct vchiq_queue_bulk_transfer args;
|
||||||
(struct vchiq_queue_bulk_transfer32 __user *)arg;
|
enum vchiq_bulk_dir dir = (cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT) ?
|
||||||
long ret;
|
VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE;
|
||||||
|
|
||||||
args = compat_alloc_user_space(sizeof(*args));
|
if (copy_from_user(&args32, argp, sizeof(args32)))
|
||||||
if (!args)
|
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
if (copy_from_user(&args32, ptrargs32, sizeof(args32)))
|
args = (struct vchiq_queue_bulk_transfer) {
|
||||||
return -EFAULT;
|
.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) ||
|
return vchiq_irq_queue_bulk_tx_rx(file->private_data, &args,
|
||||||
put_user(compat_ptr(args32.data), &args->data) ||
|
dir, &argp->mode);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct vchiq_completion_data32 {
|
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);
|
return vchiq_compat_ioctl_queue_message(file, cmd, argp);
|
||||||
case VCHIQ_IOC_QUEUE_BULK_TRANSMIT32:
|
case VCHIQ_IOC_QUEUE_BULK_TRANSMIT32:
|
||||||
case VCHIQ_IOC_QUEUE_BULK_RECEIVE32:
|
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:
|
case VCHIQ_IOC_AWAIT_COMPLETION32:
|
||||||
return vchiq_compat_ioctl_await_completion(file, cmd, arg);
|
return vchiq_compat_ioctl_await_completion(file, cmd, arg);
|
||||||
case VCHIQ_IOC_DEQUEUE_MESSAGE32:
|
case VCHIQ_IOC_DEQUEUE_MESSAGE32:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user