[PATCH V2 4/7] staging: vchiq_arm: Add compat ioctl for await completion
Michael Zoran
mzoran at crowfest.net
Sat Jan 21 09:48:13 PST 2017
Add compat ioctl for await completion
Signed-off-by: Michael Zoran <mzoran at 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
More information about the linux-rpi-kernel
mailing list