diff options
author | Laxman Dewangan <ldewangan@nvidia.com> | 2014-04-16 14:57:34 +0530 |
---|---|---|
committer | Seema Khowala <seemaj@nvidia.com> | 2014-04-23 14:05:26 -0700 |
commit | e8f37ec9c24b4ff3a1331237702c51918bd67c2c (patch) | |
tree | 9f063f795aa2b9a5ccb407218f4abd33ac04556b /drivers/gpio | |
parent | a7d310f5cfcd6e59dc6cc8ee04e3cdbbfc474e1f (diff) |
gpiolib: avoid sleeping lock from atomic context in gpiochip_add
If GPIO device is added from device tree then gpiochip_add() try
to find the aliased base for that device on atomic context.
The of_alias_get_id() is sleeping calls, so it dumps warning of
sleeping calls from atomic context as:
--
[ 6.320139] BUG: sleeping function called from invalid context at kernel/kernel/mutex.c:413
[ 6.332406] in_atomic(): 1, irqs_disabled(): 128, pid: 1, name: swapper/0
[ 6.339157] INFO: lockdep is turned off.
[ 6.343058] irq event stamp: 106315
[ 6.346523] hardirqs last enabled at (106315): [<ffffffc0000a6724>] vprintk_emit+0x1ac/0x57c
[ 6.355017] hardirqs last disabled at (106314): [<ffffffc0000a65c8>] vprintk_emit+0x50/0x57c
[ 6.363423] softirqs last enabled at (105022): [<ffffffc0000ac730>] __do_softirq+0x1b8/0x2c0
[ 6.371917] softirqs last disabled at (105017): [<ffffffc0000ac91c>] do_softirq+0x6c/0x84
[ 6.380074] CPU: 0 PID: 1 Comm: swapper/0 Tainted: G W 3.10.33-g60742c9-dirty #47
[ 6.388385] Call trace:
[ 6.390818] [<ffffffc0000885f4>] dump_backtrace+0x0/0x16c
[ 6.396192] [<ffffffc000088770>] show_stack+0x10/0x1c
[ 6.401218] [<ffffffc000956a30>] dump_stack+0x1c/0x28
[ 6.406243] [<ffffffc0000d604c>] __might_sleep+0x118/0x120
[ 6.411703] [<ffffffc00095b788>] mutex_lock_nested+0x40/0x350
[ 6.417424] [<ffffffc0006d05f8>] of_alias_get_id+0x2c/0x94
[ 6.422891] [<ffffffc000350170>] gpiochip_add+0x1b8/0x3f0
[ 6.428255] [<ffffffc0003531dc>] pca953x_probe+0x214/0x4fc
----
Rewrite this piece of code such that of_alias_get_id() will get called
from non-atomic context to avoid above bug.
Change-Id: I0c003f9fbec9661acaa3c5e41c4bc90f6d50729f
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Reviewed-on: http://git-master/r/396957
Reviewed-by: Thomas Cherry <tcherry@nvidia.com>
GVS: Gerrit_Virtual_Submit
Diffstat (limited to 'drivers/gpio')
-rw-r--r-- | drivers/gpio/gpiolib.c | 44 |
1 files changed, 22 insertions, 22 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 0e6c9bd34f53..92e03d478dd5 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -184,37 +184,31 @@ struct gpio_chip *gpio_to_chip(unsigned gpio) } /* dynamic allocation of GPIOs, e.g. on a hotplugged device */ -static int gpiochip_find_base(struct gpio_chip *req_chip) +static int gpiochip_find_base(struct gpio_chip *req_chip, int aliased_base) { struct gpio_chip *chip; int ngpio = req_chip->ngpio; int base = -1; -#ifdef CONFIG_OF - if (req_chip->of_node) { - int gpio_nr; + if (aliased_base >= 0) { + int start_gpio = aliased_base; + int end_gpio = aliased_base + req_chip->ngpio; - gpio_nr = of_alias_get_id(req_chip->of_node, "gpio"); - if (gpio_nr >= 0) { - int start_gpio = gpio_nr; - int end_gpio = gpio_nr + req_chip->ngpio; + /* Check if aliased base is not already allocated */ + list_for_each_entry(chip, &gpio_chips, list) { + if (chip->base > end_gpio) + continue; - list_for_each_entry(chip, &gpio_chips, list) { - if (chip->base > end_gpio) + if ((chip->base < start_gpio) && + ((chip->base + chip->ngpio) < start_gpio)) continue; - - if ((chip->base < start_gpio) && - ((chip->base + chip->ngpio) < start_gpio)) - continue; - pr_err("GPIO %d to %d is already allocated\n", - start_gpio, end_gpio); - gpio_nr = -1; - break; - } + pr_err("GPIO %d to %d is already allocated\n", + start_gpio, end_gpio); + aliased_base = -1; + break; } - base = gpio_nr; + base = aliased_base; } -#endif if (base >= 0) goto found; @@ -1206,6 +1200,7 @@ int gpiochip_add(struct gpio_chip *chip) int status = 0; unsigned id; int base = chip->base; + int aliased_base = -1; if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - 1)) && base >= 0) { @@ -1213,10 +1208,15 @@ int gpiochip_add(struct gpio_chip *chip) goto fail; } +#ifdef CONFIG_OF + if (chip->of_node) + aliased_base = of_alias_get_id(chip->of_node, "gpio"); +#endif + spin_lock_irqsave(&gpio_lock, flags); if (base < 0) { - base = gpiochip_find_base(chip); + base = gpiochip_find_base(chip, aliased_base); if (base < 0) { status = base; goto unlock; |