summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Hurley <peter@hurleysoftware.com>2011-06-30 13:53:53 -0400
committerGustavo F. Padovan <padovan@profusion.mobi>2011-06-30 15:47:50 -0300
commit7bb59df83b190817f56c4e2fec3078d99d906ad4 (patch)
tree371add6842b2620dbedad6d4d240681f09118fe8
parent7ac28817536797fd40e9646452183606f9e17f71 (diff)
downloadlinux-7bb59df83b190817f56c4e2fec3078d99d906ad4.tar.gz
linux-7bb59df83b190817f56c4e2fec3078d99d906ad4.tar.bz2
linux-7bb59df83b190817f56c4e2fec3078d99d906ad4.zip
Bluetooth: Fix hidp disconnect deadlocks and lost wakeup
Partial revert of commit aabf6f89. When the hidp session thread was converted from kernel_thread to kthread, the atomic/wakeups were replaced with kthread_stop. kthread_stop has blocking semantics which are inappropriate for the hidp session kthread. In addition, the kthread signals itself to terminate in hidp_process_hid_control() - it cannot do this with kthread_stop(). Lastly, a wakeup can be lost if the wakeup happens between checking for the loop exit condition and setting the current state to TASK_INTERRUPTIBLE. (Without appropriate synchronization mechanisms, the task state should not be changed between the condition test and the yield - via schedule() - as this creates a race between the wakeup and resetting the state back to interruptible.) Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
-rw-r--r--net/bluetooth/hidp/core.c18
-rw-r--r--net/bluetooth/hidp/hidp.h1
2 files changed, 12 insertions, 7 deletions
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index c405a954a603..43b4c2deb7cc 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -464,7 +464,8 @@ static void hidp_idle_timeout(unsigned long arg)
{
struct hidp_session *session = (struct hidp_session *) arg;
- kthread_stop(session->task);
+ atomic_inc(&session->terminate);
+ wake_up_process(session->task);
}
static void hidp_set_timer(struct hidp_session *session)
@@ -535,7 +536,8 @@ static void hidp_process_hid_control(struct hidp_session *session,
skb_queue_purge(&session->ctrl_transmit);
skb_queue_purge(&session->intr_transmit);
- kthread_stop(session->task);
+ atomic_inc(&session->terminate);
+ wake_up_process(current);
}
}
@@ -706,9 +708,8 @@ static int hidp_session(void *arg)
add_wait_queue(sk_sleep(intr_sk), &intr_wait);
session->waiting_for_startup = 0;
wake_up_interruptible(&session->startup_queue);
- while (!kthread_should_stop()) {
- set_current_state(TASK_INTERRUPTIBLE);
-
+ set_current_state(TASK_INTERRUPTIBLE);
+ while (!atomic_read(&session->terminate)) {
if (ctrl_sk->sk_state != BT_CONNECTED ||
intr_sk->sk_state != BT_CONNECTED)
break;
@@ -726,6 +727,7 @@ static int hidp_session(void *arg)
hidp_process_transmit(session);
schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(sk_sleep(intr_sk), &intr_wait);
@@ -1060,7 +1062,8 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
err_add_device:
hid_destroy_device(session->hid);
session->hid = NULL;
- kthread_stop(session->task);
+ atomic_inc(&session->terminate);
+ wake_up_process(session->task);
unlink:
hidp_del_timer(session);
@@ -1111,7 +1114,8 @@ int hidp_del_connection(struct hidp_conndel_req *req)
skb_queue_purge(&session->ctrl_transmit);
skb_queue_purge(&session->intr_transmit);
- kthread_stop(session->task);
+ atomic_inc(&session->terminate);
+ wake_up_process(session->task);
}
} else
err = -ENOENT;
diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h
index 19e95004b286..af1bcc823f26 100644
--- a/net/bluetooth/hidp/hidp.h
+++ b/net/bluetooth/hidp/hidp.h
@@ -142,6 +142,7 @@ struct hidp_session {
uint ctrl_mtu;
uint intr_mtu;
+ atomic_t terminate;
struct task_struct *task;
unsigned char keys[8];