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:
committed by
Greg Kroah-Hartman
parent
2152b417be
commit
3e38cbbfa0
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user