lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <6c6cqaxmsy7miesel4ghdeiea6nrpe4gti4xf5enfyg4uqro5u@vpmtd2t7gydi>
Date: Fri, 28 Feb 2025 17:44:27 +0100
From: Uwe Kleine-König <u.kleine-koenig@...libre.com>
To: David Jander <david@...tonic.nl>
Cc: linux-kernel@...r.kernel.org, linux-iio@...r.kernel.org, 
	Jonathan Corbet <corbet@....net>, Rob Herring <robh@...nel.org>, 
	Krzysztof Kozlowski <krzk+dt@...nel.org>, Conor Dooley <conor+dt@...nel.org>, devicetree@...r.kernel.org, 
	linux-doc@...r.kernel.org, Nuno Sa <nuno.sa@...log.com>, 
	Jonathan Cameron <jic23@...nel.org>, Oleksij Rempel <o.rempel@...gutronix.de>
Subject: Re: [RFC PATCH 1/7] drivers: Add motion control subsystem

Hello David,

just a few highlevel review comments inline.

On Thu, Feb 27, 2025 at 05:28:17PM +0100, David Jander wrote:
> diff --git a/drivers/motion/motion-core.c b/drivers/motion/motion-core.c
> new file mode 100644
> index 000000000000..2963f1859e8b
> --- /dev/null
> +++ b/drivers/motion/motion-core.c
> @@ -0,0 +1,823 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Motion Control Subsystem - Core
> + *
> + * Copyright (C) 2024 Protonic Holland
> + *                    David Jander <david@...tonic.nl>
> + */
> +
> +#include <asm-generic/bitops/builtin-fls.h>
> +#include <asm-generic/errno-base.h>
> +#include <linux/interrupt.h>
> +#include <linux/irqreturn.h>
> +#include <linux/container_of.h>
> +#include <linux/hrtimer_types.h>
> +#include <linux/gfp_types.h>
> +#include <linux/module.h>
> +
> +#include <linux/fs.h>
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/major.h>
> +#include <linux/init.h>
> +#include <linux/device.h>
> +#include <linux/kmod.h>
> +#include <linux/motion.h>
> +#include <linux/poll.h>
> +#include <linux/ptrace.h>
> +#include <linux/ktime.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/gpio/consumer.h>
> +
> +#include "motion-core.h"
> +#include "motion-helpers.h"
> +#include <linux/time.h>
> +#include <linux/uaccess.h>
> +#include <linux/string.h>
> +#include <linux/math64.h>
> +#include <linux/mutex.h>
> +#include <linux/math.h>
> +#include <linux/math64.h>

Order all <...> includes over the "..." ones.

> +#define MOTION_PROFILE_VALID BIT(31)
> +
> +static LIST_HEAD(motion_list);
> +static DEFINE_MUTEX(motion_mtx);
> +static int motion_major;
> +static DEFINE_IDA(motion_minors_ida);
> +
> +struct iio_motion_trigger_info {
> +	unsigned int minor;
> +};
> +
> +static int motion_minor_alloc(void)
> +{
> +	int ret;
> +
> +	ret = ida_alloc_range(&motion_minors_ida, 0, MINORMASK, GFP_KERNEL);
> +	return ret;

This could be a one-liner.

> +}
> +
> +static void motion_minor_free(int minor)
> +{
> +	ida_free(&motion_minors_ida, minor);
> +}
> +
> +static int motion_open(struct inode *inode, struct file *file)
> +{
> +	int minor = iminor(inode);
> +	struct motion_device *mdev = NULL, *iter;
> +	int err;
> +
> +	mutex_lock(&motion_mtx);

If you use guard(), error handling gets a bit easier.

> +	list_for_each_entry(iter, &motion_list, list) {
> +		if (iter->minor != minor)
> +			continue;
> +		mdev = iter;
> +		break;
> +	}

This should be easier. If you use a cdev you can just do
container_of(inode->i_cdev, ...);

> +	if (!mdev) {
> +		err = -ENODEV;
> +		goto fail;
> +	}
> +
> +	dev_info(mdev->dev, "MOTION: open %d\n", mdev->minor);

degrade to dev_dbg.

> +	file->private_data = mdev;
> +
> +	if (mdev->ops.device_open)
> +		err = mdev->ops.device_open(mdev);
> +	else
> +		err = 0;
> +fail:
> +	mutex_unlock(&motion_mtx);
> +	return err;
> +}
> +
> +static int motion_release(struct inode *inode, struct file *file)
> +{
> +	struct motion_device *mdev = file->private_data;
> +	int i;
> +
> +	if (mdev->ops.device_release)
> +		mdev->ops.device_release(mdev);
> +
> +	for (i = 0; i < mdev->num_gpios; i++) {
> +		int irq;
> +		struct motion_gpio_input *gpio = &mdev->gpios[i];
> +
> +		if (gpio->function == MOT_INP_FUNC_NONE)
> +			continue;
> +		irq = gpiod_to_irq(gpio->gpio);
> +		devm_free_irq(mdev->dev, irq, gpio);

It seems devm is just overhead here if you release by hand anyhow.

> +		gpio->function = MOT_INP_FUNC_NONE;
> +	}
> +
> +	if (!kfifo_is_empty(&mdev->events))
> +		kfifo_reset(&mdev->events);
> +
> +	/* FIXME: Stop running motions? Probably not... */
> +
> +	return 0;
> +}
> +
> +static ssize_t motion_read(struct file *file, char __user *buffer,
> +			  size_t count, loff_t *ppos)
> +{
> +	struct motion_device *mdev = file->private_data;
> +	unsigned int copied = 0L;
> +	int ret;
> +
> +	if (!mdev->dev)
> +		return -ENODEV;
> +
> +	if (count < sizeof(struct mot_event))
> +		return -EINVAL;
> +
> +	do {
> +		if (kfifo_is_empty(&mdev->events)) {
> +			if (file->f_flags & O_NONBLOCK)
> +				return -EAGAIN;
> +
> +			ret = wait_event_interruptible(mdev->wait,
> +					!kfifo_is_empty(&mdev->events) ||
> +					mdev->dev == NULL);
> +			if (ret)
> +				return ret;
> +			if (mdev->dev == NULL)
> +				return -ENODEV;
> +		}
> +
> +		if (mutex_lock_interruptible(&mdev->read_mutex))
> +			return -ERESTARTSYS;
> +		ret = kfifo_to_user(&mdev->events, buffer, count, &copied);
> +		mutex_unlock(&mdev->read_mutex);
> +
> +		if (ret)
> +			return ret;
> +	} while (!copied);
> +
> +	return copied;
> +}
> +
> +static __poll_t motion_poll(struct file *file, poll_table *wait)
> +{
> +	struct motion_device *mdev = file->private_data;
> +	__poll_t mask = 0;
> +
> +	poll_wait(file, &mdev->wait, wait);
> +	if (!kfifo_is_empty(&mdev->events))
> +		mask = EPOLLIN | EPOLLRDNORM;
> +	dev_info(mdev->dev, "Obtained POLL events: 0x%08x\n", mask);

dev_dbg

> +
> +	return mask;
> +}
> +
> [...]
> +
> +static long motion_start_locked(struct motion_device *mdev, struct mot_start *s)
> +{
> +	long ret = 0L;
> +	mot_time_t conv_duration;
> +
> +	lockdep_assert_held(&mdev->mutex);
> +
> +	if (s->reserved1 || s->reserved2)
> +		return -EINVAL;
> +	if (s->channel >= mdev->capabilities.num_channels)
> +		return -EINVAL;
> +	if ((s->index >= MOT_MAX_PROFILES) || (s->direction > MOT_DIRECTION_RIGHT))
> +		return -EINVAL;
> +	if (!(mdev->profiles[s->index].index & MOTION_PROFILE_VALID))
> +		return -EINVAL;
> +	if (s->when >= MOT_WHEN_NUM_WHENS)
> +		return -EINVAL;
> +	if (s->duration && s->distance)
> +		return -EINVAL;
> +	if (!mdev->ops.motion_distance && !mdev->ops.motion_timed)
> +		return -EOPNOTSUPP;

I would add empty lines between these ifs to improve readability. Maybe
thats subjective though.

> +	if (s->duration) {
> +		if (!mdev->ops.motion_timed)
> +			return -EOPNOTSUPP;
> +		/* FIXME: Implement time to distance conversion? */
> +		return mdev->ops.motion_timed(mdev, s->channel, s->index,
> +				s->direction, s->duration, s->when);
> +	}
> +	if (!mdev->ops.motion_distance) {
> +		ret = motion_distance_to_time(mdev, s->index, s->distance,
> +				&conv_duration);
> +		if (ret)
> +			return ret;
> +		return mdev->ops.motion_timed(mdev, s->channel, s->index,
> +				s->direction, conv_duration, s->when);
> +	}
> +	ret = mdev->ops.motion_distance(mdev, s->channel, s->index,
> +			s->distance, s->when);
> +
> +	return ret;
> +}
> [...]
> +
> +static long motion_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> +{
> +	struct motion_device *mdev = file->private_data;
> +	void __user *argp = (void __user *)arg;
> +	long ret;
> +
> +	switch (cmd) {
> +	case MOT_IOCTL_APIVER:
> +		force_successful_syscall_return();
> +		return MOT_UAPI_VERSION;

force_successful_syscall_return() is only needed if the return value is
negative but no error.

> +	case MOT_IOCTL_BASIC_RUN: {
> +		struct mot_speed_duration spd;
> +
> +		if (copy_from_user(&spd, argp, sizeof(spd)))
> +			return -EFAULT;
> +		if (!mdev->ops.basic_run)
> +			return -EINVAL;
> [...]
> +
> +static const struct class motion_class = {
> +	.name		= "motion",
> +	.devnode	= motion_devnode,

IIRC it's recommended to not create new classes, but a bus.

> +};
> +
> +static const struct file_operations motion_fops = {
> +	.owner		= THIS_MODULE,
> +	.read		= motion_read,
> +	.poll		= motion_poll,
> +	.unlocked_ioctl = motion_ioctl,
> +	.open		= motion_open,
> +	.llseek		= noop_llseek,
> +	.release	= motion_release,
> +};
> +
> +static int motion_of_parse_gpios(struct motion_device *mdev)
> +{
> +	int ngpio, i;
> +
> +	ngpio = gpiod_count(mdev->parent, "motion,input");
> +	if (ngpio < 0) {
> +		if (ngpio == -ENOENT)
> +			return 0;
> +		return ngpio;
> +	}
> +
> +	if (ngpio >= MOT_MAX_INPUTS)
> +		return -EINVAL;
> +
> +	for (i = 0; i < ngpio; i++) {
> +		mdev->gpios[i].gpio = devm_gpiod_get_index(mdev->parent,
> +				"motion,input", i, GPIOD_IN);
> +		if (IS_ERR(mdev->gpios[i].gpio))
> +			return PTR_ERR(mdev->gpios[i].gpio);
> +		mdev->gpios[i].function = MOT_INP_FUNC_NONE;
> +		mdev->gpios[i].chmask = 0;
> +		mdev->gpios[i].index = i;
> +	}
> +
> +	mdev->num_gpios = ngpio;
> +	mdev->capabilities.num_ext_triggers += ngpio;
> +
> +	return 0;
> +}
> +
> +static void motion_trigger_work(struct irq_work *work)
> +{
> +	struct motion_device *mdev = container_of(work, struct motion_device,
> +							iiowork);
> +	iio_trigger_poll(mdev->iiotrig);
> +}
> +
> +/**
> + * motion_register_device - Register a new Motion Device
> + * @mdev: description and handle of the motion device
> + *
> + * Register a new motion device with the motion subsystem core.
> + * It also handles OF parsing of external trigger GPIOs and registers an IIO
> + * trigger device if IIO support is configured.
> + *
> + * Return: 0 on success, negative errno on failure.
> + */
> +int motion_register_device(struct motion_device *mdev)
> +{
> +	dev_t devt;
> +	int err = 0;
> +	struct iio_motion_trigger_info *trig_info;
> +
> +	if (!mdev->capabilities.num_channels)
> +		mdev->capabilities.num_channels = 1;
> +	if (mdev->capabilities.features | MOT_FEATURE_PROFILE)
> +		mdev->capabilities.max_profiles = MOT_MAX_PROFILES;
> +	if (!mdev->capabilities.speed_conv_mul)
> +		mdev->capabilities.speed_conv_mul = 1;
> +	if (!mdev->capabilities.speed_conv_div)
> +		mdev->capabilities.speed_conv_div = 1;
> +	if (!mdev->capabilities.accel_conv_mul)
> +		mdev->capabilities.accel_conv_mul = 1;
> +	if (!mdev->capabilities.accel_conv_div)
> +		mdev->capabilities.accel_conv_div = 1;
> +
> +	mutex_init(&mdev->mutex);
> +	mutex_init(&mdev->read_mutex);
> +	INIT_KFIFO(mdev->events);
> +	init_waitqueue_head(&mdev->wait);
> +
> +	err = motion_of_parse_gpios(mdev);
> +	if (err)
> +		return err;
> +
> +	mdev->minor = motion_minor_alloc();
> +
> +	mdev->iiotrig = iio_trigger_alloc(NULL, "mottrig%d", mdev->minor);
> +	if (!mdev->iiotrig) {
> +		err = -ENOMEM;
> +		goto error_free_minor;
> +	}
> +
> +	trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL);
> +	if (!trig_info) {
> +		err = -ENOMEM;
> +		goto error_free_trigger;
> +	}
> +
> +	iio_trigger_set_drvdata(mdev->iiotrig, trig_info);
> +
> +	trig_info->minor = mdev->minor;
> +	err = iio_trigger_register(mdev->iiotrig);
> +	if (err)
> +		goto error_free_trig_info;
> +
> +	mdev->iiowork = IRQ_WORK_INIT_HARD(motion_trigger_work);
> +
> +	INIT_LIST_HEAD(&mdev->list);
> +
> +	mutex_lock(&motion_mtx);
> +
> +	devt = MKDEV(motion_major, mdev->minor);
> +	mdev->dev = device_create_with_groups(&motion_class, mdev->parent,
> +				devt, mdev, mdev->groups, "motion%d", mdev->minor);

What makes sure that mdev doesn't go away while one of the attributes is
accessed?

> +	if (IS_ERR(mdev->dev)) {
> +		dev_err(mdev->parent, "Error creating motion device %d\n",
> +				mdev->minor);
> +		mutex_unlock(&motion_mtx);
> +		goto error_free_trig_info;
> +	}
> +	list_add_tail(&mdev->list, &motion_list);
> +	mutex_unlock(&motion_mtx);
> +
> +	return 0;
> +
> +error_free_trig_info:
> +	kfree(trig_info);
> +error_free_trigger:
> +	iio_trigger_free(mdev->iiotrig);
> +error_free_minor:
> +	motion_minor_free(mdev->minor);
> +	dev_info(mdev->parent, "Registering motion device err=%d\n", err);
> +	return err;
> +}
> +EXPORT_SYMBOL(motion_register_device);
> [...]
> +struct mot_capabilities {
> +	__u32 features;
> +	__u8 type;
> +	__u8 num_channels;
> +	__u8 num_int_triggers;
> +	__u8 num_ext_triggers;
> +	__u8 max_profiles;
> +	__u8 max_vpoints;
> +	__u8 max_apoints;
> +	__u8 reserved1;
> +	__u32 subdiv; /* Position unit sub-divisions, microsteps, etc... */
> +	/*
> +	 * Coefficients for converting to/from controller time <--> seconds.
> +	 * Speed[1/s] = Speed[controller_units] * conv_mul / conv_div
> +	 * Accel[1/s^2] = Accel[controller_units] * conv_mul / conv_div
> +	 */
> +	__u32 speed_conv_mul;
> +	__u32 speed_conv_div;
> +	__u32 accel_conv_mul;
> +	__u32 accel_conv_div;
> +	__u32 reserved2;
> +};

https://docs.kernel.org/gpu/imagination/uapi.html (which has some
generic bits that apply here, too) has: "The overall struct must be
padded to 64-bit alignment." If you drop reserved2 the struct is
properly sized (or I counted wrongly).

> +struct mot_speed_duration {
> +	__u32 channel;
> +	speed_raw_t speed;

What is the unit here?

> +	mot_time_t duration;

duration_ns? That makes usage much more ideomatic and there should be no
doubts what the unit is.

> +	pos_raw_t distance;

What is the unit here?

> +	__u32 reserved[3];

Again the padding is wrong here.

> +};

Best regards
Uwe

Download attachment "signature.asc" of type "application/pgp-signature" (489 bytes)

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ