Bluedroidの実装 GKIのバッファ管理を読む

BluedroidのソースコードのGKI部分を読みました。

Bluedroidは、Android 4.x - Android 5.1 で使われていたBluedroidプロトコルスタックです。
Android 6.0以降は、BludrdoidをフォークしたFluorideに変更されています。

GKI (General Kernel Interface)はBluedroidのOS依存部分のコードです。

# ソースコード

* https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/master
 * https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/master/gki/common/gki_buffer.c
 * https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/master/gki/common/gki.h 
 * https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/master/gki/common/gki_common.h
 * https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/master/gki/ulinux/gki_int.h

# バッファ管理
## 解説
* メモリプールでバッファを管理する
* uITRONの固定長メモリプールと同様に、固定サイズのメモリブロックとして扱う。
* メモリブロックの使用/空きの管理はqueueを用いて管理する。
* 各メモリブロックの先頭には管理用のヘッダを持つ。

## APIリスト

* GKI_freebuf()はバッファの解放、GKI_getbuf()はサイズを指定してバッファの取得、GKI_get_buf_size()はバッファのサイズを取得、GKI_getpoolbuf()はプールを指定してバッファの取得。
* GKI_poolcount()はプール内のバッファの個数を取得、GKI_poolfreecount()はプール内の空いているバッファの個数、GKI_poolutilization はプールのバッファ使用率を0-100で返す。

```c:gki.h 
/* To get and release buffers, change owner and get size
*/
GKI_API extern void    GKI_freebuf (void *);
GKI_API extern void   *GKI_getbuf (UINT16);
GKI_API extern UINT16  GKI_get_buf_size (void *);
GKI_API extern void   *GKI_getpoolbuf (UINT8);
GKI_API extern UINT16  GKI_poolcount (UINT8);
GKI_API extern UINT16  GKI_poolfreecount (UINT8);
GKI_API extern UINT16  GKI_poolutilization (UINT8);
```

## バッファの初期化

* gki_buffer.c の gki_buffer_init() でバッファの初期化を行います。

* 制御用変数の初期化

```c:gki_buffer.c
    for (tt = 0; tt < GKI_NUM_TOTAL_BUF_POOLS; tt++)
    {
        p_cb->pool_start[tt] = NULL;
        p_cb->pool_end[tt]   = NULL;
        p_cb->pool_size[tt]  = 0;
        p_cb->freeq[tt].p_first = 0;
        p_cb->freeq[tt].p_last  = 0;
        p_cb->freeq[tt].size    = 0;
        p_cb->freeq[tt].total   = 0;
        p_cb->freeq[tt].cur_cnt = 0;
        p_cb->freeq[tt].max_cnt = 0;
    }
```

* GKI_USE_DYNAMIC_BUFFERS == TRUE ならmallocでプールを作成

```c:gki_buffer.c
#if (GKI_NUM_FIXED_BUF_POOLS > 0)
    p_cb->bufpool0 = (UINT8 *)GKI_os_malloc ((GKI_BUF0_SIZE + BUFFER_PADDING_SIZE) * GKI_BUF0_MAX);
#endif
```

* キューの初期化

```c:gki_buffer.c
#if (GKI_NUM_FIXED_BUF_POOLS > 0)
    gki_init_free_queue(0, GKI_BUF0_SIZE, GKI_BUF0_MAX, p_cb->bufpool0);
#endif
```

* バッファ制御用変数の更新

```c:gki_buffer.c
    /* add pools to the pool_list which is arranged in the order of size */
    for(i=0; i < GKI_NUM_FIXED_BUF_POOLS ; i++)
    {
        p_cb->pool_list[i] = i;
    }
    p_cb->curr_total_no_of_pools = GKI_NUM_FIXED_BUF_POOLS;
```

## 静的バッファの初期化
* GKI_USE_DYNAMIC_BUFFERS == FALSEならプールはgki_common.hで静的に確保

```c:gki_common.h
#if (GKI_NUM_FIXED_BUF_POOLS > 0)
    UINT8 bufpool0[(ALIGN_POOL(GKI_BUF0_SIZE) + BUFFER_PADDING_SIZE) * GKI_BUF0_MAX];
#endif
```

## バッファの取得

* gki_buffer.c の GKI_getpoolbuf () の中身です。

```c:gki_buffer.c
void *GKI_getpoolbuf (UINT8 pool_id)
{
    FREE_QUEUE_T  *Q;
    BUFFER_HDR_T  *p_hdr;
    tGKI_COM_CB *p_cb = &gki_cb.com;
```

* pool_idのチェック

```c:gki_buffer.c
    if (pool_id >= GKI_NUM_TOTAL_BUF_POOLS)
    {
        GKI_exception(GKI_ERROR_GETPOOLBUF_BAD_QID, "getpoolbuf bad pool");
        return (NULL);
    }
```

* GKI_disable()とGKI_enable()で排他制御
* p_cb->freeq[pool_id]で空きを確認
* GKI_USE_DEFERED_ALLOC_BUF_POOLS ならpoolの拡張ができる
* p_hdrが取得するバッファのヘッダの先頭
* バッファのヘッダに、task_id status p_next Type を設定する


```c:gki_buffer.c
    /* Make sure the buffers aren't disturbed til finished with allocation */
    GKI_disable();
    Q = &p_cb->freeq[pool_id];
    if(Q->cur_cnt < Q->total)
    {
// btla-specific ++
#ifdef GKI_USE_DEFERED_ALLOC_BUF_POOLS
        if(Q->p_first == 0 && gki_alloc_free_queue(pool_id) != TRUE)
        {
            GKI_enable();
            return NULL;
        }
#endif
// btla-specific --
        p_hdr = Q->p_first;
        Q->p_first = p_hdr->p_next;
        if (!Q->p_first)
            Q->p_last = NULL;
        if(++Q->cur_cnt > Q->max_cnt)
            Q->max_cnt = Q->cur_cnt;
        GKI_enable();
        p_hdr->task_id = GKI_get_taskid();
        p_hdr->status  = BUF_STATUS_UNLINKED;
        p_hdr->p_next  = NULL;
        p_hdr->Type    = 0;
        return ((void *) ((UINT8 *)p_hdr + BUFFER_HDR_SIZE));
    }
```

* 指定したpoolが空いていなかったら、GKI_getbuf()を試す。

```c:gki_buffer.c
    /* If here, no buffers in the specified pool */
    GKI_enable();
    /* try for free buffers in public pools */
    return (GKI_getbuf(p_cb->freeq[pool_id].size));
}
```


## バッファの解放

* gki_buffer.c の GKI_freebuf () の中身です。

```c:gki_buffer.c
void GKI_freebuf (void *p_buf)
{
    FREE_QUEUE_T    *Q;
    BUFFER_HDR_T    *p_hdr;
```

* バッファのヘッダをチェックし、問題があるなら、GKI_exception()を呼ぶ。

```c:gki_buffer.c
#if (GKI_ENABLE_BUF_CORRUPTION_CHECK == TRUE)
    if (!p_buf || gki_chk_buf_damage(p_buf))
    {
        GKI_exception(GKI_ERROR_BUF_CORRUPTED, "Free - Buf Corrupted");
        return;
    }
#endif
    p_hdr = (BUFFER_HDR_T *) ((UINT8 *)p_buf - BUFFER_HDR_SIZE);
    if (p_hdr->status != BUF_STATUS_UNLINKED)
    {
        GKI_exception(GKI_ERROR_FREEBUF_BUF_LINKED, "Freeing Linked Buf");
        return;
    }
    if (p_hdr->q_id >= GKI_NUM_TOTAL_BUF_POOLS)
    {
        GKI_exception(GKI_ERROR_FREEBUF_BAD_QID, "Bad Buf QId");
        return;
    }
```

* poolのquueueにバッファを追加する。
* バッファのヘッダ p_next status task_id を更新する

```c:gki_buffer.c
    GKI_disable();
    /*
    ** Release the buffer
    */
    Q  = &gki_cb.com.freeq[p_hdr->q_id];
    if (Q->p_last)
        Q->p_last->p_next = p_hdr;
    else
        Q->p_first = p_hdr;
    Q->p_last      = p_hdr;
    p_hdr->p_next  = NULL;
    p_hdr->status  = BUF_STATUS_FREE;
    p_hdr->task_id = GKI_INVALID_TASK;
    if (Q->cur_cnt > 0)
        Q->cur_cnt--;
    GKI_enable();
    return;
}
```