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>
This commit is contained in:
David Herrmann 2011-08-26 14:06:02 +02:00 committed by Gustavo F. Padovan
parent a5fd6f3004
commit 794d175698
1 changed files with 20 additions and 14 deletions

View File

@ -255,6 +255,9 @@ static int __hidp_send_ctrl_message(struct hidp_session *session,
BT_DBG("session %p data %p size %d", session, data, size); BT_DBG("session %p data %p size %d", session, data, size);
if (atomic_read(&session->terminate))
return -EIO;
skb = alloc_skb(size + 1, GFP_ATOMIC); skb = alloc_skb(size + 1, GFP_ATOMIC);
if (!skb) { if (!skb) {
BT_ERR("Can't allocate memory for new frame"); BT_ERR("Can't allocate memory for new frame");
@ -329,6 +332,7 @@ static int hidp_get_raw_report(struct hid_device *hid,
struct sk_buff *skb; struct sk_buff *skb;
size_t len; size_t len;
int numbered_reports = hid->report_enum[report_type].numbered; int numbered_reports = hid->report_enum[report_type].numbered;
int ret;
switch (report_type) { switch (report_type) {
case HID_FEATURE_REPORT: case HID_FEATURE_REPORT:
@ -352,8 +356,9 @@ static int hidp_get_raw_report(struct hid_device *hid,
session->waiting_report_number = numbered_reports ? report_number : -1; session->waiting_report_number = numbered_reports ? report_number : -1;
set_bit(HIDP_WAITING_FOR_RETURN, &session->flags); set_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
data[0] = report_number; data[0] = report_number;
if (hidp_send_ctrl_message(hid->driver_data, report_type, data, 1)) ret = hidp_send_ctrl_message(hid->driver_data, report_type, data, 1);
goto err_eio; if (ret)
goto err;
/* Wait for the return of the report. The returned report /* Wait for the return of the report. The returned report
gets put in session->report_return. */ gets put in session->report_return. */
@ -365,11 +370,13 @@ static int hidp_get_raw_report(struct hid_device *hid,
5*HZ); 5*HZ);
if (res == 0) { if (res == 0) {
/* timeout */ /* timeout */
goto err_eio; ret = -EIO;
goto err;
} }
if (res < 0) { if (res < 0) {
/* signal */ /* signal */
goto err_restartsys; ret = -ERESTARTSYS;
goto err;
} }
} }
@ -390,14 +397,10 @@ static int hidp_get_raw_report(struct hid_device *hid,
return len; return len;
err_restartsys: err:
clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
mutex_unlock(&session->report_mutex); mutex_unlock(&session->report_mutex);
return -ERESTARTSYS; return ret;
err_eio:
clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
mutex_unlock(&session->report_mutex);
return -EIO;
} }
static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count, 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
/* Set up our wait, and send the report request to the device. */ /* Set up our wait, and send the report request to the device. */
set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
if (hidp_send_ctrl_message(hid->driver_data, report_type, ret = hidp_send_ctrl_message(hid->driver_data, report_type, data,
data, count)) { count);
ret = -ENOMEM; if (ret)
goto err; goto err;
}
/* Wait for the ACK from the device. */ /* Wait for the ACK from the device. */
while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) { while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) {
@ -739,6 +741,10 @@ static int hidp_session(void *arg)
remove_wait_queue(sk_sleep(intr_sk), &intr_wait); remove_wait_queue(sk_sleep(intr_sk), &intr_wait);
remove_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait); remove_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait);
clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
wake_up_interruptible(&session->report_queue);
down_write(&hidp_session_sem); down_write(&hidp_session_sem);
hidp_del_timer(session); hidp_del_timer(session);