linux内存管理之DMA
说起DMA我们并不陌生,但是实际编程中去用的人不多吧,最多就是网卡驱动里的环形buffer,再有就是设备的dma,下面我们就分析分析.
DMA用来在设备内存和内存之间直接数据交互。而无需cpu干预
内核为了方便驱动的开发,已经提供了几个dma 函数接口。
dma跟硬件架构相关,所以linux关于硬件部分已经给屏蔽了,有兴趣的可以深入跟踪学习.
按照linux内核对dma层的架构设计,各平台dma缓冲区映射之间的差异由内核定义的一个dma操作集
include/linux/dma-mapping.h:点击(此处)折叠或打开
- struct dma_map_ops {
- void* (*alloc)(struct device *dev, size_t size,
- dma_addr_t *dma_handle, gfp_t gfp,
- struct dma_attrs *attrs);
- void (*free)(struct device *dev, size_t size,
- void *vaddr, dma_addr_t dma_handle,
- struct dma_attrs *attrs);
- int (*mmap)(struct device *, struct vm_area_struct *,
- void *, dma_addr_t, size_t, struct dma_attrs *attrs);
- int (*get_sgtable)(struct device *dev, struct sg_table *sgt, void *,
- dma_addr_t, size_t, struct dma_attrs *attrs);
- dma_addr_t (*map_page)(struct device *dev, struct page *page,
- unsigned long offset, size_t size,
- enum dma_data_direction dir,
- struct dma_attrs *attrs);
- void (*unmap_page)(struct device *dev, dma_addr_t dma_handle,
- size_t size, enum dma_data_direction dir,
- struct dma_attrs *attrs);
- int (*map_sg)(struct device *dev, struct scatterlist *sg,
- int nents, enum dma_data_direction dir,
- struct dma_attrs *attrs);
- void (*unmap_sg)(struct device *dev,
- struct scatterlist *sg, int nents,
- enum dma_data_direction dir,
- struct dma_attrs *attrs);
- void (*sync_single_for_cpu)(struct device *dev,
- dma_addr_t dma_handle, size_t size,
- enum dma_data_direction dir);
- void (*sync_single_for_device)(struct device *dev,
- dma_addr_t dma_handle, size_t size,
- enum dma_data_direction dir);
- void (*sync_sg_for_cpu)(struct device *dev,
- struct scatterlist *sg, int nents,
- enum dma_data_direction dir);
- void (*sync_sg_for_device)(struct device *dev,
- struct scatterlist *sg, int nents,
- enum dma_data_direction dir);
- int (*mapping_error)(struct device *dev, dma_addr_t dma_addr);
- int (*dma_supported)(struct device *dev, u64 mask);
- int (*set_dma_mask)(struct device *dev, u64 mask);
- #ifdef ARCH_HAS_DMA_GET_REQUIRED_MASK
- u64 (*get_required_mask)(struct device *dev);
- #endif
- int is_phys;
- }
来统一屏蔽实现的差异.
不同差异主要来来自cache的问题
Cache与dma同步问题,这里不深入讨论.
另外一个常用的函数是Dma_set_mask, 为了通知内核设备能够寻址的范围,很多时候设备能够寻址的范围有限。
Dma映射可以分为三类:
1. 一致性dma映射 dma_alloc_coherent (问题:驱动使用的buffer不是自身申请的,而是其他模块)
当驱动模块主动分配一个Dma缓冲区并且dma生存期和模块一样时
参数说明:
(1)这个函数的返回值是缓冲的一个内核虚拟地址, 它可被驱动使用
(2)第三个参数dma_handle:
其间相关的物理地址在 dma_handle 中返回
2. 流式dma映射 dma_map_single
通常用于把内核一段buffer映射,返回物理地址.
如果驱动模块需要使用从别的模块传进来的虚拟地址空间作为dma缓冲区,保证地址的线性 cache一致性
一致性api接口:sync_single_for_cpu
3.分散/聚集映射(scatter/gather map) Dma_map_sgs
有时候我们还需要
1. 回弹缓冲区 bounce buffer:当cpu侧物理地址不适合设备的dma操作的时候
2.
DmA内存池:一般dma映射都是单个page的整数倍,如果驱动程序需要更小的一致性映射的dma缓冲区,可以使用。类似于slab机制,
Dma_pool_create
下面我们就那网卡驱动的例子说说dma的具体应用,参考linux kernel e1000网卡
drivers/net/ethernet/intel/e1000/*
Ring buffer
Dma不能为高端内存,一般为32,默认低端内存,由于设备能够访问的地址范围有限。
设备使用物理地址,而代码使用虚拟地址。
就看看如何发送数据包:e1000_main.c:
e1000_xmit_frame: 关于帧的发送流程这里不多说.
点击(此处)折叠或打开
- static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb,
- struct net_device *netdev)
- {
- struct e1000_adapter *adapter = netdev_priv(netdev);
- struct e1000_hw *hw = &adapter->hw;
- struct e1000_tx_ring *tx_ring;
- unsigned int first, max_per_txd = E1000_MAX_DATA_PER_TXD;
- unsigned int max_txd_pwr = E1000_MAX_TXD_PWR;
- unsigned int tx_flags = 0;
- unsigned int len = skb_headlen(skb);
- unsigned int nr_frags;
- unsigned int mss;
- int count = 0;
- int tso;
- unsigned int f;
- /* This goes back to the question of how to logically map a tx queue
- * to a flow. Right now, performance is impacted slightly negatively
- * if using multiple tx queues. If the stack breaks away from a
- * single qdisc implementation, we can look at this again. */
- tx_ring = adapter->tx_ring;
- if (unlikely(skb->len <= 0)) {
- dev_kfree_skb_any(skb);
- return NETDEV_TX_OK;
- }
- /* On PCI/PCI-X HW, if packet size is less than ETH_ZLEN,
- * packets may get corrupted during padding by HW.
- * To WA this issue, pad all small packets manually.
- */
- if (skb->len < ETH_ZLEN) {
- if (skb_pad(skb, ETH_ZLEN - skb->len))
- return NETDEV_TX_OK;
- skb->len = ETH_ZLEN;
- skb_set_tail_pointer(skb, ETH_ZLEN);
- }
- mss = skb_shinfo(skb)->gso_size;
- /* The controller does a simple calculation to
- * make sure there is enough room in the FIFO before
- * initiating the DMA for each buffer. The calc is:
- * 4 = ceil(buffer len/mss). To make sure we don't
- * overrun the FIFO, adjust the max buffer len if mss
- * drops. */
- if (mss) {
- u8 hdr_len;
- max_per_txd = min(mss << 2, max_per_txd);
- max_txd_pwr = fls(max_per_txd) - 1;
- hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
- if (skb->data_len && hdr_len == len) {
- switch (hw->mac_type) {
- unsigned int pull_size;
- case e1000_82544:
- /* Make sure we have room to chop off 4 bytes,
- * and that the end alignment will work out to
- * this hardware's requirements
- * NOTE: this is a TSO only workaround
- * if end byte alignment not correct move us
- * into the next dword */
- if ((unsigned long)(skb_tail_pointer(skb) - 1) & 4)
- break;
- /* fall through */
- pull_size = min((unsigned int)4, skb->data_len);
- if (!__pskb_pull_tail(skb, pull_size)) {
- e_err(drv, "__pskb_pull_tail "
- "failed.\n");
- dev_kfree_skb_any(skb);
- return NETDEV_TX_OK;
- }
- len = skb_headlen(skb);
- break;
- default:
- /* do nothing */
- break;
- }
- }
- }
- /* reserve a descriptor for the offload context */
- if ((mss) || (skb->ip_summed == CHECKSUM_PARTIAL))
- count++;
- count++;
- /* Controller Erratum workaround */
- if (!skb->data_len && tx_ring->last_tx_tso && !skb_is_gso(skb))
- count++;
- count += TXD_USE_COUNT(len, max_txd_pwr);
- if (adapter->pcix_82544)
- count++;
- /* work-around for errata 10 and it applies to all controllers
- * in PCI-X mode, so add one more descriptor to the count
- */
- if (unlikely((hw->bus_type == e1000_bus_type_pcix) &&
- (len > 2015)))
- count++;
- nr_frags = skb_shinfo(skb)->nr_frags;
- for (f = 0; f < nr_frags; f++)
- count += TXD_USE_COUNT(skb_frag_size(&skb_shinfo(skb)->frags[f]),
- max_txd_pwr);
- if (adapter->pcix_82544)
- count += nr_frags;
- /* need: count + 2 desc gap to keep tail from touching
- * head, otherwise try next time */
- if (unlikely(e1000_maybe_stop_tx(netdev, tx_ring, count + 2)))
- return NETDEV_TX_BUSY;
- if (unlikely((hw->mac_type == e1000_82547) &&
- (e1000_82547_fifo_workaround(adapter, skb)))) {
- netif_stop_queue(netdev);
- if (!test_bit(__E1000_DOWN, &adapter->flags))
- schedule_delayed_work(&adapter->fifo_stall_task, 1);
- return NETDEV_TX_BUSY;
- }
- if (vlan_tx_tag_present(skb)) {
- tx_flags |= E1000_TX_FLAGS_VLAN;
- tx_flags |= (vlan_tx_tag_get(skb) << E1000_TX_FLAGS_VLAN_SHIFT);
- }
- first = tx_ring->next_to_use;
- tso = e1000_tso(adapter, tx_ring, skb);
- if (tso < 0) {
- dev_kfree_skb_any(skb);
- return NETDEV_TX_OK;
- }
- if (likely(tso)) {
- if (likely(hw->mac_type != e1000_82544))
- tx_ring->last_tx_tso = true;
- tx_flags |= E1000_TX_FLAGS_TSO;
- } else if (likely(e1000_tx_csum(adapter, tx_ring, skb)))
- tx_flags |= E1000_TX_FLAGS_CSUM;
- if (likely(skb->protocol == htons(ETH_P_IP)))
- tx_flags |= E1000_TX_FLAGS_IPV4;
- if (unlikely(skb->no_fcs))
- tx_flags |= E1000_TX_FLAGS_NO_FCS;
- count = e1000_tx_map(adapter, tx_ring, skb, first, max_per_txd,
- nr_frags, mss);
- if (count) {
- netdev_sent_queue(netdev, skb->len);
- skb_tx_timestamp(skb);
- e1000_tx_queue(adapter, tx_ring, tx_flags, count);
- /* Make sure there is space in the ring for the next send. */
- e1000_maybe_stop_tx(netdev, tx_ring, MAX_SKB_FRAGS + 2);
- } else {
- dev_kfree_skb_any(skb);
- tx_ring->buffer_info[first].time_stamp = 0;
- tx_ring->next_to_use = first;
- }
- return NETDEV_TX_OK;
- }
经过上次,邻居子系统后,数据帧已经到达驱动,数据放在skb指定的内存里.
看代码
tx_ring = adapter->tx_ring; // 获取发送的ring buffer
接着我们看关键代码:
count = e1000_tx_map(adapter, tx_ring, skb, first, max_per_txd, nr_frags, mss);
它做了什么呢?
点击(此处)折叠或打开
- static int e1000_tx_map(struct e1000_adapter *adapter,
- struct e1000_tx_ring *tx_ring,
- struct sk_buff *skb, unsigned int first,
- unsigned int max_per_txd, unsigned int nr_frags,
- unsigned int mss)
- {
- struct e1000_hw *hw = &adapter->hw;
- struct pci_dev *pdev = adapter->pdev;
- struct e1000_buffer *buffer_info;
- unsigned int len = skb_headlen(skb);
- unsigned int offset = 0, size, count = 0, i;
- unsigned int f, bytecount, segs;
- i = tx_ring->next_to_use;
- while (len) {
- buffer_info = &tx_ring->buffer_info[i];
- size = min(len, max_per_txd);
- /* Workaround for Controller erratum --
- * descriptor for non-tso packet in a linear SKB that follows a
- * tso gets written back prematurely before the data is fully
- * DMA'd to the controller */
- if (!skb->data_len && tx_ring->last_tx_tso &&
- !skb_is_gso(skb)) {
- tx_ring->last_tx_tso = false;
- size -= 4;
- }
- /* Workaround for premature desc write-backs
- * in TSO mode. Append 4-byte sentinel desc */
- if (unlikely(mss && !nr_frags && size == len && size > 8))
- size -= 4;
- /* work-around for errata 10 and it applies
- * to all controllers in PCI-X mode
- * The fix is to make sure that the first descriptor of a
- * packet is smaller than 2048 - 16 - 16 (or 2016) bytes
- */
- if (unlikely((hw->bus_type == e1000_bus_type_pcix) &&
- (size > 2015) && count == 0))
- size = 2015;
- /* Workaround for potential 82544 hang in PCI-X. Avoid
- * terminating buffers within evenly-aligned dwords. */
- if (unlikely(adapter->pcix_82544 &&
- !((unsigned long)(skb->data + offset + size - 1) & 4) &&
- size > 4))
- size -= 4;
- buffer_info->length = size;
- /* set time_stamp *before* dma to help avoid a possible race */
- buffer_info->time_stamp = jiffies;
- buffer_info->mapped_as_page = false;
- buffer_info->dma = dma_map_single(&pdev->dev,
- skb->data + offset,
- size, DMA_TO_DEVICE);
- if (dma_mapping_error(&pdev->dev, buffer_info->dma))
- goto dma_error;
- buffer_info->next_to_watch = i;
- len -= size;
- offset += size;
- count++;
- if (len) {
- i++;
- if (unlikely(i == tx_ring->count))
- i = 0;
- }
- }
- for (f = 0; f < nr_frags; f++) {
- const struct skb_frag_struct *frag;
- frag = &skb_shinfo(skb)->frags[f];
- len = skb_frag_size(frag);
- offset = 0;
- while (len) {
- unsigned long bufend;
- i++;
- if (unlikely(i == tx_ring->count))
- i = 0;
- buffer_info = &tx_ring->buffer_info[i];
- size = min(len, max_per_txd);
- /* Workaround for premature desc write-backs
- * in TSO mode. Append 4-byte sentinel desc */
- if (unlikely(mss && f == (nr_frags-1) && size == len && size > 8))
- size -= 4;
- /* Workaround for potential 82544 hang in PCI-X.
- * Avoid terminating buffers within evenly-aligned
- * dwords. */
- bufend = (unsigned long)
- page_to_phys(skb_frag_page(frag));
- bufend += offset + size - 1;
- if (unlikely(adapter->pcix_82544 &&
- !(bufend & 4) &&
- size > 4))
- size -= 4;
- buffer_info->length = size;
- buffer_info->time_stamp = jiffies;
- buffer_info->mapped_as_page = true;
- buffer_info->dma = skb_frag_dma_map(&pdev->dev, frag,
- offset, size, DMA_TO_DEVICE);
- if (dma_mapping_error(&pdev->dev, buffer_info->dma))
- goto dma_error;
- buffer_info->next_to_watch = i;
- len -= size;
- offset += size;
- count++;
- }
- }
- segs = skb_shinfo(skb)->gso_segs ?: 1;
- /* multiply data chunks by size of headers */
- bytecount = ((segs - 1) * skb_headlen(skb)) + skb->len;
- tx_ring->buffer_info[i].skb = skb;
- tx_ring->buffer_info[i].segs = segs;
- tx_ring->buffer_info[i].bytecount = bytecount;
- tx_ring->buffer_info[first].next_to_watch = i;
- return count;
- dma_error:
- dev_err(&pdev->dev, "TX DMA map failed\n");
- buffer_info->dma = 0;
- if (count)
- count--;
- while (count--) {
- if (i==0)
- i += tx_ring->count;
- i--;
- buffer_info = &tx_ring->buffer_info[i];
- e1000_unmap_and_free_tx_resource(adapter, buffer_info);
- }
- return 0;
- }
默认数据报文没有分片或者碎片什么的。
那么进入第一个while(len)
获取buffer_info = &tx_ring->buffer_info[i];
然后:调用dma_map_single进行流式映射. 即把skb->data(虚拟地址) 和buffer_info->dma(物理地址)对应起来.操作两个地址等于操作同一片区域。
点击(此处)折叠或打开
- buffer_info->length = size;
- /* set time_stamp *before* dma to help avoid a possible race */
- buffer_info->time_stamp = jiffies;
- buffer_info->mapped_as_page = false;
- buffer_info->dma = dma_map_single(&pdev->dev,
- skb->data + offset,
- size, DMA_TO_DEVICE);
回到主发送函数:
点击(此处)折叠或打开
- if (count) {
- netdev_sent_queue(netdev, skb->len);
- skb_tx_timestamp(skb);
- e1000_tx_queue(adapter, tx_ring, tx_flags, count);
- /* Make sure there is space in the ring for the next send. */
- e1000_maybe_stop_tx(netdev, tx_ring, MAX_SKB_FRAGS + 2);
- }
调用e1000_tx_queue把数据发送出去:
点击(此处)折叠或打开
- static void e1000_tx_queue(struct e1000_adapter *adapter,
- struct e1000_tx_ring *tx_ring, int tx_flags,
- int count)
- {
- struct e1000_hw *hw = &adapter->hw;
- struct e1000_tx_desc *tx_desc = NULL;
- struct e1000_buffer *buffer_info;
- u32 txd_upper = 0, txd_lower = E1000_TXD_CMD_IFCS;
- unsigned int i;
- ...
- i = tx_ring->next_to_use;
- while (count--) {
- buffer_info = &tx_ring->buffer_info[i];
- tx_desc = E1000_TX_DESC(*tx_ring, i);
- tx_desc->buffer_addr = cpu_to_le64(buffer_info->dma);
- tx_desc->lower.data =
- cpu_to_le32(txd_lower | buffer_info->length);
- tx_desc->upper.data = cpu_to_le32(txd_upper);
- if (unlikely(++i == tx_ring->count)) i = 0;
- }
- tx_desc->lower.data |= cpu_to_le32(adapter->txd_cmd);
- /* txd_cmd re-enables FCS, so we'll re-disable it here as desired. */
- if (unlikely(tx_flags & E1000_TX_FLAGS_NO_FCS))
- tx_desc->lower.data &= ~(cpu_to_le32(E1000_TXD_CMD_IFCS));
- /* Force memory writes to complete before letting h/w
- * know there are new descriptors to fetch. (Only
- * applicable for weak-ordered memory model archs,
- * such as IA-64). */
- wmb();
- tx_ring->next_to_use = i;
- writel(i, hw->hw_addr + tx_ring->tdt);
- /* we need this if more than one processor can write to our tail
- * at a time, it syncronizes IO on IA64/Altix systems */
- mmiowb();
- }
我们看到它把刚才dma_map_singe里的映射赋值了:
tx_desc->buffer_addr = cpu_to_le64(buffer_info->dma);
说明发送的时候是根据发送描述符来发送的。
然后操作寄存器:
writel(i, hw->hw_addr + tx_ring->tdt);
那么网卡就会自动读取tx desc 然后把数据发送出去。
总结下流程:
1. linux os会调用网卡的start_xmit()函数。在e1000里,对应的函数是 e1000_xmit_frame,
2. e1000_xmit_frame又会调用e1000_tx_queue(adapter, tx_ring, tx_flags, count)。
这里的tx_queue指的是发送Descriptor的queue。
3. e1000_tx_queue 在检查了一些参数后,最终调用 writel(i, hw->hw_addr + tx_ring->tdt)。
这里的tx_ring->tdt中的tdt全写为 tx_descriptor_tail。从网卡的开发手册中可以查到,如果写了descriptor tail,那么网卡就会自动读取 descriptor,然后把包发送出去。
descroptor的主要内容是addr pointer和length。前者是要发送的包的起始物理地址。后者是包的长度。有了这些,硬件就可以通过dma来读取包并发出去了。其他网卡也基本会用descriptor的结构。
虽然流程明白了,但是还有几个点,
1. tx_ring在哪初始化?
2. 网卡到底是如何操作映射的dma地址的,把数据发送出去的?
tx ring 在e1000_open 的时候:
调用:
点击(此处)折叠或打开
- /**
- * e1000_setup_all_tx_resources - wrapper to allocate Tx resources
- * (Descriptors) for all queues
- * @adapter: board private structure
- *
- * Return 0 on success, negative on failure
- **/
- int e1000_setup_all_tx_resources(struct e1000_adapter *adapter)
- {
- int i, err = 0;
- for (i = 0; i < adapter->num_tx_queues; i++) {
- err = e1000_setup_tx_resources(adapter, &adapter->tx_ring[i]);
- if (err) {
- e_err(probe, "Allocation for Tx Queue %u failed\n", i);
- for (i-- ; i >= 0; i--)
- e1000_free_tx_resources(adapter,
- &adapter->tx_ring[i]);
- break;
- }
- }
- return err;
- }
点击(此处)折叠或打开
- /**
- * e1000_setup_tx_resources - allocate Tx resources (Descriptors)
- * @adapter: board private structure
- * @txdr: tx descriptor ring (for a specific queue) to setup
- *
- * Return 0 on success, negative on failure
- **/
- static int e1000_setup_tx_resources(struct e1000_adapter *adapter,
- struct e1000_tx_ring *txdr)
- {
- struct pci_dev *pdev = adapter->pdev;
- int size;
- size = sizeof(struct e1000_buffer) * txdr->count;
- txdr->buffer_info = vzalloc(size);
- if (!txdr->buffer_info) {
- e_err(probe, "Unable to allocate memory for the Tx descriptor "
- "ring\n");
- return -ENOMEM;
- }
- /* round up to nearest 4K */
- txdr->size = txdr->count * sizeof(struct e1000_tx_desc);
- txdr->size = ALIGN(txdr->size, 4096);
- txdr->desc = dma_alloc_coherent(&pdev->dev, txdr->size, &txdr->dma,
- GFP_KERNEL);
- if (!txdr->desc) {
- setup_tx_desc_die:
- vfree(txdr->buffer_info);
- e_err(probe, "Unable to allocate memory for the Tx descriptor "
- "ring\n");
- return -ENOMEM;
- }
- /* Fix for errata 23, can't cross 64kB boundary */
- if (!e1000_check_64k_bound(adapter, txdr->desc, txdr->size)) {
- void *olddesc = txdr->desc;
- dma_addr_t olddma = txdr->dma;
- e_err(tx_err, "txdr align check failed: %u bytes at %p\n",
- txdr->size, txdr->desc);
- /* Try again, without freeing the previous */
- txdr->desc = dma_alloc_coherent(&pdev->dev, txdr->size,
- &txdr->dma, GFP_KERNEL);
- /* Failed allocation, critical failure */
- if (!txdr->desc) {
- dma_free_coherent(&pdev->dev, txdr->size, olddesc,
- olddma);
- goto setup_tx_desc_die;
- }
- if (!e1000_check_64k_bound(adapter, txdr->desc, txdr->size)) {
- /* give up */
- dma_free_coherent(&pdev->dev, txdr->size, txdr->desc,
- txdr->dma);
- dma_free_coherent(&pdev->dev, txdr->size, olddesc,
- olddma);
- e_err(probe, "Unable to allocate aligned memory "
- "for the transmit descriptor ring\n");
- vfree(txdr->buffer_info);
- return -ENOMEM;
- } else {
- /* Free old allocation, new allocation was successful */
- dma_free_coherent(&pdev->dev, txdr->size, olddesc,
- olddma);
- }
- }
- memset(txdr->desc, 0, txdr->size);
- txdr->next_to_use = 0;
- txdr->next_to_clean = 0;
- return 0;
- }
我们看:它建立了一致性dma映射.
- txdr->desc = dma_alloc_coherent(&pdev->dev, txdr->size,
- &txdr->dma, GFP_KERNEL);
desc是结构指针:它的结构跟网卡寄存器结构有关,e1000_hw.h
点击(此处)折叠或打开
- /* Transmit Descriptor */
- struct e1000_tx_desc {
- __le64 buffer_addr; /* Address of the descriptor's data buffer */
- union {
- __le32 data;
- struct {
- __le16 length; /* Data buffer length */
- u8 cso; /* Checksum offset */
- u8 cmd; /* Descriptor control */
- } flags;
- } lower;
- union {
- __le32 data;
- struct {
- u8 status; /* Descriptor status */
- u8 css; /* Checksum start */
- __le16 special;
- } fields;
- } upper;
- }
我们稍微屡一下,
1. skb->data --- ring->buffer_info->dma
2.ring->dma --- ring->desc
3. ring->desc->buffer_addr ---ring->buffer_info->dma
那么网卡又是如何和dma地址关联的呢?
点击(此处)折叠或打开
- /**
- * e1000_configure_tx - Configure 8254x Transmit Unit after Reset
- * @adapter: board private structure
- *
- * Configure the Tx unit of the MAC after a reset.
- **/
- static void e1000_configure_tx(struct e1000_adapter *adapter)
- {
- u64 tdba;
- struct e1000_hw *hw = &adapter->hw;
- u32 tdlen, tctl, tipg;
- u32 ipgr1, ipgr2;
- /* Setup the HW Tx Head and Tail descriptor pointers */
- switch (adapter->num_tx_queues) {
- case 1:
- default:
- tdba = adapter->tx_ring[0].dma;
- tdlen = adapter->tx_ring[0].count *
- sizeof(struct e1000_tx_desc);
- ew32(TDLEN, tdlen);
- ew32(TDBAH, (tdba >> 32));
- ew32(TDBAL, (tdba & 0x00000000ffffffffULL));
- ew32(TDT, 0);
- ew32(TDH, 0);
- adapter->tx_ring[0].tdh = ((hw->mac_type >= e1000_82543) ? E1000_TDH : E1000_82542_TDH);
- adapter->tx_ring[0].tdt = ((hw->mac_type >= e1000_82543) ? E1000_TDT : E1000_82542_TDT);
- break;
- }
很明显它把dma地址写入了网卡dma寄存器。所以dma还需要网卡硬件的支持才行.
当然e1000这个网卡驱动还是相当的复杂,不过它把一致性映射和流式映射都用上了。
linux内存管理之DMA的更多相关文章
- Linux内存管理原理
本文以32位机器为准,串讲一些内存管理的知识点. 1. 虚拟地址.物理地址.逻辑地址.线性地址 虚拟地址又叫线性地址.linux没有采用分段机制,所以逻辑地址和虚拟地址(线性地址)(在用户态,内核态逻 ...
- Linux内存管理原理【转】
转自:http://www.cnblogs.com/zhaoyl/p/3695517.html 本文以32位机器为准,串讲一些内存管理的知识点. 1. 虚拟地址.物理地址.逻辑地址.线性地址 虚拟地址 ...
- Windows内存管理和linux内存管理
windows内存管理 windows 内存管理方式主要分为:页式管理,段式管理,段页式管理. 页式管理的基本原理是将各进程的虚拟空间划分为若干个长度相等的页:页式管理把内存空间按照页的大小划分成片或 ...
- Linux内存管理 (5)slab分配器
专题:Linux内存管理专题 关键词:slab/slub/slob.slab描述符.kmalloc.本地/共享对象缓冲池.slabs_partial/slabs_full/slabs_free.ava ...
- Linux内存管理专题
Linux的内存管理涉及到的内容非常庞杂,而且与内核的方方面面耦合在一起,想要理解透彻非常困难. 在开始学习之前进行了一些准备工作<如何展开Linux Memory Management学习?& ...
- 伙伴系统之伙伴系统概述--Linux内存管理(十五)
在内核初始化完成之后, 内存管理的责任就由伙伴系统来承担. 伙伴系统基于一种相对简单然而令人吃惊的强大算法. Linux内核使用二进制伙伴算法来管理和分配物理内存页面, 该算法由Knowlton设计, ...
- 启动期间的内存管理之bootmem_init初始化内存管理–Linux内存管理(十二)
1. 启动过程中的内存初始化 首先我们来看看start_kernel是如何初始化系统的, start_kerne定义在init/main.c?v=4.7, line 479 其代码很复杂, 我们只截取 ...
- 启动期间的内存管理之初始化过程概述----Linux内存管理(九)
在内存管理的上下文中, 初始化(initialization)可以有多种含义. 在许多CPU上, 必须显式设置适用于Linux内核的内存模型. 例如在x86_32上需要切换到保护模式, 然后内核才能检 ...
- Linux内存管理 (25)内存sysfs节点解读
1. General 1.1 /proc/meminfo /proc/meminfo是了解Linux系统内存使用状况主要接口,也是free等命令的数据来源. 下面是cat /proc/meminfo的 ...
随机推荐
- Android网络:开发浏览器(二)——功能完善之书签功能
经过上述的编写,基本的功能已经完成了,不过工具栏里面基本还是一片空白,只有一个刷新的功能,现在咱们就先完善这些功能(之前有朋友说来点图,那么这次我会截些图更好的来描述). 既然是浏览器,怎么能没有书签 ...
- uni - 介绍
uni-app 是一个使用 Vue.js 开发跨平台应用的前端框架,开发者编写一套代码,可编译到iOS.Android.H5.小程序等多个平台 优点:基于vue.mpvue.微信小程序 微信小程序AP ...
- php之快速入门学习-6(字符串变量)
PHP 字符串变量 字符串变量用于存储并处理文本. PHP 中的字符串变量 字符串变量用于包含有字符的值. 在创建字符串之后,我们就可以对它进行操作了.您可以直接在函数中使用字符串,或者把它存储在变量 ...
- java 设计模式大全
在线学习网址: http://www.runoob.com/design-pattern/
- Linux 驱动之内核定时器
1.定时器 之前说过两类跟时间相关的内核结构. 1.延时:通过忙等待或者睡眠机制实现延时. 2.tasklet和工作队列,通过某种机制使工作推后运行,但不知道运行的详细时间. 接下来要介绍的定时器,可 ...
- Loader拉取图片,由于redirect重定向,导致策略文件无效 设置checkPolicyFile后还是无效:需要一个策略文件,但在加载此媒体时未设置 checkPolicyFile 标志
大家好,在这里分享一下flash里边处理redirect的方法. 一般而言,大家不会遇到这个问题,毕竟图片地址一般杠杠的,不会redirect.但昨天在拉取空间的照片就会出现redirect.神啊!! ...
- HTML5 Canvas画图与动画学习59例
HTML5 Canvas画图与动画学习59例 学习HTML5 动画,画图的好资料. HTML5 Canvas画图与动画学习59例
- 〖Linux〗打开qtcreater出现错误的解决方法
1. 更换了显卡驱动,发现打开qtcreater时出现了以下的错误: qtcreator: error : cannot open shared object file: No such file o ...
- ___cxa_pure_virtual", referenced from
加入百度地图之后报这种错,解决方法:将project中的.m文件改一个成为.mm文件.
- Web Service基础——四种客户端调用方式
通过访问公网服务地址 http://www.webxml.com.cn/zh_cn/index.aspx 来演示四种不同的客户端调用方式 1. 生成客户端调用方式 1.1 Wsimport命令介绍 首 ...