]> www.infradead.org Git - users/hch/misc.git/commitdiff
HID: hid-steam: Move hidraw input (un)registering to work
authorVicki Pfau <vi@endrift.com>
Wed, 5 Feb 2025 03:55:27 +0000 (19:55 -0800)
committerJiri Kosina <jkosina@suse.com>
Fri, 7 Feb 2025 13:27:48 +0000 (14:27 +0100)
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: 385a4886778f6 ("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>
drivers/hid/hid-steam.c

index b008fd0834b94cdbeaa98ee33dd321bfb73c8ac9..5a17714fedea0f676e3207a4d0902e5a6100c036 100644 (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;