我有一個外部FPGA設備,通過PCIe將大量數據轉儲到保留(使用引導加載程序參數)連續內存區域。該內存區域將始終在相同的位置開始。如何拼接/ dev/mem?
我現在想要儘可能快地通過UDP轉儲數據。我不在乎檢查這些數據,因此不需要將其帶入用戶空間。因此,我的研究表明使用零拷貝是最快/最好的方法。
我試圖int memFd = open("/dev/mem", O_RDONLY);
,然後用memFd
在sendfile
和splice
函數調用,但是這些都失敗了。
過了幾天,但我終於在sendfile
源輸入文件描述符必須是一個普通的文件(一個細節,據我可以告訴令人沮喪的離開了這個男人頁面)看見了,/dev/mem
不一個常規文件。無論如何,我看了更多,現在有信心splice
是我想要使用的電話。
但是,這也失敗了,以及錯誤的14-EFAULT,這意味着「壞地址」(再次令人沮喪的是,這個錯誤代碼沒有在splice
手冊頁中提到)。我查看了splice
的源代碼,並且可以看到幾次返回EFAULT的地方,但我只是看不到我傳遞的參數是如何導致問題的。
我的簡化的非錯誤檢查代碼如下;
int filedes[2];
int memFd = open("/dev/mem", O_RDONLY);
int fileFd = open("myTestFile.txt", O_RDONLY);
loff_t offset = START_OF_MEM_REGION;
int sockFd = ConfigureMySocket();
pipe(filedes); // this returns 0, so the pipes are good
int ret = splice(memFd, &offset, filedes[1], NULL, 128, SPLICE_F_MOVE); // this fails with EFAULT
//int ret = splice(memFd, NULL, filedes[1], NULL, 128, 0); // this also fails with EFAULT
//int ret = splice(fileFd, NULL, filedes[1], NULL, 128, 0); // this works just fine
// this is never reached because the splice call above hangs. If I run the
// fileFd splice call instead this works just fine
ret = splice(filedes[0], NULL, sockFd, NULL, 128, 0);
我的系統信息:
-
ARM架構
- 運行的Linux 3.1.10 root身份運行用戶
- 嵌入式設備
- 內核沒有用
CONFIG_STRICT_DEVMEM
其他編譯有趣的事實:
- 我有一個2.6 Linux的CentOS虛擬機,這個代碼工作正常,偏移量爲〜1MB。然而,這個內核是用
CONFIG_STRICT_DEVMEM
編譯的,所以我把1MB的限制歸因於此。 - 我可以
mmap
到內存區就好了,看看FPGA寫的數據。
我的問題是:
- 是使用
splice
正確的方式做到這一點?有人認爲有更好的方法嗎? - 如果
splice
是對的,任何人都知道這裏會發生什麼?有沒有內核編譯器標誌阻止這個工作?我正在閱讀splice.c
的源代碼,但它不是3.1.10版本,所以可能有所改變?無論哪種方式,在虛擬機中看到這項工作很好,但在嵌入式環境中卻看不到。
編輯:我已經從kernal.org下載了3.1.10源代碼,很遺憾,看到我在free-electrons.com上看到的與其他版本沒有什麼大的區別。在我看來,所有拼接代碼都位於/fs/splice.c中。do_splice(...)
必須是被執行的代碼。我的第一個電話splice
(使用memFd
和filedes[1]
)應該下降到if (opipe) {
...在這裏你可以看到EFAULT
返回,如果copy_from_user
或copy_to_user
失敗..這些怎麼會失敗?我的&offset
變量不會有任何問題,因爲如果這是NULL
,我會得到相同的錯誤,或者如果我用fileFd
代替memFd
,則會發生錯誤。另外感興趣的是,如果我用128替換爲0(寫入的字節數),則不會有錯誤。其中返回EFAULT
的地方,我只是不明白怎麼文件描述符甚至因素成邏輯,,除非EFAULT
是越來越被一些較深的函數調用返回...
這些都是片段從splice.c
SYSCALL_DEFINE6(splice, int, fd_in, loff_t __user *, off_in,
int, fd_out, loff_t __user *, off_out,
size_t, len, unsigned int, flags)
{
long error;
struct file *in, *out;
int fput_in, fput_out;
if (unlikely(!len))
return 0;
error = -EBADF;
in = fget_light(fd_in, &fput_in);
if (in) {
if (in->f_mode & FMODE_READ) {
out = fget_light(fd_out, &fput_out);
if (out) {
if (out->f_mode & FMODE_WRITE)
error = do_splice(in, off_in,
out, off_out,
len, flags);
fput_light(out, fput_out);
}
}
fput_light(in, fput_in);
}
return error;
}
static long do_splice(struct file *in, loff_t __user *off_in,
struct file *out, loff_t __user *off_out,
size_t len, unsigned int flags)
{
struct pipe_inode_info *ipipe;
struct pipe_inode_info *opipe;
loff_t offset, *off;
long ret;
ipipe = get_pipe_info(in);
opipe = get_pipe_info(out);
if (ipipe && opipe) {
if (off_in || off_out)
return -ESPIPE;
if (!(in->f_mode & FMODE_READ))
return -EBADF;
if (!(out->f_mode & FMODE_WRITE))
return -EBADF;
/* Splicing to self would be fun, but... */
if (ipipe == opipe)
return -EINVAL;
return splice_pipe_to_pipe(ipipe, opipe, len, flags);
}
if (ipipe) {
if (off_in)
return -ESPIPE;
if (off_out) {
if (!(out->f_mode & FMODE_PWRITE))
return -EINVAL;
if (copy_from_user(&offset, off_out, sizeof(loff_t)))
return -EFAULT;
off = &offset;
} else
off = &out->f_pos;
ret = do_splice_from(ipipe, out, off, len, flags);
if (off_out && copy_to_user(off_out, off, sizeof(loff_t)))
ret = -EFAULT;
return ret;
}
if (opipe) {
if (off_out)
return -ESPIPE;
if (off_in) {
if (!(in->f_mode & FMODE_PREAD))
return -EINVAL;
if (copy_from_user(&offset, off_in, sizeof(loff_t)))
return -EFAULT;
off = &offset;
} else
off = &in->f_pos;
ret = do_splice_to(in, off, opipe, len, flags);
if (off_in && copy_to_user(off_in, off, sizeof(loff_t)))
ret = -EFAULT;
return ret;
}
return -EINVAL;
}
爲什麼不直接DMA將網卡?這在你的ARM上可能是可行的。你必須(實際上)爲在FPGA中實現的NIC設備驅動程序,如果你正在做UDP,那麼不會那麼糟糕。如果UDP的速度比FPGA的數據速率更快,那麼它只會是一件好事......通過像你這樣的CPU內存去做會增加時間。 – bazza
@bazza這是一個好主意,但是爲fpga編寫固件的人不想設置DMA控制器。我們也試圖擺脫內核驅動程序空間,以節省複雜性。如果我們走到最後,速度太慢,我們仍然有時間/金錢,這將是一個好主意。我們期望FPGA速度最快,而其他一切都比較慢,因此數據將會在某處丟失。 – yano
根據您確切的系統架構,不一定必須是內核驅動程序。這僅僅是FPGA對CPU控制之外的NIC的影響。很好地丟棄數據可能會很棘手。除此之外,我想你需要花費時間在FPGA中完成所有工作,而不是根據需要花費時間來完成Linux。你可以看看內核旁路,這至少可以讓你自己做數據包,而不是試圖通過內核自己的網絡堆棧傳遞數據。 – bazza