页面片段

页面片段是位于 0 或更高阶复合页面内的任意长度、任意偏移的内存区域。该页面内的多个片段在页面的引用计数器中被单独引用计数。

page_frag 函数,page_frag_alloc 和 page_frag_free,为页面片段提供了一个简单的分配框架。这被网络堆栈和网络设备驱动程序使用,以提供一个后备内存区域,用作 sk_buff->head,或在 skb_shared_info 的 “frags” 部分中使用。

为了使用页面片段 API,需要一个后备页面片段缓存。这为片段分配提供了一个中心点,并跟踪允许多次调用来使用缓存的页面。这样做的好处是可以避免多次调用 get_page,这在分配时可能会很昂贵。然而,由于这种缓存的性质,要求对缓存的任何调用都受到每个 CPU 的限制,或者受到每个 CPU 的限制,并在执行片段分配时强制禁用中断。

网络堆栈为每个 CPU 使用两个单独的缓存来处理片段分配。netdev_alloc_cache 被使用 netdev_alloc_frag 和 __netdev_alloc_skb 调用的调用者使用。napi_alloc_cache 被 __napi_alloc_frag 和 napi_alloc_skb 调用的调用者使用。这两个调用的主要区别在于它们可能被调用的上下文。“netdev” 前缀的函数可以在任何上下文中使用,因为这些函数将禁用中断,而 “napi” 前缀的函数只能在软中断上下文中使用。

许多网络设备驱动程序使用类似的方法来分配页面片段,但页面片段缓存在环或描述符级别。为了启用这些情况,有必要提供一种通用的方式来拆除页面缓存。因此,实现了 __page_frag_cache_drain。它允许通过单个调用从单个页面释放多个引用。这样做的好处是,它允许清理添加到页面中的多个引用,以避免每次分配都调用 get_page。

Alexander Duyck,2016 年 11 月 29 日。