小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Linux之DMA API -- 通用設(shè)備的動態(tài)DMA映射--轉(zhuǎn)

 ScorpioRen360 2013-02-21

Linux之DMA API -- 通用設(shè)備的動態(tài)DMA映射

通用設(shè)備的動態(tài)DMA映射

by JHJ(jianghuijun211@gmail.com) 

本文描述DMA API。更詳細(xì)的介紹請參看Documentation/DMA-API-HOWTO.txt。

API分為兩部分,第一部分描述API,第二部分描述可以支持非一致性內(nèi)存機(jī)器的擴(kuò)展API。你應(yīng)該使用第一部分所描述的API,除非你知道你的驅(qū)動必須要支持非一致性平臺。

第一部分 DMA API

為了可以引用DMA API,你必須 #include <linux/dma-mapping.h>

1-1 使用大塊DMA一致性緩沖區(qū)(dma-coherent buffers)

void * 
dma_alloc_coherent
(struct device *dev, size_t size,
                    dma_addr_t *dma_handle, gfp_t flag)

一致性內(nèi)存:設(shè)備對一塊內(nèi)存進(jìn)行寫操作,處理器可以立即進(jìn)行讀操作,而無需擔(dān)心處理器高速緩存(cache)的影響。同樣的,處理器對一塊內(nèi)存進(jìn)行些操作,設(shè)備可以立即進(jìn)行讀操作。(在告訴設(shè)備讀內(nèi)存時,你可能需要確定刷新處理器的寫緩存。)

此函數(shù)申請一段大小為size字節(jié)的一致性內(nèi)存,返回兩個參數(shù)。一個是dma_handle,它可以用作這段內(nèi)存的物理地址。 另一個是指向被分配內(nèi)存的指針(處理器的虛擬地址)。

注意:由于在某些平臺上,使用一致性內(nèi)存代價很高,比如最小的分配長度為一個頁。因此你應(yīng)該盡可能合并申請一致性內(nèi)存的請求。最簡單的辦法是使用dma_pool函數(shù)調(diào)用(詳見下文)。

參數(shù)flag(僅存在于dma_alloc_coherent中)運(yùn)行調(diào)用者定義申請內(nèi)存時的GFP_flags(詳見kmalloc)。

void * 
dma_zalloc_coherent
(struct device *dev, size_t size, 
                    dma_addr_t *dma_handle, gfp_t flag)

dma_alloc_coherent()的封裝,如果內(nèi)存分配成功,則返回清零的內(nèi)存。

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

釋放之前申請的一致性內(nèi)存。dev, size及dma_handle必須和申請一致性內(nèi)存的函數(shù)參數(shù)相同。cpu_addr必須為申請一致性內(nèi)存函數(shù)的返回虛擬地址。

注意:和其他內(nèi)存分配函數(shù)不同,這些函數(shù)必須要在中斷使能的情況下使用。

1-2 使用小塊DMA一致性緩沖區(qū)

如果要使用這部分DMA API,必須#include <linux/dmapool.h>。

許多驅(qū)動程序需要為DMA描述符或者I/O內(nèi)存申請大量小塊DMA一致性內(nèi)存。你可以使用DMA 內(nèi)存池,而不是申請以頁為單位的內(nèi)存塊或者調(diào)用dma_alloc_coherent()。這種機(jī)制有點(diǎn)像struct kmem_cache,只是它利用了DMA一致性內(nèi)存分配器,而不是調(diào)用 __get_free_pages()。同樣地,DMA 內(nèi)存池知道通用硬件的對齊限制,比如隊(duì)列頭需要N字節(jié)對齊。

struct dma_pool * 
dma_pool_create
(const char *name, struct device *dev, 
                size_t size, size_t align, size_t alloc);

create( )函數(shù)為設(shè)備初始化DMA一致性內(nèi)存的內(nèi)存池。它必須要在可睡眠上下文調(diào)用。

name為內(nèi)存池的名字(就像struct kmem_cache name一樣)。dev及size就如dma_alloc_coherent()參數(shù)一樣。align為設(shè)備硬件需要的對齊大小(單位為字節(jié),必須為2的冪次方)。如果設(shè)備沒有邊界限制,可以設(shè)置該參數(shù)為0。如果設(shè)置為4096,則表示從內(nèi)存池分配的內(nèi)存不能超過4K字節(jié)的邊界。

void *
dma_pool_alloc
(struct dma_pool *pool, gfp_t gfp_flags, 
                dma_addr_t *dma_handle);

從內(nèi)存池中分配內(nèi)存。返回的內(nèi)存同時滿足申請的大小及對齊要求。設(shè)置GFP_ATOMIC可以確保內(nèi)存分配被block,設(shè)置GFP_KERNEL(不能再中斷上下文,不會保持SMP鎖)允許內(nèi)存分配被block。和dma_alloc_coherent()一樣,這個函數(shù)會返回兩個值:一個值是cpu可以使用的虛擬地址,另一個值是內(nèi)存池設(shè)備可以使用的dma物理地址。

void 
dma_pool_free
(struct dma_pool *pool, void *vaddr, 
                dma_addr_t addr);

返回內(nèi)存給內(nèi)存池。參數(shù)pool為傳遞給dma_pool_alloc()的pool,參數(shù)vaddr及addr為dma_pool_alloc()的返回值。

void 
dma_pool_destroy
(struct dma_pool *pool);

內(nèi)存池析構(gòu)函數(shù)用于釋放內(nèi)存池的資源。這個函數(shù)在可睡眠上下文調(diào)用。請確認(rèn)在調(diào)用此函數(shù)時,所有從該內(nèi)存池申請的內(nèi)存必須都要?dú)w還給內(nèi)存池。

1-3 DMA尋址限制

int 
dma_supported
(struct device *dev, u64 mask)

用來檢測該設(shè)備是否支持掩碼所表示的DMA尋址能力。比如mask為0x0FFFFFF,則檢測該設(shè)備是否支持24位尋址。

返回1表示支持,0表示不支持。

注意:該函數(shù)很少用于檢測是否掩碼為可用的,它不會改變當(dāng)前掩碼設(shè)置。它是一個內(nèi)部API而非供驅(qū)動者使用的外部API。

int 
dma_set_mask
(struct device *dev, u64 mask)

檢測該掩碼是否合法,如果合法,則更新設(shè)備參數(shù)。即更新設(shè)備的尋址能力。

返回0表示成功,返回負(fù)值表示失敗。

int 
dma_set_coherent_mask
(struct device *dev, u64 mask)

檢測該掩碼是否合法,如果合法,則更新設(shè)備參數(shù)。即更新設(shè)備的尋址能力。

返回0表示成功,返回負(fù)值表示失敗。

u64 
dma_get_required_mask
(struct device *dev)

該函數(shù)返回平臺可以高效工作的掩碼。通常這意味著返回掩碼是可以尋址到所有內(nèi)存的最小值。檢查該值可以讓DMA描述符的大小盡量的小。

請求平臺需要的掩碼并不會改變當(dāng)前掩碼。如果你想利用這點(diǎn),可以利用改返回值通過dma_set_mask()設(shè)置當(dāng)前掩碼。

1-4 流式DMA映射

dma_addr_t 
dma_map_single
(struct device *dev, void *cpu_addr, size_t size,
                enum dma_data_direction direction)

映射一塊處理器的虛擬地址,這樣可以讓外設(shè)訪問。該函數(shù)返回內(nèi)存的物理地址。

在dma_API中強(qiáng)烈建議使用表示DMA傳輸方向的枚舉類型。

DMA_NONE    僅用于調(diào)試目的
DMA_TO_DEVICE    數(shù)據(jù)從內(nèi)存?zhèn)鬏數(shù)皆O(shè)備,可認(rèn)為是寫操作。
DMA_FROM_DEVICE    數(shù)據(jù)從設(shè)備傳輸?shù)絻?nèi)存,可認(rèn)為是讀操作。
DMA_BIDIRECTIONAL    不清楚傳輸方向則可用該類型。

請注意:并非一臺機(jī)器上所有的內(nèi)存區(qū)域都可以用這個API映射。進(jìn)一步說,對于內(nèi)核連續(xù)虛擬地址空間所對應(yīng)的物理地 址并不一定連續(xù)(比如這段地址空間由vmalloc申請)。因?yàn)檫@種函數(shù)并未提供任何分散/聚集能力,因此用戶在企圖映射一塊非物理連續(xù)的內(nèi)存時,會返回 失敗。基于此原因,如果想使用該函數(shù),則必須確保緩沖區(qū)的物理內(nèi)存連續(xù)(比如使用kmalloc)。

更進(jìn)一步,所申請內(nèi)存的物理地址必須要在設(shè)備的dma_mask尋址范圍內(nèi)(dma_mask表示與設(shè)備尋址能力對 應(yīng)的位)。為了確保由kmalloc申請的內(nèi)存在dma_mask中,驅(qū)動程序需要定義板級相關(guān)的標(biāo)志位來限制分配的物理內(nèi)存范圍(比如在x86 上,GFP_DMA用于保證申請的內(nèi)存在可用物理內(nèi)存的前16Mb空間,可以由ISA設(shè)備使用)。

同時還需注意,如果平臺有IOMMU(設(shè)備擁有MMU單元,可以進(jìn)行I/O內(nèi)存總線和設(shè)備的映射,即總線地址和內(nèi)存物理地址的映射),則上述物理地址連續(xù)性及外設(shè)尋址能力的限制就不存在了。當(dāng)然為了方便起見,設(shè)備驅(qū)動開發(fā)者可以假設(shè)不存在IOMMU。

警告:內(nèi)存一致性操作基于高速緩存行(cache line)的寬度。為了可以正確操作該API創(chuàng)建的內(nèi)存映射,該映射區(qū)域的起始地址和結(jié)束地址都必須是高速緩存行的邊界(防止在一個高速緩存行中有兩個或 多個獨(dú)立的映射區(qū)域)。因?yàn)樵诰幾g時無法知道高速緩存行的大小,所以該API無法確保該需求。因此建議那些對高速緩存行的大小不特別關(guān)注的驅(qū)動開發(fā)者們, 在映射虛擬內(nèi)存時保證起始地址和結(jié)束地址都是頁對齊的(頁對齊會保證高速緩存行邊界對齊的)。

DMA_TO_DEVICE    軟件對內(nèi)存區(qū)域做最后一次修改后,且在傳輸給設(shè)備前,需要做一次同步。一旦該使用該原語,內(nèi)存區(qū)域可被視作設(shè)備只讀緩沖區(qū)。如果設(shè)備需要對該內(nèi)存區(qū)域進(jìn)行寫操作,則應(yīng)該使用DMA_BIDIRECTIONAL(如下所示)

DMA_FROM_DEVICE    驅(qū)動在訪問數(shù)據(jù)前必須做一次同步,因?yàn)閿?shù)據(jù)可能被設(shè)備修改了。內(nèi)存緩沖區(qū)應(yīng)該被當(dāng)做驅(qū)動只讀緩沖區(qū)。如果驅(qū)動需要進(jìn)行寫操作,應(yīng)該使用DMA_BIDIRECTIONAL(如下所示)。

DMA_BIDIRECTIONAL    需要特別處理:這意味著驅(qū)動并不確定內(nèi)存數(shù)據(jù)傳輸?shù)皆O(shè)備前,內(nèi)存是否被修改了,同時也不確定設(shè)備是否會修改內(nèi)存。因此,你必須需要兩次同步雙向內(nèi)存:一次 在內(nèi)存數(shù)據(jù)傳輸?shù)皆O(shè)備前(確保所有緩沖區(qū)數(shù)據(jù)改變都從處理器的高速緩存刷新到內(nèi)存中),另一次是在設(shè)備可能訪問該緩沖區(qū)數(shù)據(jù)前(確保所有處理器的高速緩存 行都得到了更新,設(shè)備可能改變了緩沖區(qū)數(shù)據(jù))。即在處理器寫操作完成時,需要做一次刷高速緩存的操作,以確保數(shù)據(jù)都同步到了內(nèi)存緩沖區(qū)中。在處理器讀操作前,需要更新高速緩沖區(qū)的行,已確保設(shè)備對內(nèi)存緩沖區(qū)的改變都同步到了高速緩沖區(qū)中。

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

取消先前的內(nèi)存映射。傳入該函數(shù)的所有參數(shù)必須和映射API函數(shù)的傳入(包括返回)參數(shù)相同。

dma_addr_t 
dma_map_page(struct device *dev, struct page *page,
                    unsigned long offset, size_t size,
                    enum dma_data_direction direction)

void 
dma_unmap_page
(struct device *dev, dma_addr_t dma_address, size_t size,
                enum dma_data_direction direction)

對頁進(jìn)行映射/取消映射的API。對其他映射API的注意事項(xiàng)及警告對此都使用。同樣的,參數(shù)<offset>及<size>用于部分頁映射,如果你對高速緩存行的寬度不清楚的話,建議你不要使用這些參數(shù)。

int 
dma_mapping_error
(struct device *dev, dma_addr_t dma_addr)

在某些場景下,通過dma_map_single及dma_map_page創(chuàng)建映射可能會失敗。驅(qū)動程序可以通過此函數(shù)來檢測這些錯誤。一個非零返回值表示未成功創(chuàng)建映射,驅(qū)動程序需要采取適當(dāng)措施(比如降低當(dāng)前DMA映射使用率或者等待一段時間再嘗試)。

int
dma_map_sg(struct device *dev, struct scatterlist *sg,
        int nents, enum dma_data_direction direction)

返回值:被映射的物理內(nèi)存塊的數(shù)量(如果在分散/聚集鏈表中一些元素是物理地址或虛擬地址相鄰的,切IOMMU可以將它們映射成單個內(nèi)存塊,則返回值可能比輸入值<nents>?。?。

請注意如果sg已經(jīng)映射過了,其不能再次被映射。再次映射會銷毀sg中的信息。

如果返回0,則表示dma_map_sg映射失敗,驅(qū)動程序需要采取適當(dāng)措施。驅(qū)動程序在此時做一些事情顯得格外重要,一個阻塞驅(qū)動中斷請求或者oopsing都總比什么都不做導(dǎo)致文件系統(tǒng)癱瘓強(qiáng)很多。

下面是個分散/聚集映射的例子,假設(shè)scatterlists已經(jīng)存在。

int i, count = dma_map_sg(dev, sglist, nents, direction);
struct scatterlist *sg;

for_each_sg(sglist, sg, count, i) {
        hw_address[i] = sg_dma_address(sg);
        hw_len[i] = sg_dma_len(sg); 
}

其中nents為sglist條目的個數(shù)。

這種實(shí)現(xiàn)可以很方便將幾個連續(xù)的sglist條目合并成一個(比如在IOMMU系統(tǒng)中,或者一些頁正好是物理連續(xù)的)。

然后你就可以循環(huán)多次(可能小于nents次)使用sg_dma_address() 及sg_dma_len()來獲取sg的物理地址及長度。

void 
dma_unmap_sg
(struct device *dev, struct scatterlist *sg,
        int nhwentries, enum dma_data_direction direction)

取消先前分散/聚集鏈表的映射。所有參數(shù)和分散/聚集映射API的參數(shù)相同。

注意:<nents>是傳入的參數(shù),不一定是實(shí)際返回條目的數(shù)值。

void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size,
                                enum dma_data_direction direction)

void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size,
                                enum dma_data_direction direction)

void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems,
                            enum dma_data_direction direction)

void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems,
                            enum dma_data_direction direction)

為CPU及外設(shè)同步single contiguous或分散/聚集映射。

注意:你必須要做這個工作,

  • 在CPU讀操作前,此時緩沖區(qū)由設(shè)備通過DMA寫入數(shù)據(jù)(DMA_FROM_DEVICE)

  • 在CPU寫操作后,緩沖區(qū)數(shù)據(jù)將通過DMA傳輸?shù)皆O(shè)備(DMA_TO_DEVICE)

  • 在傳輸數(shù)據(jù)到設(shè)備前后(DMA_BIDIRECTIONAL)

dma_addr_t 
dma_map_single_attrs
(struct device *dev, void *cpu_addr, size_t size,
                    enum dma_data_direction dir,
                    struct dma_attrs *attrs)

void 
dma_unmap_single_attrs
(struct device *dev, dma_addr_t dma_addr, 
                    size_t size, enum dma_data_direction dir,
                    struct dma_attrs *attrs)

int 
dma_map_sg_attrs
(struct device *dev, struct scatterlist *sgl, 
                int nents, enum dma_data_direction dir,
                struct dma_attrs *attrs)

void 
dma_unmap_sg_attrs
(struct device *dev, struct scatterlist *sgl, 
                    int nents, enum dma_data_direction dir,
                    struct dma_attrs *attrs)

這四個函數(shù)除了傳入可選的struct dma_attrs*之外,其他和不帶_attrs后綴的函數(shù)一樣。

struct dma_attrs概述了一組DMA屬性。struct dma_attrs詳細(xì)定義請參見linux/dma-attrs.h。

DMA屬性的定義是和體系結(jié)構(gòu)相關(guān)的,并且Documentation/DMA-attributes.txt有詳細(xì)描述。

如果struct dma_attrs* 為空,則這些函數(shù)可以認(rèn)為和不帶_attrs后綴的函數(shù)相同。

下面給出一個如何使用*_attrs 函數(shù)的例子,當(dāng)進(jìn)行DMA內(nèi)存映射時,如何傳入一個名為DMA_ATTR_FOO的屬性:

#include <linux/dma-attrs.h> 
/* DMA_ATTR_FOO should be defined in linux/dma-attrs.h and
* documented in Documentation/DMA-attributes.txt */ 
...
        DEFINE_DMA_ATTRS(attrs);
        dma_set_attr(DMA_ATTR_FOO, &attrs);
        ....
        n = dma_map_sg_attrs(dev, sg, nents, DMA_TO_DEVICE, &attr);
        ....

在映射/取消映射的函數(shù)中,可以檢查DMA_ATTR_FOO是否存在:

void whizco_dma_map_sg_attrs(struct device *dev, dma_addr_t dma_addr,
                            size_t size, enum dma_data_direction dir,
                            struct dma_attrs *attrs) 
{
        ....
        int foo = dma_get_attr(DMA_ATTR_FOO, attrs);
        ....
        if (foo)
            /* twizzle the frobnozzle */
        ....

第二部分  高級DMA使用方法

警告:下面這些DMA API在大多數(shù)情況下不應(yīng)該被使用。因?yàn)樗鼈優(yōu)橐恍┨厥獾男枨蠖鴾?zhǔn)備的,大部分驅(qū)動程序并沒有這些需求。

如果你不清楚如何確保橋接處理器和I/O設(shè)備之間的高速緩存行的一致性,你就根本不應(yīng)該使用該部分所提到的API。

void * 
dma_alloc_noncoherent(struct device *dev, size_t size,
                            dma_addr_t *dma_handle, gfp_t flag)

平臺會根據(jù)自身適應(yīng)條件來選擇返回一致性或非一致性內(nèi)存,其他和dma_alloc_coherent()相同。在使用該函數(shù)時,你應(yīng)該確保在驅(qū)動程序中對該內(nèi)存做了正確的和必要的同步操作。

注意,如果返回一致性內(nèi)存,則它會確保所有同步操作都變成空操作。

警告:處理非一致性內(nèi)存是件痛苦的事情。如果你確信你的驅(qū)動要在非常罕見的平臺上(通常是非PCI平臺)運(yùn)行,這些平臺無法分配一致性內(nèi)存時,你才可以使用該API。

void 
dma_free_noncoherent(struct device *dev, size_t size, void *cpu_addr,
                            dma_addr_t dma_handle)

釋放由非一致性API申請的內(nèi)存。

int 
dma_get_cache_alignment(void)

返回處理器高速緩存對齊值。應(yīng)該注意在你打算映射內(nèi)存或者做局部映射時,該值為最小對齊值。

注意:該API可能返回一個比實(shí)際緩存行的大的值。通常為了方便對齊,該值為2的冪次方。

void 
dma_cache_sync(struct device *dev, void *vaddr, size_t size, 
                enum dma_data_direction direction)

對由dma_alloc_noncoherent()申請的內(nèi)存做局部映射,其實(shí)虛擬地址為vaddr。在做該操作時,請注意緩存行的邊界。

int 
dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
                            dma_addr_t device_addr, size_t size, int flags)

當(dāng)設(shè)備需要一段一致性內(nèi)存時,申請由dma_alloc_coherent分配的一段內(nèi)存區(qū)域。

flag 可以由下面這些標(biāo)志位進(jìn)行或操作。

DMA_MEMORY_MAP    請求由dma_alloc_coherent()申請的內(nèi)存為直接可寫。

DMA_MEMORY_IO    請求由dma_alloc_coherent()申請的內(nèi)存可以通過read/write/memcpy_toio等函數(shù)尋址到。

flag必須包含上述其中一個或者兩個標(biāo)志位。

DMA_MEMORY_INCLUDES_CHILDREN   

DMA_MEMORY_EXCLUSIVE   

為了使操作簡單化,每個設(shè)備只能申申明一個該內(nèi)存區(qū)域。

處于效率考慮的目的,大多數(shù)平臺選擇頁對齊的區(qū)域。對于更小的內(nèi)存分配,可以使用dma_pool() API。

void 
dma_release_declared_memory(struct device *dev)

從系統(tǒng)中移除先前申明的內(nèi)存區(qū)域。該函數(shù)不會檢測當(dāng)前區(qū)域是否在使用。確保該內(nèi)存區(qū)域當(dāng)前沒有被使用這是驅(qū)動程序的事情。

void * 
dma_mark_declared_memory_occupied(struct device *dev, 
                dma_addr_t device_addr, size_t size)

該函數(shù)用于覆蓋特殊內(nèi)存區(qū)域(dma_alloc_coherent()會分配出第一個可用內(nèi)存區(qū)域)。

返回值為指向該內(nèi)存的處理器虛擬地址,或者如果其中福分區(qū)域被覆蓋,則返回一個錯誤(通過PRT_ERR())。

第三部分  調(diào)試驅(qū)動程序?qū)MA-API的使用情況

DMA-API如前文所述有一些限制。在支持硬件IOMMU的系統(tǒng)中,驅(qū)動程序不能違反這些限制將變得更加重要。最糟糕的情況是,如果違反了這些限制準(zhǔn)則,會導(dǎo)致數(shù)據(jù)出錯知道摧毀文件系統(tǒng)。

為了debug驅(qū)動程序及發(fā)現(xiàn)使用DMA-API時的bug,檢測代碼可以編譯到kernel中,它們可以告訴開發(fā) 者那些違規(guī)行為。如果你的體系結(jié)構(gòu)支持,你可以選擇編譯選項(xiàng)“Enable debugging of DMA-API usage”,使能這個選項(xiàng)會影響系統(tǒng)性能,所以請勿在產(chǎn)品內(nèi)核中加入該選項(xiàng)。

如果你用使能debug選項(xiàng)的內(nèi)核啟動,那么它會記錄哪些設(shè)備會使用什么DMA內(nèi)存。如果檢測到錯誤信息,則會在內(nèi)核log中打印一些警告信息。下面是一個警告提示的例子:

------------[ cut here ]------------
WARNING: at /data2/repos/linux-2.6-iommu/lib/dma-debug.c:448 
        check_unmap+0x203/0x490() 
Hardware name: 
forcedeth 0000:00:08.0: DMA-API: device driver frees DMA memory with wrong 
        function [device address=0x00000000640444be] [size=66 bytes] [mapped as 
single] [unmapped as page] 
Modules linked in: nfsd exportfs bridge stp llc r8169 
Pid: 0, comm: swapper Tainted: G W 2.6.28-dmatest-09289-g8bb99c0 #1 
Call Trace: 
<IRQ> [<ffffffff80240b22>] warn_slowpath+0xf2/0x130
[<ffffffff80647b70>] _spin_unlock+0x10/0x30
[<ffffffff80537e75>] usb_hcd_link_urb_to_ep+0x75/0xc0
[<ffffffff80647c22>] _spin_unlock_irqrestore+0x12/0x40
[<ffffffff8055347f>] ohci_urb_enqueue+0x19f/0x7c0
[<ffffffff80252f96>] queue_work+0x56/0x60
[<ffffffff80237e10>] enqueue_task_fair+0x20/0x50
[<ffffffff80539279>] usb_hcd_submit_urb+0x379/0xbc0
[<ffffffff803b78c3>] cpumask_next_and+0x23/0x40
[<ffffffff80235177>] find_busiest_group+0x207/0x8a0
[<ffffffff8064784f>] _spin_lock_irqsave+0x1f/0x50
[<ffffffff803c7ea3>] check_unmap+0x203/0x490
[<ffffffff803c8259>] debug_dma_unmap_page+0x49/0x50
[<ffffffff80485f26>] nv_tx_done_optimized+0xc6/0x2c0
[<ffffffff80486c13>] nv_nic_irq_optimized+0x73/0x2b0
[<ffffffff8026df84>] handle_IRQ_event+0x34/0x70
[<ffffffff8026ffe9>] handle_edge_irq+0xc9/0x150
[<ffffffff8020e3ab>] do_IRQ+0xcb/0x1c0
[<ffffffff8020c093>] ret_from_intr+0x0/0xa
<EOI> <4>---[ end trace f6435a98e2a38c0e ]---

驅(qū)動開發(fā)者可以通過DMA-API的?;厮菪畔⒄页鍪裁磳?dǎo)致這些警告。

默認(rèn)情況下只有第一個錯誤會打印警告信息,其他錯誤不會打印警告信息。這種機(jī)制保證當(dāng)前警告打印信息不會沖了你的內(nèi)核信息。為了debug設(shè)備驅(qū)動,可以通過debugfs禁止該功能。請看下面詳細(xì)的defbugfs接口文檔。

調(diào)試DMA-API代碼的debugfs目錄叫dma-api/。下列文件存在于該個目錄下:

dma-api/all_errors    該文件節(jié)點(diǎn)包含一個數(shù)值。如果該值不為零,則調(diào)試代碼會在遇到每個錯誤的時候都打印警告信息。請注意這個選項(xiàng)會輕易覆蓋你的內(nèi)核信息緩沖區(qū)。

dma-api/disabled    只讀文件節(jié)點(diǎn),如果禁止調(diào)試代碼則顯示字符“Y”。當(dāng)系統(tǒng)沒有足夠內(nèi)存或者在系統(tǒng)啟動時禁止調(diào)試功能時,該節(jié)點(diǎn)顯示“Y”。

dma-api/error_count    只讀文件節(jié)點(diǎn),顯示發(fā)現(xiàn)錯誤的次數(shù)。

dma-api/num_errors    該文件節(jié)點(diǎn)顯示在打印停止前一共打印多少個警告信息。該值在系統(tǒng)啟動時初始化為1,通過寫該文件節(jié)點(diǎn)來設(shè)置該值。

dma-api/min_free_entries    只讀文件節(jié)點(diǎn),顯示分配器記錄的可用dma_debug_entries的最小數(shù)目。如果該值變?yōu)榱悖瑒t禁止調(diào)試代碼。

dma-api/num_free_entries    當(dāng)前分配器可用dma_debug_entries的數(shù)目。

dma-api/driver-filter    通過向該文件節(jié)點(diǎn)寫入驅(qū)動的名字來限制特定驅(qū)動的調(diào)試輸出。如果向該節(jié)點(diǎn)輸入空字符,則可以再次看到全部錯誤信息。

如果這些代碼默認(rèn)編譯到你的內(nèi)核中,該調(diào)試功能被默認(rèn)打開。如果在啟動時你不想使用該功能,則可以設(shè)置“dma_debug=off”作為啟動參數(shù),該參數(shù)會禁止該功能。如果你想在系統(tǒng)啟動后再次打開該功能,則必須重啟系統(tǒng)。

如果你指向看到特定設(shè)備驅(qū)動的調(diào)試信息,則可以設(shè)置“dma_debug_driver=<drivername>”作為參數(shù)。它會在系統(tǒng)啟動時使能驅(qū)動過濾器。調(diào)試代碼只會打印和該驅(qū)動相關(guān)的錯誤信息。過濾器可以通過debugfs來關(guān)閉或者改變。

如果該調(diào)試功能在系統(tǒng)運(yùn)行時自動關(guān)閉,則可能是超出了dma_debug_entries的最大限制。這些 debug條目在啟動時就分配好了,條目數(shù)量由每個體系結(jié)構(gòu)自己定義。你可以在啟動時使用“dma_debug_entries=< your_desired_number>”來重寫該值。 

參考文獻(xiàn)

[1] documentation/DMA-API.txt

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多