]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/bluetooth/l2cap_core.c
rps: NUMA flow limit allocations
[karo-tx-linux.git] / net / bluetooth / l2cap_core.c
index 82aa6f352b202a1980cb59f0a37b9689622fbb53..b6bca64b320d23ba786573598174e7d0a8b73d32 100644 (file)
@@ -49,6 +49,9 @@ static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP | L2CAP_FC_CONNLESS, };
 static LIST_HEAD(chan_list);
 static DEFINE_RWLOCK(chan_list_lock);
 
+static u16 le_max_credits = L2CAP_LE_MAX_CREDITS;
+static u16 le_default_mps = L2CAP_LE_DEFAULT_MPS;
+
 static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
                                       u8 code, u8 ident, u16 dlen, void *data);
 static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
@@ -213,9 +216,14 @@ int l2cap_add_scid(struct l2cap_chan *chan,  __u16 scid)
 
 static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
 {
-       u16 cid = L2CAP_CID_DYN_START;
+       u16 cid, dyn_end;
+
+       if (conn->hcon->type == LE_LINK)
+               dyn_end = L2CAP_CID_LE_DYN_END;
+       else
+               dyn_end = L2CAP_CID_DYN_END;
 
-       for (; cid < L2CAP_CID_DYN_END; cid++) {
+       for (cid = L2CAP_CID_DYN_START; cid < dyn_end; cid++) {
                if (!__l2cap_get_chan_by_scid(conn, cid))
                        return cid;
        }
@@ -490,13 +498,16 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan)
        set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
 }
 
-void l2cap_le_flowctl_init(struct l2cap_chan *chan)
+static void l2cap_le_flowctl_init(struct l2cap_chan *chan)
 {
-       chan->imtu = L2CAP_DEFAULT_MTU;
-       chan->omtu = L2CAP_LE_MIN_MTU;
-       chan->mode = L2CAP_MODE_LE_FLOWCTL;
+       chan->sdu = NULL;
+       chan->sdu_last_frag = NULL;
+       chan->sdu_len = 0;
        chan->tx_credits = 0;
-       chan->rx_credits = L2CAP_LE_MAX_CREDITS;
+       chan->rx_credits = le_max_credits;
+       chan->mps = min_t(u16, chan->imtu, L2CAP_LE_DEFAULT_MPS);
+
+       skb_queue_head_init(&chan->tx_q);
 }
 
 void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
@@ -511,12 +522,12 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
        switch (chan->chan_type) {
        case L2CAP_CHAN_CONN_ORIENTED:
                if (conn->hcon->type == LE_LINK) {
-                       /* LE connection */
-                       chan->omtu = L2CAP_DEFAULT_MTU;
-                       if (chan->dcid == L2CAP_CID_ATT)
+                       if (chan->dcid == L2CAP_CID_ATT) {
+                               chan->omtu = L2CAP_DEFAULT_MTU;
                                chan->scid = L2CAP_CID_ATT;
-                       else
+                       } else {
                                chan->scid = l2cap_alloc_cid(conn);
+                       }
                } else {
                        /* Alloc CID for connection-oriented socket */
                        chan->scid = l2cap_alloc_cid(conn);
@@ -645,7 +656,7 @@ static void l2cap_chan_le_connect_reject(struct l2cap_chan *chan)
 
        rsp.dcid    = cpu_to_le16(chan->scid);
        rsp.mtu     = cpu_to_le16(chan->imtu);
-       rsp.mps     = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS);
+       rsp.mps     = cpu_to_le16(chan->mps);
        rsp.credits = cpu_to_le16(chan->rx_credits);
        rsp.result  = cpu_to_le16(result);
 
@@ -1195,26 +1206,14 @@ static void l2cap_move_done(struct l2cap_chan *chan)
        }
 }
 
-static void l2cap_le_flowctl_start(struct l2cap_chan *chan)
-{
-       chan->sdu = NULL;
-       chan->sdu_last_frag = NULL;
-       chan->sdu_len = 0;
-
-       skb_queue_head_init(&chan->tx_q);
-
-       if (!chan->tx_credits)
-               chan->ops->suspend(chan);
-}
-
 static void l2cap_chan_ready(struct l2cap_chan *chan)
 {
        /* This clears all conf flags, including CONF_NOT_COMPLETE */
        chan->conf_state = 0;
        __clear_chan_timer(chan);
 
-       if (chan->mode == L2CAP_MODE_LE_FLOWCTL)
-               l2cap_le_flowctl_start(chan);
+       if (chan->mode == L2CAP_MODE_LE_FLOWCTL && !chan->tx_credits)
+               chan->ops->suspend(chan);
 
        chan->state = BT_CONNECTED;
 
@@ -1232,7 +1231,7 @@ static void l2cap_le_connect(struct l2cap_chan *chan)
        req.psm     = chan->psm;
        req.scid    = cpu_to_le16(chan->scid);
        req.mtu     = cpu_to_le16(chan->imtu);
-       req.mps     = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS);
+       req.mps     = cpu_to_le16(chan->mps);
        req.credits = cpu_to_le16(chan->rx_credits);
 
        chan->ident = l2cap_get_ident(conn);
@@ -1846,6 +1845,18 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
        return c1;
 }
 
+static bool is_valid_psm(u16 psm, u8 dst_type)
+{
+       if (!psm)
+               return false;
+
+       if (bdaddr_type_is_le(dst_type))
+               return (psm <= 0x00ff);
+
+       /* PSM must be odd and lsb of upper byte must be 0 */
+       return ((psm & 0x0101) == 0x0001);
+}
+
 int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
                       bdaddr_t *dst, u8 dst_type)
 {
@@ -1866,8 +1877,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
 
        l2cap_chan_lock(chan);
 
-       /* PSM must be odd and lsb of upper byte must be 0 */
-       if ((__le16_to_cpu(psm) & 0x0101) != 0x0001 && !cid &&
+       if (!is_valid_psm(__le16_to_cpu(psm), dst_type) && !cid &&
            chan->chan_type != L2CAP_CHAN_RAW) {
                err = -EINVAL;
                goto done;
@@ -1880,7 +1890,9 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
 
        switch (chan->mode) {
        case L2CAP_MODE_BASIC:
+               break;
        case L2CAP_MODE_LE_FLOWCTL:
+               l2cap_le_flowctl_init(chan);
                break;
        case L2CAP_MODE_ERTM:
        case L2CAP_MODE_STREAMING:
@@ -3826,7 +3838,7 @@ void __l2cap_le_connect_rsp_defer(struct l2cap_chan *chan)
 
        rsp.dcid    = cpu_to_le16(chan->scid);
        rsp.mtu     = cpu_to_le16(chan->imtu);
-       rsp.mps     = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS);
+       rsp.mps     = cpu_to_le16(chan->mps);
        rsp.credits = cpu_to_le16(chan->rx_credits);
        rsp.result  = __constant_cpu_to_le16(L2CAP_CR_SUCCESS);
 
@@ -5634,6 +5646,8 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
                goto response_unlock;
        }
 
+       l2cap_le_flowctl_init(chan);
+
        bacpy(&chan->src, &conn->hcon->src);
        bacpy(&chan->dst, &conn->hcon->dst);
        chan->src_type = bdaddr_type(conn->hcon, conn->hcon->src_type);
@@ -5671,7 +5685,7 @@ response_unlock:
 response:
        if (chan) {
                rsp.mtu = cpu_to_le16(chan->imtu);
-               rsp.mps = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS);
+               rsp.mps = cpu_to_le16(chan->mps);
        } else {
                rsp.mtu = 0;
                rsp.mps = 0;
@@ -5722,6 +5736,31 @@ static inline int l2cap_le_credits(struct l2cap_conn *conn,
        return 0;
 }
 
+static inline int l2cap_le_command_rej(struct l2cap_conn *conn,
+                                      struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+                                      u8 *data)
+{
+       struct l2cap_cmd_rej_unk *rej = (struct l2cap_cmd_rej_unk *) data;
+       struct l2cap_chan *chan;
+
+       if (cmd_len < sizeof(*rej))
+               return -EPROTO;
+
+       mutex_lock(&conn->chan_lock);
+
+       chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
+       if (!chan)
+               goto done;
+
+       l2cap_chan_lock(chan);
+       l2cap_chan_del(chan, ECONNREFUSED);
+       l2cap_chan_unlock(chan);
+
+done:
+       mutex_unlock(&conn->chan_lock);
+       return 0;
+}
+
 static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
                                   struct l2cap_cmd_hdr *cmd, u16 cmd_len,
                                   u8 *data)
@@ -5741,6 +5780,7 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
 
        switch (cmd->code) {
        case L2CAP_COMMAND_REJ:
+               l2cap_le_command_rej(conn, cmd, cmd_len, data);
                break;
 
        case L2CAP_CONN_PARAM_UPDATE_REQ:
@@ -6805,10 +6845,10 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan)
        /* We return more credits to the sender only after the amount of
         * credits falls below half of the initial amount.
         */
-       if (chan->rx_credits >= (L2CAP_LE_MAX_CREDITS + 1) / 2)
+       if (chan->rx_credits >= (le_max_credits + 1) / 2)
                return;
 
-       return_credits = L2CAP_LE_MAX_CREDITS - chan->rx_credits;
+       return_credits = le_max_credits - chan->rx_credits;
 
        BT_DBG("chan %p returning %u credits to sender", chan, return_credits);
 
@@ -7422,6 +7462,11 @@ int __init l2cap_init(void)
        l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs,
                                            NULL, &l2cap_debugfs_fops);
 
+       debugfs_create_u16("l2cap_le_max_credits", 0466, bt_debugfs,
+                          &le_max_credits);
+       debugfs_create_u16("l2cap_le_default_mps", 0466, bt_debugfs,
+                          &le_default_mps);
+
        return 0;
 }