regulator: userspace-consumer: Add regulator event support

Message ID 20230803111225.107572-1-Naresh.Solanki@9elements.com
State New
Headers
Series regulator: userspace-consumer: Add regulator event support |

Commit Message

Naresh Solanki Aug. 3, 2023, 11:12 a.m. UTC
  Add sysfs attribute to track regulator events received from regulator
notifier block handler.

Signed-off-by: Naresh Solanki <Naresh.Solanki@9elements.com>
---
 drivers/regulator/userspace-consumer.c | 52 +++++++++++++++++++++++++-
 1 file changed, 51 insertions(+), 1 deletion(-)


base-commit: 4fb53b2377c364e3753d6e293913b57dad68e98b
  

Comments

Mark Brown Aug. 3, 2023, 2:57 p.m. UTC | #1
On Thu, 03 Aug 2023 13:12:25 +0200, Naresh Solanki wrote:
> Add sysfs attribute to track regulator events received from regulator
> notifier block handler.
> 
> 

Applied to

   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator.git for-next

Thanks!

[1/1] regulator: userspace-consumer: Add regulator event support
      commit: 22475bcc2083196544fa55b861d76e0e7ee9da11

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark
  
Zev Weiss Aug. 3, 2023, 8:45 p.m. UTC | #2
On Thu, Aug 03, 2023 at 04:12:25AM PDT, Naresh Solanki wrote:
>Add sysfs attribute to track regulator events received from regulator
>notifier block handler.
>

Hi Naresh,

Could you provide a bit more detail on how this is intended to be used?  
Some of the details (more below) seem a bit odd to me...

>Signed-off-by: Naresh Solanki <Naresh.Solanki@9elements.com>
>---
> drivers/regulator/userspace-consumer.c | 52 +++++++++++++++++++++++++-
> 1 file changed, 51 insertions(+), 1 deletion(-)
>
>diff --git a/drivers/regulator/userspace-consumer.c b/drivers/regulator/userspace-consumer.c
>index 97f075ed68c9..a0b980022993 100644
>--- a/drivers/regulator/userspace-consumer.c
>+++ b/drivers/regulator/userspace-consumer.c
>@@ -29,6 +29,10 @@ struct userspace_consumer_data {
>
> 	int num_supplies;
> 	struct regulator_bulk_data *supplies;
>+
>+	struct kobject *kobj;
>+	struct notifier_block nb;
>+	unsigned long events;
> };
>
> static ssize_t name_show(struct device *dev,
>@@ -89,12 +93,30 @@ static ssize_t state_store(struct device *dev, struct device_attribute *attr,
> 	return count;
> }
>
>+static DEFINE_MUTEX(events_lock);
>+
>+static ssize_t events_show(struct device *dev,
>+			   struct device_attribute *attr, char *buf)
>+{
>+	struct userspace_consumer_data *data = dev_get_drvdata(dev);
>+	unsigned long e;
>+
>+	mutex_lock(&events_lock);
>+	e = data->events;
>+	data->events = 0;

...particularly this bit -- a read operation on a read-only file (and 
especially one with 0644 permissions) having side-effects (clearing the 
value it accesses) seems on the face of it like fairly surprising 
behavior.  Is this a pattern that's used elsewhere in any other sysfs 
files?

>+	mutex_unlock(&events_lock);
>+
>+	return sprintf(buf, "0x%lx\n", e);
>+}
>+
> static DEVICE_ATTR_RO(name);
> static DEVICE_ATTR_RW(state);
>+static DEVICE_ATTR_RO(events);

New sysfs attributes should be documented in Documentation/ABI, which 
this appears to be missing.

However, it looks like this would expose the values of all the 
REGULATOR_EVENT_* constants as a userspace-visible ABI -- is that 
something we really want to do?

>
> static struct attribute *attributes[] = {
> 	&dev_attr_name.attr,
> 	&dev_attr_state.attr,
>+	&dev_attr_events.attr,
> 	NULL,
> };
>
>@@ -115,12 +137,28 @@ static const struct attribute_group attr_group = {
> 	.is_visible =  attr_visible,
> };
>
>+static int regulator_userspace_notify(struct notifier_block *nb,
>+				      unsigned long event,
>+				      void *ignored)
>+{
>+	struct userspace_consumer_data *data =
>+		container_of(nb, struct userspace_consumer_data, nb);
>+
>+	mutex_lock(&events_lock);
>+	data->events |= event;
>+	mutex_unlock(&events_lock);
>+

Using a single global mutex (events_lock) to protect a single member of 
a per-device struct looks weird.  Unless there's something subtle going 
on that I'm not seeing, it seems like the lock should be a member of the 
data struct instead of global, and since no blocking operations happen 
under it could it just be a spinlock?  Or since it's just some simple 
updates to a single variable, why not just use an atomic_t and skip the 
lock entirely?

>+	sysfs_notify(data->kobj, NULL, dev_attr_events.attr.name);
>+
>+	return NOTIFY_OK;
>+}
>+
> static int regulator_userspace_consumer_probe(struct platform_device *pdev)
> {
> 	struct regulator_userspace_consumer_data tmpdata;
> 	struct regulator_userspace_consumer_data *pdata;
> 	struct userspace_consumer_data *drvdata;
>-	int ret;
>+	int i, ret;
>
> 	pdata = dev_get_platdata(&pdev->dev);
> 	if (!pdata) {
>@@ -153,6 +191,7 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev)
> 	drvdata->num_supplies = pdata->num_supplies;
> 	drvdata->supplies = pdata->supplies;
> 	drvdata->no_autoswitch = pdata->no_autoswitch;
>+	drvdata->kobj = &pdev->dev.kobj;
>
> 	mutex_init(&drvdata->lock);
>
>@@ -186,6 +225,13 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev)
> 	}
> 	drvdata->enabled = !!ret;
>
>+	drvdata->nb.notifier_call = regulator_userspace_notify;
>+	for (i = 0; i < drvdata->num_supplies; i++) {
>+		ret = devm_regulator_register_notifier(drvdata->supplies[i].consumer, &drvdata->nb);
>+		if (ret)
>+			goto err_enable;
>+	}
>+
> 	return 0;
>
> err_enable:
>@@ -197,6 +243,10 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev)
> static int regulator_userspace_consumer_remove(struct platform_device *pdev)
> {
> 	struct userspace_consumer_data *data = platform_get_drvdata(pdev);
>+	int i;
>+
>+	for (i = 0; i < data->num_supplies; i++)
>+		devm_regulator_unregister_notifier(data->supplies[i].consumer, &data->nb);
>
> 	sysfs_remove_group(&pdev->dev.kobj, &attr_group);
>
>
>base-commit: 4fb53b2377c364e3753d6e293913b57dad68e98b
>-- 
>2.41.0
>
  
Naresh Solanki Aug. 4, 2023, 8:59 a.m. UTC | #3
Hi Zev,


On Fri, 4 Aug 2023 at 02:15, Zev Weiss <zev@bewilderbeest.net> wrote:
>
> On Thu, Aug 03, 2023 at 04:12:25AM PDT, Naresh Solanki wrote:
> >Add sysfs attribute to track regulator events received from regulator
> >notifier block handler.
> >
>
> Hi Naresh,
>
> Could you provide a bit more detail on how this is intended to be used?
> Some of the details (more below) seem a bit odd to me...
My application registers a event callback on the 'events' to track regulator
events
Reference:
https://github.com/9elements/pwrseqd/blob/main/src/VoltageRegulatorSysfs.cpp#L258
>
> >Signed-off-by: Naresh Solanki <Naresh.Solanki@9elements.com>
> >---
> > drivers/regulator/userspace-consumer.c | 52 +++++++++++++++++++++++++-
> > 1 file changed, 51 insertions(+), 1 deletion(-)
> >
> >diff --git a/drivers/regulator/userspace-consumer.c b/drivers/regulator/userspace-consumer.c
> >index 97f075ed68c9..a0b980022993 100644
> >--- a/drivers/regulator/userspace-consumer.c
> >+++ b/drivers/regulator/userspace-consumer.c
> >@@ -29,6 +29,10 @@ struct userspace_consumer_data {
> >
> >       int num_supplies;
> >       struct regulator_bulk_data *supplies;
> >+
> >+      struct kobject *kobj;
> >+      struct notifier_block nb;
> >+      unsigned long events;
> > };
> >
> > static ssize_t name_show(struct device *dev,
> >@@ -89,12 +93,30 @@ static ssize_t state_store(struct device *dev, struct device_attribute *attr,
> >       return count;
> > }
> >
> >+static DEFINE_MUTEX(events_lock);
> >+
> >+static ssize_t events_show(struct device *dev,
> >+                         struct device_attribute *attr, char *buf)
> >+{
> >+      struct userspace_consumer_data *data = dev_get_drvdata(dev);
> >+      unsigned long e;
> >+
> >+      mutex_lock(&events_lock);
> >+      e = data->events;
> >+      data->events = 0;
>
> ...particularly this bit -- a read operation on a read-only file (and
> especially one with 0644 permissions) having side-effects (clearing the
> value it accesses) seems on the face of it like fairly surprising
> behavior.  Is this a pattern that's used elsewhere in any other sysfs
> files?
These are regulator events & are valid when it occurs.
Userspace application is intended to consume them as soon as the
event is notified by kernel sysfs_notify.

>
> >+      mutex_unlock(&events_lock);
> >+
> >+      return sprintf(buf, "0x%lx\n", e);
> >+}
> >+
> > static DEVICE_ATTR_RO(name);
> > static DEVICE_ATTR_RW(state);
> >+static DEVICE_ATTR_RO(events);
>
> New sysfs attributes should be documented in Documentation/ABI, which
> this appears to be missing.
Sure I can check.
>
> However, it looks like this would expose the values of all the
> REGULATOR_EVENT_* constants as a userspace-visible ABI -- is that
> something we really want to do?
Yes.
>
> >
> > static struct attribute *attributes[] = {
> >       &dev_attr_name.attr,
> >       &dev_attr_state.attr,
> >+      &dev_attr_events.attr,
> >       NULL,
> > };
> >
> >@@ -115,12 +137,28 @@ static const struct attribute_group attr_group = {
> >       .is_visible =  attr_visible,
> > };
> >
> >+static int regulator_userspace_notify(struct notifier_block *nb,
> >+                                    unsigned long event,
> >+                                    void *ignored)
> >+{
> >+      struct userspace_consumer_data *data =
> >+              container_of(nb, struct userspace_consumer_data, nb);
> >+
> >+      mutex_lock(&events_lock);
> >+      data->events |= event;
> >+      mutex_unlock(&events_lock);
> >+
>
> Using a single global mutex (events_lock) to protect a single member of
> a per-device struct looks weird.  Unless there's something subtle going
> on that I'm not seeing, it seems like the lock should be a member of the
> data struct instead of global, and since no blocking operations happen
> under it could it just be a spinlock?  Or since it's just some simple
> updates to a single variable, why not just use an atomic_t and skip the
> lock entirely?
Intent is that only one thread at a time is to be allowed to access/modify
the data->events variable to prevent potential data corruption and
race conditions. Sure can change it to spinlock or atomic_t.

>
> >+      sysfs_notify(data->kobj, NULL, dev_attr_events.attr.name);
> >+
> >+      return NOTIFY_OK;
> >+}
> >+
> > static int regulator_userspace_consumer_probe(struct platform_device *pdev)
> > {
> >       struct regulator_userspace_consumer_data tmpdata;
> >       struct regulator_userspace_consumer_data *pdata;
> >       struct userspace_consumer_data *drvdata;
> >-      int ret;
> >+      int i, ret;
> >
> >       pdata = dev_get_platdata(&pdev->dev);
> >       if (!pdata) {
> >@@ -153,6 +191,7 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev)
> >       drvdata->num_supplies = pdata->num_supplies;
> >       drvdata->supplies = pdata->supplies;
> >       drvdata->no_autoswitch = pdata->no_autoswitch;
> >+      drvdata->kobj = &pdev->dev.kobj;
> >
> >       mutex_init(&drvdata->lock);
> >
> >@@ -186,6 +225,13 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev)
> >       }
> >       drvdata->enabled = !!ret;
> >
> >+      drvdata->nb.notifier_call = regulator_userspace_notify;
> >+      for (i = 0; i < drvdata->num_supplies; i++) {
> >+              ret = devm_regulator_register_notifier(drvdata->supplies[i].consumer, &drvdata->nb);
> >+              if (ret)
> >+                      goto err_enable;
> >+      }
> >+
> >       return 0;
> >
> > err_enable:
> >@@ -197,6 +243,10 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev)
> > static int regulator_userspace_consumer_remove(struct platform_device *pdev)
> > {
> >       struct userspace_consumer_data *data = platform_get_drvdata(pdev);
> >+      int i;
> >+
> >+      for (i = 0; i < data->num_supplies; i++)
> >+              devm_regulator_unregister_notifier(data->supplies[i].consumer, &data->nb);
> >
> >       sysfs_remove_group(&pdev->dev.kobj, &attr_group);
> >
> >
> >base-commit: 4fb53b2377c364e3753d6e293913b57dad68e98b
> >--
> >2.41.0
> >
  
Zev Weiss Aug. 4, 2023, 12:02 p.m. UTC | #4
On Fri, Aug 04, 2023 at 01:59:44AM PDT, Naresh Solanki wrote:
>Hi Zev,
>
>
>On Fri, 4 Aug 2023 at 02:15, Zev Weiss <zev@bewilderbeest.net> wrote:
>>
>> On Thu, Aug 03, 2023 at 04:12:25AM PDT, Naresh Solanki wrote:
>> >Add sysfs attribute to track regulator events received from regulator
>> >notifier block handler.
>> >
>>
>> Hi Naresh,
>>
>> Could you provide a bit more detail on how this is intended to be used?
>> Some of the details (more below) seem a bit odd to me...
>My application registers a event callback on the 'events' to track regulator
>events
>Reference:
>https://github.com/9elements/pwrseqd/blob/main/src/VoltageRegulatorSysfs.cpp#L258
>>
>> >Signed-off-by: Naresh Solanki <Naresh.Solanki@9elements.com>
>> >---
>> > drivers/regulator/userspace-consumer.c | 52 +++++++++++++++++++++++++-
>> > 1 file changed, 51 insertions(+), 1 deletion(-)
>> >
>> >diff --git a/drivers/regulator/userspace-consumer.c b/drivers/regulator/userspace-consumer.c
>> >index 97f075ed68c9..a0b980022993 100644
>> >--- a/drivers/regulator/userspace-consumer.c
>> >+++ b/drivers/regulator/userspace-consumer.c
>> >@@ -29,6 +29,10 @@ struct userspace_consumer_data {
>> >
>> >       int num_supplies;
>> >       struct regulator_bulk_data *supplies;
>> >+
>> >+      struct kobject *kobj;
>> >+      struct notifier_block nb;
>> >+      unsigned long events;
>> > };
>> >
>> > static ssize_t name_show(struct device *dev,
>> >@@ -89,12 +93,30 @@ static ssize_t state_store(struct device *dev, struct device_attribute *attr,
>> >       return count;
>> > }
>> >
>> >+static DEFINE_MUTEX(events_lock);
>> >+
>> >+static ssize_t events_show(struct device *dev,
>> >+                         struct device_attribute *attr, char *buf)
>> >+{
>> >+      struct userspace_consumer_data *data = dev_get_drvdata(dev);
>> >+      unsigned long e;
>> >+
>> >+      mutex_lock(&events_lock);
>> >+      e = data->events;
>> >+      data->events = 0;
>>
>> ...particularly this bit -- a read operation on a read-only file (and
>> especially one with 0644 permissions) having side-effects (clearing the
>> value it accesses) seems on the face of it like fairly surprising
>> behavior.  Is this a pattern that's used elsewhere in any other sysfs
>> files?
>These are regulator events & are valid when it occurs.
>Userspace application is intended to consume them as soon as the
>event is notified by kernel sysfs_notify.
>

Sure, but that doesn't really address what I was concerned about -- as 
written this is a read operation on a read-only file (0444, not 0644 as 
I mistakenly wrote above) that nevertheless alters the state of an 
internal kernel data structure.  Can you point to any other sysfs 
attributes that behave like that?  I can't think of one offhand, and I'd 
be reluctant to establish the precedent.

Would a uevent-based mechanism maybe be a better fit for the problem 
you're trying to solve?

>>
>> >+      mutex_unlock(&events_lock);
>> >+
>> >+      return sprintf(buf, "0x%lx\n", e);
>> >+}
>> >+
>> > static DEVICE_ATTR_RO(name);
>> > static DEVICE_ATTR_RW(state);
>> >+static DEVICE_ATTR_RO(events);
>>
>> New sysfs attributes should be documented in Documentation/ABI, which
>> this appears to be missing.
>Sure I can check.
>>
>> However, it looks like this would expose the values of all the
>> REGULATOR_EVENT_* constants as a userspace-visible ABI -- is that
>> something we really want to do?
>Yes.

Given that the REGULATOR_EVENT_* constants are defined in headers under 
include/linux and not include/uapi, it doesn't seem like they were 
intended to be used as part of a userspace-visible interface.  If 
they're going to be, I think they should be moved to the uapi directory 
so that applications can use the proper definitions from the kernel 
instead of manually replicating it on their own (but I suspect we should 
probably find a different approach instead).

>>
>> >
>> > static struct attribute *attributes[] = {
>> >       &dev_attr_name.attr,
>> >       &dev_attr_state.attr,
>> >+      &dev_attr_events.attr,
>> >       NULL,
>> > };
>> >
>> >@@ -115,12 +137,28 @@ static const struct attribute_group attr_group = {
>> >       .is_visible =  attr_visible,
>> > };
>> >
>> >+static int regulator_userspace_notify(struct notifier_block *nb,
>> >+                                    unsigned long event,
>> >+                                    void *ignored)
>> >+{
>> >+      struct userspace_consumer_data *data =
>> >+              container_of(nb, struct userspace_consumer_data, nb);
>> >+
>> >+      mutex_lock(&events_lock);
>> >+      data->events |= event;
>> >+      mutex_unlock(&events_lock);
>> >+
>>
>> Using a single global mutex (events_lock) to protect a single member of
>> a per-device struct looks weird.  Unless there's something subtle going
>> on that I'm not seeing, it seems like the lock should be a member of the
>> data struct instead of global, and since no blocking operations happen
>> under it could it just be a spinlock?  Or since it's just some simple
>> updates to a single variable, why not just use an atomic_t and skip the
>> lock entirely?
>Intent is that only one thread at a time is to be allowed to access/modify
>the data->events variable to prevent potential data corruption and
>race conditions. Sure can change it to spinlock or atomic_t.
>
>>
>> >+      sysfs_notify(data->kobj, NULL, dev_attr_events.attr.name);
>> >+
>> >+      return NOTIFY_OK;
>> >+}
>> >+
>> > static int regulator_userspace_consumer_probe(struct platform_device *pdev)
>> > {
>> >       struct regulator_userspace_consumer_data tmpdata;
>> >       struct regulator_userspace_consumer_data *pdata;
>> >       struct userspace_consumer_data *drvdata;
>> >-      int ret;
>> >+      int i, ret;
>> >
>> >       pdata = dev_get_platdata(&pdev->dev);
>> >       if (!pdata) {
>> >@@ -153,6 +191,7 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev)
>> >       drvdata->num_supplies = pdata->num_supplies;
>> >       drvdata->supplies = pdata->supplies;
>> >       drvdata->no_autoswitch = pdata->no_autoswitch;
>> >+      drvdata->kobj = &pdev->dev.kobj;
>> >
>> >       mutex_init(&drvdata->lock);
>> >
>> >@@ -186,6 +225,13 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev)
>> >       }
>> >       drvdata->enabled = !!ret;
>> >
>> >+      drvdata->nb.notifier_call = regulator_userspace_notify;
>> >+      for (i = 0; i < drvdata->num_supplies; i++) {
>> >+              ret = devm_regulator_register_notifier(drvdata->supplies[i].consumer, &drvdata->nb);
>> >+              if (ret)
>> >+                      goto err_enable;
>> >+      }
>> >+
>> >       return 0;
>> >
>> > err_enable:
>> >@@ -197,6 +243,10 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev)
>> > static int regulator_userspace_consumer_remove(struct platform_device *pdev)
>> > {
>> >       struct userspace_consumer_data *data = platform_get_drvdata(pdev);
>> >+      int i;
>> >+
>> >+      for (i = 0; i < data->num_supplies; i++)
>> >+              devm_regulator_unregister_notifier(data->supplies[i].consumer, &data->nb);
>> >
>> >       sysfs_remove_group(&pdev->dev.kobj, &attr_group);
>> >
>> >
>> >base-commit: 4fb53b2377c364e3753d6e293913b57dad68e98b
>> >--
>> >2.41.0
>> >
  
Naresh Solanki Aug. 11, 2023, 5:27 p.m. UTC | #5
Hi Zev,

On Fri, 4 Aug 2023 at 17:32, Zev Weiss <zev@bewilderbeest.net> wrote:
>
> On Fri, Aug 04, 2023 at 01:59:44AM PDT, Naresh Solanki wrote:
> >Hi Zev,
> >
> >
> >On Fri, 4 Aug 2023 at 02:15, Zev Weiss <zev@bewilderbeest.net> wrote:
> >>
> >> On Thu, Aug 03, 2023 at 04:12:25AM PDT, Naresh Solanki wrote:
> >> >Add sysfs attribute to track regulator events received from regulator
> >> >notifier block handler.
> >> >
> >>
> >> Hi Naresh,
> >>
> >> Could you provide a bit more detail on how this is intended to be used?
> >> Some of the details (more below) seem a bit odd to me...
> >My application registers a event callback on the 'events' to track regulator
> >events
> >Reference:
> >https://github.com/9elements/pwrseqd/blob/main/src/VoltageRegulatorSysfs.cpp#L258
> >>
> >> >Signed-off-by: Naresh Solanki <Naresh.Solanki@9elements.com>
> >> >---
> >> > drivers/regulator/userspace-consumer.c | 52 +++++++++++++++++++++++++-
> >> > 1 file changed, 51 insertions(+), 1 deletion(-)
> >> >
> >> >diff --git a/drivers/regulator/userspace-consumer.c b/drivers/regulator/userspace-consumer.c
> >> >index 97f075ed68c9..a0b980022993 100644
> >> >--- a/drivers/regulator/userspace-consumer.c
> >> >+++ b/drivers/regulator/userspace-consumer.c
> >> >@@ -29,6 +29,10 @@ struct userspace_consumer_data {
> >> >
> >> >       int num_supplies;
> >> >       struct regulator_bulk_data *supplies;
> >> >+
> >> >+      struct kobject *kobj;
> >> >+      struct notifier_block nb;
> >> >+      unsigned long events;
> >> > };
> >> >
> >> > static ssize_t name_show(struct device *dev,
> >> >@@ -89,12 +93,30 @@ static ssize_t state_store(struct device *dev, struct device_attribute *attr,
> >> >       return count;
> >> > }
> >> >
> >> >+static DEFINE_MUTEX(events_lock);
> >> >+
> >> >+static ssize_t events_show(struct device *dev,
> >> >+                         struct device_attribute *attr, char *buf)
> >> >+{
> >> >+      struct userspace_consumer_data *data = dev_get_drvdata(dev);
> >> >+      unsigned long e;
> >> >+
> >> >+      mutex_lock(&events_lock);
> >> >+      e = data->events;
> >> >+      data->events = 0;
> >>
> >> ...particularly this bit -- a read operation on a read-only file (and
> >> especially one with 0644 permissions) having side-effects (clearing the
> >> value it accesses) seems on the face of it like fairly surprising
> >> behavior.  Is this a pattern that's used elsewhere in any other sysfs
> >> files?
> >These are regulator events & are valid when it occurs.
> >Userspace application is intended to consume them as soon as the
> >event is notified by kernel sysfs_notify.
> >
>
> Sure, but that doesn't really address what I was concerned about -- as
> written this is a read operation on a read-only file (0444, not 0644 as
> I mistakenly wrote above) that nevertheless alters the state of an
> internal kernel data structure.  Can you point to any other sysfs
> attributes that behave like that?  I can't think of one offhand, and I'd
> be reluctant to establish the precedent.
I guess many hwmon properties on input are readonly & its possible to
send sysfs_notify on the properties.
Like in
https://github.com/torvalds/linux/blob/master/drivers/hwmon/hwmon.c#L668

>
> Would a uevent-based mechanism maybe be a better fit for the problem
> you're trying to solve?
If the application also needs uevent then that can be added as done in hwmon.
>
> >>
> >> >+      mutex_unlock(&events_lock);
> >> >+
> >> >+      return sprintf(buf, "0x%lx\n", e);
> >> >+}
> >> >+
> >> > static DEVICE_ATTR_RO(name);
> >> > static DEVICE_ATTR_RW(state);
> >> >+static DEVICE_ATTR_RO(events);
> >>
> >> New sysfs attributes should be documented in Documentation/ABI, which
> >> this appears to be missing.
> >Sure I can check.
For Documentation/ABI, 'sysfs-driver-regulator-output' below. let me know
if this looks ok.
What:           /sys/bus/platform/drivers/regulator-output/*/events
Date:           August 2023
Contact:        Naresh Solanki <naresh.solanki@9elements.com>
Description:    Provided regulator events.

                Read provides various events the regulator associated with the
                driver has encountered. All REGULATOR_EVENT_* are
                defined in include/linux/regulator/consumer.h

                e.g.
                cat /sys/bus/platform/drivers/regulator-output/ssb_rssd32/events
                0x0
> >>
> >> However, it looks like this would expose the values of all the
> >> REGULATOR_EVENT_* constants as a userspace-visible ABI -- is that
> >> something we really want to do?
> >Yes.
>
> Given that the REGULATOR_EVENT_* constants are defined in headers under
> include/linux and not include/uapi, it doesn't seem like they were
> intended to be used as part of a userspace-visible interface.  If
> they're going to be, I think they should be moved to the uapi directory
> so that applications can use the proper definitions from the kernel
> instead of manually replicating it on their own (but I suspect we should
> probably find a different approach instead).
Yes they have to be moved to include/uapi in that case.
>
> >>
> >> >
> >> > static struct attribute *attributes[] = {
> >> >       &dev_attr_name.attr,
> >> >       &dev_attr_state.attr,
> >> >+      &dev_attr_events.attr,
> >> >       NULL,
> >> > };
> >> >
> >> >@@ -115,12 +137,28 @@ static const struct attribute_group attr_group = {
> >> >       .is_visible =  attr_visible,
> >> > };
> >> >
> >> >+static int regulator_userspace_notify(struct notifier_block *nb,
> >> >+                                    unsigned long event,
> >> >+                                    void *ignored)
> >> >+{
> >> >+      struct userspace_consumer_data *data =
> >> >+              container_of(nb, struct userspace_consumer_data, nb);
> >> >+
> >> >+      mutex_lock(&events_lock);
> >> >+      data->events |= event;
> >> >+      mutex_unlock(&events_lock);
> >> >+
> >>
> >> Using a single global mutex (events_lock) to protect a single member of
> >> a per-device struct looks weird.  Unless there's something subtle going
> >> on that I'm not seeing, it seems like the lock should be a member of the
> >> data struct instead of global, and since no blocking operations happen
> >> under it could it just be a spinlock?  Or since it's just some simple
> >> updates to a single variable, why not just use an atomic_t and skip the
> >> lock entirely?
> >Intent is that only one thread at a time is to be allowed to access/modify
> >the data->events variable to prevent potential data corruption and
> >race conditions. Sure can change it to spinlock or atomic_t.
> >
> >>
> >> >+      sysfs_notify(data->kobj, NULL, dev_attr_events.attr.name);
> >> >+
> >> >+      return NOTIFY_OK;
> >> >+}
> >> >+
> >> > static int regulator_userspace_consumer_probe(struct platform_device *pdev)
> >> > {
> >> >       struct regulator_userspace_consumer_data tmpdata;
> >> >       struct regulator_userspace_consumer_data *pdata;
> >> >       struct userspace_consumer_data *drvdata;
> >> >-      int ret;
> >> >+      int i, ret;
> >> >
> >> >       pdata = dev_get_platdata(&pdev->dev);
> >> >       if (!pdata) {
> >> >@@ -153,6 +191,7 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev)
> >> >       drvdata->num_supplies = pdata->num_supplies;
> >> >       drvdata->supplies = pdata->supplies;
> >> >       drvdata->no_autoswitch = pdata->no_autoswitch;
> >> >+      drvdata->kobj = &pdev->dev.kobj;
> >> >
> >> >       mutex_init(&drvdata->lock);
> >> >
> >> >@@ -186,6 +225,13 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev)
> >> >       }
> >> >       drvdata->enabled = !!ret;
> >> >
> >> >+      drvdata->nb.notifier_call = regulator_userspace_notify;
> >> >+      for (i = 0; i < drvdata->num_supplies; i++) {
> >> >+              ret = devm_regulator_register_notifier(drvdata->supplies[i].consumer, &drvdata->nb);
> >> >+              if (ret)
> >> >+                      goto err_enable;
> >> >+      }
> >> >+
> >> >       return 0;
> >> >
> >> > err_enable:
> >> >@@ -197,6 +243,10 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev)
> >> > static int regulator_userspace_consumer_remove(struct platform_device *pdev)
> >> > {
> >> >       struct userspace_consumer_data *data = platform_get_drvdata(pdev);
> >> >+      int i;
> >> >+
> >> >+      for (i = 0; i < data->num_supplies; i++)
> >> >+              devm_regulator_unregister_notifier(data->supplies[i].consumer, &data->nb);
> >> >
> >> >       sysfs_remove_group(&pdev->dev.kobj, &attr_group);
> >> >
> >> >
> >> >base-commit: 4fb53b2377c364e3753d6e293913b57dad68e98b
> >> >--
> >> >2.41.0
> >> >
  

Patch

diff --git a/drivers/regulator/userspace-consumer.c b/drivers/regulator/userspace-consumer.c
index 97f075ed68c9..a0b980022993 100644
--- a/drivers/regulator/userspace-consumer.c
+++ b/drivers/regulator/userspace-consumer.c
@@ -29,6 +29,10 @@  struct userspace_consumer_data {
 
 	int num_supplies;
 	struct regulator_bulk_data *supplies;
+
+	struct kobject *kobj;
+	struct notifier_block nb;
+	unsigned long events;
 };
 
 static ssize_t name_show(struct device *dev,
@@ -89,12 +93,30 @@  static ssize_t state_store(struct device *dev, struct device_attribute *attr,
 	return count;
 }
 
+static DEFINE_MUTEX(events_lock);
+
+static ssize_t events_show(struct device *dev,
+			   struct device_attribute *attr, char *buf)
+{
+	struct userspace_consumer_data *data = dev_get_drvdata(dev);
+	unsigned long e;
+
+	mutex_lock(&events_lock);
+	e = data->events;
+	data->events = 0;
+	mutex_unlock(&events_lock);
+
+	return sprintf(buf, "0x%lx\n", e);
+}
+
 static DEVICE_ATTR_RO(name);
 static DEVICE_ATTR_RW(state);
+static DEVICE_ATTR_RO(events);
 
 static struct attribute *attributes[] = {
 	&dev_attr_name.attr,
 	&dev_attr_state.attr,
+	&dev_attr_events.attr,
 	NULL,
 };
 
@@ -115,12 +137,28 @@  static const struct attribute_group attr_group = {
 	.is_visible =  attr_visible,
 };
 
+static int regulator_userspace_notify(struct notifier_block *nb,
+				      unsigned long event,
+				      void *ignored)
+{
+	struct userspace_consumer_data *data =
+		container_of(nb, struct userspace_consumer_data, nb);
+
+	mutex_lock(&events_lock);
+	data->events |= event;
+	mutex_unlock(&events_lock);
+
+	sysfs_notify(data->kobj, NULL, dev_attr_events.attr.name);
+
+	return NOTIFY_OK;
+}
+
 static int regulator_userspace_consumer_probe(struct platform_device *pdev)
 {
 	struct regulator_userspace_consumer_data tmpdata;
 	struct regulator_userspace_consumer_data *pdata;
 	struct userspace_consumer_data *drvdata;
-	int ret;
+	int i, ret;
 
 	pdata = dev_get_platdata(&pdev->dev);
 	if (!pdata) {
@@ -153,6 +191,7 @@  static int regulator_userspace_consumer_probe(struct platform_device *pdev)
 	drvdata->num_supplies = pdata->num_supplies;
 	drvdata->supplies = pdata->supplies;
 	drvdata->no_autoswitch = pdata->no_autoswitch;
+	drvdata->kobj = &pdev->dev.kobj;
 
 	mutex_init(&drvdata->lock);
 
@@ -186,6 +225,13 @@  static int regulator_userspace_consumer_probe(struct platform_device *pdev)
 	}
 	drvdata->enabled = !!ret;
 
+	drvdata->nb.notifier_call = regulator_userspace_notify;
+	for (i = 0; i < drvdata->num_supplies; i++) {
+		ret = devm_regulator_register_notifier(drvdata->supplies[i].consumer, &drvdata->nb);
+		if (ret)
+			goto err_enable;
+	}
+
 	return 0;
 
 err_enable:
@@ -197,6 +243,10 @@  static int regulator_userspace_consumer_probe(struct platform_device *pdev)
 static int regulator_userspace_consumer_remove(struct platform_device *pdev)
 {
 	struct userspace_consumer_data *data = platform_get_drvdata(pdev);
+	int i;
+
+	for (i = 0; i < data->num_supplies; i++)
+		devm_regulator_unregister_notifier(data->supplies[i].consumer, &data->nb);
 
 	sysfs_remove_group(&pdev->dev.kobj, &attr_group);