Commit ea53fdd4 authored by Vicki Pfau's avatar Vicki Pfau Committed by Wentao Guan
Browse files

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

stable inclusion
from stable-v6.6.79
commit e1147961b2145fa61c3078a4a797d9576cde91ab
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/IBXANC

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=e1147961b2145fa61c3078a4a797d9576cde91ab



--------------------------------

[ 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: 385a4886 ("HID: steam: remove input device when a hid client is running.")
Signed-off-by: default avatarVicki Pfau <vi@endrift.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.com>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
(cherry picked from commit e1147961b2145fa61c3078a4a797d9576cde91ab)
Signed-off-by: default avatarWentao Guan <guanwentao@uniontech.com>
parent 21e195d2
Loading
Loading
Loading
Loading
+31 −7
Original line number Diff line number Diff line
@@ -312,6 +312,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,
@@ -1070,6 +1071,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;
@@ -1115,8 +1141,7 @@ static int steam_client_ll_open(struct hid_device *hdev)
	steam->client_opened = true;
	spin_unlock_irqrestore(&steam->lock, flags);

	steam_sensors_unregister(steam);
	steam_input_unregister(steam);
	schedule_work(&steam->unregister_work);

	return 0;
}
@@ -1133,11 +1158,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,
@@ -1229,6 +1250,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.
@@ -1289,6 +1311,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;
}
@@ -1305,6 +1328,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 = false;