DMA即Direct Memory Access,是一種允許外設(shè)直接存取內(nèi)存數(shù)據(jù)而沒(méi)有CPU參與的技術(shù),當(dāng)外設(shè)對(duì)于該塊內(nèi)存的讀寫(xiě)完成之后,DMAC通過(guò)中斷通知CPU,這種技術(shù)多用于對(duì)數(shù)據(jù)量和數(shù)據(jù)傳輸速度都有很高要求的外設(shè)控制,如顯示設(shè)備等。
DMA和Cache一致性
我們知道,為了提高系統(tǒng)運(yùn)行效率,現(xiàn)代的CPU都采用多級(jí)緩存結(jié)構(gòu),其中就包括使用多級(jí)Cache技術(shù)來(lái)緩存內(nèi)存中的數(shù)據(jù)來(lái)緩解CPU和內(nèi)存速度差異問(wèn)題。在這種前提下,顯而易見(jiàn),如果DMA內(nèi)存的數(shù)據(jù)已經(jīng)被Cache緩存了,而外設(shè)又修改了其中的數(shù)據(jù),這就會(huì)造成Cache數(shù)據(jù)和內(nèi)存數(shù)據(jù)不匹配的問(wèn)題,即DMA與Cache的一致性問(wèn)題。為了解決這個(gè)問(wèn)題,最簡(jiǎn)單的辦法就是禁掉對(duì)DMA內(nèi)存的Cache功能,顯然,這會(huì)導(dǎo)致性能的降低
虛擬地址 VS 物理地址 VS 總線地址
在有MMU的計(jì)算機(jī)中,CPU看到的是虛擬地址,發(fā)給MMU后轉(zhuǎn)換成物理地址,虛擬地址再經(jīng)過(guò)相應(yīng)的電路轉(zhuǎn)換成總線地址,就是外設(shè)看到的地址。所以,DMA外設(shè)看到的地址其實(shí)是總線地址。Linux內(nèi)核提供了相應(yīng)的API來(lái)實(shí)現(xiàn)三種地址間的轉(zhuǎn)換:
//虛擬->物理virt_to_phys()//物理->虛擬ioremap()//虛擬->總線virt_to_bus()//總線->虛擬bus_to_virt()
DMA地址掩碼
DMA外設(shè)并不一定能在所有的內(nèi)存地址上執(zhí)行DMA操作,此時(shí)應(yīng)該使用DMA地址掩碼
int dma_set_mask(struct device *dev,u64 mask);
比如一個(gè)只能訪問(wèn)24位地址的DMA外設(shè),就使用dma_set_mask(dev,0xffffff)
編程流程
下面是在內(nèi)核程序中使用DMA內(nèi)存的流程:
一致性DMA
如果在驅(qū)動(dòng)中使用DMA緩沖區(qū),可以使用內(nèi)核提供的已經(jīng)考慮到一致性的API:
/** * request_dma - 申請(qǐng)DMA通道 * On certain platforms, we have to allocate an interrupt as well... */int request_dma(unsigned int chan, const char *device_id);/** * dma_alloc_coherent - allocate consistent memory for DMA * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices * @size: required memory size * @handle: bus-specific DMA address * * Allocate some memory for a device for performing DMA. This function * allocates pages, and will return the CPU-viewed address, and sets @handle * to be the device-viewed address. */void * dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag)//申請(qǐng)PCI設(shè)備的DMA緩沖區(qū)void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle)//釋放DMA緩沖區(qū)void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t dma_handle )//釋放PCI設(shè)備的DMA緩沖區(qū)void pci_free_consistent()/** * free_dma - 釋放DMA通道 * On certain platforms, we have to free interrupt as well... */void free_dma(unsigned int chan);
流式DMA
如果使用應(yīng)用層的緩沖區(qū)建立的DMA申請(qǐng)而不是驅(qū)動(dòng)中的緩沖區(qū),可能僅僅使用kmalloc等函數(shù)進(jìn)行申請(qǐng),那么就需要使用流式DMA緩沖區(qū),此外,還要解決Cache一致性的問(wèn)題。
/** * request_dma - 申請(qǐng)DMA通道 * On certain platforms, we have to allocate an interrupt as well... */int request_dma(unsigned int chan, const char *device_id);//映射流式DMAdma_addr_t dma_map_single(struct device *dev,void *buf, size_t size, enum dma_datadirection direction);//驅(qū)動(dòng)獲得DMA擁有權(quán),通常驅(qū)動(dòng)不該這么做void dma_sync_single_for_cpu(struct device *dev,dma_addr_t dma_handle_t bus_addr,size_t size, enum dma_data_direction direction);//將DMA擁有權(quán)還給設(shè)備void dma_sync_single_for_device(struct device *dev,dma_addr_t dma_handle_t bus_addr,size_t size, enum dma_data_direction direction);//去映射流式DMAdma_addr_t dma_unmap_single(struct device *dev,void *buf, size_t size, enum dma_datadirection direction);/** * free_dma - 釋放DMA通道 * On certain platforms, we have to free interrupt as well... */void free_dma(unsigned int chan);
?
評(píng)論