Commit 3027743f authored by Laura Abbott's avatar Laura Abbott Committed by Linus Walleij

gpio: Remove VLA from gpiolib

The new challenge is to remove VLAs from the kernel
(see https://lkml.org/lkml/2018/3/7/621) to eventually
turn on -Wvla.

Using a kmalloc array is the easy way to fix this but kmalloc is still
more expensive than stack allocation. Introduce a fast path with a
fixed size stack array to cover most chip with gpios below some fixed
amount. The slow path dynamically allocates an array to cover those
chips with a large number of gpios.
Reviewed-by: default avatarGeert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: default avatarPhil Reid <preid@electromag.com.au>
Reviewed-and-tested-by: default avatarLukas Wunner <lukas@wunner.de>
Signed-off-by: default avatarLukas Wunner <lukas@wunner.de>
Signed-off-by: default avatarLaura Abbott <labbott@redhat.com>
Reviewed-by: default avatarAndy Shevchenko <andy.shevchenko@gmail.com>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent ed5cab43
...@@ -22,6 +22,18 @@ menuconfig GPIOLIB ...@@ -22,6 +22,18 @@ menuconfig GPIOLIB
if GPIOLIB if GPIOLIB
config GPIOLIB_FASTPATH_LIMIT
int "Maximum number of GPIOs for fast path"
range 32 512
default 512
help
This adjusts the point at which certain APIs will switch from
using a stack allocated buffer to a dynamically allocated buffer.
You shouldn't need to change this unless you really need to
optimize either stack space or performance. Change this carefully
since setting an incorrect value could cause stack corruption.
config OF_GPIO config OF_GPIO
def_bool y def_bool y
depends on OF depends on OF
......
...@@ -61,6 +61,11 @@ static struct bus_type gpio_bus_type = { ...@@ -61,6 +61,11 @@ static struct bus_type gpio_bus_type = {
.name = "gpio", .name = "gpio",
}; };
/*
* Number of GPIOs to use for the fast path in set array
*/
#define FASTPATH_NGPIO CONFIG_GPIOLIB_FASTPATH_LIMIT
/* gpio_lock prevents conflicts during gpio_desc[] table updates. /* gpio_lock prevents conflicts during gpio_desc[] table updates.
* While any GPIO is requested, its gpio_chip is not removable; * While any GPIO is requested, its gpio_chip is not removable;
* each GPIO's "requested" flag serves as a lock and refcount. * each GPIO's "requested" flag serves as a lock and refcount.
...@@ -453,12 +458,11 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd, ...@@ -453,12 +458,11 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
vals[i] = !!ghd.values[i]; vals[i] = !!ghd.values[i];
/* Reuse the array setting function */ /* Reuse the array setting function */
gpiod_set_array_value_complex(false, return gpiod_set_array_value_complex(false,
true, true,
lh->numdescs, lh->numdescs,
lh->descs, lh->descs,
vals); vals);
return 0;
} }
return -EINVAL; return -EINVAL;
} }
...@@ -1281,6 +1285,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, ...@@ -1281,6 +1285,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
goto err_free_descs; goto err_free_descs;
} }
if (chip->ngpio > FASTPATH_NGPIO)
chip_warn(chip, "line cnt %u is greater than fast path cnt %u\n",
chip->ngpio, FASTPATH_NGPIO);
gdev->label = kstrdup_const(chip->label ?: "unknown", GFP_KERNEL); gdev->label = kstrdup_const(chip->label ?: "unknown", GFP_KERNEL);
if (!gdev->label) { if (!gdev->label) {
status = -ENOMEM; status = -ENOMEM;
...@@ -2758,16 +2766,28 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, ...@@ -2758,16 +2766,28 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
while (i < array_size) { while (i < array_size) {
struct gpio_chip *chip = desc_array[i]->gdev->chip; struct gpio_chip *chip = desc_array[i]->gdev->chip;
unsigned long mask[BITS_TO_LONGS(chip->ngpio)]; unsigned long fastpath[2 * BITS_TO_LONGS(FASTPATH_NGPIO)];
unsigned long bits[BITS_TO_LONGS(chip->ngpio)]; unsigned long *mask, *bits;
int first, j, ret; int first, j, ret;
if (likely(chip->ngpio <= FASTPATH_NGPIO)) {
mask = fastpath;
} else {
mask = kmalloc_array(2 * BITS_TO_LONGS(chip->ngpio),
sizeof(*mask),
can_sleep ? GFP_KERNEL : GFP_ATOMIC);
if (!mask)
return -ENOMEM;
}
bits = mask + BITS_TO_LONGS(chip->ngpio);
bitmap_zero(mask, chip->ngpio);
if (!can_sleep) if (!can_sleep)
WARN_ON(chip->can_sleep); WARN_ON(chip->can_sleep);
/* collect all inputs belonging to the same chip */ /* collect all inputs belonging to the same chip */
first = i; first = i;
memset(mask, 0, sizeof(mask));
do { do {
const struct gpio_desc *desc = desc_array[i]; const struct gpio_desc *desc = desc_array[i];
int hwgpio = gpio_chip_hwgpio(desc); int hwgpio = gpio_chip_hwgpio(desc);
...@@ -2778,8 +2798,11 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, ...@@ -2778,8 +2798,11 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
(desc_array[i]->gdev->chip == chip)); (desc_array[i]->gdev->chip == chip));
ret = gpio_chip_get_multiple(chip, mask, bits); ret = gpio_chip_get_multiple(chip, mask, bits);
if (ret) if (ret) {
if (mask != fastpath)
kfree(mask);
return ret; return ret;
}
for (j = first; j < i; j++) { for (j = first; j < i; j++) {
const struct gpio_desc *desc = desc_array[j]; const struct gpio_desc *desc = desc_array[j];
...@@ -2791,6 +2814,9 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, ...@@ -2791,6 +2814,9 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
value_array[j] = value; value_array[j] = value;
trace_gpio_value(desc_to_gpio(desc), 1, value); trace_gpio_value(desc_to_gpio(desc), 1, value);
} }
if (mask != fastpath)
kfree(mask);
} }
return 0; return 0;
} }
...@@ -2974,7 +3000,7 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip, ...@@ -2974,7 +3000,7 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip,
} }
} }
void gpiod_set_array_value_complex(bool raw, bool can_sleep, int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size, unsigned int array_size,
struct gpio_desc **desc_array, struct gpio_desc **desc_array,
int *value_array) int *value_array)
...@@ -2983,14 +3009,26 @@ void gpiod_set_array_value_complex(bool raw, bool can_sleep, ...@@ -2983,14 +3009,26 @@ void gpiod_set_array_value_complex(bool raw, bool can_sleep,
while (i < array_size) { while (i < array_size) {
struct gpio_chip *chip = desc_array[i]->gdev->chip; struct gpio_chip *chip = desc_array[i]->gdev->chip;
unsigned long mask[BITS_TO_LONGS(chip->ngpio)]; unsigned long fastpath[2 * BITS_TO_LONGS(FASTPATH_NGPIO)];
unsigned long bits[BITS_TO_LONGS(chip->ngpio)]; unsigned long *mask, *bits;
int count = 0; int count = 0;
if (likely(chip->ngpio <= FASTPATH_NGPIO)) {
mask = fastpath;
} else {
mask = kmalloc_array(2 * BITS_TO_LONGS(chip->ngpio),
sizeof(*mask),
can_sleep ? GFP_KERNEL : GFP_ATOMIC);
if (!mask)
return -ENOMEM;
}
bits = mask + BITS_TO_LONGS(chip->ngpio);
bitmap_zero(mask, chip->ngpio);
if (!can_sleep) if (!can_sleep)
WARN_ON(chip->can_sleep); WARN_ON(chip->can_sleep);
memset(mask, 0, sizeof(mask));
do { do {
struct gpio_desc *desc = desc_array[i]; struct gpio_desc *desc = desc_array[i];
int hwgpio = gpio_chip_hwgpio(desc); int hwgpio = gpio_chip_hwgpio(desc);
...@@ -3021,7 +3059,11 @@ void gpiod_set_array_value_complex(bool raw, bool can_sleep, ...@@ -3021,7 +3059,11 @@ void gpiod_set_array_value_complex(bool raw, bool can_sleep,
/* push collected bits to outputs */ /* push collected bits to outputs */
if (count != 0) if (count != 0)
gpio_chip_set_multiple(chip, mask, bits); gpio_chip_set_multiple(chip, mask, bits);
if (mask != fastpath)
kfree(mask);
} }
return 0;
} }
/** /**
...@@ -3096,13 +3138,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_value); ...@@ -3096,13 +3138,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_value);
* This function should be called from contexts where we cannot sleep, and will * This function should be called from contexts where we cannot sleep, and will
* complain if the GPIO chip functions potentially sleep. * complain if the GPIO chip functions potentially sleep.
*/ */
void gpiod_set_raw_array_value(unsigned int array_size, int gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array, int *value_array) struct gpio_desc **desc_array, int *value_array)
{ {
if (!desc_array) if (!desc_array)
return; return -EINVAL;
gpiod_set_array_value_complex(true, false, array_size, desc_array, return gpiod_set_array_value_complex(true, false, array_size,
value_array); desc_array, value_array);
} }
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value); EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);
...@@ -3422,14 +3464,14 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep); ...@@ -3422,14 +3464,14 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
* *
* This function is to be called from contexts that can sleep. * This function is to be called from contexts that can sleep.
*/ */
void gpiod_set_raw_array_value_cansleep(unsigned int array_size, int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array, struct gpio_desc **desc_array,
int *value_array) int *value_array)
{ {
might_sleep_if(extra_checks); might_sleep_if(extra_checks);
if (!desc_array) if (!desc_array)
return; return -EINVAL;
gpiod_set_array_value_complex(true, true, array_size, desc_array, return gpiod_set_array_value_complex(true, true, array_size, desc_array,
value_array); value_array);
} }
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value_cansleep); EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value_cansleep);
......
...@@ -188,7 +188,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, ...@@ -188,7 +188,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size, unsigned int array_size,
struct gpio_desc **desc_array, struct gpio_desc **desc_array,
int *value_array); int *value_array);
void gpiod_set_array_value_complex(bool raw, bool can_sleep, int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size, unsigned int array_size,
struct gpio_desc **desc_array, struct gpio_desc **desc_array,
int *value_array); int *value_array);
......
...@@ -116,7 +116,7 @@ int gpiod_get_raw_array_value(unsigned int array_size, ...@@ -116,7 +116,7 @@ int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array, struct gpio_desc **desc_array,
int *value_array); int *value_array);
void gpiod_set_raw_value(struct gpio_desc *desc, int value); void gpiod_set_raw_value(struct gpio_desc *desc, int value);
void gpiod_set_raw_array_value(unsigned int array_size, int gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array, struct gpio_desc **desc_array,
int *value_array); int *value_array);
...@@ -134,7 +134,7 @@ int gpiod_get_raw_array_value_cansleep(unsigned int array_size, ...@@ -134,7 +134,7 @@ int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array, struct gpio_desc **desc_array,
int *value_array); int *value_array);
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value); void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value);
void gpiod_set_raw_array_value_cansleep(unsigned int array_size, int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array, struct gpio_desc **desc_array,
int *value_array); int *value_array);
...@@ -369,12 +369,13 @@ static inline void gpiod_set_raw_value(struct gpio_desc *desc, int value) ...@@ -369,12 +369,13 @@ static inline void gpiod_set_raw_value(struct gpio_desc *desc, int value)
/* GPIO can never have been requested */ /* GPIO can never have been requested */
WARN_ON(1); WARN_ON(1);
} }
static inline void gpiod_set_raw_array_value(unsigned int array_size, static inline int gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array, struct gpio_desc **desc_array,
int *value_array) int *value_array)
{ {
/* GPIO can never have been requested */ /* GPIO can never have been requested */
WARN_ON(1); WARN_ON(1);
return 0;
} }
static inline int gpiod_get_value_cansleep(const struct gpio_desc *desc) static inline int gpiod_get_value_cansleep(const struct gpio_desc *desc)
...@@ -423,12 +424,13 @@ static inline void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, ...@@ -423,12 +424,13 @@ static inline void gpiod_set_raw_value_cansleep(struct gpio_desc *desc,
/* GPIO can never have been requested */ /* GPIO can never have been requested */
WARN_ON(1); WARN_ON(1);
} }
static inline void gpiod_set_raw_array_value_cansleep(unsigned int array_size, static inline int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array, struct gpio_desc **desc_array,
int *value_array) int *value_array)
{ {
/* GPIO can never have been requested */ /* GPIO can never have been requested */
WARN_ON(1); WARN_ON(1);
return 0;
} }
static inline int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) static inline int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment