Commit a4367cd2 authored by Arnd Bergmann's avatar Arnd Bergmann Committed by Greg Kroah-Hartman
Browse files

staging: vchiq: convert compat bulk transfer



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: default avatarArnd Bergmann <arnd@arndb.de>
Link: https://lore.kernel.org/r/20200918095441.1446041-5-arnd@arndb.de


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent f618affa
Loading
Loading
Loading
Loading
+109 −111
Original line number Diff line number Diff line
@@ -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);

	if (ret < 0)
		return ret;
	struct vchiq_queue_bulk_transfer args;
	enum vchiq_bulk_dir dir = (cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT) ?
				  VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE;

	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: