Linux 5.1'i bir çipte iki ARMv7 çekirdeği olan bir FPGA olan Cyclone V SoC'de çalıştırıyorum. Amacım bir dış arabirimden çok sayıda veri toplamak ve bu verileri bir TCP soketi aracılığıyla dışarı aktarmak. Buradaki zorluk, veri hızının çok yüksek olması ve GbE arayüzünü doyurmaya yaklaşmasıdır. Ben sadece write()
soket çağrıları kullanan bir çalışma uygulaması var , ama 55MB / s başında tops; teorik GbE sınırının yaklaşık yarısı. Şimdi verimi artırmak için çalışmak için sıfır kopya TCP iletimini almaya çalışıyorum, ama bir duvara çarpıyorum.
Veriyi FPGA'dan Linux kullanıcı alanına almak için bir çekirdek sürücüsü yazdım. Bu sürücü, harici bir arayüzden büyük miktarda veriyi ARMv7 çekirdeklerine bağlı DDR3 belleğine kopyalamak için FPGA'da bir DMA bloğu kullanır. Sürücüsü ayırır bitişik 1MB tampon bir grup olarak bu bellek probed kullanırken dma_alloc_coherent()
ile GFP_USER
ve uygulamak suretiyle userspace uygulamasına bu ortaya mmap()
bir dosya üzerinde /dev/
ve kullanma uygulamasına bir adres dönen dma_mmap_coherent()
preallocation'u tampon üzerinde.
Çok uzak çok iyi; kullanıcı alanı uygulaması geçerli verileri görüyor ve verim> 360MB / s'de yedek odaya sahip olandan fazlasıyla yeterli (harici arayüz üst sınırın ne olduğunu gerçekten görecek kadar hızlı değil).
Sıfır kopya TCP ağını uygulamak için ilk yaklaşımım SO_ZEROCOPY
soket üzerinde kullanmaktı :
sent_bytes = send(fd, buf, len, MSG_ZEROCOPY);
if (sent_bytes < 0) {
perror("send");
return -1;
}
Ancak, bu sonuçlanır send: Bad address
.
Biraz googling yaptıktan sonra, ikinci yaklaşımım bir boru kullanmak ve splice()
bunu takip etmekti 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;
}
Ancak, sonuç aynıdır: vmsplice: Bad address
.
Not ben çağrısına değiştirirseniz vmsplice()
veya send()
sadece işaret ettiği verileri yazdırır bir işleve buf
(veya send()
olmadan MSG_ZEROCOPY
), her şey iyi çalışıyor; bu nedenle verilere kullanıcı alanı erişilebilir, ancak vmsplice()
/ send(..., MSG_ZEROCOPY)
çağrıları işleyemiyor gibi görünüyor.
Burada ne eksik? Çekirdek sürücüsünden alınan kullanıcı alanı adresiyle sıfır kopya TCP gönderimi kullanmanın herhangi bir yolu var mı dma_mmap_coherent()
? Kullanabileceğim başka bir yaklaşım var mı?
GÜNCELLEME
Bu yüzden sendmsg()
MSG_ZEROCOPY
çekirdekteki yola biraz daha derine iniyorum ve sonunda başarısız olan çağrı get_user_pages_fast()
. Bu çağrı döndürür -EFAULT
çünkü check_vma_flags()
bulur VM_PFNMAP
bayrak kümesi vma
. Bu işaret, sayfalar remap_pfn_range()
veya kullanılarak kullanıcı alanına eşlendiğinde görünür dma_mmap_coherent()
. Bir sonraki yaklaşımım mmap
bu sayfalara başka bir yol bulmak .