首页 > 其他分享 >pin_user_pages()及相关调用 【ChatGPT】

pin_user_pages()及相关调用 【ChatGPT】

时间:2023-12-09 17:13:48浏览次数:57  
标签:PIN pin user FOLL ChatGPT pages 页面

pin_user_pages()及相关调用

概述

本文档描述以下函数:

  • pin_user_pages()
  • pin_user_pages_fast()
  • pin_user_pages_remote()

FOLL_PIN的基本描述

FOLL_PIN和FOLL_LONGTERM是可以传递给get_user_pages*()("gup")函数族的标志。FOLL_PIN与FOLL_LONGTERM有重要的交互和相互依赖关系,因此这两者都在此处进行讨论。

FOLL_PIN是gup内部使用的标志,这意味着它不应该出现在gup调用点。这允许相关的包装函数(如pin_user_pages*()等)设置正确的这些标志的组合,并检查问题。

另一方面,FOLL_LONGTERM允许在gup调用点设置。这是为了避免创建大量的包装函数来覆盖所有get()、pin()、FOLL_LONGTERM等组合。此外,pin_user_pages() API明显不同于get_user_pages() API,因此这是一个自然的分界线,也是一个很好的分隔点。换句话说,对于DMA固定页面,请使用pin_user_pages(),对于其他情况,请使用get_user_pages()。后面将进一步描述五种情况,以进一步澄清这个概念。

对于给定的gup调用,FOLL_PIN和FOLL_GET是互斥的。然而,多个线程和调用点可以通过FOLL_PIN和FOLL_GET来固定相同的结构页面。只是调用点需要选择其中一个,而不是结构页面。

FOLL_PIN的实现几乎与FOLL_GET相同,只是FOLL_PIN使用了不同的引用计数技术。

FOLL_PIN是FOLL_LONGTERM的先决条件。换句话说,FOLL_LONGTERM是FOLL_PIN的一个更具体、更严格的情况。

每个包装函数设置的标志

对于这些pin_user_pages()函数,FOLL_PIN总是与调用者提供的gup标志进行OR运算。调用者需要传递一个非空的struct pages数组,然后函数通过将每个页面的引用计数增加一个特殊值(GUP_PIN_COUNTING_BIAS)来固定页面。

对于大的foliage,不使用GUP_PIN_COUNTING_BIAS方案。相反,使用struct folio中可用的额外空间直接存储固定计数。

这种大foliage的方法避免了下面讨论的计数上限问题。这些限制将因为巨大页面而严重恶化,因为每个尾页都会向头页添加一个引用计数。实际测试表明,在某些巨大页面压力测试中,如果没有单独的固定计数字段,会出现引用计数溢出问题。

这也意味着巨大页面和大foliage不会受到下面提到的假阳性问题的影响。

函数

  • pin_user_pages:此函数内部始终设置FOLL_PIN。
  • pin_user_pages_fast:此函数内部始终设置FOLL_PIN。
  • pin_user_pages_remote:此函数内部始终设置FOLL_PIN。

对于这些get_user_pages()函数,FOLL_GET甚至可能不会被指定。行为比上面稍微复杂一些。如果没有指定FOLL_GET,但调用者传递了一个非空的struct pages数组,则函数会为您设置FOLL_GET,并通过将每个页面的引用计数增加1来固定页面。

函数

  • get_user_pages:此函数有时会在内部设置FOLL_GET。
  • get_user_pages_fast:此函数有时会在内部设置FOLL_GET。
  • get_user_pages_remote:此函数有时会在内部设置FOLL_GET。

跟踪DMA固定页面

跟踪DMA固定页面的一些关键设计约束和解决方案:

  • 需要每个struct page的实际引用计数,因为多个进程可能固定和取消固定页面。
  • 假阳性(报告页面被DMA固定,而实际上并没有)是可以接受的,但假阴性是不可以接受的。
  • struct page可能不会增加尺寸,而且所有字段已经被使用。
  • 鉴于上述情况,我们可以通过使用page->_refcount字段的上位比特来重载该字段,以实现一种中等大值(GUP_PIN_COUNTING_BIAS,最初选择为1024:10位)的DMA固定计数。这提供了模糊行为:如果一个页面被调用了1024次get_page(),那么它将看起来有一个单独的DMA固定计数。这也导致了一些限制:只有31-10==21位可用于每次增加10位的计数器。
  • 由于这种限制,在使用FOLL_PIN时,对零页面应用特殊处理。我们只是假装固定一个零页面 - 我们根本不改变它的引用计数或固定计数(它是永久的,所以没有必要)。取消固定函数也不会对零页面执行任何操作。这对调用者是透明的。
  • 调用者必须明确请求“跟踪页面的DMA固定”。换句话说,仅仅调用get_user_pages()是不够的;必须使用一组新的函数,如pin_user_page()等。

FOLL_PIN、FOLL_GET、FOLL_LONGTERM:何时使用哪些标志

感谢Jan Kara、Vlastimil Babka和其他一些-mm人员,对这些类别进行了描述:

CASE 1:直接IO(DIO)

存在对用作DIO缓冲区的页面的GUP引用。这些缓冲区只需要相对较短的时间(因此它们不是“长期”)。不提供与page_mkclean()或munmap()的特殊同步。因此,在调用点设置的标志是:

  • FOLL_PIN

...但是,调用点应该使用设置FOLL_PIN的pin_user_pages*()例程,而不是直接设置FOLL_PIN。

CASE 2:RDMA

存在对用作DMA缓冲区的页面的GUP引用。这些缓冲区需要长时间(“长期”)。不提供与page_mkclean()或munmap()的特殊同步。因此,在调用点设置的标志是:

  • FOLL_PIN | FOLL_LONGTERM

注意:某些页面,如DAX页面,不能使用长期固定。这是因为DAX页面没有单独的页面缓存,因此“固定”意味着锁定文件系统块,目前还不支持这种方式。

CASE 3:MMU通知器注册,带或不带页面错误硬件

设备驱动程序可以通过get_user_pages*()固定页面,并为内存范围注册mmu通知器回调。然后,在接收到通知器“使范围无效”回调时,停止设备使用该范围,并取消固定页面。可能还有其他可能的方案,例如显式地与待处理IO同步,以实现大致相同的功能。

或者,如果硬件支持可重放的页面错误,那么设备驱动程序可以完全避免固定(这是理想的),方法是:注册mmu通知器回调,如上所述,但在回调中,而不是在回调中停止设备并取消固定,只需从设备的页表中删除该范围。

无论哪种方式,只要驱动程序在mmu通知器回调时取消固定页面,就可以与文件系统和mm(page_mkclean()、munmap()等)进行适当的同步,因此不需要设置任何标志。

CASE 4:仅用于struct page操作的固定

如果只影响struct page数据(而不是页面实际跟踪的内存内容),则普通的GUP调用就足够了,不需要设置任何标志。

CASE 5:固定以便写入页面内的数据

即使不涉及DMA或直接IO,只是简单的“固定、写入页面数据、取消固定”也可能会导致问题。Case 5可以被认为是Case 1的超集,再加上Case 2,再加上任何调用这种模式的模式。换句话说,如果代码既不是Case 1也不是Case 2,可能仍然需要FOLL_PIN,用于这种模式:

正确的(使用FOLL_PIN调用):

  • pin_user_pages() 写入页面内的数据 unpin_user_pages()

不正确的(使用FOLL_GET调用):

  • get_user_pages() 写入页面内的数据 put_page()

page_maybe_dma_pinned(): 固定页面的整个目的

将页面标记为“DMA-pinned”或“gup-pinned”的整个目的是为了能够查询“这个页面是否被DMA固定?”这允许诸如page_mkclean()(以及文件系统回写代码)这样的代码在由于这些固定而无法取消映射时做出知情决策。

在这些情况下该做什么是多年来一系列讨论和辩论的主题(请参见本文档末尾的参考资料)。这是这里的一个待办事项:一旦解决了这个问题,填写详细信息。与此同时,可以肯定地说,拥有这个:

static inline bool page_maybe_dma_pinned(struct page *page)

…是解决长期存在的gup+DMA问题的先决条件。

另一种思考FOLL_GET、FOLL_PIN和FOLL_LONGTERM的方式

对这些标志的另一种思考方式是作为限制的逐步加强:FOLL_GET用于struct page操作,而不影响struct page所引用的数据。FOLL_PIN是对FOLL_GET的替代,用于对将要访问其数据的页面进行短期固定。因此,FOLL_PIN是一种“更严格”的固定形式。最后,FOLL_LONGTERM是一个更为严格的情况,其具有FOLL_PIN作为先决条件:这是用于长期固定并将访问其数据的页面。

单元测试

这个文件:

tools/testing/selftests/mm/gup_test.c

具有以下新调用来测试新的pin*()包装函数:

PIN_FAST_BENCHMARK (./gup_test -a)

PIN_BASIC_TEST (./gup_test -b)

您可以通过两个新的/proc/vmstat条目来监视自系统启动以来已获取和释放的总DMA固定页面数量:

/proc/vmstat/nr_foll_pin_acquired
/proc/vmstat/nr_foll_pin_released

在正常情况下,这两个值将相等,除非存在任何长期的[R]DMA固定,或在固定/取消固定转换期间。

  • nr_foll_pin_acquired: 这是自系统上电以来已获取的逻辑固定数量。对于大页,对于大页内的每个页面(头页面和每个尾页面),头页面将被固定一次。这遵循了get_user_pages()对大页使用的相同行为:当get_user_pages()应用于大页时,头页面的引用计数将对每个大页的尾部或头部页面增加一次。

  • nr_foll_pin_released: 这是自系统上电以来已释放的逻辑固定数量。请注意,即使原始固定是应用于大页,页面也将以PAGE_SIZE粒度被释放(unpinned)。由于上述“nr_foll_pin_acquired”中描述的固定计数行为,会进行平衡计数,因此在执行以下操作后:

pin_user_pages(huge_page);
for (each page in huge_page)
    unpin_user_page(page);

预期如下:

nr_foll_pin_released == nr_foll_pin_acquired

(…除非由于存在长期RDMA固定而已经失衡。)

其他诊断

dump_page()已稍作增强,以处理这些新的计数字段,并更好地报告大folio。具体来说,对于大folio,将报告确切的固定计数。

参考资料

约翰·哈伯德,2019年10月

标签:PIN,pin,user,FOLL,ChatGPT,pages,页面
From: https://www.cnblogs.com/pengdonglin137/p/17891192.html

相关文章

  • 内存热插拔 【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/core-api/memory-hotplug.html内存热插拔内存热插拔事件通知热插拔事件被发送到一个通知队列中。在include/linux/memory.h中定义了六种通知类型:MEM_GOING_ONLINE在新内存可用之前生成,以便准备子系统处理内存。页分配器仍然无法......
  • 动态DMA映射指南 【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/core-api/dma-api-howto.html动态DMA映射指南作者[email protected]@[email protected]本指南旨在向设备驱动程序编写者介绍如何使用DMAAPI,并提供伪代码示例。有关A......
  • DMA属性 【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/core-api/dma-attributes.htmlDMA属性本文档描述了在linux/dma-mapping.h中定义的DMA属性的语义。DMA_ATTR_WEAK_ORDERINGDMA_ATTR_WEAK_ORDERING指定了对映射的读写可能是弱有序的,也就是说读和写可能会相互交错。由于平台可以选择性......
  • DMA与ISA和LPC设备 【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/core-api/dma-isa-lpc.htmlDMA与ISA和LPC设备作者[email protected]本文档描述了如何使用旧的ISADMA控制器进行DMA传输。尽管ISA在今天已经基本淘汰,但LPC总线使用相同的DMA系统,因此它将在相当长的时间内存在。头文件和......
  • genalloc/genpool 子系统 【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/core-api/genalloc.htmlgenalloc/genpool子系统内核中有许多内存分配子系统,每个子系统都针对特定的需求。然而,有时内核开发人员需要为特定范围的特定用途内存实现新的分配器;通常这些内存位于设备的某个位置。该设备的驱动程序的作者当......
  • 引导时内存管理 【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/core-api/boot-time-mm.html#c.memblock_flags"Boottimememorymanagement"(引导时间内存管理)早期系统初始化不能简单地使用“正常”的内存管理,因为它还没有设置。但是仍然需要为各种数据结构分配内存,例如物理页面分配器。一种名为mem......
  • 动态DMA映射使用通用设备 【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/core-api/dma-api.html动态DMA映射使用通用设备作者[email protected]本文档描述了DMAAPI。要了解API的更详细介绍(以及实际示例),请参阅动态DMA映射指南。该API分为两部分。第一部分描述了......
  • 内存管理文档 【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/mm/index.html内存管理指南这是关于Linux内存管理子系统的指南。如果您只是想了解如何分配内存,请参阅内存分配指南。有关控制和调整指南,请参阅管理员指南。物理内存页表进程地址引导内存页分配虚拟连续内存分配Slab分配高内存......
  • 内存分配指南 【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/core-api/memory-allocation.html内存分配指南Linux提供了各种用于内存分配的API。您可以使用kmalloc或kmem_cache_alloc系列来分配小块内存,使用vmalloc及其衍生物来分配大的虚拟连续区域,或者直接使用alloc_pages从页面分配器请求页面。......
  • 核心API文档 【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/core-api/index.html#memory-management核心API文档这是核心内核API手册的开头部分。非常感谢您进行文档的转换和编写!核心实用程序本节包含一般和“核心核心”文档。首先是一大堆来自docbook时代的kerneldoc信息,实际上应该在有人有精......