diff options
| author | Jiri Kosina <jkosina@suse.cz> | 2014-06-10 05:05:43 -0400 |
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.cz> | 2014-06-10 05:05:43 -0400 |
| commit | 0ccf091d1fbc1f99bb7f93bff8cf346769a9b0cd (patch) | |
| tree | 6423de601e8ee6eb56bc79403d6a05351e735ade | |
| parent | d6b92c2c373e0beefa8048c1448992cd5cda6e07 (diff) | |
HID: sensor-hub: make dyn_callback_lock IRQ-safe
dyn_callback_lock is being taken from IRQ context through hid_irq_in() ->
hid_input_report() -> sensor_hub_raw_event() -> sensor_hub_get_callback(),
therefore anyone else acquiring it needs to disable IRQs to disable deadlocks.
Reported-by: Alexander Holler <holler@ahsoftware.de>
Tested-by: Alexander Holler <holler@ahsoftware.de>
Reported-by: Reyad Attiyat <reyad.attiyat@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
| -rw-r--r-- | drivers/hid/hid-sensor-hub.c | 24 |
1 files changed, 14 insertions, 10 deletions
diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index a8d5c8faf8cf..13ce4e3aebf4 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c | |||
| @@ -159,17 +159,18 @@ int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev, | |||
| 159 | { | 159 | { |
| 160 | struct hid_sensor_hub_callbacks_list *callback; | 160 | struct hid_sensor_hub_callbacks_list *callback; |
| 161 | struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev); | 161 | struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev); |
| 162 | unsigned long flags; | ||
| 162 | 163 | ||
| 163 | spin_lock(&pdata->dyn_callback_lock); | 164 | spin_lock_irqsave(&pdata->dyn_callback_lock, flags); |
| 164 | list_for_each_entry(callback, &pdata->dyn_callback_list, list) | 165 | list_for_each_entry(callback, &pdata->dyn_callback_list, list) |
| 165 | if (callback->usage_id == usage_id && | 166 | if (callback->usage_id == usage_id && |
| 166 | callback->hsdev == hsdev) { | 167 | callback->hsdev == hsdev) { |
| 167 | spin_unlock(&pdata->dyn_callback_lock); | 168 | spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); |
| 168 | return -EINVAL; | 169 | return -EINVAL; |
| 169 | } | 170 | } |
| 170 | callback = kzalloc(sizeof(*callback), GFP_ATOMIC); | 171 | callback = kzalloc(sizeof(*callback), GFP_ATOMIC); |
| 171 | if (!callback) { | 172 | if (!callback) { |
| 172 | spin_unlock(&pdata->dyn_callback_lock); | 173 | spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); |
| 173 | return -ENOMEM; | 174 | return -ENOMEM; |
| 174 | } | 175 | } |
| 175 | callback->hsdev = hsdev; | 176 | callback->hsdev = hsdev; |
| @@ -177,7 +178,7 @@ int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev, | |||
| 177 | callback->usage_id = usage_id; | 178 | callback->usage_id = usage_id; |
| 178 | callback->priv = NULL; | 179 | callback->priv = NULL; |
| 179 | list_add_tail(&callback->list, &pdata->dyn_callback_list); | 180 | list_add_tail(&callback->list, &pdata->dyn_callback_list); |
| 180 | spin_unlock(&pdata->dyn_callback_lock); | 181 | spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); |
| 181 | 182 | ||
| 182 | return 0; | 183 | return 0; |
| 183 | } | 184 | } |
| @@ -188,8 +189,9 @@ int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev, | |||
| 188 | { | 189 | { |
| 189 | struct hid_sensor_hub_callbacks_list *callback; | 190 | struct hid_sensor_hub_callbacks_list *callback; |
| 190 | struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev); | 191 | struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev); |
| 192 | unsigned long flags; | ||
| 191 | 193 | ||
| 192 | spin_lock(&pdata->dyn_callback_lock); | 194 | spin_lock_irqsave(&pdata->dyn_callback_lock, flags); |
| 193 | list_for_each_entry(callback, &pdata->dyn_callback_list, list) | 195 | list_for_each_entry(callback, &pdata->dyn_callback_list, list) |
| 194 | if (callback->usage_id == usage_id && | 196 | if (callback->usage_id == usage_id && |
| 195 | callback->hsdev == hsdev) { | 197 | callback->hsdev == hsdev) { |
| @@ -197,7 +199,7 @@ int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev, | |||
| 197 | kfree(callback); | 199 | kfree(callback); |
| 198 | break; | 200 | break; |
| 199 | } | 201 | } |
| 200 | spin_unlock(&pdata->dyn_callback_lock); | 202 | spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); |
| 201 | 203 | ||
| 202 | return 0; | 204 | return 0; |
| 203 | } | 205 | } |
| @@ -378,15 +380,16 @@ static int sensor_hub_suspend(struct hid_device *hdev, pm_message_t message) | |||
| 378 | { | 380 | { |
| 379 | struct sensor_hub_data *pdata = hid_get_drvdata(hdev); | 381 | struct sensor_hub_data *pdata = hid_get_drvdata(hdev); |
| 380 | struct hid_sensor_hub_callbacks_list *callback; | 382 | struct hid_sensor_hub_callbacks_list *callback; |
| 383 | unsigned long flags; | ||
| 381 | 384 | ||
| 382 | hid_dbg(hdev, " sensor_hub_suspend\n"); | 385 | hid_dbg(hdev, " sensor_hub_suspend\n"); |
| 383 | spin_lock(&pdata->dyn_callback_lock); | 386 | spin_lock_irqsave(&pdata->dyn_callback_lock, flags); |
| 384 | list_for_each_entry(callback, &pdata->dyn_callback_list, list) { | 387 | list_for_each_entry(callback, &pdata->dyn_callback_list, list) { |
| 385 | if (callback->usage_callback->suspend) | 388 | if (callback->usage_callback->suspend) |
| 386 | callback->usage_callback->suspend( | 389 | callback->usage_callback->suspend( |
| 387 | callback->hsdev, callback->priv); | 390 | callback->hsdev, callback->priv); |
| 388 | } | 391 | } |
| 389 | spin_unlock(&pdata->dyn_callback_lock); | 392 | spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); |
| 390 | 393 | ||
| 391 | return 0; | 394 | return 0; |
| 392 | } | 395 | } |
| @@ -395,15 +398,16 @@ static int sensor_hub_resume(struct hid_device *hdev) | |||
| 395 | { | 398 | { |
| 396 | struct sensor_hub_data *pdata = hid_get_drvdata(hdev); | 399 | struct sensor_hub_data *pdata = hid_get_drvdata(hdev); |
| 397 | struct hid_sensor_hub_callbacks_list *callback; | 400 | struct hid_sensor_hub_callbacks_list *callback; |
| 401 | unsigned long flags; | ||
| 398 | 402 | ||
| 399 | hid_dbg(hdev, " sensor_hub_resume\n"); | 403 | hid_dbg(hdev, " sensor_hub_resume\n"); |
| 400 | spin_lock(&pdata->dyn_callback_lock); | 404 | spin_lock_irqsave(&pdata->dyn_callback_lock, flags); |
| 401 | list_for_each_entry(callback, &pdata->dyn_callback_list, list) { | 405 | list_for_each_entry(callback, &pdata->dyn_callback_list, list) { |
| 402 | if (callback->usage_callback->resume) | 406 | if (callback->usage_callback->resume) |
| 403 | callback->usage_callback->resume( | 407 | callback->usage_callback->resume( |
| 404 | callback->hsdev, callback->priv); | 408 | callback->hsdev, callback->priv); |
| 405 | } | 409 | } |
| 406 | spin_unlock(&pdata->dyn_callback_lock); | 410 | spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); |
| 407 | 411 | ||
| 408 | return 0; | 412 | return 0; |
| 409 | } | 413 | } |
