staging: vchiq: convert compat bulk transfer
authorArnd Bergmann <arnd@arndb.de>
Fri, 18 Sep 2020 09:54:40 +0000 (11:54 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 22 Sep 2020 07:48:22 +0000 (09:48 +0200)
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>
drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c

index cbe9583..50af7f4 100644 (file)
@@ -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;
-
-       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: