diff options
author | David Herrmann <dh.herrmann@googlemail.com> | 2011-08-26 08:06:02 -0400 |
---|---|---|
committer | Gustavo F. Padovan <padovan@profusion.mobi> | 2011-10-06 21:15:48 -0400 |
commit | 794d175698f0e78be7f2e3f4bdbe0e7cd3f2d6ae (patch) | |
tree | e96dcec6fc0ee32252ef51214348fa0066b1f637 /net | |
parent | a5fd6f300433ef7458c6d934f81f47ebd7c7e805 (diff) |
Bluetooth: hidp: Stop I/O on shutdown
Current hidp driver purges the in/out queue on HID shutdown, but does
not prevent further I/O. If a driver uses hidp_output_raw_report or
hidp_get_raw_report during shutdown, the driver hangs for 5 or 10
seconds per call until it gets a timeout.
That is, if the output queue of an HID driver has 10 messages pending,
it will take 50s until hid_destroy_device() will return. The
hidp_session_sem semaphore is held during shutdown so no other HID
device may be added/removed during this time.
This patch makes hidp_output_raw_report and hidp_get_raw_report fail if
session->terminate is true. Also hidp_session will wakeup all current
calls to these functions to cancel the current operations.
We already purge the current I/O queues on hidp_stop(), so this data loss
does not change the behaviour of the HID drivers.
Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
Diffstat (limited to 'net')
-rw-r--r-- | net/bluetooth/hidp/core.c | 34 |
1 files changed, 20 insertions, 14 deletions
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 075a3e920caf..d7bae2be83b2 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c | |||
@@ -255,6 +255,9 @@ static int __hidp_send_ctrl_message(struct hidp_session *session, | |||
255 | 255 | ||
256 | BT_DBG("session %p data %p size %d", session, data, size); | 256 | BT_DBG("session %p data %p size %d", session, data, size); |
257 | 257 | ||
258 | if (atomic_read(&session->terminate)) | ||
259 | return -EIO; | ||
260 | |||
258 | skb = alloc_skb(size + 1, GFP_ATOMIC); | 261 | skb = alloc_skb(size + 1, GFP_ATOMIC); |
259 | if (!skb) { | 262 | if (!skb) { |
260 | BT_ERR("Can't allocate memory for new frame"); | 263 | BT_ERR("Can't allocate memory for new frame"); |
@@ -329,6 +332,7 @@ static int hidp_get_raw_report(struct hid_device *hid, | |||
329 | struct sk_buff *skb; | 332 | struct sk_buff *skb; |
330 | size_t len; | 333 | size_t len; |
331 | int numbered_reports = hid->report_enum[report_type].numbered; | 334 | int numbered_reports = hid->report_enum[report_type].numbered; |
335 | int ret; | ||
332 | 336 | ||
333 | switch (report_type) { | 337 | switch (report_type) { |
334 | case HID_FEATURE_REPORT: | 338 | case HID_FEATURE_REPORT: |
@@ -352,8 +356,9 @@ static int hidp_get_raw_report(struct hid_device *hid, | |||
352 | session->waiting_report_number = numbered_reports ? report_number : -1; | 356 | session->waiting_report_number = numbered_reports ? report_number : -1; |
353 | set_bit(HIDP_WAITING_FOR_RETURN, &session->flags); | 357 | set_bit(HIDP_WAITING_FOR_RETURN, &session->flags); |
354 | data[0] = report_number; | 358 | data[0] = report_number; |
355 | if (hidp_send_ctrl_message(hid->driver_data, report_type, data, 1)) | 359 | ret = hidp_send_ctrl_message(hid->driver_data, report_type, data, 1); |
356 | goto err_eio; | 360 | if (ret) |
361 | goto err; | ||
357 | 362 | ||
358 | /* Wait for the return of the report. The returned report | 363 | /* Wait for the return of the report. The returned report |
359 | gets put in session->report_return. */ | 364 | gets put in session->report_return. */ |
@@ -365,11 +370,13 @@ static int hidp_get_raw_report(struct hid_device *hid, | |||
365 | 5*HZ); | 370 | 5*HZ); |
366 | if (res == 0) { | 371 | if (res == 0) { |
367 | /* timeout */ | 372 | /* timeout */ |
368 | goto err_eio; | 373 | ret = -EIO; |
374 | goto err; | ||
369 | } | 375 | } |
370 | if (res < 0) { | 376 | if (res < 0) { |
371 | /* signal */ | 377 | /* signal */ |
372 | goto err_restartsys; | 378 | ret = -ERESTARTSYS; |
379 | goto err; | ||
373 | } | 380 | } |
374 | } | 381 | } |
375 | 382 | ||
@@ -390,14 +397,10 @@ static int hidp_get_raw_report(struct hid_device *hid, | |||
390 | 397 | ||
391 | return len; | 398 | return len; |
392 | 399 | ||
393 | err_restartsys: | 400 | err: |
394 | clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); | ||
395 | mutex_unlock(&session->report_mutex); | ||
396 | return -ERESTARTSYS; | ||
397 | err_eio: | ||
398 | clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); | 401 | clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); |
399 | mutex_unlock(&session->report_mutex); | 402 | mutex_unlock(&session->report_mutex); |
400 | return -EIO; | 403 | return ret; |
401 | } | 404 | } |
402 | 405 | ||
403 | static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count, | 406 | static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count, |
@@ -422,11 +425,10 @@ static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, s | |||
422 | 425 | ||
423 | /* Set up our wait, and send the report request to the device. */ | 426 | /* Set up our wait, and send the report request to the device. */ |
424 | set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); | 427 | set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); |
425 | if (hidp_send_ctrl_message(hid->driver_data, report_type, | 428 | ret = hidp_send_ctrl_message(hid->driver_data, report_type, data, |
426 | data, count)) { | 429 | count); |
427 | ret = -ENOMEM; | 430 | if (ret) |
428 | goto err; | 431 | goto err; |
429 | } | ||
430 | 432 | ||
431 | /* Wait for the ACK from the device. */ | 433 | /* Wait for the ACK from the device. */ |
432 | while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) { | 434 | while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) { |
@@ -739,6 +741,10 @@ static int hidp_session(void *arg) | |||
739 | remove_wait_queue(sk_sleep(intr_sk), &intr_wait); | 741 | remove_wait_queue(sk_sleep(intr_sk), &intr_wait); |
740 | remove_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait); | 742 | remove_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait); |
741 | 743 | ||
744 | clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); | ||
745 | clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); | ||
746 | wake_up_interruptible(&session->report_queue); | ||
747 | |||
742 | down_write(&hidp_session_sem); | 748 | down_write(&hidp_session_sem); |
743 | 749 | ||
744 | hidp_del_timer(session); | 750 | hidp_del_timer(session); |