From 721c41812daf7b38759942563773a7832e3c990d Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 23 Jun 2011 19:29:58 -0300 Subject: Bluetooth: Move L2CAP timers to workqueue L2CAP timers also need to run in process context. As the works in l2cap are small we are using the system worqueue. Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'include/net/bluetooth/l2cap.h') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 30719eb2e77c..03be9111dc51 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -482,10 +482,11 @@ struct l2cap_chan { __u32 remote_acc_lat; __u32 remote_flush_to; - struct timer_list chan_timer; - struct timer_list retrans_timer; - struct timer_list monitor_timer; - struct timer_list ack_timer; + struct delayed_work chan_timer; + struct delayed_work retrans_timer; + struct delayed_work monitor_timer; + struct delayed_work ack_timer; + struct sk_buff *tx_send_head; struct sk_buff_head tx_q; struct sk_buff_head srej_q; @@ -595,16 +596,16 @@ enum { }; #define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t)) -#define __clear_chan_timer(c) l2cap_clear_timer(c, &c->chan_timer) +#define __clear_chan_timer(c) l2cap_clear_timer(&c->chan_timer) #define __set_retrans_timer(c) l2cap_set_timer(c, &c->retrans_timer, \ L2CAP_DEFAULT_RETRANS_TO); -#define __clear_retrans_timer(c) l2cap_clear_timer(c, &c->retrans_timer) +#define __clear_retrans_timer(c) l2cap_clear_timer(&c->retrans_timer) #define __set_monitor_timer(c) l2cap_set_timer(c, &c->monitor_timer, \ L2CAP_DEFAULT_MONITOR_TO); -#define __clear_monitor_timer(c) l2cap_clear_timer(c, &c->monitor_timer) +#define __clear_monitor_timer(c) l2cap_clear_timer(&c->monitor_timer) #define __set_ack_timer(c) l2cap_set_timer(c, &chan->ack_timer, \ L2CAP_DEFAULT_ACK_TO); -#define __clear_ack_timer(c) l2cap_clear_timer(c, &c->ack_timer) +#define __clear_ack_timer(c) l2cap_clear_timer(&c->ack_timer) static inline int __seq_offset(struct l2cap_chan *chan, __u16 seq1, __u16 seq2) { -- cgit From d01b2ff4e6496bc48a1917b6340e13263f871a15 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 9 Dec 2011 04:45:12 -0200 Subject: Bluetooth: convert chan_lock to mutex spin lock doesn't fit ok anymore on the new code based on workqueues. Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/net/bluetooth/l2cap.h') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 03be9111dc51..a1750912824f 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -536,7 +536,7 @@ struct l2cap_conn { struct smp_chan *smp_chan; struct list_head chan_l; - rwlock_t chan_lock; + struct mutex chan_lock; }; #define L2CAP_INFO_CL_MTU_REQ_SENT 0x01 -- cgit From f878fcad1760247c054a9c80964d0b7450d2379b Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 15 Dec 2011 01:16:14 -0200 Subject: Bluetooth: convert info timer to delayed_work Another step of remove interrupt context from Bluetooth Core. Use the system workqueue. Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/net/bluetooth/l2cap.h') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index a1750912824f..f79137476cb6 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -522,7 +522,7 @@ struct l2cap_conn { __u8 info_state; __u8 info_ident; - struct timer_list info_timer; + struct delayed_work info_work; spinlock_t lock; -- cgit From 03a001948166d966d0d580cddb8ae3a23f8b795b Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 9 Dec 2011 04:48:17 -0200 Subject: Bluetooth: invert locking order in connect path This move some checking code that was in l2cap_sock_connect() to l2cap_chan_connect(). Thus we can invert the lock calls, i.e., call lock_sock() before hci_dev_lock() to avoid a deadlock scenario. Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/net/bluetooth/l2cap.h') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index f79137476cb6..c0d168adf9be 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -806,7 +806,8 @@ int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid); struct l2cap_chan *l2cap_chan_create(struct sock *sk); void l2cap_chan_close(struct l2cap_chan *chan, int reason); void l2cap_chan_destroy(struct l2cap_chan *chan); -int l2cap_chan_connect(struct l2cap_chan *chan); +inline int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, + bdaddr_t *dst); int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, u32 priority); void l2cap_chan_busy(struct l2cap_chan *chan, int busy); -- cgit From c2ec9c1bbd17cdd1fc962f000b4ecb98c1dad830 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 20 Dec 2011 10:57:26 -0200 Subject: Bluetooth: Move l2cap_{set,clear}_timer to l2cap.h It is the only place where it is used. Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'include/net/bluetooth/l2cap.h') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index c0d168adf9be..e199c2f0e4dc 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -595,6 +595,21 @@ enum { FLAG_EFS_ENABLE, }; +static inline void l2cap_set_timer(struct l2cap_chan *chan, + struct delayed_work *work, long timeout) +{ + BT_DBG("chan %p state %d timeout %ld", chan, chan->state, timeout); + + cancel_delayed_work_sync(work); + + schedule_delayed_work(work, timeout); +} + +static inline void l2cap_clear_timer(struct delayed_work *work) +{ + cancel_delayed_work_sync(work); +} + #define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t)) #define __clear_chan_timer(c) l2cap_clear_timer(&c->chan_timer) #define __set_retrans_timer(c) l2cap_set_timer(c, &c->retrans_timer, \ -- cgit From 6c9d42a1615c6dc19c4a57a77d9c4b3d779bb741 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 20 Dec 2011 10:57:27 -0200 Subject: Bluetooth: convert security timer to delayed_work This one also needs to run in process context Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/net/bluetooth/l2cap.h') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index e199c2f0e4dc..fc481d1ebf0b 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -532,7 +532,7 @@ struct l2cap_conn { __u8 disc_reason; - struct timer_list security_timer; + struct delayed_work security_timer; struct smp_chan *smp_chan; struct list_head chan_l; -- cgit From 030013d8585bfc9479bb367bf771d96ef8e289a4 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 20 Dec 2011 10:57:28 -0200 Subject: Bluetooth: Rename info_work to info_timer It makes more sense this way, since info_timer is a timer using delayed work API. Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/net/bluetooth/l2cap.h') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index fc481d1ebf0b..f141fbecfa40 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -522,7 +522,7 @@ struct l2cap_conn { __u8 info_state; __u8 info_ident; - struct delayed_work info_work; + struct delayed_work info_timer; spinlock_t lock; -- cgit From 371fd83563252f550ce59476a7366d0b5171d316 Mon Sep 17 00:00:00 2001 From: Ulisses Furquim Date: Wed, 21 Dec 2011 20:02:36 -0200 Subject: Bluetooth: Fix deadlocks with sock lock and L2CAP timers locks When cancelling a delayed work (timer) in L2CAP we can not sleep holding the sock mutex otherwise we might deadlock with an L2CAP timer handler. This is possible because RX/TX and L2CAP timers run in different workqueues. The scenario below illustrates the problem. Thus we are now avoiding to sleep on the timers locks. ====================================================== [ INFO: possible circular locking dependency detected ] 3.1.0-05270-ga978dc7-dirty #239 ------------------------------------------------------- kworker/1:1/873 is trying to acquire lock: (sk_lock-AF_BLUETOOTH-BTPROTO_L2CAP){+.+...}, at: [] l2cap_chan_timeout+0x3c/0xe0 [bluetooth] but task is already holding lock: ((&(&chan->chan_timer)->work)){+.+...}, at: [] process_one_work+0x126/0x450 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 ((&(&chan->chan_timer)->work)){+.+...}: [] check_prevs_add+0xf6/0x170 [] validate_chain+0x613/0x790 [] __lock_acquire+0x4be/0xac0 [] lock_acquire+0x8d/0xb0 [] wait_on_work+0x4f/0x160 [] __cancel_work_timer+0x73/0x80 [] cancel_delayed_work_sync+0xd/0x10 [] l2cap_chan_connect+0x22d/0x470 [bluetooth] [] l2cap_sock_connect+0xb1/0x140 [bluetooth] [] kernel_connect+0xb/0x10 [] rfcomm_session_create+0x12a/0x1c0 [rfcomm] [] __rfcomm_dlc_open+0x1c7/0x240 [rfcomm] [] rfcomm_dlc_open+0x42/0x70 [rfcomm] [] rfcomm_sock_connect+0x103/0x150 [rfcomm] [] sys_connect+0xae/0xc0 [] compat_sys_socketcall+0xb2/0x220 [] sysenter_dispatch+0x7/0x30 -> #0 (sk_lock-AF_BLUETOOTH-BTPROTO_L2CAP){+.+...}: [] check_prev_add+0x6cd/0x6e0 [] check_prevs_add+0xf6/0x170 [] validate_chain+0x613/0x790 [] __lock_acquire+0x4be/0xac0 [] lock_acquire+0x8d/0xb0 [] lock_sock_nested+0x8a/0xa0 [] l2cap_chan_timeout+0x3c/0xe0 [bluetooth] [] process_one_work+0x184/0x450 [] worker_thread+0x15e/0x340 [] kthread+0x96/0xa0 [] kernel_thread_helper+0x4/0x10 other info that might help us debug this: Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock((&(&chan->chan_timer)->work)); lock(sk_lock-AF_BLUETOOTH-BTPROTO_L2CAP); lock((&(&chan->chan_timer)->work)); lock(sk_lock-AF_BLUETOOTH-BTPROTO_L2CAP); *** DEADLOCK *** 2 locks held by kworker/1:1/873: #0: (events){.+.+.+}, at: [] process_one_work+0x126/0x450 #1: ((&(&chan->chan_timer)->work)){+.+...}, at: [] process_one_work+0x126/0x450 stack backtrace: Pid: 873, comm: kworker/1:1 Not tainted 3.1.0-05270-ga978dc7-dirty #239 Call Trace: [] print_circular_bug+0xd2/0xe3 [] check_prev_add+0x6cd/0x6e0 [] check_prevs_add+0xf6/0x170 [] validate_chain+0x613/0x790 [] __lock_acquire+0x4be/0xac0 [] ? lock_sock_nested+0x66/0xa0 [] ? lock_release_nested+0x100/0x110 [] ? lock_sock_nested+0x66/0xa0 [] lock_acquire+0x8d/0xb0 [] ? l2cap_chan_timeout+0x3c/0xe0 [bluetooth] [] lock_sock_nested+0x8a/0xa0 [] ? l2cap_chan_timeout+0x3c/0xe0 [bluetooth] [] ? process_one_work+0x126/0x450 [] l2cap_chan_timeout+0x3c/0xe0 [bluetooth] [] process_one_work+0x184/0x450 [] ? process_one_work+0x126/0x450 [] ? l2cap_security_cfm+0x4e0/0x4e0 [bluetooth] [] worker_thread+0x15e/0x340 [] ? manage_workers+0x110/0x110 [] kthread+0x96/0xa0 [] kernel_thread_helper+0x4/0x10 [] ? retint_restore_args+0xe/0xe [] ? __init_kthread_worker+0x70/0x70 [] ? gs_change+0xb/0xb Signed-off-by: Ulisses Furquim Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) (limited to 'include/net/bluetooth/l2cap.h') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index f141fbecfa40..9572cbd12a7a 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -595,32 +595,45 @@ enum { FLAG_EFS_ENABLE, }; +static inline void l2cap_chan_hold(struct l2cap_chan *c) +{ + atomic_inc(&c->refcnt); +} + +static inline void l2cap_chan_put(struct l2cap_chan *c) +{ + if (atomic_dec_and_test(&c->refcnt)) + kfree(c); +} + static inline void l2cap_set_timer(struct l2cap_chan *chan, struct delayed_work *work, long timeout) { BT_DBG("chan %p state %d timeout %ld", chan, chan->state, timeout); - cancel_delayed_work_sync(work); - + if (!__cancel_delayed_work(work)) + l2cap_chan_hold(chan); schedule_delayed_work(work, timeout); } -static inline void l2cap_clear_timer(struct delayed_work *work) +static inline void l2cap_clear_timer(struct l2cap_chan *chan, + struct delayed_work *work) { - cancel_delayed_work_sync(work); + if (__cancel_delayed_work(work)) + l2cap_chan_put(chan); } #define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t)) -#define __clear_chan_timer(c) l2cap_clear_timer(&c->chan_timer) +#define __clear_chan_timer(c) l2cap_clear_timer(c, &c->chan_timer) #define __set_retrans_timer(c) l2cap_set_timer(c, &c->retrans_timer, \ L2CAP_DEFAULT_RETRANS_TO); -#define __clear_retrans_timer(c) l2cap_clear_timer(&c->retrans_timer) +#define __clear_retrans_timer(c) l2cap_clear_timer(c, &c->retrans_timer) #define __set_monitor_timer(c) l2cap_set_timer(c, &c->monitor_timer, \ L2CAP_DEFAULT_MONITOR_TO); -#define __clear_monitor_timer(c) l2cap_clear_timer(&c->monitor_timer) +#define __clear_monitor_timer(c) l2cap_clear_timer(c, &c->monitor_timer) #define __set_ack_timer(c) l2cap_set_timer(c, &chan->ack_timer, \ L2CAP_DEFAULT_ACK_TO); -#define __clear_ack_timer(c) l2cap_clear_timer(&c->ack_timer) +#define __clear_ack_timer(c) l2cap_clear_timer(c, &c->ack_timer) static inline int __seq_offset(struct l2cap_chan *chan, __u16 seq1, __u16 seq2) { -- cgit