mynewt-nimble icon indicating copy to clipboard operation
mynewt-nimble copied to clipboard

Mismatch of number of completed packtes in host flow control

Open IshaESP opened this issue 1 year ago • 0 comments

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.

IshaESP avatar Apr 04 '23 09:04 IshaESP