Skip to content

Commit d4abf87

Browse files
committed
tmff2,t500rs: route FF_GAIN/FF_AUTOCENTER via base workqueue; drop per-wheel worker; fix "scheduling while atomic"
The T500RS backend still had its own workqueue for gain and autocenter. The maintainer requested to completely remove the per-wheel workqueue and use the base driver workqueue instead. While playing a game we also hit a WARN ("scheduling while atomic") when FF_GAIN was updated: input_ff_event() runs in an RCU read-side section, but the T500RS callbacks called usb_interrupt_msg(), which can sleep. This change centralizes the deferral in the core (tmff2) and removes the extra worker from the wheel backend: - tmff2: add pending Gain/Autocenter fields and flags; have tmff2_set_gain()/tmff2_set_autocenter() only cache the values under the spinlock and schedule the existing delayed_work. The actual USB I/O is performed from tmff2_work_handler() in process context after the lock is released. The sysfs gain store is routed through the same mechanism so everything converges on a single path. - t500rs: drop the per-wheel workqueue and any related helpers; use the shared send_buffer and let the base driver workqueue perform the USB interrupt transfer. Also remove a now-unused helper (t500rs_do_set_gain_byte) and minor cleanups. Why: - Satisfies the maintainer request to remove the extra workqueue and reuse the base driver workqueue. - Fixes the "scheduling while atomic" warning by ensuring that gain and autocenter packets are always sent from process context. - Reduces duplication and keeps all FF deferral logic in one place. Notes: - Effect handlers and the rest of the USB packet layout are unchanged. - Logging behaviour is unchanged; follow-ups can add rate limiting if needed. Build/validate: - Builds cleanly (no warnings). - Smoke-tested: module reloads, wheel init OK, no WARN/OOPS when a game changes FF gain/autocenter. MSG
1 parent dd21177 commit d4abf87

File tree

4 files changed

+117
-284
lines changed

4 files changed

+117
-284
lines changed

docs/T500RS-async-refactor-plan.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,5 +83,5 @@ Goal: Call wheel callbacks from tmff2_work_handler outside of tmff2->lock so the
8383

8484
---
8585

86-
Status: minimal change (global high‑pri workqueue) implemented; long‑term core split remains TODO.
86+
Status: core work handler now performs I/O outside spinlock (commit 5095d47); T500RS now performs synchronous USB I/O directly; the previous per-device op queue/worker has been removed.
8787

src/hid-tmff2.c

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,15 @@ static ssize_t gain_store(struct device *dev,
258258
}
259259

260260
gain = value;
261-
if (tmff2->set_gain) /* if we can, update gain immediately */
262-
tmff2->set_gain(tmff2->data, (GAIN_MAX * gain) / GAIN_MAX);
261+
if (tmff2->set_gain) {
262+
unsigned long flags;
263+
spin_lock_irqsave(&tmff2->lock, flags);
264+
tmff2->pending_gain_value = GAIN_MAX;
265+
tmff2->gain_pending = 1;
266+
spin_unlock_irqrestore(&tmff2->lock, flags);
267+
if (!delayed_work_pending(&tmff2->work) && tmff2->allow_scheduling)
268+
schedule_delayed_work(&tmff2->work, 0);
269+
}
263270

264271
return count;
265272
}
@@ -274,6 +281,7 @@ static DEVICE_ATTR_RW(gain);
274281
static void tmff2_set_gain(struct input_dev *dev, uint16_t value)
275282
{
276283
struct tmff2_device_entry *tmff2 = tmff2_from_input(dev);
284+
unsigned long flags;
277285

278286
if (!tmff2)
279287
return;
@@ -283,13 +291,20 @@ static void tmff2_set_gain(struct input_dev *dev, uint16_t value)
283291
return;
284292
}
285293

286-
if (tmff2->set_gain(tmff2->data, (value * gain) / GAIN_MAX))
287-
hid_warn(tmff2->hdev, "unable to set gain\n");
294+
/* Defer to workqueue: store pending gain and schedule */
295+
spin_lock_irqsave(&tmff2->lock, flags);
296+
tmff2->pending_gain_value = value;
297+
tmff2->gain_pending = 1;
298+
spin_unlock_irqrestore(&tmff2->lock, flags);
299+
300+
if (!delayed_work_pending(&tmff2->work) && tmff2->allow_scheduling)
301+
schedule_delayed_work(&tmff2->work, 0);
288302
}
289303

290304
static void tmff2_set_autocenter(struct input_dev *dev, uint16_t value)
291305
{
292306
struct tmff2_device_entry *tmff2 = tmff2_from_input(dev);
307+
unsigned long flags;
293308

294309
if (!tmff2)
295310
return;
@@ -299,8 +314,14 @@ static void tmff2_set_autocenter(struct input_dev *dev, uint16_t value)
299314
return;
300315
}
301316

302-
if (tmff2->set_autocenter(tmff2->data, value))
303-
hid_warn(tmff2->hdev, "unable to set autocenter\n");
317+
/* Defer to workqueue: store pending autocenter and schedule */
318+
spin_lock_irqsave(&tmff2->lock, flags);
319+
tmff2->pending_autocenter_value = value;
320+
tmff2->autocenter_pending = 1;
321+
spin_unlock_irqrestore(&tmff2->lock, flags);
322+
323+
if (!delayed_work_pending(&tmff2->work) && tmff2->allow_scheduling)
324+
schedule_delayed_work(&tmff2->work, 0);
304325
}
305326

306327
static void tmff2_work_handler(struct work_struct *w)
@@ -317,6 +338,30 @@ static void tmff2_work_handler(struct work_struct *w)
317338
if (!tmff2)
318339
return;
319340

341+
/* Apply pending control changes (gain/autocenter) in process context */
342+
{
343+
unsigned long f2;
344+
uint16_t pg = 0, pac = 0;
345+
int do_gain = 0, do_ac = 0;
346+
spin_lock_irqsave(&tmff2->lock, f2);
347+
if (tmff2->gain_pending) {
348+
pg = tmff2->pending_gain_value;
349+
tmff2->gain_pending = 0;
350+
do_gain = 1;
351+
}
352+
if (tmff2->autocenter_pending) {
353+
pac = tmff2->pending_autocenter_value;
354+
tmff2->autocenter_pending = 0;
355+
do_ac = 1;
356+
}
357+
spin_unlock_irqrestore(&tmff2->lock, f2);
358+
359+
if (do_gain && tmff2->set_gain)
360+
tmff2->set_gain(tmff2->data, (pg * gain) / GAIN_MAX);
361+
if (do_ac && tmff2->set_autocenter)
362+
tmff2->set_autocenter(tmff2->data, pac);
363+
}
364+
320365
for (effect_id = 0; effect_id < tmff2->max_effects; ++effect_id) {
321366
unsigned long actions = 0;
322367
struct tmff2_effect_state effect;

src/hid-tmff2.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ struct tmff2_device_entry {
6464

6565
spinlock_t lock;
6666

67+
/* Pending control changes to be applied from workqueue context */
68+
uint16_t pending_gain_value;
69+
uint16_t pending_autocenter_value;
70+
int gain_pending;
71+
int autocenter_pending;
72+
6773
int allow_scheduling;
6874

6975
/* fields relevant to each actual device (T300, T248...) */

0 commit comments

Comments
 (0)