Linux 5.1をCyclone V SoCで実行しています。これは、1つのチップに2つのARMv7コアを備えたFPGAです。私の目標は、外部インターフェースから大量のデータを収集し、このデータ(の一部)をTCPソケット経由でストリーミングすることです。ここでの課題は、データレートが非常に高く、GbEインターフェイスが飽和状態になる可能性があることです。私はwrite()
ソケットへの呼び出しを使用するだけの実用的な実装を持っていますが、55MB /秒で最高です。理論上のGbE制限の約半分。現在、ゼロコピーTCP送信を機能させてスループットを向上させようとしていますが、壁にぶつかっています。
FPGAからLinuxユーザー空間にデータを取り込むために、カーネルドライバーを作成しました。このドライバーは、FPGAのDMAブロックを使用して、外部インターフェイスからARMv7コアに接続されたDDR3メモリに大量のデータをコピーします。ドライバは、使用してプローブするときの連続1MBのバッファの束としてこのメモリを割り当てdma_alloc_coherent()
てGFP_USER
、そして実装することで、ユーザ空間アプリケーションにこれらを公開mmap()
中のファイルに/dev/
して使用してアプリケーションにアドレスを返すdma_mmap_coherent()
事前に割り当てられたバッファに。
ここまでは順調ですね; ユーザー空間アプリケーションは有効なデータを表示しており、スループットは360MB /秒以上で十分であり、余裕があります(外部インターフェイスは、上限が実際にわかるほど高速ではありません)。
ゼロコピーTCPネットワークを実装するために、私の最初のアプローチはSO_ZEROCOPY
ソケットで使用することでした:
sent_bytes = send(fd, buf, len, MSG_ZEROCOPY);
if (sent_bytes < 0) {
perror("send");
return -1;
}
ただし、これによりが発生しsend: Bad address
ます。
少しグーグルで調べた後、私の2番目のアプローチはパイプを使用し、splice()
その後にvmsplice()
:
ssize_t sent_bytes;
int pipes[2];
struct iovec iov = {
.iov_base = buf,
.iov_len = len
};
pipe(pipes);
sent_bytes = vmsplice(pipes[1], &iov, 1, 0);
if (sent_bytes < 0) {
perror("vmsplice");
return -1;
}
sent_bytes = splice(pipes[0], 0, fd, 0, sent_bytes, SPLICE_F_MOVE);
if (sent_bytes < 0) {
perror("splice");
return -1;
}
ただし、結果は同じですvmsplice: Bad address
。
なお、私は電話を交換する場合vmsplice()
やsend()
、単にはが指すデータを印刷することを関数にbuf
(またはsend()
なし MSG_ZEROCOPY
)、すべてがうまく働いています。そのため、データにはユーザー空間からアクセスできますが、vmsplice()
/ send(..., MSG_ZEROCOPY)
呼び出しはデータを処理できないようです。
ここで何が欠けていますか?カーネルドライバーから取得したユーザー空間アドレスでゼロコピーTCP送信を使用する方法はありますdma_mmap_coherent()
か?私が使用できる別のアプローチはありますか?
更新
ですからsendmsg()
MSG_ZEROCOPY
、カーネルのパスを少し深く掘り下げると、最終的に失敗する呼び出しはになりますget_user_pages_fast()
。で設定されたフラグを見つける-EFAULT
ためcheck_vma_flags()
、この呼び出しは戻ります。このフラグは、またはを使用してページがユーザー空間にマッピングされるときに設定されるようです。私の次のアプローチは、これらのページへの別の方法を見つけることです。VM_PFNMAP
vma
remap_pfn_range()
dma_mmap_coherent()
mmap