新聞中心

        linux dma cache

        作者: 時(shí)間:2012-04-12 來(lái)源:網(wǎng)絡(luò) 收藏

        說(shuō)到DMA,就會(huì)想到Cache,兩者本身似乎是好不相關(guān)的事物。的確,假設(shè)DMA針對(duì)內(nèi)存的目的地址和Cache緩存的對(duì)象沒有重疊區(qū)域,DMA和Cache之間就相安無(wú)事,但是,如果有重疊呢,經(jīng)過(guò)DMA操作,Cache緩存對(duì)應(yīng)的內(nèi)存的數(shù)據(jù)已經(jīng)被修改,而CPU本身并不知道,它仍然認(rèn)為Cache中的數(shù)據(jù)仍然還是內(nèi)存中的數(shù)據(jù),以后訪問Cache映射的內(nèi)存時(shí),它仍然使用陳舊的Cache數(shù)據(jù),這就會(huì)發(fā)生Cache與內(nèi)存之間數(shù)據(jù)不一致性的錯(cuò)誤。一旦出現(xiàn)這樣的情況,沒有處理好,驅(qū)動(dòng)就將無(wú)法正常運(yùn)行。那么怎樣解決呢?最簡(jiǎn)單的方法是直接禁止DMA目標(biāo)地址范圍內(nèi)內(nèi)存的Cache功能,當(dāng)然這是犧牲性能的,但卻高可靠。不是嗎,所以這兩者之間究竟怎么平衡,還真不好解決。 其實(shí)啊,Cache不一致的情況在其他地方也是可能發(fā)生的,比如,對(duì)于帶MMU功能的arm處理器,在開啟MMU之前,需要設(shè)置Cache無(wú)效,TLB也是如此。

        本文引用地址:http://www.104case.com/article/149251.htm

        說(shuō)了,那么多DMA理論的東西,剩下的來(lái)點(diǎn)Linux下DMA編程的東西,當(dāng)然,這里也是點(diǎn)一下,具體怎么操作,我可要賣個(gè)關(guān)子到下節(jié)了。 在內(nèi)存中用于與外設(shè)交互數(shù)據(jù)的一塊區(qū)域被稱作DMA緩沖區(qū),在設(shè)備不支持scatter/gatherCSG,分散/聚集操作的情況下,DMA緩沖區(qū)必須是物理上聯(lián)系的。

        對(duì)于ISA設(shè)備而言,其DMA操作只能在16MB以下的內(nèi)存進(jìn)行,因此,在使用kmalloc()和__get_free_pages()及其類似函數(shù)申請(qǐng)DMA緩沖區(qū)時(shí)應(yīng)使用GFP_DMA標(biāo)志,這樣能保證獲得的內(nèi)存是具備DMA能力的。內(nèi)核中定義了__get_free_pages()針對(duì)DMA的快捷方式__get__pages(),它在申請(qǐng)標(biāo)志中添加了GFP_DMA,如下所示:

        #define __get___pages(gfp_mask, order)

        __get_free_pages((gfp_mask) | GFP_DMA, (order))

        我不想使用order為參數(shù)的申請(qǐng)DMA內(nèi)存,感覺就是怪怪的,那咋辦?

        那?這樣吧,你就用另外一個(gè)函數(shù)_mem_alloc()源代碼如下:

        /* dma_mem_alloc()返回值為虛擬地址 */

        static unsigned long dma_mem_alloc(int size)

        {

        int order = get_order(size);//大小->指數(shù)

        return __get_dma_pages(GFP_KERNEL, order);

        }

        上節(jié)我們說(shuō)到了dma_mem_alloc()函數(shù),需要說(shuō)明的是DMA的硬件使用總線地址而非物理地址,總線地址是從設(shè)備角度上看到的內(nèi)存地址,物理地址是從CPU角度上看到的未經(jīng)轉(zhuǎn)換的內(nèi)存地址(經(jīng)過(guò)轉(zhuǎn)換的那叫虛擬地址)。在PC上,對(duì)于ISA和PCI而言,總線即為物理地址,但并非每個(gè)平臺(tái)都是如此。由于有時(shí)候接口總線是通過(guò)橋接電路被連接,橋接電路會(huì)將IO地址映射為不同的物理地址。例如,在PRep(PowerPC Reference Platform)系統(tǒng)中,物理地址0在設(shè)備端看起來(lái)是0X80000000,而0通常又被映射為虛擬地址0xC0000000,所以同一地址就具備了三重身份:物理地址0,總線地址0x80000000及虛擬地址0xC0000000,還有一些系統(tǒng)提供了頁(yè)面映射機(jī)制,它能將任意的頁(yè)面映射為連續(xù)的外設(shè)總線地址。內(nèi)核提供了如下函數(shù)用于進(jìn)行簡(jiǎn)單的虛擬地址/總線地址轉(zhuǎn)換:

        unsigned long virt_to_bus(volatile void *address);

        void *bus_to_virt(unsigned long address);

        在使用IOMMU或反彈緩沖區(qū)的情況下,上述函數(shù)一般不會(huì)正常工作。而且,這兩個(gè)函數(shù)并不建議使用。

        需要說(shuō)明的是設(shè)備不一定能在所有的內(nèi)存地址上執(zhí)行DMA操作,在這種情況下應(yīng)該通過(guò)下列函數(shù)執(zhí)行DMA地址掩碼:

        int dma_set_mask(struct device *dev, u64 mask);

        比如,對(duì)于只能在24位地址上執(zhí)行DMA操作的設(shè)備而言,就應(yīng)該調(diào)用dma_set_mask(dev, 0xffffffff)。DMA映射包括兩個(gè)方面的工作:分配一片DMA緩沖區(qū);為這片緩沖區(qū)產(chǎn)生設(shè)備可訪問的地址。結(jié)合前面所講的,DMA映射必須考慮Cache一致性問題。內(nèi)核中提供了一下函數(shù)用于分配一個(gè)DMA一致性的內(nèi)存區(qū)域:

        void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp);

        這個(gè)函數(shù)的返回值為申請(qǐng)到的DMA緩沖區(qū)的虛擬地址。此外,該函數(shù)還通過(guò)參數(shù)handle返回DMA緩沖區(qū)的總線地址。與之對(duì)應(yīng)的釋放函數(shù)為:

        void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle);

        以下函數(shù)用于分配一個(gè)寫合并(writecombinbing)的DMA緩沖區(qū):

        void *dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp);

        與之對(duì)應(yīng)的是釋放函數(shù):dma_free_writecombine(),它其實(shí)就是dma_free_conherent,只不過(guò)是用了#define重命名而已。

        此外,Linux內(nèi)核還提供了PCI設(shè)備申請(qǐng)DMA緩沖區(qū)的函數(shù)pci_alloc_consistent(),原型為:

        void *pci_alloc_consistent(struct pci_dev *dev, size_t size, dma_addr_t *dma_addrp); 對(duì)應(yīng)的釋放函數(shù)為:

        void pci_free_consistent(struct pci_dev *pdev, size_t size, void *cpu_addr, dma_addr_t dma_addr);

        相對(duì)于一致性DMA映射而言,流式DMA映射的接口較為復(fù)雜。對(duì)于單個(gè)已經(jīng)分配的緩沖區(qū)而言,使用dma_map_single()可實(shí)現(xiàn)流式DMA映射:

        dma_addr_t dma_map_single(struct device *dev, void *buffer, size_t size, enum dma_data_direction direction); 如果映射成功,返回的是總線地址,否則返回NULL.最后一個(gè)參數(shù)DMA的方向,可能取DMA_TO_DEVICE, DMA_FORM_DEVICE, DMA_BIDIRECTIONAL和DMA_NONE;

        與之對(duì)應(yīng)的反函數(shù)是:

        void dma_unmap_single(struct device *dev,dma_addr_t *dma_addrp,size_t size,enum dma_data_direction direction);

        通常情況下,設(shè)備驅(qū)動(dòng)不應(yīng)該訪問unmap()的流式DMA緩沖區(qū),如果你說(shuō)我就愿意這么做,我又說(shuō)寫什么呢,選擇了權(quán)利,就選擇了責(zé)任,對(duì)吧。這時(shí)可先使用如下函數(shù)獲得DMA緩沖區(qū)的擁有權(quán):

        void dma_sync_single_for_cpu(struct device *dev,dma_handle_t bus_addr, size_t size, enum dma_data_direction direction);

        在驅(qū)動(dòng)訪問完DMA緩沖區(qū)后,應(yīng)該將其所有權(quán)還給設(shè)備,通過(guò)下面的函數(shù):

        linux操作系統(tǒng)文章專題:linux操作系統(tǒng)詳解(linux不再難懂)

        上一頁(yè) 1 2 下一頁(yè)

        關(guān)鍵詞: cache dma linux

        評(píng)論


        相關(guān)推薦

        技術(shù)專區(qū)

        關(guān)閉
        主站蜘蛛池模板: 茂名市| 顺昌县| 财经| 扬中市| 定兴县| 油尖旺区| 焦作市| 彩票| 桦南县| 安陆市| 荥经县| 大渡口区| 安平县| 依安县| 柘城县| 杂多县| 奇台县| 白朗县| 桑植县| 高唐县| 库伦旗| 华阴市| 舞钢市| 南陵县| 黎平县| 蓬莱市| 延长县| 漳州市| 昌平区| 惠州市| 镶黄旗| 黄冈市| 奎屯市| 五河县| 普宁市| 六盘水市| 微博| 永嘉县| 五常市| 新余市| 洛阳市|