summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/gpio/gpiolib-cdev.c20
-rw-r--r--drivers/gpio/gpiolib-sysfs.c17
-rw-r--r--drivers/gpio/gpiolib.c106
-rw-r--r--drivers/gpio/gpiolib.h2
4 files changed, 47 insertions, 98 deletions
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index 75f4912339a6..3588aaf90e45 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -2302,18 +2302,16 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
memset(info, 0, sizeof(*info));
info->offset = gpio_chip_hwgpio(desc);
- scoped_guard(spinlock_irqsave, &gpio_lock) {
- if (desc->name)
- strscpy(info->name, desc->name, sizeof(info->name));
-
- scoped_guard(srcu, &desc->srcu) {
- label = gpiod_get_label(desc);
- if (label)
- strscpy(info->consumer, label,
- sizeof(info->consumer));
- }
+ if (desc->name)
+ strscpy(info->name, desc->name, sizeof(info->name));
+
+ dflags = READ_ONCE(desc->flags);
- dflags = READ_ONCE(desc->flags);
+ scoped_guard(srcu, &desc->srcu) {
+ label = gpiod_get_label(desc);
+ if (label && test_bit(FLAG_REQUESTED, &dflags))
+ strscpy(info->consumer, label,
+ sizeof(info->consumer));
}
/*
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index 54b77ae1d176..3ae7704b2996 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -563,7 +563,6 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
struct gpio_device *gdev;
struct gpiod_data *data;
struct gpio_chip *chip;
- unsigned long flags;
struct device *dev;
int status, offset;
@@ -578,6 +577,9 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
return -EINVAL;
}
+ if (!test_and_set_bit(FLAG_EXPORT, &desc->flags))
+ return -EPERM;
+
gdev = desc->gdev;
chip = gdev->chip;
@@ -589,18 +591,11 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
goto err_unlock;
}
- spin_lock_irqsave(&gpio_lock, flags);
- if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
- test_bit(FLAG_EXPORT, &desc->flags)) {
- spin_unlock_irqrestore(&gpio_lock, flags);
- gpiod_dbg(desc, "%s: unavailable (requested=%d, exported=%d)\n",
- __func__,
- test_bit(FLAG_REQUESTED, &desc->flags),
- test_bit(FLAG_EXPORT, &desc->flags));
+ if (!test_bit(FLAG_REQUESTED, &desc->flags)) {
+ gpiod_dbg(desc, "%s: unavailable (not requested)\n", __func__);
status = -EPERM;
goto err_unlock;
}
- spin_unlock_irqrestore(&gpio_lock, flags);
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
@@ -628,7 +623,6 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
goto err_free_data;
}
- set_bit(FLAG_EXPORT, &desc->flags);
mutex_unlock(&sysfs_lock);
return 0;
@@ -636,6 +630,7 @@ err_free_data:
kfree(data);
err_unlock:
mutex_unlock(&sysfs_lock);
+ clear_bit(FLAG_EXPORT, &desc->flags);
gpiod_dbg(desc, "%s: status %d\n", __func__, status);
return status;
}
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index f0cbbab17dc3..04b4636baff1 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -76,12 +76,6 @@ static const struct bus_type gpio_bus_type = {
*/
#define FASTPATH_NGPIO CONFIG_GPIOLIB_FASTPATH_LIMIT
-/* gpio_lock prevents conflicts during gpio_desc[] table updates.
- * While any GPIO is requested, its gpio_chip is not removable;
- * each GPIO's "requested" flag serves as a lock and refcount.
- */
-DEFINE_SPINLOCK(gpio_lock);
-
static DEFINE_MUTEX(gpio_lookup_lock);
static LIST_HEAD(gpio_lookup_list);
@@ -123,8 +117,7 @@ static int desc_set_label(struct gpio_desc *desc, const char *label)
const char *new = NULL, *old;
if (label) {
- /* FIXME: make this GFP_KERNEL once the spinlock is out. */
- new = kstrdup_const(label, GFP_ATOMIC);
+ new = kstrdup_const(label, GFP_KERNEL);
if (!new)
return -ENOMEM;
}
@@ -1093,7 +1086,6 @@ EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key);
void gpiochip_remove(struct gpio_chip *gc)
{
struct gpio_device *gdev = gc->gpiodev;
- unsigned long flags;
unsigned int i;
down_write(&gdev->sem);
@@ -1119,12 +1111,10 @@ void gpiochip_remove(struct gpio_chip *gc)
*/
gpiochip_set_data(gc, NULL);
- spin_lock_irqsave(&gpio_lock, flags);
for (i = 0; i < gdev->ngpio; i++) {
if (test_bit(FLAG_REQUESTED, &gdev->descs[i].flags))
break;
}
- spin_unlock_irqrestore(&gpio_lock, flags);
if (i != gdev->ngpio)
dev_crit(&gdev->dev,
@@ -2227,62 +2217,43 @@ EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges);
static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
{
struct gpio_chip *gc = desc->gdev->chip;
- unsigned long flags;
unsigned int offset;
int ret;
+ if (test_and_set_bit(FLAG_REQUESTED, &desc->flags))
+ return -EBUSY;
+
if (label) {
label = kstrdup_const(label, GFP_KERNEL);
if (!label)
return -ENOMEM;
}
- spin_lock_irqsave(&gpio_lock, flags);
-
/* NOTE: gpio_request() can be called in early boot,
* before IRQs are enabled, for non-sleeping (SOC) GPIOs.
*/
- if (test_and_set_bit(FLAG_REQUESTED, &desc->flags)) {
- ret = -EBUSY;
- goto out_free_unlock;
- }
-
if (gc->request) {
- /* gc->request may sleep */
- spin_unlock_irqrestore(&gpio_lock, flags);
offset = gpio_chip_hwgpio(desc);
if (gpiochip_line_is_valid(gc, offset))
ret = gc->request(gc, offset);
else
ret = -EINVAL;
- spin_lock_irqsave(&gpio_lock, flags);
-
- if (ret) {
- desc_set_label(desc, NULL);
- clear_bit(FLAG_REQUESTED, &desc->flags);
- goto out_free_unlock;
- }
+ if (ret)
+ goto out_clear_bit;
}
- if (gc->get_direction) {
- /* gc->get_direction may sleep */
- spin_unlock_irqrestore(&gpio_lock, flags);
+
+ if (gc->get_direction)
gpiod_get_direction(desc);
- spin_lock_irqsave(&gpio_lock, flags);
- }
- spin_unlock_irqrestore(&gpio_lock, flags);
ret = desc_set_label(desc, label ? : "?");
- if (ret) {
- clear_bit(FLAG_REQUESTED, &desc->flags);
- return ret;
- }
+ if (ret)
+ goto out_clear_bit;
return 0;
-out_free_unlock:
- spin_unlock_irqrestore(&gpio_lock, flags);
- kfree_const(label);
+out_clear_bit:
+ clear_bit(FLAG_REQUESTED, &desc->flags);
return ret;
}
@@ -2352,35 +2323,32 @@ static bool gpiod_free_commit(struct gpio_desc *desc)
might_sleep();
- spin_lock_irqsave(&gpio_lock, flags);
-
gc = desc->gdev->chip;
- if (gc && test_bit(FLAG_REQUESTED, &desc->flags)) {
- if (gc->free) {
- spin_unlock_irqrestore(&gpio_lock, flags);
- might_sleep_if(gc->can_sleep);
+ flags = READ_ONCE(desc->flags);
+
+ if (gc && test_bit(FLAG_REQUESTED, &flags)) {
+ if (gc->free)
gc->free(gc, gpio_chip_hwgpio(desc));
- spin_lock_irqsave(&gpio_lock, flags);
- }
- clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
- clear_bit(FLAG_REQUESTED, &desc->flags);
- clear_bit(FLAG_OPEN_DRAIN, &desc->flags);
- clear_bit(FLAG_OPEN_SOURCE, &desc->flags);
- clear_bit(FLAG_PULL_UP, &desc->flags);
- clear_bit(FLAG_PULL_DOWN, &desc->flags);
- clear_bit(FLAG_BIAS_DISABLE, &desc->flags);
- clear_bit(FLAG_EDGE_RISING, &desc->flags);
- clear_bit(FLAG_EDGE_FALLING, &desc->flags);
- clear_bit(FLAG_IS_HOGGED, &desc->flags);
+
+ clear_bit(FLAG_ACTIVE_LOW, &flags);
+ clear_bit(FLAG_REQUESTED, &flags);
+ clear_bit(FLAG_OPEN_DRAIN, &flags);
+ clear_bit(FLAG_OPEN_SOURCE, &flags);
+ clear_bit(FLAG_PULL_UP, &flags);
+ clear_bit(FLAG_PULL_DOWN, &flags);
+ clear_bit(FLAG_BIAS_DISABLE, &flags);
+ clear_bit(FLAG_EDGE_RISING, &flags);
+ clear_bit(FLAG_EDGE_FALLING, &flags);
+ clear_bit(FLAG_IS_HOGGED, &flags);
#ifdef CONFIG_OF_DYNAMIC
WRITE_ONCE(desc->hog, NULL);
#endif
ret = true;
- }
+ desc_set_label(desc, NULL);
+ WRITE_ONCE(desc->flags, flags);
- spin_unlock_irqrestore(&gpio_lock, flags);
- desc_set_label(desc, NULL);
- gpiod_line_state_notify(desc, GPIOLINE_CHANGED_RELEASED);
+ gpiod_line_state_notify(desc, GPIOLINE_CHANGED_RELEASED);
+ }
return ret;
}
@@ -2422,22 +2390,12 @@ char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset)
if (IS_ERR(desc))
return NULL;
- guard(spinlock_irqsave)(&gpio_lock);
-
if (!test_bit(FLAG_REQUESTED, &desc->flags))
return NULL;
guard(srcu)(&desc->srcu);
- /*
- * FIXME: Once we mark gpiod_direction_input/output() and
- * gpiod_get_direction() with might_sleep(), we'll be able to protect
- * the GPIO descriptors with mutex (while value setting operations will
- * become lockless).
- *
- * Until this happens, this allocation needs to be atomic.
- */
- label = kstrdup(gpiod_get_label(desc), GFP_ATOMIC);
+ label = kstrdup(gpiod_get_label(desc), GFP_KERNEL);
if (!label)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 2bf3f9e13ae4..9b7afe87f1bd 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -135,8 +135,6 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
int gpiod_set_transitory(struct gpio_desc *desc, bool transitory);
-extern spinlock_t gpio_lock;
-
void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action);
/**