From 2dbd3acb4dcaf94efc7cc64fb9a3155562c0e8f3 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 4 Jan 2018 05:06:21 -0800 Subject: [PATCH] Minimize amount of Flow Control packets for L2CAP CoC In order to not waste bandwidth on Flow Control packets with credits: 1. Set Initial Credits in the connect request/response to the maximum value 2. Send credits only when the remote has less than 64 of them left, increasing the value back to maximum. Bug: 68359837 Test: observe data flow in btsnoop log Change-Id: I34e1fecc55422b34df0b1e82bb2b402eb06620bd --- stack/gap/gap_conn.cc | 6 +++++- stack/l2cap/l2c_api.cc | 10 ++++++++-- stack/l2cap/l2c_csm.cc | 2 +- stack/l2cap/l2c_int.h | 25 +++++++++++++++++++------ stack/l2cap/l2c_main.cc | 15 ++++++++++++--- 5 files changed, 45 insertions(+), 13 deletions(-) diff --git a/stack/gap/gap_conn.cc b/stack/gap/gap_conn.cc index 663482f9f..72183bb06 100644 --- a/stack/gap/gap_conn.cc +++ b/stack/gap/gap_conn.cc @@ -210,7 +210,7 @@ uint16_t GAP_ConnOpen(const char* p_serv_name, uint8_t service_id, /* Configure L2CAP COC, if transport is LE */ if (transport == BT_TRANSPORT_LE) { - p_ccb->local_coc_cfg.credits = L2CAP_LE_DEFAULT_CREDIT; + p_ccb->local_coc_cfg.credits = L2CAP_LE_CREDIT_DEFAULT; p_ccb->local_coc_cfg.mtu = p_cfg->mtu; uint16_t max_mps = controller_get_interface()->get_acl_data_size_ble(); @@ -219,6 +219,10 @@ uint16_t GAP_ConnOpen(const char* p_serv_name, uint8_t service_id, le_mps = max_mps; } p_ccb->local_coc_cfg.mps = le_mps; + + VLOG(2) << __func__ << ": credits=" << p_ccb->local_coc_cfg.credits + << ", mps=" << p_ccb->local_coc_cfg.mps + << ", mtu=" << p_ccb->local_coc_cfg.mtu; } p_ccb->p_callback = p_cb; diff --git a/stack/l2cap/l2c_api.cc b/stack/l2cap/l2c_api.cc index 5ce141eef..e280b6702 100644 --- a/stack/l2cap/l2c_api.cc +++ b/stack/l2cap/l2c_api.cc @@ -555,7 +555,10 @@ uint16_t L2CA_ConnectLECocReq(uint16_t psm, const RawAddress& p_bd_addr, p_ccb->p_rcb = p_rcb; /* Save the configuration */ - if (p_cfg) memcpy(&p_ccb->local_conn_cfg, p_cfg, sizeof(tL2CAP_LE_CFG_INFO)); + if (p_cfg) { + memcpy(&p_ccb->local_conn_cfg, p_cfg, sizeof(tL2CAP_LE_CFG_INFO)); + p_ccb->remote_credit_count = p_cfg->credits; + } /* If link is up, start the L2CAP connection */ if (p_lcb->link_state == LST_CONNECTED) { @@ -625,7 +628,10 @@ bool L2CA_ConnectLECocRsp(const RawAddress& p_bd_addr, uint8_t id, return false; } - if (p_cfg) memcpy(&p_ccb->local_conn_cfg, p_cfg, sizeof(tL2CAP_LE_CFG_INFO)); + if (p_cfg) { + memcpy(&p_ccb->local_conn_cfg, p_cfg, sizeof(tL2CAP_LE_CFG_INFO)); + p_ccb->remote_credit_count = p_cfg->credits; + } if (result == L2CAP_CONN_OK) l2c_csm_execute(p_ccb, L2CEVT_L2CA_CONNECT_RSP, NULL); diff --git a/stack/l2cap/l2c_csm.cc b/stack/l2cap/l2c_csm.cc index 2fe0d0be1..97841f246 100644 --- a/stack/l2cap/l2c_csm.cc +++ b/stack/l2cap/l2c_csm.cc @@ -1105,7 +1105,7 @@ static void l2c_csm_open(tL2C_CCB* p_ccb, uint16_t event, void* p_data) { case L2CEVT_L2CAP_RECV_FLOW_CONTROL_CREDIT: credit = (uint16_t*)p_data; L2CAP_TRACE_DEBUG("%s Credits received %d", __func__, *credit); - if ((p_ccb->peer_conn_cfg.credits + *credit) > L2CAP_LE_MAX_CREDIT) { + if ((p_ccb->peer_conn_cfg.credits + *credit) > L2CAP_LE_CREDIT_MAX) { /* we have received credits more than max coc credits, * so disconnecting the Le Coc Channel */ diff --git a/stack/l2cap/l2c_int.h b/stack/l2cap/l2c_int.h index 83c5833d5..421e34d7d 100644 --- a/stack/l2cap/l2c_int.h +++ b/stack/l2cap/l2c_int.h @@ -37,12 +37,21 @@ #define L2CAP_MIN_MTU 48 /* Minimum acceptable MTU is 48 bytes */ /* LE credit based L2CAP connection parameters */ -#define L2CAP_LE_MIN_MTU 23 -#define L2CAP_LE_MIN_MPS 23 -#define L2CAP_LE_MAX_MPS 65533 -#define L2CAP_LE_MIN_CREDIT 0 -#define L2CAP_LE_MAX_CREDIT 65535 -#define L2CAP_LE_DEFAULT_CREDIT 1 +constexpr uint16_t L2CAP_LE_MIN_MTU = 23; // Minimum SDU size +constexpr uint16_t L2CAP_LE_MIN_MPS = 23; +constexpr uint16_t L2CAP_LE_MAX_MPS = 65533; +constexpr uint16_t L2CAP_LE_CREDIT_MAX = 65535; + +// This is initial amout of credits we send, and amount to which we increase +// credits once they fall below threshold +constexpr uint16_t L2CAP_LE_CREDIT_DEFAULT = 0xffff; + +// If credit count on remote fall below this value, we send back credits to +// reach default value. +constexpr uint16_t L2CAP_LE_CREDIT_THRESHOLD = 0x0040; + +static_assert(L2CAP_LE_CREDIT_THRESHOLD < L2CAP_LE_CREDIT_DEFAULT, + "Threshold must be smaller then default credits"); /* * Timeout values (in milliseconds). @@ -327,6 +336,10 @@ typedef struct t_l2c_ccb { uint16_t fixed_chnl_idle_tout; /* Idle timeout to use for the fixed channel */ #endif uint16_t tx_data_len; + + /* Number of LE frames that the remote can send to us (credit count in + * remote). Valid only for LE CoC */ + uint16_t remote_credit_count; } tL2C_CCB; /*********************************************************************** diff --git a/stack/l2cap/l2c_main.cc b/stack/l2cap/l2c_main.cc index 611470247..686148879 100644 --- a/stack/l2cap/l2c_main.cc +++ b/stack/l2cap/l2c_main.cc @@ -215,9 +215,18 @@ void l2c_rcv_acl_data(BT_HDR* p_msg) { if (p_lcb->transport == BT_TRANSPORT_LE) { l2c_lcc_proc_pdu(p_ccb, p_msg); - // Got a pkt, valid send out credits to the peer device - uint16_t credit = L2CAP_LE_DEFAULT_CREDIT; - l2c_csm_execute(p_ccb, L2CEVT_L2CA_SEND_FLOW_CONTROL_CREDIT, &credit); + + /* The remote device has one less credit left */ + --p_ccb->remote_credit_count; + + /* If the credits left on the remote device are getting low, send some */ + if (p_ccb->remote_credit_count <= L2CAP_LE_CREDIT_THRESHOLD) { + uint16_t credits = L2CAP_LE_CREDIT_DEFAULT - p_ccb->remote_credit_count; + p_ccb->remote_credit_count = L2CAP_LE_CREDIT_DEFAULT; + + /* Return back credits */ + l2c_csm_execute(p_ccb, L2CEVT_L2CA_SEND_FLOW_CONTROL_CREDIT, &credits); + } } else { /* Basic mode packets go straight to the state machine */ if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE) -- 2.11.0