[v1,2/2] gpiolib: of: Integrate of_gpiochip_init_valid_mask() into gpiochip_init_valid_mask()

Message ID 20221107161027.43384-2-andriy.shevchenko@linux.intel.com
State New
Headers
Series [v1,1/2] gpiolib: of: Prepare of_gpiochip_add() / of_gpiochip_remove() for fwnode |

Commit Message

Andy Shevchenko Nov. 7, 2022, 4:10 p.m. UTC
  In preparation to complete fwnode switch, integrate
of_gpiochip_init_valid_mask() into gpiochip_init_valid_mask().

Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 drivers/gpio/gpiolib-of.c | 42 -------------------------------
 drivers/gpio/gpiolib-of.h |  5 ----
 drivers/gpio/gpiolib.c    | 52 ++++++++++++++++++++++++++++++++++++++-
 3 files changed, 51 insertions(+), 48 deletions(-)
  

Comments

Dmitry Torokhov Nov. 7, 2022, 6:20 p.m. UTC | #1
Hi Andy,

On Mon, Nov 07, 2022 at 06:10:27PM +0200, Andy Shevchenko wrote:
> +static unsigned int gpiochip_count_reserved_ranges(struct gpio_chip *gc)
> +{
> +	int size;
> +
> +	size = fwnode_property_count_u32(gc->fwnode, "gpio-reserved-ranges");

I wonder if a comment why we need even size would not be helpful.

> +	if (size > 0 && size % 2 == 0)
> +		return size;
> +
> +	return 0;
> +}
> +
>  static int gpiochip_alloc_valid_mask(struct gpio_chip *gc)
>  {
> -	if (!(of_gpio_need_valid_mask(gc) || gc->init_valid_mask))
> +	if (!(gpiochip_count_reserved_ranges(gc) || gc->init_valid_mask))
>  		return 0;
>  
>  	gc->valid_mask = gpiochip_allocate_mask(gc);
> @@ -457,8 +468,47 @@ static int gpiochip_alloc_valid_mask(struct gpio_chip *gc)
>  	return 0;
>  }
>  
> +static int gpiochip_apply_reserved_ranges(struct gpio_chip *gc, unsigned int sz)
> +{
> +	u32 *ranges;
> +	int ret;
> +
> +	ranges = kmalloc_array(sz, sizeof(*ranges), GFP_KERNEL);
> +	if (!ranges)
> +		return -ENOMEM;
> +
> +	ret = fwnode_property_read_u32_array(gc->fwnode, "gpio-reserved-ranges", ranges, sz);
> +	if (ret) {
> +		kfree(ranges);
> +		return ret;
> +	}
> +
> +	while (sz) {
> +		u32 count = ranges[--sz];
> +		u32 start = ranges[--sz];

I know we checked sz validity, but I wonder if re-checking it in this
function would not insulate us from errors creeping in after some other
code refactoring.

In any case,

Reviewed-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

Thanks.
  
Andy Shevchenko Nov. 7, 2022, 9:09 p.m. UTC | #2
On Mon, Nov 07, 2022 at 10:20:37AM -0800, Dmitry Torokhov wrote:
> On Mon, Nov 07, 2022 at 06:10:27PM +0200, Andy Shevchenko wrote:
> > +static unsigned int gpiochip_count_reserved_ranges(struct gpio_chip *gc)
> > +{
> > +	int size;
> > +
> > +	size = fwnode_property_count_u32(gc->fwnode, "gpio-reserved-ranges");
> 
> I wonder if a comment why we need even size would not be helpful.

Was it in the original code?
Anyway, if Bart thinks so as well, I may add it in v2.

> > +	if (size > 0 && size % 2 == 0)
> > +		return size;
> > +
> > +	return 0;
> > +}
> > +
> >  static int gpiochip_alloc_valid_mask(struct gpio_chip *gc)
> >  {
> > -	if (!(of_gpio_need_valid_mask(gc) || gc->init_valid_mask))
> > +	if (!(gpiochip_count_reserved_ranges(gc) || gc->init_valid_mask))
> >  		return 0;
> >  
> >  	gc->valid_mask = gpiochip_allocate_mask(gc);
> > @@ -457,8 +468,47 @@ static int gpiochip_alloc_valid_mask(struct gpio_chip *gc)
> >  	return 0;
> >  }
> >  
> > +static int gpiochip_apply_reserved_ranges(struct gpio_chip *gc, unsigned int sz)
> > +{
> > +	u32 *ranges;
> > +	int ret;
> > +
> > +	ranges = kmalloc_array(sz, sizeof(*ranges), GFP_KERNEL);
> > +	if (!ranges)
> > +		return -ENOMEM;
> > +
> > +	ret = fwnode_property_read_u32_array(gc->fwnode, "gpio-reserved-ranges", ranges, sz);
> > +	if (ret) {
> > +		kfree(ranges);
> > +		return ret;
> > +	}
> > +
> > +	while (sz) {
> > +		u32 count = ranges[--sz];
> > +		u32 start = ranges[--sz];
> 
> I know we checked sz validity, but I wonder if re-checking it in this
> function would not insulate us from errors creeping in after some other
> code refactoring.

I'm not sure I understand what you meant. The fwnode_property_read_u32_array()
will fail if the given sz is too big for the real data, so while (sz) would
never even go on the invalid data.

> In any case,
> 
> Reviewed-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

Thank you!
  
Dmitry Torokhov Nov. 7, 2022, 9:40 p.m. UTC | #3
On Mon, Nov 07, 2022 at 11:09:19PM +0200, Andy Shevchenko wrote:
> On Mon, Nov 07, 2022 at 10:20:37AM -0800, Dmitry Torokhov wrote:
> > On Mon, Nov 07, 2022 at 06:10:27PM +0200, Andy Shevchenko wrote:
> > > +static unsigned int gpiochip_count_reserved_ranges(struct gpio_chip *gc)
> > > +{
> > > +	int size;
> > > +
> > > +	size = fwnode_property_count_u32(gc->fwnode, "gpio-reserved-ranges");
> > 
> > I wonder if a comment why we need even size would not be helpful.
> 
> Was it in the original code?
> Anyway, if Bart thinks so as well, I may add it in v2.
> 
> > > +	if (size > 0 && size % 2 == 0)
> > > +		return size;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > >  static int gpiochip_alloc_valid_mask(struct gpio_chip *gc)
> > >  {
> > > -	if (!(of_gpio_need_valid_mask(gc) || gc->init_valid_mask))
> > > +	if (!(gpiochip_count_reserved_ranges(gc) || gc->init_valid_mask))
> > >  		return 0;
> > >  
> > >  	gc->valid_mask = gpiochip_allocate_mask(gc);
> > > @@ -457,8 +468,47 @@ static int gpiochip_alloc_valid_mask(struct gpio_chip *gc)
> > >  	return 0;
> > >  }
> > >  
> > > +static int gpiochip_apply_reserved_ranges(struct gpio_chip *gc, unsigned int sz)
> > > +{
> > > +	u32 *ranges;
> > > +	int ret;
> > > +
> > > +	ranges = kmalloc_array(sz, sizeof(*ranges), GFP_KERNEL);
> > > +	if (!ranges)
> > > +		return -ENOMEM;
> > > +
> > > +	ret = fwnode_property_read_u32_array(gc->fwnode, "gpio-reserved-ranges", ranges, sz);
> > > +	if (ret) {
> > > +		kfree(ranges);
> > > +		return ret;
> > > +	}
> > > +
> > > +	while (sz) {
> > > +		u32 count = ranges[--sz];
> > > +		u32 start = ranges[--sz];
> > 
> > I know we checked sz validity, but I wonder if re-checking it in this
> > function would not insulate us from errors creeping in after some other
> > code refactoring.
> 
> I'm not sure I understand what you meant. The fwnode_property_read_u32_array()
> will fail if the given sz is too big for the real data, so while (sz) would
> never even go on the invalid data.

I am more worried about sz being odd and the loop ending up trying to
dereference ranges[-1].

Thanks.
  
Andy Shevchenko Nov. 8, 2022, 8:41 a.m. UTC | #4
On Mon, Nov 07, 2022 at 01:40:53PM -0800, Dmitry Torokhov wrote:
> On Mon, Nov 07, 2022 at 11:09:19PM +0200, Andy Shevchenko wrote:
> > On Mon, Nov 07, 2022 at 10:20:37AM -0800, Dmitry Torokhov wrote:
> > > On Mon, Nov 07, 2022 at 06:10:27PM +0200, Andy Shevchenko wrote:

...

> > > > +static int gpiochip_apply_reserved_ranges(struct gpio_chip *gc, unsigned int sz)
> > > > +{
> > > > +	u32 *ranges;
> > > > +	int ret;
> > > > +
> > > > +	ranges = kmalloc_array(sz, sizeof(*ranges), GFP_KERNEL);
> > > > +	if (!ranges)
> > > > +		return -ENOMEM;
> > > > +
> > > > +	ret = fwnode_property_read_u32_array(gc->fwnode, "gpio-reserved-ranges", ranges, sz);
> > > > +	if (ret) {
> > > > +		kfree(ranges);
> > > > +		return ret;
> > > > +	}
> > > > +
> > > > +	while (sz) {
> > > > +		u32 count = ranges[--sz];
> > > > +		u32 start = ranges[--sz];
> > > 
> > > I know we checked sz validity, but I wonder if re-checking it in this
> > > function would not insulate us from errors creeping in after some other
> > > code refactoring.
> > 
> > I'm not sure I understand what you meant. The fwnode_property_read_u32_array()
> > will fail if the given sz is too big for the real data, so while (sz) would
> > never even go on the invalid data.
> 
> I am more worried about sz being odd and the loop ending up trying to
> dereference ranges[-1].

I see. What if we take amount of ranges as the parameter and convert to size by
multiplying by 2?
  
Andy Shevchenko Nov. 8, 2022, 1:23 p.m. UTC | #5
On Tue, Nov 08, 2022 at 10:41:43AM +0200, Andy Shevchenko wrote:
> On Mon, Nov 07, 2022 at 01:40:53PM -0800, Dmitry Torokhov wrote:
> > On Mon, Nov 07, 2022 at 11:09:19PM +0200, Andy Shevchenko wrote:

...

> > I am more worried about sz being odd and the loop ending up trying to
> > dereference ranges[-1].
> 
> I see. What if we take amount of ranges as the parameter and convert to size by
> multiplying by 2?

Okay, I found a way how to avoid additional churn and add validation.
I will incorporate that in v2 with your tags.
  

Patch

diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index 000020eb78d8..4be3c21aa718 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -112,24 +112,6 @@  static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip,
 	return gpiochip_get_desc(chip, ret);
 }
 
-/**
- * of_gpio_need_valid_mask() - figure out if the OF GPIO driver needs
- * to set the .valid_mask
- * @gc: the target gpio_chip
- *
- * Return: true if the valid mask needs to be set
- */
-bool of_gpio_need_valid_mask(const struct gpio_chip *gc)
-{
-	int size;
-	const struct device_node *np = gc->of_node;
-
-	size = of_property_count_u32_elems(np,  "gpio-reserved-ranges");
-	if (size > 0 && size % 2 == 0)
-		return true;
-	return false;
-}
-
 /*
  * Overrides stated polarity of a gpio line and warns when there is a
  * discrepancy.
@@ -989,28 +971,6 @@  void of_mm_gpiochip_remove(struct of_mm_gpio_chip *mm_gc)
 }
 EXPORT_SYMBOL_GPL(of_mm_gpiochip_remove);
 
-static void of_gpiochip_init_valid_mask(struct gpio_chip *chip)
-{
-	int len, i;
-	u32 start, count;
-	struct device_node *np = chip->of_node;
-
-	len = of_property_count_u32_elems(np,  "gpio-reserved-ranges");
-	if (len < 0 || len % 2 != 0)
-		return;
-
-	for (i = 0; i < len; i += 2) {
-		of_property_read_u32_index(np, "gpio-reserved-ranges",
-					   i, &start);
-		of_property_read_u32_index(np, "gpio-reserved-ranges",
-					   i + 1, &count);
-		if (start >= chip->ngpio || start + count > chip->ngpio)
-			continue;
-
-		bitmap_clear(chip->valid_mask, start, count);
-	}
-};
-
 #ifdef CONFIG_PINCTRL
 static int of_gpiochip_add_pin_range(struct gpio_chip *chip)
 {
@@ -1119,8 +1079,6 @@  int of_gpiochip_add(struct gpio_chip *chip)
 	if (chip->of_gpio_n_cells > MAX_PHANDLE_ARGS)
 		return -EINVAL;
 
-	of_gpiochip_init_valid_mask(chip);
-
 	ret = of_gpiochip_add_pin_range(chip);
 	if (ret)
 		return ret;
diff --git a/drivers/gpio/gpiolib-of.h b/drivers/gpio/gpiolib-of.h
index 1b5df39a952e..2a2f7d17fa7e 100644
--- a/drivers/gpio/gpiolib-of.h
+++ b/drivers/gpio/gpiolib-of.h
@@ -23,7 +23,6 @@  struct gpio_desc *of_find_gpio(struct device *dev,
 int of_gpiochip_add(struct gpio_chip *gc);
 void of_gpiochip_remove(struct gpio_chip *gc);
 int of_gpio_get_count(struct device *dev, const char *con_id);
-bool of_gpio_need_valid_mask(const struct gpio_chip *gc);
 void of_gpio_dev_init(struct gpio_chip *gc, struct gpio_device *gdev);
 #else
 static inline struct gpio_desc *of_find_gpio(struct device *dev,
@@ -39,10 +38,6 @@  static inline int of_gpio_get_count(struct device *dev, const char *con_id)
 {
 	return 0;
 }
-static inline bool of_gpio_need_valid_mask(const struct gpio_chip *gc)
-{
-	return false;
-}
 static inline void of_gpio_dev_init(struct gpio_chip *gc,
 				    struct gpio_device *gdev)
 {
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index e8faedca6b14..2ab7b7949171 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -445,9 +445,20 @@  static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc)
 	return p;
 }
 
+static unsigned int gpiochip_count_reserved_ranges(struct gpio_chip *gc)
+{
+	int size;
+
+	size = fwnode_property_count_u32(gc->fwnode, "gpio-reserved-ranges");
+	if (size > 0 && size % 2 == 0)
+		return size;
+
+	return 0;
+}
+
 static int gpiochip_alloc_valid_mask(struct gpio_chip *gc)
 {
-	if (!(of_gpio_need_valid_mask(gc) || gc->init_valid_mask))
+	if (!(gpiochip_count_reserved_ranges(gc) || gc->init_valid_mask))
 		return 0;
 
 	gc->valid_mask = gpiochip_allocate_mask(gc);
@@ -457,8 +468,47 @@  static int gpiochip_alloc_valid_mask(struct gpio_chip *gc)
 	return 0;
 }
 
+static int gpiochip_apply_reserved_ranges(struct gpio_chip *gc, unsigned int sz)
+{
+	u32 *ranges;
+	int ret;
+
+	ranges = kmalloc_array(sz, sizeof(*ranges), GFP_KERNEL);
+	if (!ranges)
+		return -ENOMEM;
+
+	ret = fwnode_property_read_u32_array(gc->fwnode, "gpio-reserved-ranges", ranges, sz);
+	if (ret) {
+		kfree(ranges);
+		return ret;
+	}
+
+	while (sz) {
+		u32 count = ranges[--sz];
+		u32 start = ranges[--sz];
+
+		if (start >= gc->ngpio || start + count > gc->ngpio)
+			continue;
+
+		bitmap_clear(gc->valid_mask, start, count);
+	}
+
+	kfree(ranges);
+	return 0;
+}
+
 static int gpiochip_init_valid_mask(struct gpio_chip *gc)
 {
+	unsigned int sz;
+	int ret;
+
+	sz = gpiochip_count_reserved_ranges(gc);
+	if (sz) {
+		ret = gpiochip_apply_reserved_ranges(gc, sz);
+		if (ret)
+			return ret;
+	}
+
 	if (gc->init_valid_mask)
 		return gc->init_valid_mask(gc,
 					   gc->valid_mask,