Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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: 1cd53df733c2 ("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>

+58 -54
+58 -54
drivers/gpio/gpiolib-sysfs.c
··· 919 919 } 920 920 EXPORT_SYMBOL_GPL(gpiod_export_link); 921 921 922 + static void gpiod_unexport_unlocked(struct gpio_desc *desc) 923 + { 924 + struct gpiod_data *tmp, *desc_data = NULL; 925 + struct gpiodev_data *gdev_data; 926 + struct gpio_device *gdev; 927 + 928 + if (!test_bit(GPIOD_FLAG_EXPORT, &desc->flags)) 929 + return; 930 + 931 + gdev = gpiod_to_gpio_device(desc); 932 + gdev_data = gdev_get_data(gdev); 933 + if (!gdev_data) 934 + return; 935 + 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; 940 + } 941 + } 942 + 943 + if (!desc_data) 944 + return; 945 + 946 + list_del(&desc_data->list); 947 + clear_bit(GPIOD_FLAG_EXPORT, &desc->flags); 948 + #if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) 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); 958 + #endif /* CONFIG_GPIO_SYSFS_LEGACY */ 959 + 960 + sysfs_remove_groups(desc_data->parent, 961 + desc_data->chip_attr_groups); 962 + 963 + mutex_destroy(&desc_data->mutex); 964 + kfree(desc_data); 965 + } 966 + 922 967 /** 923 968 * gpiod_unexport - reverse effect of gpiod_export() 924 969 * @desc: GPIO to make unavailable ··· 972 927 */ 973 928 void gpiod_unexport(struct gpio_desc *desc) 974 929 { 975 - struct gpiod_data *tmp, *desc_data = NULL; 976 - struct gpiodev_data *gdev_data; 977 - struct gpio_device *gdev; 978 - 979 930 if (!desc) { 980 931 pr_warn("%s: invalid GPIO\n", __func__); 981 932 return; 982 933 } 983 934 984 - scoped_guard(mutex, &sysfs_lock) { 985 - if (!test_bit(GPIOD_FLAG_EXPORT, &desc->flags)) 986 - return; 935 + guard(mutex)(&sysfs_lock); 987 936 988 - gdev = gpiod_to_gpio_device(desc); 989 - gdev_data = gdev_get_data(gdev); 990 - if (!gdev_data) 991 - return; 992 - 993 - list_for_each_entry(tmp, &gdev_data->exported_lines, list) { 994 - if (gpiod_is_equal(desc, tmp->desc)) { 995 - desc_data = tmp; 996 - break; 997 - } 998 - } 999 - 1000 - if (!desc_data) 1001 - return; 1002 - 1003 - list_del(&desc_data->list); 1004 - clear_bit(GPIOD_FLAG_EXPORT, &desc->flags); 1005 - #if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) 1006 - sysfs_put(desc_data->value_kn); 1007 - device_unregister(desc_data->dev); 1008 - 1009 - /* 1010 - * Release irq after deregistration to prevent race with 1011 - * edge_store. 1012 - */ 1013 - if (desc_data->irq_flags) 1014 - gpio_sysfs_free_irq(desc_data); 1015 - #endif /* CONFIG_GPIO_SYSFS_LEGACY */ 1016 - 1017 - sysfs_remove_groups(desc_data->parent, 1018 - desc_data->chip_attr_groups); 1019 - } 1020 - 1021 - mutex_destroy(&desc_data->mutex); 1022 - kfree(desc_data); 937 + gpiod_unexport_unlocked(desc); 1023 938 } 1024 939 EXPORT_SYMBOL_GPL(gpiod_unexport); 1025 940 ··· 1059 1054 struct gpio_desc *desc; 1060 1055 struct gpio_chip *chip; 1061 1056 1062 - scoped_guard(mutex, &sysfs_lock) { 1063 - data = gdev_get_data(gdev); 1064 - if (!data) 1065 - return; 1057 + guard(mutex)(&sysfs_lock); 1066 1058 1067 - #if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) 1068 - device_unregister(data->cdev_base); 1069 - #endif /* CONFIG_GPIO_SYSFS_LEGACY */ 1070 - device_unregister(data->cdev_id); 1071 - kfree(data); 1072 - } 1059 + data = gdev_get_data(gdev); 1060 + if (!data) 1061 + return; 1073 1062 1074 1063 guard(srcu)(&gdev->srcu); 1075 - 1076 1064 chip = srcu_dereference(gdev->chip, &gdev->srcu); 1077 1065 if (!chip) 1078 1066 return; 1079 1067 1080 1068 /* unregister gpiod class devices owned by sysfs */ 1081 1069 for_each_gpio_desc_with_flag(chip, desc, GPIOD_FLAG_SYSFS) { 1082 - gpiod_unexport(desc); 1070 + gpiod_unexport_unlocked(desc); 1083 1071 gpiod_free(desc); 1084 1072 } 1073 + 1074 + #if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) 1075 + device_unregister(data->cdev_base); 1076 + #endif /* CONFIG_GPIO_SYSFS_LEGACY */ 1077 + device_unregister(data->cdev_id); 1078 + kfree(data); 1085 1079 } 1086 1080 1087 1081 /*