Bluetooth: L2CAP: Remove use of k_sem for credits

With the changes that introduced a queue k_sem is only used with
K_NO_WAIT which means it is no longer possible to wait/block for credits
so the usage of k_sem is no longer needed and can be safely replaced
with atomic_t just to count the available credits at a given instant.

Fixes #19922

Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
Luiz Augusto von Dentz 2019-12-18 13:27:36 -08:00 committed by Johan Hedberg
commit 4ff711d8a5
2 changed files with 33 additions and 23 deletions

View file

@ -118,7 +118,7 @@ struct bt_l2cap_le_endpoint {
/** Endpoint initial credits */ /** Endpoint initial credits */
u16_t init_credits; u16_t init_credits;
/** Endpoint credits */ /** Endpoint credits */
struct k_sem credits; atomic_t credits;
}; };
/** @brief LE L2CAP Channel structure. */ /** @brief LE L2CAP Channel structure. */

View file

@ -719,7 +719,7 @@ static void l2cap_chan_rx_init(struct bt_l2cap_le_chan *chan)
* be used. * be used.
*/ */
chan->rx.mps = MIN(chan->rx.mtu + 2, L2CAP_MAX_LE_MPS); chan->rx.mps = MIN(chan->rx.mtu + 2, L2CAP_MAX_LE_MPS);
k_sem_init(&chan->rx.credits, 0, UINT_MAX); atomic_set(&chan->rx.credits, 0);
if (BT_DBG_ENABLED && if (BT_DBG_ENABLED &&
chan->rx.init_credits * chan->rx.mps < chan->rx.mtu + 2) { chan->rx.init_credits * chan->rx.mps < chan->rx.mtu + 2) {
@ -772,7 +772,7 @@ static void l2cap_chan_tx_init(struct bt_l2cap_le_chan *chan)
BT_DBG("chan %p", chan); BT_DBG("chan %p", chan);
(void)memset(&chan->tx, 0, sizeof(chan->tx)); (void)memset(&chan->tx, 0, sizeof(chan->tx));
k_sem_init(&chan->tx.credits, 0, UINT_MAX); atomic_set(&chan->tx.credits, 0);
k_fifo_init(&chan->tx_queue); k_fifo_init(&chan->tx_queue);
k_work_init(&chan->tx_work, l2cap_chan_tx_process); k_work_init(&chan->tx_work, l2cap_chan_tx_process);
} }
@ -782,9 +782,7 @@ static void l2cap_chan_tx_give_credits(struct bt_l2cap_le_chan *chan,
{ {
BT_DBG("chan %p credits %u", chan, credits); BT_DBG("chan %p credits %u", chan, credits);
while (credits--) { atomic_add(&chan->tx.credits, credits);
k_sem_give(&chan->tx.credits);
}
if (!atomic_test_and_set_bit(chan->chan.status, BT_L2CAP_STATUS_OUT) && if (!atomic_test_and_set_bit(chan->chan.status, BT_L2CAP_STATUS_OUT) &&
chan->chan.ops->status) { chan->chan.ops->status) {
@ -797,9 +795,7 @@ static void l2cap_chan_rx_give_credits(struct bt_l2cap_le_chan *chan,
{ {
BT_DBG("chan %p credits %u", chan, credits); BT_DBG("chan %p credits %u", chan, credits);
while (credits--) { atomic_add(&chan->rx.credits, credits);
k_sem_give(&chan->rx.credits);
}
} }
static void l2cap_chan_destroy(struct bt_l2cap_chan *chan) static void l2cap_chan_destroy(struct bt_l2cap_chan *chan)
@ -1226,7 +1222,7 @@ segment:
static void l2cap_chan_tx_resume(struct bt_l2cap_le_chan *ch) static void l2cap_chan_tx_resume(struct bt_l2cap_le_chan *ch)
{ {
if (!k_sem_count_get(&ch->tx.credits) || if (!atomic_get(&ch->tx.credits) ||
(k_fifo_is_empty(&ch->tx_queue) && !ch->tx_buf)) { (k_fifo_is_empty(&ch->tx_queue) && !ch->tx_buf)) {
return; return;
} }
@ -1256,6 +1252,22 @@ static void l2cap_chan_seg_sent(struct bt_conn *conn, void *user_data)
l2cap_chan_tx_resume(BT_L2CAP_LE_CHAN(chan)); l2cap_chan_tx_resume(BT_L2CAP_LE_CHAN(chan));
} }
static bool test_and_dec(atomic_t *target)
{
atomic_t old_value, new_value;
do {
old_value = atomic_get(target);
if (!old_value) {
return false;
}
new_value = old_value - 1;
} while (atomic_cas(target, old_value, new_value) == 0);
return true;
}
/* This returns -EAGAIN whenever a segment cannot be send immediately which can /* This returns -EAGAIN whenever a segment cannot be send immediately which can
* happen under the following circuntances: * happen under the following circuntances:
* *
@ -1273,8 +1285,7 @@ static int l2cap_chan_le_send(struct bt_l2cap_le_chan *ch,
struct net_buf_simple_state state; struct net_buf_simple_state state;
int len, err; int len, err;
/* Wait for credits */ if (!test_and_dec(&ch->tx.credits)) {
if (k_sem_take(&ch->tx.credits, K_NO_WAIT)) {
BT_WARN("No credits to transmit packet"); BT_WARN("No credits to transmit packet");
return -EAGAIN; return -EAGAIN;
} }
@ -1284,12 +1295,12 @@ static int l2cap_chan_le_send(struct bt_l2cap_le_chan *ch,
seg = l2cap_chan_create_seg(ch, buf, sdu_hdr_len); seg = l2cap_chan_create_seg(ch, buf, sdu_hdr_len);
if (!seg) { if (!seg) {
k_sem_give(&ch->tx.credits); atomic_inc(&ch->tx.credits);
return -EAGAIN; return -EAGAIN;
} }
BT_DBG("ch %p cid 0x%04x len %u credits %u", ch, ch->tx.cid, BT_DBG("ch %p cid 0x%04x len %u credits %u", ch, ch->tx.cid,
seg->len, k_sem_count_get(&ch->tx.credits)); seg->len, atomic_get(&ch->tx.credits));
len = seg->len - sdu_hdr_len; len = seg->len - sdu_hdr_len;
@ -1306,7 +1317,7 @@ static int l2cap_chan_le_send(struct bt_l2cap_le_chan *ch,
if (err) { if (err) {
BT_WARN("Unable to send seg %d", err); BT_WARN("Unable to send seg %d", err);
k_sem_give(&ch->tx.credits); atomic_inc(&ch->tx.credits);
if (err == -ENOBUFS) { if (err == -ENOBUFS) {
/* Restore state since segment could not be sent */ /* Restore state since segment could not be sent */
@ -1320,7 +1331,7 @@ static int l2cap_chan_le_send(struct bt_l2cap_le_chan *ch,
/* Check if there is no credits left clear output status and notify its /* Check if there is no credits left clear output status and notify its
* change. * change.
*/ */
if (!k_sem_count_get(&ch->tx.credits)) { if (!atomic_get(&ch->tx.credits)) {
atomic_clear_bit(ch->chan.status, BT_L2CAP_STATUS_OUT); atomic_clear_bit(ch->chan.status, BT_L2CAP_STATUS_OUT);
if (ch->chan.ops->status) { if (ch->chan.ops->status) {
ch->chan.ops->status(&ch->chan, ch->chan.status); ch->chan.ops->status(&ch->chan, ch->chan.status);
@ -1414,7 +1425,7 @@ static void le_credits(struct bt_l2cap *l2cap, u8_t ident,
ch = BT_L2CAP_LE_CHAN(chan); ch = BT_L2CAP_LE_CHAN(chan);
if (k_sem_count_get(&ch->tx.credits) + credits > UINT16_MAX) { if (atomic_get(&ch->tx.credits) + credits > UINT16_MAX) {
BT_ERR("Credits overflow"); BT_ERR("Credits overflow");
bt_l2cap_chan_disconnect(chan); bt_l2cap_chan_disconnect(chan);
return; return;
@ -1422,8 +1433,7 @@ static void le_credits(struct bt_l2cap *l2cap, u8_t ident,
l2cap_chan_tx_give_credits(ch, credits); l2cap_chan_tx_give_credits(ch, credits);
BT_DBG("chan %p total credits %u", ch, BT_DBG("chan %p total credits %u", ch, atomic_get(&ch->tx.credits));
k_sem_count_get(&ch->tx.credits));
l2cap_chan_tx_resume(ch); l2cap_chan_tx_resume(ch);
} }
@ -1583,7 +1593,7 @@ static void l2cap_chan_send_credits(struct bt_l2cap_le_chan *chan,
bt_l2cap_send(chan->chan.conn, BT_L2CAP_CID_LE_SIG, buf); bt_l2cap_send(chan->chan.conn, BT_L2CAP_CID_LE_SIG, buf);
BT_DBG("chan %p credits %u", chan, k_sem_count_get(&chan->rx.credits)); BT_DBG("chan %p credits %u", chan, atomic_get(&chan->rx.credits));
} }
static void l2cap_chan_update_credits(struct bt_l2cap_le_chan *chan, static void l2cap_chan_update_credits(struct bt_l2cap_le_chan *chan,
@ -1594,7 +1604,7 @@ static void l2cap_chan_update_credits(struct bt_l2cap_le_chan *chan,
/* Restore enough credits to complete the sdu */ /* Restore enough credits to complete the sdu */
credits = ((chan->_sdu_len - net_buf_frags_len(buf)) + credits = ((chan->_sdu_len - net_buf_frags_len(buf)) +
(chan->rx.mps - 1)) / chan->rx.mps; (chan->rx.mps - 1)) / chan->rx.mps;
credits -= k_sem_count_get(&chan->rx.credits); credits -= atomic_get(&chan->rx.credits);
if (credits <= 0) { if (credits <= 0) {
return; return;
} }
@ -1705,7 +1715,7 @@ static void l2cap_chan_le_recv_seg(struct bt_l2cap_le_chan *chan,
* should only happen if the remote cannot fully utilize the * should only happen if the remote cannot fully utilize the
* MPS for some reason. * MPS for some reason.
*/ */
if (!k_sem_count_get(&chan->rx.credits) && if (!atomic_get(&chan->rx.credits) &&
seg == chan->rx.init_credits) { seg == chan->rx.init_credits) {
l2cap_chan_update_credits(chan, buf); l2cap_chan_update_credits(chan, buf);
} }
@ -1725,7 +1735,7 @@ static void l2cap_chan_le_recv(struct bt_l2cap_le_chan *chan,
u16_t sdu_len; u16_t sdu_len;
int err; int err;
if (k_sem_take(&chan->rx.credits, K_NO_WAIT)) { if (!test_and_dec(&chan->rx.credits)) {
BT_ERR("No credits to receive packet"); BT_ERR("No credits to receive packet");
bt_l2cap_chan_disconnect(&chan->chan); bt_l2cap_chan_disconnect(&chan->chan);
return; return;