Add compat ioctl for await completion

Signed-off-by: Michael Zoran <mzo...@crowfest.net>
---
 .../vc04_services/interface/vchiq_arm/vchiq_arm.c  | 498 ++++++++++++++-------
 1 file changed, 341 insertions(+), 157 deletions(-)

diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c 
b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
index 60f4d92dede5..8e8162d4b2e4 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
@@ -1020,6 +1020,341 @@ vchiq_ioctl_compat_queue_bulk(struct vchiq_ioctl_ctxt 
*ctxt)
 
 #endif
 
+typedef int (*vchiq_get_msgbuf_ptr_callback_t)(VCHIQ_AWAIT_COMPLETION_T *args,
+                                              void __user **msgbuf);
+
+typedef int (*vchiq_put_completion_callback_t)(VCHIQ_AWAIT_COMPLETION_T *args,
+                                              long num,
+                                              VCHIQ_COMPLETION_DATA_T 
*completion);
+
+static long
+vchiq_ioctl_do_process_completions(
+       struct vchiq_ioctl_ctxt *ctxt,
+       vchiq_get_msgbuf_ptr_callback_t get_msgbuf,
+       vchiq_put_completion_callback_t put_completion)
+{
+       VCHIQ_AWAIT_COMPLETION_T *args = ctxt->args;
+       VCHIQ_INSTANCE_T instance = ctxt->instance;
+       int remove = instance->completion_remove;
+       long ret;
+
+       for (ret = 0; ret < args->count; ret++) {
+               VCHIQ_COMPLETION_DATA_T *completion;
+               VCHIQ_SERVICE_T *service;
+               USER_SERVICE_T *user_service;
+               VCHIQ_HEADER_T *header;
+
+               if (instance->completion_remove == instance->completion_insert)
+                       return ret;
+
+               completion = &instance->completions[
+                       instance->completion_remove &
+                       (MAX_COMPLETIONS - 1)];
+
+               /*
+                * A read memory barrier is needed to stop
+                * prefetch of a stale completion record
+                */
+               rmb();
+
+               service = completion->service_userdata;
+               user_service = service->base.userdata;
+               completion->service_userdata = user_service->userdata;
+
+               header = completion->header;
+               if (header) {
+                       void __user *msgbuf;
+                       int msglen;
+
+                       msglen = header->size + sizeof(VCHIQ_HEADER_T);
+                       /* This must be a VCHIQ-style service */
+                       if (args->msgbufsize < msglen) {
+                               vchiq_log_error(
+                                       vchiq_arm_log_level,
+                                       "header %pK: msgbufsize %x < msglen %x",
+                                       header, args->msgbufsize,
+                                       msglen);
+                               WARN(1, "invalid message size\n");
+                               if (!ret)
+                                       ret = -EMSGSIZE;
+                               return ret;
+                       }
+                       if (args->msgbufcount <= 0)
+                               /* Stall here for lack of a
+                                * buffer for the message.
+                                */
+                               return ret;
+
+                       /* Get the pointer from user space */
+                       args->msgbufcount--;
+                       if (get_msgbuf(args, &msgbuf)) {
+                               if (!ret)
+                                       ret = -EFAULT;
+                               return ret;
+                       }
+
+                       /* Copy the message to user space */
+                       if (copy_to_user(msgbuf, header,
+                                        msglen)) {
+                               if (!ret)
+                                       ret = -EFAULT;
+                               return ret;
+                       }
+
+                       /* Now it has been copied, the message
+                        * can be released.
+                        */
+                       vchiq_release_message(service->handle, header);
+
+                       /* The completion must point to the
+                        * msgbuf.
+                        */
+                       completion->header = msgbuf;
+               }
+
+               if ((completion->reason == VCHIQ_SERVICE_CLOSED) &&
+                   !instance->use_close_delivered)
+                       unlock_service(service);
+
+               if (put_completion(args, ret, completion)) {
+                       if (!ret)
+                               ret = -EFAULT;
+                       return ret;
+               }
+
+               /*
+                * Ensure that the above copy has completed
+                * before advancing the remove pointer.
+                */
+               mb();
+               remove++;
+               instance->completion_remove = remove;
+       }
+
+       return ret;
+}
+
+static long
+vchiq_ioctl_do_await_completion(struct vchiq_ioctl_ctxt *ctxt,
+                               vchiq_get_msgbuf_ptr_callback_t get_msgbuf,
+                               vchiq_put_completion_callback_t put_completion)
+{
+       VCHIQ_AWAIT_COMPLETION_T *args = ctxt->args;
+       VCHIQ_INSTANCE_T instance = ctxt->instance;
+       unsigned int initialmsgbufcount = args->msgbufcount;
+       long ret;
+
+       DEBUG_INITIALISE(g_state.local)
+
+       mutex_lock(&instance->completion_mutex);
+
+       DEBUG_TRACE(AWAIT_COMPLETION_LINE);
+       while ((instance->completion_remove == instance->completion_insert) &&
+              !instance->closing) {
+               int rc;
+
+               DEBUG_TRACE(AWAIT_COMPLETION_LINE);
+               mutex_unlock(&instance->completion_mutex);
+               rc = down_interruptible(&instance->insert_event);
+               mutex_lock(&instance->completion_mutex);
+
+               if (rc) {
+                       DEBUG_TRACE(AWAIT_COMPLETION_LINE);
+                       vchiq_log_info(vchiq_arm_log_level,
+                                      "AWAIT_COMPLETION interrupted");
+
+                       up(&instance->remove_event);
+                       mutex_unlock(&instance->completion_mutex);
+                       DEBUG_TRACE(AWAIT_COMPLETION_LINE);
+
+                       return -EINTR;
+               }
+       }
+
+       DEBUG_TRACE(AWAIT_COMPLETION_LINE);
+
+       ret = vchiq_ioctl_do_process_completions(ctxt,
+                                                get_msgbuf,
+                                                put_completion);
+
+       if (initialmsgbufcount != args->msgbufcount) {
+               if (ctxt->cpyret_handler(ctxt))
+                       ret = -EFAULT;
+       }
+
+       if (ret)
+               up(&instance->remove_event);
+       mutex_unlock(&instance->completion_mutex);
+       DEBUG_TRACE(AWAIT_COMPLETION_LINE);
+
+       return ret;
+}
+
+static int
+vchiq_ioctl_get_msgbuf(VCHIQ_AWAIT_COMPLETION_T *args,
+                      void __user **msgbuf)
+{
+       unsigned int msgbufcount = args->msgbufcount;
+
+       return copy_from_user(msgbuf,
+                             &args->msgbufs[msgbufcount],
+                             sizeof(*msgbuf));
+}
+
+static int
+vchiq_ioctl_put_completion(VCHIQ_AWAIT_COMPLETION_T *args,
+                          long num,
+                          VCHIQ_COMPLETION_DATA_T *completion)
+{
+       if (num < 0)
+               return -EFAULT;
+
+       return copy_to_user((void *)args->buf +
+                           num * sizeof(VCHIQ_COMPLETION_DATA_T),
+                           completion,
+                           sizeof(VCHIQ_COMPLETION_DATA_T));
+}
+
+static long
+vchiq_ioctl_await_completion_cpyret(struct vchiq_ioctl_ctxt *ctxt)
+{
+       VCHIQ_AWAIT_COMPLETION_T __user *puargs =
+               (VCHIQ_AWAIT_COMPLETION_T __user *)ctxt->arg;
+       VCHIQ_AWAIT_COMPLETION_T *args = ctxt->args;
+
+       return copy_to_user(&puargs->msgbufcount,
+                           &args->msgbufcount,
+                           sizeof(args->msgbufcount));
+}
+
+static long
+vchiq_ioctl_await_completion(struct vchiq_ioctl_ctxt *ctxt)
+{
+       VCHIQ_AWAIT_COMPLETION_T __user *puargs =
+               (VCHIQ_AWAIT_COMPLETION_T __user *)ctxt->arg;
+       VCHIQ_AWAIT_COMPLETION_T args;
+
+       DEBUG_INITIALISE(g_state.local)
+
+       DEBUG_TRACE(AWAIT_COMPLETION_LINE);
+
+       if (!ctxt->instance->connected)
+               return -ENOTCONN;
+
+       if (copy_from_user(&args, puargs, sizeof(args)))
+               return -EFAULT;
+
+       ctxt->args = &args;
+       ctxt->cpyret_handler = vchiq_ioctl_await_completion_cpyret;
+
+       return vchiq_ioctl_do_await_completion(ctxt,
+                                              vchiq_ioctl_get_msgbuf,
+                                              vchiq_ioctl_put_completion);
+}
+
+#if defined(CONFIG_COMPAT)
+
+struct vchiq_completion_data32 {
+       VCHIQ_REASON_T reason;
+       compat_uptr_t header;
+       compat_uptr_t service_userdata;
+       compat_uptr_t bulk_userdata;
+};
+
+struct vchiq_await_completion32 {
+       unsigned int count;
+       compat_uptr_t buf;
+       unsigned int msgbufsize;
+       unsigned int msgbufcount; /* IN/OUT */
+       compat_uptr_t msgbufs;
+};
+
+#define VCHIQ_IOC_AWAIT_COMPLETION32 \
+       _IOWR(VCHIQ_IOC_MAGIC, 7, struct vchiq_await_completion32)
+
+static int
+vchiq_ioctl_compat_get_msgbuf(VCHIQ_AWAIT_COMPLETION_T *args,
+                             void __user **msgbuf)
+{
+       u32 msgbuf32;
+       unsigned int msgbufcount = args->msgbufcount;
+
+       if (copy_from_user(&msgbuf32,
+                          ((void *)args->msgbufs) +
+                          (sizeof(u32) * msgbufcount),
+                          sizeof(msgbuf32)))
+               return -EFAULT;
+
+       *msgbuf = compat_ptr(msgbuf32);
+       return 0;
+}
+
+static int
+vchiq_ioctl_compat_put_completion(VCHIQ_AWAIT_COMPLETION_T *args,
+                                 long num,
+                                 VCHIQ_COMPLETION_DATA_T *completion)
+{
+       struct vchiq_completion_data32 completion32;
+
+       completion32.reason = completion->reason;
+       completion32.header = ptr_to_compat(completion->header);
+       completion32.service_userdata =
+               ptr_to_compat(completion->service_userdata);
+       completion32.bulk_userdata =
+               ptr_to_compat(completion->bulk_userdata);
+
+       return copy_to_user(((void *)args->buf) +
+                            num * sizeof(struct vchiq_completion_data32),
+                            &completion32,
+                            sizeof(struct vchiq_completion_data32));
+}
+
+static long
+vchiq_ioctl_compat_await_completion_cpyret(struct vchiq_ioctl_ctxt *ctxt)
+{
+       struct vchiq_await_completion32 __user *puargs32 =
+               (struct vchiq_await_completion32 __user *)ctxt->arg;
+       VCHIQ_AWAIT_COMPLETION_T *args = ctxt->args;
+
+       return copy_to_user(&puargs32->msgbufcount,
+                           &args->msgbufcount,
+                           sizeof(args->msgbufcount));
+}
+
+static long
+vchiq_ioctl_compat_await_completion(struct vchiq_ioctl_ctxt *ctxt)
+{
+       struct vchiq_await_completion32 __user *puargs32 =
+               (struct vchiq_await_completion32 __user *)ctxt->arg;
+       VCHIQ_AWAIT_COMPLETION_T args;
+       struct vchiq_await_completion32 args32;
+
+       DEBUG_INITIALISE(g_state.local)
+
+       DEBUG_TRACE(AWAIT_COMPLETION_LINE);
+
+       if (!ctxt->instance->connected)
+               return -ENOTCONN;
+
+       if (copy_from_user(&args32, puargs32, sizeof(args32)))
+               return -EFAULT;
+
+       args.count       = args32.count;
+       args.buf         = compat_ptr(args32.buf);
+       args.msgbufsize  = args32.msgbufsize;
+       args.msgbufcount = args32.msgbufcount;
+       args.msgbufs     = compat_ptr(args32.msgbufs);
+
+       ctxt->args = &args;
+       ctxt->cpyret_handler = vchiq_ioctl_compat_await_completion_cpyret;
+
+       return vchiq_ioctl_do_await_completion(ctxt,
+                                              vchiq_ioctl_compat_get_msgbuf,
+                                              
vchiq_ioctl_compat_put_completion);
+}
+
+#endif
+
 /****************************************************************************
 *
 *   vchiq_ioctl
@@ -1046,6 +1381,9 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned 
long arg)
        case VCHIQ_IOC_QUEUE_BULK_RECEIVE:
                return vchiq_dispatch_ioctl(vchiq_ioctl_queue_bulk,
                                            file, cmd, arg);
+       case VCHIQ_IOC_AWAIT_COMPLETION:
+               return vchiq_dispatch_ioctl(vchiq_ioctl_await_completion,
+                                           file, cmd, arg);
        default:
                break;
        }
@@ -1184,163 +1522,6 @@ vchiq_ioctl(struct file *file, unsigned int cmd, 
unsigned long arg)
                        ret = -EINVAL;
        } break;
 
-       case VCHIQ_IOC_AWAIT_COMPLETION: {
-               VCHIQ_AWAIT_COMPLETION_T args;
-
-               DEBUG_TRACE(AWAIT_COMPLETION_LINE);
-               if (!instance->connected) {
-                       ret = -ENOTCONN;
-                       break;
-               }
-
-               if (copy_from_user(&args, (const void __user *)arg,
-                       sizeof(args)) != 0) {
-                       ret = -EFAULT;
-                       break;
-               }
-
-               mutex_lock(&instance->completion_mutex);
-
-               DEBUG_TRACE(AWAIT_COMPLETION_LINE);
-               while ((instance->completion_remove ==
-                       instance->completion_insert)
-                       && !instance->closing) {
-                       int rc;
-                       DEBUG_TRACE(AWAIT_COMPLETION_LINE);
-                       mutex_unlock(&instance->completion_mutex);
-                       rc = down_interruptible(&instance->insert_event);
-                       mutex_lock(&instance->completion_mutex);
-                       if (rc != 0) {
-                               DEBUG_TRACE(AWAIT_COMPLETION_LINE);
-                               vchiq_log_info(vchiq_arm_log_level,
-                                       "AWAIT_COMPLETION interrupted");
-                               ret = -EINTR;
-                               break;
-                       }
-               }
-               DEBUG_TRACE(AWAIT_COMPLETION_LINE);
-
-               if (ret == 0) {
-                       int msgbufcount = args.msgbufcount;
-                       int remove = instance->completion_remove;
-
-                       for (ret = 0; ret < args.count; ret++) {
-                               VCHIQ_COMPLETION_DATA_T *completion;
-                               VCHIQ_SERVICE_T *service;
-                               USER_SERVICE_T *user_service;
-                               VCHIQ_HEADER_T *header;
-
-                               if (remove == instance->completion_insert)
-                                       break;
-
-                               completion = &instance->completions[
-                                       remove & (MAX_COMPLETIONS - 1)];
-
-                               /*
-                                * A read memory barrier is needed to stop
-                                * prefetch of a stale completion record
-                                */
-                               rmb();
-
-                               service = completion->service_userdata;
-                               user_service = service->base.userdata;
-                               completion->service_userdata =
-                                       user_service->userdata;
-
-                               header = completion->header;
-                               if (header) {
-                                       void __user *msgbuf;
-                                       int msglen;
-
-                                       msglen = header->size +
-                                               sizeof(VCHIQ_HEADER_T);
-                                       /* This must be a VCHIQ-style service */
-                                       if (args.msgbufsize < msglen) {
-                                               vchiq_log_error(
-                                                       vchiq_arm_log_level,
-                                                       "header %pK: msgbufsize 
%x < msglen %x",
-                                                       header, args.msgbufsize,
-                                                       msglen);
-                                               WARN(1, "invalid message "
-                                                       "size\n");
-                                               if (ret == 0)
-                                                       ret = -EMSGSIZE;
-                                               break;
-                                       }
-                                       if (msgbufcount <= 0)
-                                               /* Stall here for lack of a
-                                               ** buffer for the message. */
-                                               break;
-                                       /* Get the pointer from user space */
-                                       msgbufcount--;
-                                       if (copy_from_user(&msgbuf,
-                                               (const void __user *)
-                                               &args.msgbufs[msgbufcount],
-                                               sizeof(msgbuf)) != 0) {
-                                               if (ret == 0)
-                                                       ret = -EFAULT;
-                                               break;
-                                       }
-
-                                       /* Copy the message to user space */
-                                       if (copy_to_user(msgbuf, header,
-                                               msglen) != 0) {
-                                               if (ret == 0)
-                                                       ret = -EFAULT;
-                                               break;
-                                       }
-
-                                       /* Now it has been copied, the message
-                                       ** can be released. */
-                                       vchiq_release_message(service->handle,
-                                               header);
-
-                                       /* The completion must point to the
-                                       ** msgbuf. */
-                                       completion->header = msgbuf;
-                               }
-
-                               if ((completion->reason ==
-                                       VCHIQ_SERVICE_CLOSED) &&
-                                       !instance->use_close_delivered)
-                                       unlock_service(service);
-
-                               if (copy_to_user((void __user *)(
-                                       (size_t)args.buf +
-                                       ret * sizeof(VCHIQ_COMPLETION_DATA_T)),
-                                       completion,
-                                       sizeof(VCHIQ_COMPLETION_DATA_T)) != 0) {
-                                               if (ret == 0)
-                                                       ret = -EFAULT;
-                                       break;
-                               }
-
-                               /*
-                                * Ensure that the above copy has completed
-                                * before advancing the remove pointer.
-                                */
-                               mb();
-                               remove++;
-                               instance->completion_remove = remove;
-                       }
-
-                       if (msgbufcount != args.msgbufcount) {
-                               if (copy_to_user((void __user *)
-                                       &((VCHIQ_AWAIT_COMPLETION_T *)arg)->
-                                               msgbufcount,
-                                       &msgbufcount,
-                                       sizeof(msgbufcount)) != 0) {
-                                       ret = -EFAULT;
-                               }
-                       }
-               }
-
-               if (ret != 0)
-                       up(&instance->remove_event);
-               mutex_unlock(&instance->completion_mutex);
-               DEBUG_TRACE(AWAIT_COMPLETION_LINE);
-       } break;
-
        case VCHIQ_IOC_DEQUEUE_MESSAGE: {
                VCHIQ_DEQUEUE_MESSAGE_T args;
                USER_SERVICE_T *user_service;
@@ -1559,6 +1740,9 @@ vchiq_ioctl_compat(struct file *file, unsigned int cmd, 
unsigned long arg)
        case VCHIQ_IOC_QUEUE_BULK_RECEIVE32:
                return vchiq_dispatch_ioctl(vchiq_ioctl_compat_queue_bulk,
                                            file, cmd, arg);
+       case VCHIQ_IOC_AWAIT_COMPLETION32:
+               return vchiq_dispatch_ioctl(vchiq_ioctl_compat_await_completion,
+                                           file, cmd, arg);
        default:
                return vchiq_ioctl(file, cmd, arg);
        }
-- 
2.11.0

_______________________________________________
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

Reply via email to