HID: hid-steam: Move hidraw input (un)registering to work

[ Upstream commit 79504249d7e27cad4a3eeb9afc6386e418728ce0 ]

Due to an interplay between locking in the input and hid transport subsystems,
attempting to register or deregister the relevant input devices during the
hidraw open/close events can lead to a lock ordering issue. Though this
shouldn't cause a deadlock, this commit moves the input device manipulation to
deferred work to sidestep the issue.

Fixes: 385a488677 ("HID: steam: remove input device when a hid client is running.")
Signed-off-by: Vicki Pfau <vi@endrift.com>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Vicki Pfau
2025-02-04 19:55:27 -08:00
committed by Greg Kroah-Hartman
parent 2152b417be
commit 3e38cbbfa0

View File

@@ -313,6 +313,7 @@ struct steam_device {
u16 rumble_left;
u16 rumble_right;
unsigned int sensor_timestamp_us;
struct work_struct unregister_work;
};
static int steam_recv_report(struct steam_device *steam,
@@ -1072,6 +1073,31 @@ static void steam_mode_switch_cb(struct work_struct *work)
}
}
static void steam_work_unregister_cb(struct work_struct *work)
{
struct steam_device *steam = container_of(work, struct steam_device,
unregister_work);
unsigned long flags;
bool connected;
bool opened;
spin_lock_irqsave(&steam->lock, flags);
opened = steam->client_opened;
connected = steam->connected;
spin_unlock_irqrestore(&steam->lock, flags);
if (connected) {
if (opened) {
steam_sensors_unregister(steam);
steam_input_unregister(steam);
} else {
steam_set_lizard_mode(steam, lizard_mode);
steam_input_register(steam);
steam_sensors_register(steam);
}
}
}
static bool steam_is_valve_interface(struct hid_device *hdev)
{
struct hid_report_enum *rep_enum;
@@ -1117,8 +1143,7 @@ static int steam_client_ll_open(struct hid_device *hdev)
steam->client_opened++;
spin_unlock_irqrestore(&steam->lock, flags);
steam_sensors_unregister(steam);
steam_input_unregister(steam);
schedule_work(&steam->unregister_work);
return 0;
}
@@ -1135,11 +1160,7 @@ static void steam_client_ll_close(struct hid_device *hdev)
connected = steam->connected && !steam->client_opened;
spin_unlock_irqrestore(&steam->lock, flags);
if (connected) {
steam_set_lizard_mode(steam, lizard_mode);
steam_input_register(steam);
steam_sensors_register(steam);
}
schedule_work(&steam->unregister_work);
}
static int steam_client_ll_raw_request(struct hid_device *hdev,
@@ -1231,6 +1252,7 @@ static int steam_probe(struct hid_device *hdev,
INIT_LIST_HEAD(&steam->list);
INIT_WORK(&steam->rumble_work, steam_haptic_rumble_cb);
steam->sensor_timestamp_us = 0;
INIT_WORK(&steam->unregister_work, steam_work_unregister_cb);
/*
* With the real steam controller interface, do not connect hidraw.
@@ -1291,6 +1313,7 @@ err_cancel_work:
cancel_work_sync(&steam->work_connect);
cancel_delayed_work_sync(&steam->mode_switch);
cancel_work_sync(&steam->rumble_work);
cancel_work_sync(&steam->unregister_work);
return ret;
}
@@ -1307,6 +1330,7 @@ static void steam_remove(struct hid_device *hdev)
cancel_delayed_work_sync(&steam->mode_switch);
cancel_work_sync(&steam->work_connect);
cancel_work_sync(&steam->rumble_work);
cancel_work_sync(&steam->unregister_work);
hid_destroy_device(steam->client_hdev);
steam->client_hdev = NULL;
steam->client_opened = 0;