Skip to content

Commit f50f9aa

Browse files
David HerrmannJiri Kosina
authored andcommitted
HID: wiimote: fix FF deadlock
The input core has an internal spinlock that is acquired during event injection via input_event() and friends but also held during FF callbacks. That means, there is no way to share a lock between event-injection and FF handling. Unfortunately, this is what is required for wiimote state tracking and what we do with state.lock and input->lock. This deadlock can be triggered when using continuous data reporting and FF on a wiimote device at the same time. I takes me at least 30m of stress-testing to trigger it but users reported considerably shorter times (http://bpaste.net/show/132504/) when using some gaming-console emulators. The real problem is that we have two copies of internal state, one in the wiimote objects and the other in the input device. As the input-lock is not supposed to be accessed from outside of input-core, we have no other chance than offloading FF handling into a worker. This actually works pretty nice and also allows to implictly merge fast rumble changes into a single request. Due to the 3-layered workers (rumble+queue+l2cap) this might reduce FF responsiveness. Initial tests were fine so lets fix the race first and if it turns out to be too slow we can always handle FF out-of-band and skip the queue-worker. Cc: <[email protected]> # 3.11+ Reported-by: Thomas Schneider Signed-off-by: David Herrmann <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent 7da7cbb commit f50f9aa

File tree

2 files changed

+32
-12
lines changed

2 files changed

+32
-12
lines changed

drivers/hid/hid-wiimote-modules.c

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,22 @@ static const struct wiimod_ops wiimod_keys = {
119119
* the rumble motor, this flag shouldn't be set.
120120
*/
121121

122+
/* used by wiimod_rumble and wiipro_rumble */
123+
static void wiimod_rumble_worker(struct work_struct *work)
124+
{
125+
struct wiimote_data *wdata = container_of(work, struct wiimote_data,
126+
rumble_worker);
127+
128+
spin_lock_irq(&wdata->state.lock);
129+
wiiproto_req_rumble(wdata, wdata->state.cache_rumble);
130+
spin_unlock_irq(&wdata->state.lock);
131+
}
132+
122133
static int wiimod_rumble_play(struct input_dev *dev, void *data,
123134
struct ff_effect *eff)
124135
{
125136
struct wiimote_data *wdata = input_get_drvdata(dev);
126137
__u8 value;
127-
unsigned long flags;
128138

129139
/*
130140
* The wiimote supports only a single rumble motor so if any magnitude
@@ -137,16 +147,19 @@ static int wiimod_rumble_play(struct input_dev *dev, void *data,
137147
else
138148
value = 0;
139149

140-
spin_lock_irqsave(&wdata->state.lock, flags);
141-
wiiproto_req_rumble(wdata, value);
142-
spin_unlock_irqrestore(&wdata->state.lock, flags);
150+
/* Locking state.lock here might deadlock with input_event() calls.
151+
* schedule_work acts as barrier. Merging multiple changes is fine. */
152+
wdata->state.cache_rumble = value;
153+
schedule_work(&wdata->rumble_worker);
143154

144155
return 0;
145156
}
146157

147158
static int wiimod_rumble_probe(const struct wiimod_ops *ops,
148159
struct wiimote_data *wdata)
149160
{
161+
INIT_WORK(&wdata->rumble_worker, wiimod_rumble_worker);
162+
150163
set_bit(FF_RUMBLE, wdata->input->ffbit);
151164
if (input_ff_create_memless(wdata->input, NULL, wiimod_rumble_play))
152165
return -ENOMEM;
@@ -159,6 +172,8 @@ static void wiimod_rumble_remove(const struct wiimod_ops *ops,
159172
{
160173
unsigned long flags;
161174

175+
cancel_work_sync(&wdata->rumble_worker);
176+
162177
spin_lock_irqsave(&wdata->state.lock, flags);
163178
wiiproto_req_rumble(wdata, 0);
164179
spin_unlock_irqrestore(&wdata->state.lock, flags);
@@ -1731,7 +1746,6 @@ static int wiimod_pro_play(struct input_dev *dev, void *data,
17311746
{
17321747
struct wiimote_data *wdata = input_get_drvdata(dev);
17331748
__u8 value;
1734-
unsigned long flags;
17351749

17361750
/*
17371751
* The wiimote supports only a single rumble motor so if any magnitude
@@ -1744,9 +1758,10 @@ static int wiimod_pro_play(struct input_dev *dev, void *data,
17441758
else
17451759
value = 0;
17461760

1747-
spin_lock_irqsave(&wdata->state.lock, flags);
1748-
wiiproto_req_rumble(wdata, value);
1749-
spin_unlock_irqrestore(&wdata->state.lock, flags);
1761+
/* Locking state.lock here might deadlock with input_event() calls.
1762+
* schedule_work acts as barrier. Merging multiple changes is fine. */
1763+
wdata->state.cache_rumble = value;
1764+
schedule_work(&wdata->rumble_worker);
17501765

17511766
return 0;
17521767
}
@@ -1756,6 +1771,8 @@ static int wiimod_pro_probe(const struct wiimod_ops *ops,
17561771
{
17571772
int ret, i;
17581773

1774+
INIT_WORK(&wdata->rumble_worker, wiimod_rumble_worker);
1775+
17591776
wdata->extension.input = input_allocate_device();
17601777
if (!wdata->extension.input)
17611778
return -ENOMEM;
@@ -1817,12 +1834,13 @@ static void wiimod_pro_remove(const struct wiimod_ops *ops,
18171834
if (!wdata->extension.input)
18181835
return;
18191836

1837+
input_unregister_device(wdata->extension.input);
1838+
wdata->extension.input = NULL;
1839+
cancel_work_sync(&wdata->rumble_worker);
1840+
18201841
spin_lock_irqsave(&wdata->state.lock, flags);
18211842
wiiproto_req_rumble(wdata, 0);
18221843
spin_unlock_irqrestore(&wdata->state.lock, flags);
1823-
1824-
input_unregister_device(wdata->extension.input);
1825-
wdata->extension.input = NULL;
18261844
}
18271845

18281846
static const struct wiimod_ops wiimod_pro = {

drivers/hid/hid-wiimote.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,13 +133,15 @@ struct wiimote_state {
133133
__u8 *cmd_read_buf;
134134
__u8 cmd_read_size;
135135

136-
/* calibration data */
136+
/* calibration/cache data */
137137
__u16 calib_bboard[4][3];
138+
__u8 cache_rumble;
138139
};
139140

140141
struct wiimote_data {
141142
struct hid_device *hdev;
142143
struct input_dev *input;
144+
struct work_struct rumble_worker;
143145
struct led_classdev *leds[4];
144146
struct input_dev *accel;
145147
struct input_dev *ir;

0 commit comments

Comments
 (0)