Commit 0ccf091d authored by Jiri Kosina's avatar Jiri Kosina

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: default avatarAlexander Holler <holler@ahsoftware.de>
Tested-by: default avatarAlexander Holler <holler@ahsoftware.de>
Reported-by: default avatarReyad Attiyat <reyad.attiyat@gmail.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent d6b92c2c
...@@ -159,17 +159,18 @@ int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev, ...@@ -159,17 +159,18 @@ int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev,
{ {
struct hid_sensor_hub_callbacks_list *callback; struct hid_sensor_hub_callbacks_list *callback;
struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev); 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) list_for_each_entry(callback, &pdata->dyn_callback_list, list)
if (callback->usage_id == usage_id && if (callback->usage_id == usage_id &&
callback->hsdev == hsdev) { callback->hsdev == hsdev) {
spin_unlock(&pdata->dyn_callback_lock); spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags);
return -EINVAL; return -EINVAL;
} }
callback = kzalloc(sizeof(*callback), GFP_ATOMIC); callback = kzalloc(sizeof(*callback), GFP_ATOMIC);
if (!callback) { if (!callback) {
spin_unlock(&pdata->dyn_callback_lock); spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags);
return -ENOMEM; return -ENOMEM;
} }
callback->hsdev = hsdev; callback->hsdev = hsdev;
...@@ -177,7 +178,7 @@ int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev, ...@@ -177,7 +178,7 @@ int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev,
callback->usage_id = usage_id; callback->usage_id = usage_id;
callback->priv = NULL; callback->priv = NULL;
list_add_tail(&callback->list, &pdata->dyn_callback_list); 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; return 0;
} }
...@@ -188,8 +189,9 @@ int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev, ...@@ -188,8 +189,9 @@ int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev,
{ {
struct hid_sensor_hub_callbacks_list *callback; struct hid_sensor_hub_callbacks_list *callback;
struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev); 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) list_for_each_entry(callback, &pdata->dyn_callback_list, list)
if (callback->usage_id == usage_id && if (callback->usage_id == usage_id &&
callback->hsdev == hsdev) { callback->hsdev == hsdev) {
...@@ -197,7 +199,7 @@ int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev, ...@@ -197,7 +199,7 @@ int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev,
kfree(callback); kfree(callback);
break; break;
} }
spin_unlock(&pdata->dyn_callback_lock); spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags);
return 0; return 0;
} }
...@@ -378,15 +380,16 @@ static int sensor_hub_suspend(struct hid_device *hdev, pm_message_t message) ...@@ -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 sensor_hub_data *pdata = hid_get_drvdata(hdev);
struct hid_sensor_hub_callbacks_list *callback; struct hid_sensor_hub_callbacks_list *callback;
unsigned long flags;
hid_dbg(hdev, " sensor_hub_suspend\n"); 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) { list_for_each_entry(callback, &pdata->dyn_callback_list, list) {
if (callback->usage_callback->suspend) if (callback->usage_callback->suspend)
callback->usage_callback->suspend( callback->usage_callback->suspend(
callback->hsdev, callback->priv); callback->hsdev, callback->priv);
} }
spin_unlock(&pdata->dyn_callback_lock); spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags);
return 0; return 0;
} }
...@@ -395,15 +398,16 @@ static int sensor_hub_resume(struct hid_device *hdev) ...@@ -395,15 +398,16 @@ static int sensor_hub_resume(struct hid_device *hdev)
{ {
struct sensor_hub_data *pdata = hid_get_drvdata(hdev); struct sensor_hub_data *pdata = hid_get_drvdata(hdev);
struct hid_sensor_hub_callbacks_list *callback; struct hid_sensor_hub_callbacks_list *callback;
unsigned long flags;
hid_dbg(hdev, " sensor_hub_resume\n"); 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) { list_for_each_entry(callback, &pdata->dyn_callback_list, list) {
if (callback->usage_callback->resume) if (callback->usage_callback->resume)
callback->usage_callback->resume( callback->usage_callback->resume(
callback->hsdev, callback->priv); callback->hsdev, callback->priv);
} }
spin_unlock(&pdata->dyn_callback_lock); spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment