| From 7a7b5df84b6b4e5d599c7289526eed96541a0654 Mon Sep 17 00:00:00 2001 |
| From: Johan Hovold <johan@kernel.org> |
| Date: Mon, 30 Jan 2017 11:26:38 +0100 |
| Subject: HID: cp2112: fix sleep-while-atomic |
| |
| From: Johan Hovold <johan@kernel.org> |
| |
| commit 7a7b5df84b6b4e5d599c7289526eed96541a0654 upstream. |
| |
| A recent commit fixing DMA-buffers on stack added a shared transfer |
| buffer protected by a spinlock. This is broken as the USB HID request |
| callbacks can sleep. Fix this up by replacing the spinlock with a mutex. |
| |
| Fixes: 1ffb3c40ffb5 ("HID: cp2112: make transfer buffers DMA capable") |
| Signed-off-by: Johan Hovold <johan@kernel.org> |
| Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> |
| Signed-off-by: Jiri Kosina <jkosina@suse.cz> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/hid/hid-cp2112.c | 26 +++++++++++--------------- |
| 1 file changed, 11 insertions(+), 15 deletions(-) |
| |
| --- a/drivers/hid/hid-cp2112.c |
| +++ b/drivers/hid/hid-cp2112.c |
| @@ -167,7 +167,7 @@ struct cp2112_device { |
| atomic_t xfer_avail; |
| struct gpio_chip gc; |
| u8 *in_out_buffer; |
| - spinlock_t lock; |
| + struct mutex lock; |
| }; |
| |
| static int gpio_push_pull = 0xFF; |
| @@ -179,10 +179,9 @@ static int cp2112_gpio_direction_input(s |
| struct cp2112_device *dev = gpiochip_get_data(chip); |
| struct hid_device *hdev = dev->hdev; |
| u8 *buf = dev->in_out_buffer; |
| - unsigned long flags; |
| int ret; |
| |
| - spin_lock_irqsave(&dev->lock, flags); |
| + mutex_lock(&dev->lock); |
| |
| ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, |
| CP2112_GPIO_CONFIG_LENGTH, HID_FEATURE_REPORT, |
| @@ -206,7 +205,7 @@ static int cp2112_gpio_direction_input(s |
| ret = 0; |
| |
| exit: |
| - spin_unlock_irqrestore(&dev->lock, flags); |
| + mutex_unlock(&dev->lock); |
| return ret <= 0 ? ret : -EIO; |
| } |
| |
| @@ -215,10 +214,9 @@ static void cp2112_gpio_set(struct gpio_ |
| struct cp2112_device *dev = gpiochip_get_data(chip); |
| struct hid_device *hdev = dev->hdev; |
| u8 *buf = dev->in_out_buffer; |
| - unsigned long flags; |
| int ret; |
| |
| - spin_lock_irqsave(&dev->lock, flags); |
| + mutex_lock(&dev->lock); |
| |
| buf[0] = CP2112_GPIO_SET; |
| buf[1] = value ? 0xff : 0; |
| @@ -230,7 +228,7 @@ static void cp2112_gpio_set(struct gpio_ |
| if (ret < 0) |
| hid_err(hdev, "error setting GPIO values: %d\n", ret); |
| |
| - spin_unlock_irqrestore(&dev->lock, flags); |
| + mutex_unlock(&dev->lock); |
| } |
| |
| static int cp2112_gpio_get(struct gpio_chip *chip, unsigned offset) |
| @@ -238,10 +236,9 @@ static int cp2112_gpio_get(struct gpio_c |
| struct cp2112_device *dev = gpiochip_get_data(chip); |
| struct hid_device *hdev = dev->hdev; |
| u8 *buf = dev->in_out_buffer; |
| - unsigned long flags; |
| int ret; |
| |
| - spin_lock_irqsave(&dev->lock, flags); |
| + mutex_lock(&dev->lock); |
| |
| ret = hid_hw_raw_request(hdev, CP2112_GPIO_GET, buf, |
| CP2112_GPIO_GET_LENGTH, HID_FEATURE_REPORT, |
| @@ -255,7 +252,7 @@ static int cp2112_gpio_get(struct gpio_c |
| ret = (buf[1] >> offset) & 1; |
| |
| exit: |
| - spin_unlock_irqrestore(&dev->lock, flags); |
| + mutex_unlock(&dev->lock); |
| |
| return ret; |
| } |
| @@ -266,10 +263,9 @@ static int cp2112_gpio_direction_output( |
| struct cp2112_device *dev = gpiochip_get_data(chip); |
| struct hid_device *hdev = dev->hdev; |
| u8 *buf = dev->in_out_buffer; |
| - unsigned long flags; |
| int ret; |
| |
| - spin_lock_irqsave(&dev->lock, flags); |
| + mutex_lock(&dev->lock); |
| |
| ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, |
| CP2112_GPIO_CONFIG_LENGTH, HID_FEATURE_REPORT, |
| @@ -290,7 +286,7 @@ static int cp2112_gpio_direction_output( |
| goto fail; |
| } |
| |
| - spin_unlock_irqrestore(&dev->lock, flags); |
| + mutex_unlock(&dev->lock); |
| |
| /* |
| * Set gpio value when output direction is already set, |
| @@ -301,7 +297,7 @@ static int cp2112_gpio_direction_output( |
| return 0; |
| |
| fail: |
| - spin_unlock_irqrestore(&dev->lock, flags); |
| + mutex_unlock(&dev->lock); |
| return ret < 0 ? ret : -EIO; |
| } |
| |
| @@ -1057,7 +1053,7 @@ static int cp2112_probe(struct hid_devic |
| if (!dev->in_out_buffer) |
| return -ENOMEM; |
| |
| - spin_lock_init(&dev->lock); |
| + mutex_init(&dev->lock); |
| |
| ret = hid_parse(hdev); |
| if (ret) { |