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 000000000000..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(&counter->events_lock, flags); + + /* Search for event in the list */ + list_for_each_entry(event_node, &counter->events_list, l) + if (event_node->event == event && + event_node->channel == channel) + break; + + /* If event is not in the list */ + if (&event_node->l == &counter->events_list) + goto exit_early; + + /* Read and queue relevant comp for userspace */ + list_for_each_entry(comp_node, &event_node->comp_list, l) { + err = counter_get_data(counter, comp_node, &ev.value_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(&counter->events, ev); + } + + if (copied) + wake_up_poll(&counter->events_wait, EPOLLIN); + +exit_early: + raw_spin_unlock_irqrestore(&counter->events_lock, flags); + + return 0; + +err_counter_get_data: + raw_spin_unlock_irqrestore(&counter->events_lock, flags); + return err; +}