[PATCH 6/9] VC04_SERVICES: Add compat ioctl handler for "await completion"

Michael Zoran mzoran at crowfest.net
Wed Jan 18 07:04:50 PST 2017


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 <mzoran at 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




More information about the linux-rpi-kernel mailing list