diff options
Diffstat (limited to 'drivers/gpio/gpiolib-cdev.c')
-rw-r--r-- | drivers/gpio/gpiolib-cdev.c | 96 |
1 files changed, 50 insertions, 46 deletions
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 5cca9e8803..9dad67ea25 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -24,7 +24,6 @@ #include <linux/pinctrl/consumer.h> #include <linux/poll.h> #include <linux/rbtree.h> -#include <linux/rwsem.h> #include <linux/seq_file.h> #include <linux/spinlock.h> #include <linux/timekeeping.h> @@ -61,11 +60,6 @@ static_assert(IS_ALIGNED(sizeof(struct gpio_v2_line_values), 8)); * interface to gpiolib GPIOs via ioctl()s. */ -typedef __poll_t (*poll_fn)(struct file *, struct poll_table_struct *); -typedef long (*ioctl_fn)(struct file *, unsigned int, unsigned long); -typedef ssize_t (*read_fn)(struct file *, char __user *, - size_t count, loff_t *); - /* * GPIO line handle management */ @@ -210,9 +204,9 @@ static long linehandle_ioctl(struct file *file, unsigned int cmd, unsigned int i; int ret; - guard(rwsem_read)(&lh->gdev->sem); + guard(srcu)(&lh->gdev->srcu); - if (!lh->gdev->chip) + if (!rcu_access_pointer(lh->gdev->chip)) return -ENODEV; switch (cmd) { @@ -337,7 +331,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) /* Request each GPIO */ for (i = 0; i < handlereq.lines; i++) { u32 offset = handlereq.lineoffsets[i]; - struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset); + struct gpio_desc *desc = gpio_device_get_desc(gdev, offset); if (IS_ERR(desc)) { ret = PTR_ERR(desc); @@ -1572,9 +1566,9 @@ static long linereq_ioctl(struct file *file, unsigned int cmd, struct linereq *lr = file->private_data; void __user *ip = (void __user *)arg; - guard(rwsem_read)(&lr->gdev->sem); + guard(srcu)(&lr->gdev->srcu); - if (!lr->gdev->chip) + if (!rcu_access_pointer(lr->gdev->chip)) return -ENODEV; switch (cmd) { @@ -1603,9 +1597,9 @@ static __poll_t linereq_poll(struct file *file, struct linereq *lr = file->private_data; __poll_t events = 0; - guard(rwsem_read)(&lr->gdev->sem); + guard(srcu)(&lr->gdev->srcu); - if (!lr->gdev->chip) + if (!rcu_access_pointer(lr->gdev->chip)) return EPOLLHUP | EPOLLERR; poll_wait(file, &lr->wait, wait); @@ -1625,9 +1619,9 @@ static ssize_t linereq_read(struct file *file, char __user *buf, ssize_t bytes_read = 0; int ret; - guard(rwsem_read)(&lr->gdev->sem); + guard(srcu)(&lr->gdev->srcu); - if (!lr->gdev->chip) + if (!rcu_access_pointer(lr->gdev->chip)) return -ENODEV; if (count < sizeof(le)) @@ -1791,7 +1785,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) /* Request each GPIO */ for (i = 0; i < ulr.num_lines; i++) { u32 offset = ulr.offsets[i]; - struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset); + struct gpio_desc *desc = gpio_device_get_desc(gdev, offset); if (IS_ERR(desc)) { ret = PTR_ERR(desc); @@ -1926,9 +1920,9 @@ static __poll_t lineevent_poll(struct file *file, struct lineevent_state *le = file->private_data; __poll_t events = 0; - guard(rwsem_read)(&le->gdev->sem); + guard(srcu)(&le->gdev->srcu); - if (!le->gdev->chip) + if (!rcu_access_pointer(le->gdev->chip)) return EPOLLHUP | EPOLLERR; poll_wait(file, &le->wait, wait); @@ -1964,9 +1958,9 @@ static ssize_t lineevent_read(struct file *file, char __user *buf, ssize_t ge_size; int ret; - guard(rwsem_read)(&le->gdev->sem); + guard(srcu)(&le->gdev->srcu); - if (!le->gdev->chip) + if (!rcu_access_pointer(le->gdev->chip)) return -ENODEV; /* @@ -2047,9 +2041,9 @@ static long lineevent_ioctl(struct file *file, unsigned int cmd, void __user *ip = (void __user *)arg; struct gpiohandle_data ghd; - guard(rwsem_read)(&le->gdev->sem); + guard(srcu)(&le->gdev->srcu); - if (!le->gdev->chip) + if (!rcu_access_pointer(le->gdev->chip)) return -ENODEV; /* @@ -2176,7 +2170,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) lflags = eventreq.handleflags; eflags = eventreq.eventflags; - desc = gpiochip_get_desc(gdev->chip, offset); + desc = gpio_device_get_desc(gdev, offset); if (IS_ERR(desc)) return PTR_ERR(desc); @@ -2356,21 +2350,26 @@ static void gpio_v2_line_info_changed_to_v1( static void gpio_desc_to_lineinfo(struct gpio_desc *desc, struct gpio_v2_line_info *info) { - struct gpio_chip *gc = desc->gdev->chip; unsigned long dflags; + const char *label; + + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return; 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)); + if (desc->name) + strscpy(info->name, desc->name, sizeof(info->name)); - if (desc->label) - strscpy(info->consumer, desc->label, - sizeof(info->consumer)); + dflags = READ_ONCE(desc->flags); - dflags = READ_ONCE(desc->flags); + scoped_guard(srcu, &desc->gdev->desc_srcu) { + label = gpiod_get_label(desc); + if (label && test_bit(FLAG_REQUESTED, &dflags)) + strscpy(info->consumer, label, + sizeof(info->consumer)); } /* @@ -2390,8 +2389,8 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc, test_bit(FLAG_USED_AS_IRQ, &dflags) || test_bit(FLAG_EXPORT, &dflags) || test_bit(FLAG_SYSFS, &dflags) || - !gpiochip_line_is_valid(gc, info->offset) || - !pinctrl_gpio_can_use_line(gc, info->offset)) + !gpiochip_line_is_valid(guard.gc, info->offset) || + !pinctrl_gpio_can_use_line(guard.gc, info->offset)) info->flags |= GPIO_V2_LINE_FLAG_USED; if (test_bit(FLAG_IS_OUT, &dflags)) @@ -2478,7 +2477,7 @@ static int lineinfo_get_v1(struct gpio_chardev_data *cdev, void __user *ip, return -EFAULT; /* this doubles as a range check on line_offset */ - desc = gpiochip_get_desc(cdev->gdev->chip, lineinfo.line_offset); + desc = gpio_device_get_desc(cdev->gdev, lineinfo.line_offset); if (IS_ERR(desc)) return PTR_ERR(desc); @@ -2515,7 +2514,7 @@ static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip, if (memchr_inv(lineinfo.padding, 0, sizeof(lineinfo.padding))) return -EINVAL; - desc = gpiochip_get_desc(cdev->gdev->chip, lineinfo.offset); + desc = gpio_device_get_desc(cdev->gdev, lineinfo.offset); if (IS_ERR(desc)) return PTR_ERR(desc); @@ -2564,10 +2563,10 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) struct gpio_device *gdev = cdev->gdev; void __user *ip = (void __user *)arg; - guard(rwsem_read)(&gdev->sem); + guard(srcu)(&gdev->srcu); /* We fail any subsequent ioctl():s when the chip is gone */ - if (!gdev->chip) + if (!rcu_access_pointer(gdev->chip)) return -ENODEV; /* Fill in the struct and pass to userspace */ @@ -2650,9 +2649,9 @@ static __poll_t lineinfo_watch_poll(struct file *file, struct gpio_chardev_data *cdev = file->private_data; __poll_t events = 0; - guard(rwsem_read)(&cdev->gdev->sem); + guard(srcu)(&cdev->gdev->srcu); - if (!cdev->gdev->chip) + if (!rcu_access_pointer(cdev->gdev->chip)) return EPOLLHUP | EPOLLERR; poll_wait(file, &cdev->wait, pollt); @@ -2673,9 +2672,9 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, int ret; size_t event_size; - guard(rwsem_read)(&cdev->gdev->sem); + guard(srcu)(&cdev->gdev->srcu); - if (!cdev->gdev->chip) + if (!rcu_access_pointer(cdev->gdev->chip)) return -ENODEV; #ifndef CONFIG_GPIO_CDEV_V1 @@ -2750,17 +2749,17 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file) struct gpio_chardev_data *cdev; int ret = -ENOMEM; - guard(rwsem_read)(&gdev->sem); + guard(srcu)(&gdev->srcu); /* Fail on open if the backing gpiochip is gone */ - if (!gdev->chip) + if (!rcu_access_pointer(gdev->chip)) return -ENODEV; cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); if (!cdev) return -ENODEV; - cdev->watched_lines = bitmap_zalloc(gdev->chip->ngpio, GFP_KERNEL); + cdev->watched_lines = bitmap_zalloc(gdev->ngpio, GFP_KERNEL); if (!cdev->watched_lines) goto out_free_cdev; @@ -2840,6 +2839,7 @@ static const struct file_operations gpio_fileops = { int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt) { + struct gpio_chip *gc; int ret; cdev_init(&gdev->chrdev, &gpio_fileops); @@ -2850,8 +2850,12 @@ int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt) if (ret) return ret; - chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n", - MAJOR(devt), gdev->id); + guard(srcu)(&gdev->srcu); + gc = srcu_dereference(gdev->chip, &gdev->srcu); + if (!gc) + return -ENODEV; + + chip_dbg(gc, "added GPIO chardev (%d:%d)\n", MAJOR(devt), gdev->id); return 0; } |