王仕明 嚴發(fā)寶 徐智勇 付承毓
1(中國科學院光電技術研究所光束控制重點實驗室 四川 成都 610209)2(中國科學院大學 北京 100039)3(中南大學地球科學與信息物理學院 湖南 長沙 410083)
?
內核多線程直接存儲PCIE驅動設計
王仕明1,2嚴發(fā)寶3徐智勇1付承毓1
1(中國科學院光電技術研究所光束控制重點實驗室四川 成都 610209)2(中國科學院大學北京 100039)3(中南大學地球科學與信息物理學院湖南 長沙 410083)
在Linux系統(tǒng)中,高速存儲驅動會將大量數(shù)據(jù)從內核空間復制到用戶空間進行存儲,這將導致存儲速度下降和劇烈波動。針對該問題,結合自行設計開發(fā)的PCIE接口高速圖像存儲系統(tǒng),分析Linux下PCIE驅動的一般組成及實現(xiàn),提出一種在驅動中實現(xiàn)多線程多緩存直接存儲的方法。首先圖像數(shù)據(jù)乒乓DMA傳輸?shù)诫pDMA緩存中,然后將數(shù)據(jù)乒乓轉存到內核空間雙大緩存中,最后由內核線程直接寫入SSD。經該方法優(yōu)化后的SSD存儲速度達到100 MB/s以上,比常規(guī)方法提升了65%以上。在實際工程中,輸入512×512@200 Hz的14位灰度圖像能夠實現(xiàn)穩(wěn)定存儲,避免了原來的丟幀現(xiàn)象,且性能穩(wěn)定,取得了良好的優(yōu)化效果。
圖像存儲PCIEDMALinux驅動多線程
在光電檢測和跟蹤中,高幀頻、高分辨率相機得到了廣泛的應用,在提高檢測和跟蹤精度的同時也對圖像存儲系統(tǒng)速度及穩(wěn)定性提出了更高的要求[1]。某些系統(tǒng)要求存儲系統(tǒng)放置在設備內部,這對系統(tǒng)的體積提出了更高要求。自行研制的PCIE接口高速圖像存儲系統(tǒng)滿足了體積小、速度快而穩(wěn)的要求。PCIE采用了點對點串行連接, 比起 PCI 以及更早期的計算機并行總線的共享架構,每個設備都有自己的專用連接, 不需要向整個總線申請帶寬, 而且由于差分信號抗干擾性強,可以把數(shù)據(jù)傳輸率提高到極高的頻率[2]。PCIE用途廣泛、標準化程度高、通用性強。
Linux操作系統(tǒng)代碼開源,能輕松移植到多種平臺上,占用資源較少,是嵌入式設備的一個好選擇[3]。另外,Linux在硬盤讀寫和網絡通信方面,可靠性和性能均十分優(yōu)越,所以本存儲系統(tǒng)選用Linux作為操作系統(tǒng)。而驅動是連接硬件與操作系統(tǒng)的紐帶,是圖像存儲系統(tǒng)的核心和靈魂,是可靠性和性能的關鍵。因此本文結合實例詳細介紹PCIE接口的Linux驅動各組成部分與實現(xiàn),重點闡明內核多線程直接存儲的實現(xiàn)方法。
本高速存儲系統(tǒng)以PowerPC為控制核心,由圖像采集單元Frame Grabber、FPGA實現(xiàn)的PCIE接口DMA控制器、mSATA接口固態(tài)硬盤(SSD)及千兆網接口幾個部分組成。如圖1所示,圖像采集單元將高速圖像數(shù)據(jù)傳給FPGA的FIFO中。FPGA還實現(xiàn)了PCIE接口的DMA控制器,PCIE為2.5 GB/s的X4鏈路,理論總帶寬10 GB/s。當運行在PowerPC上的Linux驅動發(fā)送DMA開始信號后,將圖像數(shù)據(jù)傳入PowerPC的內存中,再由應用程序存入到掛載在PowerPC處理器SATA接口上的SSD中。千兆網用來實現(xiàn)與遠程計算機之間的通信控制和遠程顯示。
圖1 高速存儲系統(tǒng)結構框圖
2.1PCIE驅動實現(xiàn)
PCIE接口DMA控制器是字符型設備,PCIE總線作為設備與主控制器之間的高速通道,在驅動中需實現(xiàn)PCIE設備和字符設備驅動雙重功能[4]。在PCIE接口DMA控制器驅動中,不僅要實現(xiàn)PCIE的驅動,還需重點實現(xiàn)文件操作字符設備驅動,即 file_operations成員的實現(xiàn)并注冊 chr_dev。
DMA采用的是主總線 DMA傳輸方式,讀寫都是相對控制器來說的,即寫操作是由PCIE設備到內核內存,讀是由內核內存到PCIE設備。PCIE接口DMA控制器的驅動組成如圖2所示。驅動主要由驅動加載和卸載、中斷處理及字符設備文件操作函數(shù)組成。
圖2 PCIE驅動主要流程
驅動加載的主要步驟如圖2(a)所示,關鍵實現(xiàn)代碼及解析如下,每步均需檢測是否完成,如出錯則需返回出錯信息并退出;成功則置申請的資源位,方便模塊卸載時判斷并釋放相應資源。
如申請中斷:
stat_flags = stat_flags | HAVE_IRQ;(HAVE_IRQ=4)
注冊pci_dev結構體:
gDev = pci_get_device (PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_XILINX_PCIE, gDev); //根據(jù)廠商ID和設備ID找到PCI設備,自動
//掃描配置空間,填入pci_device結構體
使能MSI中斷:
pci_enable_msi(gDev);
//MSI中斷號會自動填入,pci_dev的irp
//中,即gDev->irq
獲取中斷號并綁定中斷處理程序:
gIrq = gDev->irq;
request_irq(gIrq, (irq_handler_t)(& PCIe_DMA_IRQHandler), IRQF_SHARED | IRQF_SAMPLE_RANDOM, gDrvrName, gDev);
申請DMA緩存:
gReadBuffer = pci_alloc_consistent(gDev, DMA_SIZE, &gReadHWAddr);
gWriteBuffer = pci_alloc_consistent(gDev, DMA_SIZE, &gWriteHWAddr);
字符設備初始化:
register_chrdev(gDrvrMajor, gDrvrName, &PCIe_DMA_file);
PCIe_DMA_file為文件操作結構體,如圖2(c)所示,包括讀、寫、設備控制、打開和釋放函數(shù),每個函數(shù)需獨立實現(xiàn),如ioctl(設備控制)函數(shù)如下:
int PCIe_Ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
u32 regx;
int ret = SUCCESS;
switch (cmd) {
case RDDCSR:
//讀設備狀態(tài)控制器
regx = XPCIe_ReadReg(0);
*((u32 *)arg) = regx;
break;
?
}
DMA初始化
PCIe_WriteReg(2, gWriteHWAddr0);
//寫TLP起始地址寄存器
PCIe_WriteReg(3, TLPSIZE);
//寫TLP大小寄存器
PCIe_WriteReg(4, TLPCOUNT);
DMA完成后會自動中斷,中斷處理函數(shù)流程如圖2(b)所示,通過DDMACR等寄存器即可完成,釋放信號量即可喚醒等待讀取數(shù)據(jù)的應用進程。應用進程讀數(shù)時會調用copy_to_user函數(shù),將數(shù)據(jù)由內核空間拷貝到應用空間。
2.2速度測試及分析
將FPGA產生的模擬圖像數(shù)據(jù)寫入SSD,在速率為60 MB/s時即頻繁丟幀。這是由于大量數(shù)據(jù)需從內核空間拷貝到應用空間,頻繁在內核模式和用戶模式之間切換,占用了太多的資源及時間,加上應用程序常常不能被及時調度更加劇了速度的不穩(wěn)定性。因此提出了內核多線程多級緩沖直接存儲到SSD的方法。
3.1內核直接存儲的優(yōu)勢
常規(guī)的驅動在DMA完成時,僅將數(shù)據(jù)傳入驅動的DMA緩沖區(qū)中,由于DMA緩沖區(qū)需要內存在物理上連續(xù), 由于內核的限制,Linux最大只能分配4 MB[5]。因此當DMA完成后,驅動就需要通知應用程序及時將數(shù)據(jù)取走,以便進行下一次DMA。當傳輸速率較高時,就需要把大量數(shù)據(jù)通過copy_to_user函數(shù)從內核空間拷貝到用戶空間,導致頻繁地在內核模式和用戶模式之間進行切換。這將占用大量資源和CPU時間從而導致系統(tǒng)性能的大幅下降和DMA速度的降低[6],長時間運行還會因不確定的延遲導致數(shù)據(jù)丟失。驅動運行在內核空間中,在驅動中直接將數(shù)據(jù)存入硬盤則避免了這些問題。另外由于內核線程優(yōu)先級高,實時性更好,速度更穩(wěn)定可靠。
3.2內核多線程實現(xiàn)流程
實現(xiàn)原理流程如圖3所示。應用程序首先在文件存儲路徑文件中寫入默認路徑,然后置DDMACR(設備DMA狀態(tài)控制器)D1位為1,準備開始存儲數(shù)據(jù)。FPGA檢測到此位后開始準備數(shù)據(jù),當數(shù)據(jù)達到一次DMA數(shù)據(jù)量時,置DDMACR D2數(shù)據(jù)準備好位,應用程序調用ioctl函數(shù)查詢此位,數(shù)據(jù)準備好后開始DMA,應用程序進入睡眠狀態(tài)。在驅動初始化時申請兩個DMA緩存區(qū)并設置好DMA寫操作初始值,同時初始化兩個內核線程,一個寫硬盤線程,一個開始下次DMA線程。
圖3 內核多線程直接存儲流程圖
當DMA完成時,進入中斷處理程序,清中斷后,切換DMA內核緩沖區(qū),最后釋放開始下次DMA信號量和寫文件信號量。開始下次DMA線程收到開始下次DMA信號量后,檢測數(shù)據(jù)準備好位,為真時啟動下次DMA。寫文件線程也申請兩個大緩存,收到寫文件信號量時,將已完成DMA緩沖區(qū)的數(shù)據(jù)緩存到一個大的內核緩存中,大緩存大小是DMA緩存的N倍。大緩存滿時,切換到另一個大緩存,已滿大緩存寫入到默認路徑下依自定義格式所建文件中,當存儲達到指定次數(shù)后,關閉文件并新建另一個文件。如此循環(huán),實現(xiàn)了連續(xù)的高速DMA存儲。關鍵細節(jié)主要有兩級雙緩沖的實現(xiàn)和內核中文件操作的實現(xiàn),將在后文作詳細講解。
3.2.1兩級雙緩沖的實現(xiàn)
因為每次DMA的數(shù)據(jù)量小而中斷頻率較高,為避免頻繁向硬盤寫入小文件,提高硬盤存儲速度。可在內核內存申請兩個較大的緩存來轉存DMA數(shù)據(jù),大緩存容量是DMA寫緩存的N倍,本系統(tǒng)取DMA緩存大小為4 MB,因為大緩存只需內存邏輯地址連續(xù)不需要物理地址連續(xù),所以可以使用vmalloc函數(shù)分配內存,一次最大可分配1 GB[7]??紤]到系統(tǒng)總內存大小和SSD最佳速度對應的文件大小,大緩存大小取40 MB。
乒乓緩存的數(shù)據(jù)流圖如圖4所示,每個周期大小為一次DMA時間間隔,進入中斷后通過對標志位的取反來標識周期1和周期2的交換。數(shù)據(jù)在周期1時以DMA的方式將數(shù)據(jù)傳送到DMA寫緩存1,在周期2時通過memcpy將數(shù)據(jù)轉存入大緩存,并用memset函數(shù)清零DMA寫緩存。反之在周期2時將數(shù)據(jù)存入DMA寫緩存2,周期1進行轉存,從而形成乒乓緩存。在N個周期中轉存的數(shù)據(jù)都存入大緩存1,而在另外N個周期中都存入大緩存2,從而形成以N為周期的大周期乒乓緩存,降低了寫頻率,增大了每次寫文件的大小,使得DMA和存儲速度均連續(xù)且平穩(wěn)。
圖4 兩級雙緩存數(shù)據(jù)流圖
3.2.2內核中寫硬盤的實現(xiàn)方法
在驅動模塊中,用戶空間的open、read、write等文件操作函數(shù)都是不可以使用的,而應該使用內核中對應的函數(shù)。在早期版本中可以使用sys_open、sys_read和sys_write完成文件操作,但是在較新的版本中不再由EXPORT_SYMBOL導出這幾個符號[8],因此新版本中不能再使用。分析sys_open可知調用do_sys_open后調用了filp->open,因此使用filp文件指針搭配struct file里的read和write即可完成對文件的讀寫操作。filp->f_op->read和filp->f_op->write都會對參數(shù)buf進行合法性檢查,如果不在用戶空間則會拒絕訪問。因此需要設置FS(取USER_DS、KERNEL_DS兩值)來增大尋址范圍。這些函數(shù)定義在asm/uaccess.h。關鍵代碼如下:
struct file *w_filp;
mm_segment_t org_fs;
w_filp = filp_open(filename, O_RDWR|O_APPEND| O_CREAT, 0644);
//新建追加屬性的文件并打開
if(IS_ERR(filp))
{
printk("open file error… ");
return;
}
org_fs=get_fs();
//保存FS的原始值
set_fs(KERNEL_DS);
//將buf尋址范圍增大至內核空間
//多次寫循環(huán),根據(jù)標識位切換BUF
w_filp->f_op->write(filp, big_write_buffer_N(N=0,1), BIG_BUF_SIZE,&filp->f_pos);
//將大緩存中數(shù)據(jù)寫入文件
//判斷寫次數(shù)是否達到,本系統(tǒng)取值50,單文件約為2 GB,達到后
//關閉文件,并打開新文件繼續(xù)傳輸
set_fs(org_fs);
//將buf尋址范圍恢復為原值
w_filp_close(filp,NULL);
3.3速度測試
本系統(tǒng)PowerPC提供SATA2.0接口,經Linux dd命令無格式寫SSD速度最大值約為260 MB/s,寫Ext3格式文件最大速度約為115 MB/s,速度降低的主要原因是較低性能的CPU需對文件系統(tǒng)信息生成和寫入。對兩種方法實現(xiàn)的驅動在同一系統(tǒng)下進行對比測試,測試方法為FPGA自動生成圖像數(shù)據(jù),保證DMA數(shù)據(jù)隨時準備好。因此系統(tǒng)的存儲只與驅動的效率和穩(wěn)定性有關,為減少對系統(tǒng)的影響,系統(tǒng)每隔一分鐘通過gettimeofday函數(shù)得到存儲一百幀圖像所用時間,并將時間存入另一文件中,從而可以計算出存儲速度。存儲兩小時,采用Matlab畫出速度曲線如圖5和圖6所示。可以看出,新方法實現(xiàn)的驅動不僅穩(wěn)定存儲速度達到了100 MB/s以上,而且還十分穩(wěn)定。而傳統(tǒng)驅動穩(wěn)定速度只有60 MB/s上下,速度波動也劇烈得多。應用中對內核多線程直接存儲實現(xiàn)的系統(tǒng)連續(xù)寫入512×512@200 Hz的14位灰度圖像,即速率為100 MB/s。測試5次,每次兩小時,均未發(fā)生丟幀和錯幀現(xiàn)象。而采用常規(guī)驅動的本系統(tǒng)在速率為60 MB/s時即出現(xiàn)明顯丟幀;當速率提高到80 MB/s時,丟幀會更加頻繁,而且基本都是連續(xù)丟幀。由此可見采用內核多線程多緩存直接存儲的驅動具有明顯的優(yōu)勢,存儲速度更快也更穩(wěn)定。
圖5 常規(guī)方法速度曲線
圖6 本文方法速度曲線
本文介紹PCIE驅動的一般實現(xiàn)方法和步驟,針對其不足提出了在內核中直接用多線程多緩沖存儲的方法。闡述了其實現(xiàn)的原理和流程,并對關鍵步驟和關鍵點作了詳細分析。該系統(tǒng)的瓶頸主要在于SSD的寫入速度,后續(xù)實現(xiàn)適合大量數(shù)據(jù)連續(xù)寫入的RAID3 SSD陣列后,速度將有較大提升。該存儲驅動實現(xiàn)方法顯著地提高了存儲速度及穩(wěn)定性,具有較高的工程實踐及借鑒意義。
[1] Ravi Budruk,Don Anderson,Tom Shanley. PCI Express 系統(tǒng)體系結構標準教材 [M]. 田玉敏,譯. 北京:電子工業(yè)出版社,2005.
[2] 孫科林,周維超,吳欽章. 高速實時光纖圖像傳輸系統(tǒng)的實現(xiàn) [J]. 光學 精密工程,2011,19(9):2230-2233.
[3] 博韋,西斯特. 深入理解 Linux 內核[M]. 陳莉君,張瓊聲,張宏偉,譯. 北京:中國電力出版社,2007:557-594.
[4] 周小波. 嵌入式 Linux 下 PCIE 數(shù)據(jù)采集卡驅動開發(fā)[D]. 成都:電子科技大學,2013.
[5] 宋寶華,何昭然,史海濱,等.精通 Linux 設備驅動程序開發(fā)[M].北京:人民郵電出版社,2012.
[6] 雷雨,周維超,舒懷亮. 基于 ATR 結構的嵌入式NAND Flash 圖像記錄系統(tǒng)[J]. 光電工程,2014,41(3):49-54.
[7] 楊阿鋒,吳帥,劉凱,等. PCIe 接口高速數(shù)據(jù)傳輸卡的驅動程序開發(fā)[J]. 中國測試技術,2008,34(2):67-69.
[8] 科比特,魯賓尼,哈特曼. Linux 設備驅動程序[M]. 魏永明,耿岳,鐘書毅,譯. 北京:中國電力出版社,2007:458-490.
DESIGNING KERNEL MULTITHREAD DIRECT STORAGE PCIE DRIVER
Wang Shiming1,2Yan Fabao3Xu Zhiyong1Fu Chengyu1
1(Key Laboratory of Beam Control, Institute of Optics and Electronics, Chinese Academy of Sciences, Chengdu 610209,Sichuan,China)2(UniversityofChineseAcademyofSciences,Beijing100039,China)3(SchoolofGeosciencesandInfo-Physics,CentralSouthUniversity,Changsha410083,Hunan,China)
In Linux system, high-speed storage drive will copy large amounts of data from kernel space to user space for storage, and this results in the decrease of storage speed and sharp volatility. Aiming at the problem, in combination with the self-designed and developed PCIE interface high-speed image storage system, we analyse the general composition of PCIE drive under Linux and its implementation, and put forward a method to realise multithread multi-buffer direct storage in driver. First, it transfers the ping-pong DMA of image data to double DMA buffer, then it saves the data ping-pong to double big buffer of kernel space, and finally directly writes them to SSD by kernel thread. After optimised with this method, SSD storage speed reaches more than 100 MB/s and gains over 65% improvement compared with conventional methods. In practical projects, by inputting 14 bits gray image with 512×512 @ 200 Hz, SSD can achieve stable storage, this avoids previous phenomenon of frame loss, the performance is stable as well, and achieves good optimisation result.
Image storagePCIEDMALinuxDriveMultithread
2015-03-12。國防實驗基金項目(YJ14K015)。王仕明,博士生,主研領域:高速圖像存儲系統(tǒng),Linux操作系統(tǒng)驅動和應用開發(fā)。嚴發(fā)寶,博士生。徐智勇,研究員。付承毓,研究員。
TP316.1TP311
A
10.3969/j.issn.1000-386x.2016.09.056