|
14 | 14 | #include <linux/idr.h>
|
15 | 15 | #include <linux/interrupt.h>
|
16 | 16 | #include <linux/irq.h>
|
| 17 | +#include <linux/irqdesc.h> |
17 | 18 | #include <linux/kernel.h>
|
18 | 19 | #include <linux/list.h>
|
19 | 20 | #include <linux/lockdep.h>
|
@@ -713,6 +714,45 @@ bool gpiochip_line_is_valid(const struct gpio_chip *gc,
|
713 | 714 | }
|
714 | 715 | EXPORT_SYMBOL_GPL(gpiochip_line_is_valid);
|
715 | 716 |
|
| 717 | +static void gpiod_free_irqs(struct gpio_desc *desc) |
| 718 | +{ |
| 719 | + int irq = gpiod_to_irq(desc); |
| 720 | + struct irq_desc *irqd = irq_to_desc(irq); |
| 721 | + void *cookie; |
| 722 | + |
| 723 | + for (;;) { |
| 724 | + /* |
| 725 | + * Make sure the action doesn't go away while we're |
| 726 | + * dereferencing it. Retrieve and store the cookie value. |
| 727 | + * If the irq is freed after we release the lock, that's |
| 728 | + * alright - the underlying maple tree lookup will return NULL |
| 729 | + * and nothing will happen in free_irq(). |
| 730 | + */ |
| 731 | + scoped_guard(mutex, &irqd->request_mutex) { |
| 732 | + if (!irq_desc_has_action(irqd)) |
| 733 | + return; |
| 734 | + |
| 735 | + cookie = irqd->action->dev_id; |
| 736 | + } |
| 737 | + |
| 738 | + free_irq(irq, cookie); |
| 739 | + } |
| 740 | +} |
| 741 | + |
| 742 | +/* |
| 743 | + * The chip is going away but there may be users who had requested interrupts |
| 744 | + * on its GPIO lines who have no idea about its removal and have no way of |
| 745 | + * being notified about it. We need to free any interrupts still in use here or |
| 746 | + * we'll leak memory and resources (like procfs files). |
| 747 | + */ |
| 748 | +static void gpiochip_free_remaining_irqs(struct gpio_chip *gc) |
| 749 | +{ |
| 750 | + struct gpio_desc *desc; |
| 751 | + |
| 752 | + for_each_gpio_desc_with_flag(gc, desc, FLAG_USED_AS_IRQ) |
| 753 | + gpiod_free_irqs(desc); |
| 754 | +} |
| 755 | + |
716 | 756 | static void gpiodev_release(struct device *dev)
|
717 | 757 | {
|
718 | 758 | struct gpio_device *gdev = to_gpio_device(dev);
|
@@ -1125,6 +1165,7 @@ void gpiochip_remove(struct gpio_chip *gc)
|
1125 | 1165 | /* FIXME: should the legacy sysfs handling be moved to gpio_device? */
|
1126 | 1166 | gpiochip_sysfs_unregister(gdev);
|
1127 | 1167 | gpiochip_free_hogs(gc);
|
| 1168 | + gpiochip_free_remaining_irqs(gc); |
1128 | 1169 |
|
1129 | 1170 | scoped_guard(mutex, &gpio_devices_lock)
|
1130 | 1171 | list_del_rcu(&gdev->list);
|
|
0 commit comments