Commit 218bb9df authored by Gustavo F. Padovan's avatar Gustavo F. Padovan Committed by Marcel Holtmann

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: default avatarGustavo F. Padovan <padovan@profusion.mobi>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent e0f66218
...@@ -77,6 +77,8 @@ static void l2cap_sock_kill(struct sock *sk); ...@@ -77,6 +77,8 @@ static void l2cap_sock_kill(struct sock *sk);
static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
u8 code, u8 ident, u16 dlen, void *data); u8 code, u8 ident, u16 dlen, void *data);
static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb);
/* ---- L2CAP timers ---- */ /* ---- L2CAP timers ---- */
static void l2cap_sock_timeout(unsigned long arg) static void l2cap_sock_timeout(unsigned long arg)
{ {
...@@ -2447,6 +2449,8 @@ static inline void l2cap_ertm_init(struct sock *sk) ...@@ -2447,6 +2449,8 @@ static inline void l2cap_ertm_init(struct sock *sk)
__skb_queue_head_init(BUSY_QUEUE(sk)); __skb_queue_head_init(BUSY_QUEUE(sk));
INIT_WORK(&l2cap_pi(sk)->busy_work, l2cap_busy_work); INIT_WORK(&l2cap_pi(sk)->busy_work, l2cap_busy_work);
sk->sk_backlog_rcv = l2cap_ertm_data_rcv;
} }
static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask) static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask)
...@@ -4171,42 +4175,13 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str ...@@ -4171,42 +4175,13 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str
return 0; return 0;
} }
static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb) static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb)
{ {
struct sock *sk; struct l2cap_pinfo *pi = l2cap_pi(sk);
struct l2cap_pinfo *pi;
u16 control; u16 control;
u8 tx_seq, req_seq; u8 req_seq;
int len, next_tx_seq_offset, req_seq_offset; int len, next_tx_seq_offset, req_seq_offset;
sk = l2cap_get_chan_by_scid(&conn->chan_list, cid);
if (!sk) {
BT_DBG("unknown cid 0x%4.4x", cid);
goto drop;
}
pi = l2cap_pi(sk);
BT_DBG("sk %p, len %d", sk, skb->len);
if (sk->sk_state != BT_CONNECTED)
goto drop;
switch (pi->mode) {
case L2CAP_MODE_BASIC:
/* If socket recv buffers overflows we drop data here
* which is *bad* because L2CAP has to be reliable.
* But we don't have any other choice. L2CAP doesn't
* provide flow control mechanism. */
if (pi->imtu < skb->len)
goto drop;
if (!sock_queue_rcv_skb(sk, skb))
goto done;
break;
case L2CAP_MODE_ERTM:
control = get_unaligned_le16(skb->data); control = get_unaligned_le16(skb->data);
skb_pull(skb, 2); skb_pull(skb, 2);
len = skb->len; len = skb->len;
...@@ -4255,6 +4230,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk ...@@ -4255,6 +4230,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
l2cap_data_channel_iframe(sk, control, skb); l2cap_data_channel_iframe(sk, control, skb);
} else { } else {
if (len != 0) { if (len != 0) {
BT_ERR("%d", len);
l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); l2cap_send_disconn_req(pi->conn, sk, ECONNRESET);
goto drop; goto drop;
} }
...@@ -4262,6 +4238,56 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk ...@@ -4262,6 +4238,56 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
l2cap_data_channel_sframe(sk, control, skb); l2cap_data_channel_sframe(sk, control, skb);
} }
return 0;
drop:
kfree_skb(skb);
return 0;
}
static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb)
{
struct sock *sk;
struct l2cap_pinfo *pi;
u16 control;
u8 tx_seq;
int len;
sk = l2cap_get_chan_by_scid(&conn->chan_list, cid);
if (!sk) {
BT_DBG("unknown cid 0x%4.4x", cid);
goto drop;
}
pi = l2cap_pi(sk);
BT_DBG("sk %p, len %d", sk, skb->len);
if (sk->sk_state != BT_CONNECTED)
goto drop;
switch (pi->mode) {
case L2CAP_MODE_BASIC:
/* If socket recv buffers overflows we drop data here
* which is *bad* because L2CAP has to be reliable.
* But we don't have any other choice. L2CAP doesn't
* provide flow control mechanism. */
if (pi->imtu < skb->len)
goto drop;
if (!sock_queue_rcv_skb(sk, skb))
goto done;
break;
case L2CAP_MODE_ERTM:
if (!sock_owned_by_user(sk)) {
l2cap_ertm_data_rcv(sk, skb);
} else {
if (sk_add_backlog(sk, skb))
goto drop;
}
goto done; goto done;
case L2CAP_MODE_STREAMING: case L2CAP_MODE_STREAMING:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment