Skip to content

Commit

Permalink
HID: sensor-hub: make dyn_callback_lock IRQ-safe
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jiri Kosina committed Jun 10, 2014
1 parent d6b92c2 commit 0ccf091
Showing 1 changed file with 14 additions and 10 deletions.
24 changes: 14 additions & 10 deletions drivers/hid/hid-sensor-hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -159,25 +159,26 @@ int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev,
{
struct hid_sensor_hub_callbacks_list *callback;
struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev);
unsigned long flags;

spin_lock(&pdata->dyn_callback_lock);
spin_lock_irqsave(&pdata->dyn_callback_lock, flags);
list_for_each_entry(callback, &pdata->dyn_callback_list, list)
if (callback->usage_id == usage_id &&
callback->hsdev == hsdev) {
spin_unlock(&pdata->dyn_callback_lock);
spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags);
return -EINVAL;
}
callback = kzalloc(sizeof(*callback), GFP_ATOMIC);
if (!callback) {
spin_unlock(&pdata->dyn_callback_lock);
spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags);
return -ENOMEM;
}
callback->hsdev = hsdev;
callback->usage_callback = usage_callback;
callback->usage_id = usage_id;
callback->priv = NULL;
list_add_tail(&callback->list, &pdata->dyn_callback_list);
spin_unlock(&pdata->dyn_callback_lock);
spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags);

return 0;
}
Expand All @@ -188,16 +189,17 @@ int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev,
{
struct hid_sensor_hub_callbacks_list *callback;
struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev);
unsigned long flags;

spin_lock(&pdata->dyn_callback_lock);
spin_lock_irqsave(&pdata->dyn_callback_lock, flags);
list_for_each_entry(callback, &pdata->dyn_callback_list, list)
if (callback->usage_id == usage_id &&
callback->hsdev == hsdev) {
list_del(&callback->list);
kfree(callback);
break;
}
spin_unlock(&pdata->dyn_callback_lock);
spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags);

return 0;
}
Expand Down Expand Up @@ -378,15 +380,16 @@ static int sensor_hub_suspend(struct hid_device *hdev, pm_message_t message)
{
struct sensor_hub_data *pdata = hid_get_drvdata(hdev);
struct hid_sensor_hub_callbacks_list *callback;
unsigned long flags;

hid_dbg(hdev, " sensor_hub_suspend\n");
spin_lock(&pdata->dyn_callback_lock);
spin_lock_irqsave(&pdata->dyn_callback_lock, flags);
list_for_each_entry(callback, &pdata->dyn_callback_list, list) {
if (callback->usage_callback->suspend)
callback->usage_callback->suspend(
callback->hsdev, callback->priv);
}
spin_unlock(&pdata->dyn_callback_lock);
spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags);

return 0;
}
Expand All @@ -395,15 +398,16 @@ static int sensor_hub_resume(struct hid_device *hdev)
{
struct sensor_hub_data *pdata = hid_get_drvdata(hdev);
struct hid_sensor_hub_callbacks_list *callback;
unsigned long flags;

hid_dbg(hdev, " sensor_hub_resume\n");
spin_lock(&pdata->dyn_callback_lock);
spin_lock_irqsave(&pdata->dyn_callback_lock, flags);
list_for_each_entry(callback, &pdata->dyn_callback_list, list) {
if (callback->usage_callback->resume)
callback->usage_callback->resume(
callback->hsdev, callback->priv);
}
spin_unlock(&pdata->dyn_callback_lock);
spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags);

return 0;
}
Expand Down

0 comments on commit 0ccf091

Please sign in to comment.