Linuxでユーザー空間で動作するプログラムとハードウェアがメモリを共有するためのデバイスドライバ(キャッシュのフラッシュと無効化を追加)

#はじめに

前回投稿した[「Linuxでユーザー空間で動作するプログラムとハードウェアがメモリを共有するためのデバイスドライバ](http://qiita.com/ikwzm/items/cc1bb33ff43a491440ea)で紹介した[udmabuf](https://github.com/ikwzm/udmabuf)に、外部から明示的にCPUキャッシュのフラッシュ(Flush)および無効化(Invalidiate)を指示する手段を追加しました。
インストールやデバイスツリーの設定に関しては[前の記事](http://qiita.com/ikwzm/items/cc1bb33ff43a491440ea)を参照してください。

ソースコードはこちらにあります。
[https://github.com/ikwzm/udmabuf](https://github.com/ikwzm/udmabuf)

#デバイスファイル

udmabufをinsmodでカーネルにロードすると、次のようなデバイスファイルが作成されます。

 - /dev/udmabuf[0-31]
 - /sys/class/udmabuf/udmabuf[0-31]/phys_addr
 - /sys/class/udmabuf/udmabuf[0-31]/size
 - /sys/class/udmabuf/udmabuf[0-31]/sync_mode
 - /sys/class/udmabuf/udmabuf[0-31]/sync_offset
 - /sys/class/udmabuf/udmabuf[0-31]/sync_size
 - /sys/class/udmabuf/udmabuf[0-31]/sync_direction
 - /sys/class/udmabuf/udmabuf[0-31]/sync_owner
 - /sys/class/udmabuf/udmabuf[0-31]/sync_for_cpu
 - /sys/class/udmabuf/udmabuf[0-31]/sync_for_device

sync_offset、sync_size、sync_direction、sync_owner、sync_for_cpu、sync_for_device が新たに追加した変数です。

### sync_offset

/sys/class/udmabuf/udmabuf[0-31]/sync_offset は udmabufのキャッシュ制御を手動で行う際のバッファの範囲の先頭を指定します。


```C:udmabuf_test.c
    unsigned char  attr[1024];
    unsigned long  sync_offset = 0x00000000;
    if ((fd  = open("/sys/class/udmabuf/udmabuf0/sync_offset", O_WRONLY)) != -1) {
        sprintf(attr, "%d", sync_offset);
        write(fd, attr, strlen(attr));
        close(fd);
    }
```

### sync_size

/sys/class/udmabuf/udmabuf[0-31]/sync_size は udmabufのキャッシュ制御を手動で行う際のバッファの範囲のサイズを指定します。


```C:udmabuf_test.c
    unsigned char  attr[1024];
    unsigned long  sync_size = 1024;
    if ((fd  = open("/sys/class/udmabuf/udmabuf0/sync_size", O_WRONLY)) != -1) {
        sprintf(attr, "%d", sync_size);
        write(fd, attr, strlen(attr));
        close(fd);
    }
```

### sync_direction

/sys/class/udmabuf/udmabuf[0-31]/sync_direction は udmabufのキャッシュ制御を手動で行う際のDMAの方向を指定します。

 - 0: DMA_BIDIRECTIONALを指定します。
 - 1: DMA_TO_DEVICEを指定します。
 - 2: DMA_FROM_DEVICEを指定します。


```C:udmabuf_test.c
    unsigned char  attr[1024];
    unsigned long  sync_direction = 1;
    if ((fd  = open("/sys/class/udmabuf/udmabuf0/sync_direction", O_WRONLY)) != -1) {
        sprintf(attr, "%d", sync_direction);
        write(fd, attr, strlen(attr));
        close(fd);
    }
```

### sync_owner

/sys/class/udmabuf/udmabuf[0-31]/sync_owner は udmabufのキャッシュ制御を手動で行った際に、現在のバッファのオーナーがCPUかDEVICEを読み取ります。


```C:udmabuf_test.c
    unsigned char  attr[1024];
    int sync_owner;
    if ((fd  = open("/sys/class/udmabuf/udmabuf0/sync_owner", O_RDONLY)) != -1) {
        read(fd, attr, 1024);
        sscanf(attr, "%x", &sync_owner);
        close(fd);
    }

```

### sync_for_cpu

/sys/class/udmabuf/udmabuf[0-31]/sync_for_cpu はudmabufのキャッシュ制御を手動で行う際に、このデバイスドライバに1を書き込むことでバッファのオーナーをCPUにします。その際、sync_directionが2(=DMA_FROM_DEVICE)または0(=DMA_BIDIRECTIONAL)だった時、sync_offsetとsync_size で指定された領域のCPUキャッシュが無効化(Invalidiate)されます。


```C:udmabuf_test.c
    unsigned char  attr[1024];
    unsigned long  sync_for_cpu = 1;
    if ((fd  = open("/sys/class/udmabuf/udmabuf0/sync_for_cpu", O_WRONLY)) != -1) {
        sprintf(attr, "%d", sync_for_cpu);
        write(fd, attr, strlen(attr));
        close(fd);
    }
```

### sync_for_device

/sys/class/udmabuf/udmabuf[0-31]/sync_for_deviceはudmabufのキャッシュ制御を手動で行う際に、このデバイスドライバに1を書き込むことでバッファのオーナーをDEVICEにします。その際、sync_directionが1(=DMA_TO_DEVICE)または0(=DMA_BIDIRECTIONAL)だった時、sync_offsetとsync_size で指定された領域のCPUキャッシュがフラッシュされます。


```C:udmabuf_test.c
    unsigned char  attr[1024];
    unsigned long  sync_for_device = 1;
    if ((fd  = open("/sys/class/udmabuf/udmabuf0/sync_for_device", O_WRONLY)) != -1) {
        sprintf(attr, "%d", sync_for_device);
        write(fd, attr, strlen(attr));
        close(fd);
    }
```

# 使い方

CPUキャッシュを有効にする場合は、O_SYNCフラグを設定せずにudmabufをopen します。

```C:udmabuf_test.c
    /* CPUキャッシュを有効にする場合はO_SYNCをつけずにopen する */
    if ((fd  = open("/dev/udmabuf0", O_RDWR)) != -1) {
        buf = mmap(NULL, buf_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
        /* ここでbufに読み書きする処理を行う */
        close(fd);
    }

```

アクセラレーターと共有するバッファの範囲をsync_offsetとsync_sizeで指定します。sync_offsetはmmap()で確保した先頭アドレスからのオフセット値を指定します。sync_sizeは共有するバッファの大きさをバイト数で指定します。

アクセラレータがバッファからデータを読むだけの場合は、sync_direction に1(=DMA_TO_DEVICE)を指定します。

アクセラレータがバッファにデータを書き込むだけの場合は、sync_direction に2(=DMA_FROM_DEVICE)を指定します。

アクセラレータがバッファにデータを読み書き両方行う場合は、sync_direction に0(=DMA_BIDIRECTIONAL)を指定します。



以上の設定の後、CPUがバッファにアクセスする前に sync_for_cpu に1を書いてバッファのオーナーをCPUにします。この際、sync_direction が2か0の時、sync_offsetとsync_sizeで指定された範囲のCPUキャッシュを無効化(Invalidiate)します。一度この操作を行ってバッファのオーナーをCPUにした後は、アクセラレーターがバッファをアクセスしないようにしなければなりません。


アクセラレータがバッファにアクセスする前にsync_for_deviceに1を書いてバッファのオーナーをデバイスにします。この際、sync_directionが1か0の時、sync_offsetとsync_sizeで指定された範囲のCPUキャッシュをフラッシュします。一度この操作を行ってバッファのオーナーをアクセラレーターにした後は、CPUがこのバッファをアクセスしてはいけません。