Skip to content

Commit 6766f59

Browse files
author
Bartosz Golaszewski
committed
gpio: sysfs: fix chip removal with GPIOs exported over sysfs
Currently if we export a GPIO over sysfs and unbind the parent GPIO controller, the exported attribute will remain under /sys/class/gpio because once we remove the parent device, we can no longer associate the descriptor with it in gpiod_unexport() and never drop the final reference. Rework the teardown code: provide an unlocked variant of gpiod_unexport() and remove all exported GPIOs with the sysfs_lock taken before unregistering the parent device itself. This is done to prevent any new exports happening before we unregister the device completely. Cc: stable@vger.kernel.org Fixes: 1cd53df ("gpio: sysfs: don't look up exported lines as class devices") Link: https://patch.msgid.link/20260212133505.81516-1-bartosz.golaszewski@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
1 parent ff91965 commit 6766f59

1 file changed

Lines changed: 55 additions & 51 deletions

File tree

drivers/gpio/gpiolib-sysfs.c

Lines changed: 55 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -919,63 +919,68 @@ int gpiod_export_link(struct device *dev, const char *name,
919919
}
920920
EXPORT_SYMBOL_GPL(gpiod_export_link);
921921

922-
/**
923-
* gpiod_unexport - reverse effect of gpiod_export()
924-
* @desc: GPIO to make unavailable
925-
*
926-
* This is implicit on gpiod_free().
927-
*/
928-
void gpiod_unexport(struct gpio_desc *desc)
922+
static void gpiod_unexport_unlocked(struct gpio_desc *desc)
929923
{
930924
struct gpiod_data *tmp, *desc_data = NULL;
931925
struct gpiodev_data *gdev_data;
932926
struct gpio_device *gdev;
933927

934-
if (!desc) {
935-
pr_warn("%s: invalid GPIO\n", __func__);
928+
if (!test_bit(GPIOD_FLAG_EXPORT, &desc->flags))
936929
return;
937-
}
938930

939-
scoped_guard(mutex, &sysfs_lock) {
940-
if (!test_bit(GPIOD_FLAG_EXPORT, &desc->flags))
941-
return;
942-
943-
gdev = gpiod_to_gpio_device(desc);
944-
gdev_data = gdev_get_data(gdev);
945-
if (!gdev_data)
946-
return;
931+
gdev = gpiod_to_gpio_device(desc);
932+
gdev_data = gdev_get_data(gdev);
933+
if (!gdev_data)
934+
return;
947935

948-
list_for_each_entry(tmp, &gdev_data->exported_lines, list) {
949-
if (gpiod_is_equal(desc, tmp->desc)) {
950-
desc_data = tmp;
951-
break;
952-
}
936+
list_for_each_entry(tmp, &gdev_data->exported_lines, list) {
937+
if (gpiod_is_equal(desc, tmp->desc)) {
938+
desc_data = tmp;
939+
break;
953940
}
941+
}
954942

955-
if (!desc_data)
956-
return;
943+
if (!desc_data)
944+
return;
957945

958-
list_del(&desc_data->list);
959-
clear_bit(GPIOD_FLAG_EXPORT, &desc->flags);
946+
list_del(&desc_data->list);
947+
clear_bit(GPIOD_FLAG_EXPORT, &desc->flags);
960948
#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)
961-
sysfs_put(desc_data->value_kn);
962-
device_unregister(desc_data->dev);
963-
964-
/*
965-
* Release irq after deregistration to prevent race with
966-
* edge_store.
967-
*/
968-
if (desc_data->irq_flags)
969-
gpio_sysfs_free_irq(desc_data);
949+
sysfs_put(desc_data->value_kn);
950+
device_unregister(desc_data->dev);
951+
952+
/*
953+
* Release irq after deregistration to prevent race with
954+
* edge_store.
955+
*/
956+
if (desc_data->irq_flags)
957+
gpio_sysfs_free_irq(desc_data);
970958
#endif /* CONFIG_GPIO_SYSFS_LEGACY */
971959

972-
sysfs_remove_groups(desc_data->parent,
973-
desc_data->chip_attr_groups);
974-
}
960+
sysfs_remove_groups(desc_data->parent,
961+
desc_data->chip_attr_groups);
975962

976963
mutex_destroy(&desc_data->mutex);
977964
kfree(desc_data);
978965
}
966+
967+
/**
968+
* gpiod_unexport - reverse effect of gpiod_export()
969+
* @desc: GPIO to make unavailable
970+
*
971+
* This is implicit on gpiod_free().
972+
*/
973+
void gpiod_unexport(struct gpio_desc *desc)
974+
{
975+
if (!desc) {
976+
pr_warn("%s: invalid GPIO\n", __func__);
977+
return;
978+
}
979+
980+
guard(mutex)(&sysfs_lock);
981+
982+
gpiod_unexport_unlocked(desc);
983+
}
979984
EXPORT_SYMBOL_GPL(gpiod_unexport);
980985

981986
int gpiochip_sysfs_register(struct gpio_device *gdev)
@@ -1054,29 +1059,28 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev)
10541059
struct gpio_desc *desc;
10551060
struct gpio_chip *chip;
10561061

1057-
scoped_guard(mutex, &sysfs_lock) {
1058-
data = gdev_get_data(gdev);
1059-
if (!data)
1060-
return;
1062+
guard(mutex)(&sysfs_lock);
10611063

1062-
#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)
1063-
device_unregister(data->cdev_base);
1064-
#endif /* CONFIG_GPIO_SYSFS_LEGACY */
1065-
device_unregister(data->cdev_id);
1066-
kfree(data);
1067-
}
1064+
data = gdev_get_data(gdev);
1065+
if (!data)
1066+
return;
10681067

10691068
guard(srcu)(&gdev->srcu);
1070-
10711069
chip = srcu_dereference(gdev->chip, &gdev->srcu);
10721070
if (!chip)
10731071
return;
10741072

10751073
/* unregister gpiod class devices owned by sysfs */
10761074
for_each_gpio_desc_with_flag(chip, desc, GPIOD_FLAG_SYSFS) {
1077-
gpiod_unexport(desc);
1075+
gpiod_unexport_unlocked(desc);
10781076
gpiod_free(desc);
10791077
}
1078+
1079+
#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)
1080+
device_unregister(data->cdev_base);
1081+
#endif /* CONFIG_GPIO_SYSFS_LEGACY */
1082+
device_unregister(data->cdev_id);
1083+
kfree(data);
10801084
}
10811085

10821086
/*

0 commit comments

Comments
 (0)