Re: [PATCH v5 3/5] counter: Add character device interface
On Sun, Oct 25, 2020 at 11:34:43AM -0500, David Lechner wrote: > On 10/25/20 8:18 AM, William Breathitt Gray wrote: > > On Tue, Oct 20, 2020 at 11:06:42AM -0500, David Lechner wrote: > >> On 10/18/20 11:58 AM, William Breathitt Gray wrote: > >>> On Wed, Oct 14, 2020 at 05:40:44PM -0500, David Lechner wrote: > On 9/26/20 9:18 PM, William Breathitt Gray wrote: > > +static ssize_t counter_chrdev_read(struct file *filp, char __user *buf, > > + size_t len, loff_t *f_ps) > > +{ > > + struct counter_device *const counter = filp->private_data; > > + int err; > > + unsigned long flags; > > + unsigned int copied; > > + > > + if (len < sizeof(struct counter_event)) > > + return -EINVAL; > > + > > + do { > > + if (kfifo_is_empty(>events)) { > > + if (filp->f_flags & O_NONBLOCK) > > + return -EAGAIN; > > + > > + err = > > wait_event_interruptible(counter->events_wait, > > + > > !kfifo_is_empty(>events)); > > + if (err) > > + return err; > > + } > > + > > + raw_spin_lock_irqsave(>events_lock, flags); > > + err = kfifo_to_user(>events, buf, len, > > ); > > + raw_spin_unlock_irqrestore(>events_lock, > > flags); > > + if (err) > > + return err; > > + } while (!copied); > > + > > + return copied; > > +} > > All other uses of kfifo_to_user() I saw use a mutex instead of spin > lock. I don't see a reason for disabling interrupts here. > >>> > >>> The Counter character device interface is special in this case because > >>> counter->events could be accessed from an interrupt context. This is > >>> possible if counter_push_event() is called for an interrupt (as is the > >>> case for the 104_quad_8 driver). In this case, we can't use mutex > >>> because we can't sleep in an interrupt context, so our only option is to > >>> use spin lock. > >>> > >> > >> > >> The way I understand it, locking is only needed for concurrent readers > >> and locking between reader and writer is not needed. > > > > You're right, it does say in the kfifo.h comments that with only one > > concurrent reader and one current write, we don't need extra locking to > > use these macros. Because we only have one kfifo_to_user() operating on > > counter->events, does that mean we don't need locking at all here for > > the counter_chrdev_read() function? > > > > William Breathitt Gray > > > > Even if we have the policy that only one file handle to the chrdev > can be open at a time, it is still possible that the it could be > read from multiple threads. So it I think it makes sense to keep > it just to be safe. All right, I'll keep the locks in the code for now to keep it safe in case we have multiple threads reading. William Breathitt Gray signature.asc Description: PGP signature
Re: [PATCH v5 3/5] counter: Add character device interface
diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c index e66ed99dd5ea..cefef61f170d 100644 --- a/drivers/counter/counter-sysfs.c +++ b/drivers/counter/counter-sysfs.c Not sure why sysfs changes are in the chrdev patch. Are these changes related somehow? Sorry, I forgot to explain this in the cover letter. The changes here are only useful for the character device interface. These changes introduce the extensionZ_name and extensionZ_width sysfs attributes. In the character device interface, extensions are selected by their id number, and the value returned depends on the type of data. The new sysfs attributes introduced here allow users to match the id of an extension with its name, as well as the bit width of the value returned so that the user knows whether to use the value_u8 or value_u64 union member in struct counter_event. Are we sure that all value types will always be CPU-endian unsigned integers? Or should we make an enum to describe the data type instead of just the width? It should be safe to assume that the character device interface will only ever return CPU-endian unsigned integers. The device driver should handle the conversion of any strange endianness from the device before the character device interface, while userspace is the one responsible for interpreting the meaning of count in the context of the application. Let's create a scenario for the sake of example. Suppose we want to use a counter device to track the vertical position of a component moved by a linear actuator. The operator considers some vertical position as the horizon, where anything above would be a positive position and anything below a negative position. The counter device stores its count in big-endian format; but the system CPU expects little-endian. The flow of data for this scenario would look like the following (where BE = big-endian, LE = little-endian): +--+ +---+ ++ | Raw data | - BE -> | Device driver | -> LE -> | chrdev | - u64 -> +--+ +---+ ++ At this point, the userspace application would read the unsigned integer from the character device and determine how to interpret the position -- whether the count be converted to a signed value to represent a negative physical position. Whether or not a position should be considered negative is dependent on the user application and context. Because the character device does not know the context of the user application, it should only provide unsigned integers in order to ensure a standard interface for counter devices; userspace will be responsible for interpreting those counts to something meaningful for the context of their applications. William Breathitt Gray Sounds reasonable to me.
Re: [PATCH v5 3/5] counter: Add character device interface
On 10/25/20 8:18 AM, William Breathitt Gray wrote: On Tue, Oct 20, 2020 at 11:06:42AM -0500, David Lechner wrote: On 10/18/20 11:58 AM, William Breathitt Gray wrote: On Wed, Oct 14, 2020 at 05:40:44PM -0500, David Lechner wrote: On 9/26/20 9:18 PM, William Breathitt Gray wrote: +static ssize_t counter_chrdev_read(struct file *filp, char __user *buf, + size_t len, loff_t *f_ps) +{ + struct counter_device *const counter = filp->private_data; + int err; + unsigned long flags; + unsigned int copied; + + if (len < sizeof(struct counter_event)) + return -EINVAL; + + do { + if (kfifo_is_empty(>events)) { + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + err = wait_event_interruptible(counter->events_wait, + !kfifo_is_empty(>events)); + if (err) + return err; + } + + raw_spin_lock_irqsave(>events_lock, flags); + err = kfifo_to_user(>events, buf, len, ); + raw_spin_unlock_irqrestore(>events_lock, flags); + if (err) + return err; + } while (!copied); + + return copied; +} All other uses of kfifo_to_user() I saw use a mutex instead of spin lock. I don't see a reason for disabling interrupts here. The Counter character device interface is special in this case because counter->events could be accessed from an interrupt context. This is possible if counter_push_event() is called for an interrupt (as is the case for the 104_quad_8 driver). In this case, we can't use mutex because we can't sleep in an interrupt context, so our only option is to use spin lock. The way I understand it, locking is only needed for concurrent readers and locking between reader and writer is not needed. You're right, it does say in the kfifo.h comments that with only one concurrent reader and one current write, we don't need extra locking to use these macros. Because we only have one kfifo_to_user() operating on counter->events, does that mean we don't need locking at all here for the counter_chrdev_read() function? William Breathitt Gray Even if we have the policy that only one file handle to the chrdev can be open at a time, it is still possible that the it could be read from multiple threads. So it I think it makes sense to keep it just to be safe.
Re: [PATCH v5 3/5] counter: Add character device interface
On Tue, Oct 20, 2020 at 11:06:42AM -0500, David Lechner wrote: > On 10/18/20 11:58 AM, William Breathitt Gray wrote: > > On Wed, Oct 14, 2020 at 05:40:44PM -0500, David Lechner wrote: > >> On 9/26/20 9:18 PM, William Breathitt Gray wrote: > >>> +static ssize_t counter_chrdev_read(struct file *filp, char __user *buf, > >>> +size_t len, loff_t *f_ps) > >>> +{ > >>> + struct counter_device *const counter = filp->private_data; > >>> + int err; > >>> + unsigned long flags; > >>> + unsigned int copied; > >>> + > >>> + if (len < sizeof(struct counter_event)) > >>> + return -EINVAL; > >>> + > >>> + do { > >>> + if (kfifo_is_empty(>events)) { > >>> + if (filp->f_flags & O_NONBLOCK) > >>> + return -EAGAIN; > >>> + > >>> + err = wait_event_interruptible(counter->events_wait, > >>> + !kfifo_is_empty(>events)); > >>> + if (err) > >>> + return err; > >>> + } > >>> + > >>> + raw_spin_lock_irqsave(>events_lock, flags); > >>> + err = kfifo_to_user(>events, buf, len, ); > >>> + raw_spin_unlock_irqrestore(>events_lock, flags); > >>> + if (err) > >>> + return err; > >>> + } while (!copied); > >>> + > >>> + return copied; > >>> +} > >> > >> All other uses of kfifo_to_user() I saw use a mutex instead of spin > >> lock. I don't see a reason for disabling interrupts here. > > > > The Counter character device interface is special in this case because > > counter->events could be accessed from an interrupt context. This is > > possible if counter_push_event() is called for an interrupt (as is the > > case for the 104_quad_8 driver). In this case, we can't use mutex > > because we can't sleep in an interrupt context, so our only option is to > > use spin lock. > > > > > The way I understand it, locking is only needed for concurrent readers > and locking between reader and writer is not needed. You're right, it does say in the kfifo.h comments that with only one concurrent reader and one current write, we don't need extra locking to use these macros. Because we only have one kfifo_to_user() operating on counter->events, does that mean we don't need locking at all here for the counter_chrdev_read() function? William Breathitt Gray signature.asc Description: PGP signature
Re: [PATCH v5 3/5] counter: Add character device interface
On Tue, Oct 20, 2020 at 10:53:32AM -0500, David Lechner wrote: > >>> + > >>> +static int counter_chrdev_release(struct inode *inode, struct file *filp) > >>> +{ > >>> + struct counter_device *const counter = filp->private_data; > >>> + unsigned long flags; > >>> + > >>> + put_device(>dev); > >> > >> put_device() should be at the end of the function in case it is the last > >> reference. > > > > put_device() shouldn't affect the counter_device events members, so I > > don't think there's a difference in this case if it's called at the > > beginning or end of the counter_chrdev_release function. > > > > It isn't possible the some memory allocated with devm_kalloc() could be > be referenced after calling put_device() now or in the future? You're right, if put_device() is called before then we could end up in a garbage state where the device memory is released but events list has not yet been freed. I'll move put_device() to after the events list is freed then. > >>> diff --git a/drivers/counter/counter-sysfs.c > >>> b/drivers/counter/counter-sysfs.c > >>> index e66ed99dd5ea..cefef61f170d 100644 > >>> --- a/drivers/counter/counter-sysfs.c > >>> +++ b/drivers/counter/counter-sysfs.c > >> > >> > >> Not sure why sysfs changes are in the chrdev patch. Are these > >> changes related somehow? > > > > Sorry, I forgot to explain this in the cover letter. The changes here > > are only useful for the character device interface. These changes > > introduce the extensionZ_name and extensionZ_width sysfs attributes. > > > > In the character device interface, extensions are selected by their id > > number, and the value returned depends on the type of data. The new > > sysfs attributes introduced here allow users to match the id of an > > extension with its name, as well as the bit width of the value returned > > so that the user knows whether to use the value_u8 or value_u64 union > > member in struct counter_event. > > > > Are we sure that all value types will always be CPU-endian unsigned > integers? Or should we make an enum to describe the data type instead > of just the width? It should be safe to assume that the character device interface will only ever return CPU-endian unsigned integers. The device driver should handle the conversion of any strange endianness from the device before the character device interface, while userspace is the one responsible for interpreting the meaning of count in the context of the application. Let's create a scenario for the sake of example. Suppose we want to use a counter device to track the vertical position of a component moved by a linear actuator. The operator considers some vertical position as the horizon, where anything above would be a positive position and anything below a negative position. The counter device stores its count in big-endian format; but the system CPU expects little-endian. The flow of data for this scenario would look like the following (where BE = big-endian, LE = little-endian): +--+ +---+ ++ | Raw data | - BE -> | Device driver | -> LE -> | chrdev | - u64 -> +--+ +---+ ++ At this point, the userspace application would read the unsigned integer from the character device and determine how to interpret the position -- whether the count be converted to a signed value to represent a negative physical position. Whether or not a position should be considered negative is dependent on the user application and context. Because the character device does not know the context of the user application, it should only provide unsigned integers in order to ensure a standard interface for counter devices; userspace will be responsible for interpreting those counts to something meaningful for the context of their applications. William Breathitt Gray signature.asc Description: PGP signature
Re: [PATCH v5 3/5] counter: Add character device interface
On 10/18/20 11:58 AM, William Breathitt Gray wrote: On Wed, Oct 14, 2020 at 05:40:44PM -0500, David Lechner wrote: On 9/26/20 9:18 PM, William Breathitt Gray wrote: +static ssize_t counter_chrdev_read(struct file *filp, char __user *buf, + size_t len, loff_t *f_ps) +{ + struct counter_device *const counter = filp->private_data; + int err; + unsigned long flags; + unsigned int copied; + + if (len < sizeof(struct counter_event)) + return -EINVAL; + + do { + if (kfifo_is_empty(>events)) { + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + err = wait_event_interruptible(counter->events_wait, + !kfifo_is_empty(>events)); + if (err) + return err; + } + + raw_spin_lock_irqsave(>events_lock, flags); + err = kfifo_to_user(>events, buf, len, ); + raw_spin_unlock_irqrestore(>events_lock, flags); + if (err) + return err; + } while (!copied); + + return copied; +} All other uses of kfifo_to_user() I saw use a mutex instead of spin lock. I don't see a reason for disabling interrupts here. The Counter character device interface is special in this case because counter->events could be accessed from an interrupt context. This is possible if counter_push_event() is called for an interrupt (as is the case for the 104_quad_8 driver). In this case, we can't use mutex because we can't sleep in an interrupt context, so our only option is to use spin lock. The way I understand it, locking is only needed for concurrent readers and locking between reader and writer is not needed.
Re: [PATCH v5 3/5] counter: Add character device interface
+static long counter_chrdev_ioctl(struct file *filp, unsigned int cmd, +unsigned long arg) +{ + struct counter_device *const counter = filp->private_data; + raw_spinlock_t *const events_lock = >events_lock; + unsigned long flags; + struct list_head *const events_list = >events_list; + struct list_head *const next_events_list = >next_events_list; + + switch (cmd) { + case COUNTER_CLEAR_WATCHES_IOCTL: + raw_spin_lock_irqsave(events_lock, flags); + counter_events_list_free(events_list); + raw_spin_unlock_irqrestore(events_lock, flags); + counter_events_list_free(next_events_list); I think this ioctl is doing too much. If we have to use it for both stopping events and clearing the list accumulated by COUNTER_SET_WATCH_IOCTL, then we have a race condition of no events after clearing watches during the time we are adding new ones and until we load the new ones. It would probably make more sense to call this ioctl COUNTER_STOP_WATCHES_IOCTL and move counter_events_list_free( next_events_list) to the end of COUNTER_LOAD_WATCHES_IOCTL. I don't think we will necessarily have a race condition here. COUNTER_CLEAR_WATCHES_IOCTL is intended to just clear the watches; e.g. bring us back to a clear state when some sort of job has completely finished and the user is no longer going to watch events for a while (maybe they're adjusting the conveyor for the next job or some similar operation). I think the scenario you're concerned about is when you need to swap watches in the middle of a job without losing events. In this case, you wouldn't need to use COUNTER_CLEAR_WATCHES_IOCTL at all. Instead, you would just set up the watches via COUNTER_SET_WATCH_IOCTL, and then use COUNTER_LOAD_WATCHES_IOCTL to perform the swap; after COUNTER_LOAD_WATCHES_IOCTL completes, next_events_list is empty (thanks to list_replace_init()) and you're ready for the next set of watches. Got it. I think I missed the fact that list_replace_init() clears next_events_list. + +static int counter_chrdev_release(struct inode *inode, struct file *filp) +{ + struct counter_device *const counter = filp->private_data; + unsigned long flags; + + put_device(>dev); put_device() should be at the end of the function in case it is the last reference. put_device() shouldn't affect the counter_device events members, so I don't think there's a difference in this case if it's called at the beginning or end of the counter_chrdev_release function. It isn't possible the some memory allocated with devm_kalloc() could be be referenced after calling put_device() now or in the future? +} +EXPORT_SYMBOL_GPL(counter_push_event); diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c index e66ed99dd5ea..cefef61f170d 100644 --- a/drivers/counter/counter-sysfs.c +++ b/drivers/counter/counter-sysfs.c Not sure why sysfs changes are in the chrdev patch. Are these changes related somehow? Sorry, I forgot to explain this in the cover letter. The changes here are only useful for the character device interface. These changes introduce the extensionZ_name and extensionZ_width sysfs attributes. In the character device interface, extensions are selected by their id number, and the value returned depends on the type of data. The new sysfs attributes introduced here allow users to match the id of an extension with its name, as well as the bit width of the value returned so that the user knows whether to use the value_u8 or value_u64 union member in struct counter_event. Are we sure that all value types will always be CPU-endian unsigned integers? Or should we make an enum to describe the data type instead of just the width?
Re: [PATCH v5 3/5] counter: Add character device interface
On Wed, Oct 14, 2020 at 05:40:44PM -0500, David Lechner wrote: > On 9/26/20 9:18 PM, William Breathitt Gray wrote: > > +static ssize_t counter_chrdev_read(struct file *filp, char __user *buf, > > + size_t len, loff_t *f_ps) > > +{ > > + struct counter_device *const counter = filp->private_data; > > + int err; > > + unsigned long flags; > > + unsigned int copied; > > + > > + if (len < sizeof(struct counter_event)) > > + return -EINVAL; > > + > > + do { > > + if (kfifo_is_empty(>events)) { > > + if (filp->f_flags & O_NONBLOCK) > > + return -EAGAIN; > > + > > + err = wait_event_interruptible(counter->events_wait, > > + !kfifo_is_empty(>events)); > > + if (err) > > + return err; > > + } > > + > > + raw_spin_lock_irqsave(>events_lock, flags); > > + err = kfifo_to_user(>events, buf, len, ); > > + raw_spin_unlock_irqrestore(>events_lock, flags); > > + if (err) > > + return err; > > + } while (!copied); > > + > > + return copied; > > +} > > All other uses of kfifo_to_user() I saw use a mutex instead of spin > lock. I don't see a reason for disabling interrupts here. The Counter character device interface is special in this case because counter->events could be accessed from an interrupt context. This is possible if counter_push_event() is called for an interrupt (as is the case for the 104_quad_8 driver). In this case, we can't use mutex because we can't sleep in an interrupt context, so our only option is to use spin lock. William Breathitt Gray signature.asc Description: PGP signature
Re: [PATCH v5 3/5] counter: Add character device interface
On Tue, Oct 13, 2020 at 08:40:18PM -0500, David Lechner wrote: > On 9/26/20 9:18 PM, William Breathitt Gray wrote: > > This patch introduces a character device interface for the Counter > > subsystem. Device data is exposed through standard character device read > > operations. Device data is gathered when a Counter event is pushed by > > the respective Counter device driver. Configuration is handled via ioctl > > operations on the respective Counter character device node. > > > > Probably don't need to duplicate the full documentation in the commit > message. Ack. > > diff --git a/drivers/counter/counter-chrdev.c > > b/drivers/counter/counter-chrdev.c > > new file mode 100644 > > index ..2be3846e4105 > > --- /dev/null > > +++ b/drivers/counter/counter-chrdev.c > > > > + > > +static int counter_set_event_node(struct counter_device *const counter, > > + struct counter_watch *const watch, > > + const struct counter_comp_node *const cfg) > > +{ > > + struct counter_event_node *event_node; > > + int err; > > + struct counter_comp_node *comp_node; > > + > > + /* Search for event in the list */ > > + list_for_each_entry(event_node, >next_events_list, l) > > + if (event_node->event == watch->event && > > + event_node->channel == watch->channel) > > + break; > > + > > + /* If event is not already in the list */ > > + if (_node->l == >next_events_list) { > > + /* Allocate new event node */ > > + event_node = kmalloc(sizeof(*event_node), GFP_ATOMIC); > > + if (!event_node) > > + return -ENOMEM; > > + > > + /* Configure event node and add to the list */ > > + event_node->event = watch->event; > > + event_node->channel = watch->channel; > > + INIT_LIST_HEAD(_node->comp_list); > > + list_add(_node->l, >next_events_list); > > + } > > + > > + /* Check if component watch has already been set before */ > > + list_for_each_entry(comp_node, _node->comp_list, l) > > + if (comp_node->parent == cfg->parent && > > + comp_node->comp.count_u8_read == cfg->comp.count_u8_read) > > + return -EINVAL; > > There are already enough things that could cause EINVAL, we could > probably skip this duplicate check to make troubleshooting easier. I'm not sure it would be safe to remove this check. This prevents duplicate watches from being allocated for the same component -- the rationale is that there's no benefit in repeating a component value grab for the same event. Although a typical user will not have the motivation to do this, a bad actor could purposely keep allocating duplicate watches in order to starve all the available memory. > > + > > + /* Allocate component node */ > > + comp_node = kmalloc(sizeof(*comp_node), GFP_ATOMIC); > > + if (!comp_node) { > > + err = -ENOMEM; > > + goto err_comp_node; > > Since there is only one goto, we could just handle the error and > return here instead. Ack. > > + } > > + *comp_node = *cfg; > > + > > + /* Add component node to event node */ > > + list_add_tail(_node->l, _node->comp_list); > > + > > + return 0; > > + > > +err_comp_node: > > A comment explaining the list_empty() check would be nice. > It makes sense if you think about it, but it is not super > obvious. Ack. > > + if (list_empty(_node->comp_list)) { > > + list_del(_node->l); > > + kfree(event_node); > > + } > > + return err; > > +} > > + > > +static int counter_set_watch(struct counter_device *const counter, > > +const unsigned long arg) > > +{ > > + void __user *const uwatch = (void __user *)arg; > > + struct counter_watch watch; > > + struct counter_comp_node comp_node; > > + size_t parent, id; > > + struct counter_comp *ext; > > + size_t num_ext; > > + > > + if (copy_from_user(, uwatch, sizeof(watch))) > > + return -EFAULT; > > + parent = watch.component.parent; > > + id = watch.component.id; > > + > > + /* Configure parent component info for comp node */ > > + switch (watch.component.scope) { > > + case COUNTER_SCOPE_DEVICE: > > + comp_node.parent = NULL; > > + > > + ext = counter->ext; > > + num_ext = counter->num_ext; > > + break; > > + case COUNTER_SCOPE_SIGNAL: > > + if (counter->num_signals < parent + 1) > > I think it would be more conventional this way: > > if (parent >= counter->num_signals) Ack. > > + return -EINVAL; > > + > > + comp_node.parent = counter->signals + parent; > > + > > + ext = counter->signals[parent].ext; > > + num_ext = counter->signals[parent].num_ext; > > + break; > > + case COUNTER_SCOPE_COUNT: > > + if (counter->num_counts < parent + 1) > > Same here. Ack. > > +
Re: [PATCH v5 3/5] counter: Add character device interface
On 9/26/20 9:18 PM, William Breathitt Gray wrote: +static ssize_t counter_chrdev_read(struct file *filp, char __user *buf, + size_t len, loff_t *f_ps) +{ + struct counter_device *const counter = filp->private_data; + int err; + unsigned long flags; + unsigned int copied; + + if (len < sizeof(struct counter_event)) + return -EINVAL; + + do { + if (kfifo_is_empty(>events)) { + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + err = wait_event_interruptible(counter->events_wait, + !kfifo_is_empty(>events)); + if (err) + return err; + } + + raw_spin_lock_irqsave(>events_lock, flags); + err = kfifo_to_user(>events, buf, len, ); + raw_spin_unlock_irqrestore(>events_lock, flags); + if (err) + return err; + } while (!copied); + + return copied; +} All other uses of kfifo_to_user() I saw use a mutex instead of spin lock. I don't see a reason for disabling interrupts here. Example: static ssize_t iio_event_chrdev_read(struct file *filep, char __user *buf, size_t count, loff_t *f_ps) { struct iio_dev *indio_dev = filep->private_data; struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); struct iio_event_interface *ev_int = iio_dev_opaque->event_interface; unsigned int copied; int ret; if (!indio_dev->info) return -ENODEV; if (count < sizeof(struct iio_event_data)) return -EINVAL; do { if (kfifo_is_empty(_int->det_events)) { if (filep->f_flags & O_NONBLOCK) return -EAGAIN; ret = wait_event_interruptible(ev_int->wait, !kfifo_is_empty(_int->det_events) || indio_dev->info == NULL); if (ret) return ret; if (indio_dev->info == NULL) return -ENODEV; } if (mutex_lock_interruptible(_int->read_lock)) return -ERESTARTSYS; ret = kfifo_to_user(_int->det_events, buf, count, ); mutex_unlock(_int->read_lock); if (ret) return ret; /* * If we couldn't read anything from the fifo (a different * thread might have been faster) we either return -EAGAIN if * the file descriptor is non-blocking, otherwise we go back to * sleep and wait for more data to arrive. */ if (copied == 0 && (filep->f_flags & O_NONBLOCK)) return -EAGAIN; } while (copied == 0); return copied; }
Re: [PATCH v5 3/5] counter: Add character device interface
On 10/14/20 2:05 PM, William Breathitt Gray wrote: On Wed, Oct 14, 2020 at 12:43:08PM -0500, David Lechner wrote: On 9/26/20 9:18 PM, William Breathitt Gray wrote: diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c new file mode 100644 index ..2be3846e4105 --- /dev/null +++ b/drivers/counter/counter-chrdev.c +/** + * counter_push_event - queue event for userspace reading + * @counter: pointer to Counter structure + * @event: triggered event + * @channel: event channel + * + * Note: If no one is watching for the respective event, it is silently + * discarded. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int counter_push_event(struct counter_device *const counter, const u8 event, + const u8 channel) +{ + struct counter_event ev = {0}; + unsigned int copied = 0; + unsigned long flags; + struct counter_event_node *event_node; + struct counter_comp_node *comp_node; + int err; + + ev.timestamp = ktime_get_ns(); + ev.watch.event = event; + ev.watch.channel = channel; + + raw_spin_lock_irqsave(>events_lock, flags); + + /* Search for event in the list */ + list_for_each_entry(event_node, >events_list, l) + if (event_node->event == event && + event_node->channel == channel) + break; + + /* If event is not in the list */ + if (_node->l == >events_list) + goto exit_early; + + /* Read and queue relevant comp for userspace */ + list_for_each_entry(comp_node, _node->comp_list, l) { + err = counter_get_data(counter, comp_node, _u8); Currently all counter devices are memory mapped devices so calling counter_get_data() here with interrupts disabled is probably OK, but if any counter drivers are added that use I2C/SPI/etc. that will take a long time to read, it would cause problems leaving interrupts disabled here. Brainstorming: Would it make sense to separate the event from the component value being read? As I mentioned in one of my previous reviews, I think there are some cases where we would just want to know when an event happened and not read any additional data anyway. In the case of a slow communication bus, this would also let us queue the event in the kfifo and notify poll right away and then defer the reads in a workqueue for later. I don't see any problems with reporting just an event without any component value attached (e.g. userspace could handle the component reads via sysfs at a later point). We would just need a way to inform userspace that the struct counter_component in the struct counter_watch returned should be ignored. Perhaps we can add an additional member to struct counter_watch indicating whether it's an empty watch; or alternatively, add a new component scope define to differentiate between an actual component and an empty one (e.g. COUNTER_SCOPE_EVENT). What do you think? William Breathitt Gray I made the same suggestion in one of my other replies - except I called it COUNTER_SCOPE_NONE.
Re: [PATCH v5 3/5] counter: Add character device interface
On Wed, Oct 14, 2020 at 12:43:08PM -0500, David Lechner wrote: > On 9/26/20 9:18 PM, William Breathitt Gray wrote: > > diff --git a/drivers/counter/counter-chrdev.c > > b/drivers/counter/counter-chrdev.c > > new file mode 100644 > > index ..2be3846e4105 > > --- /dev/null > > +++ b/drivers/counter/counter-chrdev.c > > > > +/** > > + * counter_push_event - queue event for userspace reading > > + * @counter: pointer to Counter structure > > + * @event: triggered event > > + * @channel: event channel > > + * > > + * Note: If no one is watching for the respective event, it is silently > > + * discarded. > > + * > > + * RETURNS: > > + * 0 on success, negative error number on failure. > > + */ > > +int counter_push_event(struct counter_device *const counter, const u8 > > event, > > + const u8 channel) > > +{ > > + struct counter_event ev = {0}; > > + unsigned int copied = 0; > > + unsigned long flags; > > + struct counter_event_node *event_node; > > + struct counter_comp_node *comp_node; > > + int err; > > + > > + ev.timestamp = ktime_get_ns(); > > + ev.watch.event = event; > > + ev.watch.channel = channel; > > + > > + raw_spin_lock_irqsave(>events_lock, flags); > > + > > + /* Search for event in the list */ > > + list_for_each_entry(event_node, >events_list, l) > > + if (event_node->event == event && > > + event_node->channel == channel) > > + break; > > + > > + /* If event is not in the list */ > > + if (_node->l == >events_list) > > + goto exit_early; > > + > > + /* Read and queue relevant comp for userspace */ > > + list_for_each_entry(comp_node, _node->comp_list, l) { > > + err = counter_get_data(counter, comp_node, _u8); > > Currently all counter devices are memory mapped devices so calling > counter_get_data() here with interrupts disabled is probably OK, but > if any counter drivers are added that use I2C/SPI/etc. that will take > a long time to read, it would cause problems leaving interrupts > disabled here. > > Brainstorming: Would it make sense to separate the event from the > component value being read? As I mentioned in one of my previous > reviews, I think there are some cases where we would just want to > know when an event happened and not read any additional data anyway. > In the case of a slow communication bus, this would also let us > queue the event in the kfifo and notify poll right away and then > defer the reads in a workqueue for later. I don't see any problems with reporting just an event without any component value attached (e.g. userspace could handle the component reads via sysfs at a later point). We would just need a way to inform userspace that the struct counter_component in the struct counter_watch returned should be ignored. Perhaps we can add an additional member to struct counter_watch indicating whether it's an empty watch; or alternatively, add a new component scope define to differentiate between an actual component and an empty one (e.g. COUNTER_SCOPE_EVENT). What do you think? William Breathitt Gray signature.asc Description: PGP signature
Re: [PATCH v5 3/5] counter: Add character device interface
On 9/26/20 9:18 PM, William Breathitt Gray wrote: diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c new file mode 100644 index ..2be3846e4105 --- /dev/null +++ b/drivers/counter/counter-chrdev.c +/** + * counter_push_event - queue event for userspace reading + * @counter: pointer to Counter structure + * @event: triggered event + * @channel: event channel + * + * Note: If no one is watching for the respective event, it is silently + * discarded. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int counter_push_event(struct counter_device *const counter, const u8 event, + const u8 channel) +{ + struct counter_event ev = {0}; + unsigned int copied = 0; + unsigned long flags; + struct counter_event_node *event_node; + struct counter_comp_node *comp_node; + int err; + + ev.timestamp = ktime_get_ns(); + ev.watch.event = event; + ev.watch.channel = channel; + + raw_spin_lock_irqsave(>events_lock, flags); + + /* Search for event in the list */ + list_for_each_entry(event_node, >events_list, l) + if (event_node->event == event && + event_node->channel == channel) + break; + + /* If event is not in the list */ + if (_node->l == >events_list) + goto exit_early; + + /* Read and queue relevant comp for userspace */ + list_for_each_entry(comp_node, _node->comp_list, l) { + err = counter_get_data(counter, comp_node, _u8); Currently all counter devices are memory mapped devices so calling counter_get_data() here with interrupts disabled is probably OK, but if any counter drivers are added that use I2C/SPI/etc. that will take a long time to read, it would cause problems leaving interrupts disabled here. Brainstorming: Would it make sense to separate the event from the component value being read? As I mentioned in one of my previous reviews, I think there are some cases where we would just want to know when an event happened and not read any additional data anyway. In the case of a slow communication bus, this would also let us queue the event in the kfifo and notify poll right away and then defer the reads in a workqueue for later. + if (err) + goto err_counter_get_data; + + ev.watch.component = comp_node->component; + + copied += kfifo_put(>events, ev); + } + + if (copied) + wake_up_poll(>events_wait, EPOLLIN); + +exit_early: + raw_spin_unlock_irqrestore(>events_lock, flags); + + return 0; + +err_counter_get_data: + raw_spin_unlock_irqrestore(>events_lock, flags); + return err; +}
Re: [PATCH v5 3/5] counter: Add character device interface
On 9/26/20 9:18 PM, William Breathitt Gray wrote: This patch introduces a character device interface for the Counter subsystem. Device data is exposed through standard character device read operations. Device data is gathered when a Counter event is pushed by the respective Counter device driver. Configuration is handled via ioctl operations on the respective Counter character device node. Probably don't need to duplicate the full documentation in the commit message. diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c new file mode 100644 index ..2be3846e4105 --- /dev/null +++ b/drivers/counter/counter-chrdev.c + +static int counter_set_event_node(struct counter_device *const counter, + struct counter_watch *const watch, + const struct counter_comp_node *const cfg) +{ + struct counter_event_node *event_node; + int err; + struct counter_comp_node *comp_node; + + /* Search for event in the list */ + list_for_each_entry(event_node, >next_events_list, l) + if (event_node->event == watch->event && + event_node->channel == watch->channel) + break; + + /* If event is not already in the list */ + if (_node->l == >next_events_list) { + /* Allocate new event node */ + event_node = kmalloc(sizeof(*event_node), GFP_ATOMIC); + if (!event_node) + return -ENOMEM; + + /* Configure event node and add to the list */ + event_node->event = watch->event; + event_node->channel = watch->channel; + INIT_LIST_HEAD(_node->comp_list); + list_add(_node->l, >next_events_list); + } + + /* Check if component watch has already been set before */ + list_for_each_entry(comp_node, _node->comp_list, l) + if (comp_node->parent == cfg->parent && + comp_node->comp.count_u8_read == cfg->comp.count_u8_read) + return -EINVAL; There are already enough things that could cause EINVAL, we could probably skip this duplicate check to make troubleshooting easier. + + /* Allocate component node */ + comp_node = kmalloc(sizeof(*comp_node), GFP_ATOMIC); + if (!comp_node) { + err = -ENOMEM; + goto err_comp_node; Since there is only one goto, we could just handle the error and return here instead. + } + *comp_node = *cfg; + + /* Add component node to event node */ + list_add_tail(_node->l, _node->comp_list); + + return 0; + +err_comp_node: A comment explaining the list_empty() check would be nice. It makes sense if you think about it, but it is not super obvious. + if (list_empty(_node->comp_list)) { + list_del(_node->l); + kfree(event_node); + } + return err; +} + +static int counter_set_watch(struct counter_device *const counter, +const unsigned long arg) +{ + void __user *const uwatch = (void __user *)arg; + struct counter_watch watch; + struct counter_comp_node comp_node; + size_t parent, id; + struct counter_comp *ext; + size_t num_ext; + + if (copy_from_user(, uwatch, sizeof(watch))) + return -EFAULT; + parent = watch.component.parent; + id = watch.component.id; + + /* Configure parent component info for comp node */ + switch (watch.component.scope) { + case COUNTER_SCOPE_DEVICE: + comp_node.parent = NULL; + + ext = counter->ext; + num_ext = counter->num_ext; + break; + case COUNTER_SCOPE_SIGNAL: + if (counter->num_signals < parent + 1) I think it would be more conventional this way: if (parent >= counter->num_signals) + return -EINVAL; + + comp_node.parent = counter->signals + parent; + + ext = counter->signals[parent].ext; + num_ext = counter->signals[parent].num_ext; + break; + case COUNTER_SCOPE_COUNT: + if (counter->num_counts < parent + 1) Same here. + return -EINVAL; + + comp_node.parent = counter->counts + parent; + + ext = counter->counts[parent].ext; + num_ext = counter->counts[parent].num_ext; + break; + default: + return -EINVAL; + } + + /* Configure component info for comp node */ + switch (watch.component.type) { + case COUNTER_COMPONENT_SIGNAL: + if (watch.component.scope != COUNTER_SCOPE_SIGNAL) + return -EINVAL; + + comp_node.comp.type = COUNTER_COMP_SIGNAL_LEVEL; + comp_node.comp.signal_u8_read =
[PATCH v5 3/5] counter: Add character device interface
This patch introduces a character device interface for the Counter subsystem. Device data is exposed through standard character device read operations. Device data is gathered when a Counter event is pushed by the respective Counter device driver. Configuration is handled via ioctl operations on the respective Counter character device node. A high-level view of how a count value is passed down from a counter driver is exemplified by the following. The driver callbacks are first registered to the Counter core component for use by the Counter userspace interface components: ++ | Counter device driver | ++ | Processes data from device | ++ | --- / driver callbacks / --- | V +--+ | Counter core | +--+ | Routes device driver | | callbacks to the | | userspace interfaces | +--+ | --- / driver callbacks / --- | +---+---+ | | V V ++ +-+ | Counter sysfs | | Counter chrdev | ++ +-+ | Translates to the | | Translates to the | | standard Counter | | standard Counter| | sysfs output | | character device| ++ +-+ Thereafter, data can be transferred directly between the Counter device driver and Counter userspace interface: -- / Counter device \ +--+ | Count register: 0x28 | +--+ | - / raw count data / - | V ++ | Counter device driver | ++ | Processes data from device | || | Type: u64 | | Value: 42 | ++ | -- / u64 / -- | +---+---+ | | V V ++ +-+ | Counter sysfs | | Counter chrdev | ++ +-+ | Translates to the | | Translates to the | | standard Counter | | standard Counter| | sysfs output | | character device| || |-| | Type: const char * | | Type: u64 | | Value: "42"| | Value: 42 | ++ +-+ | | --- --- / const char * // struct counter_event / --- --- | | | V | +---+ | | read | | +---+ | \ Count: 42 / |--- | V +--+ | `/sys/bus/counter/devices/counterX/countY/count` |