[PATCH v4 000/115] pwm: Fix lifetime issues for pwm_chips

Thierry Reding thierry.reding at gmail.com
Mon Dec 11 03:33:04 PST 2023


On Fri, Dec 08, 2023 at 07:50:33PM +0100, Uwe Kleine-König wrote:
> Hello Thierry,
> 
> On Fri, Dec 08, 2023 at 04:41:39PM +0100, Thierry Reding wrote:
> > On Wed, Dec 06, 2023 at 12:43:14PM +0100, Uwe Kleine-König wrote:
> > > This series is based on Thierry's for-next.
> > > 
> > > It starts with some cleanups that were all sent out separately already:
> > > 
> > >  - "pwm: Reduce number of pointer dereferences in pwm_device_request()"
> > >    https://lore.kernel.org/linux-pwm/20231130072105.966848-1-u.kleine-koenig@pengutronix.de
> > >  - "pwm: crc: Use consistent variable naming for driver data"
> > >    https://lore.kernel.org/linux-pwm/20231130074133.969806-1-u.kleine-koenig@pengutronix.de
> > >  - Two leds/qcom-lpg patches
> > >    https://lore.kernel.org/linux-leds/20231126095230.683204-1-u.kleine-koenig@pengutronix.de
> > >    Lee already claimed to have taken the series already, but it's not yet in
> > >    next.
> > >  - "leds: qcom-lpg: Introduce a wrapper for getting driver data from a pwm chip"
> > >    https://lore.kernel.org/linux-leds/20231124215208.616551-3-u.kleine-koenig@pengutronix.de
> > > 
> > > In the following patches I changed:
> > > 
> > >  - "pwm: cros-ec: Change prototype of helper to prepare further changes" +
> > >    This was simplified in response to feedback by Tzung-Bi Shih
> > >  - Make pwmchip_priv() static (and don't export it), let drivers use a new
> > >    pwmchip_get_drvdata() instead.
> > >  - For drm/ti-sn65dsi86.c and leds/qcom-lpg make use of
> > >    pwmchip_set_drvdata() which makes the conversion to
> > >    devm_pwmchip_alloc() much prettier.
> > >  - Some cleanups here and there
> > >  - Add review tags received in v3
> > >    I kept all tags even though the pwmchip_alloc() patches changed
> > >    slightly. Most of the time that's only
> > >    s/pwmchip_priv/pwmchip_get_drvdata/ though. Still, if you object,
> > >    just tell me. (This affects Paul Cercueil on patch #68, Conor Dooley
> > >    on patch #76 and Greg for patch #109.)
> > > 
> > > I kept the pwmchip_alloc() + pwmchip_register() approach despite Bart
> > > not liking it. To balance that out I don't like Bart's alternative
> > > approach. There are no technically relevant differences between the two
> > > approaches and no benchmarks that show either of the two to be better
> > > than the other. Conceptually the design ideas around pwmchip_alloc() +
> > > pwmchip_register() are used in several other subsystems, so it's a
> > > proven way to do things.
> > 
> > [Trimming the recipients, keeping only Bart and the mailing lists.]
> > 
> > I do think there are technically relevant differences. For one, the
> > better we isolate the internal data structure, the easier this becomes
> > to manage. I'm attaching a patch that I've prototyped which should
> > basically get us to somewhere around patch 110 of this series but with
> > something like 1/8th of the changes. It doesn't need every driver to
> > change and (mostly) decouples driver code from the core code.
> 
> You don't need to touch all drivers because you didn't change struct
> pwm_chip::dev yet. (If you really want, you don't need to change that,
> but then you have some duplication as chip->dev holds the same value as
> priv->dev.parent in the end.)

I don't think that's a problem. These are for two logically separate
things, after all. Duplication can also sometimes be useful to simplify
things. There are plently of cases where we use local variables for the
same reason.

> > Now, I know that you think this is all bad because it's not a single
> > allocation, but I much prefer the end result because it's got the driver
> > and internals much more cleanly separated. Going forward I think it
> > would be easier to apply all the ref-counting on top of that because we
> > only need to keep the PWM framework-internal data structure alive after
> > a PWM chip has gone away.
> 
> Nearly all drivers need driver private data. Isn't it a nice service by
> the pwm core to make handling this private data easier for the lowlevel
> drivers?

That's only true if you think pwmchip_alloc() makes it easier. I happen
to think that it doesn't. On the contrary, I think having drivers pass
in a PWM chip descriptor that can be embedded in driver-private data is
much easier.

> > From 72ea79887d96850f9ccc832ce52b907ca276c940 Mon Sep 17 00:00:00 2001
> > From: Thierry Reding <thierry.reding at gmail.com>
> > Date: Tue, 28 Nov 2023 15:42:39 +0100
> > Subject: [PATCH] pwm: Isolate internal data into a separate structure
> > 
> > In order to prepare for proper reference counting of PWM chips and PWM
> > devices, move the internal data from the public PWM chip to a private
> > PWM chip structure. This ensures that the data that the subsystem core
> > may need to reference later on can stick around beyond the lifetime of
> > the driver-private data.
> > 
> > Signed-off-by: Thierry Reding <thierry.reding at gmail.com>
> > ---
> >  drivers/pwm/core.c            | 185 +++++++++++++++++++++-------------
> >  drivers/pwm/internal.h        |  38 +++++++
> >  drivers/pwm/pwm-atmel-hlcdc.c |   8 +-
> >  drivers/pwm/pwm-fsl-ftm.c     |   6 +-
> >  drivers/pwm/pwm-lpc18xx-sct.c |   4 +-
> >  drivers/pwm/pwm-lpss.c        |  14 +--
> >  drivers/pwm/pwm-pca9685.c     |   6 +-
> >  drivers/pwm/pwm-samsung.c     |   6 +-
> >  drivers/pwm/pwm-sifive.c      |   4 +-
> >  drivers/pwm/pwm-stm32-lp.c    |   6 +-
> >  drivers/pwm/pwm-stm32.c       |   6 +-
> >  drivers/pwm/pwm-tiecap.c      |   6 +-
> >  drivers/pwm/pwm-tiehrpwm.c    |   6 +-
> >  drivers/pwm/sysfs.c           |  48 ++++-----
> >  include/linux/pwm.h           |  26 +----
> >  15 files changed, 228 insertions(+), 141 deletions(-)
> >  create mode 100644 drivers/pwm/internal.h
> > 
> > diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
> > index f60b715abe62..54d57dec6dce 100644
> > --- a/drivers/pwm/core.c
> > +++ b/drivers/pwm/core.c
> > @@ -24,17 +24,19 @@
> >  #define CREATE_TRACE_POINTS
> >  #include <trace/events/pwm.h>
> >  
> > +#include "internal.h"
> > +
> >  static DEFINE_MUTEX(pwm_lookup_lock);
> >  static LIST_HEAD(pwm_lookup_list);
> >  
> > -/* protects access to pwmchip_idr */
> > +/* protects access to pwm_chips */
> >  static DEFINE_MUTEX(pwm_lock);
> >  
> > -static DEFINE_IDR(pwmchip_idr);
> > +static DEFINE_IDR(pwm_chips);
> 
> That's an unrelated change.

Yeah, it should probably go into the patch that changes the list to an
IDR. There's really no reason why this needed to change in the first
place.

> 
> >  static struct pwm_chip *pwmchip_find_by_name(const char *name)
> >  {
> > -	struct pwm_chip *chip;
> > +	struct pwm_chip_private *priv;
> >  	unsigned long id, tmp;
> >  
> >  	if (!name)
> > @@ -42,12 +44,12 @@ static struct pwm_chip *pwmchip_find_by_name(const char *name)
> >  
> >  	mutex_lock(&pwm_lock);
> >  
> > -	idr_for_each_entry_ul(&pwmchip_idr, chip, tmp, id) {
> > -		const char *chip_name = dev_name(chip->dev);
> > +	idr_for_each_entry_ul(&pwm_chips, priv, tmp, id) {
> > +		const char *chip_name = dev_name(priv->chip->dev);
> 
> two pointer dereferences instead of one before.
> 
> >  		if (chip_name && strcmp(chip_name, name) == 0) {
> >  			mutex_unlock(&pwm_lock);
> > -			return chip;
> > +			return priv->chip;
> 
> one pointer reference instead of none before.
> 
> >  		}
> >  	}
> >  
> > @@ -58,23 +60,24 @@ static struct pwm_chip *pwmchip_find_by_name(const char *name)
> >  
> >  static int pwm_device_request(struct pwm_device *pwm, const char *label)
> >  {
> > +	struct pwm_chip *chip = pwm->priv->chip;
> 
> With my approach getting the chip of a struct pwm_device is only one
> pointer dereference away. You need two.

None of the functions here are called very often, so even if this isn't
optimized away it would hardly matter.

> >  	int err;
> >  
> >  	if (test_bit(PWMF_REQUESTED, &pwm->flags))
> >  		return -EBUSY;
> >  
> > -	if (!try_module_get(pwm->chip->owner))
> > +	if (!try_module_get(pwm->priv->owner))
> >  		return -ENODEV;
> >  
> > -	if (pwm->chip->ops->request) {
> > -		err = pwm->chip->ops->request(pwm->chip, pwm);
> > +	if (chip->ops->request) {
> > +		err = chip->ops->request(chip, pwm);
> 
> You seem to have reimplemented half of
> https://lore.kernel.org/linux-pwm/20231130072105.966848-1-u.kleine-koenig@pengutronix.de
> here.

I started prototyping this before you sent out that patch.

> >  		if (err) {
> > -			module_put(pwm->chip->owner);
> > +			module_put(pwm->priv->owner);
> >  			return err;
> >  		}
> >  	}
> >  
> > -	if (pwm->chip->ops->get_state) {
> > +	if (chip->ops->get_state) {
> >  		/*
> >  		 * Zero-initialize state because most drivers are unaware of
> >  		 * .usage_power. The other members of state are supposed to be
> > @@ -84,7 +87,7 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label)
> >  		 */
> >  		struct pwm_state state = { 0, };
> >  
> > -		err = pwm->chip->ops->get_state(pwm->chip, pwm, &state);
> > +		err = chip->ops->get_state(chip, pwm, &state);
> >  		trace_pwm_get(pwm, &state, err);
> >  
> >  		if (!err)
> > @@ -196,6 +199,64 @@ static bool pwm_ops_check(const struct pwm_chip *chip)
> >  	return true;
> >  }
> >  
> > +static struct pwm_chip_private *pwmchip_alloc(struct pwm_chip *chip,
> > +					      struct module *owner)
> > +{
> > +	struct pwm_chip_private *priv;
> > +	struct pwm_device *pwm;
> > +	unsigned int i;
> > +	int err;
> > +
> > +	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> > +	if (!priv)
> > +		return ERR_PTR(-ENOMEM);
> > +
> > +	priv->pwms = kcalloc(chip->npwm, sizeof(*pwm), GFP_KERNEL);
> > +	if (!priv->pwms) {
> > +		err = -ENOMEM;
> > +		goto free;
> > +	}
> 
> Oh, so you don't use one additional allocation but two.

This could easily be folded into one, but we already determined that we
both have different opinions about whether this matters a lot or not.

> 
> > +	priv->owner = owner;
> > +	priv->chip = chip;
> > +
> > +	for (i = 0; i < chip->npwm; i++) {
> > +		struct pwm_device *pwm = &priv->pwms[i];
> > +
> > +		pwm->priv = priv;
> > +		pwm->hwpwm = i;
> > +	}
> > +
> > +	mutex_lock(&pwm_lock);
> > +
> > +	err = idr_alloc(&pwm_chips, priv, 0, 0, GFP_KERNEL);
> > +	if (err < 0) {
> > +		mutex_unlock(&pwm_lock);
> > +		goto free;
> > +	}
> > +
> > +	mutex_unlock(&pwm_lock);
> > +
> > +	priv->id = err;
> > +
> > +	return priv;
> > +
> > +free:
> > +	kfree(priv->pwms);
> > +	kfree(priv);
> > +	return ERR_PTR(err);
> > +}
> > +
> > +static void pwmchip_free(struct pwm_chip_private *priv)
> > +{
> > +	mutex_lock(&pwm_lock);
> > +	idr_remove(&pwm_chips, priv->id);
> > +	mutex_unlock(&pwm_lock);
> > +
> > +	kfree(priv->pwms);
> > +	kfree(priv);
> > +}
> > +
> >  /**
> >   * __pwmchip_add() - register a new PWM chip
> >   * @chip: the PWM chip to add
> > @@ -208,8 +269,7 @@ static bool pwm_ops_check(const struct pwm_chip *chip)
> >   */
> >  int __pwmchip_add(struct pwm_chip *chip, struct module *owner)
> >  {
> > -	unsigned int i;
> > -	int ret;
> > +	struct pwm_chip_private *priv;
> >  
> >  	if (!chip || !chip->dev || !chip->ops || !chip->npwm)
> >  		return -EINVAL;
> > @@ -217,36 +277,16 @@ int __pwmchip_add(struct pwm_chip *chip, struct module *owner)
> >  	if (!pwm_ops_check(chip))
> >  		return -EINVAL;
> >  
> > -	chip->owner = owner;
> > -
> > -	chip->pwms = kcalloc(chip->npwm, sizeof(*chip->pwms), GFP_KERNEL);
> > -	if (!chip->pwms)
> > +	priv = pwmchip_alloc(chip, owner);
> > +	if (!priv)
> >  		return -ENOMEM;
> >  
> > -	mutex_lock(&pwm_lock);
> > -
> > -	ret = idr_alloc(&pwmchip_idr, chip, 0, 0, GFP_KERNEL);
> > -	if (ret < 0) {
> > -		mutex_unlock(&pwm_lock);
> > -		kfree(chip->pwms);
> > -		return ret;
> > -	}
> > -
> > -	chip->id = ret;
> > -
> > -	for (i = 0; i < chip->npwm; i++) {
> > -		struct pwm_device *pwm = &chip->pwms[i];
> > -
> > -		pwm->chip = chip;
> > -		pwm->hwpwm = i;
> > -	}
> > -
> > -	mutex_unlock(&pwm_lock);
> > +	chip->priv = priv;
> >  
> >  	if (IS_ENABLED(CONFIG_OF))
> >  		of_pwmchip_add(chip);
> >  
> > -	pwmchip_sysfs_export(chip);
> > +	pwmchip_sysfs_export(priv);
> >  
> >  	return 0;
> >  }
> > @@ -260,18 +300,14 @@ EXPORT_SYMBOL_GPL(__pwmchip_add);
> >   */
> >  void pwmchip_remove(struct pwm_chip *chip)
> >  {
> > -	pwmchip_sysfs_unexport(chip);
> > -
> > -	if (IS_ENABLED(CONFIG_OF))
> > -		of_pwmchip_remove(chip);
> > -
> >  	mutex_lock(&pwm_lock);
> >  
> > -	idr_remove(&pwmchip_idr, chip->id);
> > +	pwmchip_sysfs_unexport(chip->priv);
> >  
> > -	mutex_unlock(&pwm_lock);
> > +	if (IS_ENABLED(CONFIG_OF))
> > +		of_pwmchip_remove(chip);
> >  
> > -	kfree(chip->pwms);
> > +	pwmchip_free(chip->priv);
> >  }
> >  EXPORT_SYMBOL_GPL(pwmchip_remove);
> >  
> > @@ -315,7 +351,7 @@ struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
> >  		return ERR_PTR(-EINVAL);
> >  
> >  	mutex_lock(&pwm_lock);
> > -	pwm = &chip->pwms[index];
> > +	pwm = &chip->priv->pwms[index];
> 
> two pointer dereferences instead of one before.
> 
> >  	err = pwm_device_request(pwm, label);
> >  	if (err < 0)
> > @@ -329,9 +365,9 @@ EXPORT_SYMBOL_GPL(pwm_request_from_chip);
> >  static void pwm_apply_state_debug(struct pwm_device *pwm,
> >  				  const struct pwm_state *state)
> >  {
> > -	struct pwm_state *last = &pwm->last;
> > -	struct pwm_chip *chip = pwm->chip;
> > +	struct pwm_chip *chip = pwm->priv->chip;
> >  	struct pwm_state s1 = { 0 }, s2 = { 0 };
> > +	struct pwm_state *last = &pwm->last;
> >  	int err;
> >  
> >  	if (!IS_ENABLED(CONFIG_PWM_DEBUG))
> > @@ -439,7 +475,6 @@ static void pwm_apply_state_debug(struct pwm_device *pwm,
> >   */
> >  int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state)
> >  {
> > -	struct pwm_chip *chip;
> >  	int err;
> >  
> >  	/*
> > @@ -455,8 +490,6 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state)
> >  	    state->duty_cycle > state->period)
> >  		return -EINVAL;
> >  
> > -	chip = pwm->chip;
> > -
> >  	if (state->period == pwm->state.period &&
> >  	    state->duty_cycle == pwm->state.duty_cycle &&
> >  	    state->polarity == pwm->state.polarity &&
> > @@ -464,7 +497,7 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state)
> >  	    state->usage_power == pwm->state.usage_power)
> >  		return 0;
> >  
> > -	err = chip->ops->apply(chip, pwm, state);
> > +	err = pwm->priv->chip->ops->apply(pwm->priv->chip, pwm, state);
> 
> four pointer dereferences instead of two before.
> 
> >  	trace_pwm_apply(pwm, state, err);
> >  	if (err)
> >  		return err;
> > @@ -492,16 +525,19 @@ EXPORT_SYMBOL_GPL(pwm_apply_state);
> >  int pwm_capture(struct pwm_device *pwm, struct pwm_capture *result,
> >  		unsigned long timeout)
> >  {
> > +	struct pwm_chip *chip;
> >  	int err;
> >  
> > -	if (!pwm || !pwm->chip->ops)
> > +	if (!pwm)
> >  		return -EINVAL;
> >  
> > -	if (!pwm->chip->ops->capture)
> > +	chip = pwm->priv->chip;
> 
> One pointer deference more than before (assuming sensible compilation).
> 
> > +	if (!chip || !chip->ops || !chip->ops->capture)
> >  		return -ENOSYS;
> 
> Changing the return code for !chip->ops isn't intended, is it?

It's a quick prototype, I didn't pay particular attention to keeping all
error codes exactly as they were before.

> > diff --git a/drivers/pwm/internal.h b/drivers/pwm/internal.h
> > new file mode 100644
> > index 000000000000..44fffd3b6744
> > --- /dev/null
> > +++ b/drivers/pwm/internal.h
> > @@ -0,0 +1,38 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +
> > +#ifndef PWM_INTERNAL_H
> > +#define PWM_INTERNAL_H
> > +
> > +#include <linux/list.h>
> 
> This is unused?

Yes, can be dropped now. This was prototyped prior to the IDR rework and
I forgot to drop this after rebasing it on top.

> > +struct pwm_chip;
> > +struct pwm_device;
> > +
> > +/**
> > + * struct pwm_chip_private - subsystem-private PWM chip data
> > + * @chip: driver-private PWM chip data
> > + * @owner: module providing the chip
> > + * @pwms: array of PWM devices allocated by the framework
> > + * @base: number of first PWM controlled by this chip
> > + */
> > +struct pwm_chip_private {
> > +	struct pwm_chip *chip;
> > +	struct module *owner;
> > +	struct pwm_device *pwms;
> > +	unsigned int id;
> > +};
> 
> With my approach the struct pwm_chip * pointer isn't needed because it's
> all in one structure. So you have two structures where I only have one
> and your two are bigger in sum than my single one (not counting memory
> management overhead and alignment).

The split is on purpose and has the benefit of clearly separating the
PWM internal data structures from the driver accessible ones. So in my
book this is an advantage.

> > +#ifdef CONFIG_PWM_SYSFS
> > +void pwmchip_sysfs_export(struct pwm_chip_private *priv);
> > +void pwmchip_sysfs_unexport(struct pwm_chip_private *priv);
> > +#else
> > +static inline void pwmchip_sysfs_export(struct pwm_chip_private *priv)
> > +{
> > +}
> > +
> > +static inline void pwmchip_sysfs_unexport(struct pwm_chip_private *priv)
> > +{
> > +}
> > +#endif /* CONFIG_PWM_SYSFS */
> > +
> > +#endif /* PWM_INTERNAL_H */
> > diff --git a/drivers/pwm/pwm-atmel-hlcdc.c b/drivers/pwm/pwm-atmel-hlcdc.c
> > index 3f2c5031a3ba..6bbbfaa582c1 100644
> > --- a/drivers/pwm/pwm-atmel-hlcdc.c
> > +++ b/drivers/pwm/pwm-atmel-hlcdc.c
> > @@ -15,6 +15,8 @@
> >  #include <linux/pwm.h>
> >  #include <linux/regmap.h>
> >  
> > +#include "internal.h"
> > +
> >  #define ATMEL_HLCDC_PWMCVAL_MASK	GENMASK(15, 8)
> >  #define ATMEL_HLCDC_PWMCVAL(x)		(((x) << 8) & ATMEL_HLCDC_PWMCVAL_MASK)
> >  #define ATMEL_HLCDC_PWMPOL		BIT(4)
> > @@ -185,7 +187,7 @@ static int atmel_hlcdc_pwm_suspend(struct device *dev)
> >  	struct atmel_hlcdc_pwm *atmel = dev_get_drvdata(dev);
> >  
> >  	/* Keep the periph clock enabled if the PWM is still running. */
> > -	if (pwm_is_enabled(&atmel->chip.pwms[0]))
> > +	if (pwm_is_enabled(&atmel->chip.priv->pwms[0]))
> 
> Didn't work so well to hide the internals of pwm_chip_private from the
> drivers?
> 
> >  		clk_disable_unprepare(atmel->hlcdc->periph_clk);
> >  
> >  	return 0;
> >  
> 
> [... a few more drivers where hiding the internals failed ...]

Yes, and you'll recall that these are for exactly the cases where in the
past you have argued that the core should take a more active role in
making sure that PWMs get properly disabled. These should be temporary
and once this logic is all moved into the core these should disappear.

> 
> > diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c
> > index 4edb994fa2e1..653df20afe1c 100644
> > --- a/drivers/pwm/sysfs.c
> > +++ b/drivers/pwm/sysfs.c
> > @@ -14,6 +14,8 @@
> >  #include <linux/kdev_t.h>
> >  #include <linux/pwm.h>
> >  
> > +#include "internal.h"
> > +
> >  struct pwm_export {
> >  	struct device child;
> >  	struct pwm_device *pwm;
> > @@ -311,7 +313,7 @@ static ssize_t export_store(struct device *parent,
> >  			    struct device_attribute *attr,
> >  			    const char *buf, size_t len)
> >  {
> > -	struct pwm_chip *chip = dev_get_drvdata(parent);
> > +	struct pwm_chip_private *priv = dev_get_drvdata(parent);
> >  	struct pwm_device *pwm;
> >  	unsigned int hwpwm;
> >  	int ret;
> > @@ -320,10 +322,10 @@ static ssize_t export_store(struct device *parent,
> >  	if (ret < 0)
> >  		return ret;
> >  
> > -	if (hwpwm >= chip->npwm)
> > +	if (hwpwm >= priv->chip->npwm)
> 
> One more pointer dereference than before. You could trade that for some
> data duplication by copying npwm to struct pwmchip_priv for the case
> when you need .npwm but the chip is already gone.

When the chip is gone, none of these functions should be doing anything
other than returning an error. None of that is implemented in this yet.
This is merely a preparatory patch.

> 
> >  		return -ENODEV;
> >  
> > -	pwm = pwm_request_from_chip(chip, hwpwm, "sysfs");
> > +	pwm = pwm_request_from_chip(priv->chip, hwpwm, "sysfs");
> >  	if (IS_ERR(pwm))
> >  		return PTR_ERR(pwm);
> >  
> > @@ -339,7 +341,7 @@ static ssize_t unexport_store(struct device *parent,
> >  			      struct device_attribute *attr,
> >  			      const char *buf, size_t len)
> >  {
> > -	struct pwm_chip *chip = dev_get_drvdata(parent);
> > +	struct pwm_chip_private *priv = dev_get_drvdata(parent);
> >  	unsigned int hwpwm;
> >  	int ret;
> >  
> > @@ -347,10 +349,10 @@ static ssize_t unexport_store(struct device *parent,
> >  	if (ret < 0)
> >  		return ret;
> >  
> > -	if (hwpwm >= chip->npwm)
> > +	if (hwpwm >= priv->chip->npwm)
> 
> One more pointer dereference than before.
> 
> >  		return -ENODEV;
> >  
> > -	ret = pwm_unexport_child(parent, &chip->pwms[hwpwm]);
> > +	ret = pwm_unexport_child(parent, &priv->pwms[hwpwm]);
> >  
> >  	return ret ? : len;
> >  }
> > @@ -359,9 +361,9 @@ static DEVICE_ATTR_WO(unexport);
> >  static ssize_t npwm_show(struct device *parent, struct device_attribute *attr,
> >  			 char *buf)
> >  {
> > -	const struct pwm_chip *chip = dev_get_drvdata(parent);
> > +	const struct pwm_chip_private *priv = dev_get_drvdata(parent);
> >  
> > -	return sysfs_emit(buf, "%u\n", chip->npwm);
> > +	return sysfs_emit(buf, "%u\n", priv->chip->npwm);
> >  }
> >  static DEVICE_ATTR_RO(npwm);
> >  
> > @@ -411,12 +413,12 @@ static int pwm_class_apply_state(struct pwm_export *export,
> >  
> >  static int pwm_class_resume_npwm(struct device *parent, unsigned int npwm)
> >  {
> > -	struct pwm_chip *chip = dev_get_drvdata(parent);
> > +	struct pwm_chip_private *priv = dev_get_drvdata(parent);
> >  	unsigned int i;
> >  	int ret = 0;
> >  
> >  	for (i = 0; i < npwm; i++) {
> > -		struct pwm_device *pwm = &chip->pwms[i];
> > +		struct pwm_device *pwm = &priv->pwms[i];
> >  		struct pwm_state state;
> >  		struct pwm_export *export;
> >  
> > @@ -442,12 +444,12 @@ static int pwm_class_resume_npwm(struct device *parent, unsigned int npwm)
> >  
> >  static int pwm_class_suspend(struct device *parent)
> >  {
> > -	struct pwm_chip *chip = dev_get_drvdata(parent);
> > +	struct pwm_chip_private *priv = dev_get_drvdata(parent);
> >  	unsigned int i;
> >  	int ret = 0;
> >  
> > -	for (i = 0; i < chip->npwm; i++) {
> > -		struct pwm_device *pwm = &chip->pwms[i];
> > +	for (i = 0; i < priv->chip->npwm; i++) {
> > +		struct pwm_device *pwm = &priv->pwms[i];
> >  		struct pwm_state state;
> >  		struct pwm_export *export;
> >  
> > @@ -483,9 +485,9 @@ static int pwm_class_suspend(struct device *parent)
> >  
> >  static int pwm_class_resume(struct device *parent)
> >  {
> > -	struct pwm_chip *chip = dev_get_drvdata(parent);
> > +	struct pwm_chip_private *priv = dev_get_drvdata(parent);
> >  
> > -	return pwm_class_resume_npwm(parent, chip->npwm);
> > +	return pwm_class_resume_npwm(parent, priv->chip->npwm);
> 
> One more pointer dereference than before.
> 
> >  }
> >  
> >  static DEFINE_SIMPLE_DEV_PM_OPS(pwm_class_pm_ops, pwm_class_suspend, pwm_class_resume);
> > @@ -501,7 +503,7 @@ static int pwmchip_sysfs_match(struct device *parent, const void *data)
> >  	return dev_get_drvdata(parent) == data;
> >  }
> >  
> > -void pwmchip_sysfs_export(struct pwm_chip *chip)
> > +void pwmchip_sysfs_export(struct pwm_chip_private *priv)
> >  {
> >  	struct device *parent;
> >  
> > @@ -509,26 +511,26 @@ void pwmchip_sysfs_export(struct pwm_chip *chip)
> >  	 * If device_create() fails the pwm_chip is still usable by
> >  	 * the kernel it's just not exported.
> >  	 */
> > -	parent = device_create(&pwm_class, chip->dev, MKDEV(0, 0), chip,
> > -			       "pwmchip%d", chip->id);
> > +	parent = device_create(&pwm_class, priv->chip->dev, MKDEV(0, 0), priv,
> > +			       "pwmchip%d", priv->id);
> >  	if (IS_ERR(parent)) {
> > -		dev_warn(chip->dev,
> > +		dev_warn(priv->chip->dev,
> 
> One more pointer dereference than before.
> 
> >  			 "device_create failed for pwm_chip sysfs export\n");
> >  	}
> >  }
> 
> The TL;DR; is essentially what I already wrote in my last reply to Bart
> in the v3 thread[1]:
> 
>  - My approach needs more changes to the individual drivers (which I
>    don't consider a relevant disadvantage given that the resulting code
>    is better);
>  - My approach works with less pointer dereferences which IMHO also
>    simplifies understanding the code as all relevant data is in a single
>    place.
>  - My approach has a weaker separation between the core and the lowlevel
>    drivers. That's ok in my book given that this doesn't complicate the
>    lowlevel drivers and that hiding details considerably better doesn't
>    work anyhow (see the drivers that need internal.h in your patch).
> 
> For me the single allocation issue is only an added bonus. The relevant
> advantage of my approach is that the code is easier and (probably) more
> efficient.

I happen to disagree. I think adding pwmchip_alloc() makes things much
more complicated for low level drivers.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-mediatek/attachments/20231211/61861d34/attachment-0001.sig>


More information about the Linux-mediatek mailing list