From: Andy Shevchenko <andy.shevchenko@gmail.com>
To: Bartosz Golaszewski <brgl@bgdev.pl>
Cc: Andy Shevchenko <andy.shevchenko@gmail.com>,
Kent Gibson <warthog618@gmail.com>,
Linus Walleij <linus.walleij@linaro.org>,
linux-gpio@vger.kernel.org, linux-kernel@vger.kernel.org,
Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Subject: Re: [PATCH v7] gpio: virtuser: new virtual driver
Date: Mon, 10 Jun 2024 17:57:49 +0300 [thread overview]
Message-ID: <ZmcUbe1aQfezZy5B@surfacebook.localdomain> (raw)
In-Reply-To: <CAMRc=MftW0y7GicBy4vwABomUYuMndsJBUTdsQzZijDtgX1ohQ@mail.gmail.com>
Mon, Jun 10, 2024 at 03:22:32PM +0200, Bartosz Golaszewski kirjoitti:
> On Wed, May 29, 2024 at 11:00 PM Andy Shevchenko
> <andy.shevchenko@gmail.com> wrote:
> > Mon, May 27, 2024 at 04:40:54PM +0200, Bartosz Golaszewski kirjoitti:
...
> > > User must pass exactly the number of values that the array contains
> >
> > Can't we assume non-active values for the rest if less than needed were
> > provided? For more than that, why do we care?
>
> Honestly, what good would it do? It would just be more confusing IMO.
Let's say you can leave documentation as is, but relax the code. That's the
benefit, less complex checks in the code.
...
> > > +#include <linux/atomic.h>
> > > +#include <linux/bitmap.h>
> > > +#include <linux/cleanup.h>
> > > +#include <linux/completion.h>
> > > +#include <linux/configfs.h>
> > > +#include <linux/device.h>
> > > +#include <linux/err.h>
> > > +#include <linux/gpio/consumer.h>
> > > +#include <linux/gpio/driver.h>
> > > +#include <linux/gpio/machine.h>
> >
> > > +#include <linux/idr.h>
> >
> > > +#include <linux/interrupt.h>
> > > +#include <linux/irq_work.h>
> >
> > > +#include <linux/kernel.h>
> >
> > Do you need this?
>
> ARRAY_SIZE() used to live here when I first wrote this but it was
> since moved. I'll drop this.
Rather replace with array_size.h.
> > > +#include <linux/limits.h>
> > > +#include <linux/list.h>
> > > +#include <linux/lockdep.h>
> > > +#include <linux/mod_devicetable.h>
> > > +#include <linux/module.h>
> > > +#include <linux/mutex.h>
> > > +#include <linux/notifier.h>
> > > +#include <linux/of.h>
> > > +#include <linux/overflow.h>
> > > +#include <linux/platform_device.h>
> > > +#include <linux/printk.h>
> > > +#include <linux/property.h>
> > > +#include <linux/slab.h>
> >
> > > +#include <linux/string.h>
> >
> > Implied by string_helpers.h
>
> Yeah, but we still use symbols directly from string.h, we shouldn't
> depend on implicit includes.
string_helpers.h is and will continue guranteening inclusion of string.h.
It's the same as we drop bits.h when we include, for instance, bitmap.h.
> > > +#include <linux/string_helpers.h>
> > > +#include <linux/sysfs.h>
> > > +#include <linux/types.h>
...
> > > +struct gpio_virtuser_attr_descr {
> > > + const char *name;
> > > + ssize_t (*show)(struct device *, struct device_attribute *, char *);
> > > + ssize_t (*store)(struct device *, struct device_attribute *,
> > > + const char *, size_t);
> > > +};
> >
> > struct device_attribute ? (Yes, I know that that one is a bit bigger but
> > benefit is that we have some code that you may reuse)
>
> Not sure what you mean here, these are callbacks for sysfs.
I mean to replace your custom data type with the existing device_attribute.
...
> > > +static ssize_t gpio_virtuser_sysfs_emit_value_array(char *buf,
> > > + unsigned long *values,
> > > + size_t num_values)
> > > +{
> > > + ssize_t len = 0;
> > > + size_t i;
> > > +
> > > + for (i = 0; i < num_values; i++)
> > > + len += sysfs_emit_at(buf, len, "%d",
> > > + test_bit(i, values) ? 1 : 0);
> > > + return len + sysfs_emit_at(buf, len, "\n");
> >
> > Why not use %pb?
>
> Because it outputs hex? I want to output binary, can I do it?
But why do you need that? You can also print a list of numbers of bits that
set (%pbl).
We have a few ABIs in the kernel that works nice and people are familiar with
(CPU sets, IRQ affinity masks, etc). Why to reinvent the wheel?
> > > +}
...
> > > +static int gpio_virtuser_sysfs_parse_value_array(const char *buf, size_t len,
> > > + unsigned long *values)
> > > +{
> > > + size_t i;
> > > +
> > > + for (i = 0; i < len; i++) {
> >
> > Perhaps
> >
> > bool val;
> > int ret;
> >
> > ret = kstrtobool(...);
>
> kstrtobool() accepts values we don't want here like [Tt]rue and [Ff]alse.
Yes, see below.
> > if (ret)
> > return ret;
> >
> > assign_bit(...); // btw, why atomic?
> >
> > > + if (buf[i] == '0')
> > > + clear_bit(i, values);
> > > + else if (buf[i] == '1')
> > > + set_bit(i, values);
> > > + else
> > > + return -EINVAL;
> >
> > > + }
> >
> > BUT, why not bitmap_parse()?
>
> Because it parses hex, not binary.
So, why do we reinvent a wheel? Wouldn't be better that users may apply the
knowledge they familiar with (and I believe the group of the users who know
about bitmaps is much bigger than those who will use this driver).
> > > + return 0;
> > > +}
...
> > > + return sysfs_emit(buf, "%s\n",
> > > + dir == GPIO_LINE_DIRECTION_IN ? "input" : "output");
> >
> > I think this maybe transformed to something like str_input_output() in
> > string_choices.h (and you don't even need to include that as it's implied by
> > string_helpers.h)
>
> These helpers take bool as argument. Hard to tell whether input or
> output should correspond to true. I'd leave it as is.
There is a convention: str_TRUE_FALSE().
...
> > > +static int gpio_virtuser_parse_direction(const char *buf, int *dir, int *val)
> > > +{
> > > + if (sysfs_streq(buf, "input")) {
> > > + *dir = GPIO_LINE_DIRECTION_IN;
> > > + return 0;
> > > + }
> > > +
> > > + if (sysfs_streq(buf, "output-high"))
> > > + *val = 1;
> > > + else if (sysfs_streq(buf, "output-low"))
> > > + *val = 0;
> > > + else
> > > + return -EINVAL;
> > > +
> > > + *dir = GPIO_LINE_DIRECTION_OUT;
> >
> > This can be transformed to use sysfs_match_string() with
> >
> > static const char * const dirs[] = { "output-low", "output-high", "input" };
> >
> > int ret;
> >
> > ret = sysfs_match_string(...);
> > if (ret < 0)
> > return ret;
> >
> > *val = ret;
> > *dir = ret == 2 ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT;
> >
> > And with this approach it even not clear why do you need dir and val to be
> > separated here (esp. if we add a enum like
>
> We do want them to be separated not for better UX but to be able to
> test all kernel APIs (gpiod_direction_input|output() and
> gpiod_set_value()).
Still you can do some optimisations I proposed above.
> > GPIO_VIRTUSER_OUT_LOW,
> > GPIO_VIRTUSER_OUT_HIGH,
> > GPIO_VIRTUSER_IN,
> >
> > (with it the string array can also be indexed).
> >
> > > + return 0;
> > > +}
...
> > > +static int gpio_virtuser_parse_value(const char *buf)
> > > +{
> > > + int value, ret;
> > > +
> > > + value = sysfs_match_string(gpio_virtuser_sysfs_value_strings, buf);
> > > + if (value < 0) {
> > > + /* Can be 0 or 1 too. */
> > > + ret = kstrtoint(buf, 0, &value);
> > > + if (ret)
> > > + return ret;
> >
> > > + if (value != 0 && value != 1)
> > > + return -EINVAL;
> >
> > Why not kstrtobool()?
>
> I don't want to accept all the other strings kstrtobool() is fine with.
What's wrong with other strings?
At bare minumum you can reduce the range by using kstrtou8().
> > > + }
> > > +
> > > + return value;
> > > +}
...
> > > + ret = kstrtouint(buf, 10, &debounce);
> >
> > Why restrict to decimal?
>
> Not sure what you gain from passing a period in hex?
For example, if I compare this to the real HW, I might be able to do something
like 0x1234 (let's say it's debounce step) and shifting it by 4 bits will give
me something I want. But despite that quite unlikely case the restriction here
doesn't bring us much.
> > > + if (ret)
> > > + return ret;
...
> > > + return dash && strcmp(dash, "-gpios") == 0;
> >
> > Can't we reuse the suffix from the array from the gpiolib internal header?
> > Also I don't like the form of '-' in the line. "gpios" is good and chance
> > that linker deduplicates the same string if it occurs somewhere else in the
> > binary (in case this goes with =y in .config).
>
> I'm not sure I follow what you're saying here. Please rephrase.
Do strcmp() against one from the gpio_suffixes array.
...
> > > +/*
> > > + * If this is an OF-based system, then we iterate over properties and consider
> > > + * all whose names end in "-gpios". For configfs we expect an additional string
> > > + * array property - "gpio-virtuser,ids" - containing the list of all GPIO IDs
> > > + * to request.
> >
> > Why not any other system? What's wrong for having this available for ACPI, for
> > example? Okay, I see that this is probably due to absence of API.
> >
> > OTOH the last call in the function assumes non-OF cases. Why can't we have the
> > same approach in both?
>
> Again: I have no idea what you mean. We support device-tree and
> configfs as sources of configuration for these virtual consumers. If
> you want to add something more, be my guest once it's upstream.
>
> The reason to use a different approach is to not require the
> "gpio-virtuser,ids" property in device-tree.
Yes, and I'm asking why can't we unify and require it there as well?
But okay, I might give up on the trying of the DT/ACPI property unification.
> > > + */
...
> > > + if (gpio_virtuser_prop_is_gpio(prop))
> > > + ++ret;
> >
> > Why pre-increment?
>
> Why not?
Because we have a pattern. Pre-increment adds into additional questioning
"why?". I.e. What does make this case special? When I read such a code I need
more brain power to parse it.
...
> > > + dash = strpbrk(prop->name, "-");
Btw, don't you want strrchr() here? (Note 'r' variant).
> > > + diff = dash - prop->name;
> > > +
> > > + tmp = devm_kmemdup(dev, prop->name, diff + 1,
> > > + GFP_KERNEL);
> >
> > devm_kstrndup() is not okay? Okay, we don't have it (yet?), but at least I
> > would rather expect wrapped kstrndup() than this.
>
> Meh, this logic is fine as we know the range exactly. IMO kstrndup()
> here would be overkill. I'd leave it for now.
>
> > > + if (!tmp)
> > > + return -ENOMEM;
> > > + tmp[diff] = '\0';
This line will gone with kstrndup(). I think we will benefit from it.
...
> > > + int i = 0;
> >
> > Why signed? And in all this kind of case, I would split assignment...
(1)
> > > + memset(properties, 0, sizeof(properties));
> > > +
> > > + num_ids = list_count_nodes(&dev->lookup_list);
> > > + char **ids __free(kfree) = kcalloc(num_ids + 1, sizeof(*ids),
> > > + GFP_KERNEL);
> > > + if (!ids)
> > > + return ERR_PTR(-ENOMEM);
> > > +
> >
> > To be here, that the reader will see immediately (close enough) what is the
> > initial values. Moreover this code will be robuse against changes in between
> > (if i become reusable).
>
> Sorry, I can't parse it.
I meant to see here
i = 0;
instead of the above (1).
> > > + list_for_each_entry(lookup, &dev->lookup_list, siblings)
> > > + ids[i++] = lookup->con_id;
--
With Best Regards,
Andy Shevchenko
next prev parent reply other threads:[~2024-06-10 14:57 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-05-27 14:40 [PATCH v7] gpio: virtuser: new virtual driver Bartosz Golaszewski
2024-05-29 14:32 ` Dan Carpenter
2024-05-29 21:00 ` Andy Shevchenko
2024-06-10 13:22 ` Bartosz Golaszewski
2024-06-10 14:57 ` Andy Shevchenko [this message]
2024-06-12 18:38 ` Bartosz Golaszewski
2024-06-13 18:03 ` Andy Shevchenko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ZmcUbe1aQfezZy5B@surfacebook.localdomain \
--to=andy.shevchenko@gmail.com \
--cc=bartosz.golaszewski@linaro.org \
--cc=brgl@bgdev.pl \
--cc=linus.walleij@linaro.org \
--cc=linux-gpio@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=warthog618@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).