dma_buf学习记录之二核心接口

dma_buf学习记录之一基础知识-CSDN博客
本章学习linux\include\linux\dma-buf.h

   dma-buf.h 是 Linux 内核中用于实现 DMA 缓冲区共享框架的核心头文件。它定义了 dma-buf 子系统的主要数据结构和操作接口,允许设备驱动程序之间共享缓冲区,并支持异步硬件访问的同步。通过 dma-buf,Linux 内核能够有效地管理跨设备驱动程序的缓冲区共享,同时确保访问的安全性和一致性。

一、主要数据结构

1. **`struct dma_buf_ops`**

/**
 * struct dma_buf_ops - 在 struct dma_buf 上可执行的操作
 * @vmap: [可选] 将缓冲区映射到内核地址空间,创建虚拟映射。
 *         与 vmap 和相关函数相同的限制适用。
 * @vunmap: [可选] 解除缓冲区的虚拟映射。
 */
struct dma_buf_ops {
  /**
   * 如果为 true,则框架将缓存每个附件的第一个映射。这可以避免多次为同一附件创建映射。
   */
	bool cache_sgt_mapping;

  /**
   * 此回调由 dma_buf_attach() 调用,确保给定的 &dma_buf_attachment.dev 可以访问提供的 &dma_buf。
   * 支持特殊位置(如 VRAM 或设备特定的 carveout 区域)缓冲区对象的导出者应检查缓冲区是否可以移动到系统内存(或被提供的设备直接访问),否则需要失败 attach 操作。
   * 导出者通常还应检查当前分配是否满足新设备的 DMA 约束。如果不满足,并且分配无法移动,则也应失败 attach 操作。
   * 导出者的私有数据可以存储在 &dma_buf_attachment.priv 指针中。
   * 此回调是可选的。
   * 返回值:
   * 成功返回 0,失败返回负错误码。如果返回 -EBUSY,则表示后端存储已分配且与请求设备的要求不兼容。
   */
	int (*attach)(struct dma_buf *, struct dma_buf_attachment *);

  /**
   * 此回调由 dma_buf_detach() 调用,释放 &dma_buf_attachment。
   * 提供此回调以便导出者清理与 &dma_buf_attachment 相关的任何私有数据。
   * 此回调是可选的。
   */
	void (*detach)(struct dma_buf *, struct dma_buf_attachment *);

  /**
   * 此回调由 dma_buf_pin() 调用,通知导出者 DMA 缓冲区不能再移动。理想情况下,导出者应固定缓冲区,使其对所有设备都可访问。
   * 此回调在 dmabuf.resv 对象锁定时调用,并与 @cache_sgt_mapping 互斥。
   * 对于非动态导入者,此回调会在 dma_buf_attach() 中自动调用。
   * 注意:类似于非动态导出者在其 @map_dma_buf 回调中的行为,驱动程序必须保证在此函数返回时内存可用并清除旧数据。内部流水线化缓冲区移动的驱动程序必须等待所有移动和清除完成。
   * 返回值:
   * 成功返回 0,失败返回负错误码。
   */
	int (*pin)(struct dma_buf_attachment *attach);

  /**
   * 此回调由 dma_buf_unpin() 调用,通知导出者 DMA 缓冲区可以再次移动。
   * 此回调在 dmabuf->resv 对象锁定时调用,并与 @cache_sgt_mapping 互斥。
   * 此回调是可选的。
   */
	void (*unpin)(struct dma_buf_attachment *attach);

  /**
   * 此回调由 dma_buf_map_attachment() 调用,用于将共享的 &dma_buf 映射到设备地址空间,是强制性的。只有在 @attach 成功调用后才能调用此函数。
   * 此调用可能会休眠,例如当后端存储首次需要分配,或需要移动到适合当前所有附加设备的位置时。
   * 注意:此函数所需的任何特定缓冲区属性应添加到通过 &device.dma_params 访问的 device_dma_parameters 中。@attach 回调也应检查这些约束。
   * 如果这是第一次调用,导出者可以选择扫描此缓冲区的所有附件,收集附加设备的要求,并选择合适的后端存储。
   * 根据枚举 dma_data_direction,可能允许多个用户同时访问(例如读取),或导出者希望提供给缓冲区用户的任何其他类型的共享。
   * 当 dynamic_mapping 标志为 true 时,此回调始终在 dmabuf->resv 对象锁定时调用。
   * 注意:对于非动态导出者,驱动程序必须保证在此函数返回时内存可用并清除旧数据。内部流水线化缓冲区移动的驱动程序必须等待所有移动和清除完成。动态导出者不需要遵循此规则:对于非动态导入者,缓冲区已经通过 @pin 固定,具有相同的要求。动态导入者则需要遵守 dma_resv 围栏。
   * 返回值:
   * 成功返回指向 DMA 缓冲区后端存储的 &sg_table 散列表,该散列表已映射到使用提供的 &dma_buf_attachment 附加的设备地址空间。散列表中的地址和长度以 PAGE_SIZE 对齐。
   * 失败时返回一个包含负错误值的指针。当信号到达时也可能返回 -EINTR。
   * 注意:导出者不应尝试缓存散列表,或为多次调用返回相同的散列表。缓存由 DMA-BUF 代码(针对非动态导入者)或导入者完成。散列表的所有权转移给调用者,并通过 @unmap_dma_buf 返回。
   */
	struct sg_table * (*map_dma_buf)(struct dma_buf_attachment *,
					 enum dma_data_direction);
  /**
   * 此回调由 dma_buf_unmap_attachment() 调用,用于解除并释放 @map_dma_buf 分配的 &sg_table,是强制性的。对于静态 dma_buf 处理,如果这是 DMA 缓冲区的最后一个映射,也可能解除固定后端存储。
   */
	void (*unmap_dma_buf)(struct dma_buf_attachment *,
			      struct sg_table *,
			      enum dma_data_direction);

	/* TODO: Add try_map_dma_buf version, to return immed with -EBUSY
	 * if the call would block.
	 */

  /**
   * 在最后一次 dma_buf_put 调用后调用,释放 &dma_buf,是强制性的。
   */
	void (*release)(struct dma_buf *);

  /**
   * 此回调由 dma_buf_begin_cpu_access() 调用,允许导出者确保内存实际对 CPU 访问是连贯的。导出者还应确保 CPU 访问对访问方向是连贯的。方向可用于优化缓存刷新,例如以不同的方向(读取而不是写入)访问可能会返回陈旧甚至错误的数据(例如当导出者需要将数据复制到临时存储时)。
   * 注意:此回调既通过 DMA_BUF_IOCTL_SYNC IOCTL 命令为通过 @mmap 建立的用户空间映射调用,也通过 @vmap 为内核映射调用。
   * 此回调是可选的。
   * 返回值:
   * 成功返回 0,失败返回负错误码。例如,当后端存储无法分配时可能会失败。当调用被中断并需要重新启动时,也可以返回 -ERESTARTSYS 或 -EINTR。
   */
	int (*begin_cpu_access)(struct dma_buf *, enum dma_data_direction);

  /**
   * 此回调由 dma_buf_end_cpu_access() 调用,当导入者完成对 CPU 的访问时调用。导出者可以使用此回调刷新缓存并撤销在 @begin_cpu_access 中所做的任何操作。
   * 此回调是可选的。
   * 返回值:
   * 成功返回 0,失败返回负错误码。当调用被中断并需要重新启动时,可以返回 -ERESTARTSYS 或 -EINTR。
   */
	int (*end_cpu_access)(struct dma_buf *, enum dma_data_direction);

  /**
   * 此回调由 dma_buf_mmap() 函数使用。
   * 注意:映射需要是非连贯的,用户空间预计会使用 DMA_BUF_IOCTL_SYNC 接口来括号化 CPU 访问。
   * 由于 dma-buf 缓冲区在其生命周期内的大小不变,dma-buf 核心会检查 vma 是否过大并拒绝此类映射。因此导出者无需重复此检查。驱动程序也不需要自己进行检查。
   * 如果导出者需要手动刷新缓存并伪造 mmap 支持的连贯性,则需要能够清除指向后端存储的所有 pte。Linux 内存管理需要与存储在 vma->vm_file 中的 struct file 关联的 struct address_space 来通过 unmap_mapping_range 函数完成此操作。但 dma_buf 框架只为每个 dma_buf fd 使用匿名文件 struct file,即所有 dma_buf 共享同一个文件。
   * 因此,导出者需要通过设置 vma->vm_file 并调整 vma->vm_pgoff 来在 dma_buf mmap 回调中建立自己的文件(和 address_space)关联。在 gem 驱动程序的具体情况下,导出者可以使用 gem 已提供的 shmem 文件(并设置 vm_pgoff = 0)。然后导出者可以通过取消映射与其文件关联的 struct address_space 的相应范围来清除 pte。
   * 此回调是可选的。
   * 返回值:
   * 成功返回 0,失败返回负错误码。
   */
	int (*mmap)(struct dma_buf *, struct vm_area_struct *vma);

	int (*vmap)(struct dma_buf *dmabuf, struct iosys_map *map);
	void (*vunmap)(struct dma_buf *dmabuf, struct iosys_map *map);
};

   - 定义了对 `struct dma_buf` 可执行的操作。
   - 包含以下成员:
     - `cache_sgt_mapping`:是否缓存 scatterlist 映射。
     - `attach` 和 `detach`:管理缓冲区附件的回调函数。
     - `pin` 和 `unpin`:固定和释放缓冲区的回调函数。
     - `map_dma_buf` 和 `unmap_dma_buf`:映射和取消映射 DMA 缓冲区的回调函数。
     - `release`:释放 DMA 缓冲区的回调函数。
     - `begin_cpu_access` 和 `end_cpu_access`:开始和结束 CPU 访问的回调函数。
     - `mmap`、`vmap` 和 `vunmap`:内存映射和虚拟映射的回调函数。

2. **`struct dma_buf`**

/**
 * struct dma_buf - 共享缓冲区对象
 * 此结构表示通过调用 dma_buf_export() 创建的共享缓冲区。用户空间表示是一个普通的文件描述符,可以通过调用 dma_buf_fd() 创建。
 * 共享的 dma 缓冲区通过 dma_buf_put() 和 get_dma_buf() 进行引用计数。
 * 设备 DMA 访问由单独的 &struct dma_buf_attachment 处理。
 */
struct dma_buf {
  /**
   * 缓冲区的大小;在缓冲区生命周期内不变。
   */
	size_t size;

  /**
   * 用于跨进程共享缓冲区的文件指针,并用于引用计数。参见 dma_buf_get() 和 dma_buf_put()。
   */
	struct file *file;

  /**
   * 表示所有已附加设备的 dma_buf_attachment 列表,受 &dma_resv 锁 @resv 保护。
   */
	struct list_head attachments;

  /** 与此缓冲区对象关联的 dma_buf_ops。*/
	const struct dma_buf_ops *ops;

  /**
   * 用于引用计数由 dma_buf_vmap() 返回的 vmaps。受 @lock 保护。
   */
	unsigned vmapping_counter;

  /**
   * 如果 @vmapping_counter > 0,则为当前的 vmap 指针。受 @lock 保护。
   */
	struct iosys_map vmap_ptr;

  /**
   * 导出者的名称;对调试有用。不能为空。
   */
	const char *exp_name;

  /**
   * 用户空间提供的名称。默认值为 NULL。如果非空,则长度不能超过 DMA_BUF_NAME_LEN,包括 NIL 字符。对会计和调试有用。读/写访问受 @name_lock 保护。
   * 参见 IOCTLs DMA_BUF_SET_NAME 或 DMA_BUF_SET_NAME_A/B。
   */
	const char *name;

	/** 自旋锁,用于保护 name 字段的访问 */
	spinlock_t name_lock;

  /**
   * 指向导出模块的指针;用于内核模块的引用计数。
   */
	struct module *owner;

#if IS_ENABLED(CONFIG_DEBUG_FS)
	/** 用于 dma_buf 的会计和调试的节点 */
	struct list_head list_node;
#endif

	/** 供导出者为此缓冲区对象存储特定的私有数据 */
	void *priv;

  /**
   * 链接到此 dma-buf 的预留对象。
   * 隐式同步规则:
   * 支持隐式缓冲区访问同步的驱动程序(例如在 `Implicit Fence Poll Support`_ 中暴露的)必须遵循以下规则:
   * - 驱动程序必须通过 dma_resv_add_fence() 添加读围栏,并使用 DMA_RESV_USAGE_READ 标志,对于用户空间 API 认为是读访问的任何内容。这高度依赖于 API 和窗口系统。
   * - 同样,驱动程序必须通过 dma_resv_add_fence() 添加写围栏,并使用 DMA_RESV_USAGE_WRITE 标志,对于用户空间 API 认为是写访问的任何内容。
   * - 驱动程序也可以总是添加写围栏,因为这只会导致不必要的同步,但不会引起正确性问题。
   * - 某些驱动程序仅暴露同步的用户空间 API,没有跨驱动程序的流水线。这些驱动程序不会为其访问设置任何围栏。例如 v4l。
   * - 驱动程序应在检索围栏作为隐式同步的依赖项时使用 dma_resv_usage_rw()。
   * 动态导入者规则:
   * 动态导入者(参见 dma_buf_attachment_is_dynamic())在设置围栏时有额外的约束:
   * - 动态导入者必须遵守写围栏,并在允许通过设备访问缓冲区的底层存储之前等待它们完成。
   * - 动态导入者应为任何无法立即从其 &dma_buf_attach_ops.move_notify 回调中禁用的访问设置围栏。
   * 重要:
   * 所有驱动程序和与内存管理相关的函数必须遵守 struct dma_resv 规则,特别是更新和遵守围栏的规则。参见 enum dma_resv_usage 获取进一步描述。
   */
	struct dma_resv *resv;

	/** @poll: for userspace poll support */
	wait_queue_head_t poll;

	/** @cb_in: for userspace poll support */
	/** @cb_out: for userspace poll support */
	struct dma_buf_poll_cb_t {
		struct dma_fence_cb cb;
		wait_queue_head_t *poll;

		__poll_t active;
	} cb_in, cb_out;
#ifdef CONFIG_DMABUF_SYSFS_STATS
	/**
	 * @sysfs_entry:
	 *
	 * For exposing information about this buffer in sysfs. See also
	 * `DMA-BUF statistics`_ for the uapi this enables.
	 */
	struct dma_buf_sysfs_entry {
		struct kobject kobj;
		struct dma_buf *dmabuf;
	} *sysfs_entry;
#endif
};

   - 表示一个共享缓冲区对象。
   - 包含以下成员:
     - `size`:缓冲区的大小,生命周期内不变。
     - `file`:用于跨进程共享和引用计数的文件指针。
     - `attachments`:所有附加设备的列表。
     - `ops`:与该缓冲区对象关联的 `dma_buf_ops`。
     - `vmapping_counter` 和 `vmap_ptr`:用于内部 vmap 引用计数和当前 vmap 指针。
     - `exp_name` 和 `name`:导出者名称和用户空间提供的名称。
     - `owner`:导出者模块指针。
     - `resv`:与此 DMA 缓冲区链接的保留对象。
     - `poll` 和 `cb_in`、`cb_out`:用于用户空间轮询支持。
     - `sysfs_entry`:用于在 sysfs 中暴露此缓冲区的信息。

3. **`struct dma_buf_attach_ops`**
   - 定义了导入者为附件实现的操作。
   - 包含以下成员:
     - `allow_peer2peer`:导入者是否可以处理没有页面的对等资源。
     - `move_notify`:通知 DMA 缓冲区正在移动的回调函数。

4. **`struct dma_buf_attachment`**
   - 保存设备-缓冲区附件的数据。此结构保存 dma_buf 缓冲区与其用户设备之间的连接信息。列表中包含一个每设备附加到缓冲区的 attachment 结构。连接通过调用 dma_buf_attach() 创建,并通过调用 dma_buf_detach() 再次释放。DMA 映射本身(用于发起传输)通过调用 dma_buf_map_attachment() 创建,并通过调用 dma_buf_unmap_attachment() 再次释放。
   - 包含以下成员:
     - `dmabuf`:此附件的缓冲区。
     - `dev`:附加到缓冲区的设备。
     - `node`:DMA 缓冲区附件的列表。
     - `sgt` 和 `dir`:缓存的映射和方向。
     - `peer2peer`:导入者是否可以处理没有页面的对等资源。
     - `importer_ops` 和 `importer_priv`:导入者的操作和私有数据。
     - `priv`:导出者的私有附件数据。

5. **`struct dma_buf_export_info`**
   - 保存导出 DMA 缓冲区所需的信息。此结构持有导出缓冲区所需的信息。仅与 dma_buf_export() 一起使用。
   - 包含以下成员:
     - `exp_name`:导出者名称。
     - `owner`:导出者模块指针。
     - `ops`:与新缓冲区关联的 `dma_buf_ops`。
     - `size`:缓冲区的大小。
     - `flags`:文件模式标志。
     - `resv`:保留对象。
     - `priv`:导出者的私有数据。

6. **宏定义**
   - `DEFINE_DMA_BUF_EXPORT_INFO`:定义并初始化 `dma_buf_export_info` 结构体。

7. **内联函数**
   - `get_dma_buf`:增加 DMA 缓冲区的引用计数。
   - `dma_buf_is_dynamic` 和 `dma_buf_attachment_is_dynamic`:检查 DMA 缓冲区和附件是否使用动态映射。

二、 主要函数

   - `dma_buf_attach` 和 `dma_buf_detach`:附加和分离 DMA 缓冲区。
   - `dma_buf_pin` 和 `dma_buf_unpin`:固定和释放 DMA 缓冲区附件。
   - `dma_buf_export`:导出 DMA 缓冲区。
   - `dma_buf_fd` 和 `dma_buf_get`:获取 DMA 缓冲区的文件描述符和从文件描述符获取 DMA 缓冲区。
   - `dma_buf_map_attachment` 和 `dma_buf_unmap_attachment`:映射和取消映射 DMA 缓冲区附件。
   - `dma_buf_move_notify`:通知 DMA 缓冲区正在移动。
   - `dma_buf_begin_cpu_access` 和 `dma_buf_end_cpu_access`:开始和结束 CPU 访问。
   - `dma_buf_mmap`、`dma_buf_vmap` 和 `dma_buf_vunmap`:内存映射和虚拟映射函数。

要实现一个 dma-buf exporter驱动,需要关注3个重点:

  • dma_buf_ops
  • DEFINE_DMA_BUF_EXPORT_INFO
  • dma_buf_export()

注意: 其中 dma_buf_ops 的回调接口中,如下接口又是必须要实现的,缺少任何一个都将导致 dma_buf_export() 函数调用失败!

  • map_dma_buf
  • unmap_dma_buf
  • mmap
  • release
  • map(4.19后废除)
  • unmap(4.19后废除)
  • map_atomic(4.12后废除)
  • unmap_atomic(4.12后废除)

<think>嗯,用户这次问的是关于`siwifi_rx_data_skb`函数中的一部分逻辑,特别是处理A-MSDU的部分。我需要先仔细看看用户提供的代码片段,理解其结构和流程。用户之前已经询问过关于`netif_receive_skb`处理后的skb状态,现在转向了另一个具体函数的分析,说明他们可能在进行内核网络驱动开发或相关调试,需要理解接收数据包的处理流程。 首先,我需要确定这段代码的功能。函数名`siwifi_rx_data_skb`提示这是特定于某个WiFi驱动(可能是siwifi)的接收数据处理函数。参数包括硬件结构体、虚拟接口、skb和接收头。函数内部处理A-MSDU聚合帧,可能涉及拆分子帧并转发到协议栈。 用户提供的代码中,处理A-MSDU的逻辑部分包括初始化队列,检查amsdu标志,处理第一个skb,然后循环处理后续的子帧。这里的关键点在于理解A-MSDU的处理流程,包括如何分解多个子帧,以及每个子帧的处理步骤。 我需要回忆A-MSDU的概念。A-MSDU是802.11n中引入的帧聚合技术,允许将多个MSDU(MAC服务数据单元)聚合到一个大的帧中传输,提高效率。接收端需要拆解这些子帧并分别处理。因此,函数中的逻辑应该是将聚合的帧拆分为多个独立的skb,并逐个处理。 接下来,代码中首先检查amsdu标志,如果是的话,开始处理。第一个skb被放入队列,然后循环处理每个hostid对应的子帧。每个子帧对应的ipc_buf通过`siwifi_ipc_rxbuf_from_hostid`获取,然后提取rx_skb。检查每个子帧的长度是否有效,调整skb的tailroom,设置优先级和设备,加入队列,并释放ipc_buf。最后,统计处理的子帧数量。 用户可能想知道这部分代码的具体作用,以及如何处理A-MSDU。需要解释代码的每一步,特别是skb的操作,比如`skb_put`的作用,队列的使用,以及资源管理(如释放ipc_buf)。此外,需要指出可能的错误处理,比如无效hostid或长度检查失败时设置forward为false的影响。 另外,用户可能关心这段代码如何确保数据正确传递到协议栈,或者如何处理异常情况。例如,如果某个子帧的长度无效,forward会被设为false,这可能影响后续是否转发整个队列。需要解释这些条件判断的作用和整体流程。 还要注意代码中的`res`变量记录处理的子帧数量,这可能用于返回给调用者,表示成功处理的帧数。同时,`siwifi_ipc_buf_e2a_release`可能是在释放IPC缓冲区,确保资源不泄漏。 总结来说,用户需要了解这段代码如何分解A-MSDU聚合帧,处理每个子帧,管理相关资源,以及可能的错误处理。需要逐步解释代码逻辑,并结合网络驱动开发的相关知识,说明其设计意图和实现细节。</think>--- ### 功能概述 这是 WiFi 驱动中处理 **A-MSDU 聚合帧** 的核心逻辑,主要完成以下关键操作: 1. **拆解 A-MSDU 聚合包**:将硬件接收的聚合数据拆分为多个子帧 2. **构建转发队列**:将拆解后的子帧按顺序加入链表(`sk_buff_head list`) 3. **资源管理**:释放硬件缓冲区(`siwifi_ipc_buf_e2a_release`) --- ### 代码逻辑分步解析 #### 1. 基础准备 ```c skb->dev = siwifi_vif->ndev; // 绑定网络设备 __skb_queue_head_init(&list); // 初始化链表头 ``` - **作用**:为后续子帧处理建立基础设施绑定 --- #### 2. A-MSDU 主帧处理 ```c if (amsdu) { // 检查第一个子帧长度有效性 if (!rxhdr->amsdu_len[0] || (rxhdr->amsdu_len[0] > skb_tailroom(skb))) forward = false; // 长度非法则标记不转发 else skb_put(skb, rxhdr->amsdu_len[0]); // 扩展 skb 数据区 __skb_queue_tail(&list, skb); // 主帧加入队列 ``` - **关键操作**: - `skb_put()`:将 skb 的 `data_len` 扩展指定字节(相当于确认有效载荷) - 长度检查防止缓冲区溢出 --- #### 3. 循环处理子帧 ```c while ((count < ARRAY_SIZE(rxhdr->amsdu_hostids)) && (hostid = rxhdr->amsdu_hostids[count++])) { // 通过 hostid 获取硬件缓冲区 struct siwifi_ipc_buf *ipc_buf = siwifi_ipc_rxbuf_from_hostid(siwifi_hw, hostid); if (!ipc_buf) { wiphy_err(...); // 错误日志 continue; } rx_skb = ipc_buf->addr; // 获取子帧 skb // 检查子帧长度(注意 count 偏移 +1) if (!rxhdr->amsdu_len[count] || (rxhdr->amsdu_len[count] > skb_tailroom(rx_skb))) forward = false; else skb_put(rx_skb, rxhdr->amsdu_len[count]); rx_skb->priority = skb->priority; // 继承优先级 rx_skb->dev = skb->dev; // 绑定相同网络设备 __skb_queue_tail(&list, rx_skb); // 子帧入队 siwifi_ipc_buf_e2a_release(siwifi_hw, ipc_buf); // 释放硬件缓冲区 res++; // 记录处理帧数 } ``` - **核心机制**: - **hostid 循环**:遍历预定义的 hostid 数组获取子帧 - **双缓冲管理**: - `ipc_buf`:硬件层 DMA 缓冲区 - `rx_skb`:软件层 sk_buff - **长度对齐**:`amsdu_len[count]` 的索引比 `amsdu_hostids` 偏移 +1 --- ### 关键数据结构关系 ```text 硬件接收流: +-----------------+ +-----------------+ | DMA 缓冲区 |<----->| ipc_buf (hostid)| +-----------------+ +--------+--------+ | v 软件处理流: +--------+--------+ | rx_skb (sk_buff)| +--------+--------+ | v +-------+-------+ | skb队列 (list) | +---------------+ ``` --- ### 异常处理逻辑 | 场景 | 处理方式 | 影响 | |------|----------|------| | 无效 hostid | 记录错误日志,跳过子帧 | 丢失单个子帧 | | 子帧长度非法 | 设置 `forward = false` | 可能丢弃整个聚合包 | | 缓冲区不足 | 同长度非法处理 | 确保内存安全 | --- ### 设计特点分析 1. **零拷贝优化**: - 直接复用 DMA 缓冲区地址(`ipc_buf->addr`) - 避免数据在内存中的二次拷贝 2. **原子性保证**: - 所有子帧处理完成前不提交队列 - `forward` 标志统一控制转发行为 3. **优先级继承**: - 所有子帧继承主帧的 QoS 优先级(`rx_skb->priority = skb->priority`) --- ### 后续处理推测 在函数返回后,预计会有类似如下操作: ```c if (forward) { skb_queue_walk(&list, skb) { netif_receive_skb(skb); // 提交协议栈 } } else { __skb_queue_purge(&list); // 丢弃所有帧 } ``` - **注意**:实际实现需结合具体驱动代码确认 --- ### 总结 这段代码实现了 WiFi 驱动中 **A-MSDU 聚合帧的拆解与预处理**,通过硬件缓冲区直接映射、多子帧队列化管理和严格的长度校验,在保证性能的同时实现了内存安全。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值