diff options
author | Gustavo F. Padovan <padovan@profusion.mobi> | 2011-12-17 07:56:45 -0500 |
---|---|---|
committer | Gustavo F. Padovan <padovan@profusion.mobi> | 2011-12-18 14:07:57 -0500 |
commit | 3d57dc6806599ca7d389fc9410eefbc1a7dc32bc (patch) | |
tree | bcb1efa2cec7f2e12aff5a5d40b2115c3b738d38 | |
parent | 03a001948166d966d0d580cddb8ae3a23f8b795b (diff) |
Bluetooth: Change l2cap chan_list to use RCU
This list has much more reads than writes, so RCU makes senses here, also
it avoid deadlock against the socket lock.
Acked-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
-rw-r--r-- | net/bluetooth/l2cap_core.c | 119 |
1 files changed, 58 insertions, 61 deletions
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index d6165199fc8b..a1766adee397 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c | |||
@@ -89,24 +89,36 @@ static inline void chan_put(struct l2cap_chan *c) | |||
89 | 89 | ||
90 | static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid) | 90 | static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid) |
91 | { | 91 | { |
92 | struct l2cap_chan *c; | 92 | struct l2cap_chan *c, *r = NULL; |
93 | 93 | ||
94 | list_for_each_entry(c, &conn->chan_l, list) { | 94 | rcu_read_lock(); |
95 | if (c->dcid == cid) | 95 | |
96 | return c; | 96 | list_for_each_entry_rcu(c, &conn->chan_l, list) { |
97 | if (c->dcid == cid) { | ||
98 | r = c; | ||
99 | break; | ||
100 | } | ||
97 | } | 101 | } |
98 | return NULL; | 102 | |
103 | rcu_read_unlock(); | ||
104 | return r; | ||
99 | } | 105 | } |
100 | 106 | ||
101 | static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid) | 107 | static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid) |
102 | { | 108 | { |
103 | struct l2cap_chan *c; | 109 | struct l2cap_chan *c, *r = NULL; |
104 | 110 | ||
105 | list_for_each_entry(c, &conn->chan_l, list) { | 111 | rcu_read_lock(); |
106 | if (c->scid == cid) | 112 | |
107 | return c; | 113 | list_for_each_entry_rcu(c, &conn->chan_l, list) { |
114 | if (c->scid == cid) { | ||
115 | r = c; | ||
116 | break; | ||
117 | } | ||
108 | } | 118 | } |
109 | return NULL; | 119 | |
120 | rcu_read_unlock(); | ||
121 | return r; | ||
110 | } | 122 | } |
111 | 123 | ||
112 | /* Find channel with given SCID. | 124 | /* Find channel with given SCID. |
@@ -115,34 +127,36 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 ci | |||
115 | { | 127 | { |
116 | struct l2cap_chan *c; | 128 | struct l2cap_chan *c; |
117 | 129 | ||
118 | mutex_lock(&conn->chan_lock); | ||
119 | c = __l2cap_get_chan_by_scid(conn, cid); | 130 | c = __l2cap_get_chan_by_scid(conn, cid); |
120 | if (c) | 131 | if (c) |
121 | lock_sock(c->sk); | 132 | lock_sock(c->sk); |
122 | mutex_unlock(&conn->chan_lock); | ||
123 | return c; | 133 | return c; |
124 | } | 134 | } |
125 | 135 | ||
126 | static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident) | 136 | static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident) |
127 | { | 137 | { |
128 | struct l2cap_chan *c; | 138 | struct l2cap_chan *c, *r = NULL; |
129 | 139 | ||
130 | list_for_each_entry(c, &conn->chan_l, list) { | 140 | rcu_read_lock(); |
131 | if (c->ident == ident) | 141 | |
132 | return c; | 142 | list_for_each_entry_rcu(c, &conn->chan_l, list) { |
143 | if (c->ident == ident) { | ||
144 | r = c; | ||
145 | break; | ||
146 | } | ||
133 | } | 147 | } |
134 | return NULL; | 148 | |
149 | rcu_read_unlock(); | ||
150 | return r; | ||
135 | } | 151 | } |
136 | 152 | ||
137 | static inline struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident) | 153 | static inline struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident) |
138 | { | 154 | { |
139 | struct l2cap_chan *c; | 155 | struct l2cap_chan *c; |
140 | 156 | ||
141 | mutex_lock(&conn->chan_lock); | ||
142 | c = __l2cap_get_chan_by_ident(conn, ident); | 157 | c = __l2cap_get_chan_by_ident(conn, ident); |
143 | if (c) | 158 | if (c) |
144 | lock_sock(c->sk); | 159 | lock_sock(c->sk); |
145 | mutex_unlock(&conn->chan_lock); | ||
146 | return c; | 160 | return c; |
147 | } | 161 | } |
148 | 162 | ||
@@ -323,7 +337,7 @@ void l2cap_chan_destroy(struct l2cap_chan *chan) | |||
323 | chan_put(chan); | 337 | chan_put(chan); |
324 | } | 338 | } |
325 | 339 | ||
326 | static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) | 340 | static void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) |
327 | { | 341 | { |
328 | BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, | 342 | BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, |
329 | chan->psm, chan->dcid); | 343 | chan->psm, chan->dcid); |
@@ -364,7 +378,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) | |||
364 | 378 | ||
365 | chan_hold(chan); | 379 | chan_hold(chan); |
366 | 380 | ||
367 | list_add(&chan->list, &conn->chan_l); | 381 | list_add_rcu(&chan->list, &conn->chan_l); |
368 | } | 382 | } |
369 | 383 | ||
370 | /* Delete channel. | 384 | /* Delete channel. |
@@ -381,9 +395,9 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) | |||
381 | 395 | ||
382 | if (conn) { | 396 | if (conn) { |
383 | /* Delete from channel list */ | 397 | /* Delete from channel list */ |
384 | mutex_lock(&conn->chan_lock); | 398 | list_del_rcu(&chan->list); |
385 | list_del(&chan->list); | 399 | synchronize_rcu(); |
386 | mutex_unlock(&conn->chan_lock); | 400 | |
387 | chan_put(chan); | 401 | chan_put(chan); |
388 | 402 | ||
389 | chan->conn = NULL; | 403 | chan->conn = NULL; |
@@ -750,13 +764,13 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c | |||
750 | /* ---- L2CAP connections ---- */ | 764 | /* ---- L2CAP connections ---- */ |
751 | static void l2cap_conn_start(struct l2cap_conn *conn) | 765 | static void l2cap_conn_start(struct l2cap_conn *conn) |
752 | { | 766 | { |
753 | struct l2cap_chan *chan, *tmp; | 767 | struct l2cap_chan *chan; |
754 | 768 | ||
755 | BT_DBG("conn %p", conn); | 769 | BT_DBG("conn %p", conn); |
756 | 770 | ||
757 | mutex_lock(&conn->chan_lock); | 771 | rcu_read_lock(); |
758 | 772 | ||
759 | list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) { | 773 | list_for_each_entry_rcu(chan, &conn->chan_l, list) { |
760 | struct sock *sk = chan->sk; | 774 | struct sock *sk = chan->sk; |
761 | 775 | ||
762 | bh_lock_sock(sk); | 776 | bh_lock_sock(sk); |
@@ -780,9 +794,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) | |||
780 | &chan->conf_state)) { | 794 | &chan->conf_state)) { |
781 | /* l2cap_chan_close() calls list_del(chan) | 795 | /* l2cap_chan_close() calls list_del(chan) |
782 | * so release the lock */ | 796 | * so release the lock */ |
783 | mutex_unlock(&conn->chan_lock); | ||
784 | l2cap_chan_close(chan, ECONNRESET); | 797 | l2cap_chan_close(chan, ECONNRESET); |
785 | utex_lock(&conn->chan_lock); | ||
786 | bh_unlock_sock(sk); | 798 | bh_unlock_sock(sk); |
787 | continue; | 799 | continue; |
788 | } | 800 | } |
@@ -838,7 +850,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) | |||
838 | bh_unlock_sock(sk); | 850 | bh_unlock_sock(sk); |
839 | } | 851 | } |
840 | 852 | ||
841 | mutex_unlock(&conn->chan_lock); | 853 | rcu_read_unlock(); |
842 | } | 854 | } |
843 | 855 | ||
844 | /* Find socket with cid and source bdaddr. | 856 | /* Find socket with cid and source bdaddr. |
@@ -903,8 +915,6 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) | |||
903 | 915 | ||
904 | sk = chan->sk; | 916 | sk = chan->sk; |
905 | 917 | ||
906 | mutex_lock(&conn->chan_lock); | ||
907 | |||
908 | hci_conn_hold(conn->hcon); | 918 | hci_conn_hold(conn->hcon); |
909 | 919 | ||
910 | bacpy(&bt_sk(sk)->src, conn->src); | 920 | bacpy(&bt_sk(sk)->src, conn->src); |
@@ -912,15 +922,13 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) | |||
912 | 922 | ||
913 | bt_accept_enqueue(parent, sk); | 923 | bt_accept_enqueue(parent, sk); |
914 | 924 | ||
915 | __l2cap_chan_add(conn, chan); | 925 | l2cap_chan_add(conn, chan); |
916 | 926 | ||
917 | __set_chan_timer(chan, sk->sk_sndtimeo); | 927 | __set_chan_timer(chan, sk->sk_sndtimeo); |
918 | 928 | ||
919 | l2cap_state_change(chan, BT_CONNECTED); | 929 | l2cap_state_change(chan, BT_CONNECTED); |
920 | parent->sk_data_ready(parent, 0); | 930 | parent->sk_data_ready(parent, 0); |
921 | 931 | ||
922 | mutex_unlock(&conn->chan_lock); | ||
923 | |||
924 | clean: | 932 | clean: |
925 | release_sock(parent); | 933 | release_sock(parent); |
926 | } | 934 | } |
@@ -954,9 +962,9 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) | |||
954 | if (conn->hcon->out && conn->hcon->type == LE_LINK) | 962 | if (conn->hcon->out && conn->hcon->type == LE_LINK) |
955 | smp_conn_security(conn, conn->hcon->pending_sec_level); | 963 | smp_conn_security(conn, conn->hcon->pending_sec_level); |
956 | 964 | ||
957 | mutex_lock(&conn->chan_lock); | 965 | rcu_read_lock(); |
958 | 966 | ||
959 | list_for_each_entry(chan, &conn->chan_l, list) { | 967 | list_for_each_entry_rcu(chan, &conn->chan_l, list) { |
960 | struct sock *sk = chan->sk; | 968 | struct sock *sk = chan->sk; |
961 | 969 | ||
962 | bh_lock_sock(sk); | 970 | bh_lock_sock(sk); |
@@ -976,7 +984,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) | |||
976 | bh_unlock_sock(sk); | 984 | bh_unlock_sock(sk); |
977 | } | 985 | } |
978 | 986 | ||
979 | mutex_unlock(&conn->chan_lock); | 987 | rcu_read_unlock(); |
980 | } | 988 | } |
981 | 989 | ||
982 | /* Notify sockets that we cannot guaranty reliability anymore */ | 990 | /* Notify sockets that we cannot guaranty reliability anymore */ |
@@ -986,16 +994,16 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err) | |||
986 | 994 | ||
987 | BT_DBG("conn %p", conn); | 995 | BT_DBG("conn %p", conn); |
988 | 996 | ||
989 | mutex_lock(&conn->chan_lock); | 997 | rcu_read_lock(); |
990 | 998 | ||
991 | list_for_each_entry(chan, &conn->chan_l, list) { | 999 | list_for_each_entry_rcu(chan, &conn->chan_l, list) { |
992 | struct sock *sk = chan->sk; | 1000 | struct sock *sk = chan->sk; |
993 | 1001 | ||
994 | if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags)) | 1002 | if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags)) |
995 | sk->sk_err = err; | 1003 | sk->sk_err = err; |
996 | } | 1004 | } |
997 | 1005 | ||
998 | mutex_unlock(&conn->chan_lock); | 1006 | rcu_read_unlock(); |
999 | } | 1007 | } |
1000 | 1008 | ||
1001 | static void l2cap_info_timeout(struct work_struct *work) | 1009 | static void l2cap_info_timeout(struct work_struct *work) |
@@ -1087,7 +1095,6 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) | |||
1087 | conn->feat_mask = 0; | 1095 | conn->feat_mask = 0; |
1088 | 1096 | ||
1089 | spin_lock_init(&conn->lock); | 1097 | spin_lock_init(&conn->lock); |
1090 | mutex_init(&conn->chan_lock); | ||
1091 | 1098 | ||
1092 | INIT_LIST_HEAD(&conn->chan_l); | 1099 | INIT_LIST_HEAD(&conn->chan_l); |
1093 | 1100 | ||
@@ -1102,13 +1109,6 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) | |||
1102 | return conn; | 1109 | return conn; |
1103 | } | 1110 | } |
1104 | 1111 | ||
1105 | static inline void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) | ||
1106 | { | ||
1107 | mutex_lock(&conn->chan_lock); | ||
1108 | __l2cap_chan_add(conn, chan); | ||
1109 | mutex_unlock(&conn->chan_lock); | ||
1110 | } | ||
1111 | |||
1112 | /* ---- Socket interface ---- */ | 1112 | /* ---- Socket interface ---- */ |
1113 | 1113 | ||
1114 | /* Find socket with psm and source bdaddr. | 1114 | /* Find socket with psm and source bdaddr. |
@@ -1825,8 +1825,9 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) | |||
1825 | 1825 | ||
1826 | BT_DBG("conn %p", conn); | 1826 | BT_DBG("conn %p", conn); |
1827 | 1827 | ||
1828 | mutex_lock(&conn->chan_lock); | 1828 | rcu_read_lock(); |
1829 | list_for_each_entry(chan, &conn->chan_l, list) { | 1829 | |
1830 | list_for_each_entry_rcu(chan, &conn->chan_l, list) { | ||
1830 | struct sock *sk = chan->sk; | 1831 | struct sock *sk = chan->sk; |
1831 | if (chan->chan_type != L2CAP_CHAN_RAW) | 1832 | if (chan->chan_type != L2CAP_CHAN_RAW) |
1832 | continue; | 1833 | continue; |
@@ -1841,7 +1842,8 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) | |||
1841 | if (chan->ops->recv(chan->data, nskb)) | 1842 | if (chan->ops->recv(chan->data, nskb)) |
1842 | kfree_skb(nskb); | 1843 | kfree_skb(nskb); |
1843 | } | 1844 | } |
1844 | mutex_unlock(&conn->chan_lock); | 1845 | |
1846 | rcu_read_unlock(); | ||
1845 | } | 1847 | } |
1846 | 1848 | ||
1847 | /* ---- L2CAP signalling commands ---- */ | 1849 | /* ---- L2CAP signalling commands ---- */ |
@@ -2641,11 +2643,8 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd | |||
2641 | 2643 | ||
2642 | sk = chan->sk; | 2644 | sk = chan->sk; |
2643 | 2645 | ||
2644 | mutex_lock(&conn->chan_lock); | ||
2645 | |||
2646 | /* Check if we already have channel with that dcid */ | 2646 | /* Check if we already have channel with that dcid */ |
2647 | if (__l2cap_get_chan_by_dcid(conn, scid)) { | 2647 | if (__l2cap_get_chan_by_dcid(conn, scid)) { |
2648 | mutex_unlock(&conn->chan_lock); | ||
2649 | sock_set_flag(sk, SOCK_ZAPPED); | 2648 | sock_set_flag(sk, SOCK_ZAPPED); |
2650 | chan->ops->close(chan->data); | 2649 | chan->ops->close(chan->data); |
2651 | goto response; | 2650 | goto response; |
@@ -2660,7 +2659,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd | |||
2660 | 2659 | ||
2661 | bt_accept_enqueue(parent, sk); | 2660 | bt_accept_enqueue(parent, sk); |
2662 | 2661 | ||
2663 | __l2cap_chan_add(conn, chan); | 2662 | l2cap_chan_add(conn, chan); |
2664 | 2663 | ||
2665 | dcid = chan->scid; | 2664 | dcid = chan->scid; |
2666 | 2665 | ||
@@ -2691,8 +2690,6 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd | |||
2691 | status = L2CAP_CS_NO_INFO; | 2690 | status = L2CAP_CS_NO_INFO; |
2692 | } | 2691 | } |
2693 | 2692 | ||
2694 | mutex_unlock(&conn->chan_lock); | ||
2695 | |||
2696 | response: | 2693 | response: |
2697 | release_sock(parent); | 2694 | release_sock(parent); |
2698 | 2695 | ||
@@ -4528,9 +4525,9 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) | |||
4528 | del_timer(&conn->security_timer); | 4525 | del_timer(&conn->security_timer); |
4529 | } | 4526 | } |
4530 | 4527 | ||
4531 | mutex_lock(&conn->chan_lock); | 4528 | rcu_read_lock(); |
4532 | 4529 | ||
4533 | list_for_each_entry(chan, &conn->chan_l, list) { | 4530 | list_for_each_entry_rcu(chan, &conn->chan_l, list) { |
4534 | struct sock *sk = chan->sk; | 4531 | struct sock *sk = chan->sk; |
4535 | 4532 | ||
4536 | bh_lock_sock(sk); | 4533 | bh_lock_sock(sk); |
@@ -4608,7 +4605,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) | |||
4608 | bh_unlock_sock(sk); | 4605 | bh_unlock_sock(sk); |
4609 | } | 4606 | } |
4610 | 4607 | ||
4611 | mutex_unlock(&conn->chan_lock); | 4608 | rcu_read_unlock(); |
4612 | 4609 | ||
4613 | return 0; | 4610 | return 0; |
4614 | } | 4611 | } |