aboutsummaryrefslogtreecommitdiffstats
path: root/net/bluetooth/l2cap.c
diff options
context:
space:
mode:
authorGustavo F. Padovan <padovan@profusion.mobi>2010-06-21 17:53:22 -0400
committerMarcel Holtmann <marcel@holtmann.org>2010-07-21 13:39:09 -0400
commit218bb9dfd21472128f86b38ad2eab123205c2991 (patch)
treea380097efa336c128a81460c9f316f04f39aeadc /net/bluetooth/l2cap.c
parente0f66218b3a7d0bcf37ca95186123c257fda0ba5 (diff)
Bluetooth: Add backlog queue to ERTM code
backlog queue is the canonical mechanism to avoid race conditions due interrupts in bottom half context. After the socket lock is released the net core take care of push all skb in its backlog queue. Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net/bluetooth/l2cap.c')
-rw-r--r--net/bluetooth/l2cap.c132
1 files changed, 79 insertions, 53 deletions
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index dc8601fc2404..cf4481f7f566 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -77,6 +77,8 @@ static void l2cap_sock_kill(struct sock *sk);
77static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, 77static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
78 u8 code, u8 ident, u16 dlen, void *data); 78 u8 code, u8 ident, u16 dlen, void *data);
79 79
80static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb);
81
80/* ---- L2CAP timers ---- */ 82/* ---- L2CAP timers ---- */
81static void l2cap_sock_timeout(unsigned long arg) 83static void l2cap_sock_timeout(unsigned long arg)
82{ 84{
@@ -2447,6 +2449,8 @@ static inline void l2cap_ertm_init(struct sock *sk)
2447 __skb_queue_head_init(BUSY_QUEUE(sk)); 2449 __skb_queue_head_init(BUSY_QUEUE(sk));
2448 2450
2449 INIT_WORK(&l2cap_pi(sk)->busy_work, l2cap_busy_work); 2451 INIT_WORK(&l2cap_pi(sk)->busy_work, l2cap_busy_work);
2452
2453 sk->sk_backlog_rcv = l2cap_ertm_data_rcv;
2450} 2454}
2451 2455
2452static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask) 2456static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask)
@@ -4171,13 +4175,83 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str
4171 return 0; 4175 return 0;
4172} 4176}
4173 4177
4178static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb)
4179{
4180 struct l2cap_pinfo *pi = l2cap_pi(sk);
4181 u16 control;
4182 u8 req_seq;
4183 int len, next_tx_seq_offset, req_seq_offset;
4184
4185 control = get_unaligned_le16(skb->data);
4186 skb_pull(skb, 2);
4187 len = skb->len;
4188
4189 /*
4190 * We can just drop the corrupted I-frame here.
4191 * Receiver will miss it and start proper recovery
4192 * procedures and ask retransmission.
4193 */
4194 if (l2cap_check_fcs(pi, skb))
4195 goto drop;
4196
4197 if (__is_sar_start(control) && __is_iframe(control))
4198 len -= 2;
4199
4200 if (pi->fcs == L2CAP_FCS_CRC16)
4201 len -= 2;
4202
4203 if (len > pi->mps) {
4204 l2cap_send_disconn_req(pi->conn, sk, ECONNRESET);
4205 goto drop;
4206 }
4207
4208 req_seq = __get_reqseq(control);
4209 req_seq_offset = (req_seq - pi->expected_ack_seq) % 64;
4210 if (req_seq_offset < 0)
4211 req_seq_offset += 64;
4212
4213 next_tx_seq_offset =
4214 (pi->next_tx_seq - pi->expected_ack_seq) % 64;
4215 if (next_tx_seq_offset < 0)
4216 next_tx_seq_offset += 64;
4217
4218 /* check for invalid req-seq */
4219 if (req_seq_offset > next_tx_seq_offset) {
4220 l2cap_send_disconn_req(pi->conn, sk, ECONNRESET);
4221 goto drop;
4222 }
4223
4224 if (__is_iframe(control)) {
4225 if (len < 0) {
4226 l2cap_send_disconn_req(pi->conn, sk, ECONNRESET);
4227 goto drop;
4228 }
4229
4230 l2cap_data_channel_iframe(sk, control, skb);
4231 } else {
4232 if (len != 0) {
4233 BT_ERR("%d", len);
4234 l2cap_send_disconn_req(pi->conn, sk, ECONNRESET);
4235 goto drop;
4236 }
4237
4238 l2cap_data_channel_sframe(sk, control, skb);
4239 }
4240
4241 return 0;
4242
4243drop:
4244 kfree_skb(skb);
4245 return 0;
4246}
4247
4174static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb) 4248static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb)
4175{ 4249{
4176 struct sock *sk; 4250 struct sock *sk;
4177 struct l2cap_pinfo *pi; 4251 struct l2cap_pinfo *pi;
4178 u16 control; 4252 u16 control;
4179 u8 tx_seq, req_seq; 4253 u8 tx_seq;
4180 int len, next_tx_seq_offset, req_seq_offset; 4254 int len;
4181 4255
4182 sk = l2cap_get_chan_by_scid(&conn->chan_list, cid); 4256 sk = l2cap_get_chan_by_scid(&conn->chan_list, cid);
4183 if (!sk) { 4257 if (!sk) {
@@ -4207,59 +4281,11 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
4207 break; 4281 break;
4208 4282
4209 case L2CAP_MODE_ERTM: 4283 case L2CAP_MODE_ERTM:
4210 control = get_unaligned_le16(skb->data); 4284 if (!sock_owned_by_user(sk)) {
4211 skb_pull(skb, 2); 4285 l2cap_ertm_data_rcv(sk, skb);
4212 len = skb->len;
4213
4214 /*
4215 * We can just drop the corrupted I-frame here.
4216 * Receiver will miss it and start proper recovery
4217 * procedures and ask retransmission.
4218 */
4219 if (l2cap_check_fcs(pi, skb))
4220 goto drop;
4221
4222 if (__is_sar_start(control) && __is_iframe(control))
4223 len -= 2;
4224
4225 if (pi->fcs == L2CAP_FCS_CRC16)
4226 len -= 2;
4227
4228 if (len > pi->mps) {
4229 l2cap_send_disconn_req(pi->conn, sk, ECONNRESET);
4230 goto drop;
4231 }
4232
4233 req_seq = __get_reqseq(control);
4234 req_seq_offset = (req_seq - pi->expected_ack_seq) % 64;
4235 if (req_seq_offset < 0)
4236 req_seq_offset += 64;
4237
4238 next_tx_seq_offset =
4239 (pi->next_tx_seq - pi->expected_ack_seq) % 64;
4240 if (next_tx_seq_offset < 0)
4241 next_tx_seq_offset += 64;
4242
4243 /* check for invalid req-seq */
4244 if (req_seq_offset > next_tx_seq_offset) {
4245 l2cap_send_disconn_req(pi->conn, sk, ECONNRESET);
4246 goto drop;
4247 }
4248
4249 if (__is_iframe(control)) {
4250 if (len < 0) {
4251 l2cap_send_disconn_req(pi->conn, sk, ECONNRESET);
4252 goto drop;
4253 }
4254
4255 l2cap_data_channel_iframe(sk, control, skb);
4256 } else { 4286 } else {
4257 if (len != 0) { 4287 if (sk_add_backlog(sk, skb))
4258 l2cap_send_disconn_req(pi->conn, sk, ECONNRESET);
4259 goto drop; 4288 goto drop;
4260 }
4261
4262 l2cap_data_channel_sframe(sk, control, skb);
4263 } 4289 }
4264 4290
4265 goto done; 4291 goto done;