diff mbox series

[5/8] pwm: Add a struct device to struct pwm_chip

Message ID 35c65ea7f6de789a568ff39d7b6b4ce80de4b7dc.1710670958.git.u.kleine-koenig@pengutronix.de
State Accepted
Headers show
Series pwm: Add support for character devices | expand

Commit Message

Uwe Kleine-König March 17, 2024, 10:40 a.m. UTC
This replaces the formerly dynamically allocated struct device. This
allows to additionally use it to track the lifetime of the struct
pwm_chip. Otherwise the new struct device provides the same sysfs API as
was provided by the dynamic device before.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
---
 drivers/pwm/core.c  | 93 +++++++++++++++++++++++++--------------------
 include/linux/pwm.h |  5 ++-
 2 files changed, 54 insertions(+), 44 deletions(-)

Comments

David Lechner March 28, 2024, 6:44 p.m. UTC | #1
On 3/17/24 5:40 AM, Uwe Kleine-König wrote:

...

> diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
> index 8ed4c93cd0ce..09ff6ef0b634 100644
> --- a/drivers/pwm/core.c
> +++ b/drivers/pwm/core.c

...

> @@ -1520,6 +1525,8 @@ EXPORT_SYMBOL_GPL(pwm_get);
>   */
>  void pwm_put(struct pwm_device *pwm)
>  {
> +	struct pwm_chip *chip = pwm->chip;
> +

Should this be moved after the !pwm check to avoid potential null
dereference or is it safe to remove the !pwm check now?

>  	if (!pwm)
>  		return;
>
Uwe Kleine-König March 29, 2024, 10:19 a.m. UTC | #2
On Thu, Mar 28, 2024 at 01:44:45PM -0500, David Lechner wrote:
> On 3/17/24 5:40 AM, Uwe Kleine-König wrote:
> 
> ...
> 
> > diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
> > index 8ed4c93cd0ce..09ff6ef0b634 100644
> > --- a/drivers/pwm/core.c
> > +++ b/drivers/pwm/core.c
> 
> ...
> 
> > @@ -1520,6 +1525,8 @@ EXPORT_SYMBOL_GPL(pwm_get);
> >   */
> >  void pwm_put(struct pwm_device *pwm)
> >  {
> > +	struct pwm_chip *chip = pwm->chip;
> > +
> 
> Should this be moved after the !pwm check to avoid potential null
> dereference or is it safe to remove the !pwm check now?

This is indeed on oversight. Thanks for finding and reporting it. I just
sent a patch to address this by delaying the assignment until after the
check below.

See https://lore.kernel.org/r/20240329101648.544155-2-u.kleine-koenig@pengutronix.de

Best regards
Uwe
 
> >  	if (!pwm)
> >  		return;
> >
diff mbox series

Patch

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 8ed4c93cd0ce..09ff6ef0b634 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -343,9 +343,16 @@  static int pwm_device_request(struct pwm_device *pwm, const char *label)
 	if (!try_module_get(chip->owner))
 		return -ENODEV;
 
+	if (!get_device(&chip->dev)) {
+		err = -ENODEV;
+		goto err_get_device;
+	}
+
 	if (ops->request) {
 		err = ops->request(chip, pwm);
 		if (err) {
+			put_device(&chip->dev);
+err_get_device:
 			module_put(chip->owner);
 			return err;
 		}
@@ -463,7 +470,7 @@  struct pwm_export {
 
 static inline struct pwm_chip *pwmchip_from_dev(struct device *pwmchip_dev)
 {
-	return dev_get_drvdata(pwmchip_dev);
+	return container_of(pwmchip_dev, struct pwm_chip, dev);
 }
 
 static inline struct pwm_export *pwmexport_from_dev(struct device *pwm_dev)
@@ -941,46 +948,16 @@  static struct class pwm_class = {
 	.pm = pm_sleep_ptr(&pwm_class_pm_ops),
 };
 
-static int pwmchip_sysfs_match(struct device *pwmchip_dev, const void *data)
-{
-	return pwmchip_from_dev(pwmchip_dev) == data;
-}
-
-static void pwmchip_sysfs_export(struct pwm_chip *chip)
-{
-	struct device *pwmchip_dev;
-
-	/*
-	 * If device_create() fails the pwm_chip is still usable by
-	 * the kernel it's just not exported.
-	 */
-	pwmchip_dev = device_create(&pwm_class, pwmchip_parent(chip), MKDEV(0, 0), chip,
-			       "pwmchip%d", chip->id);
-	if (IS_ERR(pwmchip_dev)) {
-		dev_warn(pwmchip_parent(chip),
-			 "device_create failed for pwm_chip sysfs export\n");
-	}
-}
-
 static void pwmchip_sysfs_unexport(struct pwm_chip *chip)
 {
-	struct device *pwmchip_dev;
 	unsigned int i;
 
-	pwmchip_dev = class_find_device(&pwm_class, NULL, chip,
-					pwmchip_sysfs_match);
-	if (!pwmchip_dev)
-		return;
-
 	for (i = 0; i < chip->npwm; i++) {
 		struct pwm_device *pwm = &chip->pwms[i];
 
 		if (test_bit(PWMF_EXPORTED, &pwm->flags))
-			pwm_unexport_child(pwmchip_dev, pwm);
+			pwm_unexport_child(&chip->dev, pwm);
 	}
-
-	put_device(pwmchip_dev);
-	device_unregister(pwmchip_dev);
 }
 
 #define PWMCHIP_ALIGN ARCH_DMA_MINALIGN
@@ -993,13 +970,21 @@  static void *pwmchip_priv(struct pwm_chip *chip)
 /* This is the counterpart to pwmchip_alloc() */
 void pwmchip_put(struct pwm_chip *chip)
 {
-	kfree(chip);
+	put_device(&chip->dev);
 }
 EXPORT_SYMBOL_GPL(pwmchip_put);
 
+static void pwmchip_release(struct device *pwmchip_dev)
+{
+	struct pwm_chip *chip = pwmchip_from_dev(pwmchip_dev);
+
+	kfree(chip);
+}
+
 struct pwm_chip *pwmchip_alloc(struct device *parent, unsigned int npwm, size_t sizeof_priv)
 {
 	struct pwm_chip *chip;
+	struct device *pwmchip_dev;
 	size_t alloc_size;
 	unsigned int i;
 
@@ -1010,10 +995,15 @@  struct pwm_chip *pwmchip_alloc(struct device *parent, unsigned int npwm, size_t
 	if (!chip)
 		return ERR_PTR(-ENOMEM);
 
-	chip->dev = parent;
 	chip->npwm = npwm;
 	chip->uses_pwmchip_alloc = true;
 
+	pwmchip_dev = &chip->dev;
+	device_initialize(pwmchip_dev);
+	pwmchip_dev->class = &pwm_class;
+	pwmchip_dev->parent = parent;
+	pwmchip_dev->release = pwmchip_release;
+
 	pwmchip_set_drvdata(chip, pwmchip_priv(chip));
 
 	for (i = 0; i < chip->npwm; i++) {
@@ -1115,21 +1105,34 @@  int __pwmchip_add(struct pwm_chip *chip, struct module *owner)
 	mutex_lock(&pwm_lock);
 
 	ret = idr_alloc(&pwm_chips, chip, 0, 0, GFP_KERNEL);
-	if (ret < 0) {
-		mutex_unlock(&pwm_lock);
-		return ret;
-	}
+	if (ret < 0)
+		goto err_idr_alloc;
 
 	chip->id = ret;
 
-	mutex_unlock(&pwm_lock);
+	dev_set_name(&chip->dev, "pwmchip%u", chip->id);
 
 	if (IS_ENABLED(CONFIG_OF))
 		of_pwmchip_add(chip);
 
-	pwmchip_sysfs_export(chip);
+	ret = device_add(&chip->dev);
+	if (ret)
+		goto err_device_add;
+
+	mutex_unlock(&pwm_lock);
 
 	return 0;
+
+err_device_add:
+	if (IS_ENABLED(CONFIG_OF))
+		of_pwmchip_remove(chip);
+
+	idr_remove(&pwm_chips, chip->id);
+err_idr_alloc:
+
+	mutex_unlock(&pwm_lock);
+
+	return ret;
 }
 EXPORT_SYMBOL_GPL(__pwmchip_add);
 
@@ -1151,6 +1154,8 @@  void pwmchip_remove(struct pwm_chip *chip)
 	idr_remove(&pwm_chips, chip->id);
 
 	mutex_unlock(&pwm_lock);
+
+	device_del(&chip->dev);
 }
 EXPORT_SYMBOL_GPL(pwmchip_remove);
 
@@ -1520,6 +1525,8 @@  EXPORT_SYMBOL_GPL(pwm_get);
  */
 void pwm_put(struct pwm_device *pwm)
 {
+	struct pwm_chip *chip = pwm->chip;
+
 	if (!pwm)
 		return;
 
@@ -1530,12 +1537,14 @@  void pwm_put(struct pwm_device *pwm)
 		goto out;
 	}
 
-	if (pwm->chip->ops->free)
+	if (chip->ops->free)
 		pwm->chip->ops->free(pwm->chip, pwm);
 
 	pwm->label = NULL;
 
-	module_put(pwm->chip->owner);
+	put_device(&chip->dev);
+
+	module_put(chip->owner);
 out:
 	mutex_unlock(&pwm_lock);
 }
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 78b9061572ff..495e23761f34 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -2,6 +2,7 @@ 
 #ifndef __LINUX_PWM_H
 #define __LINUX_PWM_H
 
+#include <linux/device.h>
 #include <linux/err.h>
 #include <linux/mutex.h>
 #include <linux/of.h>
@@ -277,7 +278,7 @@  struct pwm_ops {
  * @pwms: array of PWM devices allocated by the framework
  */
 struct pwm_chip {
-	struct device *dev;
+	struct device dev;
 	const struct pwm_ops *ops;
 	struct module *owner;
 	unsigned int id;
@@ -295,7 +296,7 @@  struct pwm_chip {
 
 static inline struct device *pwmchip_parent(const struct pwm_chip *chip)
 {
-	return chip->dev;
+	return chip->dev.parent;
 }
 
 static inline void *pwmchip_get_drvdata(struct pwm_chip *chip)