Message ID | 20231121134901.208535-106-u.kleine-koenig@pengutronix.de (mailing list archive) |
---|---|
State | Handled Elsewhere |
Headers | show |
Series | pwm: Fix lifetime issues for pwm_chips | expand |
On 11/21/23 07:50, Uwe Kleine-König wrote: > It's required to not free the memory underlying a requested PWM > while a consumer still has a reference to it. While currently a pwm_chip > doesn't life long enough in all cases, linking the struct pwm to the > pwm_chip results in the right lifetime as soon as the pwmchip is living > long enough. This happens with the following commits. > > Note this is a breaking change for all pwm drivers that don't use > pwmchip_alloc(). > > Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Reviewed-by: Gustavo A. R. Silva <gustavoars@kernel.org> # for struct_size() and __counted_by() Thanks for including __counted_by(). :) -- Gustavo > --- > drivers/pwm/core.c | 26 ++++++++++---------------- > include/linux/pwm.h | 2 +- > 2 files changed, 11 insertions(+), 17 deletions(-) > > diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c > index 15942210aa08..029aa1c69591 100644 > --- a/drivers/pwm/core.c > +++ b/drivers/pwm/core.c > @@ -198,7 +198,7 @@ static bool pwm_ops_check(const struct pwm_chip *chip) > > void *pwmchip_priv(struct pwm_chip *chip) > { > - return (void *)chip + sizeof(*chip); > + return (void *)chip + struct_size(chip, pwms, chip->npwm); > } > EXPORT_SYMBOL_GPL(pwmchip_priv); > > @@ -206,8 +206,9 @@ struct pwm_chip *devm_pwmchip_alloc(struct device *parent, unsigned int npwm, si > { > struct pwm_chip *chip; > size_t alloc_size; > + unsigned int i; > > - alloc_size = size_add(sizeof(*chip), sizeof_priv); > + alloc_size = size_add(struct_size(chip, pwms, npwm), sizeof_priv); > > chip = devm_kzalloc(parent, alloc_size, GFP_KERNEL); > if (!chip) > @@ -217,6 +218,13 @@ struct pwm_chip *devm_pwmchip_alloc(struct device *parent, unsigned int npwm, si > chip->npwm = npwm; > chip->uses_pwmchip_alloc = true; > > + for (i = 0; i < chip->npwm; i++) { > + struct pwm_device *pwm = &chip->pwms[i]; > + > + pwm->chip = chip; > + pwm->hwpwm = i; > + } > + > return chip; > } > EXPORT_SYMBOL_GPL(devm_pwmchip_alloc); > @@ -234,7 +242,6 @@ EXPORT_SYMBOL_GPL(devm_pwmchip_alloc); > int __pwmchip_add(struct pwm_chip *chip, struct module *owner) > { > int ret; > - unsigned i; > > if (!chip || !chip->dev || !chip->ops || !chip->npwm) > return -EINVAL; > @@ -253,26 +260,15 @@ int __pwmchip_add(struct pwm_chip *chip, struct module *owner) > > chip->owner = owner; > > - chip->pwms = kcalloc(chip->npwm, sizeof(*chip->pwms), GFP_KERNEL); > - if (!chip->pwms) > - 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); > > @@ -303,8 +299,6 @@ void pwmchip_remove(struct pwm_chip *chip) > idr_remove(&pwmchip_idr, chip->id); > > mutex_unlock(&pwm_lock); > - > - kfree(chip->pwms); > } > EXPORT_SYMBOL_GPL(pwmchip_remove); > > diff --git a/include/linux/pwm.h b/include/linux/pwm.h > index b8e70ee01d31..a7294ef1495d 100644 > --- a/include/linux/pwm.h > +++ b/include/linux/pwm.h > @@ -302,7 +302,7 @@ struct pwm_chip { > > /* only used internally by the PWM framework */ > bool uses_pwmchip_alloc; > - struct pwm_device *pwms; > + struct pwm_device pwms[] __counted_by(npwm); > }; > > static inline struct device *pwmchip_parent(struct pwm_chip *chip)
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 15942210aa08..029aa1c69591 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -198,7 +198,7 @@ static bool pwm_ops_check(const struct pwm_chip *chip) void *pwmchip_priv(struct pwm_chip *chip) { - return (void *)chip + sizeof(*chip); + return (void *)chip + struct_size(chip, pwms, chip->npwm); } EXPORT_SYMBOL_GPL(pwmchip_priv); @@ -206,8 +206,9 @@ struct pwm_chip *devm_pwmchip_alloc(struct device *parent, unsigned int npwm, si { struct pwm_chip *chip; size_t alloc_size; + unsigned int i; - alloc_size = size_add(sizeof(*chip), sizeof_priv); + alloc_size = size_add(struct_size(chip, pwms, npwm), sizeof_priv); chip = devm_kzalloc(parent, alloc_size, GFP_KERNEL); if (!chip) @@ -217,6 +218,13 @@ struct pwm_chip *devm_pwmchip_alloc(struct device *parent, unsigned int npwm, si chip->npwm = npwm; chip->uses_pwmchip_alloc = true; + for (i = 0; i < chip->npwm; i++) { + struct pwm_device *pwm = &chip->pwms[i]; + + pwm->chip = chip; + pwm->hwpwm = i; + } + return chip; } EXPORT_SYMBOL_GPL(devm_pwmchip_alloc); @@ -234,7 +242,6 @@ EXPORT_SYMBOL_GPL(devm_pwmchip_alloc); int __pwmchip_add(struct pwm_chip *chip, struct module *owner) { int ret; - unsigned i; if (!chip || !chip->dev || !chip->ops || !chip->npwm) return -EINVAL; @@ -253,26 +260,15 @@ int __pwmchip_add(struct pwm_chip *chip, struct module *owner) chip->owner = owner; - chip->pwms = kcalloc(chip->npwm, sizeof(*chip->pwms), GFP_KERNEL); - if (!chip->pwms) - 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); @@ -303,8 +299,6 @@ void pwmchip_remove(struct pwm_chip *chip) idr_remove(&pwmchip_idr, chip->id); mutex_unlock(&pwm_lock); - - kfree(chip->pwms); } EXPORT_SYMBOL_GPL(pwmchip_remove); diff --git a/include/linux/pwm.h b/include/linux/pwm.h index b8e70ee01d31..a7294ef1495d 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -302,7 +302,7 @@ struct pwm_chip { /* only used internally by the PWM framework */ bool uses_pwmchip_alloc; - struct pwm_device *pwms; + struct pwm_device pwms[] __counted_by(npwm); }; static inline struct device *pwmchip_parent(struct pwm_chip *chip)
It's required to not free the memory underlying a requested PWM while a consumer still has a reference to it. While currently a pwm_chip doesn't life long enough in all cases, linking the struct pwm to the pwm_chip results in the right lifetime as soon as the pwmchip is living long enough. This happens with the following commits. Note this is a breaking change for all pwm drivers that don't use pwmchip_alloc(). Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> --- drivers/pwm/core.c | 26 ++++++++++---------------- include/linux/pwm.h | 2 +- 2 files changed, 11 insertions(+), 17 deletions(-)