mynewt-nimble
mynewt-nimble copied to clipboard
Mismatch of number of completed packtes in host flow control
In nimble 1.4.0, ble_hs_flow_acl_free() was getting called irrespective of whether the packet is allocate by LL layer or host. In nimble 1.5.0, ble_hs_flow_acl_free() gets called by ble_transport_acl_put() when LL layer packet is to be freed.
During execution, it is observed that if contoller sends packet of length 517 bytes , and it is received in the chunks of 255 255 and 7 ( since acl max payload size is set to 255) . Now, stripping off the header we are left with 500 bytes . Nimble host will eventually create a chain of buffer and this amount of data fits into 2 packets. While freeing up these 500 bytes, ble_transport_acl_put() gets called twice and ble_hs_flow_num_completed_pkts gets incremented twice instead of thrice(as 3 chunks were sent by controller). This causes a mismatch.
Using a sample application of continuosly doing gatt read with MTU set to 517, we easily see a issue that after BLE_ACL_BUF_COUNT - 1 number of reads the available number of buffers don't match and the controller eventually sends a disconnect.
mpe_put_cb is called to free an ACL packet.
In nimble 1.4.0:
In file ble_hs_flow.c, ble_hs_flow_acl_free() was passed to set as a callback:
ble_hci_trans_set_acl_free_cb(ble_hs_flow_acl_free, NULL);
In esp_nimble_hci.c, ble_hs_flow_acl_free() was set as mpe_put_cb:
/**
* Unsupported; the RAM transport does not have a dedicated ACL data packet
* pool.
*/
int ble_hci_trans_set_acl_free_cb(os_mempool_put_fn *cb, void *arg)
{
ble_hci_acl_pool.mpe_put_cb = cb;
ble_hci_acl_pool.mpe_put_arg = arg;
return 0;
}
mpe_cut_cb was directly calling ble_hs_flow_acl_free() thrice.
In nimble 1.5.0: In file transport.c, ble_transport_acl_put() was set as mpe_put_cb and transport_put_acl_from_ll_cb was set as ble_hs_flow_acl_free()
pool_acl.mpe_put_cb = ble_transport_acl_put;
int
ble_transport_register_put_acl_from_ll_cb(os_mempool_put_fn (*cb))
{
transport_put_acl_from_ll_cb = cb;
return 0;
}
In ble_hs_flow.c:
ble_transport_register_put_acl_from_ll_cb(ble_hs_flow_acl_free);
In ble_transport_acl_put(), if packet is from LL layer on then transport_put_acl_from_ll_cb is called :
if (from_ll && transport_put_acl_from_ll_cb) {
err = transport_put_acl_from_ll_cb(mpe, data, arg);
do_put = false;
}
This check only calls transport_put_acl_from_ll_cb twice which inturns calls ble_hs_flow_acl_free twice.