Add compat handler for "await_completion" ioctl and move
parts in common with the regular ioctl to vchiq_ioctl_await_completion

Signed-off-by: Michael Zoran <mzo...@crowfest.net>
---
 .../vc04_services/interface/vchiq_arm/vchiq_arm.c  | 398 ++++++++++++++-------
 1 file changed, 267 insertions(+), 131 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 7067bd3f4bd5..d9f3b0b34a95 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
@@ -653,6 +653,190 @@ vchiq_ioctl_queue_bulk(VCHIQ_INSTANCE_T instance,
        return 0;
 }
 
+typedef bool (*vchiq_get_msgbuf_ptr_callback_t)(VCHIQ_AWAIT_COMPLETION_T *args,
+                                               unsigned int msgbufcount,
+                                               void __user **msgbuf);
+
+typedef bool (*vchiq_put_completion_callback_t)(VCHIQ_AWAIT_COMPLETION_T *args,
+                                               long num,
+                                               VCHIQ_COMPLETION_DATA_T 
*completion);
+
+typedef bool (*vchiq_put_msgbuf_count_callback_t)(unsigned long arg,
+                                                 unsigned int msgbufcount);
+
+static long
+vchiq_ioctl_await_completion(VCHIQ_INSTANCE_T instance,
+                            unsigned long arg,
+                            VCHIQ_AWAIT_COMPLETION_T *args,
+                            vchiq_get_msgbuf_ptr_callback_t get_msgbuf,
+                            vchiq_put_completion_callback_t put_completion,
+                            vchiq_put_msgbuf_count_callback_t put_msgbuf_count)
+{
+       long ret;
+       int msgbufcount;
+
+       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);
+
+       /* A read memory barrier is needed to stop prefetch of a stale
+        * completion record
+        */
+       rmb();
+
+       msgbufcount = args->msgbufcount;
+       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)
+                       break;
+
+               completion = &instance->completions[
+                       instance->completion_remove &
+                       (MAX_COMPLETIONS - 1)];
+
+               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 (get_msgbuf(args, msgbufcount, &msgbuf)) {
+                               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 (put_completion(args, ret, completion)) {
+                       if (ret == 0)
+                               ret = -EFAULT;
+                       break;
+               }
+
+               instance->completion_remove++;
+       }
+
+       if (msgbufcount != args->msgbufcount)
+               if (put_msgbuf_count(arg, msgbufcount))
+                       ret = -EFAULT;
+
+       if (ret)
+               up(&instance->remove_event);
+       mutex_unlock(&instance->completion_mutex);
+       DEBUG_TRACE(AWAIT_COMPLETION_LINE);
+
+       return ret;
+}
+
+static bool
+vchiq_ioctl_get_msgbuf(VCHIQ_AWAIT_COMPLETION_T *args,
+                      unsigned int msgbufcount,
+                      void __user **msgbuf)
+{
+       return !!copy_from_user(msgbuf,
+                               (const void __user *)
+                               &args->msgbufs[msgbufcount],
+                               sizeof(*msgbuf));
+}
+
+static bool
+vchiq_ioctl_put_completion(VCHIQ_AWAIT_COMPLETION_T *args,
+                          long num,
+                          VCHIQ_COMPLETION_DATA_T *completion)
+{
+       return !!copy_to_user((void __user *)(
+                             (size_t)args->buf +
+                             num * sizeof(VCHIQ_COMPLETION_DATA_T)),
+                             completion,
+                             sizeof(VCHIQ_COMPLETION_DATA_T));
+}
+
+static bool
+vchiq_ioctl_put_msgbuf_count(unsigned long arg,
+                            unsigned int msgbufcount)
+{
+       return !!copy_to_user((void __user *)
+                             &((VCHIQ_AWAIT_COMPLETION_T *)arg)->msgbufcount,
+                             &msgbufcount,
+                             sizeof(msgbufcount));
+}
+
 /****************************************************************************
 *
 *   vchiq_ioctl
@@ -907,137 +1091,12 @@ vchiq_ioctl(struct file *file, unsigned int cmd, 
unsigned long arg)
                        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);
-
-               /* A read memory barrier is needed to stop prefetch of a stale
-               ** completion record
-               */
-               rmb();
-
-               if (ret == 0) {
-                       int msgbufcount = args.msgbufcount;
-                       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)
-                                       break;
-                               completion = &instance->completions[
-                                       instance->completion_remove &
-                                       (MAX_COMPLETIONS - 1)];
-
-                               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;
-                               }
-
-                               instance->completion_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);
+               ret = vchiq_ioctl_await_completion(instance,
+                                                  arg,
+                                                  &args,
+                                                  vchiq_ioctl_get_msgbuf,
+                                                  vchiq_ioctl_put_completion,
+                                                  
vchiq_ioctl_put_msgbuf_count);
        } break;
 
        case VCHIQ_IOC_DEQUEUE_MESSAGE: {
@@ -1244,6 +1303,52 @@ vchiq_ioctl(struct file *file, unsigned int cmd, 
unsigned long arg)
 
 #if defined(CONFIG_COMPAT)
 
+static bool
+vchiq_ioctl_compat_get_msgbuf(VCHIQ_AWAIT_COMPLETION_T *args,
+                             unsigned int msgbufcount,
+                             void __user **msgbuf)
+{
+       u32 msgbuf32;
+
+       if (copy_from_user(&msgbuf32,
+                          ((void *)args->msgbufs) + (sizeof(u32) * 
msgbufcount),
+                          sizeof(msgbuf32)))
+               return true;
+
+       *msgbuf = compat_ptr(msgbuf32);
+       return false;
+}
+
+static bool
+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 bool
+vchiq_ioctl_compat_put_msgbuf_count(unsigned long arg,
+                                   unsigned int msgbufcount)
+{
+       return !!copy_to_user((void __user *)
+                             &((struct vchiq_await_completion32 
*)arg)->msgbufcount,
+                             &msgbufcount,
+                             sizeof(msgbufcount));
+}
+
 static long
 vchiq_ioctl_compat_internal(
        struct file *file,
@@ -1380,6 +1485,36 @@ vchiq_ioctl_compat_internal(
                }
        } break;
 
+       case VCHIQ_IOC_AWAIT_COMPLETION32: {
+               VCHIQ_AWAIT_COMPLETION_T args;
+               struct vchiq_await_completion32 args32;
+
+               DEBUG_TRACE(AWAIT_COMPLETION_LINE);
+               if (!instance->connected) {
+                       ret = -ENOTCONN;
+                       break;
+               }
+
+               if (copy_from_user(&args32, (const void __user *)arg,
+                                  sizeof(args32)) != 0) {
+                       ret = -EFAULT;
+                       break;
+               }
+
+               args.count       = args32.count;
+               args.buf         = compat_ptr(args32.buf);
+               args.msgbufsize  = args32.msgbufsize;
+               args.msgbufcount = args32.msgbufcount;
+               args.msgbufs     = compat_ptr(args32.msgbufs);
+
+               ret = vchiq_ioctl_await_completion(instance,
+                                                  arg,
+                                                  &args,
+                                                  
vchiq_ioctl_compat_get_msgbuf,
+                                                  
vchiq_ioctl_compat_put_completion,
+                                                  
vchiq_ioctl_compat_put_msgbuf_count);
+       } break;
+
        default:
                ret = -ENOTTY;
                break;
@@ -1424,6 +1559,7 @@ vchiq_ioctl_compat(struct file *file, unsigned int cmd, 
unsigned long arg)
        case VCHIQ_IOC_QUEUE_MESSAGE32:
        case VCHIQ_IOC_QUEUE_BULK_TRANSMIT32:
        case VCHIQ_IOC_QUEUE_BULK_RECEIVE32:
+       case VCHIQ_IOC_AWAIT_COMPLETION32:
                return vchiq_ioctl_compat_internal(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