Skip to content

Commit

Permalink
HID: hid-steam: Move hidraw input (un)registering to work
Browse files Browse the repository at this point in the history
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: 385a488 ("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>
  • Loading branch information
Vicki Pfau authored and Jiri Kosina committed Feb 7, 2025
1 parent 0b43d98 commit 7950424
Showing 1 changed file with 31 additions and 7 deletions.
38 changes: 31 additions & 7 deletions drivers/hid/hid-steam.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand All @@ -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,
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -1291,6 +1313,7 @@ static int steam_probe(struct hid_device *hdev,
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;
}
Expand All @@ -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;
Expand Down

0 comments on commit 7950424

Please sign in to comment.