Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+14 −10
Original line number Diff line number Diff line
@@ -159,17 +159,18 @@ 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;
@@ -177,7 +178,7 @@ int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev,
	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;
}
@@ -188,8 +189,9 @@ 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) {
@@ -197,7 +199,7 @@ int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev,
			kfree(callback);
			break;
		}
	spin_unlock(&pdata->dyn_callback_lock);
	spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags);

	return 0;
}
@@ -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;
}
@@ -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;
}