英语

内存管理 API

用户空间内存访问

get_user

get_user (x, ptr)

从用户空间获取一个简单的变量。

参数

x

用于存储结果的变量。

ptr

源地址,在用户空间中。

上下文

仅限用户上下文。如果启用了页面错误,此函数可能会休眠。

描述

此宏将单个简单变量从用户空间复制到内核空间。它支持诸如 char 和 int 之类的简单类型,但不支持诸如结构或数组之类的较大数据类型。

ptr 必须具有指向简单变量的指针类型,并且取消引用 ptr 的结果必须可以赋值给 x 而无需强制转换。

返回

成功时返回零,出错时返回 -EFAULT。出错时,变量 x 设置为零。

__get_user

__get_user (x, ptr)

从用户空间获取一个简单的变量,检查较少。

参数

x

用于存储结果的变量。

ptr

源地址,在用户空间中。

上下文

仅限用户上下文。如果启用了页面错误,此函数可能会休眠。

描述

此宏将单个简单变量从用户空间复制到内核空间。它支持诸如 char 和 int 之类的简单类型,但不支持诸如结构或数组之类的较大数据类型。

ptr 必须具有指向简单变量的指针类型,并且取消引用 ptr 的结果必须可以赋值给 x 而无需强制转换。

调用者必须在调用此函数之前使用 access_ok() 检查指针。

返回

成功时返回零,出错时返回 -EFAULT。出错时,变量 x 设置为零。

put_user

put_user (x, ptr)

将一个简单值写入用户空间。

参数

x

要复制到用户空间的值。

ptr

目标地址,在用户空间中。

上下文

仅限用户上下文。如果启用了页面错误,此函数可能会休眠。

描述

此宏将单个简单值从内核空间复制到用户空间。它支持诸如 char 和 int 之类的简单类型,但不支持诸如结构或数组之类的较大数据类型。

ptr 必须具有指向简单变量的指针类型,并且 x 必须可以赋值给取消引用 ptr 的结果。

返回

成功时返回零,出错时返回 -EFAULT。

__put_user

__put_user (x, ptr)

将一个简单值写入用户空间,检查较少。

参数

x

要复制到用户空间的值。

ptr

目标地址,在用户空间中。

上下文

仅限用户上下文。如果启用了页面错误,此函数可能会休眠。

描述

此宏将单个简单值从内核空间复制到用户空间。它支持诸如 char 和 int 之类的简单类型,但不支持诸如结构或数组之类的较大数据类型。

ptr 必须具有指向简单变量的指针类型,并且 x 必须可以赋值给取消引用 ptr 的结果。

调用者必须在调用此函数之前使用 access_ok() 检查指针。

返回

成功时返回零,出错时返回 -EFAULT。

unsigned long clear_user(void __user *to, unsigned long n)

将用户空间中的内存块清零。

参数

void __user *to

目标地址,在用户空间中。

unsigned long n

要清零的字节数。

描述

将用户空间中的内存块清零。

返回

无法清除的字节数。成功时,此值为零。

unsigned long __clear_user(void __user *to, unsigned long n)

将用户空间中的内存块清零,检查较少。

参数

void __user *to

目标地址,在用户空间中。

unsigned long n

要清零的字节数。

描述

将用户空间中的内存块清零。调用者必须在调用此函数之前使用 access_ok() 检查指定的块。

返回

无法清除的字节数。成功时,此值为零。

int get_user_pages_fast(unsigned long start, int nr_pages, unsigned int gup_flags, struct page **pages)

在内存中固定用户页面

参数

unsigned long start

起始用户地址

int nr_pages

从起始位置要固定的页面数

unsigned int gup_flags

修改固定行为的标志

struct page **pages

接收指向已固定页面的指针的数组。长度应至少为 nr_pages。

描述

尝试在不获取 mm->mmap_lock 的情况下固定内存中的用户页面。如果不成功,它将回退到获取锁并调用 get_user_pages()。

返回固定的页面数。这可能少于请求的数量。如果 nr_pages 为 0 或负数,则返回 0。如果没有固定任何页面,则返回 -errno。

内存分配控制

页面移动性和放置提示

这些标志提供了有关页面移动性的提示。具有相似移动性的页面被放置在相同的页面块内,以最大程度地减少由于外部碎片导致的问题。

__GFP_MOVABLE(也是一个区域修饰符)表示该页面可以在内存压缩期间通过页面迁移移动或可以被回收。

__GFP_RECLAIMABLE 用于指定 SLAB_RECLAIM_ACCOUNT 的 slab 分配,其页面可以通过收缩器释放。

__GFP_WRITE 表示调用者打算弄脏页面。在可能的情况下,这些页面将在本地区域之间分散,以避免所有脏页面都在一个区域中(公平区域分配策略)。

__GFP_HARDWALL 强制执行 cpuset 内存分配策略。

__GFP_THISNODE 强制从请求的节点满足分配,而不进行回退或强制执行放置策略。

__GFP_ACCOUNT 使分配被计入 kmemcg。

__GFP_NO_OBJ_EXT 使 slab 分配没有对象扩展。

水位修饰符——控制对紧急储备的访问

__GFP_HIGH 表示调用者具有高优先级,并且在系统取得进展之前必须授予请求。例如,创建 IO 上下文以清理页面以及来自原子上下文的请求。

__GFP_MEMALLOC 允许访问所有内存。仅当调用者保证分配将允许在很短的时间内释放更多内存时,才应使用此选项,例如,进程退出或交换。用户要么应该是 MM,要么与 VM 紧密协调(例如,通过 NFS 进行交换)。此标志的用户必须非常小心,不要完全耗尽储备,并实施一种节流机制,该机制基于已释放的内存量来控制储备的消耗。在使用此标志之前,应始终考虑使用预分配的池(例如,mempool)。

__GFP_NOMEMALLOC 用于明确禁止访问紧急储备。如果同时设置了 __GFP_MEMALLOC 标志,则此选项优先于它。

回收修饰符

请注意,以下所有标志仅适用于可睡眠的分配(例如,GFP_NOWAITGFP_ATOMIC 将忽略它们)。

__GFP_IO 可以启动物理 IO。

__GFP_FS 可以向下调用到低级 FS。清除该标志可避免分配器递归到可能已持有锁的文件系统中。

__GFP_DIRECT_RECLAIM 表示调用者可能会进入直接回收。可以清除此标志以避免在有回退选项时出现不必要的延迟。

__GFP_KSWAPD_RECLAIM 表示调用者希望在达到低水位时唤醒 kswapd,并让它回收页面直到达到高水位。当回退选项可用且回收可能中断系统时,调用者可能希望清除此标志。典型的示例是 THP 分配,其中回退很便宜,但回收/压缩可能会导致间接停顿。

__GFP_RECLAIM 是允许/禁止直接和 kswapd 回收的简写。

默认的分配器行为取决于请求的大小。我们有一个所谓的“代价高昂的分配”的概念(其阶数 > PAGE_ALLOC_COSTLY_ORDER)。非“代价高昂的”分配对于成功至关重要,因此默认情况下它们是隐式非失败的(但有一些例外,例如 OOM 受害者可能会失败,因此调用者仍然必须检查失败情况),而代价高昂的请求会尽量不造成干扰,即使不调用 OOM killer 也会退避。以下三种修饰符可用于覆盖其中一些隐式规则。请注意,所有这些修饰符都必须与 __GFP_DIRECT_RECLAIM 标志一起使用。

__GFP_NORETRY:在内存压力下,VM 实现将仅尝试非常轻量级的内存直接回收来获取一些内存(因此它可以休眠)。它会避免像 OOM killer 这样的破坏性操作。调用者必须处理失败,这在重内存压力下很可能发生。当失败可以轻松地以小代价处理时,例如降低吞吐量,此标志适用。

__GFP_RETRY_MAYFAIL:如果有一些迹象表明其他地方取得了进展,VM 实现将重试以前失败的内存回收过程。它可以等待其他任务尝试高级方法来释放内存,例如压缩(消除碎片)和页面换出。重试次数仍然有明确的限制,但比 __GFP_NORETRY 的限制更大。带有此标志的分配可能会失败,但仅当真正没有未使用的内存时才会失败。虽然这些分配不会直接触发 OOM killer,但它们的失败表明系统可能很快需要使用 OOM killer。调用者必须处理失败,但可以通过失败更高层的请求或仅以效率低得多的方式完成它来合理地处理失败。如果分配确实失败,并且调用者能够释放一些非必要的内存,则这样做可能对整个系统有利。

__GFP_NOFAIL:VM 实现_必须_无限重试:调用者无法处理分配失败。分配可能会无限期地阻塞,但永远不会返回失败。测试失败毫无意义。它_必须_是可阻塞的,并且必须与 __GFP_DIRECT_RECLAIM 一起使用。它_绝不_应在不可休眠的上下文中使用。应仔细评估新用户(并且只有在没有合理的失败策略时才应使用该标志),但使用该标志绝对比在分配器周围进行无限循环的硬编码要好。不支持使用 __GFP_NOFAIL 和 order > 1 从伙伴系统分配页面。请考虑改用 kvmalloc()。

有用的 GFP 标志组合

常用的有用的 GFP 标志组合。建议子系统从这些组合之一开始,然后根据需要设置/清除 __GFP_FOO 标志。

GFP_ATOMIC 用户不能休眠,并且需要分配成功。应用较低的水印以允许访问“原子保留”。当前实现不支持 NMI 和其他一些严格的非抢占上下文(例如 raw_spin_lock)。GFP_NOWAIT 也是如此。

GFP_KERNEL 对于内核内部分配是典型的。调用者需要 ZONE_NORMAL 或较低的区域以进行直接访问,但可以进行直接回收。

GFP_KERNEL_ACCOUNT 与 GFP_KERNEL 相同,只是该分配被计入 kmemcg。

GFP_NOWAIT 用于不应因直接回收而停滞、启动物理 IO 或使用任何文件系统回调的内核分配。即使是很小的分配,它也很可能无法分配内存。

GFP_NOIO 将使用直接回收来丢弃不需要启动任何物理 IO 的干净页面或 slab 页面。请尽量避免直接使用此标志,而是使用 memalloc_noio_{save,restore} 来标记整个范围,该范围不能执行任何 IO,并简要说明原因。所有分配请求都将隐式继承 GFP_NOIO。

GFP_NOFS 将使用直接回收,但不会使用任何文件系统接口。请尽量避免直接使用此标志,而是使用 memalloc_nofs_{save,restore} 来标记整个范围,该范围不能/不应递归到 FS 层,并简要说明原因。所有分配请求都将隐式继承 GFP_NOFS。

GFP_USER 用于用户空间分配,也需要内核或硬件直接访问。它通常由硬件用于映射到用户空间(例如,图形)的缓冲区,硬件仍然必须 DMA 到这些缓冲区。这些分配会强制执行 cpuset 限制。

GFP_DMA 的存在是出于历史原因,应尽可能避免使用。该标志表示调用者需要使用最低的区域(ZONE_DMA 或 x86-64 上的 16M)。理想情况下,这应该被删除,但这需要仔细的审查,因为一些用户确实需要它,而另一些用户使用该标志来避免 ZONE_DMA 中的低内存保留,并将最低的区域视为一种紧急保留类型。

GFP_DMA32GFP_DMA 类似,只是调用者需要 32 位地址。请注意,kmalloc(..., GFP_DMA32) 不会返回 DMA32 内存,因为未实现 DMA32 kmalloc 缓存数组。(原因:内核中没有这样的用户)。

GFP_HIGHUSER 用于可能映射到用户空间的用户空间分配,不需要内核直接访问,但一旦使用就不能移动。一个例子可能是将数据直接映射到用户空间但没有寻址限制的硬件分配。

GFP_HIGHUSER_MOVABLE 用于内核不需要直接访问但可以在需要访问时使用 kmap() 的用户空间分配。它们预计可以通过页面回收或页面迁移来移动。通常,LRU 上的页面也会使用 GFP_HIGHUSER_MOVABLE 进行分配。

GFP_TRANSHUGEGFP_TRANSHUGE_LIGHT 用于 THP 分配。它们是复合分配,如果内存不可用,通常会快速失败,并且不会在失败时唤醒 kswapd/kcompactd。_LIGHT 版本根本不尝试回收/压缩,默认情况下在页面错误路径中使用,而非轻版本由 khugepaged 使用。

Slab 缓存

SLAB_HWCACHE_ALIGN

SLAB_HWCACHE_ALIGN

在缓存行边界上对齐对象。

描述

足够大的对象在缓存行边界上对齐。对于小于缓存行大小一半的对象大小,对齐在缓存行大小的一半上。一般来说,如果对象大小小于缓存行大小的 1/2^n,则对齐调整为 1/2^n。

如果各自的 struct kmem_cache_args 字段也请求了显式对齐,则应用两者中较大的对齐。

SLAB_TYPESAFE_BY_RCU

SLAB_TYPESAFE_BY_RCU

警告 请阅读此内容!

描述

这会将 SLAB 页面的释放延迟一段时间,它_不会_延迟对象释放。这意味着如果您执行 kmem_cache_free(),则该内存位置可以随时重复使用。因此,在同一个 RCU 宽限期内,可能会在该位置看到另一个对象。

此功能仅确保支持对象的内存位置保持有效,使用此功能的技巧是依赖独立的对像验证传递。例如:

begin:
 rcu_read_lock();
 obj = lockless_lookup(key);
 if (obj) {
   if (!try_get_ref(obj)) // might fail for free objects
     rcu_read_unlock();
     goto begin;

   if (obj->key != key) { // not the object we expected
     put_ref(obj);
     rcu_read_unlock();
     goto begin;
   }
 }
rcu_read_unlock();

如果我们需要从通常没有锁定的情况下获取的地址以倾斜的方式访问内核结构,这将很有用。我们可以锁定该结构以使其稳定,并检查它是否仍然位于给定的地址,只有当我们确信内存没有被重用为其他类型的对象时(我们子系统的锁可能会损坏)。

在读取地址之前 rcu_read_lock,然后在获取预期在该地址的结构中的自旋锁之后 rcu_read_unlock。

请注意,在没有首先获取如上所述的引用之前,不可能在通过 SLAB_TYPESAFE_BY_RCU 分配的结构中获取锁。原因是 SLAB_TYPESAFE_BY_RCU 页面在提供给 slab 之前不会归零,这意味着必须在每次 kmem_struct_alloc() 之后初始化任何锁。或者,使传递给 kmem_cache_create() 的 ctor 在页面分配时初始化锁,如 __i915_request_ctor()、sighand_ctor() 和 anon_vma_ctor() 中所做的那样。这样的 ctor 允许读取器在 rcu_read_lock() 保护下安全地获取这些 ctor 初始化的锁。

请注意,SLAB_TYPESAFE_BY_RCU 最初名为 SLAB_DESTROY_BY_RCU。

SLAB_ACCOUNT

SLAB_ACCOUNT

将分配计入 memcg。

描述

来自此缓存的所有对象分配都将进行 memcg 统计,无论 __GFP_ACCOUNT 是否传递给单独的分配。

SLAB_RECLAIM_ACCOUNT

SLAB_RECLAIM_ACCOUNT

对象是可回收的。

描述

将此标志用于具有关联收缩器的缓存。因此,slab 页面是使用 __GFP_RECLAIMABLE 分配的,这会影响按移动性对页面进行分组,并在 /proc/meminfo 中的 SReclaimable 计数器中进行统计

struct kmem_cache_args

用于 kmem_cache_create() 的不太常见的参数

定义:

struct kmem_cache_args {
    unsigned int align;
    unsigned int useroffset;
    unsigned int usersize;
    unsigned int freeptr_offset;
    bool use_freeptr_offset;
    void (*ctor)(void *);
};

成员

对齐

对象所需的对齐方式。

0 表示不请求特定的对齐方式。

用户偏移

用户复制区域偏移量。

usersize 不为 0 时,0 是一个有效的偏移量。

用户大小

用户复制区域的大小。

0 表示未指定用户复制区域。

空闲指针偏移

SLAB_TYPESAFE_BY_RCU 缓存中空闲指针的自定义偏移量

默认情况下,SLAB_TYPESAFE_BY_RCU 缓存将空闲指针放置在对象外部。这可能会导致对象大小增加。如果缓存创建者有理由避免这种情况,则可以在其结构中指定自定义的空闲指针偏移量,空闲指针将放置在该偏移量处。

请注意,将空闲指针放置在对象内部需要调用者确保不会使任何用于防止对象回收的字段失效(有关详细信息,请参见 SLAB_TYPESAFE_BY_RCU)。

使用 0 作为 freeptr_offset 的值是有效的。如果指定了 freeptr_offset,则必须将 use_freeptr_offset 设置为 true

请注意,目前自定义空闲指针不支持 ctor,因为 ctor 需要一个外部空闲指针。

使用空闲指针偏移

是否使用 freeptr_offset

构造函数

对象的构造函数。

对于新分配的 slab 页中的每个对象,都会调用构造函数。缓存用户有责任以与调用构造函数后相同的状态释放对象,或者适当地处理新构造的对象和重新分配的对象之间的任何差异。

NULL 表示没有构造函数。

描述

结构的任何未初始化的字段都被解释为未使用。例外情况是 freeptr_offset,其中 0 是有效值,因此为了将该字段解释为已使用,还必须将 use_freeptr_offset 设置为 true。对于 useroffset0 也是有效值,但仅当 usersize 不为 0 时才有效。

当将 NULL 参数传递给 kmem_cache_create() 时,它等效于所有字段都未使用。

struct kmem_cache *kmem_cache_create_usercopy(const char *name, unsigned int size, unsigned int align, slab_flags_t flags, unsigned int useroffset, unsigned int usersize, void (*ctor)(void*))

创建一个 kmem 缓存,其中包含适合复制到用户空间的区域。

参数

const char *name

一个字符串,用于在 /proc/slabinfo 中标识此缓存。

unsigned int size

此缓存中要创建的对象的尺寸。

unsigned int align

对象所需的对齐方式。

slab_flags_t flags

SLAB 标志

unsigned int useroffset

用户复制区域偏移量

unsigned int usersize

用户复制区域大小

void (*ctor)(void *)

对象的构造函数,或 NULL

描述

这是一个遗留的包装器,如果仅需列入单个字段的白名单,则新代码应使用 KMEM_CACHE_USERCOPY(),或者使用 kmem_cache_create() 并通过 args 参数传递必要的参数(请参见 struct kmem_cache_args

返回

成功时指向缓存的指针,失败时为 NULL。

kmem_cache_create

kmem_cache_create (__name, __object_size, __args, ...)

创建一个 kmem 缓存。

参数

__name

一个字符串,用于在 /proc/slabinfo 中标识此缓存。

__object_size

此缓存中要创建的对象的尺寸。

__args

可选参数,请参见 struct kmem_cache_args。传递 NULL 表示所有参数都将使用默认值。

...

可变参数

描述

当前使用 _Generic() 作为宏实现,以调用该函数的新变体或遗留变体。

新变体具有 4 个参数:kmem_cache_create(name, object_size, args, flags)

请参见实现此功能的 __kmem_cache_create_args()

遗留变体具有 5 个参数:kmem_cache_create(name, object_size, align, flags, ctor)

align 和 ctor 参数映射到 struct kmem_cache_args 的相应字段

上下文

不能在中断中调用,但可以中断。

返回

成功时指向缓存的指针,失败时为 NULL。

size_t ksize(const void *objp)

报告关联对象的实际分配大小

参数

const void *objp

先前从 kmalloc() 系列分配返回的指针。

描述

不应将其用于写入超出原始请求分配大小的内容。要么使用 krealloc(),要么在分配之前使用 kmalloc_size_roundup() 向上舍入分配大小。如果将其用于访问超出原始请求分配大小的内容,则 UBSAN_BOUNDS 和/或 FORTIFY_SOURCE 可能会触发,因为它们仅通过 __alloc_size 属性知道原始分配的大小。

void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags)

分配一个对象

参数

struct kmem_cache *cachep

要从中分配的缓存。

gfp_t flags

请参见 kmalloc()

描述

从此缓存分配一个对象。请参见 kmem_cache_zalloc(),了解将 __GFP_ZERO 添加到标志的快捷方式。

返回

指向新对象的指针,或者在出错时为 NULL

bool kmem_cache_charge(void *objp, gfp_t gfpflags)

memcg 为已分配的 slab 内存收费

参数

void *objp

要 memcg 收费的 slab 对象的地址

gfp_t gfpflags

描述分配上下文

描述

kmem_cache_charge 允许将 slab 对象计入当前 memcg,主要是在分配时可能无法收费的情况下,因为目标 memcg 未知(即 softirq 上下文)

objp 应该是 slab 分配器函数(例如 kmalloc(带有 __GFP_ACCOUNT 标志)或 kmem_cache_alloc)返回的指针。内存控制组 (memcg) 的计费行为可以通过 gfpflags 参数控制,该参数会影响必要的内部元数据的分配方式。包含 __GFP_NOFAIL 表示请求过度收费而不是失败,但这不适用于内部元数据分配。

在某些情况下,即使没有完成计费,它也会返回 true:更具体地说

  1. 对于 !CONFIG_MEMCGcgroup_disable=memory 系统。

  2. 已计费的 slab 对象。

  3. 对于来自 KMALLOC_NORMAL 缓存的 slab 对象 - 由 kmalloc() 分配,未使用 __GFP_ACCOUNT 标志。

  4. 分配内部元数据失败

返回

如果计费成功,则为 true,否则为 false。

void *kmalloc(size_t size, gfp_t flags)

分配内核内存

参数

size_t size

需要多少字节的内存。

gfp_t flags

描述分配上下文

描述

kmalloc 是内核中为小于页大小的对象分配内存的常用方法。

分配的对象的地址至少与 ARCH_KMALLOC_MINALIGN 字节对齐。对于 size 为 2 的幂的字节数,还保证至少与该大小对齐。对于其他大小,保证对齐至少是 size 的最大 2 的幂的除数。

flags 参数可以是 include/linux/gfp_types.h 中定义的 GFP 标志之一,并在 Documentation/core-api/mm-api.rst 中描述。

flags 的建议用法在 Documentation/core-api/memory-allocation.rst 中描述。

下面是最有用的 GFP 标志的简要概述

GFP_KERNEL

分配正常的内核内存。可能会休眠。

GFP_NOWAIT

分配不会休眠。

GFP_ATOMIC

分配不会休眠。可以使用紧急池。

还可以通过或运算一个或多个以下附加 flags 来设置不同的标志

__GFP_ZERO

返回前将分配的内存置零。另请参阅 kzalloc()

__GFP_HIGH

此分配具有高优先级,可以使用紧急池。

__GFP_NOFAIL

表示此分配绝不允许失败(使用前请三思)。

__GFP_NORETRY

如果内存不可用,则立即放弃。

__GFP_NOWARN

如果分配失败,则不发出任何警告。

__GFP_RETRY_MAYFAIL

尽最大努力使分配成功,但最终会失败。

void *kmalloc_array(size_t n, size_t size, gfp_t flags)

为数组分配内存。

参数

size_t n

元素数量。

size_t size

元素大小。

gfp_t flags

要分配的内存类型(请参阅 kmalloc)。

void *krealloc_array(void *p, size_t new_n, size_t new_size, gfp_t flags)

为数组重新分配内存。

参数

void *p

指向要重新分配的内存块的指针

size_t new_n

要分配的新元素数量

size_t new_size

数组中单个成员的新大小

gfp_t flags

要分配的内存类型(请参阅 kmalloc

描述

如果请求 __GFP_ZERO 逻辑,调用方必须确保,从初始内存分配开始,对同一内存分配的后续每次调用都标记有 __GFP_ZERO。否则,此 API 可能不会完全遵守 __GFP_ZERO

有关更多详细信息,请参阅 krealloc_noprof()

在任何情况下,都会保留指向对象的原始内容,直到新旧大小中的较小者。

kcalloc

kcalloc (n, size, flags)

为数组分配内存。内存设置为零。

参数

n

元素数量。

大小

元素大小。

标志

要分配的内存类型(请参阅 kmalloc)。

void *kzalloc(size_t size, gfp_t flags)

分配内存。内存设置为零。

参数

size_t size

需要多少字节的内存。

gfp_t flags

要分配的内存类型(请参阅 kmalloc)。

size_t kmalloc_size_roundup(size_t size)

报告给定大小的分配桶大小

参数

size_t size

要向上舍入的字节数。

描述

这会返回在 size 字节的 kmalloc() 分配中可用的字节数。例如,126 字节的请求将向上舍入到下一个大小的 kmalloc 桶,即 128 字节。(这严格适用于基于通用 kmalloc() 的分配,而不适用于基于预先大小的 kmem_cache_alloc() 的分配。)

使用此方法提前 kmalloc() 完整桶大小,而不是在分配后使用 ksize() 来查询大小。

void kmem_cache_free(struct kmem_cache *s, void *x)

释放对象

参数

struct kmem_cache *s

分配来自的缓存。

void *x

先前分配的对象。

描述

释放之前从此缓存分配的对象。

void kfree(const void *object)

释放先前分配的内存

参数

const void *object

kmalloc()kmem_cache_alloc() 返回的指针

描述

如果 object 为 NULL,则不执行任何操作。

struct kmem_cache *__kmem_cache_create_args(const char *name, unsigned int object_size, struct kmem_cache_args *args, slab_flags_t flags)

创建一个 kmem 缓存。

参数

const char *name

一个字符串,用于在 /proc/slabinfo 中标识此缓存。

unsigned int object_size

此缓存中要创建的对象的尺寸。

struct kmem_cache_args *args

用于缓存创建的附加参数(请参阅 struct kmem_cache_args)。

slab_flags_t flags

请参阅各个标志的描述。常用的标志在下面的描述中列出。

描述

不要直接调用,请使用带有相同参数的 kmem_cache_create() 包装器。

常用 flags

SLAB_ACCOUNT - 将分配记入 memcg。

SLAB_HWCACHE_ALIGN - 将对象对齐到缓存行边界。

SLAB_RECLAIM_ACCOUNT - 对象可回收。

SLAB_TYPESAFE_BY_RCU - Slab 页(而不是单个对象)的释放会被延迟一段时间 - 使用前请参阅完整描述。

上下文

不能在中断中调用,但可以中断。

返回

成功时指向缓存的指针,失败时为 NULL。

kmem_buckets *kmem_buckets_create(const char *name, slab_flags_t flags, unsigned int useroffset, unsigned int usersize, void (*ctor)(void*))

创建一组缓存,用于通过 kmem_buckets_alloc() 处理动态大小的分配

参数

const char *name

一个前缀字符串,在 /proc/slabinfo 中用于标识此缓存。各个缓存将具有其大小作为后缀。

slab_flags_t flags

SLAB 标志(有关详细信息,请参阅 kmem_cache_create())。

unsigned int useroffset

分配中可能复制到/从用户空间复制的起始偏移量。

unsigned int usersize

useroffset 开始,有多少字节可以复制到/从用户空间复制。

void (*ctor)(void *)

对象的构造函数,在进行新分配时运行。

描述

不能在中断中调用,但可以被中断。

返回

成功时指向缓存的指针,失败时为 NULL。当未启用 CONFIG_SLAB_BUCKETS 时,返回 ZERO_SIZE_PTR,并且后续对 kmem_buckets_alloc() 的调用将回退到 kmalloc()。(即,调用者只需要检查失败时的 NULL。)

int kmem_cache_shrink(struct kmem_cache *cachep)

缩小缓存。

参数

struct kmem_cache *cachep

要缩小的缓存。

描述

释放缓存尽可能多的 slab。为了帮助调试,零退出状态表示所有 slab 都已释放。

返回

0 如果所有 slab 都已释放,否则为非零值

bool kmem_dump_obj(void *object)

打印可用的 slab 出处信息

参数

void *object

要查找出处信息的 slab 对象。

描述

此函数使用 pr_cont(),因此期望调用者打印出任何适当的前导信息。出处信息取决于对象的类型以及启用了多少调试。对于 slab 缓存对象,将打印它是 slab 对象的事实,并且如果可用,则打印 slab 名称、返回地址以及该对象的分配和上次释放路径的堆栈跟踪。

返回

如果指针指向来自 kmalloc()kmem_cache_alloc() 的尚未释放的对象,则为 true,如果指针指向已释放的对象,则为 truefalse,否则为 false

void kfree_sensitive(const void *p)

在释放之前清除内存中的敏感信息

参数

const void *p

要释放其内存的对象

描述

在释放之前,将 p 指向的对象的内存清零。如果 pNULLkfree_sensitive() 不执行任何操作。

注意

此函数会将整个已分配的缓冲区清零,该缓冲区可能比传递给 kmalloc() 的请求缓冲区大小大得多。因此,在性能敏感的代码中使用此函数时要小心。

void kfree_const(const void *x)

有条件地释放内存

参数

const void *x

指向内存的指针

描述

仅当 x 不在 .rodata 段中时,该函数才调用 kfree。

void kvfree(const void *addr)

释放内存。

参数

const void *addr

指向已分配内存的指针。

描述

kvfree 释放由 vmalloc()、kmalloc() 或 kvmalloc() 分配的内存。如果您确定知道要使用哪个函数,则使用 kfree()vfree() 会稍微高效一些。

上下文

可抢占的任务上下文或非 NMI 中断。

虚拟连续映射

void vm_unmap_aliases(void)

取消映射 vmap 层中未完成的延迟别名

参数

void

无参数

描述

vmap/vmalloc 层主要延迟刷新内核虚拟映射,以分摊 TLB 刷新的开销。这意味着您现在拥有的任何页面,可能在其以前的生命周期中,已经被 vmap 层映射到内核虚拟地址中,因此可能有一些 CPU 的 TLB 条目仍然引用该页面(除了常规的 1:1 内核映射之外)。

vm_unmap_aliases 会刷新所有此类延迟映射。在它返回之后,我们可以确信我们控制的任何页面都不会有来自 vmap 层的任何别名。

void vm_unmap_ram(const void *mem, unsigned int count)

取消映射 vm_map_ram 设置的线性内核地址空间

参数

const void *mem

vm_map_ram 返回的指针

unsigned int count

传递给 vm_map_ram 调用的计数(不能取消映射部分)

void *vm_map_ram(struct page **pages, unsigned int count, int node)

将页面线性映射到内核虚拟地址(vmalloc 空间)

参数

struct page **pages

指向要映射的页面的指针数组

unsigned int count

页面数

int node

首选在此节点上分配数据结构

描述

如果使用此函数分配的页面数小于 VMAP_MAX_ALLOC,它可能比 vmap 更快,这是它的优点。但是,如果将长生命周期对象和短生命周期对象与 vm_map_ram() 混合使用,可能会因为碎片化而消耗大量地址空间(尤其是在 32 位机器上)。最终可能会看到失败。请将此函数用于短生命周期对象。

返回

指向已映射地址的指针,如果失败则返回 NULL

void vfree(const void *addr)

释放由 vmalloc() 分配的内存

参数

const void *addr

内存基地址

描述

释放从 vmalloc() 系列 API 之一获得的,以 addr 开始的虚拟连续内存区域。 这通常也会释放虚拟分配的底层物理内存,但是该内存是引用计数的,因此只有在最后一个用户离开后才会被释放。

如果 addr 为 NULL,则不执行任何操作。

上下文

如果 不是 从中断上下文调用,则可能会睡眠。 绝不能在 NMI 上下文中调用(严格来说,如果我们有 CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG 也可以调用,但是为 vfree() 设置体系结构相关的调用约定将是一个非常糟糕的主意)。

void vunmap(const void *addr)

释放由 vmap() 获取的虚拟映射

参数

const void *addr

内存基地址

描述

释放从传递给 vmap() 的页面数组创建的,以 addr 开始的虚拟连续内存区域。

不得在中断上下文中调用。

void *vmap(struct page **pages, unsigned int count, unsigned long flags, pgprot_t prot)

将页面数组映射到虚拟连续空间

参数

struct page **pages

页面指针数组

unsigned int count

要映射的页面数

unsigned long flags

vm_area->flags

pgprot_t prot

映射的页面保护

描述

将来自 pagescount 个页面映射到连续的内核虚拟空间。 如果 flags 包含 VM_MAP_PUT_PAGES,则页面数组本身的所有权(必须是 kmalloc 或 vmalloc 内存)以及其中每个页面的一个引用将从调用者转移到 vmap(),并且当在返回值上调用 vfree() 时,将会被释放/删除。

返回

区域地址,如果失败则返回 NULL

void *vmap_pfn(unsigned long *pfns, unsigned int count, pgprot_t prot)

将 PFN 数组映射到虚拟连续空间

参数

unsigned long *pfns

PFN 数组

unsigned int count

要映射的页面数

pgprot_t prot

映射的页面保护

描述

将来自 pfnscount 个 PFN 映射到连续的内核虚拟空间,并返回映射的起始地址。

int remap_vmalloc_range(struct vm_area_struct *vma, void *addr, unsigned long pgoff)

将 vmalloc 页面映射到用户空间

参数

struct vm_area_struct *vma

要覆盖的 vma (映射 vma 的全部范围)

void *addr

vmalloc 内存

unsigned long pgoff

在要映射的第一个页面之前,addr 中的页面数

返回

成功返回 0,失败返回 -Exxx

描述

此函数检查 addr 是否是有效的 vmalloc 区域,以及它是否足够大以覆盖 vma。如果未满足该条件,则将返回失败。

类似于 remap_pfn_range()(请参阅 mm/memory.c)

文件映射和页面缓存

文件映射

int filemap_fdatawrite_wbc(struct address_space *mapping, struct writeback_control *wbc)

在指定范围内的映射脏页面上启动回写

参数

struct address_space *mapping

要写入的地址空间结构

struct writeback_control *wbc

控制写出的 writeback_control

描述

使用提供的 wbc 调用映射上的 writepages 以控制写出。

返回

成功返回 0,否则返回负错误代码。

int filemap_flush(struct address_space *mapping)

主要是非阻塞刷新

参数

struct address_space *mapping

目标地址空间

描述

这主要是非阻塞刷新。不适用于数据完整性目的 - 可能不会针对所有脏页启动 I/O。

返回

成功返回 0,否则返回负错误代码。

bool filemap_range_has_page(struct address_space *mapping, loff_t start_byte, loff_t end_byte)

检查指定范围内是否存在页面。

参数

struct address_space *mapping

要在其中检查的地址空间

loff_t start_byte

范围开始的字节偏移量

loff_t end_byte

范围结束的字节偏移量(包括)

描述

在提供的范围内查找至少一个页面,通常用于检查在此范围内直接写入是否会触发回写。

返回

如果在指定的范围内存在至少一个页面,则返回 true,否则返回 false

int filemap_fdatawait_range(struct address_space *mapping, loff_t start_byte, loff_t end_byte)

等待回写完成

参数

struct address_space *mapping

要等待的地址空间结构

loff_t start_byte

范围开始的字节偏移量

loff_t end_byte

范围结束的字节偏移量(包括)

描述

遍历给定范围内给定地址空间的正在回写的页面列表,并等待所有这些页面。检查地址空间的错误状态并返回它。

由于地址空间的错误状态由此函数清除,因此调用者有责任检查返回值并处理和/或报告错误。

返回

地址空间的错误状态。

int filemap_fdatawait_range_keep_errors(struct address_space *mapping, loff_t start_byte, loff_t end_byte)

等待回写完成

参数

struct address_space *mapping

要等待的地址空间结构

loff_t start_byte

范围开始的字节偏移量

loff_t end_byte

范围结束的字节偏移量(包括)

描述

遍历给定地址空间中给定范围内的正在写回的页面列表,并等待所有页面完成。与 filemap_fdatawait_range() 不同,此函数不会清除地址空间的错误状态。

如果调用者不自行处理错误,则使用此函数。预期的调用位置是系统范围/文件系统范围的数据刷新程序:例如 sync(2)、fsfreeze(8)。

int file_fdatawait_range(struct file *file, loff_t start_byte, loff_t end_byte)

等待回写完成

参数

struct file *file

指向要等待的地址空间结构的 file

loff_t start_byte

范围开始的字节偏移量

loff_t end_byte

范围结束的字节偏移量(包括)

描述

遍历 file 指向的地址空间中给定范围内的正在写回的页面列表,并等待所有页面完成。检查地址空间的错误状态与 file->f_wb_err 指针,并返回错误状态。

由于文件的错误状态由此函数推进,因此调用者负责检查返回值并处理和/或报告错误。

返回

地址空间的错误状态与 file->f_wb_err 指针。

int filemap_fdatawait_keep_errors(struct address_space *mapping)

等待写回,但不清除错误。

参数

struct address_space *mapping

要等待的地址空间结构

描述

遍历给定地址空间的正在写回的页面列表,并等待所有页面完成。与 filemap_fdatawait() 不同,此函数不会清除地址空间的错误状态。

如果调用者不自行处理错误,则使用此函数。预期的调用位置是系统范围/文件系统范围的数据刷新程序:例如 sync(2)、fsfreeze(8)。

返回

地址空间的错误状态。

int filemap_write_and_wait_range(struct address_space *mapping, loff_t lstart, loff_t lend)

写出并等待文件范围。

参数

struct address_space *mapping

页面的 address_space。

loff_t lstart

范围开始的字节偏移量

loff_t lend

范围结束的字节偏移量(包括)

描述

写出并等待文件偏移量 lstart->lend(包括)的数据。

请注意,**lend** 是包含的(描述要写入的最后一个字节),因此可以使用此函数写入文件的末尾(end = -1)。

返回

地址空间的错误状态。

int file_check_and_advance_wb_err(struct file *file)

报告之前发生的写回错误(如果有),并将 wb_err 前进到当前值。

参数

struct file *file

正在报告错误的 struct file

描述

当用户空间调用 fsync(或类似 nfsd 执行等效操作)时,我们希望报告自上次 fsync 以来(或自打开文件以来,如果没有任何错误)发生的任何写回错误。

从 mapping 中获取 wb_err。如果它与我们在文件中拥有的值匹配,则快速返回 0。文件已全部同步。

如果它不匹配,则获取 mapping 的值,设置其中的“seen”标志,并尝试将其交换到位。如果它起作用,或者另一个任务使用新值抢先了我们,则更新 f_wb_err 并返回错误部分。此时的错误必须通过适当的渠道(例如 fsync 或 NFS COMMIT 操作等)报告。

虽然我们使用原子操作处理 mapping->wb_err,但 f_wb_err 值受 f_lock 保护,因为我们必须确保它反映为此文件描述符交换的最新值。

返回

成功返回 0,否则返回负错误代码。

int file_write_and_wait_range(struct file *file, loff_t lstart, loff_t lend)

写出并等待文件范围。

参数

struct file *file

指向具有页面的 address_space 的 file。

loff_t lstart

范围开始的字节偏移量

loff_t lend

范围结束的字节偏移量(包括)

描述

写出并等待文件偏移量 lstart->lend(包括)的数据。

请注意,**lend** 是包含的(描述要写入的最后一个字节),因此可以使用此函数写入文件的末尾(end = -1)。

在写出并等待数据之后,我们会检查并将 f_wb_err 指针前进到最新值,并返回在那里检测到的任何错误。

返回

成功返回 0,否则返回负错误代码。

void replace_page_cache_folio(struct folio *old, struct folio *new)

用新的 folio 替换页面缓存中的 folio。

参数

struct folio *old

要替换的 folio。

struct folio *new

用来替换的 folio。

描述

此函数用新的 folio 替换页面缓存中的一个 folio。成功后,它会获取新 folio 的页面缓存引用,并删除旧 folio 的引用。旧的和新的 folio 都必须被锁定。此函数不会将新的 folio 添加到 LRU,调用者必须执行此操作。

删除 + 添加是原子的。此函数不会失败。

void folio_add_wait_queue(struct folio *folio, wait_queue_entry_t *waiter)

将任意等待者添加到 folio 的等待队列。

参数

struct folio *folio

定义感兴趣的等待队列的 Folio。

wait_queue_entry_t *waiter

要添加到队列的等待者。

描述

将任意 **waiter** 添加到指定的 **folio** 的等待队列。

void folio_unlock(struct folio *folio)

解锁锁定的 folio。

参数

struct folio *folio

folio。

描述

解锁 folio 并唤醒在页面锁上休眠的任何线程。

上下文

可以从中断或进程上下文中调用。不能从 NMI 上下文中调用。

void folio_end_read(struct folio *folio, bool success)

结束 folio 上的读取。

参数

struct folio *folio

folio。

bool success

如果所有读取都成功完成,则为 True。

描述

当针对 folio 的所有读取都已完成时,文件系统应调用此函数以通知页面缓存不再有未完成的读取。这将解锁 folio 并唤醒在锁上休眠的任何线程。如果所有读取都成功,则 folio 也将被标记为最新。

上下文

可以从中断或进程上下文中调用。不能从 NMI 上下文中调用。

void folio_end_private_2(struct folio *folio)

清除 PG_private_2 并唤醒所有等待者。

参数

struct folio *folio

folio。

描述

清除 folio 上的 PG_private_2 位,并唤醒所有等待该位的休眠线程。释放为设置 PG_private_2 而持有的 folio 引用。

例如,当一个 netfs folio 被写入本地磁盘缓存时,会使用此功能,从而允许对同一 folio 的缓存写入进行串行化。

void folio_wait_private_2(struct folio *folio)

等待 folio 上的 PG_private_2 被清除。

参数

struct folio *folio

要等待的 folio。

描述

等待 folio 上的 PG_private_2 被清除。

int folio_wait_private_2_killable(struct folio *folio)

等待 folio 上的 PG_private_2 被清除。

参数

struct folio *folio

要等待的 folio。

描述

等待 folio 上的 PG_private_2 被清除,或直到调用任务收到致命信号。

返回

  • 如果成功,则返回 0。

  • 如果遇到致命信号,则返回 -EINTR。

void folio_end_writeback(struct folio *folio)

结束针对 folio 的回写操作。

参数

struct folio *folio

folio。

描述

该 folio 实际上必须正在进行回写操作。

上下文

可以从进程或中断上下文调用。

void __folio_lock(struct folio *folio)

获取 folio 的锁,假设我们需要休眠才能获取到锁。

参数

struct folio *folio

要锁定的 folio

pgoff_t page_cache_next_miss(struct address_space *mapping, pgoff_t index, unsigned long max_scan)

查找页面缓存中的下一个空隙。

参数

struct address_space *mapping

映射。

pgoff_t index

索引。

unsigned long max_scan

要搜索的最大范围。

描述

搜索范围 [index, min(index + max_scan - 1, ULONG_MAX)],找到索引最小的空隙。

此函数可以在 rcu_read_lock 下调用。但是,这不会原子地搜索缓存在单个时间点的快照。例如,如果在索引 5 处创建一个空隙,然后在索引 10 处创建一个空隙,则在 rcu_read_lock 下调用时,涵盖这两个索引的 page_cache_next_miss 可能会返回 10。

返回

如果找到空隙,则返回该空隙的索引,否则返回指定范围之外的索引(在这种情况下,“return - index >= max_scan” 将为真)。在极少数情况下,如果索引回绕,将返回 0。

pgoff_t page_cache_prev_miss(struct address_space *mapping, pgoff_t index, unsigned long max_scan)

查找页面缓存中的上一个空隙。

参数

struct address_space *mapping

映射。

pgoff_t index

索引。

unsigned long max_scan

要搜索的最大范围。

描述

搜索范围 [max(index - max_scan + 1, 0), index],找到索引最大的空隙。

此函数可以在 rcu_read_lock 下调用。但是,这不会原子地搜索缓存在单个时间点的快照。例如,如果在索引 10 处创建一个空隙,然后在索引 5 处创建一个空隙,则在 rcu_read_lock 下调用时,涵盖这两个索引的 page_cache_prev_miss() 可能会返回 5。

返回

如果找到空隙,则返回该空隙的索引,否则返回指定范围之外的索引(在这种情况下,“index - return >= max_scan” 将为真)。在极少数情况下,如果索引回绕,将返回 ULONG_MAX。

struct folio *__filemap_get_folio(struct address_space *mapping, pgoff_t index, fgf_t fgp_flags, gfp_t gfp)

查找并获取对 folio 的引用。

参数

struct address_space *mapping

要搜索的 address_space。

pgoff_t index

页面索引。

fgf_t fgp_flags

FGP 标志会修改 folio 的返回方式。

gfp_t gfp

如果指定了 FGP_CREAT,则使用的内存分配标志。

描述

查找 mapping & index 处的页面缓存条目。

如果指定了 FGP_LOCKFGP_CREAT,即使为 FGP_CREAT 指定的 GFP 标志是原子的,该函数也可能会休眠。

如果此函数返回一个 folio,则返回时引用计数会增加。

返回

找到的 folio,否则返回 ERR_PTR()

unsigned filemap_get_folios(struct address_space *mapping, pgoff_t *start, pgoff_t end, struct folio_batch *fbatch)

获取一批 folio

参数

struct address_space *mapping

要搜索的 address_space

pgoff_t *start

起始页面索引

pgoff_t end

最终页面索引(包括)

struct folio_batch *fbatch

要填充的批次。

描述

在映射中搜索并返回一批 folio,从索引 start 开始,直到索引 end(包括)。folio 将在 fbatch 中返回,并且引用计数会增加。

返回

找到的 folio 数量。我们还会更新 start 以索引下一个要遍历的 folio。

unsigned filemap_get_folios_contig(struct address_space *mapping, pgoff_t *start, pgoff_t end, struct folio_batch *fbatch)

获取一批连续的 folio

参数

struct address_space *mapping

要搜索的 address_space

pgoff_t *start

起始页面索引

pgoff_t end

最终页面索引(包括)

struct folio_batch *fbatch

要填充的批次

描述

filemap_get_folios_contig() 的工作方式与 filemap_get_folios() 完全相同,但返回的 folio 保证是连续的。如果批次被填满,则可能不会返回所有连续的 folio。

返回

找到的 folio 数量。还会更新 start,以便定位到下一个 folio 的遍历位置。

unsigned filemap_get_folios_tag(struct address_space *mapping, pgoff_t *start, pgoff_t end, xa_mark_t tag, struct folio_batch *fbatch)

获取一批与 tag 匹配的 folio

参数

struct address_space *mapping

要搜索的 address_space

pgoff_t *start

起始页面索引

pgoff_t end

最终页面索引(包括)

xa_mark_t tag

标签索引

struct folio_batch *fbatch

要填充的批次

描述

第一个页可能在 start 之前开始;如果确实如此,它将包含 start。最后一个页可能延伸到 end 之后;如果确实如此,它将包含 end。这些页具有递增的索引。如果页面缓存中存在没有页的索引,则页面之间可能存在间隙。如果在运行此操作时将页面添加到页面缓存或从页面缓存中删除,则此调用可能找到也可能找不到它们。仅返回使用 tag 标记的页面。

返回

找到的页面数量。还会更新 start 以索引下一个用于遍历的页面。

ssize_t filemap_read(struct kiocb *iocb, struct iov_iter *iter, ssize_t already_read)

从页面缓存读取数据。

参数

struct kiocb *iocb

要读取的 iocb。

struct iov_iter *iter

数据目标位置。

ssize_t already_read

调用者已读取的字节数。

描述

从页面缓存复制数据。如果数据当前不存在,则使用预读和 read_folio address_space 操作来获取数据。

返回

复制的总字节数,包括调用者已读取的字节数。如果在复制任何字节之前发生错误,则返回一个负错误号。

ssize_t generic_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)

通用文件系统读取例程

参数

struct kiocb *iocb

内核 I/O 控制块

struct iov_iter *iter

读取的数据目标位置

描述

这是所有可以直接使用页面缓存的文件系统的 “read_iter()” 例程。

iocb->ki_flags 中的 IOCB_NOWAIT 标志指示,当无法在不等待 I/O 请求完成的情况下读取数据时,应返回 -EAGAIN;它不会阻止预读。

iocb->ki_flags 中的 IOCB_NOIO 标志指示,不应对读取或预读发出新的 I/O 请求。当无法读取数据时,应返回 -EAGAIN。当将触发预读时,应返回部分读取(可能为空)。

返回

  • 复制的字节数,即使是部分读取也是如此

  • 如果未读取任何内容,则为负错误代码(如果为 IOCB_NOIO,则为 0)

ssize_t filemap_splice_read(struct file *in, loff_t *ppos, struct pipe_inode_info *pipe, size_t len, unsigned int flags)

将数据从文件的页面缓存拼接到一个管道中

参数

struct file *in

要从中读取的文件

loff_t *ppos

指向从中读取的文件位置的指针

struct pipe_inode_info *pipe

要拼接到的管道

size_t len

要拼接的数量

unsigned int flags

SPLICE_F_* 标志

描述

此函数从文件的页面缓存中获取页面,并将它们拼接到管道中。将根据需要调用预读以填充更多页面。这也可能用于块设备。

返回

成功时,将返回读取的字节数,并且如果适用,将更新 *ppos;如果没有更多数据要读取,则返回 0;如果管道没有空间,则返回 -EAGAIN,并且如果发生错误,则返回其他负错误代码。如果管道空间不足、我们到达数据末尾或遇到空洞,则可能发生短读取。

vm_fault_t filemap_fault(struct vm_fault *vmf)

读取用于页面错误处理的文件数据

参数

struct vm_fault *vmf

包含页面错误详细信息的 struct vm_fault

描述

filemap_fault() 通过映射内存区域的 vma 操作向量调用,以在页面错误期间读取文件数据。

goto 有点难看,但这简化了在页面缓存中存在文件的正常情况,并且可以合理地处理特殊情况,而不会有大量重复的代码。

在条目中,必须持有 vma->vm_mm->mmap_lock。

如果我们的返回值设置了 VM_FAULT_RETRY,则是因为 mmap_lock 可能会在执行 I/O 之前或通过 lock_folio_maybe_drop_mmap() 释放。

如果我们的返回值未设置 VM_FAULT_RETRY,则尚未释放 mmap_lock。

我们永远不会返回带有 VM_FAULT_RETRY 的值,并且设置了 VM_FAULT_ERROR 中的位。

返回

VM_FAULT_ 代码的按位或。

struct folio *read_cache_folio(struct address_space *mapping, pgoff_t index, filler_t filler, struct file *file)

读取到页面缓存中,如果需要,则填充它。

参数

struct address_space *mapping

要从中读取的 address_space。

pgoff_t index

要读取的索引。

filler_t filler

执行读取的函数,或 NULL 以使用 aops->read_folio()。

struct file *file

传递给填充函数,如果不需要,则可以为 NULL。

描述

将一个页面读取到页面缓存中。如果成功,返回的页将包含 index,但它可能不是页的第一个页面。

如果填充函数返回错误,它将被返回给调用者。

上下文

可能会休眠。期望持有 mapping->invalidate_lock。

返回

成功时,返回最新的页,失败时,返回 ERR_PTR()

struct folio *mapping_read_folio_gfp(struct address_space *mapping, pgoff_t index, gfp_t gfp)

读取到页面缓存中,使用指定的分配标志。

参数

struct address_space *mapping

页的 address_space。

pgoff_t index

分配的页将包含的索引。

gfp_t gfp

如果分配,要使用的页面分配器标志。

描述

这与 “read_cache_folio(mapping, index, NULL, NULL)” 相同,但使用指定的分配标志完成任何新的内存分配。

此函数最有可能出现的错误是 EIO,但 ENOMEM 是可能的,EINTR 也是如此。如果 ->read_folio 返回另一个错误,它将返回给调用者。

该函数期望 mapping->invalidate_lock 已被持有。

返回

成功时,返回最新的页,失败时,返回 ERR_PTR()

struct page *read_cache_page_gfp(struct address_space *mapping, pgoff_t index, gfp_t gfp)

读取到页面缓存中,使用指定的页面分配标志。

参数

struct address_space *mapping

页面的地址空间

pgoff_t index

页面索引

gfp_t gfp

如果分配页面要使用的页面分配器标志

描述

这与 “read_mapping_page(mapping, index, NULL)” 相同,但任何新的页面分配都使用指定的分配标志进行。

如果页面没有被更新,则返回 -EIO。

该函数期望 mapping->invalidate_lock 已被持有。

返回

成功时返回最新的页面,失败时返回 ERR_PTR()

ssize_t __generic_file_write_iter(struct kiocb *iocb, struct iov_iter *from)

将数据写入文件

参数

struct kiocb *iocb

IO 状态结构(文件、偏移量等)

struct iov_iter *from

包含要写入数据的 iov_iter

描述

此函数执行实际将数据写入文件所需的所有工作。它执行所有基本检查,从文件中删除 SUID,更新修改时间,并根据我们是否执行直接 IO 或标准缓冲写入调用适当的子例程。

它期望获取 i_rwsem,除非我们处理不需要锁定的块设备或类似对象。

对于 O_SYNC 写入,此函数负责同步数据。调用者必须处理它。这主要是因为我们要避免在 i_rwsem 下同步。

返回

  • 写入的字节数,即使对于截断的写入也是如此

  • 如果没有写入任何数据,则为负错误代码

ssize_t generic_file_write_iter(struct kiocb *iocb, struct iov_iter *from)

将数据写入文件

参数

struct kiocb *iocb

IO 状态结构

struct iov_iter *from

包含要写入数据的 iov_iter

描述

这是 __generic_file_write_iter() 的包装器,供大多数文件系统使用。它负责在 O_SYNC 文件的情况下同步文件,并根据需要获取 i_rwsem。

返回

  • 如果根本没有写入任何数据,或者 vfs_fsync_range() 对于同步写入失败,则为负错误代码

  • 写入的字节数,即使对于截断的写入也是如此

bool filemap_release_folio(struct folio *folio, gfp_t gfp)

释放 folio 上的文件系统特定元数据。

参数

struct folio *folio

内核尝试释放的 folio。

gfp_t gfp

内存分配标志(和 I/O 模式)。

描述

address_space 正在尝试释放附加到 folio 的任何数据(大概在 folio->private)。

如果页面上设置了 private_2 标志,则也会调用此函数,表示该 folio 具有其他与之关联的元数据。

gfp 参数指定是否可以执行 I/O 来释放此页面 (__GFP_IO),以及该调用是否可以阻塞 (__GFP_RECLAIM & __GFP_FS)。

返回

如果释放成功,则为 true,否则为 false

int filemap_invalidate_inode(struct inode *inode, bool flush, loff_t start, loff_t end)

使 inode 的页缓存的范围无效/强制写回

参数

struct inode *inode

要刷新的 inode

bool flush

设置为写回而不是简单地使无效。

loff_t start

范围中的第一个字节。

loff_t end

范围中的最后一个字节(包括),或 LLONG_MAX 表示从开始之后的所有内容。

描述

使 inode 上有助于指定范围的所有 folio 无效,可能会先写回它们。在执行操作时,将持有无效锁以防止安装新的 folio。

预读

预读用于在应用程序显式请求之前将内容读取到页面缓存中。预读只会尝试读取尚未在页面缓存中的 folio。如果存在 folio 但不是最新的,则预读不会尝试读取它。在这种情况下,将请求一个简单的 ->read_folio()。

当应用程序读取请求(无论是系统调用还是页面错误)发现所请求的 folio 不在页面缓存中,或者它在页面缓存中并且设置了预读标志时,将触发预读。此标志指示该 folio 是作为先前预读请求的一部分读取的,现在它已被访问,是进行下一次预读的时候了。

每个预读请求都是部分同步读取和部分异步预读。这反映在 struct file_ra_state 中,其中包含 ->size 作为总页数,以及 ->async_size,它是异步部分中的页数。预读标志将设置在此异步部分中的第一个 folio 上,以触发后续预读。一旦建立了一系列的顺序读取,就不需要同步组件,并且所有预读请求都将完全异步。

当其中任何一个触发器导致预读时,需要确定三个数字:要读取的区域的开始位置、区域的大小和异步尾部的大小。

区域的开始位置只是在已访问地址之后或之后的第一个页面地址,该地址当前未在页面缓存中填充。这是通过在页面缓存中进行简单搜索找到的。

异步尾部的大小是通过从确定的请求大小中减去显式请求的大小来确定的,除非这小于零 - 然后使用零。注意,当区域的开始位置不是访问的页面时,此计算是错误的。而且,此计算不一致使用。

区域的大小通常从加载前述页面的上一次预读的大小确定。这可以从简单顺序读取的 struct file_ra_state 中发现,或者从检查多个顺序读取交错时页面缓存的状态中发现。具体而言:在预读由预读标志触发的情况下,假定上一次预读的大小是从触发页面到新预读开始的页数。在这些情况下,对于新的预读,将缩放上一次预读的大小,通常会加倍,但请参阅 get_next_ra_size() 了解详细信息。

如果无法确定上一次读取的大小,则使用页面缓存中前述页面的数量来估计上一次读取的大小。此估计很容易被偶然相邻的随机读取误导,因此除非它大于当前请求,否则会忽略它,并且不会放大,除非它位于文件开头。

一般来说,在文件开头会加速预读,因为从那里读取通常是顺序的。在各种特殊情况下,对预读大小还有其他小的调整,最好通过阅读代码来发现这些调整。

上面基于上一次预读大小的计算确定了预读的大小,可以向其中添加任何请求的读取大小。

预读请求使用 ->readahead() 地址空间操作发送到文件系统,其中 mpage_readahead() 是一个规范实现。 ->readahead() 通常应启动对所有 folio 的读取,但可能无法读取任何或全部 folio 而不会导致 I/O 错误。页面缓存读取代码将为 ->readahead() 未读取的任何 folio 发出 ->read_folio() 请求,并且只有来自此的错误才是最终的。

->readahead() 通常会重复调用 readahead_folio() 以从那些准备好进行预读的页面中获取每个 folio。它可能无法通过读取 folio。

  • 没有足够多次调用 readahead_folio(),从而有效地忽略了一些 folio,这在存储路径拥塞时可能是合适的。

  • 未能实际提交给定 folio 的读取请求,可能是由于资源不足,或者

  • 在后续处理请求期间发生错误。

在后两种情况下,文件系统应解锁该 folio,以表明读取尝试失败。在第一种情况下,该 folio 将由 VFS 解锁。

不在请求最终 async_size 中的那些 folio 应被认为是重要的,并且 ->readahead() 不应由于拥塞或临时资源不可用而使其失败,而应等待必要的资源(例如内存或索引信息)变为可用。最终 async_size 中的 Folio 可以被认为不太紧急,并且未能读取它们是可以接受的。在这种情况下,最好使用 filemap_remove_folio() 将 folio 从页面缓存中删除,就像自动为未使用 readahead_folio() 获取的 folio 所做的那样。这将允许后续的同步预读请求再次尝试读取它们。如果它们保留在页面缓存中,那么将使用 ->read_folio() 单独读取它们,这可能效率较低。

void page_cache_ra_unbounded(struct readahead_control *ractl, unsigned long nr_to_read, unsigned long lookahead_size)

启动未检查的预读。

参数

struct readahead_control *ractl

预读控制。

unsigned long nr_to_read

要读取的页数。

unsigned long lookahead_size

下次预读的起始位置。

描述

此函数供文件系统在它们想要启动超出文件声明的 i_size 的预读时调用。这几乎肯定不是您想要调用的函数。请改用 page_cache_async_readahead()page_cache_sync_readahead()

上下文

文件由调用者引用。调用者可以持有互斥锁。可能会休眠,但不会重新进入文件系统以回收内存。

void readahead_expand(struct readahead_control *ractl, loff_t new_start, size_t new_len)

扩展预读请求

参数

struct readahead_control *ractl

要扩展的请求

loff_t new_start

修订后的起始位置

size_t new_len

修订后的请求大小

描述

尝试通过在当前窗口之前和之后插入锁定的页面来将预读请求从当前大小向外扩展到指定大小,以将大小增加到新窗口。这可能涉及 THP 的插入,在这种情况下,窗口可能会扩展到超出请求的大小。

如果算法遇到页面缓存中已存在的冲突页面,将会停止,并留下比请求的扩展更小的扩展。

调用者必须通过检查修订后的 ractl 对象是否存在与请求不同的扩展来检查这一点。

回写

int balance_dirty_pages_ratelimited_flags(struct address_space *mapping, unsigned int flags)

平衡脏内存状态。

参数

struct address_space *mapping

已被弄脏的 address_space。

unsigned int flags

BDP 标志。

描述

正在弄脏内存的进程应在此处为每个新弄脏的页面调用一次。该函数将定期检查系统的脏状态,并在需要时启动回写。

有关详细信息,请参阅 balance_dirty_pages_ratelimited()

返回

如果 flags 包含 BDP_ASYNC,它可能会返回 -EAGAIN,表示内存不平衡,调用者必须等待 I/O 完成。否则,它将返回 0,表示内存已处于平衡状态,或者它能够休眠,直到脏内存量恢复平衡。

void balance_dirty_pages_ratelimited(struct address_space *mapping)

平衡脏内存状态。

参数

struct address_space *mapping

已被弄脏的 address_space。

描述

正在弄脏内存的进程应在此处为每个新弄脏的页面调用一次。该函数将定期检查系统的脏状态,并在需要时启动回写。

一旦超出脏内存限制,我们就会大大降低速率限制,以防止单个进程每个都超出限制(ratelimit_pages)。

void tag_pages_for_writeback(struct address_space *mapping, pgoff_t start, pgoff_t end)

标记要由回写写入的页面

参数

struct address_space *mapping

要写入的地址空间结构

pgoff_t start

起始页面索引

pgoff_t end

结束页面索引(包括)

描述

此函数扫描从 startend(包括)的页面范围,并使用特殊的 TOWRITE 标记来标记所有已设置 DIRTY 标记的页面。然后,调用者可以使用 TOWRITE 标记来标识有资格进行回写的页面。此机制用于避免进程不断在文件中创建新的脏页导致的回写死锁(因此此函数必须快速运行,以便它可以比脏化进程创建页面的速度更快地标记页面)。

struct folio *writeback_iter(struct address_space *mapping, struct writeback_control *wbc, struct folio *folio, int *error)

迭代映射的 folio 以进行回写

参数

struct address_space *mapping

要写入的地址空间结构

struct writeback_control *wbc

回写上下文

struct folio *folio

先前迭代的 folio(NULL 表示开始)

int *error

回写错误的输入输出指针(见下文)

描述

此函数返回 wbcmapping 上描述的回写操作的下一个 folio,应在 ->writepages 实现中的 while 循环中调用。

要开始回写操作,请在 folio 参数中传入 NULL,对于每个后续迭代,应将先前返回的 folio 传回。

如果在 writeback_iter() 循环中每个 folio 的回写中出现错误,则应将 error 设置为错误值。

一旦 wbc 中描述的回写完成,此函数将返回 NULL,并且如果任何迭代中出现错误,则将其恢复为 error

注意

调用者不应使用 break 或 goto 手动跳出循环,而必须持续调用 writeback_iter(),直到它返回 NULL

返回

要写入的 folio,如果循环结束则为 NULL

int write_cache_pages(struct address_space *mapping, struct writeback_control *wbc, writepage_t writepage, void *data)

遍历给定地址空间的脏页列表,并写入所有脏页。

参数

struct address_space *mapping

要写入的地址空间结构

struct writeback_control *wbc

*wbc->nr_to_write 中减去已写入页面的数量

writepage_t writepage

为每个页面调用的函数

void *data

传递给 writepage 函数的数据

返回

成功时返回 0,否则返回负错误代码

注意

请改用 writeback_iter()

bool filemap_dirty_folio(struct address_space *mapping, struct folio *folio)

为不使用 buffer_heads 的文件系统将 folio 标记为脏。

参数

struct address_space *mapping

此 folio 所属的地址空间。

struct folio *folio

要标记为脏的 Folio。

描述

不使用 buffer heads 的文件系统应从其 dirty_folio 地址空间操作中调用此函数。它会忽略 folio_get_private() 的内容,因此如果文件系统将单个块标记为脏,则文件系统应自行处理。

当单个缓冲区被弄脏时,有时也会被使用 buffer_heads 的文件系统使用:我们希望在这种情况下将 folio 设置为脏,而不是所有的缓冲区。这是一种“自下而上”的弄脏方式,而 block_dirty_folio() 是一种“自上而下”的弄脏方式。

调用者必须确保这不会与截断竞争。大多数情况下只需持有 folio 锁,但例如 zap_pte_range() 会在映射 folio 并持有 pte 锁的情况下调用,这也会阻止截断。

bool folio_redirty_for_writepage(struct writeback_control *wbc, struct folio *folio)

拒绝写入脏 folio。

参数

struct writeback_control *wbc

写回控制。

struct folio *folio

folio。

描述

当 writepage 实现因某种原因决定不写入 folio 时,应调用此函数,解锁 folio 并返回 0。

返回

如果我们将 folio 重新标记为脏,则为 True。如果其他人先将其弄脏,则为 False。

bool folio_mark_dirty(struct folio *folio)

将 folio 标记为已修改。

参数

struct folio *folio

folio。

描述

此函数运行时可能不会截断 folio。持有 folio 锁足以防止截断,但某些调用者无法获取休眠锁。这些调用者改为持有包含此 folio 中至少一个页面的页表锁。截断将阻塞页表锁,因为它会在从其映射中删除 folio 之前取消映射页面。

返回

如果 folio 是新弄脏的,则为 True,如果它已经是脏的,则为 false。

void folio_wait_writeback(struct folio *folio)

等待 folio 完成写回。

参数

struct folio *folio

要等待的 folio。

描述

如果当前正在将 folio 写回到存储,请等待 I/O 完成。

上下文

睡眠。必须在进程上下文中调用,并且不持有自旋锁。调用者应持有对 folio 的引用。如果 folio 未锁定,则写回可能会在写回完成后再次开始。

int folio_wait_writeback_killable(struct folio *folio)

等待 folio 完成写回。

参数

struct folio *folio

要等待的 folio。

描述

如果当前正在将 folio 写回到存储,请等待 I/O 完成或出现致命信号。

上下文

睡眠。必须在进程上下文中调用,并且不持有自旋锁。调用者应持有对 folio 的引用。如果 folio 未锁定,则写回可能会在写回完成后再次开始。

返回

成功时返回 0,如果在等待时收到致命信号,则返回 -EINTR。

void folio_wait_stable(struct folio *folio)

如有必要,等待写回完成。

参数

struct folio *folio

要等待的 folio。

描述

此函数确定给定 folio 是否与需要在写回期间保持 folio 内容稳定的后备设备相关。如果是,则它将等待任何挂起的写回完成。

上下文

睡眠。必须在进程上下文中调用,并且不持有自旋锁。调用者应持有对 folio 的引用。如果 folio 未锁定,则写回可能会在写回完成后再次开始。

截断

void folio_invalidate(struct folio *folio, size_t offset, size_t length)

使 folio 的一部分或全部失效。

参数

struct folio *folio

受影响的 folio。

size_t offset

要失效的范围的起始位置

size_t length

要失效的范围的长度

描述

当 folio 的全部或部分因截断操作而失效时,会调用 folio_invalidate()

folio_invalidate() 不需要释放所有缓冲区,但它必须确保没有脏缓冲区留在 offset 之外,并且没有针对截断点之外的任何块进行 I/O 操作。因为调用者即将释放(并可能重用)磁盘上的这些块。

void truncate_inode_pages_range(struct address_space *mapping, loff_t lstart, loff_t lend)

截断由起始和结束字节偏移量指定的页面范围

参数

struct address_space *mapping

要截断的映射

loff_t lstart

要截断的起始偏移量

loff_t lend

要截断到的偏移量(包含)

描述

截断页面缓存,删除指定偏移量之间的页面(如果 lstart 或 lend + 1 未与页面对齐,则清零部分页面)。

截断需要两个过程 - 第一个过程是非阻塞的。它不会阻塞页面锁,也不会阻塞写回。第二个过程将等待。这是为了防止在受影响区域中尽可能多的 I/O。第一个过程将删除大多数页面,因此第二个过程的搜索成本较低。

我们将缓存热提示传递给页面释放代码。即使映射很大,最终页面也可能是最近触摸的,并且释放以升序文件偏移量顺序发生。

请注意,由于 ->invalidate_folio() 接受要失效的范围,因此 truncate_inode_pages_range 能够处理 lend + 1 未正确与页面对齐的情况。

void truncate_inode_pages(struct address_space *mapping, loff_t lstart)

从偏移量截断所有页面

参数

struct address_space *mapping

要截断的映射

loff_t lstart

要截断的起始偏移量

描述

在 inode->i_rwsem 和 mapping->invalidate_lock 下调用(并由它们序列化)。

注意

当此函数返回时,在指定范围内可能存在正在删除过程中的页(在 __filemap_remove_folio() 内部)。因此,即使在截断整个映射之后,当此函数返回时,mapping->nrpages 也可能非零。

void truncate_inode_pages_final(struct address_space *mapping)

在 inode 消亡之前截断所有

参数

struct address_space *mapping

要截断的映射

描述

在 inode->i_rwsem 下调用(并由此序列化)。

文件系统必须在 .evict_inode 路径中使用此函数,以通知 VM 这是最终截断,并且 inode 即将消失。

unsigned long invalidate_mapping_pages(struct address_space *mapping, pgoff_t start, pgoff_t end)

使一个 inode 的所有干净、未锁定缓存失效

参数

struct address_space *mapping

保存要失效的缓存的 address_space

pgoff_t start

要失效的起始偏移量

pgoff_t end

要失效的结束偏移量(包括)

描述

此函数删除干净、未映射和未锁定的页以及影子条目。它不会阻塞 IO 活动。

如果要删除一个 inode 的所有页,无论其使用情况和回写状态如何,请使用 truncate_inode_pages()

返回

内容已失效的索引数量

int invalidate_inode_pages2_range(struct address_space *mapping, pgoff_t start, pgoff_t end)

从 address_space 中删除一系列页

参数

struct address_space *mapping

address_space

pgoff_t start

要失效的起始页偏移量

pgoff_t end

要失效的结束页偏移量(包括)

描述

在失效之前,所有发现已映射到页表的页都会被取消映射。

返回

如果任何页无法失效,则返回 -EBUSY。

int invalidate_inode_pages2(struct address_space *mapping)

从 address_space 中删除所有页

参数

struct address_space *mapping

address_space

描述

在失效之前,所有发现已映射到页表的页都会被取消映射。

返回

如果任何页无法失效,则返回 -EBUSY。

void truncate_pagecache(struct inode *inode, loff_t newsize)

取消映射并删除已截断的页缓存

参数

struct inode *inode

inode

loff_t newsize

新的文件大小

描述

在调用 truncate_pagecache 之前,必须先写入 inode 的新 i_size。

此函数通常应在文件系统释放与释放范围相关的资源(例如,取消分配块)之前调用。这样,页缓存将始终在逻辑上与磁盘格式保持一致,并且文件系统不必处理诸如为已经取消分配了底层块的页调用 writepage 之类的情况。

void truncate_setsize(struct inode *inode, loff_t newsize)

更新 inode 和页缓存以适应新的文件大小

参数

struct inode *inode

inode

loff_t newsize

新的文件大小

描述

truncate_setsize 会更新 i_size 并执行页缓存截断(如果需要)到 newsize。通常会在传递 ATTR_SIZE 时从文件系统的 setattr 函数调用此函数。

必须在序列化截断和写入的锁(通常是 i_rwsem,但例如 xfs 使用不同的锁)下调用,并且在执行所有文件系统特定的块截断之前调用。

void pagecache_isize_extended(struct inode *inode, loff_t from, loff_t to)

在扩展 i_size 后更新页缓存

参数

struct inode *inode

i_size 已扩展的 inode

loff_t from

原始 inode 大小

loff_t to

新的 inode 大小

描述

处理 inode 大小的扩展,该扩展由扩展截断或在当前 i_size 之后开始写入引起。我们将跨越当前 i_size 的页标记为 RO,以便在第一次写入访问该页时调用 page_mkwrite()。在 i_size 更改后,文件系统将在用户通过 mmap 写入页之前更新其每块信息。

必须在 i_size 更新后调用此函数,以便在我们解锁 folio 后出现的缺页故障已经看到新的 i_size。必须在仍持有 i_rwsem 的情况下调用此函数 - 这不仅确保 i_size 稳定,而且还确保在准备好将 mmap 写入存储到新的 inode 大小之前,用户空间无法观察到新的 i_size 值。

void truncate_pagecache_range(struct inode *inode, loff_t lstart, loff_t lend)

取消映射并删除已打孔的页缓存

参数

struct inode *inode

inode

loff_t lstart

孔的起始偏移量

loff_t lend

孔的最后一个字节的偏移量

描述

此函数通常应在文件系统释放与释放范围相关的资源(例如,取消分配块)之前调用。这样,页缓存将始终在逻辑上与磁盘格式保持一致,并且文件系统不必处理诸如为已经取消分配了底层块的页调用 writepage 之类的情况。

void filemap_set_wb_err(struct address_space *mapping, int err)

在 address_space 上设置回写错误

参数

struct address_space *mapping

要在其中设置回写错误的映射

int err

要在映射中设置的错误

描述

当回写以某种方式失败时,我们必须记录该错误,以便在调用 fsync 等时通知用户空间。我们努力报告在发生错误时打开的任何文件上的错误。一些内部调用者还需要知道何时发生回写错误。

当发生回写错误时,大多数文件系统将需要调用 filemap_set_wb_err 以在映射中记录错误,以便在对文件调用 fsync 时自动报告该错误。

int filemap_check_wb_err(struct address_space *mapping, errseq_t since)

自采样标记以来是否发生了错误?

参数

struct address_space *mapping

要检查回写错误的映射

errseq_t since

先前采样的 errseq_t

描述

从映射中获取 errseq_t 值,并查看它是否自给定值采样后发生了变化。

如果发生了变化,则报告最新的错误集,否则返回 0。

errseq_t filemap_sample_wb_err(struct address_space *mapping)

采样当前的 errseq_t 以便测试后续的错误

参数

struct address_space *mapping

要采样的映射

描述

回写错误总是相对于过去特定的采样点报告的。此函数提供这些采样点。

errseq_t file_sample_sb_err(struct file *file)

采样当前的 errseq_t 以便测试后续的错误

参数

struct file *file

要采样的文件指针

描述

获取给定 struct file 的最新超级块级别的 errseq_t 值。

void mapping_set_error(struct address_space *mapping, int error)

在 address_space 中记录回写错误

参数

struct address_space *mapping

应该设置错误的映射

int error

要在映射中设置的错误

描述

当回写以某种方式失败时,我们必须记录该错误,以便在调用 fsync 等时通知用户空间。我们努力报告在发生错误时打开的任何文件上的错误。一些内部调用者还需要知道何时发生回写错误。

当发生回写错误时,大多数文件系统会希望调用 mapping_set_error 在映射中记录错误,以便在应用程序调用 fsync(2) 时可以报告该错误。

void mapping_set_large_folios(struct address_space *mapping)

指示该文件支持大型页面。

参数

struct address_space *mapping

文件的地址空间。

描述

文件系统应在其 inode 构造函数中调用此函数,以指示 VFS 可以使用大型页面来缓存文件的内容。

上下文

不应在 inode 处于活动状态时调用此函数,因为它不是原子的。

pgoff_t mapping_align_index(struct address_space *mapping, pgoff_t index)

为此映射对齐索引。

参数

struct address_space *mapping

address_space。

pgoff_t index

页面索引。

描述

页面的索引必须是自然对齐的。如果要向页面缓存添加新页面并且需要知道要给它什么索引,请调用此函数。

struct address_space *folio_file_mapping(struct folio *folio)

查找此页面所属的映射。

参数

struct folio *folio

folio。

描述

对于位于页面缓存中的页面,返回此页面所属的映射。交换缓存中的页面返回存储数据的交换文件或交换设备的映射。这与 folio_mapping() 返回的映射不同。使用它的唯一原因是,如果像 NFS 一样,你从 ->activate_swapfile 返回 0。

不要为不在页面缓存或交换缓存中的页面调用此函数。

struct address_space *folio_flush_mapping(struct folio *folio)

查找此页面所属的文件映射。

参数

struct folio *folio

folio。

描述

对于位于页面缓存中的页面,返回此页面所属的映射。匿名页面返回 NULL,即使它们在交换缓存中也是如此。其他类型的页面也返回 NULL。

这仅由架构缓存刷新代码使用。如果你不是在编写缓存刷新代码,你需要 folio_mapping()folio_file_mapping()

struct inode *folio_inode(struct folio *folio)

获取此页面的宿主 inode。

参数

struct folio *folio

folio。

描述

对于位于页面缓存中的页面,返回此页面所属的 inode。

不要为不在页面缓存中的页面调用此函数。

void folio_attach_private(struct folio *folio, void *data)

将私有数据附加到页面。

参数

struct folio *folio

要附加数据的页面。

void *data

要附加到页面的数据。

描述

将私有数据附加到页面会增加页面的引用计数。必须先分离数据,然后才能释放页面。

void *folio_change_private(struct folio *folio, void *data)

更改页面上的私有数据。

参数

struct folio *folio

要更改数据的页面。

void *data

要在页面上设置的数据。

描述

更改附加到页面的私有数据,并返回旧数据。页面以前必须附加过数据,并且在释放页面之前必须先分离数据。

返回

先前附加到页面的数据。

void *folio_detach_private(struct folio *folio)

从页面分离私有数据。

参数

struct folio *folio

要从中分离数据的页面。

描述

删除先前附加到页面的数据,并减少页面上的引用计数。

返回

附加到页面的数据。

type fgf_t

用于从页面缓存获取页面的标志。

描述

页面缓存的大多数用户不需要使用这些标志; 有一些便利函数,例如 filemap_get_folio()filemap_lock_folio()。 对于需要更精确地控制如何处理页面的用户,可以使用这些标志来控制 __filemap_get_folio()

  • FGP_ACCESSED - 将页面标记为已访问。

  • FGP_LOCK - 返回锁定的页面。

  • FGP_CREAT - 如果不存在页面,则分配一个新页面,将其添加到页面缓存和 VM 的 LRU 列表中。 返回锁定的页面。

  • FGP_FOR_MMAP - 如果页帧已经在缓存中,调用者希望自行处理加锁流程。如果页帧是新分配的,则在返回前解锁,以便调用者可以执行相同的流程。

  • FGP_WRITE - 页帧将被调用者写入。

  • FGP_NOFS - __GFP_FS 将在 gfp 中被清除。

  • FGP_NOWAIT - 不要阻塞等待页帧锁。

  • FGP_STABLE - 等待页帧稳定(写回完成)。

  • FGP_WRITEBEGIN - 在文件系统 write_begin() 实现中使用的标志。

fgf_t fgf_set_order(size_t size)

在 fgf_t 标志中编码长度。

参数

size_t size

建议创建的页帧大小。

描述

__filemap_get_folio()的调用者可以使用此方法来建议创建的页帧的首选大小。如果索引处已经存在页帧,则无论其大小如何,都将返回该页帧。如果新创建页帧,则由于对齐约束、内存压力或附近索引处其他页帧的存在,它可能与请求的大小不同。

struct folio *filemap_get_folio(struct address_space *mapping, pgoff_t index)

查找并获取一个页帧。

参数

struct address_space *mapping

要搜索的 address_space。

pgoff_t index

页面索引。

描述

mappingindex 处查找页缓存项。如果存在页帧,则返回该页帧,并增加其引用计数。

返回

如果此索引的缓存中没有页帧,则返回一个页帧或 ERR_PTR(-ENOENT)。不会返回影子、交换或 DAX 条目。

struct folio *filemap_lock_folio(struct address_space *mapping, pgoff_t index)

查找并锁定一个页帧。

参数

struct address_space *mapping

要搜索的 address_space。

pgoff_t index

页面索引。

描述

mappingindex 处查找页缓存项。如果存在页帧,则返回该页帧,并增加其引用计数,且页帧被锁定。

上下文

可能睡眠。

返回

如果此索引的缓存中没有页帧,则返回一个页帧或 ERR_PTR(-ENOENT)。不会返回影子、交换或 DAX 条目。

struct folio *filemap_grab_folio(struct address_space *mapping, pgoff_t index)

从页缓存中获取一个页帧

参数

struct address_space *mapping

要搜索的地址空间

pgoff_t index

页索引

描述

mappingindex 处查找页缓存项。如果未找到页帧,则创建一个新的页帧。该页帧被锁定,标记为已访问,然后返回。

返回

找到或创建的页帧。如果没有找到页帧并且创建页帧失败,则返回 ERR_PTR(-ENOMEM)。

struct page *find_get_page(struct address_space *mapping, pgoff_t offset)

查找并获取一个页引用

参数

struct address_space *mapping

要搜索的 address_space

pgoff_t offset

页面索引

描述

mappingoffset 处查找页缓存槽。如果存在页缓存页,则返回该页缓存页,并增加其引用计数。

否则,返回 NULL

struct page *find_lock_page(struct address_space *mapping, pgoff_t index)

定位、固定并锁定一个页缓存页

参数

struct address_space *mapping

要搜索的 address_space

pgoff_t index

页面索引

描述

mappingindex 处查找页缓存项。如果存在页缓存页,则返回该页缓存页,并增加其引用计数,且页缓存页被锁定。

上下文

可能睡眠。

返回

如果此索引的缓存中没有页,则返回一个 struct page 或 NULL

struct page *find_or_create_page(struct address_space *mapping, pgoff_t index, gfp_t gfp_mask)

定位或添加一个页缓存页

参数

struct address_space *mapping

页面的地址空间

pgoff_t index

页在映射中的索引

gfp_t gfp_mask

页分配模式

描述

mappingoffset 处查找页缓存槽。如果存在页缓存页,则返回该页缓存页,并增加其引用计数,且页缓存页被锁定。

如果该页不存在,则使用 gfp_mask 分配一个新页,并将其添加到页缓存和 VM 的 LRU 列表中。返回该页,并增加其引用计数,且该页被锁定。

在内存耗尽时,返回 NULL

find_or_create_page()可能会休眠,即使 gfp_flags 指定原子分配!

struct page *grab_cache_page_nowait(struct address_space *mapping, pgoff_t index)

返回给定缓存中给定索引处的锁定页

参数

struct address_space *mapping

目标地址空间

pgoff_t index

页面索引

描述

与 grab_cache_page() 相同,但如果页不可用,则不等待。这适用于投机性数据生成器,如果无法获取该页,则可以重新生成数据。在持有另一个页的锁时,此例程应该可以安全调用。

在分配页面时清除 __GFP_FS,以避免递归进入 fs 以及与调用者锁定的页死锁。

pgoff_t folio_index(struct folio *folio)

页帧的文件索引。

参数

struct folio *folio

folio。

描述

对于在页缓存或交换缓存中的页帧,返回它所属的地址空间中的索引。如果您知道该页肯定在页缓存中,则可以直接查看页帧的索引。

返回

页帧在其文件中的索引(以页为单位的偏移量)。

pgoff_t folio_next_index(struct folio *folio)

获取下一个 folio 的索引。

参数

struct folio *folio

当前的 folio。

返回

文件中紧随此 folio 的 folio 的索引。

struct page *folio_file_page(struct folio *folio, pgoff_t index)

获取特定索引的页。

参数

struct folio *folio

包含此索引的 folio。

pgoff_t index

我们想要查找的索引。

描述

有时在页缓存中查找 folio 后,我们需要获取特定索引的页(例如,页错误)。

返回

包含此索引的文件数据的页。

bool folio_contains(struct folio *folio, pgoff_t index)

此 folio 是否包含此索引?

参数

struct folio *folio

folio。

pgoff_t index

文件中的页索引。

上下文

调用者应锁定该页,以防止(例如)shmem 在页缓存和交换缓存之间移动该页,并在操作过程中更改其索引。

返回

true 或 false。

pgoff_t page_pgoff(const struct folio *folio, const struct page *page)

计算此页的逻辑页偏移量。

参数

const struct folio *folio

包含此页的 folio。

const struct page *page

我们需要获取偏移量的页。

描述

对于文件页,这是从文件开头以 PAGE_SIZE 为单位的偏移量。对于匿名页,这是从 anon_vma 开头以 PAGE_SIZE 为单位的偏移量。这将为 KSM 页返回无意义的值。

上下文

调用者必须具有对 folio 的引用,否则阻止其被拆分或释放。

返回

以 PAGE_SIZE 为单位的偏移量。

loff_t folio_pos(struct folio *folio)

返回此 folio 在其文件中的字节位置。

参数

struct folio *folio

folio。

bool folio_trylock(struct folio *folio)

尝试锁定 folio。

参数

struct folio *folio

要尝试锁定的 folio。

描述

有时不希望等待 folio 被解锁(例如,当锁的获取顺序错误时,或者如果按顺序处理一批 folio 比按顺序处理它们更重要)。 通常 folio_lock() 是要调用的正确函数。

上下文

任何上下文。

返回

是否成功获取锁。

void folio_lock(struct folio *folio)

锁定此 folio。

参数

struct folio *folio

要锁定的 folio。

描述

folio 锁保护许多内容,可能比它应该保护的更多。它主要在将 folio 从其后备文件或交换空间更新时持有。当从地址空间截断 folio 时也会持有该锁,因此持有锁足以保持 folio->mapping 稳定。

folio 锁还在 write() 修改页面以提供 POSIX 原子性保证时持有(只要写入不跨越页边界)。 对 folio 中数据的其他修改不会持有 folio 锁,并且可能会与写入发生竞争,例如 DMA 和对映射页的存储。

上下文

可能会睡眠。 如果需要获取两个或多个 folio 的锁,则它们必须按照索引的升序排列(如果它们在同一个地址空间中)。如果它们在不同的地址空间中,则首先获取属于内存中地址最低的地址空间的 folio 的锁。

void lock_page(struct page *page)

锁定包含此页的 folio。

参数

struct page *page

要锁定的页。

描述

有关锁保护内容的说明,请参阅 folio_lock()。 这是一个遗留函数,新代码可能应该改用 folio_lock()

上下文

可能会睡眠。同一个 folio 中的页共享一个锁,因此不要尝试锁定共享一个 folio 的两个页。

int folio_lock_killable(struct folio *folio)

锁定此 folio,可被致命信号中断。

参数

struct folio *folio

要锁定的 folio。

描述

尝试锁定 folio,类似于 folio_lock(),只是获取锁的睡眠可被致命信号中断。

上下文

可能会睡眠;请参阅 folio_lock()

返回

如果获取了锁,则为 0;如果收到致命信号,则为 -EINTR。

bool filemap_range_needs_writeback(struct address_space *mapping, loff_t start_byte, loff_t end_byte)

检查范围是否可能需要回写

参数

struct address_space *mapping

要在其中检查的地址空间

loff_t start_byte

范围开始的字节偏移量

loff_t end_byte

范围结束的字节偏移量(包括)

描述

查找提供的范围内至少一个页,通常用于检查此范围内的直接写入是否会触发回写。 与 IOCB_NOWAIT 一起由 O_DIRECT 读取/写入使用,以查看调用者是否需要在继续操作之前执行 filemap_write_and_wait_range()

返回

如果调用者应在对该范围内的页执行 O_DIRECT 之前执行 filemap_write_and_wait_range(),则为 true,否则为 false

struct readahead_control

描述预读请求。

定义:

struct readahead_control {
    struct file *file;
    struct address_space *mapping;
    struct file_ra_state *ra;
};

成员

文件

该文件主要由网络文件系统用于身份验证。如果由文件系统内部调用,则可能为 NULL。

映射

预读此文件系统对象。

ra

文件预读状态。可能为 NULL。

描述

预读请求用于连续的页。实现 ->readahead 方法的文件系统应在循环中调用 readahead_page()readahead_page_batch(),并尝试针对请求中的每个页启动 I/O。

此结构中的大多数字段是私有的,应由下面的函数访问。

void page_cache_sync_readahead(struct address_space *mapping, struct file_ra_state *ra, struct file *file, pgoff_t index, unsigned long req_count)

通用文件预读

参数

struct address_space *mapping

保存页面缓存和 I/O 向量的 address_space。

struct file_ra_state *ra

保存预读状态的 file_ra_state。

struct file *file

文件系统用于身份验证。

pgoff_t index

要读取的第一个页面的索引。

unsigned long req_count

调用方读取的总页数。

描述

当发生缓存未命中时,应调用 page_cache_sync_readahead():它将提交读取。如果访问模式表明可以提高性能,预读逻辑可能会在读取请求中附加更多页面。

void page_cache_async_readahead(struct address_space *mapping, struct file_ra_state *ra, struct file *file, struct folio *folio, unsigned long req_count)

为标记的页面进行文件预读

参数

struct address_space *mapping

保存页面缓存和 I/O 向量的 address_space。

struct file_ra_state *ra

保存预读状态的 file_ra_state。

struct file *file

文件系统用于身份验证。

struct folio *folio

触发预读调用的 folio。

unsigned long req_count

调用方读取的总页数。

描述

当使用标记为 PageReadahead 的页面时,应调用 page_cache_async_readahead();这是一个标记,表明应用程序已用完足够的预读窗口,我们应该开始拉取更多页面。

struct page *readahead_page(struct readahead_control *ractl)

获取要读取的下一个页面。

参数

struct readahead_control *ractl

当前的预读请求。

上下文

该页面已锁定并具有提升的引用计数。调用方应在将页面提交给 I/O 后减少引用计数,并在该页面的所有 I/O 完成后解锁该页面。

返回

指向下一个页面的指针,如果完成,则为 NULL

struct folio *readahead_folio(struct readahead_control *ractl)

获取要读取的下一个 folio。

参数

struct readahead_control *ractl

当前的预读请求。

上下文

该 folio 已锁定。调用方应在完成对该 folio 的所有 I/O 后解锁该 folio。

返回

指向下一个 folio 的指针,如果完成,则为 NULL

readahead_page_batch

readahead_page_batch (rac, array)

获取一批要读取的页面。

参数

rac

当前的预读请求。

array

指向 struct page 的指针数组。

上下文

这些页面已锁定并具有提升的引用计数。调用方应在将页面提交给 I/O 后减少引用计数,并在该页面的所有 I/O 完成后解锁该页面。

返回

数组中放置的页面数。0 表示请求已完成。

loff_t readahead_pos(struct readahead_control *rac)

此预读请求在文件中的字节偏移量。

参数

struct readahead_control *rac

预读请求。

size_t readahead_length(struct readahead_control *rac)

此预读请求中的字节数。

参数

struct readahead_control *rac

预读请求。

pgoff_t readahead_index(struct readahead_control *rac)

此预读请求中第一个页面的索引。

参数

struct readahead_control *rac

预读请求。

unsigned int readahead_count(struct readahead_control *rac)

此预读请求中的页面数。

参数

struct readahead_control *rac

预读请求。

size_t readahead_batch_length(struct readahead_control *rac)

当前批次中的字节数。

参数

struct readahead_control *rac

预读请求。

ssize_t folio_mkwrite_check_truncate(struct folio *folio, struct inode *inode)

检查 folio 是否被截断

参数

struct folio *folio

要检查的 folio

struct inode *inode

用于检查 folio 的 inode

返回

folio 中到 EOF 的字节数,如果 folio 被截断,则返回 -EFAULT。

int page_mkwrite_check_truncate(struct page *page, struct inode *inode)

检查页面是否被截断

参数

struct page *page

要检查的页面

struct inode *inode

用于检查页面的 inode

描述

返回页面中到 EOF 的字节数,如果页面被截断,则返回 -EFAULT。

unsigned int i_blocks_per_folio(struct inode *inode, struct folio *folio)

此 folio 中可以容纳多少个块。

参数

struct inode *inode

包含块的 inode。

struct folio *folio

folio。

描述

如果块大小大于此 folio 的大小,则返回零。

上下文

调用方应持有 folio 的引用计数,以防止其被拆分。

返回

此 folio 覆盖的文件系统块数。

内存池

void mempool_exit(mempool_t *pool)

退出使用 mempool_init() 初始化的内存池

参数

mempool_t *pool

指向使用 mempool_init() 初始化的内存池的指针。

描述

释放 poolpool 本身中所有保留的元素。此函数仅在 free_fn() 函数休眠时才会休眠。

可以在已清零但未初始化的内存池上调用(即,使用 kzalloc() 分配的内存池)。

void mempool_destroy(mempool_t *pool)

释放一个内存池

参数

mempool_t *pool

指向通过 mempool_create() 分配的内存池的指针。

描述

释放 poolpool 本身中所有保留的元素。此函数仅在 free_fn() 函数休眠时才会休眠。

int mempool_resize(mempool_t *pool, int new_min_nr)

调整现有内存池的大小

参数

mempool_t *pool

指向通过 mempool_create() 分配的内存池的指针。

int new_min_nr

保证为此池分配的最小元素数量。

描述

此函数会缩小/扩大池的大小。在扩大的情况下,不能保证池会立即扩大到新的大小,但是新的 mempool_free() 调用会重新填充它。此函数可能会休眠。

请注意,调用方必须保证在运行此函数时不会调用 mempool_destroy。mempool_alloc() 和 mempool_free() 可能会在执行此函数时被调用(例如,从 IRQ 上下文中调用)。

返回

成功返回 0,否则返回负错误代码。

void *mempool_alloc_preallocated(mempool_t *pool)

从属于特定内存池的预分配元素中分配一个元素

参数

mempool_t *pool

指向通过 mempool_create() 分配的内存池的指针。

描述

此函数类似于 mempool_alloc,但它仅尝试从预分配的元素中分配一个元素。如果没有可用的预分配元素,则它不会休眠并立即返回。

返回

指向已分配元素的指针,如果没有任何元素可用,则返回 NULL

void mempool_free(void *element, mempool_t *pool)

将元素返回到池中。

参数

void *element

池元素指针。

mempool_t *pool

指向通过 mempool_create() 分配的内存池的指针。

描述

此函数仅在 free_fn() 函数休眠时才休眠。

DMA 池

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

创建一个用于 dma 的一致性内存块池。

参数

const char *name

池的名称,用于诊断

struct device *dev

将执行 DMA 的设备

size_t size

此池中块的大小。

size_t align

块的对齐要求;必须是 2 的幂

size_t boundary

返回的块不会跨越此 2 的幂边界

上下文

不是 in_interrupt()

描述

给定这些池之一,可以使用 dma_pool_alloc() 分配内存。此类内存都将具有“一致”的 DMA 映射,设备及其驱动程序可以访问,而无需使用缓存刷新原语。由于对齐,分配的块的实际大小可能大于请求的大小。

如果 boundary 非零,则从 dma_pool_alloc() 返回的对象不会跨越该大小边界。这对于对单个 DMA 传输有寻址限制的设备非常有用,例如不跨越 4K 字节的边界。

返回

具有请求特征的 dma 分配池,如果无法创建,则返回 NULL

void dma_pool_destroy(struct dma_pool *pool)

销毁 dma 内存块池。

参数

struct dma_pool *pool

将被销毁的 dma 池

上下文

!in_interrupt()

描述

调用方保证不再使用池中的任何内存,并且在此调用之后没有任何内容会尝试使用该池。

void *dma_pool_alloc(struct dma_pool *pool, gfp_t mem_flags, dma_addr_t *handle)

获取一致内存块

参数

struct dma_pool *pool

将产生块的 dma 池

gfp_t mem_flags

GFP_* 位掩码

dma_addr_t *handle

指向块的 dma 地址的指针

返回

当前未使用块的内核虚拟地址,并通过 handle 报告其 dma 地址。如果无法分配此类内存块,则返回 NULL

void dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t dma)

将块放回 dma 池中

参数

struct dma_pool *pool

保存块的 dma 池

void *vaddr

块的虚拟地址

dma_addr_t dma

块的 dma 地址

描述

调用方承诺,除非先重新分配,否则设备或驱动程序都不会再次触及此块。

struct dma_pool *dmam_pool_create(const char *name, struct device *dev, size_t size, size_t align, size_t allocation)

托管的 dma_pool_create()

参数

const char *name

池的名称,用于诊断

struct device *dev

将执行 DMA 的设备

size_t size

此池中块的大小。

size_t align

块的对齐要求;必须是 2 的幂

size_t allocation

返回的块不会跨越此边界(或零)

描述

托管的 dma_pool_create()。使用此函数创建的 DMA 池在驱动程序分离时会自动销毁。

返回

具有请求特征的托管 dma 分配池,如果无法创建,则返回 NULL

void dmam_pool_destroy(struct dma_pool *pool)

托管的 dma_pool_destroy()

参数

struct dma_pool *pool

将被销毁的 dma 池

描述

托管的 dma_pool_destroy()

更多内存管理函数

void zap_vma_ptes(struct vm_area_struct *vma, unsigned long address, unsigned long size)

移除映射到 vma 的 pte。

参数

struct vm_area_struct *vma

vm_area_struct,持有要清除的 pte。

unsigned long address

要清除的页面的起始地址。

unsigned long size

要清除的字节数。

描述

此函数仅取消映射分配给 VM_PFNMAP vma 的 pte。

整个地址范围必须完全包含在 vma 内。

int vm_insert_pages(struct vm_area_struct *vma, unsigned long addr, struct page **pages, unsigned long *num)

将多个页面插入到用户 vma 中,批量处理 pmd 锁。

参数

struct vm_area_struct *vma

要映射到的用户 vma。

unsigned long addr

这些页面的目标起始用户地址。

struct page **pages

源内核页面。

unsigned long *num

输入:要映射的页面数。输出:*没有* 映射的页面数。(0 表示所有页面都已成功映射)。

描述

当插入多个页面时,优先选择 vm_insert_page()

如果发生错误,我们可能已映射了所提供页面的子集。调用者有责任处理这种情况。

vm_insert_page() 相同的限制适用。

int vm_insert_page(struct vm_area_struct *vma, unsigned long addr, struct page *page)

将单个页面插入到用户 vma。

参数

struct vm_area_struct *vma

要映射到的用户 vma。

unsigned long addr

此页面的目标用户地址。

struct page *page

源内核页面。

描述

这允许驱动程序将它们已分配的单个页面插入到用户 vma 中。在某些 vma 中支持 zeropage,请参见 vm_mixed_zeropage_allowed()。

该页面必须是一个不错的、干净的_单个_内核分配。如果您分配一个复合页面,则需要将其标记为 (__GFP_COMP),或者手动拆分页面 (请参见 split_page())。

注意!传统上,这是使用“remap_pfn_range()”完成的,它接受任意的页面保护参数。这不允许这样做。您的 vma 保护必须正确设置,这意味着如果您需要共享可写映射,最好请求共享可写映射!

页面不需要保留。

通常,此函数在 mm->mmap_lock 写锁下从 f_op->mmap() 处理程序调用,因此它可以更改 vma->vm_flags。如果调用者希望从其他地方(例如,从页面错误处理程序)调用此函数,则必须在 vma 上设置 VM_MIXEDMAP。

返回

成功返回 0,否则返回负错误代码。

int vm_map_pages(struct vm_area_struct *vma, struct page **pages, unsigned long num)

映射以非零偏移量开头的内核页面范围。

参数

struct vm_area_struct *vma

要映射到的用户 vma。

struct page **pages

指向源内核页面数组的指针。

unsigned long num

页面数组中的页面数。

描述

映射一个由 num 个页面组成的对象,以满足用户请求的 vm_pgoff。

如果我们无法将任何页面插入到 vma 中,该函数将立即返回,保留任何先前插入的页面。来自 mmap 处理程序的调用者可以立即返回错误,因为它们的调用者将销毁 vma,从而删除任何成功插入的页面。其他调用者应自行安排调用 unmap_region()。

上下文

进程上下文。由 mmap 处理程序调用。

返回

成功时返回 0,否则返回错误代码。

int vm_map_pages_zero(struct vm_area_struct *vma, struct page **pages, unsigned long num)

映射以零偏移量开头的内核页面范围。

参数

struct vm_area_struct *vma

要映射到的用户 vma。

struct page **pages

指向源内核页面数组的指针。

unsigned long num

页面数组中的页面数。

描述

vm_map_pages() 类似,但它明确将偏移量设置为 0。此函数适用于未考虑 vm_pgoff 的驱动程序。

上下文

进程上下文。由 mmap 处理程序调用。

返回

成功时返回 0,否则返回错误代码。

vm_fault_t vmf_insert_pfn_prot(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn, pgprot_t pgprot)

将单个 pfn 插入到具有指定 pgprot 的用户 vma。

参数

struct vm_area_struct *vma

要映射到的用户 vma。

unsigned long addr

此页面的目标用户地址。

unsigned long pfn

源内核 pfn。

pgprot_t pgprot

插入页面的 pgprot 标志。

描述

这与 vmf_insert_pfn() 完全相同,只不过它允许驱动程序逐页覆盖 pgprot。

这仅对 IO 映射有意义,对 COW 映射没有意义。通常,使用多个 vma 是更好的选择;只有在使用多个 VMA 不切实际时,才应使用 vmf_insert_pfn_prot。

当驱动程序设置的缓存和加密位与 vma->vm_page_prot 的不同时,pgprot 通常仅与 vma->vm_page_prot 不同,因为缓存或加密模式可能在 mmap() 时未知。

只要核心 vm 不使用 vma->vm_page_prot 为那些 vma 设置缓存和加密位(除了 COW 页面之外),这是可以的。这是通过核心 vm 仅使用不触摸缓存或加密位的函数来修改这些页表条目来确保的,如果需要,则使用 pte_modify()。(例如,请参见 mprotect())。

另外,当创建新的页表条目时,这仅使用 fault() 回调完成,而从不使用 vma->vm_page_prot 的值,除非页表条目指向作为 COW 结果的匿名页面。

上下文

进程上下文。可以使用 GFP_KERNEL 分配。

返回

vm_fault_t 值。

vm_fault_t vmf_insert_pfn(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn)

将单个 pfn 插入到用户 vma。

参数

struct vm_area_struct *vma

要映射到的用户 vma。

unsigned long addr

此页面的目标用户地址。

unsigned long pfn

源内核 pfn。

描述

与 vm_insert_page 类似,这允许驱动程序将它们已分配的单个页面插入到用户 vma 中。相同的注释适用。

此函数只能从 vm_ops->fault 处理程序调用,在这种情况下,处理程序应返回此函数的结果。

vma 不能是 COW 映射。

由于这仅针对当前不存在的页面调用,因此我们不需要刷新旧的虚拟缓存或 TLB。

上下文

进程上下文。可以使用 GFP_KERNEL 分配。

返回

vm_fault_t 值。

int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn, unsigned long size, pgprot_t prot)

将内核内存重新映射到用户空间。

参数

struct vm_area_struct *vma

要映射到的用户 vma。

unsigned long addr

要开始的目标页面对齐用户地址。

unsigned long pfn

内核物理内存地址的页帧号

unsigned long size

映射区域的大小

pgprot_t prot

此映射的页面保护标志

注意

只有在调用时持有 mm 信号量才是安全的。

返回

成功返回 0,否则返回负错误代码。

int vm_iomap_memory(struct vm_area_struct *vma, phys_addr_t start, unsigned long len)

将内存重新映射到用户空间

参数

struct vm_area_struct *vma

要映射到的用户 vma。

phys_addr_t start

要映射的物理内存的起始地址

unsigned long len

区域大小

描述

这是为常用驱动程序使用的简化版 io_remap_pfn_range()。驱动程序只需要提供要映射的物理内存范围,我们将从 vma 信息中计算出其余部分。

注意!某些驱动程序可能希望首先调整 vma->vm_page_prot 以获取任何写合并细节或类似信息。

返回

成功返回 0,否则返回负错误代码。

void unmap_mapping_pages(struct address_space *mapping, pgoff_t start, pgoff_t nr, bool even_cows)

从进程取消映射页面。

参数

struct address_space *mapping

包含要取消映射的页面的地址空间。

pgoff_t start

要取消映射的第一个页面的索引。

pgoff_t nr

要取消映射的页面数。0 表示取消映射到文件末尾。

bool even_cows

是否取消映射甚至私有的COW页面。

描述

从任何已映射它们的用户的用户空间进程中取消映射此地址空间中的页面。通常,当文件被截断时,您也希望删除COW页面,而不是从页面缓存中使页面失效。

void unmap_mapping_range(struct address_space *mapping, loff_t const holebegin, loff_t const holelen, int even_cows)

取消映射指定地址空间中与底层文件中指定字节范围对应的所有 mmap 的部分。

参数

struct address_space *mapping

包含要取消映射的 mmap 的地址空间。

loff_t const holebegin

要取消映射的第一个页面中的字节,相对于底层文件的开头。这将向下舍入到 PAGE_SIZE 边界。请注意,这与 truncate_pagecache() 不同,后者必须保留部分页面。相反,我们必须删除部分页面。

loff_t const holelen

预期空洞的大小(以字节为单位)。这将向上舍入到 PAGE_SIZE 边界。holelen 为零将截断到文件末尾。

int even_cows

当截断文件时,为 1,取消映射甚至私有的COW页面;但是当使页面缓存失效时,为 0,不要丢弃私有数据。

int follow_pfnmap_start(struct follow_pfnmap_args *args)

在用户虚拟地址查找 pfn 映射

参数

struct follow_pfnmap_args *args

指向结构体 follow_pfnmap_args 的指针

描述

调用者需要设置 args->vma 和 args->address 以指向虚拟地址,作为此类查找的目标。成功返回时,结果将放入其他输出字段中。

在调用者完成使用这些字段后,调用者必须调用另一个 follow_pfnmap_end() 以正确释放此类查找请求的锁和资源。

start() 和 end() 调用期间,args 中的结果将有效,因为将持有适当的锁。在调用 end() 后,follow_pfnmap_args 中的所有字段都将无效,无法进一步访问。在 end() 之后进一步使用此类信息可能需要调用者与页表更新进行适当的同步,否则可能会产生安全漏洞。

如果 PTE 映射一个引用计数的页面,则调用者有责任通过 MMU 通知器来防止失效;否则,在稍后的时间点访问 PFN 可能会触发释放后使用。

仅允许 IO 映射和原始 PFN 映射。应为读取获取 mmap 信号量,并且在调用 end() 之前不能释放 mmap 信号量。

此函数不得用于修改 PTE 内容。

返回

成功返回 0,否则返回负数。

void follow_pfnmap_end(struct follow_pfnmap_args *args)

结束 follow_pfnmap_start() 进程

参数

struct follow_pfnmap_args *args

指向结构体 follow_pfnmap_args 的指针

描述

必须与 follow_pfnmap_start() 配对使用。有关更多信息,请参阅上面的 start() 函数。

int generic_access_phys(struct vm_area_struct *vma, unsigned long addr, void *buf, int len, int write)

iomem mmap 访问的通用实现

参数

struct vm_area_struct *vma

要访问的 vma

unsigned long addr

用户空间地址,不是 vma 中的相对偏移量

void *buf

要读取/写入的缓冲区

int len

传输长度

int write

写入时设置为 FOLL_WRITE,否则为读取

描述

这是用于 iomem 映射的 vm_operations_struct.access 的通用实现。当 vma 不是基于页面时,access_process_vm() 会使用此回调。

unsigned long get_pfnblock_flags_mask(const struct page *page, unsigned long pfn, unsigned long mask)

返回页面的 pageblock_nr_pages 块的请求的标志组

参数

const struct page *page

感兴趣的块中的页面

unsigned long pfn

目标页帧号

unsigned long mask

调用者感兴趣的位的掩码

返回

pageblock_bits 标志

void set_pfnblock_flags_mask(struct page *page, unsigned long flags, unsigned long pfn, unsigned long mask)

为 pageblock_nr_pages 页面块设置请求的标志组

参数

struct page *page

感兴趣的块中的页面

unsigned long flags

要设置的标志

unsigned long pfn

目标页帧号

unsigned long mask

调用者感兴趣的位的掩码

bool move_freepages_block_isolate(struct zone *zone, struct page *page, int migratetype)

移动用于页面隔离的块中的空闲页

参数

struct zone *zone

区域

struct page *page

页块页面

int migratetype

要在页块上设置的迁移类型

描述

这类似于 move_freepages_block(),但处理页面隔离中遇到的特殊情况,即感兴趣的块可能是跨越多个页块的较大伙伴的一部分。

与常规页面分配器路径(在从空闲列表窃取伙伴时移动页面)不同,页面隔离对可能在两端都有重叠伙伴的任意 pfn 范围感兴趣。

此函数处理该情况。跨越的伙伴被拆分为单独的页块。仅移动感兴趣的块。

如果可以移动页面,则返回 true,否则返回 false

void __putback_isolated_page(struct page *page, unsigned int order, int mt)

将现在隔离的页面返回到我们获取它的地方

参数

struct page *page

已隔离的页面

unsigned int order

隔离页面的阶数

int mt

页面的页块的迁移类型

描述

此函数旨在将通过 __isolate_free_page 从空闲列表中拉取的页面返回到它们被拉取的空闲列表。

void __free_pages(struct page *page, unsigned int order)

释放使用 alloc_pages() 分配的页面。

参数

struct page *page

alloc_pages() 返回的页面指针。

unsigned int order

分配的阶数。

描述

此函数可以释放不是复合页面的多页面分配。它不检查传入的 order 是否与分配的阶数匹配,因此很容易泄漏内存。释放比分配的内存更多的内存可能会发出警告。

如果对该页面的最后一个引用是推测性的,它将由 put_page() 释放,而 put_page() 仅释放非复合分配的第一页。为防止剩余页面泄漏,我们在此处释放后续页面。如果要使用页面的引用计数来决定何时释放分配,则应分配复合页面,并使用 put_page() 而不是 __free_pages()

上下文

可以在中断上下文中或持有正常自旋锁时调用,但不能在 NMI 上下文中或持有原始自旋锁时调用。

void *alloc_pages_exact(size_t size, gfp_t gfp_mask)

分配确切数量的物理连续页面。

参数

size_t size

要分配的字节数

gfp_t gfp_mask

分配的 GFP 标志,不能包含 __GFP_COMP

描述

此函数类似于 alloc_pages(),不同之处在于它分配满足请求的最小页面数。alloc_pages() 只能以 2 的幂的页面数分配内存。

此函数也受 MAX_PAGE_ORDER 限制。

由此函数分配的内存必须由 free_pages_exact() 释放。

返回

指向已分配区域的指针,如果出现错误,则为 NULL

void *alloc_pages_exact_nid(int nid, size_t size, gfp_t gfp_mask)

在节点上分配确切数量的物理连续页面。

参数

int nid

应在其中分配内存的首选节点 ID

size_t size

要分配的字节数

gfp_t gfp_mask

分配的 GFP 标志,不能包含 __GFP_COMP

描述

类似于 alloc_pages_exact(),但先尝试在节点 nid 上分配,然后再回退。

返回

指向已分配区域的指针,如果出现错误,则为 NULL

void free_pages_exact(void *virt, size_t size)

释放通过 alloc_pages_exact() 分配的内存

参数

void *virt

alloc_pages_exact 返回的值。

size_t size

分配的大小,与传递给 alloc_pages_exact() 的值相同。

描述

释放之前调用 alloc_pages_exact 分配的内存。

unsigned long nr_free_zone_pages(int offset)

统计超出高水位线的页面数量

参数

int offset

最高区域的区域索引

描述

nr_free_zone_pages() 计算给定区域索引或以下所有区域内超出高水位线的页面数。对于每个区域,页面数计算为

nr_free_zone_pages = managed_pages - high_pages

返回

超出高水位线的页面数。

unsigned long nr_free_buffer_pages(void)

统计超出高水位线的页面数量

参数

void

无参数

描述

nr_free_buffer_pages() 计算 ZONE_DMA 和 ZONE_NORMAL 中超出高水位线的页面数。

返回

ZONE_DMA 和 ZONE_NORMAL 中超出高水位线的页面数。

int find_next_best_node(int node, nodemask_t *used_node_mask)

查找应出现在给定节点回退列表中的下一个节点

参数

int node

我们要追加其回退列表的节点

nodemask_t *used_node_mask

已使用节点的 nodemask_t

描述

我们使用多种因素来确定哪个是应出现在给定节点回退列表中的下一个节点。该节点不应已出现在 node 的回退列表中,并且它应是根据距离数组(其中包含从系统中每个节点到每个节点的任意距离值)确定的下一个最近的节点,并且还应首选没有 CPU 的节点,因为据推测,否则它们上的分配压力会非常小。

返回

找到的节点的节点 ID,如果未找到任何节点,则为 NUMA_NO_NODE

void setup_per_zone_wmarks(void)

当 min_free_kbytes 更改或内存热添加/删除时调用

参数

void

无参数

描述

确保每个区域的 watermark[min,low,high] 值相对于 min_free_kbytes 正确设置。

int alloc_contig_range(unsigned long start, unsigned long end, unsigned migratetype, gfp_t gfp_mask)
  • 尝试分配给定范围的页面

参数

unsigned long start

要分配的起始 PFN

unsigned long end

要分配的最后一个 PFN 的下一个 PFN

unsigned migratetype

底层页块的迁移类型(#MIGRATE_MOVABLE 或 #MIGRATE_CMA)。范围内的所有页块必须具有相同的迁移类型,并且必须是这两种类型之一。

gfp_t gfp_mask

压缩期间使用的 GFP 掩码

描述

PFN 范围不必是页块对齐的。PFN 范围必须属于单个区域。

此例程做的第一件事是尝试对该范围内的所有页块进行 MIGRATE_ISOLATE。一旦隔离,页块不应被其他人修改。

返回

成功时返回零,否则返回负错误代码。成功时,PFN 在 [start, end) 中的所有页面都将分配给调用者,并且需要使用 free_contig_range() 释放。

struct page *alloc_contig_pages(unsigned long nr_pages, gfp_t gfp_mask, int nid, nodemask_t *nodemask)
  • 尝试查找和分配连续范围的页面

参数

unsigned long nr_pages

要分配的连续页面的数量

gfp_t gfp_mask

用于限制搜索和在压缩期间使用的 GFP 掩码

int nid

目标节点

nodemask_t *nodemask

其他可能节点的掩码

描述

此例程是 alloc_contig_range() 的包装器。它扫描适用区域列表上的区域,以找到一个连续的 pfn 范围,然后可以使用 alloc_contig_range() 尝试分配。此例程旨在用于无法使用伙伴分配器满足的分配请求。

分配的内存始终与页面边界对齐。如果 nr_pages 是 2 的幂,则分配的范围也保证与相同的 nr_pages 对齐(例如,1GB 请求将与 1GB 对齐)。

分配的页面可以使用 free_contig_range() 释放,也可以手动对每个分配的页面调用 __free_page()。

返回

成功时返回指向连续页面的指针,如果失败则返回 NULL。

int numa_nearest_node(int node, unsigned int state)

按状态查找最近的节点

参数

int node

开始搜索的节点 ID

unsigned int state

用于过滤搜索的状态

描述

如果 nid 不在状态中,则按距离查找最近的节点。

返回

如果此 node 在状态中,则返回此节点,否则返回距离最近的节点

struct page *alloc_pages_mpol(gfp_t gfp, unsigned int order, struct mempolicy *pol, pgoff_t ilx, int nid)

根据 NUMA 内存策略分配页面。

参数

gfp_t gfp

GFP 标志。

unsigned int order

页面分配的阶数。

struct mempolicy *pol

指向 NUMA 内存策略的指针。

pgoff_t ilx

交错内存策略的索引(也区分 alloc_pages())。

int nid

首选节点(通常为 numa_node_id(),但 mpol 可能会覆盖它)。

返回

成功时返回页面,如果分配失败则返回 NULL。

struct folio *vma_alloc_folio(gfp_t gfp, int order, struct vm_area_struct *vma, unsigned long addr)

为 VMA 分配一个 folio。

参数

gfp_t gfp

GFP 标志。

int order

folio 的阶数。

struct vm_area_struct *vma

指向 VMA 的指针。

unsigned long addr

分配的虚拟地址。必须在 vma 内部。

描述

使用适当的 NUMA 策略,为 vma 中的特定地址分配一个 folio。调用者必须持有 VMA 的 mm_struct 的 mmap_lock,以防止它消失。应该用于所有将映射到用户空间的 folio 的分配,hugetlbfs 除外,以及直接使用 alloc_pages_mpol() 更合适的情况除外。

返回

成功时返回 folio,如果分配失败则返回 NULL。

struct page *alloc_pages(gfp_t gfp, unsigned int order)

分配页面。

参数

gfp_t gfp

GFP 标志。

unsigned int order

要分配的页面数的 2 的幂。

描述

分配 1 << order 个连续页面。第一页的物理地址自然对齐(例如,order-3 分配将与 8 * PAGE_SIZE 字节的倍数对齐)。在进程上下文中时,将遵守当前进程的 NUMA 策略。

上下文

可以使用适当的 GFP 标志从任何上下文中调用。

返回

成功时返回页面,如果分配失败则返回 NULL。

int mpol_misplaced(struct folio *folio, struct vm_fault *vmf, unsigned long addr)

检查当前 folio 节点在策略中是否有效

参数

struct folio *folio

要检查的 folio

struct vm_fault *vmf

描述错误的结构

unsigned long addr

用于共享策略查找和交错策略的 vma 中的虚拟地址

描述

查找 vma、addr 的当前策略节点 ID,并“比较” folio 的节点 ID。策略确定“模拟” alloc_page_vma()。从错误路径调用,我们知道 vma 和错误地址。

返回

如果页面位于此策略有效的节点中,则返回 NUMA_NO_NODE,否则返回一个合适的节点 ID,用于从中分配替换 folio。

void mpol_shared_policy_init(struct shared_policy *sp, struct mempolicy *mpol)

初始化 inode 的共享策略

参数

struct shared_policy *sp

指向 inode 共享策略的指针

struct mempolicy *mpol

要安装的结构内存策略

描述

在 inode 的共享策略 rb 树中安装非 NULL 的 mpol。在入口处,当前任务具有对非 NULL 的 mpol 的引用。必须在退出时释放此引用。这在 get_inode() 调用时调用,我们可以使用 GFP_KERNEL。

int mpol_parse_str(char *str, struct mempolicy **mpol)

将字符串解析为内存策略,用于 tmpfs mpol 挂载选项。

参数

char *str

包含要解析的内存策略的字符串

struct mempolicy **mpol

指向结构体 mempolicy 指针的指针,成功时返回。

描述

输入格式

<模式>[=<标志>][:<节点列表>]

返回

成功时返回 0,否则返回 1

void mpol_to_str(char *buffer, int maxlen, struct mempolicy *pol)

格式化 mempolicy 结构以进行打印

参数

char *buffer

用于存放格式化的 mempolicy 字符串

int maxlen

buffer 的长度

struct mempolicy *pol

指向要格式化的 mempolicy 的指针

描述

pol 转换为字符串。如果 buffer 太短,则截断字符串。建议 maxlen 至少为 51,以便容纳最长的模式 “weighted interleave”,加上最长的标志 “relative|balancing”,并显示至少几个节点 ID。

struct folio

表示一组连续的字节。

定义:

struct folio {
    unsigned long flags;
    union {
        struct list_head lru;
        unsigned int mlock_count;
    };
    struct address_space *mapping;
    pgoff_t index;
    union {
        void *private;
        swp_entry_t swap;
    };
    atomic_t _mapcount;
    atomic_t _refcount;
#ifdef CONFIG_MEMCG;
    unsigned long memcg_data;
#elif defined(CONFIG_SLAB_OBJ_EXT);
    unsigned long _unused_slab_obj_exts;
#endif;
#if defined(WANT_PAGE_VIRTUAL);
    void *virtual;
#endif;
#ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS;
    int _last_cpupid;
#endif;
    atomic_t _large_mapcount;
    atomic_t _entire_mapcount;
    atomic_t _nr_pages_mapped;
    atomic_t _pincount;
#ifdef CONFIG_64BIT;
    unsigned int _folio_nr_pages;
#endif;
    void *_hugetlb_subpool;
    void *_hugetlb_cgroup;
    void *_hugetlb_cgroup_rsvd;
    void *_hugetlb_hwpoison;
    struct list_head _deferred_list;
};

成员

标志

与页面标志相同。

{unnamed_union}

匿名

lru

最近最少使用列表;跟踪此页面最近的使用情况。

mlock_count

此页面被 mlock() 固定的次数。

映射

此页所属的文件,或者指向匿名内存的 anon_vma。

index

文件中的偏移量,以页为单位。对于匿名内存,这是从 mmap 开始的索引。

{unnamed_union}

匿名

private

每个页面的文件系统数据(参见folio_attach_private())。

swap

如果 folio_test_swapcache() 则用于 swp_entry_t。

_mapcount

不要直接访问此成员。使用folio_mapcount()查找此页面被用户空间映射的次数。

_refcount

不要直接访问此成员。使用folio_ref_count()查找对此页面的引用次数。

memcg_data

内存控制组数据。

_unused_slab_obj_exts

占位符,用于匹配结构体 slab 中的 obj_exts。

virtual

内核直接映射中的虚拟地址。

_last_cpupid

访问页面的最后一个 CPU 和最后一个进程的 ID。

_large_mapcount

不要直接使用,请调用 folio_mapcount()

_entire_mapcount

不要直接使用,请调用 folio_entire_mapcount()。

_nr_pages_mapped

不要在 rmap 和调试代码之外使用。

_pincount

不要直接使用,请调用 folio_maybe_dma_pinned()

_folio_nr_pages

不要直接使用,请调用 folio_nr_pages()

_hugetlb_subpool

不要直接使用,请使用 hugetlb.h 中的访问器。

_hugetlb_cgroup

不要直接使用,请使用 hugetlb_cgroup.h 中的访问器。

_hugetlb_cgroup_rsvd

不要直接使用,请使用 hugetlb_cgroup.h 中的访问器。

_hugetlb_hwpoison

不要直接使用,请调用 raw_hwp_list_head()。

_deferred_list

在内存压力下要拆分的页面。

描述

一个页面是一组在物理上、虚拟上和逻辑上连续的字节。它的大小是 2 的幂,并且与该 2 的幂对齐。它至少与 PAGE_SIZE 一样大。如果它在页面缓存中,则它在文件偏移量处,该文件偏移量是 2 的幂的倍数。它可以以任意页面偏移量映射到用户空间中,但其内核虚拟地址与其大小对齐。

struct ptdesc

页表的内存描述符。

定义:

struct ptdesc {
    unsigned long __page_flags;
    union {
        struct rcu_head pt_rcu_head;
        struct list_head pt_list;
        struct {
            unsigned long _pt_pad_1;
            pgtable_t pmd_huge_pte;
        };
    };
    unsigned long __page_mapping;
    union {
        pgoff_t pt_index;
        struct mm_struct *pt_mm;
        atomic_t pt_frag_refcount;
#ifdef CONFIG_HUGETLB_PMD_PAGE_TABLE_SHARING;
        atomic_t pt_share_count;
#endif;
    };
    union {
        unsigned long _pt_pad_2;
#if ALLOC_SPLIT_PTLOCKS;
        spinlock_t *ptl;
#else;
        spinlock_t ptl;
#endif;
    };
    unsigned int __page_type;
    atomic_t __page_refcount;
#ifdef CONFIG_MEMCG;
    unsigned long pt_memcg_data;
#endif;
};

成员

__page_flags

与页面标志相同。仅限 Powerpc。

{unnamed_union}

匿名

pt_rcu_head

用于释放页表页面。

pt_list

已使用的页表列表。用于 s390 和 x86。

{unnamed_struct}

匿名

_pt_pad_1

与页面的复合头别名的填充。

pmd_huge_pte

受 ptdesc->ptl 保护,用于 THP。

__page_mapping

与 page->mapping 别名。不用于页表。

{unnamed_union}

匿名

pt_index

用于 s390 gmap。

pt_mm

用于 x86 pgds。

pt_frag_refcount

用于跟踪碎片化的页表。仅限 Powerpc。

pt_share_count

用于 HugeTLB PMD 页表共享计数。

{unnamed_union}

匿名

_pt_pad_2

用于确保正确对齐的填充。

ptl

页表的锁。

ptl

页表的锁。

__page_type

与 page->page_type 相同。不用于页表。

__page_refcount

与页面引用计数相同。

pt_memcg_data

Memcg 数据。在此处跟踪页表。

描述

此结构目前覆盖了 struct page。请在充分理解问题的情况下再进行修改。

type vm_fault_t

页面错误处理程序的返回类型。

描述

页面错误处理程序返回 VM_FAULT 值的位掩码。

enum vm_fault_reason

页面错误处理程序返回这些值的位掩码,以告知核心 VM 在处理错误时发生了什么。用于决定是否向进程传递 SIGBUS 或只是增加主要/次要错误计数器。

常量

VM_FAULT_OOM

内存不足

VM_FAULT_SIGBUS

非法访问

VM_FAULT_MAJOR

从存储读取页面

VM_FAULT_HWPOISON

命中损坏的小页面

VM_FAULT_HWPOISON_LARGE

命中损坏的大页面。索引编码在高位

VM_FAULT_SIGSEGV

段错误

VM_FAULT_NOPAGE

->fault 安装了 pte,没有返回页面

VM_FAULT_LOCKED

->fault 锁定了返回的页面

VM_FAULT_RETRY

->fault 被阻塞,必须重试

VM_FAULT_FALLBACK

大页面错误失败,回退到小页面

VM_FAULT_DONE_COW

->fault 已完全处理 COW

VM_FAULT_NEEDDSYNC

->fault 没有修改页表,需要 fsync() 来完成(用于 DAX 中的同步页面错误)

VM_FAULT_COMPLETED

->fault 已完成,同时 mmap 锁已释放

VM_FAULT_HINDEX_MASK

掩码 HINDEX 值

enum fault_flag

错误标志定义。

常量

FAULT_FLAG_WRITE

错误是写入错误。

FAULT_FLAG_MKWRITE

错误是现有 PTE 的 mkwrite。

FAULT_FLAG_ALLOW_RETRY

如果被阻塞,允许重试错误。

FAULT_FLAG_RETRY_NOWAIT

重试时不要放弃 mmap_lock 并等待。

FAULT_FLAG_KILLABLE

错误任务处于 SIGKILL 可终止区域。

FAULT_FLAG_TRIED

已尝试过一次错误。

FAULT_FLAG_USER

错误源于用户空间。

FAULT_FLAG_REMOTE

错误不是针对当前任务/mm 的。

FAULT_FLAG_INSTRUCTION

错误发生在指令获取期间。

FAULT_FLAG_INTERRUPTIBLE

错误可以被非致命信号中断。

FAULT_FLAG_UNSHARE

错误是取消共享请求,用于中断 COW 映射中的 COW,确保在错误之后映射独占的匿名页面。

FAULT_FLAG_ORIG_PTE_VALID

错误是否有 vmf->orig_pte 缓存。我们只应在设置此标志时访问 orig_pte。

FAULT_FLAG_VMA_LOCK

错误在 VMA 锁下处理。

描述

关于 FAULT_FLAG_ALLOW_RETRYFAULT_FLAG_TRIED:我们可以通过正确指定这两个错误标志来指定是否允许页面错误重试。目前有三种合法的组合

  1. ALLOW_RETRY 和 !TRIED:这意味着页面错误允许重试,并且

    这是第一次尝试

  2. ALLOW_RETRY 和 TRIED:这意味着页面错误允许重试,并且

    我们已经尝试过至少一次

  3. !ALLOW_RETRY 和 !TRIED:这意味着页面错误不允许重试

未列出的组合 (!ALLOW_RETRY && TRIED) 是非法的,不应使用。请注意,页面错误可以允许多次重试,在这种情况下,我们将有一个带有标志 (a) 的初始错误,然后在后续错误中使用标志 (b)。我们应始终在重试之前尝试检测挂起的信号,以确保在必要时仍可以中断连续的页面错误。

组合 FAULT_FLAG_WRITE|FAULT_FLAG_UNSHARE 是非法的。当应用于非 COW 映射时,FAULT_FLAG_UNSHARE 会被忽略并被视为普通的读取错误。

int folio_is_file_lru(struct folio *folio)

页面应该在文件 LRU 上还是匿名 LRU 上?

参数

struct folio *folio

要测试的页面。

描述

我们希望在没有页面标志的情况下获取此信息,但是状态需要持续到页面最后一次从 LRU 中删除,这可能会一直到 __page_cache_release。

返回

一个整数(不是布尔值!),用于将页面排序到正确的 LRU 列表上,并正确计算页面。如果 folio 是常规的文件系统支持的页面缓存页面或延迟释放的匿名页面(例如,通过 MADV_FREE),则为 1。如果 folio 是普通的匿名页面、tmpfs 页面或以其他方式支持 ram 或 swap 的页面,则为 0。

void __folio_clear_lru_flags(struct folio *folio)

在释放页面之前清除页面的 lru 标志。

参数

struct folio *folio

在 lru 上并且现在引用计数为零的 folio。

enum lru_list folio_lru_list(struct folio *folio)

一个 folio 应该在哪个 LRU 列表上?

参数

struct folio *folio

要测试的页面。

返回

一个 folio 应该在的 LRU 列表,作为 LRU 列表数组的索引。

page_folio

page_folio (p)

从页面转换为 folio。

参数

p

该页面。

描述

每个页面都是 folio 的一部分。此函数不能在 NULL 指针上调用。

上下文

page 不需要引用或锁。如果调用者没有持有引用,则此调用可能会与 folio 分裂竞争,因此在获取 folio 的引用后应重新检查该 folio 是否仍然包含此页面。

返回

包含此页面的 folio。

folio_page

folio_page (folio, n)

从 folio 返回一个页面。

参数

folio

folio。

n

要返回的页码。

描述

n 相对于 folio 的起始位置。此函数不检查页码是否在 folio 内;假定调用者对页面有引用。

bool folio_xor_flags_has_waiters(struct folio *folio, unsigned long mask)

更改一些 folio 标志。

参数

struct folio *folio

folio。

unsigned long mask

此字中设置的位将被更改。

描述

这只能用于持有 folio 锁时更改的标志。例如,将其用于 PG_dirty 是不安全的,因为可以在不持有 folio 锁的情况下设置该标志。它也只能用于 0-6 范围内的标志,因为某些实现只会影响这些位。

返回

是否有任务在等待该 folio。

bool folio_test_uptodate(const struct folio *folio)

此 folio 是否是最新的?

参数

const struct folio *folio

folio。

描述

当 folio 中的每个字节至少与存储上的相应字节一样新时,会在 folio 上设置 uptodate 标志。匿名和 CoW folio 始终是最新的。如果 folio 不是最新的,则其中的某些字节可能是;请参阅 is_partially_uptodate() address_space 操作。

bool folio_test_large(const struct folio *folio)

此 folio 是否包含多个页面?

参数

const struct folio *folio

要测试的页面。

返回

如果 folio 大于一页,则为 true。

bool PageSlab(const struct page *page)

确定页面是否属于 slab 分配器

参数

const struct page *page

要测试的页面。

上下文

任何上下文。

返回

对于 slab 页面为 true,对于任何其他类型的页面为 false。

bool PageHuge(const struct page *page)

确定页面是否属于 hugetlbfs

参数

const struct page *page

要测试的页面。

上下文

任何上下文。

返回

对于 hugetlbfs 页面为 true,对于匿名页面或属于其他文件系统的页面为 false。

int folio_has_private(const struct folio *folio)

确定 folio 是否有私有内容

参数

const struct folio *folio

要检查的 folio

描述

确定 folio 是否有私有内容,指示应该在其上调用释放例程。

bool fault_flag_allow_retry_first(enum fault_flag flags)

第一次检查 ALLOW_RETRY

参数

enum fault_flag flags

故障标志。

描述

这主要用于我们希望避免在等待另一个条件更改时占用 mmap_lock 太长时间的情况,在这种情况下,我们可以尝试在第一轮中礼貌地释放 mmap_lock,以避免可能导致也需要 mmap_lock 的其他进程饥饿。

返回

如果页面错误允许重试,并且这是第一次尝试处理错误,则为 true;否则为 false。

unsigned int folio_order(const struct folio *folio)

folio 的分配阶数。

参数

const struct folio *folio

folio。

描述

一个 folio 由 2^order 个页面组成。有关阶数的定义,请参见 get_order()。

返回

folio 的阶数。

int folio_mapcount(const struct folio *folio)

此 folio 的映射数。

参数

const struct folio *folio

folio。

描述

folio 映射计数对应于引用 folio 任何部分的当前用户页表条目的数量。每个这样的当前用户页表条目必须与恰好一个 folio 引用配对。

对于普通 folio,每个用户页表条目 (PTE/PMD/PUD/...) 都精确计数一次。

对于 hugetlb folio,每个引用整个 folio 的抽象“hugetlb”用户页表条目精确计数一次,即使这种特殊的页表条目由多个普通页表条目组成。

对于无法映射到用户空间的页面(例如 slab、页表等),将报告 0。

返回

此 folio 被映射的次数。

bool folio_mapped(const struct folio *folio)

此 folio 是否已映射到用户空间?

参数

const struct folio *folio

folio。

返回

如果用户页表引用此 folio 中的任何页面,则为 true。

unsigned int thp_order(struct page *page)

透明巨页的阶数。

参数

struct page *page

透明巨页的头页面。

unsigned long thp_size(struct page *page)

透明巨页的大小。

参数

struct page *page

透明巨页的头页面。

返回

此页面中的字节数。

void folio_get(struct folio *folio)

增加 folio 的引用计数。

参数

struct folio *folio

folio。

上下文

可以在任何上下文中调用,只要您知道您在 folio 上有一个引用计数。如果您还没有引用计数,folio_try_get() 可能是您应该使用的正确接口。

void folio_put(struct folio *folio)

减少 folio 的引用计数。

参数

struct folio *folio

folio。

描述

如果 folio 的引用计数达到零,内存将被释放回页面分配器,并可能立即被另一个分配使用。在调用 folio_put() 之后,除非您可以确定它不是最后一个引用,否则不要访问内存或 struct folio

上下文

可以在进程或中断上下文中调用,但不能在 NMI 上下文中调用。可以在持有自旋锁时调用。

void folio_put_refs(struct folio *folio, int refs)

减少 folio 的引用计数。

参数

struct folio *folio

folio。

int refs

从 folio 的引用计数中减去的数量。

描述

如果 folio 的引用计数达到零,内存将被释放回页面分配器,并可能立即被另一个分配使用。在调用 folio_put_refs() 之后,除非您可以确定它们不是最后一个引用,否则不要访问内存或 struct folio

上下文

可以在进程或中断上下文中调用,但不能在 NMI 上下文中调用。可以在持有自旋锁时调用。

void folios_put(struct folio_batch *folios)

减少 folio 数组的引用计数。

参数

struct folio_batch *folios

这些 folio。

描述

folio_put() 类似,但用于一批 folio。这比你自己编写循环更有效,因为它将优化如果 folio 被释放则需要获取的锁。folio 批处理返回时为空,可以重用于另一批;无需重新初始化它。

上下文

可以在进程或中断上下文中调用,但不能在 NMI 上下文中调用。可以在持有自旋锁时调用。

unsigned long folio_pfn(const struct folio *folio)

返回 folio 的页帧号 (Page Frame Number)。

参数

const struct folio *folio

folio。

描述

一个 folio 可能包含多个页面。这些页面的页帧号是连续的。

返回

folio 中第一个页面的页帧号。

bool folio_maybe_dma_pinned(struct folio *folio)

报告 folio 是否可能为 DMA 固定。

参数

struct folio *folio

folio。

描述

此函数检查 folio 是否已通过调用 pin_user_pages() 系列中的函数固定。

对于小型 folio,返回值部分模糊:false 不是模糊的,因为它意味着“绝对没有为 DMA 固定”,但 true 意味着“可能为 DMA 固定,但可能是由于具有至少 GUP_PIN_COUNTING_BIAS 值的正常 folio 引用而导致的假阳性”。

假阳性是可以接受的,因为:a) folio 不太可能获得那么多引用计数,b) 此例程的所有调用者都应该能够优雅地处理假阳性。

对于大型 folio,结果将完全正确。这是因为我们有更多可用的跟踪数据:使用 _pincount 字段而不是 GUP_PIN_COUNTING_BIAS 方案。

有关更多信息,请参阅 pin_user_pages() 和相关调用

返回

如果 folio 很可能已“DMA 固定”,则为 True。如果 folio 绝对没有 DMA 固定,则为 False。

bool is_zero_page(const struct page *page)

查询页面是否为零页面

参数

const struct page *page

要查询的页面

描述

如果 page 是永久零页面之一,则此函数返回 true。

bool is_zero_folio(const struct folio *folio)

查询 folio 是否为零页面

参数

const struct folio *folio

要查询的 folio

描述

如果 folio 是永久零页面之一,则此函数返回 true。

long folio_nr_pages(const struct folio *folio)

folio 中的页面数。

参数

const struct folio *folio

folio。

返回

2 的正幂。

int thp_nr_pages(struct page *page)

此巨型页面中常规页面的数量。

参数

struct page *page

巨型页面的头页面。

struct folio *folio_next(struct folio *folio)

移动到下一个物理 folio。

参数

struct folio *folio

我们当前正在操作的 folio。

描述

如果您具有可能跨越多个 folio 的物理连续内存(例如 struct bio_vec),请使用此函数从一个 folio 移动到下一个 folio。如果内存只是虚拟连续的,则不要使用它,因为 folio 几乎肯定不是彼此相邻的。这相当于编写 page++ 的 folio 版本。

上下文

我们假设 folio 在更高层进行引用计数和/或锁定,并且不调整引用计数。

返回

下一个 struct folio

unsigned int folio_shift(const struct folio *folio)

此 folio 描述的内存大小。

参数

const struct folio *folio

folio。

描述

一个 folio 表示大小为 2 的幂的字节数。此函数告诉您该 folio 是 2 的哪个幂。另请参阅 folio_size()folio_order()

上下文

调用者应具有 folio 的引用,以防止其被拆分。folio 不需要锁定。

返回

此 folio 大小的以 2 为底的对数。

size_t folio_size(const struct folio *folio)

一个 folio 中的字节数。

参数

const struct folio *folio

folio。

上下文

调用者应具有 folio 的引用,以防止其被拆分。folio 不需要锁定。

返回

此 folio 中的字节数。

bool folio_likely_mapped_shared(struct folio *folio)

评估该页是否被映射到多个 MM 的页表中

参数

struct folio *folio

folio。

描述

此函数检查该页当前是否被映射到多个 MM(“共享映射”),或者该页是否仅被映射到一个 MM(“独占映射”)。

对于 KSM 页,当一个页被多次映射到同一个 MM 时,此函数也返回“共享映射”,因为各个页面映射是独立的。

由于并非所有页都可以轻松获得精确信息,因此此函数通过计算页的首个页面当前被映射到页表的次数来估计当前映射该页的 MM(“共享者”)的数量。

对于小型匿名页和匿名 hugetlb 页,返回值将完全正确:非 KSM 页最多只能被映射到一个 MM 中一次,并且不能被部分映射。即使 KSM 页被多次映射到同一个 MM 中,也被认为是共享的。

对于其他页,结果可能不准确
  1. 对于可部分映射的大页(THP),当该页仅被部分映射到至少一个 MM 中时,返回值可能会错误地指示“独占映射”(假阴性)。

  2. 对于页面缓存页(包括 hugetlb),当同一 MM 中的两个 VMA 覆盖相同的文件范围时,返回值可能会错误地指示“共享映射”(假阳性)。

此外,此函数仅考虑当前使用页的映射计数跟踪的页表映射。

此函数不考虑:
  1. 如果该页可能在(不久的)将来被映射(例如,交换缓存、页面缓存、用于迁移的临时取消映射)。

  2. 如果该页的映射方式不同(VM_PFNMAP)。

  3. 如果应用 hugetlb 页表共享。调用者可能需要检查 hugetlb_pmd_shared()。

返回

是否估计该页被映射到多个 MM 中。

struct ptdesc *pagetable_alloc(gfp_t gfp, unsigned int order)

分配页表

参数

gfp_t gfp

GFP 标志

unsigned int order

所需的页表顺序

描述

pagetable_alloc 为页表分配内存,并分配一个页表描述符来描述该内存。

返回

描述分配的页表的 ptdesc。

void pagetable_free(struct ptdesc *pt)

释放页表

参数

struct ptdesc *pt

页表描述符

描述

pagetable_free 释放页表描述符描述的所有页表的内存以及描述符本身的内存。

struct vm_area_struct *vma_lookup(struct mm_struct *mm, unsigned long addr)

查找特定地址的 VMA

参数

struct mm_struct *mm

进程地址空间。

unsigned long addr

用户地址。

返回

给定地址的 vm_area_struct,否则为 NULL

bool vma_is_special_huge(const struct vm_area_struct *vma)

Transhuge 页表项是否被认为是特殊的?

参数

const struct vm_area_struct *vma

指向要考虑的 struct vm_area_struct 的指针

描述

Transhuge 页表项是否按照 vm_normal_page() 中的定义被认为是“特殊的”。

返回

如果 Transhuge 页表项应被认为是特殊的,则为 true,否则为 false。

bool is_readonly_sealed(int seals, vm_flags_t vm_flags)

检查是否写入密封但映射为只读,在这种情况下,应禁止向前移动写入。

参数

int seals

要检查的密封

vm_flags_t vm_flags

要检查的 VMA 标志

描述

返回是否为只读密封,在这种情况下,应禁止向前写入。

int seal_check_write(int seals, struct vm_area_struct *vma)

检查 F_SEAL_WRITE 或 F_SEAL_FUTURE_WRITE 标志并处理它们。

参数

int seals

要检查的密封

struct vm_area_struct *vma

要操作的 vma

描述

检查是否设置了 F_SEAL_WRITE 或 F_SEAL_FUTURE_WRITE;如果是,则对 vma 标志进行适当的检查/处理。如果检查通过,则返回 0;如果出现错误,则返回 <0。

int folio_ref_count(const struct folio *folio)

此页的引用计数。

参数

const struct folio *folio

folio。

描述

引用计数通常通过调用 folio_get() 递增,并通过调用 folio_put() 递减。页引用计数的一些典型用户

  • 来自页表的每个引用

  • 页面缓存

  • 文件系统私有数据

  • LRU 列表

  • 管道

  • 直接 IO,它在进程地址空间中引用此页面

返回

此页的引用次数。

bool folio_try_get(struct folio *folio)

尝试增加页的引用计数。

参数

struct folio *folio

folio。

描述

如果您尚未引用页,则可以使用此函数尝试获取一个引用。如果例如,自从您找到指向它的指针以来,该页已被释放,或者为了拆分或迁移的目的而被冻结,则可能会失败。

返回

如果引用计数成功递增,则为 True。

int is_highmem(struct zone *zone)

辅助函数,用于快速检查 struct zone 是否为 highmem 区域。这是一种尝试将对 ZONE_{DMA/NORMAL/HIGHMEM/etc} 的引用在通用代码中保持最少的方法。

参数

struct zone *zone

指向 struct zone 变量的指针

返回

highmem 区域为 1,否则为 0

for_each_online_pgdat

for_each_online_pgdat (pgdat)

用于迭代所有在线节点的辅助宏

参数

pgdat

指向 pg_data_t 变量的指针

for_each_zone

for_each_zone (zone)

用于迭代所有内存区域的辅助宏

参数

zone

指向 struct zone 变量的指针

描述

用户只需要声明 zone 变量,for_each_zone 会填充它。

struct zoneref *next_zones_zonelist(struct zoneref *z, enum zone_type highest_zoneidx, nodemask_t *nodes)

使用区域列表中的游标作为起点,返回允许的节点掩码内高于或低于 highest_zoneidx 的下一个区域

参数

struct zoneref *z

用作搜索起点的游标

enum zone_type highest_zoneidx

要返回的最高区域的区域索引

nodemask_t *nodes

用于过滤区域列表的可选节点掩码

描述

此函数使用游标作为搜索起点,返回给定区域索引内或低于该索引且位于允许的节点掩码内的下一个区域。返回的 zoneref 是一个游标,表示当前正在检查的区域。在再次调用 next_zones_zonelist 之前,应将其向前移动一个。

返回

使用区域列表中的游标作为起点,返回允许的节点掩码内高于或低于 highest_zoneidx 的下一个区域

struct zoneref *first_zones_zonelist(struct zonelist *zonelist, enum zone_type highest_zoneidx, nodemask_t *nodes)

返回 zonelist 中,位于 highest_zoneidx 或更低级别,并且在允许的 nodemask 内的第一个 zone。

参数

struct zonelist *zonelist

要搜索合适 zone 的 zonelist。

enum zone_type highest_zoneidx

要返回的最高区域的区域索引

nodemask_t *nodes

用于过滤区域列表的可选节点掩码

描述

此函数返回位于给定 zone 索引或更低级别,并且在允许的 nodemask 内的第一个 zone。返回的 zoneref 是一个游标,可以通过在调用之前将其前进一位,使用 next_zones_zonelist 来遍历 zonelist。

当没有找到符合条件的 zone 时,zoneref->zone 为 NULL(zoneref 本身永远不为 NULL)。这可能是真实发生的,也可能是由于 cpuset 修改导致的并发 nodemask 更新引起的。

返回

指向找到的第一个合适 zone 的 Zoneref 指针。

for_each_zone_zonelist_nodemask

for_each_zone_zonelist_nodemask (zone, z, zlist, highidx, nodemask)

用于迭代 zonelist 中,位于给定 zone 索引或更低级别,并且在 nodemask 内的有效 zone 的辅助宏。

参数

zone

迭代器中的当前 zone。

z

正在迭代的 zonelist->_zonerefs 中的当前指针。

zlist

正在迭代的 zonelist。

highidx

要返回的最高区域的区域索引

nodemask

分配器允许的 Nodemask。

描述

此迭代器遍历给定 zone 索引或更低级别,并且在给定 nodemask 内的所有 zone。

for_each_zone_zonelist

for_each_zone_zonelist (zone, z, zlist, highidx)

用于迭代 zonelist 中,位于给定 zone 索引或更低级别的有效 zone 的辅助宏。

参数

zone

迭代器中的当前 zone。

z

正在迭代的 zonelist->zones 中的当前指针。

zlist

正在迭代的 zonelist。

highidx

要返回的最高区域的区域索引

描述

此迭代器遍历给定 zone 索引或更低级别的所有 zone。

int pfn_valid(unsigned long pfn)

检查 PFN 是否存在有效的内存映射条目。

参数

unsigned long pfn

要检查的页帧号。

描述

检查 pfn 是否存在有效的内存映射条目,即 struct page。请注意,内存映射条目的可用性并不意味着该 pfn 处存在实际可用的内存。struct page 可能表示一个空洞或一个不可用的页帧。

返回

对于具有内存映射条目的 PFN 返回 1,否则返回 0。

struct address_space *folio_mapping(struct folio *folio)

查找存储此 folio 的映射。

参数

struct folio *folio

folio。

描述

对于在页面缓存中的 folio,返回此页面所属的映射。在交换缓存中的 folio 返回存储此页面的交换映射(这与存储数据的交换文件或交换设备的映射不同)。

您可以为不在交换缓存或页面缓存中的 folio 调用此函数,它将返回 NULL。

int __anon_vma_prepare(struct vm_area_struct *vma)

将 anon_vma 附加到内存区域。

参数

struct vm_area_struct *vma

有问题的内存区域。

描述

这确保了由 “vma” 描述的内存映射附加了一个 “anon_vma”,以便我们可以将映射到其中的匿名页面与该 anon_vma 相关联。

常见的情况是我们已经有一个了,这由 anon_vma_prepare() 内联处理。但如果没有,我们需要找到一个相邻的映射,我们可以从中重复使用 anon_vma(当拆分 vma 的唯一原因是因为 mprotect() 时非常常见),或者我们分配一个新的。

Anon-vma 的分配非常微妙,因为我们可能乐观地在 folio_lock_anon_vma_read() 中查找了 anon_vma,这实际上甚至在新分配的 vma 中也会触及 rwsem(它依赖 RCU 来确保 anon_vma 实际上不会被销毁)。

因此,即使对于新的分配,我们也需要进行适当的 anon_vma 锁定。同时,我们不希望对已经拥有 anon_vma 的常见情况进行任何锁定。

unsigned long page_address_in_vma(const struct folio *folio, const struct page *page, const struct vm_area_struct *vma)

此 VMA 中页面的虚拟地址。

参数

const struct folio *folio

包含页面的 folio。

const struct page *page

folio 中的页面。

const struct vm_area_struct *vma

我们需要知道地址的 VMA。

描述

计算此页面在指定 VMA 中的用户虚拟地址。调用者有责任检查该页面是否实际位于 VMA 内。当前可能没有指向此页面的 PTE,但如果在此地址发生页面错误,则将访问此页面。

上下文

调用者应持有对 folio 的引用。调用者应持有锁(例如 i_mmap_lock 或 mmap_lock),以防止 VMA 被更改。

返回

此页面在 VMA 中对应的虚拟地址。

int folio_referenced(struct folio *folio, int is_locked, struct mem_cgroup *memcg, unsigned long *vm_flags)

测试 folio 是否被引用。

参数

struct folio *folio

要测试的页面。

int is_locked

调用者持有 folio 上的锁。

struct mem_cgroup *memcg

目标内存 cgroup。

unsigned long *vm_flags

引用该 folio 的所有 vma->vm_flags 的组合。

描述

对 folio 的所有映射进行快速 test_and_clear_referenced。

返回

引用该 folio 的映射数量。如果由于 rmap 锁争用导致函数退出,则返回 -1。

int pfn_mkclean_range(unsigned long pfn, unsigned long nr_pages, pgoff_t pgoff, struct vm_area_struct *vma)

清除映射到共享映射的 vma 内,范围为 [pfn, pfn + nr_pages) 的 PTE(包括 PMD)。并且由于干净的 PTE 也应该是只读的,因此也会对其进行写保护。

参数

unsigned long pfn

起始 pfn。

unsigned long nr_pages

pfn 开头的物理上连续的页面数。

pgoff_t pgoff

pfn 映射的页偏移。

struct vm_area_struct *vma

pfn 映射所在的 vma。

描述

返回已清理的 PTE(包括 PMD)的数量。

void folio_move_anon_rmap(struct folio *folio, struct vm_area_struct *vma)

将 folio 移动到我们的 anon_vma

参数

struct folio *folio

要移动到我们的 anon_vma 的 folio

struct vm_area_struct *vma

folio 所属的 vma

描述

当一个 folio 在 COW 事件后只属于一个进程时,该 folio 可以被移动到只属于该进程的 anon_vma 中,这样 rmap 代码就不会搜索父进程或兄弟进程。

void __folio_set_anon(struct folio *folio, struct vm_area_struct *vma, unsigned long address, bool exclusive)

为 folio 设置新的匿名 rmap

参数

struct folio *folio

要为其设置新匿名 rmap 的 folio。

struct vm_area_struct *vma

要将 folio 添加到的 VM 区域。

unsigned long address

映射的用户虚拟地址

bool exclusive

该 folio 是否为该进程独占。

void __page_check_anon_rmap(const struct folio *folio, const struct page *page, struct vm_area_struct *vma, unsigned long address)

健全性检查匿名 rmap 添加

参数

const struct folio *folio

包含 page 的 folio。

const struct page *page

要检查其映射的页面

struct vm_area_struct *vma

添加映射的 vm 区域

unsigned long address

映射的用户虚拟地址

void folio_add_anon_rmap_ptes(struct folio *folio, struct page *page, int nr_pages, struct vm_area_struct *vma, unsigned long address, rmap_t flags)

将 PTE 映射添加到匿名 folio 的页面范围

参数

struct folio *folio

要添加映射的 folio

struct page *page

要添加的第一个页面

int nr_pages

将要映射的页面数量

struct vm_area_struct *vma

添加映射的 vm 区域

unsigned long address

要映射的第一个页面的用户虚拟地址

rmap_t flags

rmap 标志

描述

folio 的页面范围由 [first_page, first_page + nr_pages) 定义

调用者需要持有页表锁,并且页面必须在 anon_vma 情况下锁定:以便在设置后序列化映射和索引检查,并确保匿名 folio 不会以竞争方式升级为 KSM folio(但 KSM folio 永远不会降级)。

void folio_add_anon_rmap_pmd(struct folio *folio, struct page *page, struct vm_area_struct *vma, unsigned long address, rmap_t flags)

将 PMD 映射添加到匿名 folio 的页面范围

参数

struct folio *folio

要添加映射的 folio

struct page *page

要添加的第一个页面

struct vm_area_struct *vma

添加映射的 vm 区域

unsigned long address

要映射的第一个页面的用户虚拟地址

rmap_t flags

rmap 标志

描述

folio 的页面范围由 [first_page, first_page + HPAGE_PMD_NR) 定义

调用者需要持有页表锁,并且页面必须在 anon_vma 情况下锁定:以便在设置后序列化映射和索引检查。

void folio_add_new_anon_rmap(struct folio *folio, struct vm_area_struct *vma, unsigned long address, rmap_t flags)

将映射添加到新的匿名 folio。

参数

struct folio *folio

要添加映射的 folio。

struct vm_area_struct *vma

添加映射的 vm 区域

unsigned long address

映射的用户虚拟地址

rmap_t flags

rmap 标志

描述

类似于 folio_add_anon_rmap_*(),但只能在新的 folio 上调用。这意味着可以绕过递增和测试。除非两个线程同时映射,否则 folio 在独占时不必锁定。但是,如果 folio 是共享的,则必须锁定。

如果 folio 是 pmd 可映射的,则将其计为 THP。

void folio_add_file_rmap_ptes(struct folio *folio, struct page *page, int nr_pages, struct vm_area_struct *vma)

将 PTE 映射添加到 folio 的页面范围

参数

struct folio *folio

要添加映射的 folio

struct page *page

要添加的第一个页面

int nr_pages

将使用 PTE 映射的页面数量

struct vm_area_struct *vma

添加映射的 vm 区域

描述

folio 的页面范围由 [page, page + nr_pages) 定义

调用者需要持有页表锁。

void folio_add_file_rmap_pmd(struct folio *folio, struct page *page, struct vm_area_struct *vma)

将 PMD 映射添加到 folio 的页面范围

参数

struct folio *folio

要添加映射的 folio

struct page *page

要添加的第一个页面

struct vm_area_struct *vma

添加映射的 vm 区域

描述

folio 的页面范围由 [page, page + HPAGE_PMD_NR) 定义

调用者需要持有页表锁。

void folio_remove_rmap_ptes(struct folio *folio, struct page *page, int nr_pages, struct vm_area_struct *vma)

从 folio 的页面范围内删除 PTE 映射。

参数

struct folio *folio

从中删除映射的 folio。

struct page *page

要删除的第一个页面。

int nr_pages

将从映射中删除的页面数量。

struct vm_area_struct *vma

从中删除映射的 vm 区域。

描述

folio 的页面范围由 [page, page + nr_pages) 定义

调用者需要持有页表锁。

void folio_remove_rmap_pmd(struct folio *folio, struct page *page, struct vm_area_struct *vma)

从 folio 的页面范围内删除 PMD 映射。

参数

struct folio *folio

从中删除映射的 folio。

struct page *page

要删除的第一个页面。

struct vm_area_struct *vma

从中删除映射的 vm 区域。

描述

folio 的页面范围由 [page, page + HPAGE_PMD_NR) 定义

调用者需要持有页表锁。

void try_to_unmap(struct folio *folio, enum ttu_flags flags)

尝试删除到 folio 的所有页表映射。

参数

struct folio *folio

要取消映射的 folio。

enum ttu_flags flags

操作和标志。

描述

尝试删除映射此 folio 的所有页表条目。调用者有责任在需要时检查 folio 是否仍然被映射(使用 TTU_SYNC 来防止统计竞争)。

上下文

调用者必须持有 folio 锁。

void try_to_migrate(struct folio *folio, enum ttu_flags flags)

尝试将所有页表映射替换为交换条目。

参数

struct folio *folio

要替换页表条目的 folio。

enum ttu_flags flags

操作和标志。

描述

尝试删除映射此 folio 的所有页表条目,并将它们替换为特殊的交换条目。调用者必须持有 folio 锁。

bool folio_make_device_exclusive(struct folio *folio, struct mm_struct *mm, unsigned long address, void *owner)

将 folio 标记为由设备独占拥有。

参数

struct folio *folio

要替换页表条目的 folio。

struct mm_struct *mm

期望映射 folio 的 mm_struct。

unsigned long address

期望映射 folio 的地址。

void *owner

传递给 MMU_NOTIFY_EXCLUSIVE 范围通知回调函数。

描述

尝试删除映射此 folio 的所有页表条目,并将它们替换为特殊的设备独占交换条目,以授予设备对 folio 的独占访问权限。

上下文

调用者必须持有 folio 锁。

返回

如果页面仍然被映射,或者无法从预期地址取消映射,则返回 false。否则返回 true(成功)。

int make_device_exclusive_range(struct mm_struct *mm, unsigned long start, unsigned long end, struct page **pages, void *owner)

标记一个范围以供设备独占使用。

参数

struct mm_struct *mm

关联目标进程的 mm_struct。

unsigned long start

要标记为独占设备访问的区域的起始地址。

unsigned long end

区域的结束地址。

struct page **pages

返回成功标记为独占访问的页面。

void *owner

传递给 MMU_NOTIFY_EXCLUSIVE 范围通知器以允许过滤。

返回

GUP 在该范围内找到的页面数量。仅当页面指针为非 NULL 时,才会将页面标记为独占访问。

描述

此函数查找将页面映射到给定地址范围的 pte,锁定它们,并将映射替换为特殊的交换条目,以防止用户空间 CPU 访问。在发生错误时,在调用 MMU 通知器后,这些条目将替换为原始映射。

使用此功能来编程设备访问的驱动程序必须使用 mmu 通知器临界区,以在编程期间保持设备特定的锁。一旦编程完成,它应该释放页面锁和引用,之后 CPU 对页面的访问将撤销独占访问权限。

int migrate_folio(struct address_space *mapping, struct folio *dst, struct folio *src, enum migrate_mode mode)

简单的 folio 迁移。

参数

struct address_space *mapping

包含 folio 的 address_space。

struct folio *dst

要将数据迁移到的 folio。

struct folio *src

包含当前数据的 folio。

enum migrate_mode mode

如何迁移页面。

描述

直接迁移适合没有私有数据的 folio 的单个 LRU folio 的通用逻辑。

Folio 在进入和退出时被锁定。

int buffer_migrate_folio(struct address_space *mapping, struct folio *dst, struct folio *src, enum migrate_mode mode)

带有缓冲区的 folio 的迁移函数。

参数

struct address_space *mapping

包含 src 的地址空间。

struct folio *dst

要迁移到的 folio。

struct folio *src

要从中迁移的 folio。

enum migrate_mode mode

如何迁移 folio。

描述

仅当底层文件系统保证不存在对 src 的其他引用时,才能使用此函数。例如,附加的缓冲区头仅在 folio 锁下访问。如果你的文件系统无法提供此保证,则 buffer_migrate_folio_norefs() 可能更合适。

返回

成功时返回 0,失败时返回负的 errno。

int buffer_migrate_folio_norefs(struct address_space *mapping, struct folio *dst, struct folio *src, enum migrate_mode mode)

带有缓冲区的 folio 的迁移函数。

参数

struct address_space *mapping

包含 src 的地址空间。

struct folio *dst

要迁移到的 folio。

struct folio *src

要从中迁移的 folio。

enum migrate_mode mode

如何迁移 folio。

描述

类似于 buffer_migrate_folio(),但此变体更加谨慎,并检查是否也没有缓冲区头引用。此函数适用于直接查找和引用缓冲区头的映射(例如块设备映射)。

返回

成功时返回 0,失败时返回负的 errno。

unsigned long unmapped_area(struct vm_unmapped_area_info *info)

info 中查找 low_limit 和 high_limit 之间具有正确对齐和偏移量的区域。注意:当前进程的 current->mm 用于搜索。

参数

struct vm_unmapped_area_info *info

未映射区域信息,包括范围 [low_limit - high_limit)、对齐偏移量和掩码。

返回

一个内存地址或 -ENOMEM。

unsigned long unmapped_area_topdown(struct vm_unmapped_area_info *info)

info 中查找 low_limit 和 high_limit 之间最高可用地址的具有正确对齐和偏移量的区域。注意:当前进程的 current->mm 用于搜索。

参数

struct vm_unmapped_area_info *info

未映射区域信息,包括范围 [low_limit - high_limit)、对齐偏移量和掩码。

返回

一个内存地址或 -ENOMEM。

struct vm_area_struct *find_vma_intersection(struct mm_struct *mm, unsigned long start_addr, unsigned long end_addr)

查找与区间相交的第一个 VMA。

参数

struct mm_struct *mm

进程地址空间。

unsigned long start_addr

包含的起始用户地址。

unsigned long end_addr

排除的结束用户地址。

返回

提供的范围内的第一个 VMA,否则为 NULL。假设 start_addr < end_addr。

struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr)

查找给定地址的 VMA,或下一个 VMA。

参数

struct mm_struct *mm

要检查的 mm_struct。

unsigned long addr

地址。

返回

与 addr 关联的 VMA,或下一个 VMA。如果在 addr 或以上没有 VMA,则可能返回 NULL

struct vm_area_struct *find_vma_prev(struct mm_struct *mm, unsigned long addr, struct vm_area_struct **pprev)

查找给定地址的 VMA,或下一个 vma,并将 pprev 设置为前一个 VMA(如果有)。

参数

struct mm_struct *mm

要检查的 mm_struct。

unsigned long addr

地址。

struct vm_area_struct **pprev

要设置为前一个 VMA 的指针。

描述

请注意,此处缺少 RCU 锁,因为改用了外部的 mmap_lock()。

返回

addr 关联的 VMA,或下一个 vma。如果在 addr 或以上没有 vma,则可能返回 NULL

void __ref kmemleak_alloc(const void *ptr, size_t size, int min_count, gfp_t gfp)

注册新分配的对象。

参数

const void *ptr

指向对象开头的指针。

size_t size

对象的大小。

int min_count

此对象的最小引用次数。如果在内存扫描期间发现的引用次数少于 min_count,则该对象将被报告为内存泄漏。如果 min_count 为 0,则永远不会将该对象报告为泄漏。如果 min_count 为 -1,则该对象将被忽略(不扫描,也不报告为泄漏)。

gfp_t gfp

用于 kmemleak 内部内存分配的 kmalloc() 标志。

描述

当分配新对象(内存块)时(kmem_cache_alloc、kmalloc 等),将从内核分配器调用此函数。

void __ref kmemleak_alloc_percpu(const void __percpu *ptr, size_t size, gfp_t gfp)

注册新分配的 __percpu 对象。

参数

const void __percpu *ptr

指向对象开头的 __percpu 指针。

size_t size

对象的大小。

gfp_t gfp

用于 kmemleak 内部内存分配的标志。

描述

当分配新对象(内存块)时(alloc_percpu),将从内核 percpu 分配器调用此函数。

void __ref kmemleak_vmalloc(const struct vm_struct *area, size_t size, gfp_t gfp)

注册新 vmalloc'ed 的对象。

参数

const struct vm_struct *area

指向 vm_struct 的指针。

size_t size

对象的大小。

gfp_t gfp

用于 kmemleak 内部内存分配的 __vmalloc() 标志。

描述

当分配新对象(内存块)时,将从 vmalloc() 内核分配器调用此函数。

void __ref kmemleak_free(const void *ptr)

注销先前注册的对象

参数

const void *ptr

指向对象开头的指针。

描述

当对象(内存块)被释放时(kmem_cache_free、kfree、vfree 等),会从内核分配器调用此函数。

void __ref kmemleak_free_part(const void *ptr, size_t size)

部分注销先前注册的对象

参数

const void *ptr

指向对象起始位置或内部的指针。这也表示要释放的范围的开始。

size_t size

要注销的大小

描述

当仅释放内存块的一部分时(通常来自 bootmem 分配器),会调用此函数。

void __ref kmemleak_free_percpu(const void __percpu *ptr)

注销先前注册的 __percpu 对象

参数

const void __percpu *ptr

指向对象开头的 __percpu 指针。

描述

当对象(内存块)被释放时(free_percpu),会从内核 percpu 分配器调用此函数。

void __ref kmemleak_update_trace(const void *ptr)

更新对象分配堆栈跟踪

参数

const void *ptr

指向对象开头的指针。

描述

在实际分配位置并非总是有效的情况下,覆盖对象分配堆栈跟踪。

void __ref kmemleak_not_leak(const void *ptr)

将已分配的对象标记为误报

参数

const void *ptr

指向对象开头的指针。

描述

在对象上调用此函数将导致内存块不再报告为泄漏,并且始终进行扫描。

void __ref kmemleak_transient_leak(const void *ptr)

将已分配的对象标记为瞬时误报

参数

const void *ptr

指向对象开头的指针。

描述

在对象上调用此函数将导致内存块暂时不被报告为泄漏。例如,如果对象是单链表的一部分,并且对其的 ->next 引用已更改,则可能会发生这种情况。

void __ref kmemleak_ignore(const void *ptr)

忽略已分配的对象

参数

const void *ptr

指向对象开头的指针。

描述

在对象上调用此函数将导致内存块被忽略(不扫描且不报告为泄漏)。当已知对应的块不是泄漏并且不包含对其他已分配内存块的任何引用时,通常会这样做。

void __ref kmemleak_scan_area(const void *ptr, size_t size, gfp_t gfp)

限制已分配对象中要扫描的范围

参数

const void *ptr

指向对象起始位置或内部的指针。这也表示扫描区域的开始。

size_t size

扫描区域的大小

gfp_t gfp

用于 kmemleak 内部内存分配的 kmalloc() 标志。

描述

当已知对象的某些部分包含对其他对象的引用时,会使用此函数。Kmemleak 将仅扫描这些区域,从而减少误报的数量。

void __ref kmemleak_no_scan(const void *ptr)

不扫描已分配的对象

参数

const void *ptr

指向对象开头的指针。

描述

此函数通知 kmemleak 不要扫描给定的内存块。在已知给定对象不包含对其他对象的任何引用的情况下很有用。Kmemleak 将不扫描此类对象,从而减少误报的数量。

void __ref kmemleak_alloc_phys(phys_addr_t phys, size_t size, gfp_t gfp)

类似于 kmemleak_alloc,但采用物理地址参数

参数

phys_addr_t phys

对象的物理地址

size_t size

对象的大小。

gfp_t gfp

用于 kmemleak 内部内存分配的 kmalloc() 标志。

void __ref kmemleak_free_part_phys(phys_addr_t phys, size_t size)

类似于 kmemleak_free_part,但采用物理地址参数

参数

phys_addr_t phys

如果对象的起始位置或内部的物理地址。这也表示要释放的范围的开始。

size_t size

要注销的大小

void __ref kmemleak_ignore_phys(phys_addr_t phys)

类似于 kmemleak_ignore,但采用物理地址参数

参数

phys_addr_t phys

对象的物理地址

void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap)

为给定的资源重新映射并提供 memmap 支持

参数

struct device *dev

res 的托管设备

struct dev_pagemap *pgmap

指向 struct dev_pagemap 的指针

注释

1/ 至少必须初始化 pgmap 的 range 和 type 成员

在将其传递给此函数之前,由调用者进行初始化

描述

2/ 可以选择初始化 altmap 字段,在这种情况下

必须在 pgmap->flags 中设置 PGMAP_ALTMAP_VALID。

3/ 可以选择提供 ref 字段,在这种情况下,pgmap->ref 必须

在进入时为“live”,并且将在 devm_memremap_pages_release() 时,或在此例程失败时被销毁和回收。

4/ range 预计是可行的宿主内存范围,可以

被视为“系统 RAM”范围,即不是设备 mmio 范围,但这不会强制执行。

struct dev_pagemap *get_dev_pagemap(unsigned long pfn, struct dev_pagemap *pgmap)

pfn 的 dev_pagemap 上获取新的 live 引用

参数

unsigned long pfn

用于查找 page_map 的页帧号

struct dev_pagemap *pgmap

可选的已知 pgmap,它已经有了一个引用

描述

如果 pgmap 为非 NULL 并且覆盖 pfn,则将按原样返回。 如果 pgmap 为非 NULL 但不覆盖 pfn,则对其的引用将被释放。

unsigned long vma_kernel_pagesize(struct vm_area_struct *vma)

此 VMA 的页面大小粒度。

参数

struct vm_area_struct *vma

用户映射。

描述

此 VMA 中的 Folio 将与此函数返回的字节数对齐,并且至少为该字节数的大小。

返回

支持 VMA 时分配的 Folio 的默认大小。

void folio_mark_accessed(struct folio *folio)

将 Folio 标记为已访问。

参数

struct folio *folio

要标记的 Folio。

描述

此函数将执行以下转换之一

  • 非活动,未引用 -> 非活动,已引用

  • 非活动,已引用 -> 活动,未引用

  • 活动,未引用 -> 活动,已引用

当新分配的 Folio 尚未可见时(因此对于非原子操作是安全的),可以使用 __folio_set_referenced() 代替 folio_mark_accessed()

void folio_add_lru(struct folio *folio)

将 Folio 添加到 LRU 列表。

参数

struct folio *folio

要添加到 LRU 的 Folio。

描述

将 Folio 排队以添加到 LRU。关于是否将页面添加到 [活动|非活动] [文件|匿名] 列表的决定将被推迟到 folio_batch 被耗尽。这使得 folio_add_lru() 的调用者有机会使用 folio_mark_accessed() 将 Folio 添加到活动列表中。

void folio_add_lru_vma(struct folio *folio, struct vm_area_struct *vma)

将 Folio 添加到此 VMA 的适当 LRU 列表中。

参数

struct folio *folio

要添加到 LRU 的 Folio。

struct vm_area_struct *vma

映射 Folio 的 VMA。

描述

如果 VMA 已 mlock,则将 folio 添加到不可驱逐列表。否则,它的处理方式与 folio_add_lru() 相同。

void deactivate_file_folio(struct folio *folio)

停用文件 Folio。

参数

struct folio *folio

要停用的 Folio。

描述

此函数向 VM 提示 folio 是一个很好的回收候选对象,例如,如果由于 Folio 脏或正在写回而导致其失效失败。

上下文

调用者持有对 Folio 的引用。

void folio_mark_lazyfree(struct folio *folio)

使匿名 Folio 延迟释放。

参数

struct folio *folio

要停用的 Folio。

描述

folio_mark_lazyfree()folio 移动到非活动文件列表。这样做是为了加速 folio 的回收。

void folios_put_refs(struct folio_batch *folios, unsigned int *refs)

减少一批 Folio 的引用计数。

参数

struct folio_batch *folios

这些 folio。

unsigned int *refs

要从每个 Folio 中减去的引用数。

描述

类似于 folio_put(),但用于一批 Folio。这比自己编写循环更有效,因为它将优化在释放 Folio 时需要获取的锁。Folio 批处理返回为空,并且可以重新用于另一个批处理;无需重新初始化它。如果 refs 为 NULL,我们将从每个 Folio 引用计数中减去 1。

上下文

可以在进程或中断上下文中调用,但不能在 NMI 上下文中调用。可以在持有自旋锁时调用。

void release_pages(release_pages_arg arg, int nr)

批量 put_page()

参数

release_pages_arg arg

要释放的页面数组

int nr

页面数

描述

减少 arg 中所有页面的引用计数。如果引用计数降至零,则从 LRU 中删除该页面并释放它。

请注意,参数可以是页面数组、编码页面或 Folio 指针。我们忽略任何编码位,并将它们中的任何一个转换为只是一个被释放的 Folio。

void folio_batch_remove_exceptionals(struct folio_batch *fbatch)

从批处理中修剪非 Folio。

参数

struct folio_batch *fbatch

要修剪的批处理

描述

find_get_entries() 使用 Folio 和 shadow/swap/DAX 条目填充批处理。此函数从 fbatch 中修剪所有非 Folio 条目,而不留下空洞,以便可以将其传递给仅 Folio 的批处理操作。

void zpool_register_driver(struct zpool_driver *driver)

注册一个 zpool 实现。

参数

struct zpool_driver *driver

要注册的驱动程序

int zpool_unregister_driver(struct zpool_driver *driver)

注销一个 zpool 实现。

参数

struct zpool_driver *driver

要注销的驱动程序。

描述

模块使用计数用于防止在卸载期间/之后使用驱动程序,因此如果从模块退出函数调用此函数,则此函数永远不会失败;如果从模块退出函数以外的其他函数调用此函数,并且此函数返回失败,则表示该驱动程序正在使用中,并且必须保持可用。

bool zpool_has_pool(char *type)

检查池驱动程序是否可用

参数

char *type

要检查的 zpool 的类型(例如,zbud、zsmalloc)

描述

这将检查 type 池驱动程序是否可用。如果需要,这将尝试加载请求的模块,但无法保证模块在调用后仍然被加载并立即可用。如果此函数返回 true,则调用者应假定该池可用,但必须准备好处理 zpool_create_pool() 返回失败的情况。但是,如果此函数返回 false,则调用者应假定请求的池类型不可用;请求的池类型模块不存在或无法加载,并且使用池类型调用 zpool_create_pool() 将会失败。

type 字符串必须以 null 结尾。

返回

如果 type 池可用,则为 true;否则为 false

struct zpool *zpool_create_pool(const char *type, const char *name, gfp_t gfp)

创建一个新的 zpool

参数

const char *type

要创建的 zpool 类型 (例如:zbud, zsmalloc)

const char *name

zpool 的名称 (例如:zram0, zswap)

gfp_t gfp

分配池时使用的 GFP 标志。

描述

这将创建指定类型的新 zpool。如果实现支持,则在分配内存时将使用 gfp 标志。如果 ops 参数为 NULL,则创建的 zpool 将不可驱逐。

实现必须保证这是线程安全的。

typename 字符串必须以 null 结尾。

返回

成功时返回新的 zpool,失败时返回 NULL。

void zpool_destroy_pool(struct zpool *zpool)

销毁一个 zpool

参数

struct zpool *zpool

要销毁的 zpool。

描述

实现必须保证这是线程安全的,但仅在销毁不同的池时。同一个池应该只被销毁一次,并且在销毁后不应再使用。

这将销毁现有的 zpool。zpool 不应在使用中。

const char *zpool_get_type(struct zpool *zpool)

获取 zpool 的类型

参数

struct zpool *zpool

要检查的 zpool

描述

这将返回池的类型。

实现必须保证这是线程安全的。

返回

zpool 的类型。

bool zpool_malloc_support_movable(struct zpool *zpool)

检查 zpool 是否支持分配可移动内存

参数

struct zpool *zpool

要检查的 zpool

描述

如果 zpool 支持分配可移动内存,则返回 true。

实现必须保证这是线程安全的。

返回

如果 zpool 支持分配可移动内存,则为 true;否则为 false

int zpool_malloc(struct zpool *zpool, size_t size, gfp_t gfp, unsigned long *handle)

分配内存

参数

struct zpool *zpool

从中分配的 zpool。

size_t size

要分配的内存量。

gfp_t gfp

分配内存时使用的 GFP 标志。

unsigned long *handle

指向要设置的句柄的指针

描述

这将从池中分配请求的内存量。如果实现支持,则在分配内存时将使用 gfp 标志。提供的 handle 将设置为已分配的对象句柄。

实现必须保证这是线程安全的。

返回

成功时为 0,错误时为负值。

void zpool_free(struct zpool *zpool, unsigned long handle)

释放先前分配的内存

参数

struct zpool *zpool

分配内存的 zpool。

unsigned long handle

要释放的内存的句柄。

描述

这将释放先前分配的内存。这并不保证池实际上会释放内存,只是池中的内存将可供池使用。

实现必须保证这是线程安全的,但仅在释放不同的句柄时。同一个句柄应该只被释放一次,并且在释放后不应再使用。

void *zpool_map_handle(struct zpool *zpool, unsigned long handle, enum zpool_mapmode mapmode)

将先前分配的句柄映射到内存中

参数

struct zpool *zpool

分配句柄的 zpool

unsigned long handle

要映射的句柄

enum zpool_mapmode mapmode

应如何映射内存

描述

这将把先前分配的句柄映射到内存中。mapmode 参数向实现指示内存将如何使用,即只读、只写、读写。如果实现不支持,则内存将被视为读写。

这可能会持有锁、禁用中断和/或抢占,并且必须调用 zpool_unmap_handle() 来撤消这些操作。使用映射句柄的代码应快速完成其在映射句柄内存上的操作,并尽快取消映射。由于实现可能会使用每个 CPU 的数据,因此不应在任何 CPU 上同时映射多个句柄。

返回

指向句柄映射的内存区域的指针。

void zpool_unmap_handle(struct zpool *zpool, unsigned long handle)

取消映射先前映射的句柄

参数

struct zpool *zpool

分配句柄的 zpool

unsigned long handle

要取消映射的句柄

描述

这将取消映射先前映射的句柄。实现中在 zpool_map_handle() 中进行的任何锁或其他操作都将在此处撤消。从 zpool_map_handle() 返回的内存区域在此之后不应再使用。

u64 zpool_get_total_pages(struct zpool *zpool)

池的总大小

参数

struct zpool *zpool

要检查的 zpool

描述

这将返回池的总大小(以页为单位)。

返回

zpool 的总大小(以页为单位)。

bool zpool_can_sleep_mapped(struct zpool *zpool)

测试 zpool 在执行映射时是否可以睡眠。

参数

struct zpool *zpool

要测试的 zpool

描述

某些分配器在 ->map() 回调中进入不可抢占的上下文(例如,禁用页面错误),并在 ->unmap() 中退出该上下文,这限制了我们可以使用映射对象执行的操作。例如,我们不能等待异步加密 API 来解压缩此类对象,也不能获取互斥锁,因为这些操作将调用调度程序。此函数告诉我们是否使用此类分配器。

返回

如果 zpool 可以睡眠,则为 true;否则为 false。

struct cgroup_subsys_state *mem_cgroup_css_from_folio(struct folio *folio)

与 folio 关联的 memcg 的 css

参数

struct folio *folio

感兴趣的 folio

描述

如果 memcg 绑定到默认层级结构,则返回与 folio 关联的 memcg 的 css。返回的 css 会一直与 folio 关联,直到被释放。

如果 memcg 绑定到传统层级结构,则返回 root_mem_cgroup 的 css。

ino_t page_cgroup_ino(struct page *page)

返回页面所归属的 memcg 的 inode 号

参数

struct page *page

该页面

描述

查找 page 所归属的内存 cgroup 的最近在线祖先,并返回其 inode 号,如果 page 未归属任何 cgroup,则返回 0。在不持有对 page 的引用的情况下调用此函数是安全的。

注意,此函数本质上是存在竞争的,因为无法阻止 cgroup inode 在 page_cgroup_ino() 返回后立即被销毁并可能重新分配,因此它只能被不关心的调用者使用(例如 procfs 接口)。

void __mod_memcg_state(struct mem_cgroup *memcg, enum memcg_stat_item idx, int val)

更新 cgroup 内存统计信息

参数

struct mem_cgroup *memcg

内存 cgroup

enum memcg_stat_item idx

统计项 - 可以是 enum memcg_stat_item 或 enum node_stat_item

int val

要添加到计数器的增量,可以是负数

void __mod_lruvec_state(struct lruvec *lruvec, enum node_stat_item idx, int val)

更新 lruvec 内存统计信息

参数

struct lruvec *lruvec

lruvec

enum node_stat_item idx

统计项

int val

要添加到计数器的增量,可以是负数

描述

lruvec 是 NUMA 节点和 cgroup 的交集。此函数会更新此级别状态更改影响的所有三个计数器:每个节点、每个 cgroup 和每个 lruvec。

void __count_memcg_events(struct mem_cgroup *memcg, enum vm_event_item idx, unsigned long count)

在 cgroup 中记录 VM 事件

参数

struct mem_cgroup *memcg

内存 cgroup

enum vm_event_item idx

事件项

unsigned long count

发生的事件数

struct mem_cgroup *get_mem_cgroup_from_mm(struct mm_struct *mm)

获取给定 mm_struct 的 memcg 的引用。

参数

struct mm_struct *mm

应从中提取 memcg 的 mm。可以为 NULL。

描述

获取 mm->memcg 的引用,如果成功则返回该引用。如果 mm 为 NULL,则按如下方式选择 memcg:1) 如果设置了活动 memcg。2) 如果可用,则为 current->mm->memcg。3) 根 memcg 如果禁用 mem_cgroup,则返回 NULL。

struct mem_cgroup *get_mem_cgroup_from_current(void)

获取当前任务的 memcg 的引用。

参数

void

无参数

struct mem_cgroup *get_mem_cgroup_from_folio(struct folio *folio)

获取给定 folio 的 memcg 的引用。

参数

struct folio *folio

应从中提取 memcg 的 folio。

struct mem_cgroup *mem_cgroup_iter(struct mem_cgroup *root, struct mem_cgroup *prev, struct mem_cgroup_reclaim_cookie *reclaim)

遍历内存 cgroup 层级结构

参数

struct mem_cgroup *root

层级结构的根

struct mem_cgroup *prev

先前返回的 memcg,第一次调用时为 NULL

struct mem_cgroup_reclaim_cookie *reclaim

用于共享回收遍历的 cookie,对于完整遍历为 NULL

描述

返回 root 下层级结构的子级的引用,或 root 本身,或完整往返后返回 NULL

调用者必须在后续调用中将返回值传递给 prev 以进行引用计数,或者使用 mem_cgroup_iter_break() 在往返完成之前取消层级结构遍历。

回收器可以在 reclaim 中指定一个节点,以便在同一节点上操作的所有并发回收器之间划分层级结构中的 memcg。

void mem_cgroup_iter_break(struct mem_cgroup *root, struct mem_cgroup *prev)

提前中止层级结构遍历

参数

struct mem_cgroup *root

层级结构的根

struct mem_cgroup *prev

mem_cgroup_iter() 返回的上次访问的层级结构成员

void mem_cgroup_scan_tasks(struct mem_cgroup *memcg, int (*fn)(struct task_struct*, void*), void *arg)

遍历内存 cgroup 层级结构的任务

参数

struct mem_cgroup *memcg

层级结构的根

int (*fn)(struct task_struct *, void *)

为每个任务调用的函数

void *arg

传递给 fn 的参数

描述

此函数遍历附加到 memcg 或其任何后代的任务,并为每个任务调用 fn。如果 fn 返回非零值,则该函数会中断迭代循环。否则,它将遍历所有任务并返回 0。

不得对根内存 cgroup 调用此函数。

struct lruvec *folio_lruvec_lock(struct folio *folio)

锁定 folio 的 lruvec。

参数

struct folio *folio

指向 folio 的指针。

描述

在以下任何条件下,都可以安全地使用这些函数: - folio 已锁定 - folio_test_lru 为 false - folio 已冻结(引用计数为 0)

返回

此 folio 所在的 lruvec,并持有其锁。

struct lruvec *folio_lruvec_lock_irq(struct folio *folio)

锁定 folio 的 lruvec。

参数

struct folio *folio

指向 folio 的指针。

描述

在以下任何条件下,都可以安全地使用这些函数: - folio 已锁定 - folio_test_lru 为 false - folio 已冻结(引用计数为 0)

返回

此 folio 所在的 lruvec,并持有其锁且中断被禁用。

struct lruvec *folio_lruvec_lock_irqsave(struct folio *folio, unsigned long *flags)

锁定 folio 的 lruvec。

参数

struct folio *folio

指向 folio 的指针。

unsigned long *flags

指向 irqsave 标志的指针。

描述

在以下任何条件下,都可以安全地使用这些函数: - folio 已锁定 - folio_test_lru 为 false - folio 已冻结(引用计数为 0)

返回

此 folio 所在的 lruvec,并持有其锁且中断被禁用。

void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru, int zid, int nr_pages)

用于记录添加或删除 lru 页的情况

参数

struct lruvec *lruvec

每个区域的内存 cgroup lru 向量

enum lru_list lru

页面所在的 lru 列表的索引

int zid

所记录页面的区域 ID

int nr_pages

添加时为正值,删除时为负值

描述

此函数必须在 lru_lock 下调用,在页面添加到 lru 列表之前或从 lru 列表删除之后立即调用。

unsigned long mem_cgroup_margin(struct mem_cgroup *memcg)

计算内存 cgroup 的可收费空间

参数

struct mem_cgroup *memcg

内存 cgroup

描述

以页为单位返回可以向 **mem** 收取的最大内存量。

void mem_cgroup_print_oom_context(struct mem_cgroup *memcg, struct task_struct *p)

打印与内存控制器相关的 OOM 信息。

参数

struct mem_cgroup *memcg

超出限制的内存 cgroup

struct task_struct *p

即将被杀死的任务

注意

当启用层次结构时,**memcg** 和 **p** 的 mem_cgroup 可能不同

void mem_cgroup_print_oom_meminfo(struct mem_cgroup *memcg)

打印与内存控制器相关的 OOM 内存信息。

参数

struct mem_cgroup *memcg

超出限制的内存 cgroup

struct mem_cgroup *mem_cgroup_get_oom_group(struct task_struct *victim, struct mem_cgroup *oom_domain)

获取在 OOM 后需要清理的内存 cgroup

参数

struct task_struct *victim

将被 OOM killer 杀死的任务

struct mem_cgroup *oom_domain

内存 cgroup OOM 情况下的 memcg,系统级 OOM 情况下的 NULL

描述

返回指向内存 cgroup 的指针,该内存 cgroup 必须通过杀死所有属于该 cgroup 的可被 OOM 杀死的任务来清理。

调用者必须在返回的非 NULL memcg 上调用 mem_cgroup_put()。

bool consume_stock(struct mem_cgroup *memcg, unsigned int nr_pages)

尝试消耗此 CPU 上已存储的费用。

参数

struct mem_cgroup *memcg

要从中消耗的 memcg。

unsigned int nr_pages

要收取的页面数。

描述

只有当 **memcg** 与当前 CPU 的 memcg 库存匹配,并且该库存中至少有 **nr_pages** 可用时,才会发生收费。未能为分配提供服务将重新填充库存。

成功时返回 true,否则返回 false。

void mem_cgroup_cancel_charge(struct mem_cgroup *memcg, unsigned int nr_pages)

取消未提交的 try_charge() 调用。

参数

struct mem_cgroup *memcg

先前收费的 memcg。

unsigned int nr_pages

先前收费的页面数。

void mem_cgroup_commit_charge(struct folio *folio, struct mem_cgroup *memcg)

提交先前成功的 try_charge()。

参数

struct folio *folio

要将费用提交到的 folio。

struct mem_cgroup *memcg

先前收费的 memcg。

int __memcg_kmem_charge_page(struct page *page, gfp_t gfp, int order)

将 kmem 页面计入当前内存 cgroup

参数

struct page *page

要收费的页面

gfp_t gfp

回收模式

int order

分配顺序

描述

成功时返回 0,失败时返回错误代码。

void __memcg_kmem_uncharge_page(struct page *page, int order)

取消 kmem 页面的收费

参数

struct page *page

要取消收费的页面

int order

分配顺序

void mem_cgroup_wb_stats(struct bdi_writeback *wb, unsigned long *pfilepages, unsigned long *pheadroom, unsigned long *pdirty, unsigned long *pwriteback)

从其 memcg 中检索与写回相关的统计信息

参数

struct bdi_writeback *wb

有问题的 bdi_writeback

unsigned long *pfilepages

文件页数的输出参数

unsigned long *pheadroom

根据 memcg,可分配页数的输出参数

unsigned long *pdirty

脏页数的输出参数

unsigned long *pwriteback

正在写回的页数的输出参数

描述

确定 **wb** 的 memcg 中的文件、预留空间、脏页和写回页面的数量。文件、脏页和写回是不言自明的。预留空间涉及的更多。

memcg 的预留空间为 “min(max, high) - used”。在层次结构中,预留空间计算为自身及其祖先的最低预留空间。请注意,这不考虑系统中可用的实际内存量。调用者应进一步相应地限制 **\*pheadroom**。

struct mem_cgroup *mem_cgroup_from_id(unsigned short id)

从 memcg ID 查找 memcg

参数

unsigned short id

要查找的 memcg ID

描述

调用者必须持有 rcu_read_lock()

void mem_cgroup_css_reset(struct cgroup_subsys_state *css)

重置 mem_cgroup 的状态

参数

struct cgroup_subsys_state *css

目标 css

描述

重置与 css 关联的 mem_cgroup 的状态。当用户空间请求禁用默认层次结构,但 memcg 通过依赖关系被固定时,会调用此方法。memcg 应停止应用策略,并应恢复到原始状态,因为它可能会再次可见。

当前实现仅重置基本配置。这需要扩展以涵盖所有可见的部分。

void mem_cgroup_calculate_protection(struct mem_cgroup *root, struct mem_cgroup *memcg)

检查内存消耗是否在正常范围内

参数

struct mem_cgroup *root

被检查子树的顶级祖先

struct mem_cgroup *memcg

要检查的内存 cgroup

描述

警告:此函数不是无状态的!它只能作为

自顶向下树迭代的一部分使用,不能用于孤立查询。

int mem_cgroup_hugetlb_try_charge(struct mem_cgroup *memcg, gfp_t gfp, long nr_pages)

尝试为 hugetlb 页面向 memcg 收费

参数

struct mem_cgroup *memcg

要收费的 memcg。

gfp_t gfp

回收模式。

long nr_pages

要收费的页数。

描述

当分配一个大页面时调用此函数,以确定 memcg 是否有能力容纳它。它不会立即提交费用,因为 hugetlb 页面本身尚未从 hugetlb 池中获取。

一旦我们获得了 hugetlb 页面,我们可以调用 mem_cgroup_commit_charge() 来提交费用。如果我们未能获取页面,我们应该调用 mem_cgroup_cancel_charge() 来撤销 try_charge() 的效果。

成功时返回 0。否则,返回一个错误代码。

int mem_cgroup_swapin_charge_folio(struct folio *folio, struct mm_struct *mm, gfp_t gfp, swp_entry_t entry)

为换入操作收取新分配的页面的费用。

参数

struct folio *folio

要收费的页面。

struct mm_struct *mm

受害者的 mm 上下文

gfp_t gfp

回收模式

swp_entry_t entry

分配页面的交换条目

描述

此函数为换入分配的页面收费。请在将页面添加到交换缓存之前调用此函数。

成功时返回 0。否则,返回一个错误代码。

void mem_cgroup_replace_folio(struct folio *old, struct folio *new)

为页面的替换收费。

参数

struct folio *old

当前循环的页面。

struct folio *new

替换页面。

描述

new 收费为 old 的替换页面。old 将在释放时取消收费。

两个页面都必须锁定,必须设置 new->mapping

void mem_cgroup_migrate(struct folio *old, struct folio *new)

将 memcg 数据从旧页面传输到新页面。

参数

struct folio *old

当前循环的页面。

struct folio *new

替换页面。

描述

将 memcg 数据从旧页面传输到新页面以进行迁移。旧页面的数据信息将被清除。请注意,在整个过程中,内存计数器将保持不变。

两个页面都必须锁定,必须设置 new->mapping

bool mem_cgroup_charge_skmem(struct mem_cgroup *memcg, unsigned int nr_pages, gfp_t gfp_mask)

为套接字内存收费

参数

struct mem_cgroup *memcg

要收费的 memcg

unsigned int nr_pages

要收费的页数

gfp_t gfp_mask

回收模式

描述

nr_pages 收费到 memcg。如果收费在 memcg 的配置限制范围内,则返回 true,如果不在范围内,则返回 false

void mem_cgroup_uncharge_skmem(struct mem_cgroup *memcg, unsigned int nr_pages)

取消套接字内存的收费

参数

struct mem_cgroup *memcg

要取消收费的 memcg

unsigned int nr_pages

要取消收费的页数

void mem_cgroup_swapout(struct folio *folio, swp_entry_t entry)

将 memsw 费用转移到交换空间

参数

struct folio *folio

要转移 memsw 费用的页面

swp_entry_t entry

要将费用移动到的交换条目

描述

folio 的 memsw 费用转移到 entry

int __mem_cgroup_try_charge_swap(struct folio *folio, swp_entry_t entry)

尝试为页面收取交换空间费用

参数

struct folio *folio

正在添加到交换空间的页面

swp_entry_t entry

要收费的交换条目

描述

尝试为 folio 的 memcg 收取 entry 处的交换空间费用。

成功时返回 0,失败时返回 -ENOMEM。

void __mem_cgroup_uncharge_swap(swp_entry_t entry, unsigned int nr_pages)

取消交换空间的收费

参数

swp_entry_t entry

要取消收费的交换条目

unsigned int nr_pages

要取消收费的交换空间量

bool obj_cgroup_may_zswap(struct obj_cgroup *objcg)

检查此 cgroup 是否可以进行 zswap

参数

struct obj_cgroup *objcg

对象 cgroup

描述

检查是否已达到分层 zswap 限制。

这不会检查特定的余量,也不是原子的。但是使用 zswap,只有在压缩发生后才知晓分配的大小,并且这种乐观的预检查可以避免在层次结构中的某个地方已经没有空间或完全禁用 zswap 时花费周期进行压缩。

void obj_cgroup_charge_zswap(struct obj_cgroup *objcg, size_t size)

为压缩后端内存收费

参数

struct obj_cgroup *objcg

对象 cgroup

size_t size

压缩对象的大小

描述

obj_cgroup_may_zswap() 允许压缩并为该 cgroup 在 zwap 中存储后,强制进行收费。

void obj_cgroup_uncharge_zswap(struct obj_cgroup *objcg, size_t size)

取消压缩后端内存的收费

参数

struct obj_cgroup *objcg

对象 cgroup

size_t size

压缩对象的大小

描述

在页面调入时,取消 zswap 内存的收费。

void shmem_recalc_inode(struct inode *inode, long alloced, long swapped)

重新计算 inode 的块使用情况

参数

struct inode *inode

要重新计算的 inode

long alloced

分配给 inode 的页数变化

long swapped

从 inode 交换出去的页数变化

描述

我们必须计算空闲块,因为 mm 可能会在我们背后丢弃未修改的空洞页。

但通常 info->alloced == inode->i_mapping->nrpages + info->swapped,所以 mm 释放的是 info->alloced - (inode->i_mapping->nrpages + info->swapped)

int shmem_get_folio(struct inode *inode, pgoff_t index, loff_t write_end, struct folio **foliop, enum sgp_type sgp)

查找并锁定一个 shmem folio。

参数

struct inode *inode

要搜索的 inode

pgoff_t index

页索引。

loff_t write_end

写入的结尾,可能会扩展 inode 大小

struct folio **foliop

如果找到,指向 folio 的指针

enum sgp_type sgp

控制行为的 SGP_* 标志

描述

在 **inode** & **index** 处查找页面缓存条目。 如果存在 folio,则返回锁定的 folio 并增加引用计数。

如果调用者修改了 folio 中的数据,则必须在解锁 folio 之前调用 folio_mark_dirty(),以确保 folio 不会被回收。 在调用 folio_mark_dirty() 之前,无需预留空间。

当未找到 folio 时,行为取决于 **sgp**
  • 对于 SGP_READ,**foliop** 为 NULL 并返回 0

  • 对于 SGP_NOALLOC,**foliop** 为 NULL 并返回 -ENOENT

  • 对于所有其他标志,将分配一个新的 folio,插入页面缓存并在 **foliop** 中返回锁定状态。

上下文

可能睡眠。

返回

如果成功,则为 0,否则为负错误代码。

struct file *shmem_kernel_file_setup(const char *name, loff_t size, unsigned long flags)

获取一个位于 tmpfs 中且必须是内核内部的未链接文件。 将不会对底层 inode 进行 LSM 权限检查。 因此,此接口的用户必须在更高层执行 LSM 检查。 用户是 big_key 和 shm 实现。 LSM 检查是在 key 或 shm 级别而不是 inode 级别提供的。

参数

const char *name

dentry 的名称(在 /proc//maps 中可见)

loff_t size

要为文件设置的大小

unsigned long flags

VM_NORESERVE 抑制整个对象大小的预先计算

struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags)

获取一个位于 tmpfs 中的未链接文件

参数

const char *name

dentry 的名称(在 /proc//maps 中可见)

loff_t size

要为文件设置的大小

unsigned long flags

VM_NORESERVE 抑制整个对象大小的预先计算

struct file *shmem_file_setup_with_mnt(struct vfsmount *mnt, const char *name, loff_t size, unsigned long flags)

获取一个位于 tmpfs 中的未链接文件

参数

struct vfsmount *mnt

将在其中创建文件的 tmpfs 挂载点

const char *name

dentry 的名称(在 /proc//maps 中可见)

loff_t size

要为文件设置的大小

unsigned long flags

VM_NORESERVE 抑制整个对象大小的预先计算

int shmem_zero_setup(struct vm_area_struct *vma)

设置一个共享的匿名映射

参数

struct vm_area_struct *vma

由 do_mmap 准备要 mmap 的 vma

struct folio *shmem_read_folio_gfp(struct address_space *mapping, pgoff_t index, gfp_t gfp)

读取到页面缓存中,使用指定的页面分配标志。

参数

struct address_space *mapping

folio 的 address_space

pgoff_t index

folio 索引

gfp_t gfp

如果分配页面要使用的页面分配器标志

描述

此行为类似于 tmpfs 的 “read_cache_page_gfp(mapping, index, gfp)”,任何新的页面分配都使用指定的分配标志完成。 但是 read_cache_page_gfp() 使用 ->read_folio() 方法:这不适合 tmpfs,因为它可能在交换缓存中有页面,需要自己查找这些页面;尽管 drivers/gpu/drm i915 和 ttm 依赖于此支持。

i915_gem_object_get_pages_gtt() 将 __GFP_NORETRY | __GFP_NOWARN 与 mapping_gfp_mask() 混合使用,以避免不必要地耗尽机器的内存。

int migrate_vma_setup(struct migrate_vma *args)

准备迁移一定范围的内存

参数

struct migrate_vma *args

包含迁移的 vma、开始和 pfn 数组

返回

失败时返回负的 errno,成功迁移 0 个或多个页面时返回 0。

描述

准备迁移一段内存虚拟地址范围,方法是收集该范围内每个虚拟地址所支持的所有页面,并将它们保存在 src 数组中。然后锁定这些页面并取消映射。一旦页面被锁定并取消映射,检查每个页面是否被固定。未被固定的页面,其对应的 src 数组条目中会设置 MIGRATE_PFN_MIGRATE 标志(由该函数设置)。然后通过重新映射和解锁来恢复任何被固定的页面。

调用者应为所有设置了 MIGRATE_PFN_VALID 和 MIGRATE_PFN_MIGRATE 标志的条目分配目标内存,并将源内存复制到目标内存。一旦这些内存被分配和复制,调用者必须更新 dst 数组中每个对应的条目,使其包含目标页面的 pfn 值并设置 MIGRATE_PFN_VALID 标志。目标页面必须通过 lock_page() 锁定。

请注意,除非是从设备内存迁移到系统内存,否则调用者不必迁移 src 数组中标记有 MIGRATE_PFN_MIGRATE 标志的所有页面。如果调用者无法将设备页面迁移回系统内存,则必须返回 VM_FAULT_SIGBUS,这对用户空间进程有严重后果,因此必须尽可能避免。

对于 CPU 页表中为空的条目(pte_none() 或 pmd_none() 为 true),我们会在对应的源数组中设置 MIGRATE_PFN_MIGRATE 标志,从而允许调用者为那些未支持的虚拟地址分配设备内存。为此,调用者只需分配设备内存并像常规迁移一样正确设置目标条目。请注意,这仍然可能失败,因此在设备驱动程序中,必须在调用 migrate_vma_pages() 后检查这些条目的迁移是否成功,就像常规迁移一样。

之后,调用者必须调用 migrate_vma_pages() 来遍历 src 数组中所有设置了 MIGRATE_PFN_VALID 和 MIGRATE_PFN_MIGRATE 标志的条目。如果 dst 数组中对应的条目设置了 MIGRATE_PFN_VALID 标志,则 migrate_vma_pages() 会将 struct page 信息从源 struct page 迁移到目标 struct page。如果无法迁移 struct page 信息,则它会清除 src 数组中的 MIGRATE_PFN_MIGRATE 标志。

此时,所有成功迁移的页面在 src 数组中都有一个条目,其设置了 MIGRATE_PFN_VALID 和 MIGRATE_PFN_MIGRATE 标志,并且 dst 数组条目设置了 MIGRATE_PFN_VALID 标志。

一旦 migrate_vma_pages() 返回,调用者可以检查哪些页面成功迁移,哪些页面没有。成功迁移的页面的 src 数组条目会设置 MIGRATE_PFN_MIGRATE 标志。

migrate_vma_pages() 之后更新设备页表是安全的,因为目标页面和源页面仍然被锁定,并且 mmap_lock 处于读取模式(因此没有人可以取消映射正在迁移的范围)。

一旦调用者完成清理工作并更新其页表(如果选择这样做,这不是强制性的),它最终会调用 migrate_vma_finalize() 来更新 CPU 页表,使其指向成功迁移页面的新页面,否则将其恢复为指向原始源页面。

void migrate_device_pages(unsigned long *src_pfns, unsigned long *dst_pfns, unsigned long npages)

将元数据从源页面迁移到目标页面

参数

unsigned long *src_pfns

migrate_device_range() 返回的 src_pfns

unsigned long *dst_pfns

驱动程序分配的用于迁移内存的目标 pfn 数组

unsigned long npages

范围内的页面数

描述

等效于 migrate_vma_pages()。此函数用于将 struct page 元数据从源 struct page 迁移到目标 struct page。

void migrate_vma_pages(struct migrate_vma *migrate)

将元数据从源页面迁移到目标页面

参数

struct migrate_vma *migrate

包含所有迁移信息的 migrate 结构

描述

此函数将 struct page 元数据从源 struct page 迁移到目标 struct page。这有效地完成了从源页面到目标页面的迁移。

void migrate_vma_finalize(struct migrate_vma *migrate)

恢复 CPU 页表条目

参数

struct migrate_vma *migrate

包含所有迁移信息的 migrate 结构

描述

此函数将特殊的迁移 pte 条目替换为指向新页面的映射(如果该页面的迁移成功),或者指向原始页面的映射(否则)。

此函数还会解锁页面,并将它们放回 lru,或者对于设备页面,删除额外的引用计数。

int migrate_device_range(unsigned long *src_pfns, unsigned long start, unsigned long npages)

将设备私有 pfn 迁移到正常内存。

参数

unsigned long *src_pfns

足够大的数组,用于保存正在迁移的源设备私有 pfn。

unsigned long start

要迁移的范围内的起始 pfn。

unsigned long npages

要迁移的页面数。

描述

migrate_vma_setup() 在概念上类似于 migrate_vma_setup(),除了它不是基于虚拟地址映射查找页面,而是使用应迁移到系统内存的设备 pfn 范围。

当驱动程序需要释放设备内存,但不知道设备内存中每个页面的虚拟映射时,这很有用。例如,当驱动程序正在被卸载或从设备解除绑定时,通常会发生这种情况。

migrate_vma_setup() 一样,此函数将获取引用并锁定任何在取消映射之前未释放的正在迁移的页面。然后,驱动程序可以分配目标页面,并开始将数据从设备复制到 CPU 内存,然后再调用 migrate_device_pages()

struct wp_walk

用于页表遍历回调的私有结构

定义:

struct wp_walk {
    struct mmu_notifier_range range;
    unsigned long tlbflush_start;
    unsigned long tlbflush_end;
    unsigned long total;
};

成员

范围

用于 mmu 通知器的范围

tlbflush_start

第一个修改的 pte 的地址

tlbflush_end

最后一个修改的 pte 的地址 + 1

total

修改的 pte 总数

int wp_pte(pte_t *pte, unsigned long addr, unsigned long end, struct mm_walk *walk)

写保护一个 pte

参数

pte_t *pte

指向 pte 的指针

unsigned long addr

要保护的虚拟地址的起始位置

unsigned long end

要保护的虚拟地址的结束位置

struct mm_walk *walk

页表遍历回调参数

描述

此函数写保护一个 pte,并记录已接触的 pte 在虚拟地址空间中的范围,以便进行高效的范围 TLB 刷新。

struct clean_walk

用于 clean_record_pte 函数的私有结构体。

定义:

struct clean_walk {
    struct wp_walk base;
    pgoff_t bitmap_pgoff;
    unsigned long *bitmap;
    pgoff_t start;
    pgoff_t end;
};

成员

基址

我们从中派生的 struct wp_walk

bitmap_pgoff

bitmap 中第一个位的地址空间页偏移量

bitmap

位图,其中每一位对应地址空间范围内的一个页偏移量。

start

相对于 bitmap_pgoff 的第一个修改的 pte 的地址空间页偏移量

end

相对于 bitmap_pgoff 的最后一个修改的 pte 的地址空间页偏移量

int clean_record_pte(pte_t *pte, unsigned long addr, unsigned long end, struct mm_walk *walk)

清除一个 pte 并将其地址空间偏移量记录到位图中

参数

pte_t *pte

指向 pte 的指针

unsigned long addr

要清除的虚拟地址的起始位置

unsigned long end

要清除的虚拟地址的结束位置

struct mm_walk *walk

页表遍历回调参数

描述

此函数清除一个 pte,并记录已接触的 pte 在虚拟地址空间中的范围,以便进行高效的 TLB 刷新。它还将脏 pte 记录在表示地址空间中页偏移量的位图中,以及已接触的第一个和最后一个位。

unsigned long wp_shared_mapping_range(struct address_space *mapping, pgoff_t first_index, pgoff_t nr)

写保护地址空间范围内的所有 pte

参数

struct address_space *mapping

我们要写保护的地址空间

pgoff_t first_index

范围内的第一个页偏移量

pgoff_t nr

要覆盖的增量页偏移量数量

注意

此函数目前会跳过 transhuge 页表项,因为它旨在用于 PTE 级别的脏跟踪。但是,它会在遇到已启用写入的 transhuge 项时发出警告,并且可以很容易地扩展为处理它们。

返回

实际写保护的 pte 数量。请注意,已写保护的 pte 不会计算在内。

unsigned long clean_record_shared_mapping_range(struct address_space *mapping, pgoff_t first_index, pgoff_t nr, pgoff_t bitmap_pgoff, unsigned long *bitmap, pgoff_t *start, pgoff_t *end)

清除并记录地址空间范围内的所有 pte

参数

struct address_space *mapping

我们要清除的地址空间

pgoff_t first_index

范围内的第一个页偏移量

pgoff_t nr

要覆盖的增量页偏移量数量

pgoff_t bitmap_pgoff

bitmap 中第一个位的页偏移量

unsigned long *bitmap

指向至少 nr 位的位图的指针。该位图需要覆盖整个范围 first_index..**first_index** + nr

pgoff_t *start

指向 bitmap 中第一个设置位的编号的指针。当函数设置新位时,会对其进行修改。

pgoff_t *end

指向 bitmap 中最后一个设置位的编号的指针。无设置。当函数设置新位时,会修改该值。

描述

当此函数返回时,无法保证 CPU 尚未弄脏新的 pte。但是,它不会清除位图中未报告的任何 pte。保证如下:

  • 当函数开始执行时,所有脏 pte 都将最终记录在位图中。

  • 之后弄脏的所有 pte 将保持脏状态,记录在位图中,或两者都记录。

如果调用者需要确保拾取所有脏 pte 并且不添加任何额外的脏 pte,则首先需要写保护地址空间范围,并确保新的写入者在 page_mkwrite() 或 pfn_mkwrite() 中被阻止。然后在写保护后进行 TLB 刷新,拾取所有脏位。

此函数目前会跳过 transhuge 页表项,因为它旨在用于 PTE 级别的脏跟踪。但是,它会在遇到脏的 transhuge 项时发出警告,并且可以很容易地扩展为处理它们。

返回

实际清除的脏 pte 数量。

bool pcpu_addr_in_chunk(struct pcpu_chunk *chunk, void *addr)

检查地址是否由此块提供

参数

struct pcpu_chunk *chunk

感兴趣的块

void *addr

percpu 地址

返回

如果地址由此块提供,则为 True。

bool pcpu_check_block_hint(struct pcpu_block_md *block, int bits, size_t align)

根据 contig 提示进行检查

参数

struct pcpu_block_md *block

感兴趣的块

int bits

分配的大小

size_t align

区域的对齐方式(最大 PAGE_SIZE)

描述

检查分配是否适合该块的 contig 提示。请注意,块使用与块相同的提示,因此也可以针对块的 contig 提示进行检查。

void pcpu_next_md_free_region(struct pcpu_chunk *chunk, int *bit_off, int *bits)

查找下一个提示的空闲区域

参数

struct pcpu_chunk *chunk

感兴趣的块

int *bit_off

块偏移量

int *bits

空闲区域的大小

描述

pcpu_for_each_md_free_region 的辅助函数。它检查 block->contig_hint 并跨块执行聚合以查找下一个提示。它就地修改 bit_off 和 bits,以便在循环中使用。

void pcpu_next_fit_region(struct pcpu_chunk *chunk, int alloc_bits, int align, int *bit_off, int *bits)

查找给定分配请求的合适区域

参数

struct pcpu_chunk *chunk

感兴趣的块

int alloc_bits

分配的大小

int align

区域的对齐方式(最大 PAGE_SIZE)

int *bit_off

块偏移量

int *bits

空闲区域的大小

描述

查找下一个对于给定大小和对齐方式可用的空闲区域。仅当存在可用于此分配的有效区域时,才会返回。如果分配请求适合该块,则返回 block->first_free,以查看请求是否可以在 contig 提示之前实现。

void *pcpu_mem_zalloc(size_t size, gfp_t gfp)

分配内存

参数

size_t size

要分配的字节数

gfp_t gfp

分配标志

描述

分配 size 字节。如果 size 小于 PAGE_SIZE,则使用 kzalloc();否则,使用等效于 vzalloc() 的方法。这样做是为了方便传递白名单标志。返回的内存始终被清零。

返回

成功时返回指向已分配区域的指针,失败时返回 NULL。

void pcpu_mem_free(void *ptr)

释放内存

参数

void *ptr

要释放的内存

描述

释放 ptrptr 应该使用 pcpu_mem_zalloc() 分配。

void pcpu_chunk_relocate(struct pcpu_chunk *chunk, int oslot)

将 chunk 放入相应的 chunk 插槽中

参数

struct pcpu_chunk *chunk

感兴趣的块

int oslot

之前所在的插槽

描述

此函数在分配或释放更改 chunk 后调用。根据更改后的状态确定新的插槽,并将 chunk 移动到该插槽。请注意,保留的 chunk 永远不会放入 chunk 插槽中。

上下文

pcpu_lock。

void pcpu_block_update(struct pcpu_block_md *block, int start, int end)

根据空闲区域更新块

参数

struct pcpu_block_md *block

感兴趣的块

int start

块中的起始偏移量

int end

块中的结束偏移量

描述

根据已知的空闲区域更新块。区域 [start, end) 应为块内空闲区域的全部。如果 contig 提示相等,则选择最佳起始偏移量。

void pcpu_chunk_refresh_hint(struct pcpu_chunk *chunk, bool full_scan)

更新有关 chunk 的元数据

参数

struct pcpu_chunk *chunk

感兴趣的块

bool full_scan

是否应从头开始扫描

描述

迭代元数据块以查找最大的连续区域。如果打破了 contig_hint,则可以在分配路径上避免完全扫描,因为会触发完全扫描。这样,scan_hint 将在 contig_hint 之前,或者如果 scan_hint == contig_hint,则在之后。在释放时无法阻止这种情况,因为我们想找到可能跨越块的最大区域。

void pcpu_block_refresh_hint(struct pcpu_chunk *chunk, int index)

参数

struct pcpu_chunk *chunk

感兴趣的块

int index

元数据块的索引

描述

从 first_free 开始扫描块,并相应地更新块元数据。

void pcpu_block_update_hint_alloc(struct pcpu_chunk *chunk, int bit_off, int bits)

更新分配路径上的提示

参数

struct pcpu_chunk *chunk

感兴趣的块

int bit_off

块偏移量

int bits

请求的大小

描述

更新分配路径的元数据。仅当 chunk 的 contig 提示被打破时,才必须通过完全扫描来刷新元数据。如果块的 contig 提示被打破,则需要进行块级扫描。

void pcpu_block_update_hint_free(struct pcpu_chunk *chunk, int bit_off, int bits)

更新释放路径上的块提示

参数

struct pcpu_chunk *chunk

感兴趣的块

int bit_off

块偏移量

int bits

请求的大小

描述

更新分配路径的元数据。这通过利用块 contig 提示避免了盲目块刷新。如果失败,它会向前和向后扫描以确定空闲区域的范围。这被限制在块的边界内。

如果页面变为空闲、块变为空闲或释放跨越多个块,则会触发 chunk 更新。这种权衡是为了最大限度地减少迭代块元数据以更新 chunk_md->contig_hint。chunk_md->contig_hint 可能最多偏移一个页面,但它永远不会大于可用空间。如果 contig 提示包含在一个块中,它将是准确的。

bool pcpu_is_populated(struct pcpu_chunk *chunk, int bit_off, int bits, int *next_off)

确定区域是否已填充

参数

struct pcpu_chunk *chunk

感兴趣的块

int bit_off

块偏移量

int bits

区域大小

int *next_off

开始搜索的下一个偏移量的返回值

描述

对于原子分配,请检查后备页面是否已填充。

返回

如果后备页面已填充,则为 Bool 值。next_index 用于跳过 pcpu_find_block_fit 中未填充的块。

int pcpu_find_block_fit(struct pcpu_chunk *chunk, int alloc_bits, size_t align, bool pop_only)

查找开始搜索的块索引

参数

struct pcpu_chunk *chunk

感兴趣的块

int alloc_bits

请求的大小(以分配单位为单位)

size_t align

区域的对齐方式(最大 PAGE_SIZE 字节)

bool pop_only

仅使用已填充的区域

描述

给定一个 chunk 和一个分配规范,找到开始搜索空闲区域的偏移量。它会迭代位图元数据块以查找保证满足要求的偏移量。它并非完全是首次适配,因为如果分配不适合块或 chunk 的 contig 提示,则会跳过它。这谨慎地防止了过度迭代。对齐不佳可能会导致分配器跳过具有有效空闲区域的块和 chunk。

返回

位图中开始搜索的偏移量。如果未找到偏移量,则为 -1。

int pcpu_alloc_area(struct pcpu_chunk *chunk, int alloc_bits, size_t align, int start)

从 pcpu_chunk 分配区域

参数

struct pcpu_chunk *chunk

感兴趣的块

int alloc_bits

请求的大小(以分配单位为单位)

size_t align

区域的对齐方式(最大 PAGE_SIZE)

int start

开始搜索的 bit_off

描述

此函数接收一个 start 偏移量以开始搜索,以适合具有对齐方式 alignalloc_bits 分配。它需要扫描分配映射,因为如果它适合块的 contig 提示,则 start 将为 block->first_free。这是在打破 contig 提示之前尝试填充分配。

返回

成功时返回在 chunk 中分配的 addr 偏移量。如果找不到匹配的区域,则为 -1。

int pcpu_free_area(struct pcpu_chunk *chunk, int off)

释放相应的偏移量

参数

struct pcpu_chunk *chunk

感兴趣的块

int off

块内的地址偏移量

描述

此函数使用边界位图确定要释放的分配的大小,并清除分配映射。

返回

已释放的字节数。

struct pcpu_chunk *pcpu_alloc_first_chunk(unsigned long tmp_addr, int map_size)

创建用作第一个块的块

参数

unsigned long tmp_addr

所服务区域的起始地址

int map_size

所服务区域的大小

描述

此函数负责创建用作第一个块的块。base_addrtmp_addr 向下页对齐的地址,而区域末尾是向上页对齐的地址。跟踪偏移量以确定所服务的区域。完成这一切是为了避免位图分配器中的部分块。

返回

tmp_addr 处服务 map_size 区域的块。

void pcpu_chunk_populated(struct pcpu_chunk *chunk, int page_start, int page_end)

填充后记账

参数

struct pcpu_chunk *chunk

已填充的 pcpu_chunk

int page_start

起始页

int page_end

结束页

描述

[page_start,**page_end**) 中的页面已填充到 chunk。相应地更新记账信息。必须在每次成功填充后调用。

void pcpu_chunk_depopulated(struct pcpu_chunk *chunk, int page_start, int page_end)

解除填充后记账

参数

struct pcpu_chunk *chunk

已解除填充的 pcpu_chunk

int page_start

起始页

int page_end

结束页

描述

[page_start,**page_end**) 中的页面已从 chunk 中解除填充。相应地更新记账信息。必须在每次成功解除填充后调用。

确定包含指定地址的块

参数

void *addr

需要确定块的地址。

描述

这是一个内部函数,处理除静态分配之外的所有分配。静态 percpu 地址值不应传递给分配器。

返回

找到的块的地址。

void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved, gfp_t gfp)

percpu 分配器

参数

size_t size

要分配的区域的大小(以字节为单位)

size_t align

区域的对齐方式(最大 PAGE_SIZE)

bool reserved

如果可用,则从保留的块中分配

gfp_t gfp

分配标志

描述

分配对齐到 alignsize 字节的 percpu 区域。如果 gfp 不包含 GFP_KERNEL,则分配是原子的。如果 gfp 具有 __GFP_NOWARN,则不会在无效或失败的分配请求上触发警告。

返回

成功时,指向已分配区域的 Percpu 指针;失败时为 NULL。

void pcpu_balance_free(bool empty_only)

管理空闲块的数量

参数

bool empty_only

仅当没有填充页面时才释放块

描述

如果 empty_only 为 false,则回收所有完全空闲的块,而与填充页面的数量无关。否则,仅回收没有填充页面的块。

上下文

pcpu_lock(可以临时删除)

void pcpu_balance_populated(void)

管理填充页面的数量

参数

void

无参数

描述

保持一定数量的填充页面以满足原子分配。当物理内存稀缺时,可能会调用此方法,从而导致 OOM 终止进程。我们应该避免这样做,直到实际分配导致失败,因为请求有可能从已备份的区域中获得服务。

上下文

pcpu_lock(可以临时删除)

void pcpu_reclaim_populated(void)

扫描要解除填充的块并释放空页面

参数

void

无参数

描述

扫描解除填充列表中的块,并尝试将未使用的填充页面释放回系统。解除填充的块会被搁置,以防止重新填充这些页面,除非需要。完全空闲的块会被重新整合并相应地释放(保留 1 个)。如果我们低于空填充页面阈值,则如果块有空闲页面,则重新整合该块。每个块都按相反的顺序扫描,以使填充页面靠近块的开头。

上下文

pcpu_lock(可以临时删除)

void pcpu_balance_workfn(struct work_struct *work)

管理空闲块和填充页面的数量

参数

struct work_struct *work

未使用

描述

对于每种块类型,管理完全空闲的块的数量和填充页面的数量。要考虑的一个重要问题是页面何时被释放以及它们如何影响全局计数。

void free_percpu(void __percpu *ptr)

释放 percpu 区域

参数

void __percpu *ptr

指向要释放区域的指针

描述

释放 percpu 区域 ptr

上下文

可以从原子上下文中调用。

bool is_kernel_percpu_address(unsigned long addr)

测试地址是否来自静态 percpu 区域

参数

unsigned long addr

要测试的地址

描述

测试 addr 是否属于内核静态 percpu 区域。不考虑模块静态 percpu 区域。对于这些区域,请使用 is_module_percpu_address()。

返回

如果 addr 来自内核静态 percpu 区域,则为 true,否则为 false

phys_addr_t per_cpu_ptr_to_phys(void *addr)

将转换后的 percpu 地址转换为物理地址

参数

void *addr

要转换为物理地址的地址

描述

给定通过 percpu 访问宏之一获得的、可取消引用的地址 addr,此函数将其转换为其物理地址。调用者负责确保 addr 在此函数完成之前保持有效。

percpu 分配器对第一个块有特殊的设置,目前支持嵌入线性地址空间或 vmalloc 映射,并且从第二个块开始,后备分配器(当前为 vm 或 km)提供转换。

可以简单地转换地址,而无需检查它是否属于第一个块。但当前代码更好地反映了 percpu 分配器实际的工作方式,并且验证可以发现 percpu 分配器本身和 per_cpu_ptr_to_phys() 调用者中的错误。因此,我们保留当前代码。

返回

addr 的物理地址。

struct pcpu_alloc_info *pcpu_alloc_alloc_info(int nr_groups, int nr_units)

分配 percpu 分配信息

参数

int nr_groups

组的数量

int nr_units

单元的数量

描述

分配足够大的 ai 以容纳包含 nr_units 个单元的 nr_groups 个组。返回的 ai 的 groups[0].cpu_map 指向 cpu_map 数组,该数组足够长以容纳 nr_units 并且填充了 NR_CPUS。初始化其他组的 cpu_map 指针是调用者的责任。

返回

成功时,指向分配的 pcpu_alloc_info 的指针;失败时为 NULL。

void pcpu_free_alloc_info(struct pcpu_alloc_info *ai)

释放 percpu 分配信息

参数

struct pcpu_alloc_info *ai

要释放的 pcpu_alloc_info

描述

释放由 pcpu_alloc_alloc_info() 分配的 ai

void pcpu_dump_alloc_info(const char *lvl, const struct pcpu_alloc_info *ai)

打印有关 pcpu_alloc_info 的信息

参数

const char *lvl

日志级别

const struct pcpu_alloc_info *ai

要转储的分配信息

描述

使用日志级别 lvl 打印有关 ai 的信息。

void pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai, void *base_addr)

初始化第一个 percpu 块

参数

const struct pcpu_alloc_info *ai

描述 percpu 区域如何形成的 pcpu_alloc_info

void *base_addr

映射的地址

描述

初始化包含内核静态 percpu 区域的第一个 percpu 块。此函数应从 arch percpu 区域设置路径调用。

ai 包含初始化第一个块和启动动态 percpu 分配器所需的所有信息。

ai->static_size 是静态 percpu 区域的大小。

ai->reserved_size 如果非零,则指定在第一个块中的静态区域之后要保留的字节数。这会保留第一个块,使其只能通过保留的 percpu 分配使用。这主要用于在寻址模型对符号重定位的偏移范围有限的架构上,服务模块 percpu 静态区域,以保证模块 percpu 符号落在可重定位的范围内。

ai->dyn_size 确定第一个块中可用于动态分配的字节数。ai->static_size + ai->reserved_size + ai->dyn_sizeai->unit_size 之间的区域未使用。

ai->unit_size 指定单元大小,并且必须与 PAGE_SIZE 对齐,并且等于或大于 ai->static_size + ai->reserved_size + ai->dyn_size

ai->atom_size 是分配原子大小,并用作 vm 区域的对齐方式。

ai->alloc_size 是分配大小,始终是 ai->atom_size 的倍数。如果 ai->unit_size 大于 ai->atom_size,则此值大于 ai->atom_size

ai->nr_groupsai->groups 描述 percpu 区域的虚拟内存布局。应放置在一起的单元放在同一组中。动态 VM 区域将根据这些分组进行分配。如果 ai->nr_groups 为零,则假设一个包含所有单元的组。

调用者应该在 base_addr 处映射了第一个块,并将静态数据复制到每个单元。

第一个块将始终包含一个静态区域和一个动态区域。但是,静态区域不受任何块管理。如果第一个块还包含保留区域,则由两个块服务 - 一个用于保留区域,一个用于动态区域。它们共享相同的 vm,但在区域分配映射中使用偏移区域。服务于动态区域的块在块槽中循环,并且像任何其他块一样可用于动态分配。

struct pcpu_alloc_info *pcpu_build_alloc_info(size_t reserved_size, size_t dyn_size, size_t atom_size, pcpu_fc_cpu_distance_fn_t cpu_distance_fn)

构建考虑 CPU 之间距离的 alloc_info

参数

size_t reserved_size

保留的 percpu 区域的大小,以字节为单位

size_t dyn_size

动态分配的最小可用大小,以字节为单位

size_t atom_size

分配原子大小

pcpu_fc_cpu_distance_fn_t cpu_distance_fn

确定 CPU 之间距离的回调,可选

描述

此函数确定单元的分组、它们到 CPU 的映射以及其他参数,考虑了所需的 percpu 大小、分配原子大小和 CPU 之间的距离。

组始终是原子大小的倍数,并且在两个方向上都处于 LOCAL_DISTANCE 的 CPU 分组在一起,并在同一组中共享单元空间。返回的配置保证不同节点上的 CPU 位于不同的组中,并且 >=75% 的已分配虚拟地址空间被使用。

返回

成功时,返回指向新 allocation_info 的指针。失败时,返回 ERR_PTR 值。

int pcpu_embed_first_chunk(size_t reserved_size, size_t dyn_size, size_t atom_size, pcpu_fc_cpu_distance_fn_t cpu_distance_fn, pcpu_fc_cpu_to_node_fn_t cpu_to_nd_fn)

将第一个 percpu 块嵌入到 bootmem 中

参数

size_t reserved_size

保留的 percpu 区域的大小,以字节为单位

size_t dyn_size

动态分配的最小可用大小,以字节为单位

size_t atom_size

分配原子大小

pcpu_fc_cpu_distance_fn_t cpu_distance_fn

确定 CPU 之间距离的回调,可选

pcpu_fc_cpu_to_node_fn_t cpu_to_nd_fn

将 CPU 转换为其节点的回调,可选

描述

这是一个帮助简化设置嵌入式第一个 percpu 块的助手,可以在预期 pcpu_setup_first_chunk() 的地方调用。

如果使用此函数设置第一个块,则通过调用 pcpu_fc_alloc 分配该块,并按原样使用,而无需映射到 vmalloc 区域。分配始终是 atom_size 的整数倍,并与 atom_size 对齐。

这使第一个块能够搭载到线性物理映射上,该映射通常使用更大的页面大小。请注意,这可能会导致 NUMA 机器上非常稀疏的 cpu->unit 映射,因此需要大量的 vmalloc 地址空间。如果 vmalloc 空间没有比节点内存地址之间的距离大几个数量级(即 32 位 NUMA 机器),则不要使用此分配器。

dyn_size 指定最小动态区域大小。

如果所需大小小于最小值或指定的单元大小,则使用 pcpu_fc_free 返回剩余部分。

返回

成功时返回 0,失败时返回 -errno。

int pcpu_page_first_chunk(size_t reserved_size, pcpu_fc_cpu_to_node_fn_t cpu_to_nd_fn)

使用 PAGE_SIZE 页面映射第一个块

参数

size_t reserved_size

保留的 percpu 区域的大小,以字节为单位

pcpu_fc_cpu_to_node_fn_t cpu_to_nd_fn

将 CPU 转换为其节点的回调,可选

描述

这是一个帮助简化设置页面重新映射的第一个 percpu 块的助手,可以在预期 pcpu_setup_first_chunk() 的地方调用。

这是基本分配器。静态 percpu 区域逐页分配到 vmalloc 区域。

返回

成功时返回 0,失败时返回 -errno。

long copy_from_user_nofault(void *dst, const void __user *src, size_t size)

安全地尝试从用户空间位置读取

参数

void *dst

指向将接收数据的缓冲区的指针

const void __user *src

要从中读取的地址。这必须是用户地址。

size_t size

数据块的大小

描述

安全地从用户地址 src 读取到 dst 的缓冲区。如果发生内核故障,请处理该故障并返回 -EFAULT。

long copy_to_user_nofault(void __user *dst, const void *src, size_t size)

安全地尝试写入用户空间位置

参数

void __user *dst

要写入的地址

const void *src

指向要写入的数据的指针

size_t size

数据块的大小

描述

安全地从 src 的缓冲区写入到地址 dst。如果发生内核故障,请处理该故障并返回 -EFAULT。

long strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr, long count)
  • 从不安全的用户地址复制以 NUL 结尾的字符串。

参数

char *dst

内核空间中的目标地址。此缓冲区必须至少为 count 字节长。

const void __user *unsafe_addr

不安全的用户地址。

long count

要复制的最大字节数,包括尾随 NUL。

描述

将一个以 NUL 结尾的字符串从不安全的用户地址复制到内核缓冲区。

成功时,返回字符串的长度,包括结尾的 NUL。

如果访问失败,则返回 -EFAULT(可能已经复制了一些数据,并且添加了结尾的 NUL)。

如果 count 小于字符串的长度,则复制 count -1 个字节,将 dst 缓冲区的最后一个字节设置为 NUL,并返回 count

long strnlen_user_nofault(const void __user *unsafe_addr, long count)
  • 获取用户字符串的大小,包括结尾的 NUL。

参数

const void __user *unsafe_addr

要测量的字符串。

long count

最大计数(包括 NUL)。

描述

在用户空间中获取以 NUL 结尾的字符串的大小,而不会发生页错误。

返回字符串的大小,包括结尾的 NUL。

如果字符串太长,则返回大于 count 的数字。用户必须检查返回值是否“> count”。在发生异常(或无效计数)时,返回 0。

与 strnlen_user 不同,此函数可以从 IRQ 处理程序等中使用,因为它禁用了页错误。

bool writeback_throttling_sane(struct scan_control *sc)

通常的脏数据节流机制是否可用?

参数

struct scan_control *sc

有问题的 scan_control。

描述

balance_dirty_pages() 中的正常页面脏数据节流机制在使用旧版 memcg 时完全失效,并且在 shrink_folio_list() 中使用直接停顿来进行节流,这缺少诸如公平性、自适应暂停、带宽比例分配和可配置性等优点。

此函数测试当前正在进行的 vmscan 是否可以假设正常的脏数据节流机制正在运行。

unsigned long lruvec_lru_size(struct lruvec *lruvec, enum lru_list lru, int zone_idx)

返回给定 LRU 列表上的页面数量。

参数

struct lruvec *lruvec

lru 向量

enum lru_list lru

要使用的 lru。

int zone_idx

要考虑的区域(使用 MAX_NR_ZONES - 1 表示整个 LRU 列表)。

long remove_mapping(struct address_space *mapping, struct folio *folio)

尝试从其映射中删除 folio。

参数

struct address_space *mapping

地址空间。

struct folio *folio

要删除的 folio。

描述

如果 folio 是脏的、正在回写或其他人持有对它的引用,则删除将失败。

返回

从映射中删除的页面数。如果无法删除 folio,则为 0。

上下文

调用方应在 folio 上持有单个引用计数并持有其锁。

void folio_putback_lru(struct folio *folio)

将先前隔离的 folio 放到适当的 LRU 列表上。

参数

struct folio *folio

要返回到 LRU 列表的 Folio。

描述

将先前隔离的 folio 添加到适当的 LRU 列表。该 folio 由于其他原因可能仍然无法逐出。

上下文

lru_lock 不得被持有,必须启用中断。

bool folio_isolate_lru(struct folio *folio)

尝试从其 LRU 列表中隔离一个 folio。

参数

struct folio *folio

要从其 LRU 列表中隔离的 Folio。

描述

从 LRU 列表中隔离 folio,并调整与 folio 所在 LRU 列表相对应的 vmstat 统计信息。

folio 的 LRU 标志将被清除。如果在活动列表中找到,它将设置 Active 标志。如果在无法逐出的列表中找到,它将设置 Unevictable 标志。在让页面消失之前,调用方可能需要清除这些标志。

  1. 必须使用 folio 上提升的引用计数来调用。这与 isolate_lru_folios()(在没有稳定引用的情况下调用)有根本区别。

  2. 不得持有 lru_lock。

  3. 必须启用中断。

上下文

返回

如果 folio 已从 LRU 列表中删除,则为 true。如果 folio 不在 LRU 列表中,则为 false。

void check_move_unevictable_folios(struct folio_batch *fbatch)

将可逐出的 folio 移动到适当的区域 lru 列表

参数

struct folio_batch *fbatch

要检查的 lru folio 批次。

描述

检查 folio 的可逐出性,如果可逐出的 folio 在无法逐出的 lru 列表中,则将其移动到适当的可逐出的 lru 列表。此函数应仅用于 lru folio。

void __remove_pages(unsigned long pfn, unsigned long nr_pages, struct vmem_altmap *altmap)

删除页面部分

参数

unsigned long pfn

起始页帧(必须与节的开头对齐)。

unsigned long nr_pages

要删除的页面数(必须是节大小的倍数)。

struct vmem_altmap *altmap

备用设备页面映射,如果使用默认的 memmap,则为 NULL

描述

用于删除我们正在删除的内存节的节映射和 sysfs 条目的通用帮助函数。调用方需要确保通过调用 offline_pages() 将页面标记为保留,并正确调整区域。

void try_offline_node(int nid)

参数

int nid

节点 ID。

描述

如果节点的所有内存节和 CPU 都已删除,则离线该节点。

注意

调用方必须调用 lock_device_hotplug() 以在调用此函数之前序列化热插拔和上线/离线操作。

void __remove_memory(u64 start, u64 size)

如果每个内存块都已离线,则删除内存。

参数

u64 start

要删除的区域的物理地址。

u64 size

要删除的区域的大小。

注意

调用方必须调用 lock_device_hotplug() 以在调用此函数之前序列化热插拔和上线/离线操作,正如 try_offline_node() 所要求的那样。

unsigned long mmu_interval_read_begin(struct mmu_interval_notifier *interval_sub)

针对 VA 范围开始读取侧临界区。

参数

struct mmu_interval_notifier *interval_sub

间隔订阅。

描述

mmu_iterval_read_begin()/mmu_iterval_read_retry() 为订阅下的 VA 范围实现类似于 seqcount 的冲突重试方案。如果 mm 在临界区期间调用无效,则 mmu_interval_read_retry() 将返回 true。

这对于获取需要阻塞上下文的 SPTE 的拆卸或设置的影子 PTE 非常有用。由此形成的临界区可以休眠,并且所需的“user_lock”也可以是休眠锁。

调用方需要提供一个“user_lock”以序列化拆卸和设置。

返回值应传递给 mmu_interval_read_retry()。

int mmu_notifier_register(struct mmu_notifier *subscription, struct mm_struct *mm)

在 mm 上注册一个通知程序。

参数

struct mmu_notifier *subscription

要附加的通知程序。

struct mm_struct *mm

要将通知程序附加到的 mm。

描述

调用此注册函数时,不得持有 mmap_lock 或任何其他与 VM 相关的锁。还必须确保在运行此函数时 mm_users 不能降至零,以避免与 mmu_notifier_release 发生竞争,因此 mm 必须是 current->mm 或 mm 应该安全地被锁定,例如使用 get_task_mm()。如果 mm 不是 current->mm,则在 mmu_notifier_register 返回后,应该通过调用 mmput 来释放 mm_users 锁定。

必须始终调用 mmu_notifier_unregister() 或 mmu_notifier_put() 来取消注册通知器。

当调用者获得 mmu_notifier 时,subscription->mm 指针将保持有效,并且可以通过 mmget_not_zero() 转换为活动的 mm 指针。

struct mmu_notifier *mmu_notifier_get_locked(const struct mmu_notifier_ops *ops, struct mm_struct *mm)

返回 mm 和 ops 的单个 struct mmu_notifier

参数

const struct mmu_notifier_ops *ops

正在订阅的操作结构

struct mm_struct *mm

要附加通知器的 mm

描述

此函数通过 ops->alloc_notifier() 分配一个新的 mmu_notifier,或者返回列表中已存在的通知器。ops 指针的值用于确定何时两个通知器相同。

每次调用 mmu_notifier_get() 都必须与调用 mmu_notifier_put() 配对。调用者必须持有 mm->mmap_lock 的写端。

当调用者获得 mmu_notifier 时,mm 指针将保持有效,并且可以通过 mmget_not_zero() 转换为活动的 mm 指针。

void mmu_notifier_put(struct mmu_notifier *subscription)

释放通知器的引用

参数

struct mmu_notifier *subscription

要操作的通知器

描述

此函数必须与每个 mmu_notifier_get() 配对,它会释放 get 获取的引用。如果这是最后一个引用,则将异步运行释放通知器的过程。

与 mmu_notifier_unregister() 不同,get/put 流程仅在 mm_struct 被销毁时调用 ops->release。相反,始终调用 free_notifier 来释放用户持有的任何资源。

由于 ops->release 不能保证被调用,用户必须确保在调用 mmu_notifier_put() 之前,所有 sptes 都已删除,并且不能建立新的 sptes。

可以从 ops->release 回调中调用此函数,但是调用者仍然必须确保它与 mmu_notifier_get() 成对调用。

调用此函数的模块必须在其 __exit 函数中调用 mmu_notifier_synchronize(),以确保异步工作已完成。

int mmu_interval_notifier_insert(struct mmu_interval_notifier *interval_sub, struct mm_struct *mm, unsigned long start, unsigned long length, const struct mmu_interval_notifier_ops *ops)

插入一个区间通知器

参数

struct mmu_interval_notifier *interval_sub

要注册的区间订阅

struct mm_struct *mm

要附加到的 mm_struct

unsigned long start

要监视的起始虚拟地址

unsigned long length

要监视的范围的长度

const struct mmu_interval_notifier_ops *ops

在匹配事件时要调用的区间通知器操作

描述

此函数订阅区间通知器以接收来自 mm 的通知。返回后,只要发生与给定范围相交的事件,就会调用与 mmu_interval_notifier 相关的操作。

返回后,range_notifier 可能尚未出现在区间树中。调用者必须通过 mmu_interval_read_begin() 使用正常的区间通知器读取流程来为此范围建立 SPTE。

void mmu_interval_notifier_remove(struct mmu_interval_notifier *interval_sub)

删除一个区间通知器

参数

struct mmu_interval_notifier *interval_sub

要取消注册的区间订阅

描述

此函数必须与 mmu_interval_notifier_insert() 配对。它不能从任何 ops 回调中调用。

一旦此函数返回,ops 回调将不再在其他 CPU 上运行,并且将来不会被调用。

void mmu_notifier_synchronize(void)

确保所有 mmu_notifier 都已释放

参数

void

无参数

描述

此函数确保来自 mmu_notifier_put() 的所有未完成的异步 SRU 工作都已完成。返回后,将不再调用与未使用 mmu_notifier 关联的任何 mmu_notifier_ops。

在使用之前,调用者必须确保其所有 mmu_notifier 都已通过 mmu_notifier_put() 完全释放。

使用 mmu_notifier_put() API 的模块应在其 __exit 函数中调用此函数,以避免模块卸载竞争。

size_t balloon_page_list_enqueue(struct balloon_dev_info *b_dev_info, struct list_head *pages)

将页面列表插入到气球页面列表中。

参数

struct balloon_dev_info *b_dev_info

气球设备描述符,我们将在此处插入新页面

struct list_head *pages

要入队的页面 - 使用 balloon_page_alloc 分配。

描述

驱动程序必须调用此函数才能在将气球页面从客户机系统中明确删除之前,正确地将其入队。

返回

已入队的页数。

size_t balloon_page_list_dequeue(struct balloon_dev_info *b_dev_info, struct list_head *pages, size_t n_req_pages)

从气球的页面列表中删除页面,并返回页面列表。

参数

struct balloon_dev_info *b_dev_info

气球设备描述符,我们将从中获取页面。

struct list_head *pages

指向将返回给调用者的页面列表的指针。

size_t n_req_pages

请求的页数。

描述

驱动程序必须调用此函数,才能在最终将先前加入的 balloon 页释放回客户机系统之前,正确地释放这些页面的内存。此函数尝试从 balloon 页中移除 n_req_pages 个页面,并将它们通过 pages 列表返回给调用者。

请注意,即使 balloon 不为空,此函数也可能无法出列某些页面 - 因为由于隔离页面的压缩,页面列表可能会暂时为空。

返回

添加到 pages 列表中的页面数量。

vm_fault_t vmf_insert_pfn_pmd(struct vm_fault *vmf, pfn_t pfn, bool write)

插入一个 pmd 大小的 pfn

参数

struct vm_fault *vmf

描述错误的结构

pfn_t pfn

要插入的 pfn

bool write

是否为写错误

描述

插入一个 pmd 大小的 pfn。有关其他信息,请参阅 vmf_insert_pfn()

返回

vm_fault_t 值。

vm_fault_t vmf_insert_pfn_pud(struct vm_fault *vmf, pfn_t pfn, bool write)

插入一个 pud 大小的 pfn

参数

struct vm_fault *vmf

描述错误的结构

pfn_t pfn

要插入的 pfn

bool write

是否为写错误

描述

插入一个 pud 大小的 pfn。有关其他信息,请参阅 vmf_insert_pfn()

返回

vm_fault_t 值。

int io_mapping_map_user(struct io_mapping *iomap, struct vm_area_struct *vma, unsigned long addr, unsigned long pfn, unsigned long size)

将 I/O 映射重新映射到用户空间

参数

struct io_mapping *iomap

源 io_mapping

struct vm_area_struct *vma

要映射到的用户 vma。

unsigned long addr

要开始的目标用户地址

unsigned long pfn

内核内存的物理地址

unsigned long size

映射区域的大小

注意

只有在调用时持有 mm 信号量才是安全的。