Linux 内核 API

列表管理函数

void INIT_LIST_HEAD(struct list_head *list)

初始化 list_head 结构

参数

struct list_head *list

要初始化的 list_head 结构。

描述

初始化 list_head 使其指向自身。 如果它是一个列表头,则结果是一个空列表。

void list_add(struct list_head *new, struct list_head *head)

添加新条目

参数

struct list_head *new

要添加的新条目

struct list_head *head

要添加到其后的列表头

描述

在指定的头之后插入新条目。 这对于实现堆栈很有用。

void list_add_tail(struct list_head *new, struct list_head *head)

添加新条目

参数

struct list_head *new

要添加的新条目

struct list_head *head

要添加到其前的列表头

描述

在指定的头之前插入新条目。 这对于实现队列很有用。

void list_del(struct list_head *entry)

从列表中删除条目。

参数

struct list_head *entry

要从列表中删除的元素。

注意

在执行此操作后,list_empty() 不会返回 true,该条目处于未定义状态。

void list_replace(struct list_head *old, struct list_head *new)

用新条目替换旧条目

参数

struct list_head *old

要替换的元素

struct list_head *new

要插入的新元素

描述

如果 old 为空,它将被覆盖。

void list_replace_init(struct list_head *old, struct list_head *new)

用新条目替换旧条目并初始化旧条目

参数

struct list_head *old

要替换的元素

struct list_head *new

要插入的新元素

描述

如果 old 为空,它将被覆盖。

void list_swap(struct list_head *entry1, struct list_head *entry2)

用 entry2 替换 entry1,并在 entry2 的位置重新添加 entry1

参数

struct list_head *entry1

放置 entry2 的位置

struct list_head *entry2

放置 entry1 的位置

void list_del_init(struct list_head *entry)

从列表中删除条目并重新初始化它。

参数

struct list_head *entry

要从列表中删除的元素。

void list_move(struct list_head *list, struct list_head *head)

从一个列表删除并添加为另一个列表的头

参数

struct list_head *list

要移动的条目

struct list_head *head

将位于条目之前的头

void list_move_tail(struct list_head *list, struct list_head *head)

从一个列表删除并添加为另一个列表的尾

参数

struct list_head *list

要移动的条目

struct list_head *head

将跟随条目的头

void list_bulk_move_tail(struct list_head *head, struct list_head *first, struct list_head *last)

将列表的子部分移动到其尾部

参数

struct list_head *head

将跟随条目的头

struct list_head *first

要移动的第一个条目

struct list_head *last

要移动的最后一个条目,可以与 first 相同

描述

first 和包括 last 之间的所有条目移动到 head 之前。 所有三个条目必须属于同一个链表。

int list_is_first(const struct list_head *list, const struct list_head *head)
  • 测试 list 是否为列表 head 中的第一个条目

参数

const struct list_head *list

要测试的条目

const struct list_head *head

列表的头

int list_is_last(const struct list_head *list, const struct list_head *head)

测试 list 是否为列表 head 中的最后一个条目

参数

const struct list_head *list

要测试的条目

const struct list_head *head

列表的头

int list_is_head(const struct list_head *list, const struct list_head *head)

测试 **list** 是否为列表的 **head**

参数

const struct list_head *list

要测试的条目

const struct list_head *head

列表的头

int list_empty(const struct list_head *head)

测试列表是否为空

参数

const struct list_head *head

要测试的列表。

void list_del_init_careful(struct list_head *entry)

从列表中删除条目并重新初始化它。

参数

struct list_head *entry

要从列表中删除的元素。

描述

这与 list_del_init() 相同,但设计为与 list_empty_careful() 一起使用,以保证其他内存操作的顺序。

list_del_init_careful() 之前完成的任何内存操作,都保证在 list_empty_careful() 测试之后可见。

int list_empty_careful(const struct list_head *head)

测试列表是否为空且未被修改

参数

const struct list_head *head

要测试的列表

描述

测试列表是否为空,_并且_检查是否没有其他 CPU 可能正在修改任何成员(next 或 prev)

注意

如果对列表条目发生的唯一活动是 list_del_init(),那么在没有同步的情况下使用 list_empty_careful() 才是安全的。例如,如果另一个 CPU 可能会重新 list_add() 它,则不能使用。

void list_rotate_left(struct list_head *head)

将列表向左旋转

参数

struct list_head *head

列表的头

void list_rotate_to_front(struct list_head *list, struct list_head *head)

将列表旋转到特定项。

参数

struct list_head *list

列表所需的新开头。

struct list_head *head

列表的头部。

描述

旋转列表,使 **list** 成为列表的新开头。

int list_is_singular(const struct list_head *head)

测试列表是否只有一个条目。

参数

const struct list_head *head

要测试的列表。

void list_cut_position(struct list_head *list, struct list_head *head, struct list_head *entry)

将列表切割成两部分

参数

struct list_head *list

一个新列表,用于添加所有已删除的条目

struct list_head *head

一个包含条目的列表

struct list_head *entry

头部中的一个条目,它可以是头部本身,如果是,我们将不会切割列表

描述

此帮助程序将 **head** 的初始部分(包括 **entry**)从 **head** 移动到 **list**。您应该在 **entry** 上传递一个您知道在 **head** 上的元素。**list** 应该是一个空列表或一个您不关心丢失其数据的列表。

void list_cut_before(struct list_head *list, struct list_head *head, struct list_head *entry)

在给定条目之前将列表切割成两部分

参数

struct list_head *list

一个新列表,用于添加所有已删除的条目

struct list_head *head

一个包含条目的列表

struct list_head *entry

头部中的一个条目,它可以是头部本身

描述

此帮助程序将 **head** 的初始部分(直到但不包括 **entry**)从 **head** 移动到 **list**。您应该在 **entry** 中传递一个您知道在 **head** 上的元素。**list** 应该是一个空列表或一个您不关心丢失其数据的列表。如果 **entry** == **head**,则 **head** 上的所有条目都将移动到 **list**。

void list_splice(const struct list_head *list, struct list_head *head)

连接两个列表,这是为堆栈设计的

参数

const struct list_head *list

要添加的新列表。

struct list_head *head

将其添加到第一个列表中的位置。

void list_splice_tail(struct list_head *list, struct list_head *head)

连接两个列表,每个列表都是一个队列

参数

struct list_head *list

要添加的新列表。

struct list_head *head

将其添加到第一个列表中的位置。

void list_splice_init(struct list_head *list, struct list_head *head)

连接两个列表并重新初始化空列表。

参数

struct list_head *list

要添加的新列表。

struct list_head *head

将其添加到第一个列表中的位置。

描述

在 **list** 处的列表被重新初始化

void list_splice_tail_init(struct list_head *list, struct list_head *head)

连接两个列表并重新初始化空列表

参数

struct list_head *list

要添加的新列表。

struct list_head *head

将其添加到第一个列表中的位置。

描述

每个列表都是一个队列。在 **list** 处的列表被重新初始化

list_entry

list_entry (ptr, type, member)

获取此条目的结构

参数

ptr

struct list_head 指针。

type

嵌入此结构的结构类型。

member

结构中 list_head 的名称。

list_first_entry

list_first_entry (ptr, type, member)

从列表中获取第一个元素

参数

ptr

从中获取元素的列表头部。

type

嵌入此结构的结构类型。

member

结构中 list_head 的名称。

描述

注意,列表预计不为空。

list_last_entry

list_last_entry (ptr, type, member)

从列表中获取最后一个元素

参数

ptr

从中获取元素的列表头部。

type

嵌入此结构的结构类型。

member

结构中 list_head 的名称。

描述

注意,列表预计不为空。

list_first_entry_or_null

list_first_entry_or_null (ptr, type, member)

从列表中获取第一个元素

参数

ptr

从中获取元素的列表头部。

type

嵌入此结构的结构类型。

member

结构中 list_head 的名称。

描述

请注意,如果列表为空,则返回 NULL。

list_next_entry

list_next_entry (pos, member)

获取列表中的下一个元素

参数

pos

指向游标的 type *

member

结构中 list_head 的名称。

list_next_entry_circular

list_next_entry_circular (pos, head, member)

获取列表中的下一个元素

参数

pos

指向游标的 type *。

head

从中获取元素的列表头部。

member

结构中 list_head 的名称。

描述

如果 pos 是最后一个元素,则回绕(返回第一个元素)。请注意,列表应该不能为空。

list_prev_entry

list_prev_entry (pos, member)

获取列表中的前一个元素

参数

pos

指向游标的 type *

member

结构中 list_head 的名称。

list_prev_entry_circular

list_prev_entry_circular (pos, head, member)

获取列表中的前一个元素

参数

pos

指向游标的 type *。

head

从中获取元素的列表头部。

member

结构中 list_head 的名称。

描述

如果 pos 是第一个元素,则回绕(返回最后一个元素)。请注意,列表应该不能为空。

list_for_each

list_for_each (pos, head)

遍历列表

参数

pos

用作循环游标的 struct list_head

head

您的列表的头部。

list_for_each_rcu

list_for_each_rcu (pos, head)

以 RCU 安全的方式遍历列表

参数

pos

用作循环游标的 struct list_head

head

您的列表的头部。

list_for_each_continue

list_for_each_continue (pos, head)

继续遍历列表

参数

pos

用作循环游标的 struct list_head

head

您的列表的头部。

描述

继续遍历列表,从当前位置之后继续。

list_for_each_prev

list_for_each_prev (pos, head)

向后遍历列表

参数

pos

用作循环游标的 struct list_head

head

您的列表的头部。

list_for_each_safe

list_for_each_safe (pos, n, head)

遍历列表,防止删除列表条目

参数

pos

用作循环游标的 struct list_head

n

另一个 struct list_head 用作临时存储

head

您的列表的头部。

list_for_each_prev_safe

list_for_each_prev_safe (pos, n, head)

向后遍历列表,防止删除列表条目

参数

pos

用作循环游标的 struct list_head

n

另一个 struct list_head 用作临时存储

head

您的列表的头部。

size_t list_count_nodes(struct list_head *head)

计算列表中的节点数

参数

struct list_head *head

您的列表的头部。

list_entry_is_head

list_entry_is_head (pos, head, member)

测试条目是否指向列表的头部

参数

pos

指向游标的 type *

head

您的列表的头部。

member

结构中 list_head 的名称。

list_for_each_entry

list_for_each_entry (pos, head, member)

遍历给定类型的列表

参数

pos

用作循环游标的 type *。

head

您的列表的头部。

member

结构中 list_head 的名称。

list_for_each_entry_reverse

list_for_each_entry_reverse (pos, head, member)

向后遍历给定类型的列表。

参数

pos

用作循环游标的 type *。

head

您的列表的头部。

member

结构中 list_head 的名称。

list_prepare_entry

list_prepare_entry (pos, head, member)

准备 pos 条目以在 list_for_each_entry_continue() 中使用

参数

pos

用作起始点的 type *

head

列表的头

member

结构中 list_head 的名称。

描述

准备 pos 条目,以用作 list_for_each_entry_continue() 中的起始点。

list_for_each_entry_continue

list_for_each_entry_continue (pos, head, member)

继续遍历给定类型的列表

参数

pos

用作循环游标的 type *。

head

您的列表的头部。

member

结构中 list_head 的名称。

描述

继续遍历给定类型的列表,从当前位置之后继续。

list_for_each_entry_continue_reverse

list_for_each_entry_continue_reverse (pos, head, member)

从给定点向后遍历

参数

pos

用作循环游标的 type *。

head

您的列表的头部。

member

结构中 list_head 的名称。

描述

开始向后遍历给定类型的列表,从当前位置之后继续。

list_for_each_entry_from

list_for_each_entry_from (pos, head, member)

从当前点遍历给定类型的列表

参数

pos

用作循环游标的 type *。

head

您的列表的头部。

member

结构中 list_head 的名称。

描述

遍历给定类型的列表,从当前位置继续。

list_for_each_entry_from_reverse

list_for_each_entry_from_reverse (pos, head, member)

从当前点向后遍历给定类型的列表

参数

pos

用作循环游标的 type *。

head

您的列表的头部。

member

结构中 list_head 的名称。

描述

向后遍历给定类型的列表,从当前位置继续。

list_for_each_entry_safe

list_for_each_entry_safe (pos, n, head, member)

遍历给定类型的列表,防止删除列表条目

参数

pos

用作循环游标的 type *。

n

另一个 type * 用作临时存储

head

您的列表的头部。

member

结构中 list_head 的名称。

list_for_each_entry_safe_continue

list_for_each_entry_safe_continue (pos, n, head, member)

继续列表迭代,防止删除

参数

pos

用作循环游标的 type *。

n

另一个 type * 用作临时存储

head

您的列表的头部。

member

结构中 list_head 的名称。

描述

遍历给定类型的列表,从当前点之后继续,防止删除列表条目。

list_for_each_entry_safe_from

list_for_each_entry_safe_from (pos, n, head, member)

从当前点遍历列表,防止删除

参数

pos

用作循环游标的 type *。

n

另一个 type * 用作临时存储

head

您的列表的头部。

member

结构中 list_head 的名称。

描述

从当前点遍历给定类型的列表,防止删除列表条目。

list_for_each_entry_safe_reverse

list_for_each_entry_safe_reverse (pos, n, head, member)

向后遍历列表,防止删除

参数

pos

用作循环游标的 type *。

n

另一个 type * 用作临时存储

head

您的列表的头部。

member

结构中 list_head 的名称。

描述

向后遍历给定类型的列表,防止删除列表条目。

list_safe_reset_next

list_safe_reset_next (pos, n, member)

重置过时的 list_for_each_entry_safe 循环

参数

pos

在 list_for_each_entry_safe 循环中使用的循环游标

n

在 list_for_each_entry_safe 中使用的临时存储

member

结构中 list_head 的名称。

描述

如果列表可能会并发修改(例如,在循环体中释放了锁),则通常使用 list_safe_reset_next 是不安全的。一个例外是,如果游标元素 (pos) 被固定在列表中,并且在重新获取锁后且在完成循环体的当前迭代之前调用 list_safe_reset_next。

int hlist_unhashed(const struct hlist_node *h)

节点是否已从列表中移除并重新初始化?

参数

const struct hlist_node *h

要检查的节点

描述

请注意,并非所有的移除函数都会将节点置于未散列状态。例如,hlist_nulls_del_init_rcu() 会将节点置于未散列状态,而 hlist_nulls_del() 则不会。

int hlist_unhashed_lockless(const struct hlist_node *h)

用于无锁使用的 hlist_unhashed 版本

参数

const struct hlist_node *h

要检查的节点

描述

hlist_unhashed() 变体必须在无锁上下文中使用,以避免潜在的加载撕裂。READ_ONCE() 与下面定义的 hlist 帮助程序中的各种 WRITE_ONCE() 配对。

int hlist_empty(const struct hlist_head *h)

指定的 hlist_head 结构是否为空 hlist?

参数

const struct hlist_head *h

要检查的结构。

void hlist_del(struct hlist_node *n)

从其列表中删除指定的 hlist_node

参数

struct hlist_node *n

要删除的节点。

描述

请注意,此函数将节点保留在散列状态。请改用 hlist_del_init() 或类似函数来取消散列 **n**。

void hlist_del_init(struct hlist_node *n)

从其列表中删除指定的 hlist_node 并初始化

参数

struct hlist_node *n

要删除的节点。

描述

请注意,此函数将节点保留在未散列状态。

void hlist_add_head(struct hlist_node *n, struct hlist_head *h)

在 hlist 的开头添加一个新条目

参数

struct hlist_node *n

要添加的新条目

struct hlist_head *h

要添加到其后的 hlist 头

描述

在指定的头之后插入新条目。 这对于实现堆栈很有用。

void hlist_add_before(struct hlist_node *n, struct hlist_node *next)

在指定条目之前添加一个新条目

参数

struct hlist_node *n

要添加的新条目

struct hlist_node *next

在其之前添加的 hlist 节点,必须为非 NULL

void hlist_add_behind(struct hlist_node *n, struct hlist_node *prev)

在指定条目之后添加一个新条目

参数

struct hlist_node *n

要添加的新条目

struct hlist_node *prev

在其后添加的 hlist 节点,必须为非 NULL

void hlist_add_fake(struct hlist_node *n)

创建一个由单个无头节点组成的伪 hlist

参数

struct hlist_node *n

要从中创建伪列表的节点

描述

这使 **n** 看起来像是无头 hlist 上的自身前任。这样做的目的是允许诸如 hlist_del() 之类的函数在没有列表的情况下也能正常工作。

bool hlist_fake(struct hlist_node *h)

此节点是否为伪 hlist?

参数

struct hlist_node *h

要检查是否为自引用伪 hlist 的节点。

bool hlist_is_singular_node(struct hlist_node *n, struct hlist_head *h)

节点是否为指定 hlist 的唯一元素?

参数

struct hlist_node *n

要检查是否为单数的节点。

struct hlist_head *h

可能为单数列表的头。

描述

检查节点是否为头的唯一节点,而无需访问头,从而避免不必要的缓存未命中。

void hlist_move_list(struct hlist_head *old, struct hlist_head *new)

移动 hlist

参数

struct hlist_head *old

旧列表的 hlist_head。

struct hlist_head *new

新列表的 hlist_head。

描述

将列表从一个列表头移动到另一个列表头。如果存在,则修复第一个条目的 pprev 引用。

void hlist_splice_init(struct hlist_head *from, struct hlist_node *last, struct hlist_head *to)

将所有条目从一个列表移动到另一个列表

参数

struct hlist_head *from

将从中移动条目的 hlist_head

struct hlist_node *last

**from** 列表上的最后一个条目

struct hlist_head *to

将条目移动到的 hlist_head

描述

**to** 可以为空,**from** 必须至少包含 **last**。

hlist_for_each_entry

hlist_for_each_entry (pos, head, member)

遍历给定类型的列表

参数

pos

用作循环游标的 type *。

head

您的列表的头部。

member

结构体中 hlist_node 的名称。

hlist_for_each_entry_continue

hlist_for_each_entry_continue (pos, member)

在当前点之后继续迭代 hlist

参数

pos

用作循环游标的 type *。

member

结构体中 hlist_node 的名称。

hlist_for_each_entry_from

hlist_for_each_entry_from (pos, member)

从当前点继续迭代 hlist

参数

pos

用作循环游标的 type *。

member

结构体中 hlist_node 的名称。

hlist_for_each_entry_safe

hlist_for_each_entry_safe (pos, n, head, member)

遍历给定类型的列表,防止删除列表条目

参数

pos

用作循环游标的 type *。

n

一个 struct hlist_node,用作临时存储

head

您的列表的头部。

member

结构体中 hlist_node 的名称。

size_t hlist_count_nodes(struct hlist_head *head)

计算 hlist 中的节点数

参数

struct hlist_head *head

你的 hlist 的头。

基本 C 库函数

在编写驱动程序时,通常不能使用 C 库中的例程。 以下列出了一些通常有用的函数。 这些函数的行为可能与 ANSI 定义的略有不同,这些偏差在文本中注明。

字符串转换

unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base)

将字符串转换为无符号长长整型

参数

const char *cp

字符串的起始位置

char **endp

指向已解析字符串末尾的指针将放置在此处

unsigned int base

要使用的数字基数

描述

此函数存在一些注意事项。请改用 kstrtoull。

unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base)

将字符串转换为无符号长整型

参数

const char *cp

字符串的起始位置

char **endp

指向已解析字符串末尾的指针将放置在此处

unsigned int base

要使用的数字基数

描述

此函数存在一些注意事项。请改用 kstrtoul。

long simple_strtol(const char *cp, char **endp, unsigned int base)

将字符串转换为带符号长整型

参数

const char *cp

字符串的起始位置

char **endp

指向已解析字符串末尾的指针将放置在此处

unsigned int base

要使用的数字基数

描述

此函数存在一些注意事项。请改用 kstrtol。

long long simple_strtoll(const char *cp, char **endp, unsigned int base)

将字符串转换为带符号长长整型

参数

const char *cp

字符串的起始位置

char **endp

指向已解析字符串末尾的指针将放置在此处

unsigned int base

要使用的数字基数

描述

此函数存在一些注意事项。请改用 kstrtoll。

int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)

格式化字符串并将其放入缓冲区

参数

char *buf

要将结果放入的缓冲区

size_t size

缓冲区的大小,包括尾部的空字符空间

const char *fmt

要使用的格式化字符串

va_list args

格式化字符串的参数

描述

此函数通常遵循 C99 vsnprintf,但有一些扩展和一些限制

  • ``n`` 不支持

  • ``p*`` 由 pointer() 处理

有关更详细的说明,请参阅 pointer() 或 如何正确使用 printk 格式说明符

进行更改时,请更新两个位置的文档

根据 ISO C99,返回值是为给定输入生成的字符数,不包括尾部的“0”。 如果要将写入 buf 的确切字符数作为返回值(不包括尾部的“0”),请使用 vscnprintf()。 如果返回值大于或等于 size,则结果字符串将被截断。

如果您尚未处理 va_list,请考虑使用 snprintf()

int vscnprintf(char *buf, size_t size, const char *fmt, va_list args)

格式化字符串并将其放入缓冲区

参数

char *buf

要将结果放入的缓冲区

size_t size

缓冲区的大小,包括尾部的空字符空间

const char *fmt

要使用的格式化字符串

va_list args

格式化字符串的参数

描述

返回值是写入 buf 的字符数,不包括尾部的“0”。 如果 size == 0,则函数返回 0。

如果您尚未处理 va_list,请考虑使用 scnprintf()

有关 C99 格式化字符串扩展,请参阅 vsnprintf() 文档。

int snprintf(char *buf, size_t size, const char *fmt, ...)

格式化字符串并将其放入缓冲区

参数

char *buf

要将结果放入的缓冲区

size_t size

缓冲区的大小,包括尾部的空字符空间

const char *fmt

要使用的格式化字符串

...

格式化字符串的参数

描述

根据 ISO C99,返回值是为给定输入生成的字符数,不包括尾部的空字符。 如果返回值大于或等于 size,则结果字符串将被截断。

有关 C99 格式化字符串扩展,请参阅 vsnprintf() 文档。

int scnprintf(char *buf, size_t size, const char *fmt, ...)

格式化字符串并将其放入缓冲区

参数

char *buf

要将结果放入的缓冲区

size_t size

缓冲区的大小,包括尾部的空字符空间

const char *fmt

要使用的格式化字符串

...

格式化字符串的参数

描述

返回值是写入 buf 的字符数,不包括尾部的“0”。 如果 size == 0,则函数返回 0。

int vsprintf(char *buf, const char *fmt, va_list args)

格式化字符串并将其放入缓冲区

参数

char *buf

要将结果放入的缓冲区

const char *fmt

要使用的格式化字符串

va_list args

格式化字符串的参数

描述

该函数返回写入 buf 的字符数。 使用 vsnprintf()vscnprintf() 以避免缓冲区溢出。

如果您尚未处理 va_list,请考虑使用 sprintf()

有关 C99 格式化字符串扩展,请参阅 vsnprintf() 文档。

int sprintf(char *buf, const char *fmt, ...)

格式化字符串并将其放入缓冲区

参数

char *buf

要将结果放入的缓冲区

const char *fmt

要使用的格式化字符串

...

格式化字符串的参数

描述

该函数返回写入 buf 的字符数。为了避免缓冲区溢出,请使用 snprintf()scnprintf()

有关 C99 格式化字符串扩展,请参阅 vsnprintf() 文档。

int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args)

解析格式字符串并将 args 的二进制值放入缓冲区

参数

u32 *bin_buf

用于放置 args 的二进制值的缓冲区

size_t size

缓冲区的大小(以字(32 位)为单位,而不是字符)

const char *fmt

要使用的格式化字符串

va_list args

格式化字符串的参数

描述

格式遵循 C99 vsnprintf,除了 n 被忽略,并且跳过其参数。

返回值是为给定输入生成的字(32 位)的数量。

注意

如果返回值大于 size,则生成的 bin_buf 对于 bstr_printf() 是无效的。

int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)

从二进制参数格式化字符串并将其放入缓冲区

参数

char *buf

要将结果放入的缓冲区

size_t size

缓冲区的大小,包括尾部的空字符空间

const char *fmt

要使用的格式化字符串

const u32 *bin_buf

格式字符串的二进制参数

描述

此函数类似于 C99 vsnprintf,但不同之处在于 vsnprintf 从堆栈获取参数,而 bstr_printf 从由 vbin_printf 生成的二进制缓冲区 bin_buf 获取参数。

格式遵循 C99 vsnprintf,但有一些扩展

有关详细信息,请参阅 vsnprintf 注释。

根据 ISO C99,返回值是为给定输入生成的字符数,不包括尾部的“0”。 如果要将写入 buf 的确切字符数作为返回值(不包括尾部的“0”),请使用 vscnprintf()。 如果返回值大于或等于 size,则结果字符串将被截断。

int vsscanf(const char *buf, const char *fmt, va_list args)

将缓冲区解格式化为参数列表

参数

const char *buf

输入缓冲区

const char *fmt

缓冲区的格式

va_list args

参数

int sscanf(const char *buf, const char *fmt, ...)

将缓冲区解格式化为参数列表

参数

const char *buf

输入缓冲区

const char *fmt

缓冲区的格式化

...

结果参数

int kstrtoul(const char *s, unsigned int base, unsigned long *res)

将字符串转换为无符号长整型

参数

const char *s

字符串的开头。该字符串必须以 null 结尾,并且在终止 null 之前也可以包含单个换行符。第一个字符也可以是加号,但不能是减号。

unsigned int base

要使用的数字基数。支持的最大基数为 16。如果 base 给定为 0,则会使用传统语义自动检测字符串的基数 - 如果它以 0x 开头,则该数字将解析为十六进制(不区分大小写),如果它以 0 开头,则将解析为八进制数。否则,它将解析为十进制数。

unsigned long *res

成功时,将转换结果写入的位置。

描述

成功返回 0,溢出返回 -ERANGE,解析错误返回 -EINVAL。优先于 simple_strtoul()。必须检查返回代码。

int kstrtol(const char *s, unsigned int base, long *res)

将字符串转换为 long

参数

const char *s

字符串的开头。该字符串必须以 null 结尾,并且在终止 null 之前也可以包含单个换行符。第一个字符也可以是加号或减号。

unsigned int base

要使用的数字基数。支持的最大基数为 16。如果 base 给定为 0,则会使用传统语义自动检测字符串的基数 - 如果它以 0x 开头,则该数字将解析为十六进制(不区分大小写),如果它以 0 开头,则将解析为八进制数。否则,它将解析为十进制数。

long *res

成功时,将转换结果写入的位置。

描述

成功返回 0,溢出返回 -ERANGE,解析错误返回 -EINVAL。优先于 simple_strtol()。必须检查返回代码。

int kstrtoull(const char *s, unsigned int base, unsigned long long *res)

将字符串转换为无符号长长整型

参数

const char *s

字符串的开头。该字符串必须以 null 结尾,并且在终止 null 之前也可以包含单个换行符。第一个字符也可以是加号,但不能是减号。

unsigned int base

要使用的数字基数。支持的最大基数为 16。如果 base 给定为 0,则会使用传统语义自动检测字符串的基数 - 如果它以 0x 开头,则该数字将解析为十六进制(不区分大小写),如果它以 0 开头,则将解析为八进制数。否则,它将解析为十进制数。

unsigned long long *res

成功时,将转换结果写入的位置。

描述

成功返回 0,溢出返回 -ERANGE,解析错误返回 -EINVAL。优先于 simple_strtoull()。必须检查返回代码。

int kstrtoll(const char *s, unsigned int base, long long *res)

将字符串转换为 long long

参数

const char *s

字符串的开头。该字符串必须以 null 结尾,并且在终止 null 之前也可以包含单个换行符。第一个字符也可以是加号或减号。

unsigned int base

要使用的数字基数。支持的最大基数为 16。如果 base 给定为 0,则会使用传统语义自动检测字符串的基数 - 如果它以 0x 开头,则该数字将解析为十六进制(不区分大小写),如果它以 0 开头,则将解析为八进制数。否则,它将解析为十进制数。

long long *res

成功时,将转换结果写入的位置。

描述

成功返回 0,溢出返回 -ERANGE,解析错误返回 -EINVAL。优先于 simple_strtoll()。必须检查返回代码。

int kstrtouint(const char *s, unsigned int base, unsigned int *res)

将字符串转换为无符号整数

参数

const char *s

字符串的开头。该字符串必须以 null 结尾,并且在终止 null 之前也可以包含单个换行符。第一个字符也可以是加号,但不能是减号。

unsigned int base

要使用的数字基数。支持的最大基数为 16。如果 base 给定为 0,则会使用传统语义自动检测字符串的基数 - 如果它以 0x 开头,则该数字将解析为十六进制(不区分大小写),如果它以 0 开头,则将解析为八进制数。否则,它将解析为十进制数。

unsigned int *res

成功时,将转换结果写入的位置。

描述

成功返回 0,溢出返回 -ERANGE,解析错误返回 -EINVAL。优先于 simple_strtoul()。必须检查返回代码。

int kstrtoint(const char *s, unsigned int base, int *res)

将字符串转换为 int

参数

const char *s

字符串的开头。该字符串必须以 null 结尾,并且在终止 null 之前也可以包含单个换行符。第一个字符也可以是加号或减号。

unsigned int base

要使用的数字基数。支持的最大基数为 16。如果 base 给定为 0,则会使用传统语义自动检测字符串的基数 - 如果它以 0x 开头,则该数字将解析为十六进制(不区分大小写),如果它以 0 开头,则将解析为八进制数。否则,它将解析为十进制数。

int *res

成功时,将转换结果写入的位置。

描述

成功返回 0,溢出返回 -ERANGE,解析错误返回 -EINVAL。优先于 simple_strtol()。必须检查返回代码。

int kstrtobool(const char *s, bool *res)

将常见的用户输入转换为布尔值

参数

const char *s

输入字符串

bool *res

结果

描述

如果首字符是 'YyTt1NnFf0' 之一,或者对于 "on" 和 "off" 是 [oO][NnFf],则此例程返回 0。否则将返回 -EINVAL。找到匹配项后,将更新 res 指向的值。

int string_get_size(u64 size, u64 blk_size, const enum string_size_units units, char *buf, int len)

以指定单位获取大小

参数

u64 size

要转换的块大小

u64 blk_size

块的大小(以字节为单位时使用 1)

const enum string_size_units units

要使用的单位(1000 或 1024 的幂),是否包含空格分隔符

char *buf

格式化到的缓冲区

int len

缓冲区长度

描述

此函数返回一个格式化为 3 位有效数字的字符串,给出所需单位的大小。buf 应该有至少 9 个字节的空间,并且始终以零结尾。

返回值:已写入的输出字符数(如果输出被截断,可能大于 len)。

int parse_int_array_user(const char __user *from, size_t count, int **array)

将字符串拆分为整数序列

参数

const char __user *from

要读取的用户空间缓冲区

size_t count

要读取的最大字节数

int **array

返回指向整数序列的指针

描述

成功后,array 将被分配并初始化,其中包含从 from 提取的整数序列,外加一个开始该序列并指定整数计数的附加元素。

调用者负责在不再需要 array 时释放它。

int string_unescape(char *src, char *dst, size_t size, unsigned int flags)

取消给定字符串中的引号字符

参数

char *src

源缓冲区(已转义)

char *dst

目标缓冲区(未转义)

size_t size

目标缓冲区的大小(0 表示无限制)

unsigned int flags

标志的组合。

描述

该函数取消给定字符串中的引号字符。

因为输出的大小将与输入的大小相同或更小,所以转换可以就地执行。

调用者必须提供有效的源指针和目标指针。请注意,目标缓冲区将始终以 NULL 结尾。源字符串也必须以 NULL 结尾。支持的标志是

UNESCAPE_SPACE:
        '\f' - form feed
        '\n' - new line
        '\r' - carriage return
        '\t' - horizontal tab
        '\v' - vertical tab
UNESCAPE_OCTAL:
        '\NNN' - byte with octal value NNN (1 to 3 digits)
UNESCAPE_HEX:
        '\xHH' - byte with hexadecimal value HH (1 to 2 digits)
UNESCAPE_SPECIAL:
        '\"' - double quote
        '\\' - backslash
        '\a' - alert (BEL)
        '\e' - escape
UNESCAPE_ANY:
        all previous together

返回

返回处理到目标缓冲区的字符数,不包括尾随的 '0'。

int string_escape_mem(const char *src, size_t isz, char *dst, size_t osz, unsigned int flags, const char *only)

在给定内存缓冲区中添加引号字符

参数

const char *src

源缓冲区(未转义)

size_t isz

源缓冲区大小

char *dst

目标缓冲区(已转义)

size_t osz

目标缓冲区大小

unsigned int flags

标志的组合

const char *only

包含用于限制所选转义类的字符的以 NULL 结尾的字符串。如果 only 中包含的字符通常不会被 flags 中选择的类转义,则它们将以未转义的形式复制到 dst

描述

转义字节缓冲区的过程包括几个部分。它们按以下顺序应用。

  1. 该字符与 only 字符串中的字符不匹配,因此必须按原样输出。

  2. 如果要求,该字符与可打印和 ASCII 类匹配,如果匹配,则会传递到输出。

  3. 如果要求,该字符与可打印或 ASCII 类匹配,如果匹配,则会传递到输出。

  4. 检查该字符是否属于 flags 给出的类。 ESCAPE_OCTALESCAPE_HEX 将最后执行,因为它们涵盖任何字符。请注意,它们实际上不能同时使用,否则 ESCAPE_HEX 将被忽略。

调用者必须提供有效的源指针和目标指针。请注意,目标缓冲区不会以 NULL 结尾,因此如果需要,调用者必须附加它。支持的标志是

%ESCAPE_SPACE: (special white space, not space itself)
        '\f' - form feed
        '\n' - new line
        '\r' - carriage return
        '\t' - horizontal tab
        '\v' - vertical tab
%ESCAPE_SPECIAL:
        '\"' - double quote
        '\\' - backslash
        '\a' - alert (BEL)
        '\e' - escape
%ESCAPE_NULL:
        '\0' - null
%ESCAPE_OCTAL:
        '\NNN' - byte with octal value NNN (3 digits)
%ESCAPE_ANY:
        all previous together
%ESCAPE_NP:
        escape only non-printable characters, checked by isprint()
%ESCAPE_ANY_NP:
        all previous together
%ESCAPE_HEX:
        '\xHH' - byte with hexadecimal value HH (2 digits)
%ESCAPE_NA:
        escape only non-ascii characters, checked by isascii()
%ESCAPE_NAP:
        escape only non-printable or non-ascii characters
%ESCAPE_APPEND:
        append characters from @only to be escaped by the given classes

ESCAPE_APPEND 将有助于将额外的字符传递给转义的字符,当提供 ESCAPE_NPESCAPE_NAESCAPE_NAP 之一时。

一个值得注意的警告是,ESCAPE_NAPESCAPE_NPESCAPE_NA 的优先级高于其余标志(ESCAPE_NAP 最高)。如果没有 ESCAPE_OCTALESCAPE_HEX,使用它们中的任何一个都没有多大意义,因为它们涵盖了大多数其他字符类。ESCAPE_NAP 除了上述内容外,还可以利用 ESCAPE_SPACEESCAPE_SPECIAL

返回

为给定输入和标志生成的转义输出的总大小。要检查输出是否被截断,请将返回值与 osz 进行比较。当且仅当 ret < osz 时,dst 中才会留下一个 '0' 终止符的空间。

char **kasprintf_strarray(gfp_t gfp, const char *prefix, size_t n)

分配并填充顺序字符串数组

参数

gfp_t gfp

slab 分配器的标志

const char *prefix

要使用的前缀

size_t n

要分配和填充的行数

描述

使用 “s-````zu” 模式分配并填充 n 个字符串,其中前缀由调用者提供。调用者在使用后负责使用 kfree_strarray() 释放它们。

返回字符串数组,如果无法分配内存则返回 NULL。

void kfree_strarray(char **array, size_t n)

释放数组中包含的若干个动态分配的字符串以及数组本身

参数

char **array

要释放的动态分配的字符串数组。

size_t n

要释放的字符串数量(从数组的开头开始)。

描述

传递非 NULL 的 arrayn == 0 以及 NULL 的 array 都是有效的用例。如果 array 为 NULL,则该函数不执行任何操作。

char *skip_spaces(const char *str)

str 中删除前导空格。

参数

const char *str

要剥离的字符串。

描述

返回指向 str 中第一个非空白字符的指针。

char *strim(char *s)

s 中删除前导和尾随空格。

参数

char *s

要剥离的字符串。

描述

请注意,给定的字符串 s 中第一个尾随空格被替换为 NUL 终止符。 返回指向 s 中第一个非空白字符的指针。

bool sysfs_streq(const char *s1, const char *s2)

如果字符串相等,则返回 true,忽略尾随换行符

参数

const char *s1

一个字符串

const char *s2

另一个字符串

描述

当且仅当两个字符串相等时,此例程返回 true,将 NUL 和换行符然后 NUL 视为等效的字符串终止符。它适用于 sysfs 输入字符串,这些字符串通常以换行符结尾,但会与不带换行符的值进行比较。

int match_string(const char *const *array, size_t n, const char *string)

在数组中匹配给定的字符串

参数

const char * const *array

字符串数组

size_t n

数组中字符串的数量,对于以 NULL 结尾的数组为 -1

const char *string

要匹配的字符串

描述

此例程将在字符串数组中查找字符串,直到数组中的第 n 个元素或第一个 NULL 元素。

历史上,n 的值 -1 用于搜索以 NULL 结尾的数组。但是,该函数在完成搜索时不做区分:要么比较了 n 个元素,要么找到了第一个 NULL 元素。

返回

如果匹配,则为 stringarray 中的索引,否则为 -EINVAL

int __sysfs_match_string(const char *const *array, size_t n, const char *str)

在数组中匹配给定的字符串

参数

const char * const *array

字符串数组

size_t n

数组中字符串的数量,对于以 NULL 结尾的数组为 -1

const char *str

要匹配的字符串

描述

返回 strarray 中的索引或 -EINVAL,就像 match_string() 一样。使用 sysfs_streq 而不是 strcmp 进行匹配。

此例程将在字符串数组中查找字符串,直到数组中的第 n 个元素或第一个 NULL 元素。

历史上,n 的值 -1 用于搜索以 NULL 结尾的数组。但是,该函数在完成搜索时不做区分:要么比较了 n 个元素,要么找到了第一个 NULL 元素。

char *strreplace(char *str, char old, char new)

替换字符串中所有出现的字符。

参数

char *str

要操作的字符串。

char old

要替换的字符。

char new

用其替换 old 的字符。

描述

用给定的字符串 str 中的每个 old 字符替换为 new 字符。

返回

指向字符串 str 本身的指针。

void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count, int pad)

将一个缓冲区复制到另一个缓冲区,并进行填充

参数

void *dest

要复制到的位置

size_t dest_len

目标缓冲区大小

const void *src

要从中复制的位置

size_t count

要复制的字节数

int pad

如果目标中剩余空间,则用于填充的字符。

字符串操作

unsafe_memcpy

unsafe_memcpy (dst, src, bytes, justification)

没有 FORTIFY 边界检查的 memcpy 实现

参数

dst

要写入的目标内存地址

src

要从中读取的源内存地址

bytes

要从 src 写入到 dst 的字节数

justification

描述为什么需要使用的自由格式文本或注释

描述

这应该用于编译器无法正确处理的极端情况,或在 API 之间的转换期间。它应该很少使用,并且包含用于详细说明边界检查发生的位置以及为什么无法使用现有解决方案的理由的位置。

char *strncpy(char *const p, const char *q, __kernel_size_t size)

将字符串复制到内存,不保证使用 NUL 填充

参数

char * const p

指向复制目标位置的指针

const char *q

指向要复制的以 NUL 结尾的源字符串的指针

__kernel_size_t size

要写入 p 的字节数

描述

如果 strlen(q) >= size,则在 size 字节后停止 q 的复制,并且 p 将不会以 NUL 结尾

如果 strlen(q) < size,在复制 q 之后,将尾随 NUL 字节写入 p,直到写入总共 size 字节。

请勿使用此函数。虽然 FORTIFY_SOURCE 尝试避免过度读取 q,但它无法防止将未终止的结果写入 p。使用 strncpy() 仍然是模棱两可且脆弱的。请选择替代方案,以便对 p 的内容的期望是明确的

p 需要是

填充到 size

不填充

以 NUL 结尾

strscpy_pad()

strscpy()

不以 NUL 结尾

strtomem_pad()

strtomem()

请注意 strscpy*() 用于检测截断的不同返回值,以及 strtomem*() 期望目标是字符数组时使用 __nonstring 标记。

__kernel_size_t strnlen(const char *const p, __kernel_size_t maxlen)

返回以 NUL 结尾的字符串中有限的字符计数

参数

const char * const p

指向要计数的以 NUL 结尾的字符串的指针。

__kernel_size_t maxlen

要计数的最大字符数。

描述

返回 p 中的字符数(不包括最后的 NUL),或者如果到此为止没有找到 NUL,则返回 maxlen

strlen

strlen (p)

返回以 NUL 结尾的字符串中的字符计数

参数

p

指向要计数的以 NUL 结尾的字符串的指针。

描述

除非在编译时已知字符串长度,否则不要使用此函数。当 p 未终止时,此函数可能会崩溃或返回可能导致内存内容暴露的意外计数。最好使用 strnlen()

返回 p 中的字符数(不包括最后的 NUL)。

size_t strlcat(char *const p, const char *const q, size_t avail)

将字符串追加到现有字符串

参数

char * const p

指向要追加的 以NUL结尾 字符串的指针

const char * const q

指向要从中追加的 以NUL结尾 字符串的指针

size_t avail

p 中可用的最大字节数

描述

以NUL结尾 的字符串 q 追加到 p 处的 以NUL结尾 的字符串之后,但不会写入超过总共 avail 个字节,可能会截断从 q 的复制。 只有当 pavail 字节内已存在 NUL 时,p 才会保持 以NUL结尾 。 如果是这样,则从 q 复制的结果字节数最多为 “avail - strlen(p) - 1”。

不要使用此函数。 虽然 FORTIFY_SOURCE 尝试避免读取和写入溢出,但这只有在编译器知道 pq 的大小的情况下才有可能。 最好通过 scnprintf()、seq_buf 或类似的方式使用格式化构建字符串。

返回 p _将_包含的总字节数,而不考虑截断,类似于 snprintf()。 如果返回值 >= avail,则该字符串已被截断。

char *strcat(char *const p, const char *q)

将字符串追加到现有字符串

参数

char * const p

指向要追加的以 NUL 结尾的字符串的指针

const char *q

指向要从中追加的以 NUL 结尾的源字符串的指针

描述

不要使用此函数。虽然 FORTIFY_SOURCE 尝试避免读取和写入溢出,但这只有在编译器知道目标缓冲区大小时才有可能。最好通过 scnprintf() 或类似的方式使用格式化构建字符串。至少使用 strncat()

返回 p

char *strncat(char *const p, const char *const q, __kernel_size_t count)

将字符串追加到现有字符串

参数

char * const p

指向要追加的以 NUL 结尾的字符串的指针

const char * const q

指向要从中追加的源字符串的指针

__kernel_size_t count

q 读取的最大字节数

描述

p 处的以 NUL 结尾的字符串之后,最多从 q 追加 count 个字节(在第一个 NUL 字节处停止)。 p 将以 NUL 结尾。

不要使用此函数。虽然 FORTIFY_SOURCE 尝试避免读取和写入溢出,但这只有在编译器知道 pq 的大小时才有可能。最好通过 scnprintf() 或类似的方式使用格式化构建字符串。

返回 p

char *strcpy(char *const p, const char *const q)

将一个字符串复制到另一个字符串缓冲区

参数

char * const p

指向复制目标位置的指针

const char * const q

指向要复制的以 NUL 结尾的源字符串的指针

描述

不要使用此函数。 虽然 FORTIFY_SOURCE 尝试避免溢出,但这只有在编译器知道 qp 的大小时才有可能。 最好使用 strscpy(),但请注意其用于检测截断的不同返回值。

返回 p

int strncasecmp(const char *s1, const char *s2, size_t len)

不区分大小写的长度限制字符串比较

参数

const char *s1

一个字符串

const char *s2

另一个字符串

size_t len

要比较的最大字符数

char *stpcpy(char *__restrict__ dest, const char *__restrict__ src)

将字符串从 src 复制到 dest,返回指向 dest 新结尾的指针,包括 src 的 NUL 终止符。可能会覆盖 dest。

参数

char *__restrict__ dest

指向要复制到的字符串结尾的指针。必须足够大才能接收复制。

const char *__restrict__ src

指向要从中复制的字符串开头的指针。不得与 dest 重叠。

描述

stpcpy 在一个关键方面与 strcpy 不同:返回值是指向 dest 中新的 NUL 终止 字符的指针。(对于 strcpy,返回值是指向 dest 开头的指针)。此接口被认为是不安全的,因为它不执行输入的边界检查。因此,不建议使用。相反,提供它的定义是为了防止编译器将其他 libcall 降低到 stpcpy。

int strcmp(const char *cs, const char *ct)

比较两个字符串

参数

const char *cs

一个字符串

const char *ct

另一个字符串

int strncmp(const char *cs, const char *ct, size_t count)

比较两个长度受限的字符串

参数

const char *cs

一个字符串

const char *ct

另一个字符串

size_t count

要比较的最大字节数

char *strchr(const char *s, int c)

查找字符在字符串中首次出现的位置

参数

const char *s

要搜索的字符串

int c

要搜索的字符

描述

请注意,NUL-终止符被认为是字符串的一部分,可以搜索。

char *strchrnul(const char *s, int c)

查找并返回字符串中的字符,或者字符串的末尾

参数

const char *s

要搜索的字符串

int c

要搜索的字符

描述

返回指向 s 中首次出现的 ‘c’ 的指针。如果找不到 c,则返回指向 s 末尾空字节的指针。

char *strrchr(const char *s, int c)

查找字符在字符串中最后一次出现的位置

参数

const char *s

要搜索的字符串

int c

要搜索的字符

char *strnchr(const char *s, size_t count, int c)

在长度受限的字符串中查找字符

参数

const char *s

要搜索的字符串

size_t count

要搜索的字符数

int c

要搜索的字符

描述

请注意,NUL-终止符被认为是字符串的一部分,可以搜索。

size_t strspn(const char *s, const char *accept)

计算 **s** 的初始子字符串的长度,该子字符串仅包含 **accept** 中的字母

参数

const char *s

要搜索的字符串

const char *accept

要搜索的字符串

size_t strcspn(const char *s, const char *reject)

计算 **s** 的初始子字符串的长度,该子字符串不包含 **reject** 中的字母

参数

const char *s

要搜索的字符串

const char *reject

要避免的字符串

char *strpbrk(const char *cs, const char *ct)

查找一组字符中首次出现的位置

参数

const char *cs

要搜索的字符串

const char *ct

要搜索的字符

char *strsep(char **s, const char *ct)

将字符串拆分为标记

参数

char **s

要搜索的字符串

const char *ct

要搜索的字符

描述

strsep() 更新 **s** 指向标记之后,为下一次调用做好准备。

它也返回空标记,其行为与 libc 同名函数完全相同。实际上,它是从 glibc2 中窃取并简化而来的。语义相同,形状更纤薄。 ;)

void *memset(void *s, int c, size_t count)

用给定值填充内存区域

参数

void *s

指向区域起点的指针。

int c

用于填充区域的字节

size_t count

区域的大小。

描述

请勿使用 memset() 访问 IO 空间,请改用 memset_io()。

void *memset16(uint16_t *s, uint16_t v, size_t count)

用 uint16_t 填充内存区域

参数

uint16_t *s

指向区域起点的指针。

uint16_t v

用于填充区域的值

size_t count

要存储的值的数量

描述

memset() 不同之处在于,它使用 uint16_t 而不是字节进行填充。请记住,**count** 是要存储的 uint16_t 的数量,而不是字节数。

void *memset32(uint32_t *s, uint32_t v, size_t count)

用 uint32_t 填充内存区域

参数

uint32_t *s

指向区域起点的指针。

uint32_t v

用于填充区域的值

size_t count

要存储的值的数量

描述

memset() 不同之处在于,它使用 uint32_t 而不是字节进行填充。请记住,**count** 是要存储的 uint32_t 的数量,而不是字节数。

void *memset64(uint64_t *s, uint64_t v, size_t count)

用 uint64_t 填充内存区域

参数

uint64_t *s

指向区域起点的指针。

uint64_t v

用于填充区域的值

size_t count

要存储的值的数量

描述

memset() 不同之处在于,它使用 uint64_t 而不是字节进行填充。请记住,**count** 是要存储的 uint64_t 的数量,而不是字节数。

void *memcpy(void *dest, const void *src, size_t count)

将一个内存区域复制到另一个内存区域

参数

void *dest

要复制到的位置

const void *src

要从中复制的位置

size_t count

区域的大小。

描述

您不应使用此函数访问 IO 空间,请改用 memcpy_toio() 或 memcpy_fromio()。

void *memmove(void *dest, const void *src, size_t count)

将一个内存区域复制到另一个内存区域

参数

void *dest

要复制到的位置

const void *src

要从中复制的位置

size_t count

区域的大小。

描述

memcpy() 不同,memmove() 可以处理重叠区域。

__visible int memcmp(const void *cs, const void *ct, size_t count)

比较两个内存区域

参数

const void *cs

一个内存区域

const void *ct

另一个内存区域

size_t count

区域的大小。

int bcmp(const void *a, const void *b, size_t len)

当且仅当缓冲区内容相同时返回 0。

参数

const void *a

指向第一个缓冲区的指针。

const void *b

指向第二个缓冲区的指针。

size_t len

缓冲区的大小。

描述

非零返回值的符号或大小没有特定含义,并且架构可以实现它们自己更有效的 bcmp()。因此,虽然这个特定的实现是对 memcmp 的一个简单的(尾部)调用,但除了返回值是否为零或非零之外,不要依赖任何其他内容。

void *memscan(void *addr, int c, size_t size)

在内存区域中查找字符。

参数

void *addr

内存区域

int c

要搜索的字节

size_t size

区域的大小。

描述

返回第一次出现 **c** 的地址,如果未找到 **c**,则返回区域后 1 个字节

char *strstr(const char *s1, const char *s2)

在以 NUL 结尾的字符串中查找第一个子字符串

参数

const char *s1

要搜索的字符串

const char *s2

要搜索的字符串

char *strnstr(const char *s1, const char *s2, size_t len)

在长度受限的字符串中查找第一个子字符串

参数

const char *s1

要搜索的字符串

const char *s2

要搜索的字符串

size_t len

要搜索的最大字符数

void *memchr(const void *s, int c, size_t n)

在内存区域中查找字符。

参数

const void *s

内存区域

int c

要搜索的字节

size_t n

区域的大小。

描述

返回第一次出现 **c** 的地址,如果未找到 **c**,则返回 NULL

void *memchr_inv(const void *start, int c, size_t bytes)

在内存区域中查找不匹配的字符。

参数

const void *start

内存区域

int c

查找除 c 以外的字符

size_t bytes

区域的大小。

描述

返回第一个不是 **c** 的字符的地址,如果整个缓冲区仅包含 **c**,则返回 NULL

void *memdup_array_user(const void __user *src, size_t n, size_t size)

从用户空间复制数组

参数

const void __user *src

用户空间中的源地址

size_t n

要复制的数组元素的数量

size_t size

一个数组元素的大小

返回

失败时返回 ERR_PTR()。结果在物理上是连续的,应由 kfree() 释放。

void *vmemdup_array_user(const void __user *src, size_t n, size_t size)

从用户空间复制数组

参数

const void __user *src

用户空间中的源地址

size_t n

要复制的数组元素的数量

size_t size

一个数组元素的大小

返回

失败时返回 ERR_PTR()。结果在物理上可能不连续。使用 kvfree() 进行释放。

strscpy

strscpy (dst, src, ...)

将 C 字符串复制到大小已知的缓冲区

参数

dst

字符串要复制到的位置

src

字符串要从哪里复制

...

目标缓冲区的大小(可选)

描述

将源字符串 **src** 或尽可能多的内容复制到目标 **dst** 缓冲区中。如果字符串缓冲区重叠,则行为未定义。目标 **dst** 缓冲区始终以 NUL 结尾,除非其大小为零。

仅当 **dst** 不是数组,或者当复制需要小于 sizeof(**dst**) 时,才需要大小参数 **...**。

首选 strncpy(),因为它始终返回有效字符串,并且不会不必要地强制目标缓冲区的尾部填充零。如果需要填充,请使用 strscpy_pad()

返回 **dst** 中复制的字符数(不包括尾随的 NUL),如果 **size** 为 0 或从 **src** 的复制被截断,则返回 -E2BIG。

strscpy_pad

strscpy_pad (dst, src, ...)

将 C 字符串复制到大小已知的缓冲区

参数

dst

字符串要复制到的位置

src

字符串要从哪里复制

...

目标缓冲区的大小

描述

将字符串或尽可能多的内容复制到 dest 缓冲区中。如果字符串缓冲区重叠,则行为未定义。目标缓冲区始终以 NUL 结尾,除非其大小为零。

如果源字符串短于目标缓冲区,则缓冲区中的其余字节将用 NUL 字节填充。

有关为什么您可能要考虑使用“strscpy”函数的完整解释,请参阅 strscpy() 的函数文档字符串。

返回

  • 复制的字符数(不包括尾随的 NULs

  • 如果 count 为 0 或 **src** 被截断,则返回 -E2BIG。

bool mem_is_zero(const void *s, size_t n)

检查一块内存区域是否全部为 0。

参数

const void *s

内存区域

size_t n

区域的大小

返回

如果内存区域全部为 0,则为 True。

sysfs_match_string

sysfs_match_string (_a, _s)

在数组中匹配给定的字符串

参数

_a

字符串数组

_s

要匹配的字符串

描述

用于 __sysfs_match_string() 的辅助函数。自动计算 a 的大小。

bool strstarts(const char *str, const char *prefix)

str 是否以 prefix 开头?

参数

const char *str

要检查的字符串

const char *prefix

要查找的前缀。

void memzero_explicit(void *s, size_t count)

用 0 填充一块内存区域(例如,敏感的密钥数据)。

参数

void *s

指向区域起点的指针。

size_t count

区域的大小。

注意

通常使用 memset() 就足够了 (!),但在需要在作用域末尾清除 _local_ 数据的情况下,应该使用 memzero_explicit() 来防止编译器优化掉清零操作。

描述

memzero_explicit() 不需要特定于架构的版本,因为它只是隐式调用 memset() 中的一个版本。

const char *kbasename(const char *path)

返回路径名的最后一部分。

参数

const char *path

从中提取文件名的路径。

strtomem_pad

strtomem_pad (dest, src, pad)

将以 NUL 结尾的字符串复制到非 NUL 结尾的缓冲区

参数

dest

目标字符数组的指针(标记为 __nonstring)

src

指向以 NUL 结尾的字符串的指针

pad

在复制后,用于填充 dest 中任何剩余字节的填充字符

描述

这是对 strncpy() 的替代,用于目标不是以 NUL 结尾的字符串的情况,但对源大小进行了边界检查,并具有显式的填充字符。如果不需要填充,请使用 strtomem()

请注意,dest 的大小不是参数,因为 dest 的长度必须可由编译器发现。

strtomem

strtomem (dest, src)

将以 NUL 结尾的字符串复制到非 NUL 结尾的缓冲区

参数

dest

目标字符数组的指针(标记为 __nonstring)

src

指向以 NUL 结尾的字符串的指针

描述

这是对 strncpy() 的替代,用于目标不是以 NUL 结尾的字符串的情况,但对源大小进行了边界检查,且没有尾部填充。如果需要填充,请使用 strtomem_pad()

请注意,dest 的大小不是参数,因为 dest 的长度必须可由编译器发现。

memtostr

memtostr (dest, src)

将可能非 NUL 结尾的字符串复制到以 NUL 结尾的字符串

参数

dest

指向以 NUL 结尾的目标字符串的指针

src

指向字符数组的指针(可能标记为 __nonstring)

描述

这是对 strncpy() 的替代,用于源不是以 NUL 结尾的字符串的情况。

请注意,destsrc 的大小必须在编译时已知。

memtostr_pad

memtostr_pad (dest, src)

将可能非 NUL 结尾的字符串复制到以 NUL 结尾的字符串,并在目标中进行 NUL 填充

参数

dest

指向以 NUL 结尾的目标字符串的指针

src

指向字符数组的指针(可能标记为 __nonstring)

描述

这是对 strncpy() 的替代,用于源不是以 NUL 结尾的字符串的情况。

请注意,destsrc 的大小必须在编译时已知。

memset_after

memset_after (obj, v, member)

将结构成员之后的值设置为结构末尾

参数

obj

目标结构实例的地址

v

要重复写入的字节值

member

从哪个结构成员之后开始写入字节

描述

这对于清除给定成员后的填充非常有用。

memset_startat

memset_startat (obj, v, member)

将值从成员开始设置到结构的末尾

参数

obj

目标结构实例的地址

v

要重复写入的字节值

member

开始写入的结构成员

描述

请注意,如果前一个成员和目标成员之间存在填充,则应使用 memset_after() 清除之前的填充。

size_t str_has_prefix(const char *str, const char *prefix)

测试字符串是否具有给定的前缀

参数

const char *str

要测试的字符串

const char *prefix

查看 str 是否以此字符串开头的字符串

描述

测试字符串前缀的常见方法是执行

strncmp(str, prefix, sizeof(prefix) - 1)

但这可能由于拼写错误,或如果前缀是指针而不是常量而导致错误。请改用 str_has_prefix()

返回

  • 如果 strprefix 开头,则返回 strlen(prefix)

  • 如果 str 不以 prefix 开头,则返回 0

char *kstrdup(const char *s, gfp_t gfp)

为现有字符串分配空间并复制该字符串

参数

const char *s

要复制的字符串

gfp_t gfp

在分配内存时 kmalloc() 调用中使用的 GFP 掩码

返回

s 的新分配的副本,如果出错则为 NULL

const char *kstrdup_const(const char *s, gfp_t gfp)

有条件地复制现有常量字符串

参数

const char *s

要复制的字符串

gfp_t gfp

在分配内存时 kmalloc() 调用中使用的 GFP 掩码

注意

由 kstrdup_const 分配的字符串应由 kfree_const 释放,并且不得传递给 krealloc()。

返回

如果源字符串在 .rodata 部分中,则为源字符串,否则回退到 kstrdup。

char *kstrndup(const char *s, size_t max, gfp_t gfp)

为现有字符串分配空间并复制该字符串

参数

const char *s

要复制的字符串

size_t max

最多从 s 读取 max 个字符

gfp_t gfp

在分配内存时 kmalloc() 调用中使用的 GFP 掩码

注意

如果大小完全已知,请改用 kmemdup_nul()

返回

s 的新分配的副本,如果出错则为 NULL

void *kmemdup(const void *src, size_t len, gfp_t gfp)

内存区域的副本

参数

const void *src

要复制的内存区域

size_t len

内存区域的长度

gfp_t gfp

要使用的 GFP 掩码

返回

新分配的 src 副本,如果发生错误则为 NULL,结果在物理上是连续的。 使用 kfree() 进行释放。

char *kmemdup_nul(const char *s, size_t len, gfp_t gfp)

从非空终止的数据创建空终止的字符串

参数

const char *s

要转换为字符串的数据

size_t len

数据的大小

gfp_t gfp

在分配内存时 kmalloc() 调用中使用的 GFP 掩码

返回

新分配的 s 副本,带有空终止符,如果发生错误则为 NULL

void *memdup_user(const void __user *src, size_t len)

从用户空间复制内存区域

参数

const void __user *src

用户空间中的源地址

size_t len

要复制的字节数

返回

失败时返回 ERR_PTR()。结果在物理上是连续的,应由 kfree() 释放。

void *vmemdup_user(const void __user *src, size_t len)

从用户空间复制内存区域

参数

const void __user *src

用户空间中的源地址

size_t len

要复制的字节数

返回

失败时返回 ERR_PTR()。结果在物理上可能不连续。使用 kvfree() 进行释放。

char *strndup_user(const char __user *s, long n)

从用户空间复制现有字符串

参数

const char __user *s

要复制的字符串

long n

要复制的最大字节数,包括尾部的空字符。

返回

新分配的 s 副本,或者在发生错误时为 ERR_PTR()

void *memdup_user_nul(const void __user *src, size_t len)

从用户空间复制内存区域并以空字符终止

参数

const void __user *src

用户空间中的源地址

size_t len

要复制的字节数

返回

失败时返回 ERR_PTR()

基本内核库函数

Linux 内核提供了更多基本实用函数。

位操作

void set_bit(long nr, volatile unsigned long *addr)

原子地设置内存中的一位

参数

long nr

要设置的位

volatile unsigned long *addr

开始计数的地址

描述

这是一个宽松的原子操作(没有隐含的内存屏障)。

请注意,nr 可能非常大;此函数不限于对单个字数量进行操作。

void clear_bit(long nr, volatile unsigned long *addr)

清除内存中的一位

参数

long nr

要清除的位

volatile unsigned long *addr

开始计数的地址

描述

这是一个宽松的原子操作(没有隐含的内存屏障)。

void change_bit(long nr, volatile unsigned long *addr)

切换内存中的一位

参数

long nr

要更改的位

volatile unsigned long *addr

开始计数的地址

描述

这是一个宽松的原子操作(没有隐含的内存屏障)。

请注意,nr 可能非常大;此函数不限于对单个字数量进行操作。

bool test_and_set_bit(long nr, volatile unsigned long *addr)

设置一位并返回其旧值

参数

long nr

要设置的位

volatile unsigned long *addr

开始计数的地址

描述

这是一个原子的完全有序操作(隐含完全内存屏障)。

bool test_and_clear_bit(long nr, volatile unsigned long *addr)

清除一位并返回其旧值

参数

long nr

要清除的位

volatile unsigned long *addr

开始计数的地址

描述

这是一个原子的完全有序操作(隐含完全内存屏障)。

bool test_and_change_bit(long nr, volatile unsigned long *addr)

更改一位并返回其旧值

参数

long nr

要更改的位

volatile unsigned long *addr

开始计数的地址

描述

这是一个原子的完全有序操作(隐含完全内存屏障)。

void ___set_bit(unsigned long nr, volatile unsigned long *addr)

设置内存中的一位

参数

unsigned long nr

要设置的位

volatile unsigned long *addr

开始计数的地址

描述

set_bit() 不同,此函数是非原子的。 如果在同一内存区域上并发调用它,则效果可能是只有一个操作成功。

void ___clear_bit(unsigned long nr, volatile unsigned long *addr)

清除内存中的一位

参数

unsigned long nr

要清除的位

volatile unsigned long *addr

开始计数的地址

描述

clear_bit() 不同,此函数是非原子的。 如果在同一内存区域上并发调用它,则效果可能是只有一个操作成功。

void ___change_bit(unsigned long nr, volatile unsigned long *addr)

切换内存中的一位

参数

unsigned long nr

要更改的位

volatile unsigned long *addr

开始计数的地址

描述

change_bit() 不同,此函数是非原子的。 如果在同一内存区域上并发调用它,则效果可能是只有一个操作成功。

bool ___test_and_set_bit(unsigned long nr, volatile unsigned long *addr)

设置一位并返回其旧值

参数

unsigned long nr

要设置的位

volatile unsigned long *addr

开始计数的地址

描述

此操作是非原子的。 如果此操作的两个实例发生竞争,一个可能会显示为成功,但实际上会失败。

bool ___test_and_clear_bit(unsigned long nr, volatile unsigned long *addr)

清除一位并返回其旧值

参数

unsigned long nr

要清除的位

volatile unsigned long *addr

开始计数的地址

描述

此操作是非原子的。 如果此操作的两个实例发生竞争,一个可能会显示为成功,但实际上会失败。

bool ___test_and_change_bit(unsigned long nr, volatile unsigned long *addr)

更改一位并返回其旧值

参数

unsigned long nr

要更改的位

volatile unsigned long *addr

开始计数的地址

描述

此操作是非原子的。 如果此操作的两个实例发生竞争,一个可能会显示为成功,但实际上会失败。

bool _test_bit(unsigned long nr, volatile const unsigned long *addr)

确定是否设置了位

参数

unsigned long nr

要测试的位号

const volatile unsigned long *addr

开始计数的地址

bool _test_bit_acquire(unsigned long nr, volatile const unsigned long *addr)

使用获取语义确定是否设置了位

参数

unsigned long nr

要测试的位号

const volatile unsigned long *addr

开始计数的地址

void clear_bit_unlock(long nr, volatile unsigned long *addr)

清除内存中的一位,用于解锁

参数

long nr

要设置的位

volatile unsigned long *addr

开始计数的地址

描述

此操作是原子的,并提供释放屏障语义。

void __clear_bit_unlock(long nr, volatile unsigned long *addr)

清除内存中的一位

参数

long nr

要清除的位

volatile unsigned long *addr

开始计数的地址

描述

这是一个非原子操作,但意味着在内存操作之前有一个释放屏障。如果没有其他 CPU 可以同时修改字中的其他位,则可以将其用于解锁。

bool test_and_set_bit_lock(long nr, volatile unsigned long *addr)

设置一位并返回其旧值,用于锁定

参数

long nr

要设置的位

volatile unsigned long *addr

开始计数的地址

描述

如果返回值是 0,则此操作是原子的,并提供获取屏障语义。它可以用来实现位锁。

bool xor_unlock_is_negative_byte(unsigned long mask, volatile unsigned long *addr)

XOR 内存中的单个字节,并测试它是否为负数,用于解锁。

参数

unsigned long mask

更改在此掩码中设置的位。

volatile unsigned long *addr

包含要更改的字节的字的地址。

描述

更改由 addr 指向的字中的某些位 0-6。此操作是原子的,并提供释放屏障语义。用于优化一些通常与解锁或写回结束配对的 folio 操作。位 7 用作 PG_waiters,以指示是否有人在等待解锁。

返回

是否设置了字节的最高位。

位图操作

位图提供一个位数组,使用 unsigned long 数组实现。给定位图中有效位的数量_不_需要是 BITS_PER_LONG 的精确倍数。

位图最后一个部分使用的字中可能未使用的位是“无关紧要的”。实现不会特别努力将其保持为零。它确保它们的值不会影响任何操作的结果。返回布尔值(例如,bitmap_empty)或标量值(例如,bitmap_weight)结果的位图操作会仔细过滤掉这些未使用的位,使其不影响结果。

位图的字节顺序在小端架构上更自然。有关此排序的最佳解释,请参阅大端头文件 include/asm-ppc64/bitops.h 和 include/asm-s390/bitops.h。

linux/types.h 中的 DECLARE_BITMAP(name,bits) 宏可用于声明一个名为 “name” 的数组,该数组包含足够的 unsigned long,以包含从 0 到 “bits” - 1 的所有位位置。

可用位图操作及其在位图是单个 unsigned long 的情况下的粗略含义如下

当 nbits 在编译时已知并且最多为 BITS_PER_LONG 时,生成的代码效率更高。

bitmap_zero(dst, nbits)                     *dst = 0UL
bitmap_fill(dst, nbits)                     *dst = ~0UL
bitmap_copy(dst, src, nbits)                *dst = *src
bitmap_and(dst, src1, src2, nbits)          *dst = *src1 & *src2
bitmap_or(dst, src1, src2, nbits)           *dst = *src1 | *src2
bitmap_xor(dst, src1, src2, nbits)          *dst = *src1 ^ *src2
bitmap_andnot(dst, src1, src2, nbits)       *dst = *src1 & ~(*src2)
bitmap_complement(dst, src, nbits)          *dst = ~(*src)
bitmap_equal(src1, src2, nbits)             Are *src1 and *src2 equal?
bitmap_intersects(src1, src2, nbits)        Do *src1 and *src2 overlap?
bitmap_subset(src1, src2, nbits)            Is *src1 a subset of *src2?
bitmap_empty(src, nbits)                    Are all bits zero in *src?
bitmap_full(src, nbits)                     Are all bits set in *src?
bitmap_weight(src, nbits)                   Hamming Weight: number set bits
bitmap_weight_and(src1, src2, nbits)        Hamming Weight of and'ed bitmap
bitmap_weight_andnot(src1, src2, nbits)     Hamming Weight of andnot'ed bitmap
bitmap_set(dst, pos, nbits)                 Set specified bit area
bitmap_clear(dst, pos, nbits)               Clear specified bit area
bitmap_find_next_zero_area(buf, len, pos, n, mask)  Find bit free area
bitmap_find_next_zero_area_off(buf, len, pos, n, mask, mask_off)  as above
bitmap_shift_right(dst, src, n, nbits)      *dst = *src >> n
bitmap_shift_left(dst, src, n, nbits)       *dst = *src << n
bitmap_cut(dst, src, first, n, nbits)       Cut n bits from first, copy rest
bitmap_replace(dst, old, new, mask, nbits)  *dst = (*old & ~(*mask)) | (*new & *mask)
bitmap_scatter(dst, src, mask, nbits)       *dst = map(dense, sparse)(src)
bitmap_gather(dst, src, mask, nbits)        *dst = map(sparse, dense)(src)
bitmap_remap(dst, src, old, new, nbits)     *dst = map(old, new)(src)
bitmap_bitremap(oldbit, old, new, nbits)    newbit = map(old, new)(oldbit)
bitmap_onto(dst, orig, relmap, nbits)       *dst = orig relative to relmap
bitmap_fold(dst, orig, sz, nbits)           dst bits = orig bits mod sz
bitmap_parse(buf, buflen, dst, nbits)       Parse bitmap dst from kernel buf
bitmap_parse_user(ubuf, ulen, dst, nbits)   Parse bitmap dst from user buf
bitmap_parselist(buf, dst, nbits)           Parse bitmap dst from kernel buf
bitmap_parselist_user(buf, dst, nbits)      Parse bitmap dst from user buf
bitmap_find_free_region(bitmap, bits, order)  Find and allocate bit region
bitmap_release_region(bitmap, pos, order)   Free specified bit region
bitmap_allocate_region(bitmap, pos, order)  Allocate specified bit region
bitmap_from_arr32(dst, buf, nbits)          Copy nbits from u32[] buf to dst
bitmap_from_arr64(dst, buf, nbits)          Copy nbits from u64[] buf to dst
bitmap_to_arr32(buf, src, nbits)            Copy nbits from buf to u32[] dst
bitmap_to_arr64(buf, src, nbits)            Copy nbits from buf to u64[] dst
bitmap_get_value8(map, start)               Get 8bit value from map at start
bitmap_set_value8(map, value, start)        Set 8bit value to map at start
bitmap_read(map, start, nbits)              Read an nbits-sized value from
                                            map at start
bitmap_write(map, value, start, nbits)      Write an nbits-sized value to
                                            map at start

请注意,bitmap_zero() 和 bitmap_fill() 在 unsigned long 区域上操作,也就是说,位图后面的位直到 unsigned long 边界也将被置零或填充。请考虑使用 bitmap_clear() 或 bitmap_set() 来显式置零或填充。

asm/bitops.h 中的以下操作也适用于位图。

set_bit(bit, addr)                  *addr |= bit
clear_bit(bit, addr)                *addr &= ~bit
change_bit(bit, addr)               *addr ^= bit
test_bit(bit, addr)                 Is bit set in *addr?
test_and_set_bit(bit, addr)         Set bit and return old value
test_and_clear_bit(bit, addr)       Clear bit and return old value
test_and_change_bit(bit, addr)      Change bit and return old value
find_first_zero_bit(addr, nbits)    Position first zero bit in *addr
find_first_bit(addr, nbits)         Position first set bit in *addr
find_next_zero_bit(addr, nbits, bit)
                                    Position next zero bit in *addr >= bit
find_next_bit(addr, nbits, bit)     Position next set bit in *addr >= bit
find_next_and_bit(addr1, addr2, nbits, bit)
                                    Same as find_next_bit, but in
                                    (*addr1 & *addr2)
void __bitmap_shift_right(unsigned long *dst, const unsigned long *src, unsigned shift, unsigned nbits)

位图中位的逻辑右移

参数

unsigned long *dst

目标位图

const unsigned long *src

源位图

unsigned shift

移动的位数

unsigned nbits

位图大小,以位为单位

描述

右移(除法)意味着在 MS -> LS 位方向移动位。零被馈送到空出的 MS 位置,并且从底部移出的 LS 位会丢失。

void __bitmap_shift_left(unsigned long *dst, const unsigned long *src, unsigned int shift, unsigned int nbits)

位图中位的逻辑左移

参数

unsigned long *dst

目标位图

const unsigned long *src

源位图

unsigned int shift

移动的位数

unsigned int nbits

位图大小,以位为单位

描述

左移(乘法)意味着在 LS -> MS 方向移动位。零被馈送到空出的 LS 位位置,并且那些从顶部移出的 MS 位会丢失。

void bitmap_cut(unsigned long *dst, const unsigned long *src, unsigned int first, unsigned int cut, unsigned int nbits)

从位图中移除位区域,并将剩余位右移

参数

unsigned long *dst

目标位图,可能与src重叠

const unsigned long *src

源位图

unsigned int first

要移除的区域的起始位

unsigned int cut

要移除的位数

unsigned int nbits

位图大小,以位为单位

描述

src 的第 n 位被设置,且 n 小于 first 时,或者当 src 的第 m 位被设置,且 first <= n < nbits,且 m = n + cut 时,设置 dst 的第 n 位。

用图示表示,以下是一个大端 32 位架构的示例

src 位图如下

31                                   63
|                                    |
10000000 11000001 11110010 00010101  10000000 11000001 01110010 00010101
                |  |              |                                    |
               16  14             0                                   32

如果 cut 为 3,且 first 为 14,则 src 中位 14-16 被删除,而 dst 如下

31                                   63
|                                    |
10110000 00011000 00110010 00010101  00010000 00011000 00101110 01000010
                   |              |                                    |
                   14 (bit 17     0                                   32
                       from @src)

请注意,dstsrc 可能部分或完全重叠。

这是以显而易见的方式实现的,每次移动位都使用移位和进位步骤。优化的工作留给编译器完成。

unsigned long bitmap_find_next_zero_area_off(unsigned long *map, unsigned long size, unsigned long start, unsigned int nr, unsigned long align_mask, unsigned long align_offset)

查找一个连续对齐的零区域

参数

unsigned long *map

搜索基于的起始地址

unsigned long size

位图的大小(以位为单位)

unsigned long start

开始搜索的位号

unsigned int nr

正在寻找的零位的数量

unsigned long align_mask

零区域的对齐掩码

unsigned long align_offset

零区域的对齐偏移

描述

align_mask 应为 2 的幂减 1;效果是此函数找到的所有零区域的位偏移量加 align_offset 是该 2 的幂的倍数。

void bitmap_remap(unsigned long *dst, const unsigned long *src, const unsigned long *old, const unsigned long *new, unsigned int nbits)

将一对位图定义的映射应用于另一个位图

参数

unsigned long *dst

重新映射的结果

const unsigned long *src

要重新映射的子集

const unsigned long *old

定义映射的域

const unsigned long *new

定义映射的范围

unsigned int nbits

每个位图中的位数

描述

oldnew 定义位位置的映射,使得 old 中第 n 个设置位所占据的任何位置都映射到 new 中的第 n 个设置位。在更一般的情况下,允许 new 的权重 'w' 小于 old 的权重,将 old 中第 n 个设置位的位置映射到 new 中第 m 个设置位的位置,其中 m == n % w。

如果 oldnew 位图中的任何一个为空,或者如果 srcdst 指向同一位置,则此例程会将 src 复制到 dst

old 中未设置位的位置映射到自身(恒等映射)。

将上述指定的映射应用于 src,将结果放入 dst,清除先前在 dst 中设置的任何位。

例如,假设 old 设置了第 4 到第 7 位,而 new 设置了第 12 到第 15 位。这定义了位位置 4 到 12、5 到 13、6 到 14 和 7 到 15 的映射,并且所有其他位位置保持不变。因此,如果 src 进入此例程时设置了第 1、5 和 7 位,则 dst 应保留设置的第 1、13 和 15 位。

int bitmap_bitremap(int oldbit, const unsigned long *old, const unsigned long *new, int bits)

将一对位图定义的映射应用于单个位

参数

int oldbit

要映射的位位置

const unsigned long *old

定义映射的域

const unsigned long *new

定义映射的范围

int bits

每个位图中的位数

描述

oldnew 定义位位置的映射,使得 old 中第 n 个设置位所占据的任何位置都映射到 new 中的第 n 个设置位。在更一般的情况下,允许 new 的权重 'w' 小于 old 的权重,将 old 中第 n 个设置位的位置映射到 new 中第 m 个设置位的位置,其中 m == n % w。

old 中未设置位的位置映射到自身(恒等映射)。

将上述指定的映射应用于位位置 oldbit,返回新的位位置。

例如,假设 old 设置了第 4 到第 7 位,而 new 设置了第 12 到第 15 位。这定义了位位置 4 到 12、5 到 13、6 到 14 和 7 到 15 的映射,并且所有其他位位置保持不变。因此,如果说 oldbit 是 5,则此例程将返回 13。

void bitmap_from_arr32(unsigned long *bitmap, const u32 *buf, unsigned int nbits)

将 u32 位数组的内容复制到位图

参数

unsigned long *bitmap

无符号长整型数组,目标位图

const u32 *buf

u32 数组(主机字节顺序),源位图

unsigned int nbits

bitmap 中的位数

void bitmap_to_arr32(u32 *buf, const unsigned long *bitmap, unsigned int nbits)

将位图的内容复制到 u32 位数组

参数

u32 *buf

u32 数组(主机字节顺序),目标位图

const unsigned long *bitmap

无符号长整型数组,源位图

unsigned int nbits

bitmap 中的位数

void bitmap_from_arr64(unsigned long *bitmap, const u64 *buf, unsigned int nbits)

将 u64 位数组的内容复制到 bitmap。

参数

unsigned long *bitmap

无符号长整型数组,目标位图

const u64 *buf

u64 数组(主机字节序),源位图。

unsigned int nbits

bitmap 中的位数

void bitmap_to_arr64(u64 *buf, const unsigned long *bitmap, unsigned int nbits)

将 bitmap 的内容复制到 u64 位数组。

参数

u64 *buf

u64 数组(主机字节序),目标位图。

const unsigned long *bitmap

无符号长整型数组,源位图

unsigned int nbits

bitmap 中的位数

int bitmap_pos_to_ord(const unsigned long *buf, unsigned int pos, unsigned int nbits)

查找位图中给定位置的设置位的序号。

参数

const unsigned long *buf

指向位图的指针。

unsigned int pos

buf 中的一个位位置(0 <= pos < nbits)。

unsigned int nbits

buf 中有效位位置的数量。

描述

buf 中位置 pos 处的位(长度为 nbits)映射到它是第几个设置位的序号。如果未设置或者 pos 不是有效位位置,则映射到 -1。

例如,如果 buf 中仅设置了第 4 到 7 位,则 pos 值 4 到 7 将分别映射到 0 到 3,而其他 pos 值将映射到 -1。在此示例中,当 pos 值 7 映射到(返回)ord 值 3 时,表示第 7 位是 buf 中第 3 个(从第 0 个开始)设置位。

位位置 0 到 bitsbuf 中的有效位置。

void bitmap_onto(unsigned long *dst, const unsigned long *orig, const unsigned long *relmap, unsigned int bits)

相对于另一个位图转换一个位图。

参数

unsigned long *dst

生成的已转换位图。

const unsigned long *orig

原始的未转换位图。

const unsigned long *relmap

作为转换依据的相对位图。

unsigned int bits

每个位图中的位数

描述

当且仅当存在某个 m,使得 relmap 的第 n 位已设置,orig 的第 m 位已设置,并且 relmap 的第 n 位也是 relmap 的第 m 个_已设置_位时,才设置 dst 的第 n 位。(如果您第一次阅读就理解了上一句话,那么您现在的工作就有点大材小用了。)

换句话说,使用映射 { <n, m> | relmap 的第 n 位是 relmap 的第 m 个已设置位 } 将 orig 映射到(满射) dst 上。

orig 中位号高于 W 的任何设置位都映射到无处。其中 W 是 relmap 的权重(设置位数)。特别是,如果对于 orig 中设置的所有位 m,都有 m >= W,则 dst 最终将为空。在不希望出现这种空结果的情况下,一种避免方法是使用下面的 bitmap_fold() 操作符,首先将 orig 位图折叠自身,以便其所有设置位 x 都在 0 <= x < W 的范围内。bitmap_fold() 操作符通过为 orig 中设置的每个位 (m),在 dst 中设置位 (m % W) 来实现这一点。

bitmap_onto() 的示例 [1]

假设 relmap 设置了第 30-39 位,而 orig 设置了第 1、3、5、7、9 和 11 位。那么从此例程返回后,dst 将设置第 31、33、35、37 和 39 位。

orig 中设置了第 0 位时,意味着开启 dst 中与 relmap 中开启的第一个位(如果有)相对应的位。由于上述示例中第 0 位已关闭,因此我们在 dst 中关闭该位(第 30 位)。

orig 中设置了第 1 位时(如上述示例中所示),意味着开启 dst 中与 relmap 中开启的第二个位对应的位。在上述示例中,relmap 中开启的第二个位是第 31 位,因此我们开启了 dst 中的第 31 位。

类似地,我们在 dst 中开启了第 33、35、37 和 39 位,因为它们是 relmap 中设置的第 4、6、8 和 10 个设置位,并且 orig 的第 4、6、8 和 10 位(即第 3、5、7 和 9 位)也已设置。

orig 中设置了第 11 位时,意味着开启 dst 中与 relmap 中开启的第 12 位对应的位。在上述示例中,relmap 中仅开启了 10 位(30..39),因此 orig 中设置的第 11 位对 dst 没有影响。

bitmap_fold() + bitmap_onto() 的示例 [2]

假设 relmap 设置了以下 10 位

40 41 42 43 45 48 53 61 74 95

(好奇的话,这是 40 加上斐波那契数列的前十项。)

此外,假设我们使用以下代码,如上所述,先调用 bitmap_fold(),然后再调用 bitmap_onto,以避免出现空的 dst 结果

unsigned long *tmp;     // a temporary bitmap's bits

bitmap_fold(tmp, orig, bitmap_weight(relmap, bits), bits);
bitmap_onto(dst, tmp, relmap, bits);

然后,此表显示了针对各种 orig,各种 dst 值将会是什么。我列出了每个设置位的从零开始的位置。tmp 列显示了中间结果,它是通过使用 bitmap_fold()orig 位图对十(relmap 的权重)取模而计算出来的。

orig

tmp

dst

0

0

40

1

1

41

9

9

95

10

0

40 [1]

1 3 5 7

1 3 5 7

41 43 48 61

0 1 2 3 4

0 1 2 3 4

40 41 42 43 45

0 9 18 27

0 9 8 7

40 61 74 95

0 10 20 30

0

40

0 11 22 33

0 1 2 3

40 41 42 43

0 12 24 36

0 2 4 6

40 42 45 53

78 102 211

1 2 8

41 42 74 [1]

如果 origrelmap 为空(没有设置位),则将返回空的 dst

如果(如上所述)orig 中仅有的设置位位于 m 位置,其中 m >= W,(其中 W 是 relmap 的权重),则 dst 将再次返回为空。

未按上述规则设置的 dst 中的所有位都会被清除。

void bitmap_fold(unsigned long *dst, const unsigned long *orig, unsigned int sz, unsigned int nbits)

将较大的位图按指定的尺寸取模折叠为较小的位图。

参数

unsigned long *dst

生成的较小位图。

const unsigned long *orig

原始的较大位图。

unsigned int sz

指定的尺寸。

unsigned int nbits

每个位图中的位数

描述

对于 orig 中的每个位 oldbit,在 dst 中设置位 oldbit mod sz。清除 dst 中的所有其他位。 有关为什么以及如何使用此操作,请参阅 bitmap_onto() 的注释和示例 [2]。

unsigned long bitmap_find_next_zero_area(unsigned long *map, unsigned long size, unsigned long start, unsigned int nr, unsigned long align_mask)

查找一个连续对齐的零区域

参数

unsigned long *map

搜索基于的起始地址

unsigned long size

位图的大小(以位为单位)

unsigned long start

开始搜索的位号

unsigned int nr

正在寻找的零位的数量

unsigned long align_mask

零区域的对齐掩码

描述

align_mask 应该比 2 的幂小 1;其效果是此函数找到的所有零区域的位偏移量都是该 2 的幂的倍数。 align_mask 为 0 表示不需要对齐。

bool bitmap_or_equal(const unsigned long *src1, const unsigned long *src2, const unsigned long *src3, unsigned int nbits)

检查两个位图的或运算是否等于第三个位图

参数

const unsigned long *src1

指向位图 1 的指针

const unsigned long *src2

指向位图 2 的指针,将与位图 1 进行或运算

const unsigned long *src3

指向位图 3 的指针。与 *src1 | *src2 的结果进行比较

unsigned int nbits

每个位图中的位数

返回

如果 (*src1 | *src2) == *src3,则为 True,否则为 False

void bitmap_scatter(unsigned long *dst, const unsigned long *src, const unsigned long *mask, unsigned int nbits)

根据给定的掩码分散位图

参数

unsigned long *dst

分散的位图

const unsigned long *src

收集的位图

const unsigned long *mask

表示要分配到分散位图中的位的掩码

unsigned int nbits

每个位图中的位数

描述

根据给定的 mask 分散具有连续位的位图。

或者以二进制形式表示 src mask dst 0000000001011010 0001001100010011 0000001100000010

(位 0、1、2、3、4、5 被复制到 位 0、1、4、8、9、12)

操作的更“可视化”描述

src:  0000000001011010
                ||||||
         +------+|||||
         |  +----+||||
         |  |+----+|||
         |  ||   +-+||
         |  ||   |  ||
mask: ...v..vv...v..vv
      ...0..11...0..10
dst:  0000001100000010

bitmap_scatter()bitmap_gather() 之间存在关系。bitmap_gather() 可以看作是 bitmap_scatter() 操作的“反向”。有关此关系的详细信息,请参见 bitmap_scatter()

示例

如果 src 位图 = 0x005a,且 mask = 0x1313,则 dst 将为 0x0302。

void bitmap_gather(unsigned long *dst, const unsigned long *src, const unsigned long *mask, unsigned int nbits)

根据给定的掩码收集位图

参数

unsigned long *dst

收集的位图

const unsigned long *src

分散的位图

const unsigned long *mask

表示要从分散位图中提取的位的掩码

unsigned int nbits

每个位图中的位数

描述

根据给定的 mask 收集具有稀疏位的位图。

或者以二进制形式表示 src mask dst 0000001100000010 0001001100010011 0000000000011010

(位 0、1、4、8、9、12 被复制到 位 0、1、2、3、4、5)

操作的更“可视化”描述

mask: ...v..vv...v..vv
src:  0000001100000010
         ^  ^^   ^   0
         |  ||   |  10
         |  ||   > 010
         |  |+--> 1010
         |  +--> 11010
         +----> 011010
dst:  0000000000011010

bitmap_gather()bitmap_scatter() 之间存在关系。有关位图分散的详细操作,请参见 bitmap_scatter()。假设使用 bitmap_scatter(scattered, src, mask, n) 计算出的分散位图。操作 bitmap_gather(result, scattered, mask, n) 将产生等于或等效于 src 的结果。

结果可能是“等效的”,因为 bitmap_scatter()bitmap_gather() 不是双射的。结果和 src 值是等效的,因为调用 bitmap_scatter(res, src, mask, n) 和调用 bitmap_scatter(res, result, mask, n) 将导致相同的 res 值。

示例

如果 src 位图 = 0x0302,且 mask = 0x1313,则 dst 将为 0x001a。

void bitmap_release_region(unsigned long *bitmap, unsigned int pos, int order)

释放已分配的位图区域

参数

unsigned long *bitmap

与位图对应的无符号长整型数组

unsigned int pos

要释放的位区域的起始位置

int order

要释放的区域大小(位数的以 2 为底的对数)

描述

这是 __bitmap_find_free_region() 的补充,并释放找到的区域(通过在位图中清除它)。

int bitmap_allocate_region(unsigned long *bitmap, unsigned int pos, int order)

分配位图区域

参数

unsigned long *bitmap

与位图对应的无符号长整型数组

unsigned int pos

要分配的位区域的起始位置

int order

要分配的区域大小(位数的以 2 为底的对数)

描述

分配(在位图中设置位)位图的指定区域。

返回

成功时为 0,如果指定的区域未释放(并非所有位均为零),则为 -EBUSY

int bitmap_find_free_region(unsigned long *bitmap, unsigned int bits, int order)

查找一个连续的、对齐的内存区域

参数

unsigned long *bitmap

与位图对应的无符号长整型数组

unsigned int bits

位图中位的数量

int order

要查找的区域大小(以 2 为底的位数对数)

描述

bits 位的 位图 中查找一段空闲(零)位的区域,并分配它们(将它们设置为 1)。仅考虑长度为 2 的幂(order)且与该 2 的幂对齐的区域,这使得搜索算法速度更快。

返回

分配区域在位图中的位偏移量,如果失败,则为 -errno。

BITMAP_FROM_U64

BITMAP_FROM_U64 (n)

以适合位图的格式表示 u64 值。

参数

n

u64 值

描述

Linux 位图在内部是 unsigned long 数组,即 32 位环境中的 32 位整数,64 位环境中的 64 位整数。

在 Linux ABI 中,字节序和字长有四种组合:LE64、BE64、LE32 和 BE32。

在 64 位内核上,64 位 LE 和 BE 数字在位图中自然排序,因此不需要任何特殊处理。

在 32 位内核上,32 位 LE ABI 将 64 位数字的低位字存储在内存中的高位字之前,而 32 位 BE 将高位字存储在低位字之前。另一方面,位图表示为 32 位字的数组,因此位 N 的位置可以计算为:字 #(N/32) 和该字中的位 #(N``32``)。例如,位 #42 位于第 2 个字的第 10 个位置。它与 32 位 LE ABI 匹配,我们可以简单地让编译器像通常那样将 64 位值存储在内存中。但是对于 BE,我们需要手动交换高位和低位字。

考虑到所有这些,宏 BITMAP_FROM_U64() 明确地重新排序了 u64 的高位和低位部分。对于 LE32,它什么都不做,对于 BE 环境,它会交换高位和低位字,正如位图所预期的那样。

void bitmap_from_u64(unsigned long *dst, u64 mask)

检查并交换 u64 中的字。

参数

unsigned long *dst

目标位图

u64 mask

源位图

描述

在 32 位大端内核中,当使用 (u32 *)(:c:type:`val`)[*] 读取 u64 掩码时,我们将得到错误的字。即 (u32 *)(:c:type:`val`)[0] 获取高 32 位,但我们期望 u64 的低 32 位。

unsigned long bitmap_read(const unsigned long *map, unsigned long start, unsigned long nbits)

从内存区域读取 n 位的值

参数

const unsigned long *map

位图内存区域的地址

unsigned long start

n 位值的位偏移量

unsigned long nbits

值的位数大小,非零,最大为 BITS_PER_LONG

返回

位于 map 内存区域中 start 位偏移处的 nbits 位的值。对于 nbits = 0 和 nbits > BITS_PER_LONG,返回值未定义。

void bitmap_write(unsigned long *map, unsigned long value, unsigned long start, unsigned long nbits)

在内存区域内写入 n 位的值

参数

unsigned long *map

位图内存区域的地址

unsigned long value

要写入的值,钳位到 nbits

unsigned long start

n 位值的位偏移量

unsigned long nbits

值的位数大小,非零,最大为 BITS_PER_LONG。

描述

bitmap_write() 的行为类似于执行 nbits 次 __assign_bit() 调用,即忽略超出 nbits 的位

for (bit = 0; bit < nbits; bit++)

__assign_bit(start + bit, bitmap, val & BIT(bit));

对于 nbits == 0 和 nbits > BITS_PER_LONG,不执行任何写入。

命令行解析

int get_option(char **str, int *pint)

从选项字符串中解析整数

参数

char **str

选项字符串

int *pint

(可选输出)从 str 解析的整数值

从选项字符串读取整数;如果可用,则接受后续的逗号。

pint 为 NULL 时,该函数可以用作字符串中当前选项的验证器。

返回值:0 - 字符串中没有整数 1 - 找到整数,没有后续逗号 2 - 找到整数,包括后续逗号 3 - 找到连字符以表示范围

没有整数的前导连字符不是整数情况,但为了简化,我们将其消耗掉。

char *get_options(const char *str, int nints, int *ints)

将字符串解析为整数列表

参数

const char *str

要解析的字符串

int nints

整数数组的大小

int *ints

整数数组(必须至少容纳一个元素)

此函数解析包含逗号分隔的整数列表、连字符分隔的_正_整数范围或两者的组合的字符串。当数组已满或无法从字符串中检索更多数字时,解析停止。

nints 为 0 时,该函数仅验证给定的 str,并返回如下所述的可解析整数的数量。

返回

第一个元素填充范围中收集的整数的数量。其余的是从 str 解析的内容。

返回值是字符串中导致解析结束的字符(如果 str 完全可解析,则通常为 null 终止符)。

unsigned long long memparse(const char *ptr, char **retptr)

将带有内存后缀的字符串解析为数字

参数

const char *ptr

解析开始的位置

char **retptr

(输出)解析完成后指向下一个字符的可选指针

将字符串解析为数字。存储在 ptr 的数字可能带有 K、M、G、T、P、E 后缀。

错误指针

IS_ERR_VALUE

IS_ERR_VALUE (x)

检测错误指针。

参数

x

要检查的指针。

描述

类似于 IS_ERR(),但如果结果未使用,则不会生成编译器警告。

void *ERR_PTR(long error)

创建错误指针。

参数

long error

一个负的错误代码。

描述

error 编码为指针值。用户应将结果视为不透明,并且不应对错误的编码方式做任何假设。

返回

一个在其值内编码了 error 的指针。

long PTR_ERR(__force const void *ptr)

从错误指针中提取错误代码。

参数

__force const void *ptr

一个错误指针。

返回

ptr 中的错误代码。

bool IS_ERR(__force const void *ptr)

检测错误指针。

参数

__force const void *ptr

要检查的指针。

返回

如果 ptr 是错误指针,则为 true;否则为 false。

bool IS_ERR_OR_NULL(__force const void *ptr)

检测错误指针或空指针。

参数

__force const void *ptr

要检查的指针。

描述

类似于 IS_ERR(),但对于空指针也返回 true。

void *ERR_CAST(__force const void *ptr)

将错误值指针显式转换为另一种指针类型

参数

__force const void *ptr

要转换的指针。

描述

以明确表示正在进行的操作的方式,将错误值指针显式转换为另一种指针类型。

int PTR_ERR_OR_ZERO(__force const void *ptr)

如果指针有错误代码,则从中提取错误代码。

参数

__force const void *ptr

一个潜在的错误指针。

描述

方便函数,可用于返回错误代码的函数中,以传播作为错误指针接收的错误。例如, return PTR_ERR_OR_ZERO(ptr); 取代了

if (IS_ERR(ptr))
        return PTR_ERR(ptr);
else
        return 0;

返回

如果 ptr 是错误指针,则为错误代码;否则为 0。

排序

void sort_r(void *base, size_t num, size_t size, cmp_r_func_t cmp_func, swap_r_func_t swap_func, const void *priv)

对元素数组进行排序

参数

void *base

指向要排序的数据的指针

size_t num

元素数量

size_t size

每个元素的大小

cmp_r_func_t cmp_func

指向比较函数的指针

swap_r_func_t swap_func

指向交换函数的指针,如果不需要则为 NULL

const void *priv

传递给比较函数的第三个参数

描述

此函数对给定的数组执行堆排序。如果需要执行比内存复制更多的操作(例如,修复指针或辅助数据),则可以提供 swap_func 函数,但内置交换避免了缓慢的 retpoline,因此速度明显更快。

平均情况和最坏情况下的排序时间均为 O(n log n)。虽然快速排序在平均情况下稍快一些,但它存在可利用的 O(n*n) 最坏情况行为和额外的内存需求,使其不太适合内核使用。

void list_sort(void *priv, struct list_head *head, list_cmp_func_t cmp)

对列表进行排序

参数

void *priv

私有数据,对于 list_sort() 是不透明的,传递给 cmp

struct list_head *head

要排序的列表

list_cmp_func_t cmp

元素比较函数

描述

如果 a 应该在 b 之后排序(如果要升序排序,则为 “a > b”),则比较函数 cmp 必须返回 > 0;如果 a 应该在 b 之前排序应保留其原始顺序,则返回 <= 0。它始终使用输入中第一个出现的元素 a 调用,并且 list_sort 是一个稳定排序,因此无需区分 a < ba == b 的情况。

这与两种风格的 cmp 函数兼容:- 返回 <0 / =0 / >0 的传统风格,或 - 返回布尔值 0/1。后者可以在比较中节省一些周期(例如,block/blk-mq.c 中的 plug_ctx_cmp() 所使用)。

编写多字比较的好方法是

if (a->high != b->high)
        return a->high > b->high;
if (a->middle != b->middle)
        return a->middle > b->middle;
return a->low > b->low;

此归并排序尽可能急切,同时始终执行至少 2:1 的平衡合并。给定两个大小为 2^k 的待处理子列表,一旦我们有 2^k 个后续元素,它们就会合并为大小为 2^(k+1) 的列表。

因此,只要 3*2^k 个元素可以放入缓存中,它就会避免缓存抖动。不如完全急切的自底向上归并排序好,但它使用的比较次数少了 0.2*n,因此在所有内容都适合 L1 的常见情况下速度更快。

合并由“count”控制,即待处理列表中的元素数量。这是一个非常简单的代码,但相当微妙。

每次我们递增“count”时,我们都会设置一个位(位 k)并清除位 k-1 .. 0。每次发生这种情况时(除了每个位的第一次,当 count 递增到 2^k 时),我们将两个大小为 2^k 的列表合并为一个大小为 2^(k+1) 的列表。

此合并发生在计数达到 2^k 的奇数倍时,此时我们在较小的列表中有 2^k 个元素待处理,因此可以安全地合并掉两个大小为 2^k 的列表。

在这种情况发生两次后,我们创建了两个大小为 2^(k+1) 的列表,它们将在我们创建第三个大小为 2^(k+1) 的列表之前合并为大小为 2^(k+2) 的列表,因此永远不会有超过两个待处理的列表。

大小为 2^k 的待处理列表的数量由“count”的位 k 的状态加上两个额外的信息决定

  • 位 k-1 的状态(当 k == 0 时,始终考虑位 -1 设置),以及

  • 高位是否为零或非零(即 count >= 2^(k+1))。

我们区分六种状态。“x”表示一些任意位,“y”表示一些任意非零位: 0: 00x:0 个大小为 2^k 的待处理列表;x 个大小 < 2^k 的待处理列表 1: 01x:0 个大小为 2^k 的待处理列表;2^(k-1) + x 个大小 < 2^k 的待处理列表 2: x10x:0 个大小为 2^k 的待处理列表;2^k + x 个大小 < 2^k 的待处理列表 3: x11x:1 个大小为 2^k 的待处理列表;2^(k-1) + x 个大小 < 2^k 的待处理列表 4: y00x:1 个大小为 2^k 的待处理列表;2^k + x 个大小 < 2^k 的待处理列表 5: y01x:2 个大小为 2^k 的待处理列表;2^(k-1) + x 个大小 < 2^k 的待处理列表(合并并循环回状态 2)

我们在 2->3 和 4->5 转换中获得大小为 2^k 的列表(因为在更高的位非零时设置了位 k-1),并在 5->2 转换中将它们合并掉。特别要注意的是,就在 5->2 转换之前,所有较低位都是 11(状态 3),因此每个较小的大小都有一个列表。

当我们到达输入末尾时,我们将所有待处理的列表从最小到最大合并。如果您仔细研究上面的案例 2 到 5,您可以看到我们与大小为 2^k 的列表合并的元素数量从 2^(k-1) (当 x == 0 时的案例 3 和 5)到 2^(k+1) - 1(当 x == 2^(k-1) - 1 时的案例 5 的第二次合并)。

文本搜索

简介

文本搜索基础设施为线性和非线性数据提供文本搜索功能。单个搜索算法在模块中实现,并由用户选择。

架构

  User
  +----------------+
  |        finish()|<--------------(6)-----------------+
  |get_next_block()|<--------------(5)---------------+ |
  |                |                     Algorithm   | |
  |                |                    +------------------------------+
  |                |                    |  init()   find()   destroy() |
  |                |                    +------------------------------+
  |                |       Core API           ^       ^          ^
  |                |      +---------------+  (2)     (4)        (8)
  |             (1)|----->| prepare()     |---+       |          |
  |             (3)|----->| find()/next() |-----------+          |
  |             (7)|----->| destroy()     |----------------------+
  +----------------+      +---------------+

(1) User configures a search by calling textsearch_prepare() specifying
    the search parameters such as the pattern and algorithm name.
(2) Core requests the algorithm to allocate and initialize a search
    configuration according to the specified parameters.
(3) User starts the search(es) by calling textsearch_find() or
    textsearch_next() to fetch subsequent occurrences. A state variable
    is provided to the algorithm to store persistent variables.
(4) Core eventually resets the search offset and forwards the find()
    request to the algorithm.
(5) Algorithm calls get_next_block() provided by the user continuously
    to fetch the data to be searched in block by block.
(6) Algorithm invokes finish() after the last call to get_next_block
    to clean up any leftovers from get_next_block. (Optional)
(7) User destroys the configuration by calling textsearch_destroy().
(8) Core notifies the algorithm to destroy algorithm specific
    allocations. (Optional)

用法

在执行搜索之前,必须通过调用 textsearch_prepare() 来创建一个配置,该配置指定搜索算法、要查找的模式和标志。作为标志,您可以设置 TS_IGNORECASE 来执行不区分大小写的匹配。但这可能会降低算法的性能,因此您应自行承担使用它的风险。返回的配置可以任意次数使用,甚至可以并行使用,只要每个实例都提供一个单独的 struct ts_state 变量。

实际的搜索是通过调用 textsearch_find_continuous() 来处理线性数据,或者通过提供自己的 get_next_block() 实现并调用 textsearch_find() 来处理非线性数据。这两个函数都返回模式第一次出现的位置,如果没有找到匹配项则返回 UINT_MAX。可以通过调用 textsearch_next() 来查找后续的匹配项,而无需考虑数据的线性特征。

一旦您完成了配置的使用,必须通过 textsearch_destroy 将其释放。

示例

int pos;
struct ts_config *conf;
struct ts_state state;
const char *pattern = "chicken";
const char *example = "We dance the funky chicken";

conf = textsearch_prepare("kmp", pattern, strlen(pattern),
                          GFP_KERNEL, TS_AUTOLOAD);
if (IS_ERR(conf)) {
    err = PTR_ERR(conf);
    goto errout;
}

pos = textsearch_find_continuous(conf, &state, example, strlen(example));
if (pos != UINT_MAX)
    panic("Oh my god, dancing chickens at %d\n", pos);

textsearch_destroy(conf);
int textsearch_register(struct ts_ops *ops)

注册一个文本搜索模块

参数

struct ts_ops *ops

操作查找表

描述

文本搜索模块必须调用此函数以声明它们的存在。指定的 &**ops** 必须将 name 设置为唯一标识符,并且必须实现回调函数 find()、init()、get_pattern() 和 get_pattern_len()。

如果另一个模块已使用相同的名称注册,则返回 0 或 -EEXISTS。

int textsearch_unregister(struct ts_ops *ops)

注销一个文本搜索模块

参数

struct ts_ops *ops

操作查找表

描述

当模块被卸载时,文本搜索模块必须调用此函数以声明它们不再存在。 ops 参数必须与注册期间使用的参数相同。

成功时返回 0,如果未找到匹配的文本搜索注册,则返回 -ENOENT。

unsigned int textsearch_find_continuous(struct ts_config *conf, struct ts_state *state, const void *data, unsigned int len)

在连续/线性数据中搜索模式

参数

struct ts_config *conf

搜索配置

struct ts_state *state

搜索状态

const void *data

要搜索的数据

unsigned int len

数据长度

描述

textsearch_find() 的简化版本,用于连续/线性数据。调用 textsearch_next() 来检索后续的匹配项。

返回模式第一次出现的位置,如果没有找到匹配项,则返回 UINT_MAX

struct ts_config *textsearch_prepare(const char *algo, const void *pattern, unsigned int len, gfp_t gfp_mask, int flags)

准备搜索

参数

const char *algo

搜索算法的名称

const void *pattern

模式数据

unsigned int len

模式的长度

gfp_t gfp_mask

分配掩码

int flags

搜索标志

描述

查找搜索算法模块,并为指定的模式创建一个新的文本搜索配置。

根据指定的参数返回新的文本搜索配置,或返回 ERR_PTR()。如果传递零长度模式,则此函数返回 EINVAL。

注意

不同搜索算法之间的模式格式可能不兼容。

不同搜索算法之间的模式格式可能不兼容。

void textsearch_destroy(struct ts_config *conf)

销毁搜索配置

参数

struct ts_config *conf

搜索配置

描述

释放配置的所有引用并释放内存。

unsigned int textsearch_next(struct ts_config *conf, struct ts_state *state)

继续搜索模式

参数

struct ts_config *conf

搜索配置

struct ts_state *state

搜索状态

描述

继续搜索以查找更多模式出现的位置。必须调用 textsearch_find() 以查找第一个出现的位置,以便重置状态。

返回模式下一个出现的位置,如果没有找到匹配项,则返回 UINT_MAX。

unsigned int textsearch_find(struct ts_config *conf, struct ts_state *state)

开始搜索模式

参数

struct ts_config *conf

搜索配置

struct ts_state *state

搜索状态

描述

返回模式第一次出现的位置,如果没有找到匹配项,则返回 UINT_MAX。

void *textsearch_get_pattern(struct ts_config *conf)

返回模式的头部

参数

struct ts_config *conf

搜索配置

unsigned int textsearch_get_pattern_len(struct ts_config *conf)

返回模式的长度

参数

struct ts_config *conf

搜索配置

Linux 中的 CRC 和数学函数

算术溢出检查

check_add_overflow

check_add_overflow (a, b, d)

计算加法并进行溢出检查

参数

a

第一个加数

b

第二个加数

d

用于存储总和的指针

描述

如果发生回绕,则返回 true;否则返回 false。

*d 保存尝试加法的结果,无论是否发生回绕。

wrapping_add

wrapping_add (type, a, b)

有意执行回绕加法

参数

type

计算结果的类型

a

第一个加数

b

第二个加数

描述

返回可能回绕的加法,而不会触发可能已启用的任何回绕清理器。

wrapping_assign_add

wrapping_assign_add (var, offset)

有意执行回绕增量赋值

参数

var

要递增的变量

offset

要添加的量

描述

通过 offsetvar 进行环绕递增。返回 var 的结果值。不会触发任何环绕检查。

返回 var 的新值。

check_sub_overflow

check_sub_overflow (a, b, d)

计算减法并进行溢出检查

参数

a

被减数;从中减去的值

b

减数;从 a 中减去的值

d

指向存储差值的指针

描述

如果发生回绕,则返回 true;否则返回 false。

*d 保存尝试减法的结果,无论是否发生环绕。

wrapping_sub

wrapping_sub (type, a, b)

有意执行环绕减法

参数

type

计算结果的类型

a

被减数;从中减去的值

b

减数;从 a 中减去的值

描述

返回可能环绕的减法结果,而不会触发任何可能已启用的环绕检查。

wrapping_assign_sub

wrapping_assign_sub (var, offset)

有意执行环绕递减赋值

参数

var

要递减的变量

offset

要减去的量

描述

通过 offsetvar 进行环绕递减。返回 var 的结果值。不会触发任何环绕检查。

返回 var 的新值。

check_mul_overflow

check_mul_overflow (a, b, d)

计算乘法并进行溢出检查

参数

a

第一个因子

b

第二个因子

d

指向存储乘积的指针

描述

如果发生回绕,则返回 true;否则返回 false。

*d 保存尝试乘法的结果,无论是否发生环绕。

wrapping_mul

wrapping_mul (type, a, b)

有意执行环绕乘法

参数

type

计算结果的类型

a

第一个因子

b

第二个因子

描述

返回可能环绕的乘法结果,而不会触发任何可能已启用的环绕检查。

check_shl_overflow

check_shl_overflow (a, s, d)

计算左移值并检查溢出

参数

a

要移位的值

s

要左移的位数

d

指向存储结果位置的指针

描述

计算 *d = (a << s)

如果“*d”无法保存结果,或者当“a << s”没有意义时,则返回 true。示例条件

  • 当存储在 *d 中时,'a << s' 会导致位丢失。

  • s”是垃圾(例如,负数)或太大,以至于“a << s”的结果保证为 0。

  • a”为负数。

  • 如果 ‘*d’ 中有符号位,则 ‘a << s’ 会设置符号位。

*d’ 将保存尝试移位的结果,但如果返回 true,则不被认为是“安全使用”。

overflows_type

overflows_type (n, T)

用于检查值、变量或数据类型之间溢出的辅助函数

参数

n

要检查的源常量值或变量

T

建议存储 x 的目标变量或数据类型

描述

比较 x 表达式,以确定它是否可以安全地放入 T 中的类型存储中。xT 可以具有不同的类型。如果 x 是常量表达式,则这也将解析为常量表达式。

返回

如果可能发生溢出,则为 true,否则为 false。

castable_to_type

castable_to_type (n, T)

类似于 __same_type(),但也允许强制转换的字面量

参数

n

变量或常量值

T

变量或数据类型

描述

与 __same_type() 宏不同,这允许将常量值作为第一个参数。如果该值不会溢出到第二个参数类型的赋值中,则返回 true。否则,将回退到 __same_type()。

size_t size_mul(size_t factor1, size_t factor2)

计算 size_t 乘法,在 SIZE_MAX 处饱和

参数

size_t factor1

第一个因子

size_t factor2

第二个因子

返回

计算 factor1 * factor2,两者都提升为 size_t,任何溢出都会导致返回值变为 SIZE_MAX。lvalue 必须是 size_t,以避免隐式类型转换。

size_t size_add(size_t addend1, size_t addend2)

计算 size_t 加法,在 SIZE_MAX 处饱和

参数

size_t addend1

第一个加数

size_t addend2

第二个加数

返回

计算 addend1 + addend2,两者都提升为 size_t,任何溢出都会导致返回值变为 SIZE_MAX。lvalue 必须是 size_t,以避免隐式类型转换。

size_t size_sub(size_t minuend, size_t subtrahend)

计算 size_t 减法,在 SIZE_MAX 处饱和

参数

size_t minuend

要减去的值

size_t subtrahend

要从 minuend 中减去的值

返回

计算 minuend - subtrahend,两者都提升为 size_t,任何溢出都会导致返回值变为 SIZE_MAX。为了与 size_add()size_mul() 辅助函数组合,任何一个参数都不能为 SIZE_MAX(否则结果将被强制为 SIZE_MAX)。lvalue 必须是 size_t,以避免隐式类型转换。

array_size

array_size (a, b)

计算二维数组的大小。

参数

a

第一个维度

b

第二个维度

描述

计算二维数组的大小:a * b

返回

表示该数组所需的字节数,溢出时返回 SIZE_MAX。

array3_size

array3_size (a, b, c)

计算三维数组的大小。

参数

a

第一个维度

b

第二个维度

c

第三个维度

描述

计算三维数组的大小:a * b * c

返回

表示该数组所需的字节数,溢出时返回 SIZE_MAX。

flex_array_size

flex_array_size (p, member, count)

计算封闭结构中灵活数组成员的大小。

参数

p

指向结构的指针。

member

灵活数组成员的名称。

计数

数组中的元素数量。

描述

计算结构 p 末尾的 countmember 元素的灵活数组的大小。

返回

所需的字节数,溢出时返回 SIZE_MAX。

struct_size

struct_size (p, member, count)

计算带有尾随灵活数组的结构的大小。

参数

p

指向结构的指针。

member

数组成员的名称。

计数

数组中的元素数量。

描述

计算结构 p 后跟 countmember 元素的数组所需的内存大小。

返回

所需的字节数,溢出时返回 SIZE_MAX。

struct_size_t

struct_size_t (type, member, count)

计算带有尾随灵活数组的结构的大小

参数

type

结构类型名称。

member

数组成员的名称。

计数

数组中的元素数量。

描述

计算结构体 type 及其后跟随的 countmember 元素数组所需的内存大小。为了保持计算与 type 类型的特定实例变量相关联,请尽可能使用 struct_size()

返回

所需的字节数,溢出时返回 SIZE_MAX。

_DEFINE_FLEX

_DEFINE_FLEX (type, name, member, count, initializer...)

DEFINE_FLEX() 系列的辅助宏。允许调用方宏传递(不同的)初始化器。

参数

type

结构体类型名称,包括 “struct” 关键字。

name

要定义的变量的名称。

member

数组成员的名称。

计数

数组中的元素数量;必须是编译时常量。

initializer...

初始化表达式(对于不初始化的情况可以为空)。

DEFINE_RAW_FLEX

DEFINE_RAW_FLEX (type, name, member, count)

当结构体没有 __counted_by 注解时,定义一个带有尾部可变数组的栈上结构体实例。

参数

type

结构体类型名称,包括 “struct” 关键字。

name

要定义的变量的名称。

member

数组成员的名称。

计数

数组中的元素数量;必须是编译时常量。

描述

定义一个带有尾部可变数组的 type 结构体的零初始化栈上实例。之后可以使用 __struct_size(name) 获取其编译时大小。

DEFINE_FLEX

DEFINE_FLEX (TYPE, NAME, MEMBER, COUNTER, COUNT)

定义一个带有尾部可变数组的栈上结构体实例。

参数

TYPE

结构体类型名称,包括 “struct” 关键字。

NAME

要定义的变量的名称。

MEMBER

数组成员的名称。

COUNTER

__counted_by 成员的名称。

COUNT

数组中的元素数量;必须是编译时常量。

描述

定义一个带有尾部可变数组的 TYPE 结构体的零初始化栈上实例。之后可以使用 __struct_size(NAME) 获取其编译时大小。

CRC 函数

uint8_t crc4(uint8_t c, uint64_t x, int bits)

计算值的 4 位 crc。

参数

uint8_t c

起始 crc4

uint64_t x

要计算校验和的值

int bits

要计算校验和的 x 中的位数

描述

返回 x 的 crc4 值,使用多项式 0b10111。

x 值被视为左对齐,并且在 crc 计算中忽略 bits 之上的位。

u8 crc7_be(u8 crc, const u8 *buffer, size_t len)

更新数据缓冲区的 CRC7

参数

u8 crc

之前的 CRC7 值

const u8 *buffer

数据指针

size_t len

缓冲区中的字节数

上下文

任意

描述

返回更新的 CRC7 值。CRC7 在字节中左对齐(lsbit 始终为 0),因为这使得计算更容易,并且所有调用者都希望使用该形式。

void crc8_populate_msb(u8 table[CRC8_TABLE_SIZE], u8 polynomial)

以反向位顺序填充给定多项式的 crc 表。

参数

u8 table[CRC8_TABLE_SIZE]

要填充的表。

u8 polynomial

要填充表的对应多项式。

void crc8_populate_lsb(u8 table[CRC8_TABLE_SIZE], u8 polynomial)

以常规位顺序填充给定多项式的 crc 表。

参数

u8 table[CRC8_TABLE_SIZE]

要填充的表。

u8 polynomial

要填充表的对应多项式。

u8 crc8(const u8 table[CRC8_TABLE_SIZE], const u8 *pdata, size_t nbytes, u8 crc)

计算给定输入数据的 crc8。

参数

const u8 table[CRC8_TABLE_SIZE]

用于计算的 crc 表。

const u8 *pdata

指向数据缓冲区的指针。

size_t nbytes

数据缓冲区中的字节数。

u8 crc

先前返回的 crc8 值。

u16 crc16(u16 crc, u8 const *buffer, size_t len)

计算数据缓冲区的 CRC-16

参数

u16 crc

之前的 CRC 值

u8 const *buffer

数据指针

size_t len

缓冲区中的字节数

描述

返回更新后的 CRC 值。

u32 __pure crc32_le_generic(u32 crc, unsigned char const *p, size_t len, const u32 (*tab)[256], u32 polynomial)

计算按位小端以太网 AUTODIN II CRC32/CRC32C

参数

u32 crc

计算的种子值。对于以太网为 ~0,有时对于其他用途为 0,如果增量计算,则为先前的 crc32/crc32c 值。

unsigned char const *p

指向运行 CRC32/CRC32C 的缓冲区的指针

size_t len

缓冲区 p 的长度

const u32 (*tab)[256]

小端以太网表

u32 polynomial

CRC32/CRC32c LE 多项式

u32 crc32_generic_shift(u32 crc, size_t len, u32 polynomial)

在对数时间内,将 len 个 0 字节附加到 crc

参数

u32 crc

原始小端 CRC(即 lsbit 是 x^31 系数)

size_t len

字节数。crc 乘以 x^(8***len**)

u32 polynomial

用于将结果减少到 32 位的模数。

描述

可以通过计算缓冲区单独范围的 CRC,然后将它们相加来并行化 CRC 计算。这将给定的 CRC 移动 8*len 位(即产生与在数据后附加 len 个零字节相同的效果),时间与 log(len) 成正比。

u32 __pure crc32_be_generic(u32 crc, unsigned char const *p, size_t len, const u32 (*tab)[256], u32 polynomial)

计算按位大端以太网 AUTODIN II CRC32

参数

u32 crc

计算的种子值。对于以太网为 ~0,有时对于其他用途为 0,如果增量计算,则为先前的 crc32 值。

unsigned char const *p

指向运行 CRC32 的缓冲区的指针

size_t len

缓冲区 p 的长度

const u32 (*tab)[256]

大端以太网表

u32 polynomial

CRC32 BE 多项式

u16 crc_ccitt(u16 crc, u8 const *buffer, size_t len)

重新计算数据缓冲区的 CRC(CRC-CCITT 变体)

参数

u16 crc

之前的 CRC 值

u8 const *buffer

数据指针

size_t len

缓冲区中的字节数

u16 crc_itu_t(u16 crc, const u8 *buffer, size_t len)

计算数据缓冲区的 CRC-ITU-T 值

参数

u16 crc

之前的 CRC 值

const u8 *buffer

数据指针

size_t len

缓冲区中的字节数

描述

返回更新后的 CRC 值

以 2 为底的对数和幂函数

bool is_power_of_2(unsigned long n)

检查一个值是否为 2 的幂

参数

unsigned long n

要检查的值

描述

确定某个值是否为 2 的幂,其中零被视为 2 的幂。

返回

如果 n 是 2 的幂,则为 true,否则为 false。

unsigned long __roundup_pow_of_two(unsigned long n)

向上取整到最接近的 2 的幂

参数

unsigned long n

要向上取整的值

unsigned long __rounddown_pow_of_two(unsigned long n)

向下取整到最接近的 2 的幂

参数

unsigned long n

要向下取整的值

const_ilog2

const_ilog2 (n)

32 位或 64 位常量无符号值的以 2 为底的对数

参数

n

参数

描述

在 sparse 期望真常量表达式的地方使用此方法,例如,用于数组索引。

ilog2

ilog2 (n)

32 位或 64 位无符号值的以 2 为底的对数

参数

n

参数

描述

具有常量能力的以 2 为底的对数计算 - 这可用于从常量数据初始化全局变量,因此具有大规模的三元运算符结构

根据 sizeof(n) 选择适当大小的优化版本

roundup_pow_of_two

roundup_pow_of_two (n)

将给定值向上取整到最接近的 2 的幂

参数

n

参数

描述

将给定值向上取整到最接近的 2 的幂 - 当 n == 0 时,结果是未定义的 - 这可用于从常量数据初始化全局变量

rounddown_pow_of_two

rounddown_pow_of_two (n)

将给定值向下取整到最接近的 2 的幂

参数

n

参数

描述

将给定值向下取整到最接近的 2 的幂 - 当 n == 0 时,结果是未定义的 - 这可用于从常量数据初始化全局变量

order_base_2

order_base_2 (n)

计算参数的(向上取整)以 2 为底的阶数

参数

n

参数

描述

此例程计算的前几个值

ob2(0) = 0 ob2(1) = 0 ob2(2) = 1 ob2(3) = 2 ob2(4) = 2 ob2(5) = 3 ... 等等。

bits_per

bits_per (n)

计算参数所需的位数

参数

n

参数

描述

这具有常量能力,可用于编译时初始化,例如位域。

此例程计算的前几个值:bf(0) = 1 bf(1) = 1 bf(2) = 2 bf(3) = 2 bf(4) = 3 ... 等等。

整数对数和幂函数

unsigned int intlog2(u32 value)

计算一个值的 log2;结果左移 24 位

参数

u32 value

该值(必须 != 0)

描述

要使用有理值,可以使用以下方法

intlog2(value) = intlog2(value * 2^x) - x * 2^24

一些用例示例

intlog2(8) 将给出 3 << 24 = 3 * 2^24

intlog2(9) 将给出 3 << 24 + ... = 3.16... * 2^24

intlog2(1.5) = intlog2(3) - 2^24 = 0.584... * 2^24

返回

log2(value) * 2^24

unsigned int intlog10(u32 value)

计算一个值的 log10;结果左移 24 位

参数

u32 value

该值(必须 != 0)

描述

要使用有理值,可以使用以下方法

intlog10(value) = intlog10(value * 10^x) - x * 2^24

一个用例示例

intlog10(1000) 将给出 3 << 24 = 3 * 2^24

由于实现,intlog10(1000) 可能不完全是 3 * 2^24

请查看 intlog2 以了解类似的示例

返回

log10(value) * 2^24

u64 int_pow(u64 base, unsigned int exp)

计算给定基数和指数的幂

参数

u64 base

将提高到给定幂的底数

unsigned int exp

要提高到的幂

描述

计算:pow(base, exp),即 base 提高到 exp

unsigned long int_sqrt(unsigned long x)

计算整数平方根

参数

unsigned long x

要计算平方根的整数

描述

计算:floor(sqrt(x))

u32 int_sqrt64(u64 x)

当预期最小 64 位输入时,强类型 int_sqrt 函数。

参数

u64 x

要计算平方根的 64 位整数

除法函数

do_div

do_div (n, base)

返回 2 个值:计算余数并更新新的被除数

参数

n

uint64_t 被除数(将被更新)

基数

uint32_t 除数

描述

摘要:uint32_t remainder = n % base; n = n / base;

返回

(uint32_t)余数

注意

宏参数 n 会多次求值,请注意副作用!

u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder)

使用 32 位除数进行无符号 64 位除法,带余数

参数

u64 dividend

无符号 64 位被除数

u32 divisor

无符号 32 位除数

u32 *remainder

指向无符号 32 位余数的指针

返回

设置 *remainder,然后返回 dividend / divisor

描述

这通常由 32 位架构提供,以提供优化的 64 位除法。

s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder)

使用 32 位除数进行带余数的有符号 64 位除法

参数

s64 dividend

有符号 64 位被除数

s32 divisor

有符号 32 位除数

s32 *remainder

指向有符号 32 位余数的指针

返回

设置 *remainder,然后返回 dividend / divisor

u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder)

使用 64 位除数和余数进行无符号 64 位除法

参数

u64 dividend

无符号 64 位被除数

u64 divisor

无符号 64 位除数

u64 *remainder

指向无符号 64 位余数的指针

返回

设置 *remainder,然后返回 dividend / divisor

u64 div64_u64(u64 dividend, u64 divisor)

无符号 64 位除法,除数为 64 位

参数

u64 dividend

无符号 64 位被除数

u64 divisor

无符号 64 位除数

返回

被除数 / 除数

s64 div64_s64(s64 dividend, s64 divisor)

有符号 64 位除法,除数为 64 位

参数

s64 dividend

有符号 64 位被除数

s64 divisor

有符号 64 位除数

返回

被除数 / 除数

u64 div_u64(u64 dividend, u32 divisor)

无符号 64 位除法,除数为 32 位

参数

u64 dividend

无符号 64 位被除数

u32 divisor

无符号 32 位除数

描述

这是最常用的 64 位除法,如果可能,应该使用它,因为许多 32 位架构可以比完整的 64 位除法更好地优化此变体。

返回

被除数 / 除数

s64 div_s64(s64 dividend, s32 divisor)

有符号 64 位除法,除数为 32 位

参数

s64 dividend

有符号 64 位被除数

s32 divisor

有符号 32 位除数

返回

被除数 / 除数

DIV64_U64_ROUND_UP

DIV64_U64_ROUND_UP (ll, d)

无符号 64 位除法,除数为 64 位,向上取整

参数

ll

无符号 64 位被除数

d

无符号 64 位除数

描述

无符号 64 位被除数除以无符号 64 位除数并向上取整。

返回

被除数 / 除数向上取整

DIV_U64_ROUND_UP

DIV_U64_ROUND_UP (ll, d)

无符号 64 位除法,除数为 32 位,向上取整

参数

ll

无符号 64 位被除数

d

无符号 32 位除数

描述

无符号 64 位被除数除以无符号 32 位除数并向上取整。

返回

被除数 / 除数向上取整

DIV64_U64_ROUND_CLOSEST

DIV64_U64_ROUND_CLOSEST (dividend, divisor)

无符号 64 位除法,除数为 64 位,四舍五入到最接近的整数

参数

被除数

无符号 64 位被除数

除数

无符号 64 位除数

描述

无符号 64 位被除数除以无符号 64 位除数,并四舍五入到最接近的整数。

返回

被除数 / 除数四舍五入到最接近的整数

DIV_U64_ROUND_CLOSEST

DIV_U64_ROUND_CLOSEST (dividend, divisor)

无符号 64 位除法,除数为 32 位,四舍五入到最接近的整数

参数

被除数

无符号 64 位被除数

除数

无符号 32 位除数

描述

无符号 64 位被除数除以无符号 32 位除数,并四舍五入到最接近的整数。

返回

被除数 / 除数四舍五入到最接近的整数

DIV_S64_ROUND_CLOSEST

DIV_S64_ROUND_CLOSEST (dividend, divisor)

有符号 64 位除法,除数为 32 位,四舍五入到最接近的整数

参数

被除数

有符号 64 位被除数

除数

有符号 32 位除数

描述

有符号 64 位被除数除以有符号 32 位除数,并四舍五入到最接近的整数。

返回

被除数 / 除数四舍五入到最接近的整数

u64 roundup_u64(u64 x, u32 y)

将 64 位值向上舍入到下一个指定的 32 位倍数

参数

u64 x

要向上舍入的值

u32 y

要向上舍入到的 32 位倍数

描述

x 舍入到 y 的下一个倍数。对于 32 位 x 值,请参阅 roundup 以及针对 2 的幂的更快的 round_up()。

返回

向上舍入的值。

unsigned long gcd(unsigned long a, unsigned long b)

计算并返回 2 个无符号长整型的最大公约数

参数

unsigned long a

第一个值

unsigned long b

第二个值

UUID/GUID

void generate_random_uuid(unsigned char uuid[16])

生成一个随机 UUID

参数

unsigned char uuid[16]

将生成的 UUID 放在哪里

描述

随机 UUID 接口

用于创建启动 ID 或文件系统 UUID/GUID,但对于其他内核驱动程序也可能很有用。

bool uuid_is_valid(const char *uuid)

检查 UUID 字符串是否有效

参数

const char *uuid

要检查的 UUID 字符串

描述

它检查 UUID 字符串是否遵循以下格式

xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

其中 x 是一个十六进制数字。

返回

如果输入是有效的 UUID 字符串,则为 true。

内核 IPC 设施

IPC 实用程序

int ipc_init(void)

初始化 ipc 子系统

参数

void

无参数

描述

初始化各种 sysv ipc 资源(信号量、消息和共享内存)。

一个回调例程被注册到内存热插拔通知链中:由于 msgmni 缩放到低内存,此回调例程将在成功添加/删除内存时被调用,以重新计算 msmgni。

void ipc_init_ids(struct ipc_ids *ids)

初始化 ipc 标识符

参数

struct ipc_ids *ids

ipc 标识符集

描述

设置用于 ipc 标识符范围的序列范围(限制在 ipc_mni 以下),然后初始化密钥哈希表和 ids idr。

void ipc_init_proc_interface(const char *path, const char *header, int ids, int (*show)(struct seq_file*, void*))

使用 seq_file 接口为 sysipc 类型创建 proc 接口。

参数

const char *path

procfs 中的路径

const char *header

要在文件开头打印的横幅。

int ids

要迭代的 ipc id 表。

int (*show)(struct seq_file *, void *)

显示例程。

struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key)

在 ipc 标识符集合中查找键值

参数

struct ipc_ids *ids

ipc 标识符集

key_t key

要查找的键值

描述

如果找到,则返回指向 ipc 结构的已锁定指针,否则返回 NULL。如果找到键值,ipc 指向拥有的 ipc 结构。

调用时需要持有 writer ipc_ids.rwsem。

int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int limit)

添加一个 ipc 标识符

参数

struct ipc_ids *ids

ipc 标识符集

struct kern_ipc_perm *new

新的 ipc 权限集合

int limit

已使用 id 的数量限制

描述

向 ipc idr 添加一个条目 “new”。权限对象会被初始化,并设置第一个空闲条目,同时返回分配的索引。成功时,返回的 “new” 条目处于锁定状态。

失败时,条目不会被锁定,并返回一个负的错误代码。调用者必须使用 ipc_rcu_putref() 来释放该标识符。

调用时需要持有 writer ipc_ids.rwsem。

int ipcget_new(struct ipc_namespace *ns, struct ipc_ids *ids, const struct ipc_ops *ops, struct ipc_params *params)

创建一个新的 ipc 对象

参数

struct ipc_namespace *ns

ipc 命名空间

struct ipc_ids *ids

ipc 标识符集

const struct ipc_ops *ops

要调用的实际创建例程

struct ipc_params *params

其参数

描述

当键值为 IPC_PRIVATE 时,sys_msgget、sys_semget() 和 sys_shmget() 会调用此例程。

int ipc_check_perms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, const struct ipc_ops *ops, struct ipc_params *params)

检查 ipc 对象的安全性和权限

参数

struct ipc_namespace *ns

ipc 命名空间

struct kern_ipc_perm *ipcp

ipc 权限集合

const struct ipc_ops *ops

要调用的实际安全例程

struct ipc_params *params

其参数

描述

当键值不是 IPC_PRIVATE 且该键值已存在于 ds IDR 中时,sys_msgget()、sys_semget() 和 sys_shmget() 会调用此例程。

成功时,返回 ipc id。

调用时需要持有 ipc_ids.rwsem 和 ipcp->lock。

int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids, const struct ipc_ops *ops, struct ipc_params *params)

获取一个 ipc 对象或创建一个新的 ipc 对象

参数

struct ipc_namespace *ns

ipc 命名空间

struct ipc_ids *ids

ipc 标识符集

const struct ipc_ops *ops

要调用的实际创建例程

struct ipc_params *params

其参数

描述

当键值不是 IPC_PRIVATE 时,sys_msgget、sys_semget() 和 sys_shmget() 会调用此例程。如果未找到该键值,则添加一个新的条目,如果找到该键值,则进行一些权限/安全检查。

成功时,返回 ipc id。

void ipc_kht_remove(struct ipc_ids *ids, struct kern_ipc_perm *ipcp)

从键值哈希表删除一个 ipc

参数

struct ipc_ids *ids

ipc 标识符集

struct kern_ipc_perm *ipcp

包含要删除的键值的 ipc 权限结构

描述

在此函数被调用之前,需要持有 ipc_ids.rwsem(作为 writer)以及此 ID 的自旋锁,并且在退出时保持锁定状态。

int ipc_search_maxidx(struct ipc_ids *ids, int limit)

搜索最大的已分配索引

参数

struct ipc_ids *ids

ipc 标识符集

int limit

最大已分配索引的已知上限

描述

此函数确定 ids 中最大的已分配索引。它旨在在 ids->max_idx 需要更新时被调用。当当前最大索引的 ipc 对象被删除时,需要更新 ids->max_idx。如果没有分配 ipc 对象,则返回 -1。

调用者需要持有 ipc_ids.rwsem。

void ipc_rmid(struct ipc_ids *ids, struct kern_ipc_perm *ipcp)

删除一个 ipc 标识符

参数

struct ipc_ids *ids

ipc 标识符集

struct kern_ipc_perm *ipcp

包含要删除的标识符的 ipc 权限结构

描述

在此函数被调用之前,需要持有 ipc_ids.rwsem(作为 writer)以及此 ID 的自旋锁,并且在退出时保持锁定状态。

void ipc_set_key_private(struct ipc_ids *ids, struct kern_ipc_perm *ipcp)

将现有 ipc 的键值切换为 IPC_PRIVATE

参数

struct ipc_ids *ids

ipc 标识符集

struct kern_ipc_perm *ipcp

包含要修改的键值的 ipc 权限结构

描述

在此函数被调用之前,需要持有 ipc_ids.rwsem(作为 writer)以及此 ID 的自旋锁,并且在退出时保持锁定状态。

int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flag)

检查 ipc 权限

参数

struct ipc_namespace *ns

ipc 命名空间

struct kern_ipc_perm *ipcp

ipc 权限集合

short flag

所需的权限集合

描述

检查用户、组和其他访问 ipc 资源的权限。如果允许则返回 0

flag 很可能为 0 或 S_...UGO (来自 <linux/stat.h>)

void kernel_to_ipc64_perm(struct kern_ipc_perm *in, struct ipc64_perm *out)

将内核 ipc 权限转换为用户态权限

参数

struct kern_ipc_perm *in

内核权限

struct ipc64_perm *out

新风格的 ipc 权限

描述

将内核对象 in 转换为一组用于返回用户空间(out)的权限描述。

void ipc64_perm_to_ipc_perm(struct ipc64_perm *in, struct ipc_perm *out)

将新的 ipc 权限转换为旧的 ipc 权限

参数

struct ipc64_perm *in

新风格的 ipc 权限

struct ipc_perm *out

旧风格的 ipc 权限

描述

将新风格的权限对象 in 转换为兼容对象,并将其存储到 out 指针中。

struct kern_ipc_perm *ipc_obtain_object_idr(struct ipc_ids *ids, int id)

参数

struct ipc_ids *ids

ipc 标识符集

int id

要查找的 ipc id

描述

在 ipc ids idr 中查找 id 并返回关联的 ipc 对象。

在 RCU 临界区内调用。退出时 ipc 对象被锁定。

struct kern_ipc_perm *ipc_obtain_object_check(struct ipc_ids *ids, int id)

参数

struct ipc_ids *ids

ipc 标识符集

int id

要查找的 ipc id

描述

类似于 ipc_obtain_object_idr(),但也检查 ipc 对象序列号。

在 RCU 临界区内调用。退出时 ipc 对象被锁定。

int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids, const struct ipc_ops *ops, struct ipc_params *params)

常见的 sys_*get() 代码

参数

struct ipc_namespace *ns

命名空间

struct ipc_ids *ids

ipc 标识符集

const struct ipc_ops *ops

在 ipc 对象创建、权限检查和进一步检查时调用的操作

struct ipc_params *params

上述操作所需的参数。

描述

由 sys_msgget()、sys_semget() 和 sys_shmget() 调用的常用例程。

int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out)

更新 ipc 对象的权限

参数

struct ipc64_perm *in

作为输入给定的权限。

struct kern_ipc_perm *out

要设置的 ipc 的权限。

struct kern_ipc_perm *ipcctl_obtain_check(struct ipc_namespace *ns, struct ipc_ids *ids, int id, int cmd, struct ipc64_perm *perm, int extra_perm)

检索 ipc 对象并检查权限

参数

struct ipc_namespace *ns

ipc 命名空间

struct ipc_ids *ids

要查找 ipc 的 id 表

int id

要检索的 ipc 的 id

int cmd

要检查的 cmd

struct ipc64_perm *perm

要设置的权限

int extra_perm

msq 使用的一个额外权限参数

描述

此函数为某些 IPC_XXX cmd 执行一些常见的审计和权限检查,并从 semctl_down、shmctl_down 和 msgctl_down 调用。

  • 在给定的表中检索具有给定 id 的 ipc 对象。

  • 根据给定的 cmd 执行一些审计和权限检查

  • 返回指向 ipc 对象的指针,否则返回相应的错误。

调用时同时持有 rwsem 和 rcu 读取锁。

int ipc_parse_version(int *cmd)

ipc 调用版本

参数

int *cmd

指向命令的指针

描述

为新风格的 IPC 返回 IPC_64,为旧风格的 IPC 返回 IPC_OLD。cmd 值从编码的命令和版本转换为仅命令代码。

struct kern_ipc_perm *sysvipc_find_ipc(struct ipc_ids *ids, loff_t *pos)

根据 seq pos 查找并锁定 ipc 结构

参数

struct ipc_ids *ids

ipc 标识符集

loff_t *pos

预期位置

描述

此函数根据序列文件位置 pos 查找 ipc 结构。如果位置 pos 没有 ipc 结构,则选择后继者。如果找到结构,则将其锁定(rcu_read_lock() 和 ipc_lock_object()),并将 pos 设置为查找找到的 ipc 结构所需的位置。如果没有找到任何内容(即 EOF),则不修改 pos

该函数返回找到的 ipc 结构,或在 EOF 时返回 NULL。

FIFO 缓冲区

kfifo 接口

DECLARE_KFIFO_PTR

DECLARE_KFIFO_PTR (fifo, type)

用于声明 fifo 指针对象的宏

参数

fifo

声明的 fifo 的名称

type

fifo 元素的类型

DECLARE_KFIFO

DECLARE_KFIFO (fifo, type, size)

用于声明 fifo 对象的宏

参数

fifo

声明的 fifo 的名称

type

fifo 元素的类型

大小

fifo 中的元素数量,必须是 2 的幂

INIT_KFIFO

INIT_KFIFO (fifo)

初始化由 DECLARE_KFIFO 声明的 fifo

参数

fifo

声明的 fifo 数据类型的名称

DEFINE_KFIFO

DEFINE_KFIFO (fifo, type, size)

用于定义和初始化 fifo 的宏

参数

fifo

声明的 fifo 数据类型的名称

type

fifo 元素的类型

大小

fifo 中的元素数量,必须是 2 的幂

注意

该宏可用于全局和局部 fifo 数据类型变量。

kfifo_initialized

kfifo_initialized (fifo)

检查 fifo 是否已初始化

参数

fifo

要检查的 FIFO 的地址

描述

如果 FIFO 已初始化,则返回 true,否则返回 false。假设 FIFO 之前为 0。

kfifo_esize

kfifo_esize (fifo)

返回 FIFO 管理的元素的大小

参数

fifo

要使用的 FIFO 的地址

kfifo_recsize

kfifo_recsize (fifo)

返回记录长度字段的大小

参数

fifo

要使用的 FIFO 的地址

kfifo_size

kfifo_size (fifo)

返回 FIFO 中元素的数量

参数

fifo

要使用的 FIFO 的地址

kfifo_reset

kfifo_reset (fifo)

移除整个 FIFO 内容

参数

fifo

要使用的 FIFO 的地址

注意

使用 kfifo_reset() 是危险的。它应该只在 FIFO 被独占锁定时,或者在确保没有其他线程访问 FIFO 时调用。

kfifo_reset_out

kfifo_reset_out (fifo)

跳过 FIFO 内容

参数

fifo

要使用的 FIFO 的地址

注意

在仅从读取线程调用且只有一个并发读取器的情况下,使用 kfifo_reset_out() 是安全的。否则,它是危险的,必须以与 kfifo_reset() 相同的方式处理。

kfifo_len

kfifo_len (fifo)

返回 FIFO 中已使用元素的数量

参数

fifo

要使用的 FIFO 的地址

kfifo_is_empty

kfifo_is_empty (fifo)

如果 FIFO 为空,则返回 true

参数

fifo

要使用的 FIFO 的地址

kfifo_is_empty_spinlocked

kfifo_is_empty_spinlocked (fifo, lock)

如果 FIFO 为空,则返回 true,使用自旋锁进行锁定

参数

fifo

要使用的 FIFO 的地址

lock

用于锁定的自旋锁

kfifo_is_empty_spinlocked_noirqsave

kfifo_is_empty_spinlocked_noirqsave (fifo, lock)

如果 FIFO 为空,则返回 true,使用自旋锁进行锁定,不禁用中断

参数

fifo

要使用的 FIFO 的地址

lock

用于锁定的自旋锁

kfifo_is_full

kfifo_is_full (fifo)

如果 FIFO 已满,则返回 true

参数

fifo

要使用的 FIFO 的地址

kfifo_avail

kfifo_avail (fifo)

返回 FIFO 中未使用元素的数量

参数

fifo

要使用的 FIFO 的地址

kfifo_skip_count

kfifo_skip_count (fifo, count)

跳过输出数据

参数

fifo

要使用的 FIFO 的地址

计数

要跳过的数据量

kfifo_skip

kfifo_skip (fifo)

跳过输出数据

参数

fifo

要使用的 FIFO 的地址

kfifo_peek_len

kfifo_peek_len (fifo)

获取下一个 FIFO 记录的大小

参数

fifo

要使用的 FIFO 的地址

描述

此函数返回下一个 FIFO 记录的大小(以字节为单位)。

kfifo_alloc

kfifo_alloc (fifo, size, gfp_mask)

动态分配新的 FIFO 缓冲区

参数

fifo

指向 FIFO 的指针

大小

fifo 中的元素数量,必须是 2 的幂

gfp_mask

get_free_pages 掩码,传递给 kmalloc()

描述

此宏动态分配新的 FIFO 缓冲区。

元素的数量将向上取整为 2 的幂。FIFO 将使用 kfifo_free() 释放。如果没有错误,则返回 0,否则返回错误代码。

kfifo_free

kfifo_free (fifo)

释放 FIFO

参数

fifo

要释放的 FIFO

kfifo_init

kfifo_init (fifo, buffer, size)

使用预分配的缓冲区初始化 FIFO

参数

fifo

要分配缓冲区的 FIFO

buffer

要使用的预分配缓冲区

大小

内部缓冲区的大小,必须是 2 的幂

描述

此宏使用预分配的缓冲区初始化 FIFO。

元素的数量将向上取整为 2 的幂。如果没有错误,则返回 0,否则返回错误代码。

kfifo_put

kfifo_put (fifo, val)

将数据放入 FIFO

参数

fifo

要使用的 FIFO 的地址

val

要添加的数据

描述

此宏将给定的值复制到 FIFO 中。如果 FIFO 已满,则返回 0。否则,返回已处理元素的数量。

请注意,只有一个并发读取器和一个并发写入器时,您不需要额外的锁定来使用这些宏。

kfifo_get

kfifo_get (fifo, val)

从 FIFO 获取数据

参数

fifo

要使用的 FIFO 的地址

val

存储数据的地址

描述

此宏从 FIFO 读取数据。如果 FIFO 为空,则返回 0。否则,返回已处理元素的数量。

请注意,只有一个并发读取器和一个并发写入器时,您不需要额外的锁定来使用这些宏。

kfifo_peek

kfifo_peek (fifo, val)

从 FIFO 获取数据,但不移除

参数

fifo

要使用的 FIFO 的地址

val

存储数据的地址

描述

这将从 FIFO 读取数据,而不会将其从 FIFO 中移除。如果 FIFO 为空,则返回 0。否则,返回已处理元素的数量。

请注意,只有一个并发读取器和一个并发写入器时,您不需要额外的锁定来使用这些宏。

kfifo_in

kfifo_in (fifo, buf, n)

将数据放入 FIFO

参数

fifo

要使用的 FIFO 的地址

buf

要添加的数据

n

要添加的元素数量

描述

此宏将给定的缓冲区复制到 FIFO 中,并返回复制的元素数量。

请注意,只有一个并发读取器和一个并发写入器时,您不需要额外的锁定来使用这些宏。

kfifo_in_spinlocked

kfifo_in_spinlocked (fifo, buf, n, lock)

使用自旋锁将数据放入 FIFO 进行锁定

参数

fifo

要使用的 FIFO 的地址

buf

要添加的数据

n

要添加的元素数量

lock

指向用于锁定的自旋锁的指针

描述

此宏将给定的缓冲区中的值复制到 FIFO 中,并返回复制的元素数量。

kfifo_in_spinlocked_noirqsave

kfifo_in_spinlocked_noirqsave (fifo, buf, n, lock)

使用自旋锁将数据放入 FIFO 进行锁定,不禁用中断

参数

fifo

要使用的 FIFO 的地址

buf

要添加的数据

n

要添加的元素数量

lock

指向用于锁定的自旋锁的指针

描述

这是 kfifo_in_spinlocked() 的变体,但使用 spin_lock/unlock() 进行锁定,并且不禁用中断。

kfifo_out

kfifo_out (fifo, buf, n)

从 FIFO 获取数据

参数

fifo

要使用的 FIFO 的地址

buf

指向存储缓冲区的指针

n

要获取的最大元素数量

描述

此宏从 FIFO 获取一些数据,并返回复制的元素数量。

请注意,只有一个并发读取器和一个并发写入器时,您不需要额外的锁定来使用这些宏。

kfifo_out_spinlocked

kfifo_out_spinlocked (fifo, buf, n, lock)

使用自旋锁从 FIFO 获取数据进行锁定

参数

fifo

要使用的 FIFO 的地址

buf

指向存储缓冲区的指针

n

要获取的最大元素数量

lock

指向用于锁定的自旋锁的指针

描述

此宏从 FIFO 获取数据,并返回复制的元素数量。

kfifo_out_spinlocked_noirqsave

kfifo_out_spinlocked_noirqsave (fifo, buf, n, lock)

使用自旋锁从 fifo 中获取数据,不禁用中断

参数

fifo

要使用的 FIFO 的地址

buf

指向存储缓冲区的指针

n

要获取的最大元素数量

lock

指向用于锁定的自旋锁的指针

描述

这是 kfifo_out_spinlocked() 的一个变体,它使用 spin_lock/unlock() 进行锁定,并且不禁用中断。

kfifo_from_user

kfifo_from_user (fifo, from, len, copied)

将一些来自用户空间的数据放入 fifo

参数

fifo

要使用的 FIFO 的地址

from

指向要添加的数据的指针

len

要添加的数据的长度

copied

指向输出变量的指针,用于存储复制的字节数

描述

此宏最多从 from 复制 len 个字节到 fifo 中,具体取决于可用空间,并返回 -EFAULT/0。

请注意,只有一个并发读取器和一个并发写入器时,您不需要额外的锁定来使用这些宏。

kfifo_to_user

kfifo_to_user (fifo, to, len, copied)

将数据从 fifo 复制到用户空间

参数

fifo

要使用的 FIFO 的地址

to

数据必须复制到的位置

len

目标缓冲区的大小

copied

指向输出变量的指针,用于存储复制的字节数

描述

此宏最多从 fifo 复制 len 个字节到 to 缓冲区,并返回 -EFAULT/0。

请注意,只有一个并发读取器和一个并发写入器时,您不需要额外的锁定来使用这些宏。

kfifo_dma_in_prepare_mapped

kfifo_dma_in_prepare_mapped (fifo, sgl, nents, len, dma)

设置用于 DMA 输入的散列表

参数

fifo

要使用的 FIFO 的地址

sgl

指向散列表数组的指针

nents

散列表数组中的条目数

len

要传输的元素数量

dma

要填充到 sgl 中的映射的 DMA 地址

描述

此宏填充用于 DMA 输入的散列表。它返回散列表数组中的条目数。

请注意,在只有一个并发读取器和一个并发写入器的情况下,您不需要额外的锁来使用这些宏。

kfifo_dma_in_finish

kfifo_dma_in_finish (fifo, len)

完成 DMA IN 操作

参数

fifo

要使用的 FIFO 的地址

len

要接收的字节数

描述

此宏完成 DMA IN 操作。in 计数器将由 len 参数更新。不会进行错误检查。

请注意,在只有一个并发读取器和一个并发写入器的情况下,您不需要额外的锁来使用这些宏。

kfifo_dma_out_prepare_mapped

kfifo_dma_out_prepare_mapped (fifo, sgl, nents, len, dma)

设置用于 DMA 输出的散列表

参数

fifo

要使用的 FIFO 的地址

sgl

指向散列表数组的指针

nents

散列表数组中的条目数

len

要传输的元素数量

dma

要填充到 sgl 中的映射的 DMA 地址

描述

此宏填充用于 DMA 输出的散列表,最多传输 len 个字节。它返回散列表数组中的条目数。零表示没有可用空间,并且没有填充散列表。

请注意,在只有一个并发读取器和一个并发写入器的情况下,您不需要额外的锁来使用这些宏。

kfifo_dma_out_finish

kfifo_dma_out_finish (fifo, len)

完成 DMA OUT 操作

参数

fifo

要使用的 FIFO 的地址

len

已传输的字节数

描述

此宏完成 DMA OUT 操作。out 计数器将由 len 参数更新。不会进行错误检查。

请注意,在只有一个并发读取器和一个并发写入器的情况下,您不需要额外的锁来使用这些宏。

kfifo_out_peek

kfifo_out_peek (fifo, buf, n)

从 fifo 获取一些数据

参数

fifo

要使用的 FIFO 的地址

buf

指向存储缓冲区的指针

n

要获取的最大元素数量

描述

此宏从 fifo 获取数据,并返回复制的元素数。数据不会从 fifo 中删除。

请注意,只有一个并发读取器和一个并发写入器时,您不需要额外的锁定来使用这些宏。

kfifo_out_linear

kfifo_out_linear (fifo, tail, n)

获取可用数据的尾部/偏移量

参数

fifo

要使用的 FIFO 的地址

tail

指向无符号整数的指针,用于存储尾部的值

n

要指向的最大元素数

描述

此宏获取 fifo 缓冲区中可用数据的偏移量(尾部),并返回可用元素的数量。它返回到数据末尾或缓冲区末尾的可用计数。因此,它可以用于线性数据处理(例如 memcpy() of (fifo->data + tail) 与返回的计数)。

请注意,只有一个并发读取器和一个并发写入器时,您不需要额外的锁定来使用这些宏。

kfifo_out_linear_ptr

kfifo_out_linear_ptr (fifo, ptr, n)

获取指向可用数据的指针

参数

fifo

要使用的 FIFO 的地址

ptr

指向数据的指针,用于存储指向尾部的指针

n

要指向的最大元素数

描述

kfifo_out_linear() 类似,此宏获取指向 fifo 缓冲区中可用数据的指针,并返回可用元素的数量。它返回到可用数据末尾或缓冲区末尾的可用计数。因此,它可以用于线性数据处理(例如 memcpy() of ptr 与返回的计数)。

请注意,只有一个并发读取器和一个并发写入器时,您不需要额外的锁定来使用这些宏。

中继接口支持

中继接口支持旨在为工具和设施提供一种有效的机制,以便将大量数据从内核空间中继到用户空间。

中继接口

int relay_buf_full(struct rchan_buf *buf)

布尔值,通道缓冲区是否已满?

参数

struct rchan_buf *buf

通道缓冲区

如果缓冲区已满,则返回 1,否则返回 0。

void relay_reset(struct rchan *chan)

重置通道

参数

struct rchan *chan

通道

此操作的效果是从所有通道缓冲区中删除所有数据,并将通道重新启动到其初始状态。缓冲区不会被释放,因此任何映射仍然有效。

注意。在进行此调用时,应注意确保该通道实际上没有被任何东西使用。

struct rchan *relay_open(const char *base_filename, struct dentry *parent, size_t subbuf_size, size_t n_subbufs, const struct rchan_callbacks *cb, void *private_data)

创建新的中继通道

参数

const char *base_filename

要创建的文件的基本名称,仅用于缓冲时为 NULL

struct dentry *parent

父目录的 dentry,对于根目录或缓冲区,为 NULL

size_t subbuf_size

子缓冲区的大小

size_t n_subbufs

子缓冲区的数量

const struct rchan_callbacks *cb

客户端回调函数

void *private_data

用户定义的数据

如果成功,则返回通道指针,否则返回 NULL

使用指定的大小和属性为每个 CPU 创建一个通道缓冲区。创建的通道缓冲区文件将命名为 base_filename0...base_filenameN-1。文件权限将为 S_IRUSR

如果要打开一个缓冲区(parent = NULL),并且您以后希望在文件系统中注册该缓冲区,请在 parent dentry 可用后调用 relay_late_setup_files()

int relay_late_setup_files(struct rchan *chan, const char *base_filename, struct dentry *parent)

触发文件创建

参数

struct rchan *chan

要操作的通道

const char *base_filename

要创建的文件的基本名称

struct dentry *parent

父目录的 dentry,根目录为 NULL

成功返回 0,否则返回非零值。

用于为先前由 relay_open() 创建的仅缓冲区的通道设置文件,该通道的父 dentry 为 NULL。

例如,这对于在 VFS 启动之前在内核中执行早期跟踪,然后在 dentry 可用后公开早期结果很有用。

size_t relay_switch_subbuf(struct rchan_buf *buf, size_t length)

切换到新的子缓冲区

参数

struct rchan_buf *buf

通道缓冲区

size_t length

当前事件的大小

返回传入的长度,如果缓冲区已满则返回 0。

执行子缓冲区切换任务,例如调用回调、更新填充计数、唤醒读取器等。

void relay_subbufs_consumed(struct rchan *chan, unsigned int cpu, size_t subbufs_consumed)

更新缓冲区的子缓冲区消耗计数

参数

struct rchan *chan

通道

unsigned int cpu

要更新的通道缓冲区关联的 CPU

size_t subbufs_consumed

要添加到当前缓冲区计数的子缓冲区数量

添加到通道缓冲区的已消耗子缓冲区计数。 subbufs_consumed 应为新消耗的子缓冲区数量,而不是总消耗量。

注意:如果通道模式为“覆盖”,则内核客户端无需调用此函数。

void relay_close(struct rchan *chan)

关闭通道

参数

struct rchan *chan

通道

关闭所有通道缓冲区并释放通道。

void relay_flush(struct rchan *chan)

关闭通道

参数

struct rchan *chan

通道

刷新所有通道缓冲区,即强制缓冲区切换。

int relay_mmap_buf(struct rchan_buf *buf, struct vm_area_struct *vma)
  • 将通道缓冲区映射到进程地址空间

参数

struct rchan_buf *buf

relay 通道缓冲区

struct vm_area_struct *vma

描述要映射的内存的 vm_area_struct

如果正常,返回 0,否则返回负值

调用者应该已经获取了 mmap_lock。

void *relay_alloc_buf(struct rchan_buf *buf, size_t *size)

分配通道缓冲区

参数

struct rchan_buf *buf

缓冲区结构体

size_t *size

缓冲区的总大小

返回指向结果缓冲区的指针,如果不成功,则返回 NULL。 传入的大小将进行页面对齐(如果尚未对齐)。

struct rchan_buf *relay_create_buf(struct rchan *chan)

分配并初始化通道缓冲区

参数

struct rchan *chan

relay 通道

如果成功,则返回通道缓冲区,否则返回 NULL

void relay_destroy_channel(struct kref *kref)

释放通道结构体

参数

struct kref *kref

包含 relay 通道的目标内核引用

应该只从 kref_put() 调用。

void relay_destroy_buf(struct rchan_buf *buf)

销毁 rchan_buf 结构体和关联的缓冲区

参数

struct rchan_buf *buf

缓冲区结构体

void relay_remove_buf(struct kref *kref)

移除通道缓冲区

参数

struct kref *kref

包含 relay 缓冲区的目标内核引用

从文件系统中删除该文件,该操作也会释放 rchan_buf_struct 和通道缓冲区。应该只从 kref_put() 调用。

int relay_buf_empty(struct rchan_buf *buf)

布尔值,通道缓冲区是否为空?

参数

struct rchan_buf *buf

通道缓冲区

如果缓冲区为空,则返回 1,否则返回 0。

void wakeup_readers(struct irq_work *work)

唤醒等待通道的读取器

参数

struct irq_work *work

包含通道缓冲区

这是用于延迟唤醒读取器的函数

void __relay_reset(struct rchan_buf *buf, unsigned int init)

重置通道缓冲区

参数

struct rchan_buf *buf

通道缓冲区

unsigned int init

如果这是首次初始化,则为 1

有关效果的描述,请参见 relay_reset()

void relay_close_buf(struct rchan_buf *buf)

关闭通道缓冲区

参数

struct rchan_buf *buf

通道缓冲区

将缓冲区标记为最终状态,并恢复默认的回调。然后,当最后一个引用放弃时,通道缓冲区和通道缓冲区数据结构会自动释放。

int relay_file_open(struct inode *inode, struct file *filp)

relay 文件的打开文件操作

参数

struct inode *inode

inode

struct file *filp

文件

增加通道缓冲区引用计数。

int relay_file_mmap(struct file *filp, struct vm_area_struct *vma)

relay 文件的 mmap 文件操作

参数

struct file *filp

文件

struct vm_area_struct *vma

描述要映射内容的 vma

调用 relay_mmap_buf() 将文件映射到用户空间。

__poll_t relay_file_poll(struct file *filp, poll_table *wait)

用于中继文件的 poll 文件操作

参数

struct file *filp

文件

poll_table *wait

poll 表

Poll 实现。

int relay_file_release(struct inode *inode, struct file *filp)

用于中继文件的 release 文件操作

参数

struct inode *inode

inode

struct file *filp

文件

递减通道引用计数,因为文件系统不再使用它。

size_t relay_file_read_subbuf_avail(size_t read_pos, struct rchan_buf *buf)

返回子缓冲区中可用的字节数

参数

size_t read_pos

文件读取位置

struct rchan_buf *buf

relay 通道缓冲区

size_t relay_file_read_start_pos(struct rchan_buf *buf)

查找第一个可读取的字节

参数

struct rchan_buf *buf

relay 通道缓冲区

如果 read_pos 位于填充的中间,则返回第一个实际可用字节的位置,否则返回原始值。

size_t relay_file_read_end_pos(struct rchan_buf *buf, size_t read_pos, size_t count)

返回新的读取位置

参数

struct rchan_buf *buf

relay 通道缓冲区

size_t read_pos

文件读取位置

size_t count

要读取的字节数

模块支持

内核模块自动加载

int __request_module(bool wait, const char *fmt, ...)

尝试加载内核模块

参数

bool wait

等待(或不等待)操作完成

const char *fmt

用于模块名称的 printf 样式格式字符串

...

格式字符串中指定的参数

描述

使用用户模式模块加载器加载模块。该函数在成功时返回零,或在失败时返回负 errno 代码或来自“modprobe”的正退出代码。请注意,成功加载模块并不意味着该模块之后没有卸载并因自身错误而退出。调用者必须检查他们请求的服务现在是否可用,而不是盲目地调用它。

如果禁用模块自动加载支持,则此函数仅返回 -ENOENT。

模块调试

启用 CONFIG_MODULE_STATS 会启用模块调试统计信息,这些信息有助于监控和根本原因分析模块加载的内存压力问题。这些统计信息有助于我们改进生产工作负载。

当前支持的模块调试统计信息有助于跟踪模块加载失败,以便改进内核模块自动加载的使用 (request_module()) 或与用户空间的交互。提供统计信息来跟踪 finit_module() 路径中的所有可能失败,以及在此进程空间中浪费的内存。在最坏的情况下,加载模块会在 3 步内存分配过程后失败

  1. 使用 kernel_read_file_from_fd() 分配的内存

  2. 模块解压缩处理从 kernel_read_file_from_fd() 读取的文件,并且使用 vmap() 将解压缩的模块映射到新的本地缓冲区,该缓冲区表示从用户空间传递的解压缩模块的副本。来自 kernel_read_file_from_fd() 的缓冲区会立即释放。

  3. layout_and_allocate() 为最终存放位置分配空间,如果模块成功处理,我们将在此处保存该模块。

如果在三个不同的分配之后发生故障,则只会增加一个计数器,并汇总此故障期间释放的已分配字节数。同样,如果模块加载仅在步骤 b) 之后失败,则会使用单独的计数器,并针对这两个分配期间释放且未使用的字节数进行递增。

虚拟内存空间可能会受到限制,例如,在 x86 上,虚拟内存大小默认为 128 MiB。我们应尽可能努力限制和避免浪费虚拟内存分配。这些模块调试统计信息有助于评估在启动期间由于模块加载失败而浪费了多少内存。

所有计数器都设计为增量的。使用原子计数器,以便保持简单并避免延迟和死锁。

dup_failed_modules - 跟踪重复失败的模块

由于已在处理或已加载具有相同名称的现有模块而未能加载的模块的链表。finit_module() 系统调用会导致大量的虚拟内存分配。在最坏的情况下,finit_module() 系统调用最终可能会分配 3 次虚拟内存

  1. kernel_read_file_from_fd() 调用使用 vmalloc()

  2. 可选的模块解压缩使用 vmap()

  3. layout_and allocate() 可以使用 vzalloc() 或 vmalloc 的特定于 arch 的变体来处理需要特殊权限的 ELF 段

实际上,在典型的启动中,今天大多数 finit_module() 调用都因已加载或即将处理的同名模块而失败。分配给这些失败模块的所有虚拟内存都将被释放,而没有功能用途。

为了解决这个问题,dup_failed_modules 允许我们跟踪由于模块已加载或正在处理而无法加载的模块。我们只有两个点可能会失败此类调用,我们在下面列出它们以及虚拟内存分配调用的数量

  1. FAIL_DUP_MOD_BECOMING:在 layout_and_allocate() 之前的 early_mod_check() 末尾。 - 使用模块解压缩:2 个虚拟内存分配调用 - 不使用模块解压缩:1 个虚拟内存分配调用

  2. FAIL_DUP_MOD_LOAD:在 add_unformed_module() 上的 layout_and_allocate() 之后 - 使用模块解压缩:3 个虚拟内存分配调用 - 不使用模块解压缩:2 个虚拟内存分配调用

我们应努力使此列表尽可能小。如果此列表不为空,则表示内核中或用户空间中可能存在工作或优化。

模块统计 debugfs 计数器

可以通过添加总和中的总数来计算模块加载期间浪费的虚拟内存分配空间总量

  • invalid_kread_bytes + invalid_decompress_bytes + invalid_becoming_bytes + invalid_mod_bytes

可以使用以下 debugfs 计数器来检查模块加载失败

  • total_mod_size:我们在此系统上处理的所有模块曾经使用的总字节数

  • total_text_size:我们在此系统上处理的 .text 和 .init.text ELF 段大小的总字节数

  • invalid_kread_bytes:由于初始 kernel_read_file_from_fd() 发生故障而分配然后释放的字节数。kernel_read_file_from_fd() 使用 vmalloc()。除非您的系统内存不足,否则通常不应发生这种情况。

  • invalid_decompress_bytes:由于模块解压缩路径中使用 vmap() 的内存分配而分配和释放的字节数。除非您的系统内存不足,否则通常不应发生这种情况。

  • invalid_becoming_bytes:用于读取用户空间希望我们读取的内核模块的总字节数,然后再将其提升为要添加到我们的 modules 链表进行处理。如果我们在成功的 kernel_read_file_from_fd() 调用和我们为模块分配私有内存(如果模块成功加载,将保留该内存)之前进行检查,则可能会发生这些故障。此失败最常见的原因是当用户空间竞相加载尚未看到加载的模块时。第一个在 add_unformed_module() 中成功的模块会将模块添加到我们的 modules 列表中,并且随后加载具有相同名称的模块将在 early_mod_check() 末尾出错。在 early_mod_check() 末尾对 module_patient_check_exists() 的检查可防止对已在处理的模块在 layout_and_allocate() 上进行重复分配。这些重复的失败模块是无致命的,但它们通常表明用户空间尚未在用户空间中看到已加载的模块,并且不必要地试图在内核甚至有机会开始处理先前的请求之前加载模块。尽管重复失败可能是无致命的,但我们应主动尝试减少 vmalloc() 压力,因此理想情况下,在启动后,此值将尽可能接近 0。如果使用了模块解压缩,我们还会将压缩模块的初始 kernel_read_file_from_fd() 的成本添加到此计数器中。如果未使用模块解压缩,则该值表示在 kernel_read_file_from_fd() 调用中针对此类失败分配和释放的总字节数。发生这些故障的原因可能是

  • module_sig_check() - 模块签名检查

  • elf_validity_cache_copy() - 某些 ELF 验证问题

  • early_mod_check()

    • 黑名单

    • 未能重写节头

    • 版本魔术

    • 实时补丁需求未通过检查

    • 检测到该模块已存在

  • invalid_mod_bytes:这些是由于在对用户空间传递给我们的模块进行所有健全性检查以及首次检查模块是否唯一后,因失败而分配和释放的总字节数。如果在用 layout_and_allocate() 为模块分配空间后检测到模块已加载,模块仍然可能加载失败,我们在将模块作为实时模块处理并运行其初始化例程之前会执行此检查。请注意,如果出现此类型的失败,则还意味着相应的 kernel_read_file_from_fd() 内存空间也被释放且未使用,因此我们将此计数器增加模块大小的两倍。此外,如果您使用了模块解压缩,则压缩模块的大小也会添加到此计数器中。

  • modcount:我们在内核生命周期中加载的模块数量

  • failed_kreads:由于 kernel_read_file_from_fd() 失败而失败的模块数量

  • failed_decompress:我们尝试解压缩模块失败的次数。除非您的压缩/解压缩可能存在问题,否则这种情况不应该发生。

  • failed_becoming:在 kernel_read_file_from_fd() 之后,以及在使用 layout_and_allocate() 为其分配内存之前失败的模块数量。如果您设法验证了模块并为其调用了 layout_and_allocate(),则此计数器永远不会增加。

  • failed_load_modules:在使用 layout_and_allocate() 为模块分配私有空间后失败的模块数量。希望这些失败中的大部分已经得到处理。理论上仍然可能存在竞争,但这只意味着内核已开始并发处理两个线程直到 early_mod_check(),并且一个线程获胜。这些失败很好地表明内核或用户空间正在做一些非常愚蠢的事情或可以改进的事情。我们应该努力修复这些问题,但也许不容易修复它们。最近的一个例子是频率模块产生的模块请求,为系统上的每个 CPU 发出了单独的模块请求。

模块间支持

有关更多信息,请参阅 kernel/module/ 中的文件。

硬件接口

DMA 通道

int request_dma(unsigned int dmanr, const char *device_id)

请求并保留系统 DMA 通道

参数

unsigned int dmanr

DMA 通道号

const char * device_id

保留设备 ID 字符串,用于 /proc/dma 中

void free_dma(unsigned int dmanr)

释放保留的系统 DMA 通道

参数

unsigned int dmanr

DMA 通道号

资源管理

struct resource *request_resource_conflict(struct resource *root, struct resource *new)

请求并保留 I/O 或内存资源

参数

struct resource *root

根资源描述符

struct resource *new

调用者所需的资源描述符

描述

成功返回 0,错误时返回冲突资源。

int find_next_iomem_res(resource_size_t start, resource_size_t end, unsigned long flags, unsigned long desc, struct resource *res)

查找覆盖 [start..**end**] 部分的最低 iomem 资源。

参数

resource_size_t start

要搜索的资源的起始地址

resource_size_t end

同一资源的结束地址

unsigned long flags

资源必须具有的标志

unsigned long desc

资源必须具有的描述符

struct resource *res

如果找到资源,则返回指针

描述

如果找到资源,则返回 0,并且 ***res 被覆盖为资源在 [**start**..**end**] 中的部分;如果未找到,则返回 -ENODEV。对于无效参数,返回 -EINVAL。

调用者必须指定 startendflagsdesc(可以是 IORES_DESC_NONE)。

int reallocate_resource(struct resource *root, struct resource *old, resource_size_t newsize, struct resource_constraint *constraint)

在给定范围和对齐方式的资源树中分配一个槽位。如果无法在当前位置重新分配新大小,则将重新定位资源。

参数

struct resource *root

根资源描述符

struct resource *old

调用者所需的资源描述符

resource_size_t newsize

资源描述符的新大小

struct resource_constraint *constraint

要满足的内存范围和对齐约束。

struct resource *lookup_resource(struct resource *root, resource_size_t start)

通过资源起始地址查找现有资源

参数

struct resource *root

根资源描述符

resource_size_t start

资源起始地址

描述

如果找到资源,则返回指向该资源的指针,否则返回 NULL

struct resource *insert_resource_conflict(struct resource *parent, struct resource *new)

将资源插入资源树中

参数

struct resource *parent

新资源的父资源

struct resource *new

要插入的新资源

描述

成功返回 0,如果无法插入资源,则返回冲突资源。

此函数等效于未发生冲突时的 request_resource_conflict。如果发生冲突,并且冲突资源完全适合新资源的范围,则会插入新资源,并且冲突资源将成为新资源的子资源。

此函数适用于资源生产者,例如固件模块和总线驱动程序。

resource_size_t resource_alignment(struct resource *res)

计算资源的对齐方式

参数

struct resource *res

资源指针

描述

成功时返回对齐方式,失败时返回 0(无效对齐)。

void release_mem_region_adjustable(resource_size_t start, resource_size_t size)

释放先前保留的内存区域

参数

resource_size_t start

资源起始地址

resource_size_t size

资源区域大小

描述

此接口用于内存热删除。请求的区域将从当前繁忙的内存资源中释放。请求的区域必须与单个繁忙资源条目完全匹配或适合其中。在后一种情况下,剩余资源将进行相应调整。繁忙内存资源的现有子资源在请求中必须是不可变的。

注意

  • 其他释放条件,例如重叠区域,可以在确认其为有效情况后支持。

  • 当一个繁忙的内存资源被拆分为两个条目时,为了简单起见,代码假定所有子资源都保留在较低地址的条目中。必要时增强此逻辑。

void merge_system_ram_resource(struct resource *res)

标记系统 RAM 资源为可合并,并尝试将其与相邻的可合并资源合并

参数

struct resource *res

资源描述符

描述

此接口用于内存热插拔,驱动程序通过 add_memory*() 等方式添加大量连续的系统 RAM 资源,而实际的资源边界并不重要(例如,它可能与 DIMM 相关)。仅考虑标记为可合并、具有相同父级且没有任何子资源的资源。在请求期间,所有可合并资源都必须是不可变的。

注意

  • 调用者必须确保在此调用之后不再使用指向标记为可合并的资源的指针 - 该资源可能会被释放,并且指针可能变为无效!

  • release_mem_region_adjustable() 将在内存热拔出时按需拆分

int request_resource(struct resource *root, struct resource *new)

请求并保留 I/O 或内存资源

参数

struct resource *root

根资源描述符

struct resource *new

调用者所需的资源描述符

描述

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

int release_resource(struct resource *old)

释放先前保留的资源

参数

struct resource *old

资源指针

int walk_iomem_res_desc(unsigned long desc, unsigned long flags, u64 start, u64 end, void *arg, int (*func)(struct resource*, void*))

遍历 iomem 资源,并使用匹配的资源范围调用 func()。*

参数

unsigned long desc

I/O 资源描述符。使用 IORES_DESC_NONE 跳过 desc 检查。

unsigned long flags

I/O 资源标志

u64 start

起始地址

u64 end

结束地址

void *arg

回调函数 func 的函数参数

int (*func)(struct resource *, void *)

为每个符合条件的资源区域调用的回调函数

描述

所有与 start、end 重叠并且还匹配 flags 和 desc 的内存范围都是有效的候选者。

注意

对于新的描述符搜索,请在 <linux/ioport.h> 中定义新的 IORES_DESC,并在目标资源条目的“desc”中设置它。

int region_intersects(resource_size_t start, size_t size, unsigned long flags, unsigned long desc)

确定区域与已知资源的交集

参数

resource_size_t start

区域起始地址

size_t size

区域大小

unsigned long flags

资源标志(在 iomem_resource 中)

unsigned long desc

资源描述符(在 iomem_resource 中)或 IORES_DESC_NONE

描述

检查指定的区域是否部分重叠或完全覆盖由 flagsdesc 标识的资源(可选使用 IORES_DESC_NONE)。如果该区域不与 flags/desc 重叠,则返回 REGION_DISJOINT;如果该区域与 flags/desc 和另一个资源重叠,则返回 REGION_MIXED;如果该区域与 flags/desc 重叠且没有其他定义的资源,则返回 REGION_INTERSECTS。请注意,在指定区域与 RAM 和未定义的内存空洞重叠的情况下,也会返回 REGION_INTERSECTS。

内存重映射函数使用 region_intersect() 来确保用户没有重新映射 RAM,并且这比逐页遍历资源表快得多。

int find_resource_space(struct resource *root, struct resource *new, resource_size_t size, struct resource_constraint *constraint)

在资源树中查找空闲空间

参数

struct resource *root

根资源描述符

struct resource *new

等待空闲资源空间的资源描述符

resource_size_t size

空闲空间的最小大小

struct resource_constraint *constraint

要满足的范围和对齐约束

描述

在资源树中 root 下查找满足范围和对齐 constraints 的空闲空间。

返回

  • 0 - 如果成功,则 new 成员的 start、end 和 flags 将被更改。

  • -EBUSY - 如果未找到空闲空间。

int allocate_resource(struct resource *root, struct resource *new, resource_size_t size, resource_size_t min, resource_size_t max, resource_size_t align, resource_alignf alignf, void *alignf_data)

在资源树中分配给定范围和对齐的空闲插槽。如果资源已经分配,将使用新的大小重新分配

参数

struct resource *root

根资源描述符

struct resource *new

调用者所需的资源描述符

resource_size_t size

请求的资源区域大小

resource_size_t min

要分配的最小边界

resource_size_t max

要分配的最大边界

resource_size_t align

请求的对齐方式,以字节为单位

resource_alignf alignf

对齐函数,可选,如果不是 NULL 则调用

void *alignf_data

要传递给 alignf 函数的任意数据

int insert_resource(struct resource *parent, struct resource *new)

在资源树中插入资源

参数

struct resource *parent

新资源的父资源

struct resource *new

要插入的新资源

描述

成功返回 0,如果无法插入资源则返回 -EBUSY。

此函数适用于资源生产者,例如固件模块和总线驱动程序。

void insert_resource_expand_to_fit(struct resource *root, struct resource *new)

将资源插入资源树

参数

struct resource *root

根资源描述符

struct resource *new

要插入的新资源

描述

将资源插入资源树,可能会扩展资源树以使其包含任何冲突的资源。

int remove_resource(struct resource *old)

从资源树中移除一个资源

参数

struct resource *old

要移除的资源

描述

成功返回 0,如果资源无效则返回 -EINVAL。

此函数移除先前由 insert_resource()insert_resource_conflict() 插入的资源,并将子资源(如果有)向上移动到它们之前的位置。insert_resource()insert_resource_conflict() 插入一个新资源,并将任何冲突的资源向下移动到新资源的子资源中。

insert_resource()insert_resource_conflict()remove_resource() 适用于资源生产者,例如 FW 模块和总线驱动程序。

int adjust_resource(struct resource *res, resource_size_t start, resource_size_t size)

修改资源的起始位置和大小

参数

struct resource *res

要修改的资源

resource_size_t start

新的起始值

resource_size_t size

新的大小

描述

给定一个现有资源,将其起始位置和大小更改为与参数匹配。成功返回 0,如果无法适应则返回 -EBUSY。资源的现有子资源被认为是不可变的。

struct resource *__request_region(struct resource *parent, resource_size_t start, resource_size_t n, const char *name, int flags)

创建一个新的忙碌资源区域

参数

struct resource *parent

父资源描述符

resource_size_t start

资源起始地址

resource_size_t n

资源区域大小

const char *name

保留调用方的 ID 字符串

int flags

IO 资源标志

void __release_region(struct resource *parent, resource_size_t start, resource_size_t n)

释放先前保留的资源区域

参数

struct resource *parent

父资源描述符

resource_size_t start

资源起始地址

resource_size_t n

资源区域大小

描述

描述的资源区域必须与当前忙碌的区域匹配。

int devm_request_resource(struct device *dev, struct resource *root, struct resource *new)

请求并保留 I/O 或内存资源

参数

struct device *dev

要为其请求资源的设备

struct resource *root

从中请求资源的资源树的根

struct resource *new

要请求的资源的描述符

描述

这是 request_resource() 的设备管理版本。通常不需要显式释放此函数请求的资源,因为当设备与其驱动程序解除绑定时,会自动处理。如果由于排序问题等原因需要显式释放资源,驱动程序必须调用 devm_release_resource() 而不是常规的 release_resource()

当检测到任何现有资源与新请求的资源之间存在冲突时,将打印错误消息。

成功返回 0,失败返回负错误代码。

void devm_release_resource(struct device *dev, struct resource *new)

释放先前请求的资源

参数

struct device *dev

要为其释放资源的设备

struct resource *new

要释放的资源的描述符

描述

释放先前使用 devm_request_resource() 请求的资源。

struct resource *devm_request_free_mem_region(struct device *dev, struct resource *base, unsigned long size)

为设备私有内存查找空闲区域

参数

struct device *dev

用于绑定资源的设备结构

struct resource *base

要查找的资源树

unsigned long size

要添加的设备内存的字节大小

描述

此函数尝试查找一个足够大的物理地址空闲范围来包含新资源,以便稍后可以将其热插拔为 ZONE_DEVICE 内存,从而分配结构页。

struct resource *alloc_free_mem_region(struct resource *base, unsigned long size, unsigned long align, const char *name)

相对于 base 查找空闲区域

参数

struct resource *base

将作为新资源父级的资源

unsigned long size

base 分配的内存的字节大小

unsigned long align

分配的对齐要求

const char *name

资源名称

描述

像 CXL 这样可以动态实例化新内存区域的总线,需要一种方法来为这些区域分配物理地址空间。分配并插入新资源以覆盖 base 范围内未被 base 的后代声明的空闲范围。

MTRR 处理

int arch_phys_wc_add(unsigned long base, unsigned long size)

添加一个 WC MTRR,并在 PAT 不可用时处理错误

参数

unsigned long base

物理基地址

unsigned long size

区域大小

描述

如果 PAT 可用,则此操作不执行任何操作。如果 PAT 不可用,它会尝试添加一个覆盖从 base 开始的 size 字节的 WC MTRR,如果失败则记录错误。

调用方应提供大小为 2 的幂次方,并且在等效的 2 的幂次方边界上对齐。

驱动程序必须存储返回值以传递给 mtrr_del_wc_if_needed,但驱动程序不应尝试解释该返回值。

安全框架

int security_init(void)

初始化安全框架

参数

void

无参数

描述

应该在内核初始化序列的早期调用此函数。

void security_add_hooks(struct security_hook_list *hooks, int count, const struct lsm_id *lsmid)

将模块的钩子添加到钩子列表中。

参数

struct security_hook_list *hooks

要添加的钩子

int count

要添加的钩子数量

const struct lsm_id *lsmid

安全模块的识别信息

描述

每个 LSM 都必须向基础设施注册其钩子。

int lsm_blob_alloc(void **dest, size_t size, gfp_t gfp)

分配一个复合 blob

参数

void **dest

blob 的目标地址

size_t size

blob 的大小

gfp_t gfp

分配类型

描述

为所有模块分配一个 blob

返回 0,如果无法分配内存,则返回 -ENOMEM。

int lsm_cred_alloc(struct cred *cred, gfp_t gfp)

分配一个复合 cred blob

参数

struct cred *cred

需要 blob 的 cred

gfp_t gfp

分配类型

描述

为所有模块分配 cred blob

返回 0,如果无法分配内存,则返回 -ENOMEM。

void lsm_early_cred(struct cred *cred)

在初始化期间分配一个复合 cred blob

参数

struct cred *cred

需要 blob 的 cred

描述

为所有模块分配 cred blob

int lsm_file_alloc(struct file *file)

分配一个复合 file blob

参数

struct file *file

需要 blob 的 file

描述

为所有模块分配 file blob

返回 0,如果无法分配内存,则返回 -ENOMEM。

int lsm_inode_alloc(struct inode *inode, gfp_t gfp)

分配一个复合 inode blob

参数

struct inode *inode

需要 blob 的 inode

gfp_t gfp

分配标志

描述

为所有模块分配 inode blob

返回 0,如果无法分配内存,则返回 -ENOMEM。

int lsm_task_alloc(struct task_struct *task)

分配一个复合 task blob

参数

struct task_struct *task

需要 blob 的 task

描述

为所有模块分配 task blob

返回 0,如果无法分配内存,则返回 -ENOMEM。

int lsm_ipc_alloc(struct kern_ipc_perm *kip)

分配一个复合 ipc blob

参数

struct kern_ipc_perm *kip

需要 blob 的 ipc

描述

为所有模块分配 ipc blob

返回 0,如果无法分配内存,则返回 -ENOMEM。

int lsm_key_alloc(struct key *key)

分配一个复合 key blob

参数

struct key *key

需要 blob 的 key

描述

为所有模块分配 key blob

返回 0,如果无法分配内存,则返回 -ENOMEM。

int lsm_msg_msg_alloc(struct msg_msg *mp)

分配一个复合 msg_msg blob

参数

struct msg_msg *mp

需要 blob 的 msg_msg

描述

为所有模块分配 ipc blob

返回 0,如果无法分配内存,则返回 -ENOMEM。

int lsm_bdev_alloc(struct block_device *bdev)

分配一个复合 block_device blob

参数

struct block_device *bdev

需要 blob 的 block_device

描述

为所有模块分配 block_device blob

返回 0,如果无法分配内存,则返回 -ENOMEM。

void lsm_early_task(struct task_struct *task)

在初始化期间分配一个复合 task blob

参数

struct task_struct *task

需要 blob 的 task

描述

为所有模块分配 task blob

int lsm_superblock_alloc(struct super_block *sb)

分配一个复合 superblock blob

参数

struct super_block *sb

需要 blob 的 superblock

描述

为所有模块分配 superblock blob

返回 0,如果无法分配内存,则返回 -ENOMEM。

int lsm_fill_user_ctx(struct lsm_ctx __user *uctx, u32 *uctx_len, void *val, size_t val_len, u64 id, u64 flags)

填充用户空间 lsm_ctx 结构

参数

struct lsm_ctx __user *uctx

要填充的用户空间 LSM 上下文

u32 *uctx_len

可用的 uctx 大小(输入),已使用的 uctx 大小(输出)

void *val

新的 LSM 上下文值

size_t val_len

新的 LSM 上下文值的大小

u64 id

LSM ID

u64 flags

LSM 定义的标志

描述

填充用户空间 lsm_ctx 结构中的所有字段。如果 **uctx** 为 NULL,则只需计算所需的尺寸并通过 **utc_len** 输出,并返回成功。

成功时返回 0,如果用户空间缓冲区不够大则返回 -E2BIG,如果发生 copyout 错误则返回 -EFAULT,如果无法分配内存则返回 -ENOMEM。

int security_binder_set_context_mgr(const struct cred *mgr)

检查是否可以成为 binder 上下文管理器

参数

const struct cred *mgr

当前 binder 进程的任务凭据

描述

检查是否允许 mgr 成为 binder 上下文管理器。

返回

如果授权,则返回 0。

int security_binder_transaction(const struct cred *from, const struct cred *to)

检查是否允许 binder 事务

参数

const struct cred *from

发送进程

const struct cred *to

接收进程

描述

检查是否允许 from 调用 binder 事务到 to

返回

如果授权,则返回 0。

int security_binder_transfer_binder(const struct cred *from, const struct cred *to)

检查是否允许 binder 传输

参数

const struct cred *from

发送进程

const struct cred *to

接收进程

描述

检查是否允许 from 将 binder 引用传输给 to

返回

如果授权,则返回 0。

int security_binder_transfer_file(const struct cred *from, const struct cred *to, const struct file *file)

检查是否允许 binder 文件传输

参数

const struct cred *from

发送进程

const struct cred *to

接收进程

const struct file *file

正在传输的文件

描述

检查是否允许 fromfile 传输给 to

返回

如果授权,则返回 0。

int security_ptrace_access_check(struct task_struct *child, unsigned int mode)

检查是否允许跟踪

参数

struct task_struct *child

目标进程

unsigned int mode

PTRACE_MODE 标志

描述

在允许当前进程跟踪 child 进程之前,检查权限。如果进程正在被跟踪,并且其安全属性将通过 execve 更改,则安全模块可能还希望在 execve 期间的 set_security 或 apply_creds 钩子中执行进程跟踪检查,或者在 binprm_security_ops 的 bprm_set_creds 钩子中执行进程跟踪检查。

返回

如果授权,则返回 0。

int security_ptrace_traceme(struct task_struct *parent)

检查是否允许跟踪

参数

struct task_struct *parent

跟踪进程

描述

在允许当前进程将自己呈现给 parent 进程进行跟踪之前,检查 parent 进程是否具有足够的权限来跟踪当前进程。

返回

如果授权,则返回 0。

int security_capget(const struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted)

获取进程的 capability 集合

参数

const struct task_struct *target

目标进程

kernel_cap_t *effective

有效 capability 集合

kernel_cap_t *inheritable

可继承 capability 集合

kernel_cap_t *permitted

允许的 capability 集合

描述

获取 target 进程的 effectiveinheritablepermitted capability 集合。该钩子还可以执行权限检查,以确定是否允许当前进程查看 target 进程的 capability 集合。

返回

如果成功获取 capability 集合,则返回 0。

int security_capset(struct cred *new, const struct cred *old, const kernel_cap_t *effective, const kernel_cap_t *inheritable, const kernel_cap_t *permitted)

设置进程的 capability 集合

参数

struct cred *new

目标进程的新凭据

const struct cred *old

目标进程的当前凭据

const kernel_cap_t *effective

有效 capability 集合

const kernel_cap_t *inheritable

可继承 capability 集合

const kernel_cap_t *permitted

允许的 capability 集合

描述

为当前进程设置 effectiveinheritablepermitted capability 集合。

返回

如果授权,则返回 0 并更新 new

int security_capable(const struct cred *cred, struct user_namespace *ns, int cap, unsigned int opts)

检查进程是否具有必要的 capability

参数

const struct cred *cred

要检查的凭据

struct user_namespace *ns

用户命名空间

int cap

请求的 capability

unsigned int opts

capability 检查选项

描述

检查 tsk 进程在指示的凭据中是否具有 cap capability。cap 包含 capability <include/linux/capability.h>。opts 包含 capability 检查的选项 <include/linux/security.h>。

返回

如果授予 capability,则返回 0。

int security_quotactl(int cmds, int type, int id, const struct super_block *sb)

检查是否允许此文件系统的 quotactl() 系统调用

参数

int cmds

命令

int type

type

int id

id

const struct super_block *sb

文件系统

描述

检查是否允许此 sb 的 quotactl 系统调用。

返回

如果授权,则返回 0。

int security_quota_on(struct dentry *dentry)

检查是否允许对一个目录项(dentry)执行 QUOTAON 操作

参数

struct dentry *dentry

目录项(dentry)

描述

检查是否允许对 dentry 执行 QUOTAON 操作。

返回

如果授权,则返回 0。

int security_syslog(int type)

检查是否允许访问内核消息环

参数

int type

SYSLOG_ACTION_* 类型

描述

在访问内核消息环或更改控制台日志记录之前检查权限。有关 type 值的解释,请参阅 syslog(2) 手册页。

返回

如果授权,则返回 0。

int security_settime64(const struct timespec64 *ts, const struct timezone *tz)

检查是否允许更改系统时间

参数

const struct timespec64 *ts

新的时间

const struct timezone *tz

时区

描述

检查更改系统时间的权限,struct timespec64 定义在 <include/linux/time64.h> 中,timezone 定义在 <include/linux/time.h> 中。

返回

如果授权,则返回 0。

int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)

检查是否允许分配新的内存映射

参数

struct mm_struct *mm

mm 结构体

long pages

页数

描述

检查分配新的虚拟映射的权限。如果所有 LSM 返回正值,则将调用 __vm_enough_memory() 并设置 cap_sys_admin。如果至少一个 LSM 返回 0 或负值,则将调用 __vm_enough_memory() 并清除 cap_sys_admin。

返回

如果 LSM 基础设施授予调用者权限,则返回 0。

调用者。

int security_bprm_creds_for_exec(struct linux_binprm *bprm)

准备 exec() 的凭据

参数

struct linux_binprm *bprm

二进制程序信息

描述

如果 prepare_exec_creds 中的设置没有为执行 bprm->file 正确设置 bprm->cred->security,请更新 bprm->cred->security 的 LSM 部分,使其成为 commit_creds 需要为新程序安装的内容。此钩子还可以选择检查权限(例如,在安全域之间转换)。如果应设置 AT_SECURE 以请求 libc 启用安全模式,则该钩子必须将 bprm->secureexec 设置为 1。 bprm 包含 linux_binprm 结构。

返回

如果钩子成功且已授予权限,则返回 0。

int security_bprm_creds_from_file(struct linux_binprm *bprm, const struct file *file)

根据文件更新 linux_binprm 凭据

参数

struct linux_binprm *bprm

二进制程序信息

const struct file *file

关联的文件

描述

如果 file 设置了 setpcap、suid、sgid 或其他标记为在 exec 时更改特权的文件,请更新 bprm->cred 以反映该更改。这在找到将要执行的二进制文件而没有解释器后调用。这确保了凭据不会来自二进制文件将需要重新打开的脚本,而重新打开的脚本最终可能是一个完全不同的文件。此钩子还可以选择检查权限(例如,在安全域之间转换)。如果应设置 AT_SECURE 以请求 libc 启用安全模式,则该钩子必须将 bprm->secureexec 设置为 1。该钩子必须将当前 -> personality 中应清除的任何 personality 标志添加到 bprm->per_clearbprm 包含 linux_binprm 结构。

返回

如果钩子成功且已授予权限,则返回 0。

int security_bprm_check(struct linux_binprm *bprm)

调解二进制处理程序搜索

参数

struct linux_binprm *bprm

二进制程序信息

描述

此钩子调解开始搜索二进制处理程序的时间点。它允许针对在前面的 creds_for_exec 调用中设置的 bprm->cred->security 值进行检查。 argv 列表和 envp 列表在 bprm 中可靠地可用。此钩子可以在单个 execve 期间多次调用。 bprm 包含 linux_binprm 结构。

返回

如果钩子成功且已授予权限,则返回 0。

void security_bprm_committing_creds(const struct linux_binprm *bprm)

在 exec() 期间为进程安装凭据

参数

const struct linux_binprm *bprm

二进制程序信息

描述

准备安装正在由 execve 操作转换的进程的新安全属性,基于 current->cred 指向的旧凭据和 bprm_creds_for_exec 钩子在 bprm->cred 中设置的信息。 bprm 指向 linux_binprm 结构。此钩子是执行进程状态更改(例如,关闭在属性更改后不再授予访问权限的打开文件描述符)的好地方。这在 commit_creds() 之前立即调用。

void security_bprm_committed_creds(const struct linux_binprm *bprm)

在 exec() 期间在凭据安装后进行清理

参数

const struct linux_binprm *bprm

二进制程序信息

描述

在安装了正在由 execve 操作转换的进程的新安全属性后进行清理。到目前为止,新凭据已设置为 current->credbprm 指向 linux_binprm 结构。此钩子是执行进程状态更改(例如,清除不可继承的信号状态)的好地方。这在 commit_creds() 之后立即调用。

int security_fs_context_submount(struct fs_context *fc, struct super_block *reference)

初始化 fc->security

参数

struct fs_context *fc

新的文件系统上下文

struct super_block *reference

子挂载/重新挂载的目录项引用

描述

为新的 fs_context 填充 ->security 字段。

返回

成功时返回 0,失败时返回负错误代码。

int security_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc)

复制 fs_context LSM blob

参数

struct fs_context *fc

目标文件系统上下文

struct fs_context *src_fc

源文件系统上下文

描述

分配一个安全结构并将其附加到 sc->security。此指针由调用者初始化为 NULL。 fc 指示新的文件系统上下文。 src_fc 指示原始文件系统上下文。

返回

成功返回 0,失败返回负错误代码。

int security_fs_context_parse_param(struct fs_context *fc, struct fs_parameter *param)

配置文件系统上下文

参数

struct fs_context *fc

文件系统上下文

struct fs_parameter *param

文件系统参数

描述

用户空间提供了一个参数来配置超级块。LSM 可以使用该参数,也可以将其返回给调用者以在其他地方使用。

返回

如果 LSM 使用了该参数,则应返回 0,如果该参数被

返回给调用者,则返回 -ENOPARAM,否则返回负错误代码。

int security_sb_alloc(struct super_block *sb)

分配一个 super_block LSM blob

参数

struct super_block *sb

文件系统超级块

描述

分配一个安全结构并将其附加到 sb->s_security 字段。当分配结构时,s_security 字段初始化为 NULL。 sb 包含要修改的 super_block 结构。

返回

如果操作成功,则返回 0。

void security_sb_delete(struct super_block *sb)

释放 super_block LSM 相关对象

参数

struct super_block *sb

文件系统超级块

描述

释放与超级块绑定的对象(例如,inodes)。sb 包含正在释放的 super_block 结构。

void security_sb_free(struct super_block *sb)

释放 super_block LSM blob

参数

struct super_block *sb

文件系统超级块

描述

解除分配并清除 sb->s_security 字段。 sb 包含要修改的 super_block 结构。

int security_sb_kern_mount(const struct super_block *sb)

检查是否允许内核挂载

参数

const struct super_block *sb

文件系统超级块

描述

如果权限允许,则挂载此 sb

返回

如果授权,则返回 0。

int security_sb_show_options(struct seq_file *m, struct super_block *sb)

输出超级块的挂载选项

参数

struct seq_file *m

输出文件

struct super_block *sb

文件系统超级块

描述

显示(在 m 上打印)此 sb 的挂载选项。

返回

成功返回 0,失败返回负值。

int security_sb_statfs(struct dentry *dentry)

检查是否允许访问文件系统统计信息

参数

struct dentry *dentry

超级块句柄

描述

在获取 mnt 挂载点的文件系统统计信息之前检查权限。 dentry 是文件系统超级块的句柄。

返回

如果授权,则返回 0。

int security_sb_mount(const char *dev_name, const struct path *path, const char *type, unsigned long flags, void *data)

检查挂载文件系统的权限

参数

const char *dev_name

文件系统后备设备

const struct path *path

挂载点

const char *type

文件系统类型

unsigned long flags

挂载标志

void *data

文件系统特定数据

描述

在将由 dev_name 指定的对象挂载到由 nd 命名的挂载点之前,检查权限。对于普通挂载,如果文件系统类型需要设备,则 dev_name 标识设备。对于重新挂载 (flags & MS_REMOUNT),dev_name 是不相关的。对于回环/绑定挂载 (flags & MS_BIND),dev_name 标识要挂载的对象的路径名。

返回

如果授权,则返回 0。

int security_sb_umount(struct vfsmount *mnt, int flags)

检查卸载文件系统的权限

参数

struct vfsmount *mnt

已挂载的文件系统

int flags

卸载标志

描述

在卸载 mnt 文件系统之前检查权限。

返回

如果授权,则返回 0。

int security_sb_pivotroot(const struct path *old_path, const struct path *new_path)

检查旋转根文件系统的权限

参数

const struct path *old_path

当前根文件系统的新位置

const struct path *new_path

新根文件系统的位置

描述

在旋转根文件系统之前检查权限。

返回

如果授权,则返回 0。

int security_move_mount(const struct path *from_path, const struct path *to_path)

检查移动挂载的权限

参数

const struct path *from_path

源挂载点

const struct path *to_path

目标挂载点

描述

在移动挂载之前检查权限。

返回

如果授权,则返回 0。

int security_path_notify(const struct path *path, u64 mask, unsigned int obj_type)

检查是否允许设置监视

参数

const struct path *path

文件路径

u64 mask

事件掩码

unsigned int obj_type

文件路径类型

描述

在对 path 处的对象设置由 mask 定义的事件监视之前,检查权限,该对象的类型由 obj_type 定义。

返回

如果授权,则返回 0。

int security_inode_alloc(struct inode *inode, gfp_t gfp)

分配一个 inode LSM blob

参数

struct inode *inode

inode

gfp_t gfp

分配标志

描述

分配一个安全结构并将其附加到 inode->i_security。当分配 inode 结构时,i_security 字段初始化为 NULL。

返回

如果操作成功,则返回 0。

void security_inode_free(struct inode *inode)

释放 inode 的 LSM blob

参数

struct inode *inode

inode

描述

释放与 inode 关联的任何 LSM 资源,尽管由于 inode 的 RCU 保护,这些资源可能要等到当前 RCU 宽限期结束后才能完全释放。

LSM 需要注意,尽管在调用 security_inode_free() 时存在 inode,但它仍可能在 VFS 路径遍历中被引用,并且在调用 security_inode_free() 的过程中或之后,可能会调用 security_inode_permission()。因此,inode->i_security 字段是通过 call_rcu() 回调释放的,任何需要在 security_inode_permission() 中使用的 LSM 都应该只在 inode_free_security_rcu() LSM hook 回调中释放该状态。

int security_inode_init_security_anon(struct inode *inode, const struct qstr *name, const struct inode *context_inode)

初始化一个匿名 inode

参数

struct inode *inode

inode

const struct qstr *name

匿名 inode 类

const struct inode *context_inode

一个可选的相关 inode

描述

为新的匿名 inode 设置内核安全字段,并返回安全模块是否允许创建 inode。

返回

成功时返回 0,如果安全模块拒绝创建此 inode,则返回 -EACCES,或者其他错误时返回其他 -errno。

void security_path_post_mknod(struct mnt_idmap *idmap, struct dentry *dentry)

在创建常规文件后更新 inode 安全性

参数

struct mnt_idmap *idmap

挂载的 idmap

struct dentry *dentry

新文件

描述

在创建常规文件后更新 inode 安全字段。

int security_path_rmdir(const struct path *dir, struct dentry *dentry)

检查是否允许删除目录

参数

const struct path *dir

父目录

struct dentry *dentry

要删除的目录

描述

检查删除目录的权限。

返回

如果授权,则返回 0。

检查是否允许创建符号链接

参数

const struct path *dir

父目录

struct dentry *dentry

符号链接

const char *old_name

文件路径名

描述

检查创建指向文件的符号链接的权限。

返回

如果授权,则返回 0。

检查是否允许创建硬链接

参数

struct dentry *old_dentry

现有文件

const struct path *new_dir

新的父目录

struct dentry *new_dentry

新的链接

描述

在创建指向文件的新硬链接之前检查权限。

返回

如果授权,则返回 0。

int security_path_truncate(const struct path *path)

检查是否允许截断文件

参数

const struct path *path

文件

描述

在截断路径指示的文件之前检查权限。请注意,也可以根据已打开的文件检查截断权限,使用 security_file_truncate() hook。

返回

如果授权,则返回 0。

int security_path_chmod(const struct path *path, umode_t mode)

检查是否允许更改文件的模式

参数

const struct path *path

文件

umode_t mode

新的模式

描述

检查更改文件 path 的模式的权限。新模式在 mode 中指定,它是一个来自 <include/uapi/linux/stat.h> 的常量位掩码。

返回

如果授权,则返回 0。

int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid)

检查是否允许更改文件的所有者/组

参数

const struct path *path

文件

kuid_t uid

文件所有者

kgid_t gid

文件组

描述

检查更改文件或目录的所有者/组的权限。

返回

如果授权,则返回 0。

int security_path_chroot(const struct path *path)

检查是否允许更改根目录

参数

const struct path *path

目录

描述

检查更改根目录的权限。

返回

如果授权,则返回 0。

void security_inode_post_create_tmpfile(struct mnt_idmap *idmap, struct inode *inode)

更新新临时文件的 inode 安全性

参数

struct mnt_idmap *idmap

挂载的 idmap

struct inode *inode

新临时文件的 inode

描述

在创建临时文件后更新 inode 安全性数据。

检查是否允许创建硬链接

参数

struct dentry *old_dentry

现有文件

struct inode *dir

新的父目录

struct dentry *new_dentry

新的链接

描述

在创建指向文件的新硬链接之前检查权限。

返回

如果授权,则返回 0。

检查是否允许删除硬链接

参数

struct inode *dir

父目录

struct dentry *dentry

文件

描述

检查删除文件硬链接的权限。

返回

如果授权,则返回 0。

检查是否允许创建符号链接

参数

struct inode *dir

父目录

struct dentry *dentry

符号链接

const char *old_name

现有文件名

描述

检查创建指向文件的符号链接的权限。

返回

如果授权,则返回 0。

int security_inode_rmdir(struct inode *dir, struct dentry *dentry)

检查是否允许删除目录

参数

struct inode *dir

父目录

struct dentry *dentry

要删除的目录

描述

检查删除目录的权限。

返回

如果授权,则返回 0。

int security_inode_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)

检查是否允许创建特殊文件

参数

struct inode *dir

父目录

struct dentry *dentry

新文件

umode_t mode

新文件模式

dev_t dev

设备号

描述

检查创建特殊文件(或通过 mknod 系统调用创建的套接字或 fifo 文件)时的权限。请注意,如果 mknod 操作是针对常规文件执行的,则将调用 create 钩子,而不是此钩子。

返回

如果授权,则返回 0。

int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags)

检查是否允许重命名文件

参数

struct inode *old_dir

旧文件的父目录

struct dentry *old_dentry

旧文件

struct inode *new_dir

新文件的父目录

struct dentry *new_dentry

新文件

unsigned int flags

标志

描述

检查重命名文件或目录的权限。

返回

如果授权,则返回 0。

检查是否允许读取符号链接

参数

struct dentry *dentry

链接

描述

检查读取符号链接的权限。

返回

如果授权,则返回 0。

检查是否允许跟踪符号链接

参数

struct dentry *dentry

链接 dentry

struct inode *inode

链接 inode

bool rcu

如果处于 RCU 遍历模式,则为 true

描述

在查找路径名时检查跟踪符号链接的权限。如果 rcu 为 true,则 inode 不稳定。

返回

如果授权,则返回 0。

int security_inode_permission(struct inode *inode, int mask)

检查是否允许访问 inode

参数

struct inode *inode

inode

int mask

访问掩码

描述

在访问 inode 之前检查权限。此钩子由现有的 Linux 权限函数调用,因此安全模块可以使用它来为现有的 Linux 权限检查提供额外的检查。请注意,此钩子是在打开文件时调用的(以及许多其他操作),而 file_security_ops 权限钩子是在执行实际的读/写操作时调用的。

返回

如果授权,则返回 0。

void security_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry, int ia_valid)

在 setattr 操作后更新 inode

参数

struct mnt_idmap *idmap

挂载的 idmap

struct dentry *dentry

文件

int ia_valid

设置的文件属性

描述

在成功设置文件属性后更新 inode 安全字段。

int security_inode_getattr(const struct path *path)

检查是否允许获取文件属性

参数

const struct path *path

文件

描述

在获取文件属性之前检查权限。

返回

如果授权,则返回 0。

int security_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry, const char *name, const void *value, size_t size, int flags)

检查是否允许设置文件 xattrs

参数

struct mnt_idmap *idmap

挂载的 idmap

struct dentry *dentry

文件

const char *name

xattr 名称

const void *value

xattr 值

size_t size

xattr 值的大小

int flags

标志

描述

此钩子在 dentry 上设置扩展属性 (xattrs) 之前执行所需的权限检查。请务必注意,在主要的 LSM 实现调用之前,我们还有一些额外的逻辑来检测是否需要在 LSM 层执行额外的功能检查。

通常,我们在执行各种 LSM 钩子实现之前会强制执行能力检查,但是如果 LSM 想要避免这种能力检查,它可以注册一个 ‘inode_xattr_skipcap’ 钩子,并为它想要避免能力检查的 xattr 返回 1,从而让 LSM 完全负责强制执行特定 xattr 的访问控制。如果所有启用的 LSM 都避免注册 ‘inode_xattr_skipcap’ 钩子,或者返回 0(默认返回值),则仍然会执行能力检查。如果没有注册 ‘inode_xattr_skipcap’ 钩子,则会执行能力检查。

返回

如果授权,则返回 0。

int security_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, const char *acl_name, struct posix_acl *kacl)

检查是否允许设置 POSIX ACL

参数

struct mnt_idmap *idmap

挂载的 idmap

struct dentry *dentry

文件

const char *acl_name

ACL 名称

struct posix_acl *kacl

ACL 结构

描述

在设置 POSIX ACL 之前检查权限,kacl 中的 POSIX ACL 由 acl_name 标识。

返回

如果授权,则返回 0。

void security_inode_post_set_acl(struct dentry *dentry, const char *acl_name, struct posix_acl *kacl)

从设置的 POSIX ACL 更新 inode 安全性

参数

struct dentry *dentry

文件

const char *acl_name

ACL 名称

struct posix_acl *kacl

ACL 结构

描述

在成功在 dentry 上设置 POSIX ACL 后更新 inode 安全数据。kacl 中的 POSIX ACL 由 acl_name 标识。

int security_inode_get_acl(struct mnt_idmap *idmap, struct dentry *dentry, const char *acl_name)

检查是否允许读取 POSIX ACL

参数

struct mnt_idmap *idmap

挂载的 idmap

struct dentry *dentry

文件

const char *acl_name

ACL 名称

描述

在获取 POSIX ACL 之前检查权限,POSIX ACL 由 acl_name 标识。

返回

如果授权,则返回 0。

int security_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry, const char *acl_name)

检查是否允许删除 POSIX ACL

参数

struct mnt_idmap *idmap

挂载的 idmap

struct dentry *dentry

文件

const char *acl_name

ACL 名称

描述

在删除 POSIX ACL 之前检查权限,POSIX ACL 由 acl_name 标识。

返回

如果授权,则返回 0。

void security_inode_post_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry, const char *acl_name)

在删除 POSIX ACL 后更新 inode 安全性

参数

struct mnt_idmap *idmap

挂载的 idmap

struct dentry *dentry

文件

const char *acl_name

ACL 名称

描述

在成功删除 idmapdentry 上的 POSIX ACL 后更新 inode 安全数据。POSIX ACL 由 acl_name 标识。

void security_inode_post_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags)

在 setxattr 操作后更新 inode

参数

struct dentry *dentry

文件

const char *name

xattr 名称

const void *value

xattr 值

size_t size

xattr 值大小

int flags

标志

描述

在成功执行 setxattr 操作后更新 inode 安全字段。

int security_inode_getxattr(struct dentry *dentry, const char *name)

检查是否允许 xattr 访问

参数

struct dentry *dentry

文件

const char *name

xattr 名称

描述

在获取 dentry 的由 name 标识的扩展属性之前检查权限。

返回

如果授权,则返回 0。

int security_inode_listxattr(struct dentry *dentry)

检查是否允许列出 xattr

参数

struct dentry *dentry

文件

描述

在获取 dentry 的扩展属性名称列表之前检查权限。

返回

如果授权,则返回 0。

int security_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry, const char *name)

检查是否允许删除 xattr

参数

struct mnt_idmap *idmap

挂载的 idmap

struct dentry *dentry

文件

const char *name

xattr 名称

描述

此钩子在 dentry 上设置扩展属性 (xattrs) 之前执行所需的权限检查。请务必注意,在主要的 LSM 实现调用之前,我们还有一些额外的逻辑来检测是否需要在 LSM 层执行额外的功能检查。

通常,我们在执行各种 LSM 钩子实现之前会强制执行能力检查,但是如果 LSM 想要避免这种能力检查,它可以注册一个 ‘inode_xattr_skipcap’ 钩子,并为它想要避免能力检查的 xattr 返回 1,从而让 LSM 完全负责强制执行特定 xattr 的访问控制。如果所有启用的 LSM 都避免注册 ‘inode_xattr_skipcap’ 钩子,或者返回 0(默认返回值),则仍然会执行能力检查。如果没有注册 ‘inode_xattr_skipcap’ 钩子,则会执行能力检查。

返回

如果授权,则返回 0。

void security_inode_post_removexattr(struct dentry *dentry, const char *name)

在 removexattr 操作后更新 inode

参数

struct dentry *dentry

文件

const char *name

xattr 名称

描述

在成功执行 removexattr 操作后更新 inode。

int security_inode_need_killpriv(struct dentry *dentry)

检查是否需要 security_inode_killpriv()

参数

struct dentry *dentry

关联的 dentry

描述

当 inode 发生更改时调用,以确定是否应该调用 security_inode_killpriv()

返回

发生错误时返回 <0 以中止 inode 更改操作,如果

不需要调用 security_inode_killpriv(),则返回 0,如果需要调用 security_inode_killpriv(),则返回 >0。

int security_inode_killpriv(struct mnt_idmap *idmap, struct dentry *dentry)

删除 setuid 位,更新 LSM 状态

参数

struct mnt_idmap *idmap

挂载的 idmap

struct dentry *dentry

关联的 dentry

描述

正在删除 dentry 的 setuid 位。删除类似的安全标签。使用已持有的 dentry->d_inode->i_mutex 调用。

返回

成功返回 0。如果返回错误,则导致

删除 setuid 位的操作失败。

int security_inode_getsecurity(struct mnt_idmap *idmap, struct inode *inode, const char *name, void **buffer, bool alloc)

获取 inode 的 xattr 安全标签

参数

struct mnt_idmap *idmap

挂载的 idmap

struct inode *inode

inode

const char *name

xattr 名称

void **buffer

安全标签缓冲区

bool alloc

分配标志

描述

通过 buffer 检索与 inodename 关联的安全标签的扩展属性表示的副本。请注意,name 是删除安全前缀后属性名称的其余部分。 alloc 用于指定调用是通过缓冲区返回值还是仅返回值长度。

返回

成功时返回缓冲区的大小。

int security_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags)

设置 inode 的 xattr 安全标签

参数

struct inode *inode

inode

const char *name

xattr 名称

const void *value

安全标签

size_t size

安全标签的长度

int flags

标志

描述

从扩展属性值 value 设置与 inodename 关联的安全标签。 size 指示 value 的大小(以字节为单位)。flags 可以是 XATTR_CREATE、XATTR_REPLACE 或 0。请注意,name 是删除 security. 前缀后属性名称的其余部分。

返回

成功时返回 0。

void security_inode_getlsmprop(struct inode *inode, struct lsm_prop *prop)

获取 inode 的 LSM 数据

参数

struct inode *inode

inode

struct lsm_prop *prop

要返回的 lsm 特定信息

描述

获取与节点关联的 lsm 特定信息。

int security_kernfs_init_security(struct kernfs_node *kn_dir, struct kernfs_node *kn)

初始化 kernfs 节点的 LSM 上下文

参数

struct kernfs_node *kn_dir

父 kernfs 节点

struct kernfs_node *kn

要初始化的 kernfs 节点

描述

基于新创建的 kernfs 节点及其父节点的属性,初始化该节点的安全上下文。

返回

如果授权,则返回 0。

int security_file_permission(struct file *file, int mask)

检查文件权限

参数

struct file *file

文件

int mask

请求的权限

描述

在访问打开的文件之前检查文件权限。此钩子由各种读取或写入文件的操作调用。安全模块可以使用此钩子对这些操作执行额外的检查,例如,重新验证使用权限以支持特权分隔或策略更改。请注意,此钩子是在执行实际的读/写操作时使用的,而 inode_security_ops 钩子是在打开文件时(以及许多其他操作)调用的。尽管此钩子可用于重新验证读取或写入文件的各种系统调用操作的权限,但它并未解决内存映射文件的权限重新验证问题。如果安全模块需要此类重新验证,则必须单独处理。

返回

如果授权,则返回 0。

int security_file_alloc(struct file *file)

分配并初始化文件的 LSM blob

参数

struct file *file

文件

描述

分配并将安全结构附加到 file->f_security 字段。首次创建该结构时,安全字段将初始化为 NULL。

返回

如果钩子成功并且授予权限,则返回 0。

void security_file_release(struct file *file)

在释放文件引用之前执行操作

参数

struct file *file

文件

描述

在释放对文件的最后一个引用之前执行操作。

void security_file_free(struct file *file)

释放文件的 LSM blob

参数

struct file *file

文件

描述

取消分配并释放存储在 file->f_security 中的任何安全结构。

int security_mmap_file(struct file *file, unsigned long prot, unsigned long flags)

检查是否允许对文件进行 mmap 操作

参数

struct file *file

文件

unsigned long prot

内核应用的保护

unsigned long flags

标志

描述

检查 mmap 操作的权限。 **file** 可能为 NULL,例如映射匿名内存时。

返回

如果授权,则返回 0。

int security_mmap_addr(unsigned long addr)

检查是否允许在指定地址进行 mmap 操作

参数

unsigned long addr

地址

描述

检查在 **addr** 处进行 mmap 操作的权限。

返回

如果授权,则返回 0。

int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, unsigned long prot)

检查是否允许更改内存保护

参数

struct vm_area_struct *vma

内存区域

unsigned long reqprot

应用程序请求的保护

unsigned long prot

内核应用的保护

描述

在更改内存访问权限之前检查权限。

返回

如果授权,则返回 0。

int security_file_lock(struct file *file, unsigned int cmd)

检查是否允许文件锁定

参数

struct file *file

文件

unsigned int cmd

锁定操作(例如 F_RDLCK、F_WRLCK)

描述

在执行文件锁定操作之前检查权限。请注意,此钩子同时管理 flock 和 fcntl 样式的锁。

返回

如果授权,则返回 0。

int security_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg)

检查是否允许 fcntl() 操作

参数

struct file *file

文件

unsigned int cmd

fcntl 命令

unsigned long arg

命令参数

描述

在允许对文件 **file** 执行 **cmd** 指定的文件操作之前检查权限。请注意,**arg** 有时表示用户空间指针;在其他情况下,它可能是一个简单的整数值。当 **arg** 表示用户空间指针时,安全模块永远不应该使用它。

返回

如果授权,则返回 0。

void security_file_set_fowner(struct file *file)

在 LSM blob 中设置文件所有者信息

参数

struct file *file

文件

描述

将所有者安全信息(通常来自 current->security)保存在 file->f_security 中,以供 send_sigiotask 钩子稍后使用。

此钩子在持有 file->f_owner.lock 的情况下调用。

返回

成功时返回 0。

int security_file_send_sigiotask(struct task_struct *tsk, struct fown_struct *fown, int sig)

检查是否允许发送 SIGIO/SIGURG

参数

struct task_struct *tsk

目标任务

struct fown_struct *fown

信号发送者

int sig

要发送的信号,如果为 0,则发送 SIGIO

描述

检查文件所有者 **fown** 向进程 **tsk** 发送 SIGIO 或 SIGURG 的权限。请注意,此钩子有时会从中断中调用。请注意,fown_struct **fown** 永远不会在 struct file 的上下文之外,因此始终可以获得文件结构(和相关的安全信息):container_of(fown, struct file, f_owner)。

返回

如果授权,则返回 0。

int security_file_receive(struct file *file)

检查是否允许通过 IPC 接收文件

参数

struct file *file

正在接收的文件

描述

此钩子允许安全模块控制进程通过套接字 IPC 接收打开的文件描述符的能力。

返回

如果授权,则返回 0。

int security_file_open(struct file *file)

保存 open() 时间状态,以供 LSM 稍后使用

参数

struct file *file

描述

保存打开时权限检查状态,以便稍后在 file_permission 上使用,如果自 inode_permission 以来有任何更改,则重新检查访问权限。

返回

如果授权,则返回 0。

int security_file_truncate(struct file *file)

检查是否允许截断文件

参数

struct file *file

文件

描述

在截断文件之前检查权限,即使用 ftruncate。请注意,还可以使用 **path_truncate** 钩子基于路径检查截断权限。

返回

如果授权,则返回 0。

int security_task_alloc(struct task_struct *task, unsigned long clone_flags)

分配任务的 LSM blob

参数

struct task_struct *task

任务

unsigned long clone_flags

指示正在共享的内容的标志

描述

处理与任务相关的资源的分配。

返回

成功时返回零,失败时返回负值。

void security_task_free(struct task_struct *task)

释放任务的 LSM blob 和相关资源

参数

struct task_struct *task

任务

描述

处理任务相关资源的释放。请注意,可以从中断上下文中调用此函数。

int security_cred_alloc_blank(struct cred *cred, gfp_t gfp)

分配允许 cred_transfer 的最小内存

参数

struct cred *cred

凭证

gfp_t gfp

gfp 标志

描述

仅分配足够的内存并附加到 **cred**,以便 cred_transfer() 不会获得 ENOMEM。

返回

成功返回 0,失败返回负值。

void security_cred_free(struct cred *cred)

释放 cred 的 LSM blob 和相关资源

参数

struct cred *cred

凭证

描述

取消分配并清除一组凭证中的 cred->security 字段。

int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp)

准备一组新的凭证

参数

struct cred *new

新的凭证

const struct cred *old

原始凭证

gfp_t gfp

gfp 标志

描述

通过复制旧集中的数据来准备一组新的凭证。

返回

成功返回 0,失败返回负值。

void security_transfer_creds(struct cred *new, const struct cred *old)

传输凭据

参数

struct cred *new

目标凭据

const struct cred *old

原始凭证

描述

将原始凭据中的数据传输到新的凭据中。

int security_kernel_act_as(struct cred *new, u32 secid)

设置内核凭据以作为 secid 行事

参数

struct cred *new

凭证

u32 secid

secid

描述

设置内核服务用以操作的凭据(主观上下文)。当前任务必须是提名 secid 的任务。

返回

如果成功,则返回 0。

int security_kernel_create_files_as(struct cred *new, struct inode *inode)

使用 inode 设置文件创建上下文

参数

struct cred *new

目标凭据

struct inode *inode

参考 inode

描述

将一组凭据中的文件创建上下文设置为与指定 inode 的客观上下文相同。当前任务必须是提名 inode 的任务。

返回

如果成功,则返回 0。

int security_kernel_module_request(char *kmod_name)

检查是否允许加载模块

参数

char *kmod_name

模块名称

描述

触发内核自动向上调用用户空间以使用给定名称加载内核模块的能力。

返回

如果成功,则返回 0。

int security_task_fix_setuid(struct cred *new, const struct cred *old, int flags)

使用新的用户 ID 属性更新 LSM

参数

struct cred *new

更新的凭据

const struct cred *old

正在被替换的凭据

int flags

LSM_SETID_* 标志值

描述

在设置当前进程的一个或多个用户标识属性后更新模块的状态。 flags 参数指示哪个 set*uid 系统调用调用了这个钩子。如果 new 是将要安装的凭据集。应该对它进行修改,而不是对 current->cred 进行修改。

返回

成功时返回 0。

int security_task_fix_setgid(struct cred *new, const struct cred *old, int flags)

使用新的组 ID 属性更新 LSM

参数

struct cred *new

更新的凭据

const struct cred *old

正在被替换的凭据

int flags

LSM_SETID_* 标志值

描述

在设置当前进程的一个或多个组标识属性后更新模块的状态。 flags 参数指示哪个 set*gid 系统调用调用了这个钩子。 new 是将要安装的凭据集。应该对它进行修改,而不是对 current->cred 进行修改。

返回

成功时返回 0。

int security_task_fix_setgroups(struct cred *new, const struct cred *old)

使用新的补充组更新 LSM

参数

struct cred *new

更新的凭据

const struct cred *old

正在被替换的凭据

描述

在设置当前进程的补充组标识属性后更新模块的状态。 new 是将要安装的凭据集。应该对它进行修改,而不是对 current->cred 进行修改。

返回

成功时返回 0。

int security_task_setpgid(struct task_struct *p, pid_t pgid)

检查是否允许设置 pgid

参数

struct task_struct *p

正在被修改的任务

pid_t pgid

新的 pgid

描述

在将进程 p 的进程组标识符设置为 pgid 之前,检查权限。

返回

如果授权,则返回 0。

int security_task_getpgid(struct task_struct *p)

检查是否允许获取 pgid

参数

struct task_struct *p

任务

描述

在获取进程 p 的进程组标识符之前,检查权限。

返回

如果授权,则返回 0。

int security_task_getsid(struct task_struct *p)

检查是否允许获取会话 ID

参数

struct task_struct *p

任务

描述

在获取进程 p 的会话标识符之前,检查权限。

返回

如果授权,则返回 0。

int security_task_setnice(struct task_struct *p, int nice)

检查是否允许设置任务的 nice 值

参数

struct task_struct *p

目标任务

int nice

nice 值

描述

在将 p 的 nice 值设置为 nice 之前,检查权限。

返回

如果授权,则返回 0。

int security_task_setioprio(struct task_struct *p, int ioprio)

检查是否允许设置任务的 ioprio

参数

struct task_struct *p

目标任务

int ioprio

ioprio 值

描述

在将 p 的 ioprio 值设置为 ioprio 之前,检查权限。

返回

如果授权,则返回 0。

int security_task_getioprio(struct task_struct *p)

检查是否允许获取任务的 ioprio

参数

struct task_struct *p

任务

描述

在获取 p 的 ioprio 值之前,检查权限。

返回

如果授权,则返回 0。

int security_task_prlimit(const struct cred *cred, const struct cred *tcred, unsigned int flags)

检查是否允许获取/设置资源限制

参数

const struct cred *cred

当前任务凭据

const struct cred *tcred

目标任务凭据

unsigned int flags

LSM_PRLIMIT_* 标志位,指示获取/设置/两者

描述

在获取和/或设置另一个任务的资源限制之前,请检查权限。

返回

如果授权,则返回 0。

int security_task_setrlimit(struct task_struct *p, unsigned int resource, struct rlimit *new_rlim)

检查是否允许设置新的 rlimit 值

参数

struct task_struct *p

目标任务的组领导者

unsigned int resource

正在设置限制的资源

struct rlimit *new_rlim

新的资源限制

描述

在为 resource 将进程 p 的资源限制设置为 new_rlim 之前,请检查权限。可以通过解引用 (p->signal->rlim + resource) 来检查旧的资源限制值。

返回

如果授权,则返回 0。

int security_task_setscheduler(struct task_struct *p)

检查是否允许设置调度策略/参数

参数

struct task_struct *p

目标任务

描述

在设置进程 p 的调度策略和/或参数之前,请检查权限。

返回

如果授权,则返回 0。

int security_task_getscheduler(struct task_struct *p)

检查是否允许获取调度信息

参数

struct task_struct *p

目标任务

描述

在获取进程 p 的调度信息之前,请检查权限。

返回

如果授权,则返回 0。

int security_task_movememory(struct task_struct *p)

检查是否允许移动内存

参数

struct task_struct *p

任务

描述

在移动进程 p 拥有的内存之前,请检查权限。

返回

如果授权,则返回 0。

int security_task_kill(struct task_struct *p, struct kernel_siginfo *info, int sig, const struct cred *cred)

检查是否允许发送信号

参数

struct task_struct *p

目标进程

struct kernel_siginfo *info

信号信息

int sig

信号值

const struct cred *cred

信号发送者的凭据,如果为 current 则为 NULL

描述

在向 p 发送信号 sig 之前,请检查权限。 info 可以为 NULL、常量 1 或指向 kernel_siginfo 结构的指针。如果 info 为 1 或 SI_FROMKERNEL(info) 为真,则该信号应被视为来自内核,并且通常应被允许。 SIGIO 信号由 file_security_ops 中的 send_sigiotask 钩子单独处理。

返回

如果授权,则返回 0。

int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5)

检查是否允许 prctl 操作

参数

int option

操作

unsigned long arg2

参数

unsigned long arg3

参数

unsigned long arg4

参数

unsigned long arg5

参数

描述

在对当前进程执行进程控制操作之前,请检查权限。

返回

如果没有人想处理此操作,则返回 -ENOSYS,任何其他值

导致 prctl() 立即返回该值。

void security_task_to_inode(struct task_struct *p, struct inode *inode)

设置任务 inode 的安全属性

参数

struct task_struct *p

任务

struct inode *inode

inode

描述

基于关联的任务安全属性(例如,对于 /proc/pid inodes)设置 inode 的安全属性。

int security_create_user_ns(const struct cred *cred)

检查是否允许创建新的 userns

参数

const struct cred *cred

准备好的凭据

描述

在创建新的用户命名空间之前检查权限。

返回

如果成功,则返回 0,否则返回 < 0 错误代码。

int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag)

检查是否允许 sysv ipc 访问

参数

struct kern_ipc_perm *ipcp

ipc 权限结构

short flag

请求的权限

描述

检查访问 IPC 的权限。

返回

如果授权,则返回 0。

void security_ipc_getlsmprop(struct kern_ipc_perm *ipcp, struct lsm_prop *prop)

获取 sysv ipc 对象 LSM 数据

参数

struct kern_ipc_perm *ipcp

ipc 权限结构

struct lsm_prop *prop

指向 lsm 信息的指针

描述

获取与 ipc 对象关联的 lsm 信息。

int security_msg_msg_alloc(struct msg_msg *msg)

分配一个 sysv ipc 消息 LSM blob

参数

struct msg_msg *msg

消息结构

描述

分配一个安全结构并将其附加到 msg->security 字段。首次创建结构时,security 字段初始化为 NULL。

返回

如果操作成功且权限被授予,则返回 0。

void security_msg_msg_free(struct msg_msg *msg)

释放一个 sysv ipc 消息 LSM blob

参数

struct msg_msg *msg

消息结构

描述

释放此消息的安全结构。

int security_msg_queue_alloc(struct kern_ipc_perm *msq)

分配一个 sysv ipc 消息队列 LSM blob

参数

struct kern_ipc_perm *msq

sysv ipc 权限结构

描述

分配并附加一个安全结构到 msg。当该结构首次创建时,安全字段初始化为 NULL。

返回

如果操作成功且权限被授予,则返回 0。

void security_msg_queue_free(struct kern_ipc_perm *msq)

释放一个 sysv ipc 消息队列 LSM blob

参数

struct kern_ipc_perm *msq

sysv ipc 权限结构

描述

为消息队列释放安全字段 perm->security

int security_msg_queue_associate(struct kern_ipc_perm *msq, int msqflg)

检查是否允许消息队列操作

参数

struct kern_ipc_perm *msq

sysv ipc 权限结构

int msqflg

操作标志

描述

当通过 msgget 系统调用请求消息队列时检查权限。此钩子仅在返回现有消息队列的消息队列标识符时调用,而不是在创建新消息队列时调用。

返回

如果授权,则返回 0。

int security_msg_queue_msgctl(struct kern_ipc_perm *msq, int cmd)

检查是否允许消息队列操作

参数

struct kern_ipc_perm *msq

sysv ipc 权限结构

int cmd

操作

描述

检查在具有权限的消息队列上执行由 cmd 指定的消息控制操作时是否允许。

返回

如果授权,则返回 0。

int security_msg_queue_msgsnd(struct kern_ipc_perm *msq, struct msg_msg *msg, int msqflg)

检查是否允许发送 sysv ipc 消息

参数

struct kern_ipc_perm *msq

sysv ipc 权限结构

struct msg_msg *msg

消息

int msqflg

操作标志

描述

在消息 msg 入队到 msq 中指定的具有权限的消息队列之前,检查权限。

返回

如果授权,则返回 0。

int security_msg_queue_msgrcv(struct kern_ipc_perm *msq, struct msg_msg *msg, struct task_struct *target, long type, int mode)

检查是否允许接收 sysv ipc 消息

参数

struct kern_ipc_perm *msq

sysv ipc 权限结构

struct msg_msg *msg

消息

struct task_struct *target

目标任务

long type

请求的消息类型

int mode

操作标志

描述

在从消息队列中删除消息 msg 之前检查权限。 target 任务结构包含一个指向将接收消息的进程的指针(当执行内联接收时,不等于当前进程)。

返回

如果授权,则返回 0。

int security_shm_alloc(struct kern_ipc_perm *shp)

分配一个 sysv shm LSM blob

参数

struct kern_ipc_perm *shp

sysv ipc 权限结构

描述

分配一个安全结构并附加到 shp 安全字段。当该结构首次创建时,安全字段初始化为 NULL。

返回

如果操作成功且权限被授予,则返回 0。

void security_shm_free(struct kern_ipc_perm *shp)

释放一个 sysv shm LSM blob

参数

struct kern_ipc_perm *shp

sysv ipc 权限结构

描述

为内存段释放安全结构 perm->security

int security_shm_associate(struct kern_ipc_perm *shp, int shmflg)

检查是否允许 sysv shm 操作

参数

struct kern_ipc_perm *shp

sysv ipc 权限结构

int shmflg

操作标志

描述

当通过 shmget 系统调用请求共享内存区域时检查权限。此钩子仅在返回现有区域的共享内存区域标识符时调用,而不是在创建新的共享内存区域时调用。

返回

如果授权,则返回 0。

int security_shm_shmctl(struct kern_ipc_perm *shp, int cmd)

检查是否允许 sysv shm 操作

参数

struct kern_ipc_perm *shp

sysv ipc 权限结构

int cmd

操作

描述

检查在具有 shp 中权限的共享内存区域上执行由 cmd 指定的共享内存控制操作时是否允许。

返回

如果授权,则返回 0。

int security_shm_shmat(struct kern_ipc_perm *shp, char __user *shmaddr, int shmflg)

检查是否允许 sysv shm 附加操作

参数

struct kern_ipc_perm *shp

sysv ipc 权限结构

char __user *shmaddr

要附加的内存区域的地址

int shmflg

操作标志

描述

在允许 shmat 系统调用将具有权限的共享内存段 shp 附加到调用进程的数据段之前检查权限。附加地址由 shmaddr 指定。

返回

如果授权,则返回 0。

int security_sem_alloc(struct kern_ipc_perm *sma)

分配一个 sysv 信号量 LSM blob

参数

struct kern_ipc_perm *sma

sysv ipc 权限结构

描述

分配一个安全结构并附加到 sma 安全字段。当该结构首次创建时,安全字段初始化为 NULL。

返回

如果操作成功且权限被授予,则返回 0。

void security_sem_free(struct kern_ipc_perm *sma)

释放一个 sysv 信号量 LSM blob

参数

struct kern_ipc_perm *sma

sysv ipc 权限结构

描述

为信号量释放安全结构 sma->security

int security_sem_associate(struct kern_ipc_perm *sma, int semflg)

检查是否允许 sysv 信号量操作

参数

struct kern_ipc_perm *sma

sysv ipc 权限结构

int semflg

操作标志

描述

当通过 semget 系统调用请求信号量时检查权限。此钩子仅在返回现有信号量的信号量标识符时调用,而不是在必须创建新信号量时调用。

返回

如果授权,则返回 0。

int security_sem_semctl(struct kern_ipc_perm *sma, int cmd)

检查是否允许 sysv 信号量操作

参数

struct kern_ipc_perm *sma

sysv ipc 权限结构

int cmd

操作

描述

检查在信号量上执行由 cmd 指定的信号量操作时是否允许。

返回

如果授权,则返回 0。

int security_sem_semop(struct kern_ipc_perm *sma, struct sembuf *sops, unsigned nsops, int alter)

检查是否允许 sysv 信号量操作

参数

struct kern_ipc_perm *sma

sysv ipc 权限结构

struct sembuf *sops

要执行的操作

unsigned nsops

操作的数量

int alter

指示将进行更改的标志

描述

在对信号量集的成员执行操作之前检查权限。如果 alter 标志非零,则可以修改信号量集。

返回

如果授权,则返回 0。

int security_getselfattr(unsigned int attr, struct lsm_ctx __user *uctx, u32 __user *size, u32 flags)

读取当前进程的 LSM 属性。

参数

unsigned int attr

要返回哪个属性

struct lsm_ctx __user *uctx

用于存储信息的用户空间目标,或 NULL

u32 __user *size

指向可用于接收数据的空间大小的指针

u32 flags

特殊处理选项。LSM_FLAG_SINGLE 指示仅报告与传递的 ctx 中标识的 LSM 关联的属性。

描述

可以使用 uctx 的 NULL 值来获取属性的数量和数据的大小。

成功时返回找到的属性数量,错误时返回负值。size 将重置为数据的总大小。如果 size 不足以包含数据,则返回 -E2BIG。

int security_setselfattr(unsigned int attr, struct lsm_ctx __user *uctx, u32 size, u32 flags)

在当前进程上设置 LSM 属性。

参数

unsigned int attr

要设置哪个属性

struct lsm_ctx __user *uctx

用于存储信息的用户空间源

u32 size

数据的大小

u32 flags

保留供将来使用,必须为 0

描述

为当前进程设置 LSM 属性。LSM、属性和新值都包含在 uctx 中。

成功时返回 0,如果输入不一致则返回 -EINVAL,如果无法访问用户缓冲区则返回 -EFAULT,如果大小太大则返回 E2BIG,或者返回 LSM 特定的故障。

int security_getprocattr(struct task_struct *p, int lsmid, const char *name, char **value)

读取任务的属性

参数

struct task_struct *p

任务

int lsmid

LSM 标识

const char *name

属性名称

char **value

属性值

描述

读取任务 p 的属性 name,并在允许的情况下将其存储到 value 中。

返回

成功时返回 value 的长度,否则返回负值。

int security_setprocattr(int lsmid, const char *name, void *value, size_t size)

设置任务的属性

参数

int lsmid

LSM 标识

const char *name

属性名称

void *value

属性值

size_t size

属性值大小

描述

如果允许,则将当前任务的属性 name 写入 value,大小为 size

返回

成功时返回写入的字节数,否则返回负值。

保存信息并检查是否允许发送 netlink 消息

参数

struct sock *sk

发送套接字

struct sk_buff *skb

netlink 消息

描述

保存 netlink 消息的安全信息,以便在处理消息时可以执行权限检查。可以使用 netlink_skb_parms 结构的 eff_cap 字段保存安全信息。也可能用于对消息传输提供细粒度的控制。

返回

如果信息已成功保存并且消息被允许传输,则返回 0。

允许传输。

int security_post_notification(const struct cred *w_cred, const struct cred *cred, struct watch_notification *n)

检查是否可以发布监视通知

参数

const struct cred *w_cred

设置监视的任务的凭据

const struct cred *cred

触发监视的任务的凭据

struct watch_notification *n

通知

描述

检查是否可以将监视通知发布到特定队列。

返回

如果授权,则返回 0。

int security_watch_key(struct key *key)

检查是否允许任务监视密钥事件

参数

struct key *key

要监视的密钥

描述

检查是否允许进程监视来自密钥或密钥环的事件通知。

返回

如果授权,则返回 0。

int security_socket_create(int family, int type, int protocol, int kern)

检查是否允许创建新套接字

参数

int family

协议族

int type

通信类型

int protocol

请求的协议

int kern

如果请求内核套接字,则设置为 1

描述

在创建新套接字之前检查权限。

返回

如果授权,则返回 0。

int security_socket_post_create(struct socket *sock, int family, int type, int protocol, int kern)

初始化新创建的套接字

参数

struct socket *sock

socket

int family

协议族

int type

通信类型

int protocol

请求的协议

int kern

如果请求内核套接字,则设置为 1

描述

此挂钩允许模块更新或分配每个套接字的安全结构。请注意,安全字段不是直接添加到套接字结构中,而是将套接字安全信息存储在关联的 inode 中。通常,inode 的 alloc_security 挂钩会分配安全信息并将其附加到 SOCK_INODE(sock)->i_security。此挂钩可用于使用在分配 inode 时不可用的其他信息来更新 SOCK_INODE(sock)->i_security 字段。

返回

如果授权,则返回 0。

int security_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)

检查是否允许套接字绑定操作

参数

struct socket *sock

socket

struct sockaddr *address

请求的绑定地址

int addrlen

地址长度

描述

在执行套接字协议层绑定操作之前检查权限,并将套接字 sock 绑定到 address 参数中指定的地址。

返回

如果授权,则返回 0。

int security_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen)

检查是否允许套接字连接操作

参数

struct socket *sock

socket

struct sockaddr *address

远程连接点的地址

int addrlen

地址长度

描述

在套接字协议层连接操作尝试将套接字 sock 连接到远程地址 address 之前检查权限。

返回

如果授权,则返回 0。

int security_socket_listen(struct socket *sock, int backlog)

检查是否允许套接字监听

参数

struct socket *sock

socket

int backlog

连接队列大小

描述

在套接字协议层监听操作之前检查权限。

返回

如果授权,则返回 0。

int security_socket_accept(struct socket *sock, struct socket *newsock)

检查是否允许套接字接受连接

参数

struct socket *sock

监听套接字

struct socket *newsock

新创建的连接套接字

描述

在接受新连接之前检查权限。请注意,新的套接字 newsock 已被创建,并且一些信息已复制到其中,但实际上尚未执行接受操作。

返回

如果授权,则返回 0。

int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)

检查是否允许发送消息

参数

struct socket *sock

发送套接字

struct msghdr *msg

要发送的消息

int size

消息大小

描述

在向另一个套接字发送消息之前检查权限。

返回

如果授权,则返回 0。

int security_socket_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags)

检查是否允许接收消息

参数

struct socket *sock

接收套接字

struct msghdr *msg

要接收的消息

int size

消息大小

int flags

操作标志

描述

在从套接字接收消息之前检查权限。

返回

如果授权,则返回 0。

int security_socket_getsockname(struct socket *sock)

检查是否允许读取套接字地址

参数

struct socket *sock

socket

描述

在读取套接字对象的本地地址(名称)之前检查权限。

返回

如果授权,则返回 0。

int security_socket_getpeername(struct socket *sock)

检查是否允许读取对等方的地址

参数

struct socket *sock

socket

描述

在读取套接字对象的远程地址(名称)之前检查权限。

返回

如果授权,则返回 0。

int security_socket_getsockopt(struct socket *sock, int level, int optname)

检查是否允许读取套接字选项

参数

struct socket *sock

socket

int level

选项的协议级别

int optname

选项名称

描述

在检索与套接字 sock 关联的选项之前检查权限。

返回

如果授权,则返回 0。

int security_socket_setsockopt(struct socket *sock, int level, int optname)

检查是否允许设置套接字选项

参数

struct socket *sock

socket

int level

选项的协议级别

int optname

选项名称

描述

在设置与套接字 sock 关联的选项之前检查权限。

返回

如果授权,则返回 0。

int security_socket_shutdown(struct socket *sock, int how)

检查是否允许关闭套接字

参数

struct socket *sock

socket

int how

指示如何处理发送和接收的标志

描述

在套接字 sock 上关闭全部或部分连接之前检查权限。

返回

如果授权,则返回 0。

int security_socket_getpeersec_stream(struct socket *sock, sockptr_t optval, sockptr_t optlen, unsigned int len)

获取远程对等标签

参数

struct socket *sock

socket

sockptr_t optval

目标缓冲区

sockptr_t optlen

复制到缓冲区中的对等标签的大小

unsigned int len

目标缓冲区的最大大小

描述

此钩子允许安全模块通过 getsockopt SO_GETPEERSEC 为用户空间提供 unix 或已连接 tcp 套接字的对等套接字安全状态。对于 tcp 套接字,如果该套接字与 ipsec SA 相关联,则这可能是有意义的。

返回

如果一切正常,则返回 0,否则,返回典型的 getsockopt

值。

int lsm_sock_alloc(struct sock *sock, gfp_t gfp)

分配一个复合套接字 blob

参数

struct sock *sock

需要 blob 的套接字

gfp_t gfp

分配模式

描述

为所有模块分配套接字 blob

返回 0,如果无法分配内存,则返回 -ENOMEM。

int security_sk_alloc(struct sock *sk, int family, gfp_t priority)

分配并初始化套接字的 LSM blob

参数

struct sock *sk

套接字

int family

协议族

gfp_t priority

gfp 标志

描述

分配并将安全结构附加到 sk->sk_security 字段,该字段用于在本地流套接字之间复制安全属性。

返回

成功返回 0,失败返回错误。

void security_sk_free(struct sock *sk)

释放套接字的 LSM blob

参数

struct sock *sk

套接字

描述

释放安全结构。

void security_inet_csk_clone(struct sock *newsk, const struct request_sock *req)

根据 request_sock 设置新的套接字 LSM 状态

参数

struct sock *newsk

新的套接字

const struct request_sock *req

连接 request_sock

描述

使用 req 中的 LSM 状态设置 sock 的 LSM 状态。

int security_mptcp_add_subflow(struct sock *sk, struct sock *ssk)

从 MPTCP 套接字继承 LSM 标签

参数

struct sock *sk

拥有的 MPTCP 套接字

struct sock *ssk

新的子流

描述

更新给定 MPTCP 子流的标签,以匹配拥有的 MPTCP 套接字的标签。此钩子必须在通过 security_socket_create()security_socket_post_create() LSM 钩子创建和初始化套接字之后调用。

返回

成功返回 0,失败返回负错误代码。

int security_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx, struct xfrm_sec_ctx **new_ctxp)

克隆 xfrm 策略 LSM 状态

参数

struct xfrm_sec_ctx *old_ctx

xfrm 安全上下文

struct xfrm_sec_ctx **new_ctxp

目标 xfrm 安全上下文

描述

在 new_ctxp 中分配一个安全结构,其中包含 old_ctx 结构中的信息。

返回

如果操作成功,则返回 0。

int security_xfrm_policy_delete(struct xfrm_sec_ctx *ctx)

检查是否允许删除 xfrm 策略

参数

struct xfrm_sec_ctx *ctx

xfrm 安全上下文

描述

授权删除 SPD 条目。

返回

如果授权,则返回 0。

int security_xfrm_state_alloc_acquire(struct xfrm_state *x, struct xfrm_sec_ctx *polsec, u32 secid)

分配一个 xfrm 状态 LSM blob

参数

struct xfrm_state *x

正在添加到 SAD 的 xfrm 状态

struct xfrm_sec_ctx *polsec

相关策略的安全上下文

u32 secid

来自流的 secid

描述

为 x->security 字段分配一个安全结构;当分配 xfrm_state 时,security 字段初始化为 NULL。将上下文设置为与 secid 对应。

返回

如果操作成功,则返回 0。

void security_xfrm_state_free(struct xfrm_state *x)

释放 xfrm 状态

参数

struct xfrm_state *x

xfrm 状态

描述

释放 x->security。

int security_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid)

检查是否允许使用 xfrm 策略

参数

struct xfrm_sec_ctx *ctx

目标 xfrm 安全上下文

u32 fl_secid

用于授权访问的流 secid

描述

当流选择 xfrm_policy 来处理数据包上的 XFRM 时,检查权限。当选择每个套接字的策略或通用 xfrm 策略时,会调用此钩子。

返回

如果授予权限,则返回 0,否则返回 -ESRCH,或其他错误返回 -errno。

其他错误。

int security_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *xp, const struct flowi_common *flic)

检查 xfrm 是否匹配

参数

struct xfrm_state *x

要匹配的 xfrm 状态

struct xfrm_policy *xp

要检查匹配的 xfrm 策略

const struct flowi_common *flic

要检查匹配的流。

描述

检查 xpflic 是否与 x 匹配。

返回

如果匹配,则返回 1。

int security_xfrm_decode_session(struct sk_buff *skb, u32 *secid)

确定数据包的 xfrm secid

参数

struct sk_buff *skb

xfrm 数据包

u32 *secid

secid

描述

解码 skb 中的数据包,并在 secid 中返回安全标签。

返回

如果所有使用的 xfrm 具有相同的 secid,则返回 0。

int security_key_alloc(struct key *key, const struct cred *cred, unsigned long flags)

分配并初始化一个内核密钥 LSM blob

参数

struct key *key

key

const struct cred *cred

凭证

unsigned long flags

分配标志

描述

允许分配一个密钥并分配安全数据。请注意,此时密钥未分配序列号。

返回

如果权限被授予,则返回 0,否则返回 -ve 错误。

void security_key_free(struct key *key)

释放一个内核密钥 LSM blob

参数

struct key *key

key

描述

销毁通知;释放安全数据。

int security_key_permission(key_ref_t key_ref, const struct cred *cred, enum key_need_perm need_perm)

检查是否允许内核密钥操作

参数

key_ref_t key_ref

密钥引用

const struct cred *cred

请求访问的参与者的凭据

enum key_need_perm need_perm

请求的权限

描述

查看是否授予进程对密钥的特定操作权限。

返回

如果权限被授予,则返回 0,否则返回 -ve 错误。

int security_key_getsecurity(struct key *key, char **buffer)

获取密钥的安全标签

参数

struct key *key

key

char **buffer

安全标签缓冲区

描述

获取附加到密钥的安全上下文的文本表示,以遵守 KEYCTL_GETSECURITY。此函数为以 NULL 结尾的字符串分配存储空间,调用者应释放它。

返回

返回 buffer 的长度(包括终止 NULL),如果

发生错误则返回 -ve。如果未为密钥分配安全标签,也可能返回 0(和 NULL 缓冲区指针)。

void security_key_post_create_or_update(struct key *keyring, struct key *key, const void *payload, size_t payload_len, unsigned long flags, bool create)

密钥创建或更新的通知

参数

struct key *keyring

密钥链接到的密钥环

struct key *key

已创建或更新的密钥

const void *payload

用于实例化或更新密钥的数据

size_t payload_len

有效负载的长度

unsigned long flags

密钥标志

bool create

指示密钥是已创建还是已更新的标志

描述

通知调用者密钥的创建或更新。

int security_audit_rule_init(u32 field, u32 op, char *rulestr, void **lsmrule, gfp_t gfp)

分配并初始化一个 LSM 审计规则结构

参数

u32 field

审计操作

u32 op

规则运算符

char *rulestr

规则上下文

void **lsmrule

审计规则结构的接收缓冲区

gfp_t gfp

用于 kmalloc 的 GFP 标志

描述

分配并初始化 LSM 审计规则结构。

返回

如果成功设置了 lsmrule,则返回 0,如果

规则无效,则返回 -EINVAL。

int security_audit_rule_known(struct audit_krule *krule)

检查审计规则是否包含 LSM 字段

参数

struct audit_krule *krule

审计规则

描述

指定给定的 krule 是否包含与当前 LSM 相关的任何字段。

返回

如果找到关系,则返回 1,否则返回 0。

void security_audit_rule_free(void *lsmrule)

释放 LSM 审计规则结构

参数

void *lsmrule

审计规则结构

描述

释放先前由 audit_rule_init() 分配的 LSM 审计规则结构。

int security_audit_rule_match(struct lsm_prop *prop, u32 field, u32 op, void *lsmrule)

检查标签是否与审计规则匹配

参数

struct lsm_prop *prop

安全标签

u32 field

LSM 审计字段

u32 op

匹配运算符

void *lsmrule

审计规则

描述

确定给定的 secid 是否与先前由 security_audit_rule_known() 批准的规则匹配。

返回

如果 secid 与规则匹配,则返回 1;如果不匹配,则返回 0;如果失败,则返回 -ERRNO。

failure.

int security_bpf(int cmd, union bpf_attr *attr, unsigned int size)

检查是否允许 bpf 系统调用操作

参数

int cmd

命令

union bpf_attr *attr

bpf 属性

unsigned int size

大小

描述

在将属性复制到内核后,对所有 bpf 系统调用进行初始检查。实际的安全模块可以实现它们自己的规则来检查它们需要的特定命令。

返回

如果授权,则返回 0。

int security_bpf_map(struct bpf_map *map, fmode_t fmode)

检查是否允许访问 bpf 映射

参数

struct bpf_map *map

bpf 映射

fmode_t fmode

模式

描述

当内核为 eBPF 映射生成并返回文件描述符时进行检查。

返回

如果授权,则返回 0。

int security_bpf_prog(struct bpf_prog *prog)

检查是否允许访问 bpf 程序

参数

struct bpf_prog *prog

bpf 程序

描述

当内核为 eBPF 程序生成并返回文件描述符时进行检查。

返回

如果授权,则返回 0。

int security_bpf_map_create(struct bpf_map *map, union bpf_attr *attr, struct bpf_token *token)

检查是否允许创建 BPF 映射。

参数

struct bpf_map *map

BPF 映射对象。

union bpf_attr *attr

用于创建 BPF 映射的 BPF 系统调用属性。

struct bpf_token *token

用于授予用户访问权限的 BPF 令牌。

描述

当内核创建新的 BPF 映射时执行检查。这也是需要 LSM blob 的 LSM 分配 blob 的地方。

返回

成功返回 0,失败返回错误。

int security_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr, struct bpf_token *token)

检查是否允许加载 BPF 程序。

参数

struct bpf_prog *prog

BPF 程序对象。

union bpf_attr *attr

用于创建 BPF 程序的 BPF 系统调用属性。

struct bpf_token *token

用于授予用户访问 BPF 子系统权限的 BPF 令牌。

描述

当内核加载 BPF 程序并分配相关的 BPF 程序对象时,执行访问控制检查。此钩子还负责为 BPF 程序分配任何所需的 LSM 状态。

返回

成功返回 0,失败返回错误。

int security_bpf_token_create(struct bpf_token *token, union bpf_attr *attr, const struct path *path)

检查是否允许创建 BPF 令牌。

参数

struct bpf_token *token

BPF 令牌对象。

union bpf_attr *attr

用于创建 BPF 令牌的 BPF 系统调用属性。

const struct path *path

指向从中创建 BPF 令牌的 BPF FS 挂载点的路径。

描述

当内核从 BPF FS 实例实例化新的 BPF 令牌对象时执行检查。这也是可以为 LSM 分配 LSM blob 的地方。

返回

成功返回 0,失败返回错误。

int security_bpf_token_cmd(const struct bpf_token *token, enum bpf_cmd cmd)

检查是否允许 BPF 令牌委派请求的 BPF 系统调用命令。

参数

const struct bpf_token *token

BPF 令牌对象。

enum bpf_cmd cmd

请求 BPF 令牌委派的 BPF 系统调用命令。

描述

当内核决定是否应允许提供的 BPF 令牌委派请求的 BPF 系统调用命令时执行检查。

返回

成功返回 0,失败返回错误。

int security_bpf_token_capable(const struct bpf_token *token, int cap)

检查是否允许 BPF 令牌委派请求的 BPF 相关能力。

参数

const struct bpf_token *token

BPF 令牌对象。

int cap

请求 BPF 令牌委派的能力。

描述

当内核决定是否应允许提供的 BPF 令牌委派请求的 BPF 相关能力时执行检查。

返回

成功返回 0,失败返回错误。

void security_bpf_map_free(struct bpf_map *map)

释放 bpf 映射的 LSM blob。

参数

struct bpf_map *map

bpf 映射

描述

清理存储在 bpf 映射内的安全信息。

void security_bpf_prog_free(struct bpf_prog *prog)

释放 BPF 程序的 LSM blob。

参数

struct bpf_prog *prog

BPF 程序结构体。

描述

清理存储在 BPF 程序内的安全信息。

void security_bpf_token_free(struct bpf_token *token)

释放 BPF 令牌的 LSM blob。

参数

struct bpf_token *token

BPF 令牌结构体。

描述

清理存储在 BPF 令牌内的安全信息。

int security_perf_event_open(struct perf_event_attr *attr, int type)

检查是否允许打开 perf 事件。

参数

struct perf_event_attr *attr

perf 事件属性。

int type

事件类型。

描述

检查是否允许 type 类型的 perf_event_open 系统调用。

返回

如果授权,则返回 0。

int security_perf_event_alloc(struct perf_event *event)

分配 perf 事件 LSM blob。

参数

struct perf_event *event

perf 事件。

描述

分配并保存 perf_event 安全信息。

返回

成功返回 0,失败返回错误。

void security_perf_event_free(struct perf_event *event)

释放 perf 事件 LSM blob。

参数

struct perf_event *event

perf 事件。

描述

释放 (释放) perf_event 安全信息。

int security_perf_event_read(struct perf_event *event)

检查是否允许读取 perf 事件标签。

参数

struct perf_event *event

perf 事件。

描述

如果允许,则读取 perf_event 安全信息。

返回

如果授权,则返回 0。

int security_perf_event_write(struct perf_event *event)

检查是否允许写入 perf 事件标签。

参数

struct perf_event *event

perf 事件。

描述

如果允许,则写入 perf_event 安全信息。

返回

如果授权,则返回 0。

int security_uring_override_creds(const struct cred *new)

检查是否允许覆盖凭据。

参数

const struct cred *new

新的凭证

描述

检查执行 io_uring 操作的当前任务是否允许使用 new 覆盖其凭据。

返回

如果授权,则返回 0。

int security_uring_sqpoll(void)

检查是否允许 IORING_SETUP_SQPOLL。

参数

void

无参数

描述

检查是否允许当前任务生成 io_uring 轮询线程 (IORING_SETUP_SQPOLL)。

返回

如果授权,则返回 0。

int security_uring_cmd(struct io_uring_cmd *ioucmd)

检查是否允许 io_uring 直通命令。

参数

struct io_uring_cmd *ioucmd

命令

描述

检查是否允许运行 file_operations uring_cmd。

返回

如果授权,则返回 0。

void security_initramfs_populated(void)

通知 LSMs initramfs 已加载

参数

void

无参数

描述

告知 LSMs initramfs 已被解压到 rootfs 中。

struct dentry *securityfs_create_file(const char *name, umode_t mode, struct dentry *parent, void *data, const struct file_operations *fops)

在 securityfs 文件系统中创建一个文件

参数

const char *name

一个指向包含要创建的文件名的字符串的指针。

umode_t mode

该文件应具有的权限

struct dentry *parent

指向此文件的父 dentry 的指针。如果设置了此参数,则应为目录 dentry。如果此参数为 NULL,则该文件将在 securityfs 文件系统的根目录中创建。

void *data

一个指向调用者稍后想要访问的内容的指针。在 open() 调用时,inode.i_private 指针将指向此值。

const struct file_operations *fops

指向应该用于此文件的 struct file_operations 的指针。

描述

此函数在 securityfs 中创建一个具有给定name的文件。

如果成功,此函数将返回一个指向 dentry 的指针。当要删除该文件时,必须将此指针传递给 securityfs_remove() 函数(如果您的模块被卸载,则不会进行自动清理,您需要在此处负责)。如果发生错误,该函数将返回错误值(通过 ERR_PTR)。

如果内核中未启用 securityfs,则返回 -ENODEV 值。

struct dentry *securityfs_create_dir(const char *name, struct dentry *parent)

在 securityfs 文件系统中创建一个目录

参数

const char *name

一个指向包含要创建的目录名称的字符串的指针。

struct dentry *parent

指向此文件的父 dentry 的指针。如果设置了此参数,则应为目录 dentry。如果此参数为 NULL,则该目录将在 securityfs 文件系统的根目录中创建。

描述

此函数在 securityfs 中创建一个具有给定name的目录。

如果成功,此函数将返回一个指向 dentry 的指针。当要删除该文件时,必须将此指针传递给 securityfs_remove() 函数(如果您的模块被卸载,则不会进行自动清理,您需要在此处负责)。如果发生错误,该函数将返回错误值(通过 ERR_PTR)。

如果内核中未启用 securityfs,则返回 -ENODEV 值。

在 securityfs 文件系统中创建一个符号链接

参数

const char *name

一个指向包含要创建的符号链接名称的字符串的指针。

struct dentry *parent

指向符号链接的父 dentry 的指针。如果设置了此参数,则应为目录 dentry。如果此参数为 NULL,则该目录将在 securityfs 文件系统的根目录中创建。

const char *target

一个指向包含符号链接目标名称的字符串的指针。如果此参数为 NULL,则需要设置 iops 参数来处理 .readlink 和 .get_link inode_operations。

const struct inode_operations *iops

指向要用于符号链接的 struct inode_operations 的指针。如果此参数为 NULL,则将使用默认的 simple_symlink_inode 操作。

描述

此函数在 securityfs 中创建一个具有给定name的符号链接。

如果成功,此函数将返回一个指向 dentry 的指针。当要删除该文件时,必须将此指针传递给 securityfs_remove() 函数(如果您的模块被卸载,则不会进行自动清理,您需要在此处负责)。如果发生错误,该函数将返回错误值(通过 ERR_PTR)。

如果内核中未启用 securityfs,则返回 -ENODEV 值。

void securityfs_remove(struct dentry *dentry)

从 securityfs 文件系统中删除一个文件或目录

参数

struct dentry *dentry

指向要删除的文件或目录的 dentry 的指针。

描述

必须调用此函数才能删除该文件。当删除模块时,不会自动清理文件;您需要在此处负责。

此函数删除 securityfs 中先前通过调用另一个 securityfs 函数(如 securityfs_create_file() 或其变体)创建的文件或目录。

void securityfs_recursive_remove(struct dentry *dentry)

递归删除一个文件或目录

参数

struct dentry *dentry

指向要删除的文件或目录的 dentry 的指针。

描述

此函数递归删除 securityfs 中先前通过调用另一个 securityfs 函数(如 securityfs_create_file() 或其变体)创建的文件或目录。

审计接口

struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type)

获取审计缓冲区

参数

struct audit_context *ctx

audit_context (可以为 NULL)

gfp_t gfp_mask

分配类型

int type

审计消息类型

描述

成功时返回 audit_buffer 指针,失败时返回 NULL。

获取审计缓冲区。此例程执行锁定以获取审计缓冲区,然后调用 audit_log_*format 不需要锁定。如果任务 (ctx) 是当前正在系统调用中的任务,则该系统调用将被标记为可审计的,并且将在系统调用退出时写入审计记录。如果没有关联的任务,则任务上下文 (ctx) 应为 NULL。

void audit_log_format(struct audit_buffer *ab, const char *fmt, ...)

将消息格式化到审计缓冲区中。

参数

struct audit_buffer *ab

audit_buffer

const char *fmt

格式化字符串

...

fmt 字符串匹配的可选参数

描述

所有工作都在 audit_log_vformat 中完成。

void audit_log_end(struct audit_buffer *ab)

结束一个审计记录

参数

struct audit_buffer *ab

audit_buffer

描述

我们不能在 irq 上下文中执行 netlink 发送,因为它会阻塞(最后一个参数,flags,未设置为 MSG_DONTWAIT),因此审计缓冲区被放置在队列中,并计划一个 kthread 将它们从 irq 上下文之外的队列中删除。可以在任何上下文中调用。

void audit_log(struct audit_context *ctx, gfp_t gfp_mask, int type, const char *fmt, ...)

记录一个审计记录

参数

struct audit_context *ctx

审计上下文

gfp_t gfp_mask

分配类型

int type

审计消息类型

const char *fmt

要使用的格式化字符串

...

与格式化字符串匹配的变量参数

描述

这是一个方便的函数,它调用 audit_log_start、audit_log_vformat 和 audit_log_end。可以在任何上下文中调用它。

int __audit_filter_op(struct task_struct *tsk, struct audit_context *ctx, struct list_head *list, struct audit_names *name, unsigned long op)

用于操作(系统调用/uring/等)的通用过滤器辅助函数

参数

struct task_struct *tsk

关联的任务

struct audit_context *ctx

审计上下文

struct list_head *list

审计过滤器列表

struct audit_names *name

audit_name (可以为 NULL)

unsigned long op

当前系统调用/uring_op

描述

使用 ctxnameop(如果需要),针对 tsk 运行 list 中指定的 udit 过滤器;调用者负责确保在持有 RCU 读取锁时进行调用。 name 参数可以为 NULL,但所有其他参数必须指定。 如果过滤器找到匹配项,则返回 1/true,如果没有找到,则返回 0/false。

void audit_filter_uring(struct task_struct *tsk, struct audit_context *ctx)

将过滤器应用于 io_uring 操作

参数

struct task_struct *tsk

关联的任务

struct audit_context *ctx

审计上下文

void audit_reset_context(struct audit_context *ctx)

重置 audit_context 结构

参数

struct audit_context *ctx

要重置的 audit_context

描述

audit_context 中的所有字段都将重置为初始状态,字段持有的所有引用将被删除,并且私有内存将被释放。当此函数返回时,只要传递的上下文不是 NULL 或虚拟上下文,该 audit_context 就适合重复使用。

int audit_alloc(struct task_struct *tsk)

为任务分配一个审计上下文块

参数

struct task_struct *tsk

任务

描述

根据任务信息进行过滤,并在必要时分配每个任务的审计上下文。这样做会为指定的任务启用系统调用审计。这是从 copy_process 调用的,因此不需要锁。

void audit_log_uring(struct audit_context *ctx)

生成 AUDIT_URINGOP 记录

参数

struct audit_context *ctx

审计上下文

void __audit_free(struct task_struct *tsk)

释放每个任务的审计上下文

参数

struct task_struct *tsk

要释放其审计上下文块的任务

描述

从 copy_process、do_exit 和 io_uring 代码调用

void audit_return_fixup(struct audit_context *ctx, int success, long code)

修复 audit_context 中的返回代码

参数

struct audit_context *ctx

审计上下文

int success

指示操作是否成功的 true/false 值

long code

操作返回代码

描述

如果实际的返回代码稍后将被特定于架构的信号处理程序修复,我们需要修复审计日志中的返回代码。

void __audit_uring_entry(u8 op)

为 io_uring 准备内核任务的审计上下文

参数

u8 op

io_uring 操作码

描述

这类似于 audit_syscall_entry(),但旨在供 io_uring 操作使用。此函数应仅从 audit_uring_entry() 调用,因为我们依赖于该函数中存在的审计上下文检查。

void __audit_uring_exit(int success, long code)

在 io_uring 之后包装内核任务的审计上下文

参数

int success

指示操作是否成功的 true/false 值

long code

操作返回代码

描述

这类似于 audit_syscall_exit(),但旨在供 io_uring 操作使用。此函数应仅从 audit_uring_exit() 调用,因为我们依赖于该函数中存在的审计上下文检查。

void __audit_syscall_entry(int major, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4)

在系统调用入口处填写审计记录

参数

int major

主要系统调用类型(函数)

unsigned long a1

附加的系统调用寄存器 1

unsigned long a2

附加的系统调用寄存器 2

unsigned long a3

附加的系统调用寄存器 3

unsigned long a4

附加的系统调用寄存器 4

描述

在系统调用入口处填写审计上下文。这仅在创建任务时创建了审计上下文,并且状态或过滤器要求构建审计上下文时才会发生。如果来自每个任务过滤器或每个系统调用过滤器的状态是 AUDIT_STATE_RECORD,则将在系统调用退出时写入记录(否则,仅当内核的另一部分请求写入时才会写入)。

void __audit_syscall_exit(int success, long return_code)

在系统调用后释放审计上下文

参数

int success

系统调用的成功值

long return_code

系统调用的返回值

描述

系统调用后拆除。如果审计上下文已被标记为可审计的(因为来自过滤的 AUDIT_STATE_RECORD 状态,或者因为内核的另一部分写入了审计消息),则写出系统调用信息。在所有情况下,释放从 getname() 存储的名称。

struct filename *__audit_reusename(__user const char *uptr)

使用现有条目的信息填写文件名

参数

const __user char *uptr

用户空间指向路径名的指针

描述

在当前审计上下文的 audit_names 列表中搜索。如果存在具有匹配的“uptr”的现有条目,则返回与该 audit_name 关联的文件名。如果不存在,则返回 NULL。

void __audit_getname(struct filename *name)

向列表添加名称

参数

struct filename *name

要添加的名称

描述

将名称添加到此上下文的审计名称列表中。从 fs/namei.c:getname() 调用。

void __audit_inode(struct filename *name, const struct dentry *dentry, unsigned int flags)

存储来自查找的 inode 和设备信息

参数

struct filename *name

正在审计的名称

const struct dentry *dentry

正在审计的 dentry

unsigned int flags

此特定条目的属性

int auditsc_get_stamp(struct audit_context *ctx, struct timespec64 *t, unsigned int *serial)

获取 audit_context 值的本地副本

参数

struct audit_context *ctx

任务的 audit_context

struct timespec64 *t

timespec64 用于存储 audit_context 中记录的时间

unsigned int *serial

audit_context 中记录的序列号值

描述

同时将上下文设置为可审计。

void __audit_mq_open(int oflag, umode_t mode, struct mq_attr *attr)

记录 POSIX MQ 打开的审计数据

参数

int oflag

打开标志

umode_t mode

模式位

struct mq_attr *attr

队列属性

void __audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec64 *abs_timeout)

记录 POSIX MQ 定时发送/接收的审计数据

参数

mqd_t mqdes

MQ 描述符

size_t msg_len

消息长度

unsigned int msg_prio

消息优先级

const struct timespec64 *abs_timeout

消息超时(绝对时间)

void __audit_mq_notify(mqd_t mqdes, const struct sigevent *notification)

记录 POSIX MQ 通知的审计数据

参数

mqd_t mqdes

MQ 描述符

const struct sigevent *notification

通知事件

void __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat)

记录 POSIX MQ 获取/设置属性的审计数据

参数

mqd_t mqdes

MQ 描述符

struct mq_attr *mqstat

MQ 标志

void __audit_ipc_obj(struct kern_ipc_perm *ipcp)

记录 ipc 对象的审计数据

参数

struct kern_ipc_perm *ipcp

ipc 权限

void __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, umode_t mode)

记录新 ipc 权限的审计数据

参数

unsigned long qbytes

msgq 字节数

uid_t uid

msgq 用户 ID

gid_t gid

msgq 组 ID

umode_t mode

msgq 模式(权限)

描述

仅在 audit_ipc_obj() 之后调用。

int __audit_socketcall(int nargs, unsigned long *args)

记录 sys_socketcall 的审计数据

参数

int nargs

参数数量,不应超过 AUDITSC_ARGS。

unsigned long *args

参数数组

void __audit_fd_pair(int fd1, int fd2)

记录管道和 socketpair 的审计数据

参数

int fd1

第一个文件描述符

int fd2

第二个文件描述符

int __audit_sockaddr(int len, void *a)

记录 sys_bind、sys_connect、sys_sendto 的审计数据

参数

int len

用户空间中的数据长度

void *a

内核空间中的数据地址

描述

成功返回 0,NULL 上下文或错误时返回 < 0。

int audit_signal_info_syscall(struct task_struct *t)

记录系统调用的信号信息

参数

struct task_struct *t

正在发出信号的任务

描述

如果正在终止审计子系统,请记录正在执行此操作的任务 (pid) 和 uid。

int __audit_log_bprm_fcaps(struct linux_binprm *bprm, const struct cred *new, const struct cred *old)

存储有关正在加载的 bprm 和相关 fcaps 的信息

参数

struct linux_binprm *bprm

指向正在处理的 bprm 的指针

const struct cred *new

建议的新凭据

const struct cred *old

旧凭据

描述

只需检查进程是否已拥有文件给出的能力,如果没有,则存储私有权限提升信息,以便在系统调用结束时进行审计

-Eric

void __audit_log_capset(const struct cred *new, const struct cred *old)

存储关于 capset 系统调用的参数信息

参数

const struct cred *new

新的凭据

const struct cred *old

旧的(当前的)凭据

描述

记录用户空间发送给 sys_capset 的参数,以便在适用时由审计系统稍后打印

void audit_core_dumps(long signr)

记录有关异常结束的进程的信息

参数

long signr

信号值

描述

如果进程以核心转储结束,则说明发生了可疑情况,我们应该记录该事件以供调查。

void audit_seccomp(unsigned long syscall, long signr, int code)

记录有关 seccomp 操作的信息

参数

unsigned long syscall

系统调用号

long signr

信号值

int code

seccomp 操作

描述

记录与 seccomp 操作相关的信息。对于不应记录的 seccomp 操作的事件过滤在 seccomp_log() 中完成。因此,此函数强制执行审计,而与 audit_enabled 和 dummy 上下文状态无关,因为即使不使用审计也应记录 seccomp 操作。

int audit_rule_change(int type, int seq, void *data, size_t datasz)

将所有规则应用于指定的消息类型

参数

int type

审计消息类型

int seq

netlink 审计消息序列(串行)号

void *data

有效负载数据

size_t datasz

有效负载数据的大小

int audit_list_rules_send(struct sk_buff *request_skb, int seq)

列出审计规则

参数

struct sk_buff *request_skb

我们正在回复的请求的 skb(用于定位回复)

int seq

netlink 审计消息序列(串行)号

int parent_len(const char *path)

查找路径名父部分的长度

参数

const char *path

要确定长度的路径名

int audit_compare_dname_path(const struct qstr *dname, const char *path, int parentlen)

将给定的目录项名称与给定路径中的最后一个组件进行比较。返回 0 表示匹配。

参数

const struct qstr *dname

我们正在比较的目录项名称

const char *path

我们正在比较的完整路径名

int parentlen

如果已知,则为父级的长度。在此处传入 AUDIT_NAME_FULL 表示我们必须计算此值。

记账框架

long sys_acct(const char __user *name)

启用/禁用进程记账

参数

const char __user * name

记账记录的文件名,或者 NULL 以关闭记账

描述

sys_acct() 是实现进程记账所需的唯一系统调用。它接受应写入记账记录的文件的名称。如果文件名为 NULL,则将关闭记账。

返回

成功则为 0,失败则为负的 errno 值。

void acct_collect(long exitcode, int group_dead)

将记账信息收集到 pacct_struct 中

参数

long exitcode

任务退出代码

int group_dead

如果此线程是进程中的最后一个线程,则不为 0。

void acct_process(void)

处理正在退出的任务的进程记账

参数

void

无参数

块设备

void bio_advance(struct bio *bio, unsigned int nbytes)

按一定的字节数增加/完成 bio

参数

struct bio *bio

要推进的 bio

unsigned int nbytes

要完成的字节数

描述

这将更新 bi_sector、bi_size 和 bi_idx;如果完成的字节数与 bvec 边界不对齐,则还将在最后一个 bvec 上更新 bv_len 和 bv_offset。

bio 将表示 io 的剩余未完成部分。

struct folio_iter

用于迭代 bio 中所有 folios 的状态。

定义:

struct folio_iter {
    struct folio *folio;
    size_t offset;
    size_t length;
};

成员

folio

我们正在迭代的当前 folio。最后一个 folio 之后为 NULL。

offset

当前 folio 中的字节偏移量。

长度

此迭代中的字节数(不会跨越 folio 边界)。

bio_for_each_folio_all

bio_for_each_folio_all (fi, bio)

迭代 bio 中的每个 folio。

参数

fi

struct folio_iter,它为每个 folio 更新。

bio

要迭代的 struct bio。

struct bio *bio_next_split(struct bio *bio, int sectors, gfp_t gfp, struct bio_set *bs)

从 bio 中获取接下来的 sectors,必要时进行拆分

参数

struct bio *bio

要拆分的 bio

int sectors

要从 bio 前面拆分的扇区数

gfp_t gfp

gfp 掩码

struct bio_set *bs

要从中分配的 bio 集

返回

表示 bio 的接下来的 sectors 的 bio - 如果 bio 小于 sectors,则返回原始 bio 不变。

bool bio_is_zone_append(struct bio *bio)

这是一个区域追加 bio 吗?

参数

struct bio *bio

要检查的 bio

描述

检查 bio 是否为区域追加操作。核心块层代码和 end_io 处理程序必须使用此方法,而不是使用开放编码的 REQ_OP_ZONE_APPEND 检查,因为如果不支持原生操作,块层可以将 REQ_OP_ZONE_APPEND 重写为 REQ_OP_WRITE。

void blk_queue_flag_set(unsigned int flag, struct request_queue *q)

原子性地设置队列标志

参数

unsigned int flag

要设置的标志

struct request_queue *q

请求队列

void blk_queue_flag_clear(unsigned int flag, struct request_queue *q)

原子性地清除队列标志

参数

unsigned int flag

要清除的标志

struct request_queue *q

请求队列

const char *blk_op_str(enum req_op op)

返回 REQ_OP_XXX 中的字符串 XXX。

参数

enum req_op op

REQ_OP_XXX.

描述

集中块层函数将 REQ_OP_XXX 转换为字符串格式。在调试和跟踪 bio 或请求时很有用。对于无效的 REQ_OP_XXX,它返回字符串“UNKNOWN”。

void blk_sync_queue(struct request_queue *q)

取消队列上任何挂起的回调

参数

struct request_queue *q

队列

描述

块层可能会在队列上执行异步回调活动,例如在超时后调用 unplug 函数。块设备可以调用 blk_sync_queue 来确保任何此类活动被取消,从而允许它释放回调可能使用的资源。调用者必须已确保其 ->submit_bio 不会在调用此函数之前重新添加插件。

此函数不取消由电梯或节流代码引起的任何异步活动。这需要使用初始化的队列锁调用 elevator_exit() 和 blkcg_exit_queue()。

void blk_set_pm_only(struct request_queue *q)

增加 pm_only 计数器

参数

struct request_queue *q

请求队列指针

void blk_put_queue(struct request_queue *q)

减少 request_queue 的引用计数

参数

struct request_queue *q

要减少引用计数的 request_queue 结构

描述

减少 request_queue 的引用计数,并在引用计数达到 0 时释放它。

bool blk_get_queue(struct request_queue *q)

增加 request_queue 的引用计数

参数

struct request_queue *q

要增加引用计数的 request_queue 结构

描述

增加 request_queue kobject 的引用计数。

上下文

任何上下文。

void submit_bio_noacct(struct bio *bio)

将 bio 重新提交到块设备层进行 I/O

参数

struct bio *bio

描述内存和设备位置的 bio。

描述

这是 submit_bio() 的一个版本,它只能用于由堆叠块驱动程序重新提交给较低级别驱动程序的 I/O。块层的所有文件系统和其他上层用户应改为使用 submit_bio()

void submit_bio(struct bio *bio)

将 bio 提交到块设备层进行 I/O

参数

struct bio *bio

描述 I/O 的 struct bio

描述

submit_bio() 用于将 I/O 请求提交到块设备。它被传递一个完全设置好的 struct bio,该结构描述了需要完成的 I/O。bio 将被发送到 bi_bdev 字段描述的设备。

请求的成功/失败状态以及完成通知通过 bio 中的 ->bi_end_io() 回调异步传递。在 ->bi_end_io() 被调用之前,调用者不得触摸 bio。

int bio_poll(struct bio *bio, struct io_comp_batch *iob, unsigned int flags)

轮询 BIO 完成

参数

struct bio *bio

要轮询的 bio

struct io_comp_batch *iob

批量 I/O

unsigned int flags

控制行为的 BLK_POLL_* 标志

描述

轮询与 bio 关联的队列上的完成情况。返回找到的已完成条目的数量。

注意

调用者必须是提交 bio 的上下文,或者处于 RCU 临界区,以防止释放 bio

unsigned long bio_start_io_acct(struct bio *bio)

为基于 bio 的驱动程序启动 I/O 记帐

参数

struct bio *bio

要开始记帐的 bio

描述

返回应传递回 bio_end_io_acct() 的开始时间。

int blk_lld_busy(struct request_queue *q)

检查设备的底层低级驱动程序是否繁忙

参数

struct request_queue *q

正在检查的设备的队列

描述

检查设备的底层低级驱动程序是否繁忙。如果驱动程序想要导出其繁忙状态,它们必须首先使用 blk_queue_lld_busy() 设置自己的导出函数。

基本上,此函数仅由请求堆叠驱动程序使用,以在底层设备繁忙时停止向底层设备分派请求。此行为有助于在请求堆叠驱动程序的队列上进行更多 I/O 合并,并防止突发 I/O 负载下的 I/O 吞吐量下降。

返回

0 - 不繁忙(请求堆叠驱动程序应分派请求)1 - 繁忙(请求堆叠驱动程序应停止分派请求)

void blk_start_plug(struct blk_plug *plug)

初始化 blk_plug 并在 task_struct 中跟踪它

参数

struct blk_plug *plug

需要初始化的 struct blk_plug

描述

blk_start_plug() 向块层表明调用者打算批量提交多个 I/O 请求。块层可能会使用此提示来延迟提交调用者的 I/O,直到调用 blk_finish_plug()。但是,如果排队的 I/O 数量超过 BLK_MAX_REQUEST_COUNT,或者 I/O 的大小大于 BLK_PLUG_FLUSH_SIZE,则块层可能会选择在调用 blk_finish_plug() 之前提交请求。如果任务调度(见下文),也可能会提前提交排队的 I/O。

在 task_struct 内部跟踪 blk_plug 将有助于自动刷新挂起的 I/O,以防任务在 blk_start_plug()blk_finish_plug() 之间阻塞。这从性能角度来看很重要,而且还能确保我们不会死锁。例如,如果任务正在阻塞以进行内存分配,内存回收最终可能会想要释放属于当前驻留在我们私有插件中的请求的页面。通过在进程进入睡眠状态时刷新挂起的 I/O,我们避免了这种死锁。

void blk_finish_plug(struct blk_plug *plug)

标记提交的 I/O 批次的结束

参数

struct blk_plug *plug

传递给 blk_start_plug()struct blk_plug

描述

表示一批 I/O 提交已完成。此函数必须与初始调用 blk_start_plug() 配对。目的是允许块层优化 I/O 提交。有关更多信息,请参阅 blk_start_plug() 的文档。

int blk_queue_enter(struct request_queue *q, blk_mq_req_flags_t flags)

尝试增加 q->q_usage_counter

参数

struct request_queue *q

请求队列指针

blk_mq_req_flags_t flags

BLK_MQ_REQ_NOWAIT 和/或 BLK_MQ_REQ_PM

int blk_rq_map_user_iov(struct request_queue *q, struct request *rq, struct rq_map_data *map_data, const struct iov_iter *iter, gfp_t gfp_mask)

将用户数据映射到请求,用于直通请求

参数

struct request_queue *q

应插入请求的请求队列

struct request *rq

要映射数据的请求

struct rq_map_data *map_data

指向保存页面的 rq_map_data 的指针(如果需要)

const struct iov_iter *iter

iovec 迭代器

gfp_t gfp_mask

内存分配标志

描述

如果可能,数据将直接映射以实现零拷贝 I/O。否则,将使用内核反弹缓冲区。

在 I/O 结束时,仍然在进程上下文中时,必须发出匹配的 blk_rq_unmap_user()

int blk_rq_unmap_user(struct bio *bio)

取消映射具有用户数据的请求

参数

struct bio *bio

bio 列表的开头

描述

取消映射先前由 blk_rq_map_user() 映射的 rq。调用者必须提供来自 blk_rq_map_user() 返回的原始 rq->bio,因为 I/O 完成可能已更改 rq->bio。

int blk_rq_map_kern(struct request_queue *q, struct request *rq, void *kbuf, unsigned int len, gfp_t gfp_mask)

将内核数据映射到请求,用于直通请求

参数

struct request_queue *q

应插入请求的请求队列

struct request *rq

要填充的请求

void *kbuf

内核缓冲区

unsigned int len

用户数据的长度

gfp_t gfp_mask

内存分配标志

描述

如果可能,数据将直接映射。否则,将使用反弹缓冲区。可以多次调用以追加多个缓冲区。

int blk_register_queue(struct gendisk *disk)

在 sysfs 中注册块层队列

参数

struct gendisk *disk

应向 sysfs 注册请求队列的磁盘。

void blk_unregister_queue(struct gendisk *disk)

blk_register_queue() 的对应项

参数

struct gendisk *disk

应从 sysfs 中取消注册请求队列的磁盘。

注意

调用者负责保证此函数在 blk_register_queue() 完成后被调用。

void blk_set_stacking_limits(struct queue_limits *lim)

为堆叠设备设置默认限制

参数

struct queue_limits *lim

要重置的 queue_limits 结构

描述

准备队列限制,以便使用 blk_stack_limits() 应用来自底层设备的限制。

int queue_limits_commit_update(struct request_queue *q, struct queue_limits *lim)

提交队列限制的原子更新

参数

struct request_queue *q

要更新的队列

struct queue_limits *lim

要应用的限制

描述

将从 queue_limits_start_update() 获取并由调用者更新的 lim 中的限制应用到 q

如果成功,则返回 0;否则返回一个负的错误代码。

int queue_limits_set(struct request_queue *q, struct queue_limits *lim)

将队列限制应用于队列

参数

struct request_queue *q

要更新的队列

struct queue_limits *lim

要应用的限制

描述

将刚刚初始化到 qlim 中的限制应用到队列。要更新现有限制,请使用 queue_limits_start_update() 和 queue_limits_commit_update()

如果成功,则返回 0;否则返回一个负的错误代码。

int blk_stack_limits(struct queue_limits *t, struct queue_limits *b, sector_t start)

为堆叠设备调整 queue_limits

参数

struct queue_limits *t

堆叠驱动程序的限制(顶部设备)

struct queue_limits *b

底层队列的限制(底部,组件设备)

sector_t start

组件设备内的第一个数据扇区

描述

堆叠驱动程序(如 MD 和 DM)使用此函数来确保所有组件设备都具有兼容的块大小和对齐方式。堆叠驱动程序必须提供一个 queue_limits 结构(顶部),然后为所有组件(底部)设备迭代调用堆叠函数。堆叠函数将尝试合并这些值并确保正确的对齐方式。

如果顶部和底部 queue_limits 兼容,则返回 0。可能会调整顶部设备的块大小和对齐偏移量,以确保与底部设备对齐。如果不存在兼容的大小和对齐方式,则返回 -1,并且生成的顶部 queue_limits 将设置 misaligned 标志,以指示 alignment_offset 未定义。

void queue_limits_stack_bdev(struct queue_limits *t, struct block_device *bdev, sector_t offset, const char *pfx)

为堆叠设备调整 queue_limits

参数

struct queue_limits *t

堆叠驱动程序的限制(顶部设备)

struct block_device *bdev

底层的块设备(底部)

sector_t offset

组件设备内数据起始位置的偏移量

const char *pfx

用于记录警告的前缀

描述

堆叠驱动程序(如 MD 和 DM)使用此函数来确保所有组件设备都具有兼容的块大小和对齐方式。堆叠驱动程序必须提供一个 queue_limits 结构(顶部),然后为所有组件(底部)设备迭代调用堆叠函数。堆叠函数将尝试合并这些值并确保正确的对齐方式。

bool queue_limits_stack_integrity(struct queue_limits *t, struct queue_limits *b)

堆叠完整性配置文件

参数

struct queue_limits *t

目标队列限制

struct queue_limits *b

基本队列限制

描述

检查 b 中的完整性配置文件是否可以堆叠到目标 t 中。如果以下任一情况成立,则可以堆叠:

  1. 尚未在其上堆叠任何完整性信息

  2. b 中的完整性配置文件与 t 中的相同

如果 b 可以堆叠到 t 中,则返回 true。否则,返回 false 并清除 t 中的完整性信息。

void blk_set_queue_depth(struct request_queue *q, unsigned int depth)

告诉块层设备队列深度

参数

struct request_queue *q

设备的请求队列

unsigned int depth

队列深度

int blkdev_issue_flush(struct block_device *bdev)

排队一个刷新操作

参数

struct block_device *bdev

要为其发出刷新操作的块设备

描述

为有问题的块设备发出刷新操作。

int blkdev_issue_discard(struct block_device *bdev, sector_t sector, sector_t nr_sects, gfp_t gfp_mask)

排队一个丢弃操作

参数

struct block_device *bdev

要为其发出丢弃操作的块设备

sector_t sector

起始扇区

sector_t nr_sects

要丢弃的扇区数

gfp_t gfp_mask

内存分配标志(用于 bio_alloc)

描述

为有问题的扇区发出丢弃请求。

int __blkdev_issue_zeroout(struct block_device *bdev, sector_t sector, sector_t nr_sects, gfp_t gfp_mask, struct bio **biop, unsigned flags)

生成多个零填充写入 bio

参数

struct block_device *bdev

要发出的块设备

sector_t sector

起始扇区

sector_t nr_sects

要写入的扇区数

gfp_t gfp_mask

内存分配标志(用于 bio_alloc)

struct bio **biop

指向锚 bio 的指针

unsigned flags

控制详细行为

描述

使用硬件卸载或通过显式地将零写入设备来零填充一个块范围。

如果设备正在使用逻辑块配置,如果 flags 包含 BLKDEV_ZERO_NOUNMAP,则不会释放底层空间。

如果 flags 包含 BLKDEV_ZERO_NOFALLBACK,如果没有为零填充提供显式硬件卸载,则该函数将返回 -EOPNOTSUPP。

int blkdev_issue_zeroout(struct block_device *bdev, sector_t sector, sector_t nr_sects, gfp_t gfp_mask, unsigned flags)

零填充一个块范围

参数

struct block_device *bdev

要写入的块设备

sector_t sector

起始扇区

sector_t nr_sects

要写入的扇区数

gfp_t gfp_mask

内存分配标志(用于 bio_alloc)

unsigned flags

控制详细行为

描述

对块范围进行零填充,可以使用硬件卸载或显式地将零写入设备。有关 flags 的有效值,请参阅 __blkdev_issue_zeroout()

int blk_rq_map_integrity_sg(struct request *rq, struct scatterlist *sglist)

将完整性元数据映射到散列表

参数

struct request *rq

要映射的请求

struct scatterlist *sglist

目标散列表

描述

将请求中的完整性向量映射到散列表中。散列表必须足够大以容纳所有元素。即使用 blk_rq_count_integrity_sg() 或 rq->nr_integrity_segments 设置大小。

int blk_trace_ioctl(struct block_device *bdev, unsigned cmd, char __user *arg)

处理与跟踪相关的 ioctl

参数

struct block_device *bdev

块设备

unsigned cmd

ioctl 命令

char __user *arg

参数数据(如果有)

void blk_trace_shutdown(struct request_queue *q)

停止并清理跟踪结构

参数

struct request_queue *q

与设备关联的请求队列

void blk_add_trace_rq(struct request *rq, blk_status_t error, unsigned int nr_bytes, u32 what, u64 cgid)

为面向请求的操作添加跟踪

参数

struct request *rq

源请求

blk_status_t error

要记录的返回状态

unsigned int nr_bytes

已完成的字节数

u32 what

操作

u64 cgid

cgroup 信息

描述

记录针对请求的操作。将记录 bio 偏移量 + 大小。

void blk_add_trace_bio(struct request_queue *q, struct bio *bio, u32 what, int error)

为面向 bio 的操作添加跟踪

参数

struct request_queue *q

io 所属的队列

struct bio *bio

源 bio

u32 what

操作

int error

错误(如果有)

描述

记录针对 bio 的操作。将记录 bio 偏移量 + 大小。

void blk_add_trace_bio_remap(void *ignore, struct bio *bio, dev_t dev, sector_t from)

为 bio-remap 操作添加跟踪

参数

void *ignore

跟踪回调数据参数(未使用)

struct bio *bio

源 bio

dev_t dev

源设备

sector_t from

源扇区

描述

在 bio 重新映射到其他设备和/或扇区后调用。

void blk_add_trace_rq_remap(void *ignore, struct request *rq, dev_t dev, sector_t from)

为 request-remap 操作添加跟踪

参数

void *ignore

跟踪回调数据参数(未使用)

struct request *rq

源请求

dev_t dev

目标设备

sector_t from

源扇区

描述

设备映射器将请求重新映射到其他设备。为该操作添加跟踪。

void disk_release(struct device *dev)

释放 gendisk 的所有已分配资源

参数

struct device *dev

表示此磁盘的设备

描述

此函数释放 gendisk 的所有已分配资源。

使用 __device_add_disk() 的驱动程序具有分配了 request_queue 的 gendisk。由于这些驱动程序的 request_queue 位于 gendisk 之上,因此我们也为它们调用 blk_put_queue(),我们希望此时 request_queue 引用计数达到 0,因此 request_queue 也将在磁盘之前释放。

上下文

可以休眠

int __register_blkdev(unsigned int major, const char *name, void (*probe)(dev_t devt))

注册新的块设备

参数

unsigned int major

请求的主设备号 [1..BLKDEV_MAJOR_MAX-1]。如果 major = 0,则尝试分配任何未使用的主设备号。

const char *name

作为零终止字符串的新块设备的名称

void (*probe)(dev_t devt)

在访问预先创建的设备节点时,用于创建磁盘的 pre-devtmpfs/pre-udev 回调。当 probe 调用使用 add_disk() 并且失败时,驱动程序必须清理资源。此接口可能很快会被删除。

描述

name 在系统中必须是唯一的。

返回值取决于 major 输入参数

  • 如果在范围 [1..BLKDEV_MAJOR_MAX-1] 中请求了主设备号,则该函数在成功时返回零,或返回负错误代码

  • 如果使用 major = 0 参数请求了任何未使用的主设备号,则返回值是范围 [1..BLKDEV_MAJOR_MAX-1] 中分配的主设备号,否则返回负错误代码

有关已分配的主设备号的列表,请参阅 Linux 已分配设备(4.x+ 版本)

对于任何新代码,请使用 register_blkdev。

int add_disk_fwnode(struct device *parent, struct gendisk *disk, const struct attribute_group **groups, struct fwnode_handle *fwnode)

将磁盘信息添加到内核列表,并附加 fwnode。

参数

struct device *parent

磁盘的父设备

struct gendisk *disk

每个设备的独立分区信息

const struct attribute_group **groups

额外的每个设备的 sysfs 组

struct fwnode_handle *fwnode

附加的磁盘 fwnode

描述

此函数将 **disk** 中的分区信息注册到内核。同时将 fwnode 附加到磁盘设备。

int device_add_disk(struct device *parent, struct gendisk *disk, const struct attribute_group **groups)

将磁盘信息添加到内核列表。

参数

struct device *parent

磁盘的父设备

struct gendisk *disk

每个设备的独立分区信息

const struct attribute_group **groups

额外的每个设备的 sysfs 组

描述

此函数将 **disk** 中的分区信息注册到内核。

void blk_mark_disk_dead(struct gendisk *disk)

将磁盘标记为 dead。

参数

struct gendisk *disk

要标记为 dead 的磁盘。

描述

将磁盘标记为 dead(例如,意外移除),并且不再接受对该磁盘的任何新的 I/O。

void del_gendisk(struct gendisk *disk)

移除 gendisk。

参数

struct gendisk *disk

要移除的 struct gendisk。

描述

移除 gendisk 及其所有相关资源。 这会删除与 gendisk 关联的分区,并注销关联的 request_queue。

这是对相应的 __device_add_disk() 调用的计数器。

当其引用计数达到 0 时,使用 put_disk() 进行 struct gendisk 的最终移除,如果使用了 __device_add_disk(),则应在 del_gendisk() 之后调用。

存在依赖于 gendisk 的同步释放的驱动程序,它不应被延迟。

上下文

可以休眠

void invalidate_disk(struct gendisk *disk)

使磁盘失效。

参数

struct gendisk *disk

要使其失效的 struct gendisk。

描述

用于使磁盘失效的辅助函数。它将清除磁盘关联的缓冲区/页面缓存,并重置其内部状态,以便驱动程序可以重用该磁盘。

上下文

可以休眠

void put_disk(struct gendisk *disk)

递减 gendisk 的引用计数。

参数

struct gendisk *disk

要递减引用计数的 struct gendisk。

描述

这将递减 struct gendisk 的引用计数。当它达到 0 时,我们将调用 disk_release()

注意

对于 blk-mq 磁盘,在处理探测错误时(即在调用 add_disk() 之前),必须在释放 tag_set 之前调用 put_disk。

上下文

任何上下文,但最后一个引用不得从原子上下文中删除。

void set_disk_ro(struct gendisk *disk, bool read_only)

设置 gendisk 为只读。

参数

struct gendisk *disk

要操作的 gendisk。

bool read_only

true 设置磁盘为只读,false 设置磁盘为读/写。

描述

此函数用于指示给定的磁盘设备是否应设置其只读标志。设备驱动程序通常使用 set_disk_ro() 来指示底层物理设备是否受到写保护。

int bdev_freeze(struct block_device *bdev)

锁定文件系统并强制其进入一致状态。

参数

struct block_device *bdev

要锁定的块设备。

描述

如果在该设备上找到超级块,我们会在其上使用 s_umount 信号量,以确保在快照创建完成之前没有人卸载。引用计数器 (bd_fsfreeze_count) 保证,当多个冻结请求同时到达时,只有最后一个解冻进程才能真正解冻冻结的文件系统。它在 bdev_freeze() 中向上计数,在 bdev_thaw() 中向下计数。当它变为 0 时,thaw_bdev() 将真正解冻。

返回

成功时返回 0,失败时返回负错误代码。

int bdev_thaw(struct block_device *bdev)

解锁文件系统。

参数

struct block_device *bdev

要解锁的块设备。

描述

bdev_freeze() 之后解锁文件系统并将其再次标记为可写。

返回

成功时返回 0,失败时返回负错误代码。

int bd_prepare_to_claim(struct block_device *bdev, void *holder, const struct blk_holder_ops *hops)

声明一个块设备。

参数

struct block_device *bdev

感兴趣的块设备。

void *holder

尝试声明 **bdev** 的持有者。

const struct blk_holder_ops *hops

持有者操作。

描述

声明 **bdev**。如果 **bdev** 已被另一个持有者声明,则此函数会失败,如果另一个声明正在进行中,则会等待。 返回时,调用者拥有 bd_claiming 和 bd_holder[s] 的所有权。

返回

如果可以声明 **bdev**,则为 0,否则为 -EBUSY。

void bd_abort_claiming(struct block_device *bdev, void *holder)

中止对块设备的声明。

参数

struct block_device *bdev

感兴趣的块设备。

void *holder

已声明 **bdev** 的持有者。

描述

当独占打开失败时,中止对块设备的声明。这也可以用于实际上不需要独占打开,而只是需要暂时阻止其他独占打开者的情况。

void bdev_fput(struct file *bdev_file)

放弃对块设备的声明并释放文件

参数

struct file *bdev_file

打开块设备

描述

放弃对块设备的声明并释放文件。确保块设备在文件关闭(这是一个延迟操作)之前可以被回收。

int lookup_bdev(const char *pathname, dev_t *dev)

通过名称查找一个 struct block_device。

参数

const char *pathname

文件系统中块设备的名称。

dev_t *dev

指向找到的块设备的 dev_t 的指针。

描述

如果可能,在当前命名空间中查找 **pathname** 处的块设备的 dev_t,并在 **dev** 中返回它。

上下文

可能会休眠。

返回

成功返回 0,否则返回负的 errno。

void bdev_mark_dead(struct block_device *bdev, bool surprise)

将块设备标记为失效

参数

struct block_device *bdev

要操作的块设备

bool surprise

表示意外移除

描述

告知文件系统此设备或介质已失效。如果 **surprise** 设置为 true,则设备或介质已消失,否则我们正在准备有序移除。

这会调用文件系统,然后文件系统通常会同步所有脏数据并写回 inode,然后使文件系统上 inode 中的任何缓存数据无效。此外,我们还会使块设备映射无效。

字符设备

int register_chrdev_region(dev_t from, unsigned count, const char *name)

注册一系列设备号

参数

dev_t from

所需设备号范围中的第一个;必须包含主设备号。

unsigned count

所需的连续设备号的数量

const char *name

设备或驱动程序的名称。

描述

成功返回零,失败返回负错误代码。

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

注册一系列字符设备号

参数

dev_t *dev

第一个分配的号码的输出参数

unsigned baseminor

请求的次设备号范围中的第一个

unsigned count

所需的次设备号的数量

const char *name

相关设备或驱动程序的名称

描述

分配一系列字符设备号。主设备号将动态选择,并在 **dev** 中返回(以及第一个次设备号)。返回零或负错误代码。

int __register_chrdev(unsigned int major, unsigned int baseminor, unsigned int count, const char *name, const struct file_operations *fops)

创建并注册一个占用一系列次设备的 cdev

参数

unsigned int major

主设备号,如果动态分配则为 0

unsigned int baseminor

请求的次设备号范围中的第一个

unsigned int count

所需的次设备号的数量

const char *name

此设备范围的名称

const struct file_operations *fops

与此设备关联的文件操作

描述

如果 **major** == 0,此函数将动态分配一个主设备号并返回其编号。

如果 **major** > 0,此函数将尝试保留具有给定主设备号的设备,并在成功时返回零。

失败时返回负的 errno。

此设备的名称与 /dev 中设备的名称无关。它仅有助于跟踪设备的不同所有者。如果您的模块名称只有一种类型的设备,则可以在此处使用模块的名称,例如。

void unregister_chrdev_region(dev_t from, unsigned count)

注销一系列设备号

参数

dev_t from

要注销的设备号范围中的第一个

unsigned count

要注销的设备号的数量

描述

此函数将注销一系列 **count** 设备号,从 **from** 开始。调用者通常应该是最初分配这些号码的人...

void __unregister_chrdev(unsigned int major, unsigned int baseminor, unsigned int count, const char *name)

注销并销毁 cdev

参数

unsigned int major

主设备号

unsigned int baseminor

次设备号范围中的第一个

unsigned int count

此 cdev 占用的次设备号的数量

const char *name

此设备范围的名称

描述

注销并销毁占用由 **major**、**baseminor** 和 **count** 描述的区域的 cdev。此函数撤消了 __register_chrdev() 所做的事情。

int cdev_add(struct cdev *p, dev_t dev, unsigned count)

向系统添加字符设备

参数

struct cdev *p

设备的 cdev 结构

dev_t dev

此设备负责的第一个设备号

unsigned count

与此设备对应的连续次设备号的数量

描述

cdev_add() 将 **p** 表示的设备添加到系统,使其立即生效。失败时返回负错误代码。

void cdev_set_parent(struct cdev *p, struct kobject *kobj)

为字符设备设置父 kobject

参数

struct cdev *p

cdev 结构

struct kobject *kobj

要获取引用的 kobject

描述

cdev_set_parent() 设置一个父 kobject,它将被适当地引用,以便在 cdev 之前不会释放父 kobject。这应该在 cdev_add 之前调用。

int cdev_device_add(struct cdev *cdev, struct device *dev)

添加一个字符设备及其对应的 struct device,并进行链接

参数

struct cdev *cdev

cdev 结构

struct device *dev

设备结构体

描述

cdev_device_add()cdev 表示的字符设备添加到系统,就像 cdev_add 所做的那样。然后,它使用 device_add 将 dev 添加到系统。字符设备的 dev_t 将从 struct device 中获取,该结构需要先初始化。这个辅助函数会正确地获取父设备的引用,因此在释放对 cdev 的所有引用之前,父设备不会被释放。

此辅助函数使用 dev->devt 作为设备号。如果它未设置,则不会添加 cdev,并且它将等同于 device_add。

只要 struct cdev 和 struct device 是同一结构体的成员,并且该结构体的生命周期由 struct device 管理时,就应使用此函数。

注意

调用者必须假定用户空间能够打开 cdev,并且可以随时调用 cdev fops 回调函数,即使此函数失败也是如此。

void cdev_device_del(struct cdev *cdev, struct device *dev)

cdev_device_add 的逆操作

参数

struct cdev *cdev

cdev 结构

struct device *dev

设备结构体

描述

cdev_device_del() 是一个调用 cdev_del 和 device_del 的辅助函数。只要使用了 cdev_device_add 就应该使用它。

如果 dev->devt 未设置,则它将不会删除 cdev,并且将等同于 device_del。

注意

这保证了相关的 sysfs 回调不会运行或可运行,但是任何已打开的 cdev 都将保留,并且即使在此函数返回后,它们的 fops 仍然可以调用。

void cdev_del(struct cdev *p)

从系统中删除一个 cdev

参数

struct cdev *p

要删除的 cdev 结构体

描述

cdev_del() 从系统中删除 p,可能会释放结构体本身。

注意

这保证了 cdev 设备将不再能够被打开,但是任何已打开的 cdev 都将保留,并且即使在 cdev_del 返回后,它们的 fops 仍然可以调用。

struct cdev *cdev_alloc(void)

分配一个 cdev 结构体

参数

void

无参数

描述

分配并返回一个 cdev 结构体,失败时返回 NULL。

void cdev_init(struct cdev *cdev, const struct file_operations *fops)

初始化一个 cdev 结构体

参数

struct cdev *cdev

要初始化的结构体

const struct file_operations *fops

此设备的文件操作

描述

初始化 cdev,记住 fops,使其可以使用 cdev_add() 添加到系统。

时钟框架

时钟框架定义了编程接口,以支持系统时钟树的软件管理。此框架广泛用于片上系统 (SOC) 平台,以支持电源管理和可能需要自定义时钟速率的各种设备。请注意,这些“时钟”与计时或实时时钟 (RTC) 无关,每个时钟都有单独的框架。这些 struct clk 实例可用于管理例如 96 MHz 信号,该信号用于将位移入和移出外围设备或总线,或以其他方式触发系统硬件中的同步状态机转换。

显式软件时钟门控支持电源管理:禁用未使用的时钟,因此系统不会浪费功率来更改未处于活动使用状态的晶体管的状态。在某些系统上,这可能由硬件时钟门控支持,其中时钟在不禁用软件的情况下被门控。芯片中供电但未计时的部分可能能够保留其最后的状态。这种低功耗状态通常称为保持模式。这种模式仍然会产生泄漏电流,尤其是在更精细的电路几何形状下,但是对于 CMOS 电路,功率主要用于计时状态变化。

具有电源意识的驱动程序仅在其管理的设备处于活动使用状态时才启用其时钟。此外,系统睡眠状态通常根据哪些时钟域处于活动状态而有所不同:虽然“待机”状态可能允许从多个活动域唤醒,但“mem”(挂起到 RAM)状态可能需要更彻底地关闭从高速 PLL 和振荡器派生的时钟,从而限制了可能的唤醒事件源的数量。驱动程序的挂起方法可能需要了解目标睡眠状态下特定于系统的时钟约束。

一些平台支持可编程时钟发生器。这些可以被各种外部芯片使用,例如其他 CPU、多媒体编解码器以及对接口时钟有严格要求的设备。

struct clk_notifier

将 clk 与通知器关联

定义:

struct clk_notifier {
    struct clk                      *clk;
    struct srcu_notifier_head       notifier_head;
    struct list_head                node;
};

成员

clk

要与之关联通知器的 struct clk *

notifier_head

此 clk 的阻塞通知器头

node

链表指针

描述

通知器代码维护着一个 struct clk_notifier 列表。每当代码在特定的 clk 上注册第一个通知器时,就会创建一个条目。该 clk 上的未来通知器将添加到 notifier_head

struct clk_notifier_data

传递给通知器回调的速率数据

定义:

struct clk_notifier_data {
    struct clk              *clk;
    unsigned long           old_rate;
    unsigned long           new_rate;
};

成员

clk

正在更改的 struct clk *

old_rate

此 clk 的先前速率

new_rate

此 clk 的新速率

描述

对于前通知器,old_rate 是此速率更改之前 clk 的速率,而 new_rate 是未来的速率。对于后通知器,old_rate 和 new_rate 都设置为 clk 的当前速率(这样做是为了优化实现)。

struct clk_bulk_data

用于批量 clk 操作的数据。

定义:

struct clk_bulk_data {
    const char              *id;
    struct clk              *clk;
};

成员

id

时钟使用者 ID

clk

存储关联时钟的 struct clk *

描述

CLK API 提供了一系列 clk_bulk_() API 调用,方便需要多个 clk 的使用者。此结构用于管理这些调用的数据。

int clk_notifier_register(struct clk *clk, struct notifier_block *nb)

注册时钟速率更改通知器回调

参数

struct clk *clk

我们感兴趣的速率的时钟

struct notifier_block *nb

带有回调函数指针的通知器块

描述

专家提示:跨通知器链调试可能会令人沮丧。请确保您的通知器回调函数在出现故障时打印一个清晰的警告。

int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb)

注销时钟速率更改通知器回调

参数

struct clk *clk

我们不再感兴趣的速率的时钟

struct notifier_block *nb

将被注销的通知器块

int devm_clk_notifier_register(struct device *dev, struct clk *clk, struct notifier_block *nb)

注册一个托管的速率变化通知回调

参数

struct device *dev

时钟“消费者”的设备

struct clk *clk

我们感兴趣的速率的时钟

struct notifier_block *nb

带有回调函数指针的通知器块

描述

成功时返回 0,否则返回 -EERROR

long clk_get_accuracy(struct clk *clk)

获取时钟源的精度,以 ppb(十亿分率)为单位。

参数

struct clk *clk

时钟源

描述

这将获取以 ppb 表示的时钟源精度。一个完美时钟返回 0。

int clk_set_phase(struct clk *clk, int degrees)

调整时钟信号的相位偏移

参数

struct clk *clk

时钟信号源

int degrees

信号偏移的度数

描述

将时钟信号的相位偏移指定的度数。成功时返回 0,否则返回 -EERROR。

int clk_get_phase(struct clk *clk)

返回时钟信号的相位偏移

参数

struct clk *clk

时钟信号源

描述

返回时钟节点的相位偏移,以度为单位;否则返回 -EERROR。

int clk_set_duty_cycle(struct clk *clk, unsigned int num, unsigned int den)

调整时钟信号的占空比

参数

struct clk *clk

时钟信号源

unsigned int num

要应用的占空比的分子

unsigned int den

要应用的占空比的分母

描述

通过指定比例调整时钟信号的占空比。成功时返回 0,否则返回 -EERROR。

int clk_get_scaled_duty_cycle(struct clk *clk, unsigned int scale)

返回时钟信号的占空比

参数

struct clk *clk

时钟信号源

unsigned int scale

应用于将比例表示为整数的缩放因子

描述

返回乘以提供的缩放因子的占空比,否则返回 -EERROR。

bool clk_is_match(const struct clk *p, const struct clk *q)

检查两个 clk 是否指向同一个硬件时钟

参数

const struct clk *p

与 q 比较的 clk

const struct clk *q

与 p 比较的 clk

描述

如果两个 struct clk 指针都指向同一个硬件时钟节点,则返回 true。换句话说,如果 **p** 和 **q** 共享同一个 struct clk_core 对象,则返回 true。

否则返回 false。请注意,两个 NULL clk 被视为匹配。

int clk_rate_exclusive_get(struct clk *clk)

获取生产者速率控制的独占权

参数

struct clk *clk

时钟源

描述

此函数允许驱动程序获得对提供者速率的独占控制权。它阻止任何其他使用者执行,即使是间接执行,可能改变提供者速率或导致故障的操作。

如果多次在时钟上声明独占权,即使是同一个驱动程序,速率也会被有效地锁定,因为独占权无法被抢占。

不能从原子上下文中调用。

返回成功 (0) 或负的 errno。

int devm_clk_rate_exclusive_get(struct device *dev, struct clk *clk)

clk_rate_exclusive_get 的 devm 变体

参数

struct device *dev

独占权绑定的设备

struct clk *clk

时钟源

描述

在 **clk** 上调用 clk_rate_exclusive_get(),并在 **dev** 上注册一个 devm 清理处理程序以调用 clk_rate_exclusive_put()

不能从原子上下文中调用。

void clk_rate_exclusive_put(struct clk *clk)

释放对生产者速率控制的独占权

参数

struct clk *clk

时钟源

描述

此函数允许驱动程序释放之前从 clk_rate_exclusive_get() 获取的独占权。

调用者必须平衡 clk_rate_exclusive_get()clk_rate_exclusive_put() 调用的数量。

不能从原子上下文中调用。

int clk_prepare(struct clk *clk)

准备时钟源

参数

struct clk *clk

时钟源

描述

这会准备好时钟源以供使用。

不能从原子上下文中调用。

bool clk_is_enabled_when_prepared(struct clk *clk)

指示准备时钟是否也会启用它。

参数

struct clk *clk

时钟源

描述

如果 clk_prepare() 隐式启用时钟,从而有效地使 clk_enable()/clk_disable() 无操作,则返回 true,否则返回 false。

这主要对电源管理代码有意义,因为实际上禁用时钟也需要取消准备,才能产生任何实质性影响。

无论此处返回的值是什么,调用者都必须始终调用 clk_enable() 或 clk_prepare_enable() 及其对应的函数,以确保使用计数正确。

void clk_unprepare(struct clk *clk)

撤销时钟源的准备工作

参数

struct clk *clk

时钟源

描述

此操作会撤销先前准备的时钟。调用者必须平衡准备和取消准备的调用次数。

不能从原子上下文中调用。

struct clk *clk_get(struct device *dev, const char *id)

查找并获取对时钟生成器的引用。

参数

struct device *dev

时钟“消费者”的设备

const char *id

时钟使用者 ID

描述

返回与时钟生成器对应的 struct clk,或包含 errno 的有效 IS_ERR() 条件。该实现使用 devid 来确定时钟使用者,从而确定时钟生成器。(换句话说,id 可能是相同的字符串,但 clk_get 可能会根据 dev 返回不同的时钟生成器。)

驱动程序必须假设时钟源未启用。

不应从中断上下文中调用 clk_get。

int clk_bulk_get(struct device *dev, int num_clks, struct clk_bulk_data *clks)

查找并获取对时钟生成器的多个引用。

参数

struct device *dev

时钟“消费者”的设备

int num_clks

clk_bulk_data 的数量

struct clk_bulk_data *clks

消费者的 clk_bulk_data 表

描述

此辅助函数允许驱动程序在一次操作中获取多个 clk 使用者。如果任何 clk 无法获取,则会在返回调用者之前释放任何已获取的 clk。

如果成功获取 clk_bulk_data 表中指定的所有时钟,则返回 0,否则返回包含 errno 的有效 IS_ERR() 条件。该实现使用 devclk_bulk_data.id 来确定时钟使用者,从而确定时钟生成器。返回的时钟存储在每个 clk_bulk_data.clk 字段中。

驱动程序必须假设时钟源未启用。

不应从中断上下文中调用 clk_bulk_get。

int clk_bulk_get_all(struct device *dev, struct clk_bulk_data **clks)

查找并获取对时钟生成器的所有可用引用。

参数

struct device *dev

时钟“消费者”的设备

struct clk_bulk_data **clks

指向消费者的 clk_bulk_data 表的指针

描述

此辅助函数允许驱动程序在一次操作中获取所有 clk 使用者。如果任何 clk 无法获取,则会在返回调用者之前释放任何已获取的 clk。

返回获取的时钟数量的正值,同时时钟引用存储在 clks 字段中的 clk_bulk_data 表中。如果没有,则返回 0,如果出现错误,则返回负值。

驱动程序必须假设时钟源未启用。

不应从中断上下文中调用 clk_bulk_get。

int clk_bulk_get_optional(struct device *dev, int num_clks, struct clk_bulk_data *clks)

查找并获取对时钟生成器的多个引用

参数

struct device *dev

时钟“消费者”的设备

int num_clks

clk_bulk_data 的数量

struct clk_bulk_data *clks

消费者的 clk_bulk_data 表

描述

行为与 clk_bulk_get() 相同,只是在没有时钟生成器的情况下。在这种情况下,函数不会返回 -ENOENT,而是返回 0,并且对于无法确定时钟生成器的 clk 返回 NULL。

int devm_clk_bulk_get(struct device *dev, int num_clks, struct clk_bulk_data *clks)

托管获取多个 clk 使用者

参数

struct device *dev

时钟“消费者”的设备

int num_clks

clk_bulk_data 的数量

struct clk_bulk_data *clks

消费者的 clk_bulk_data 表

描述

成功返回 0,失败返回错误号。

此辅助函数允许驱动程序在一次操作中获取多个带有管理的 clk 使用者,当设备解除绑定时,这些 clk 将自动释放。

int devm_clk_bulk_get_optional(struct device *dev, int num_clks, struct clk_bulk_data *clks)

托管获取多个可选的消费者时钟

参数

struct device *dev

时钟“消费者”的设备

int num_clks

clk_bulk_data 的数量

struct clk_bulk_data *clks

指向消费者的 clk_bulk_data 表的指针

描述

行为与 devm_clk_bulk_get() 相同,只是在没有时钟生成器的情况下。在这种情况下,对于给定的 clk,函数不会返回 -ENOENT,而是返回 NULL。假设 clk_bulk_data 中的所有时钟都是可选的。

如果成功获取 clk_bulk_data 表中指定的所有时钟,或者对于任何 clk 都没有可用的 clk 提供程序,则返回 0,否则返回包含 errno 的有效 IS_ERR() 条件。该实现使用 devclk_bulk_data.id 来确定时钟使用者,从而确定时钟生成器。返回的时钟存储在每个 clk_bulk_data.clk 字段中。

驱动程序必须假设时钟源未启用。

不应从中断上下文中调用 clk_bulk_get。

int devm_clk_bulk_get_all(struct device *dev, struct clk_bulk_data **clks)

托管获取多个 clk 使用者

参数

struct device *dev

时钟“消费者”的设备

struct clk_bulk_data **clks

指向消费者的 clk_bulk_data 表的指针

描述

返回获取的时钟数量的正值,同时时钟引用存储在 clks 字段中的 clk_bulk_data 表中。如果没有,则返回 0,如果出现错误,则返回负值。

此辅助函数允许驱动程序在一次操作中获取多个带有管理的 clk 使用者,当设备解除绑定时,这些 clk 将自动释放。

int devm_clk_bulk_get_all_enabled(struct device *dev, struct clk_bulk_data **clks)

获取并启用消费者(托管)的所有时钟

参数

struct device *dev

时钟“消费者”的设备

struct clk_bulk_data **clks

指向消费者的 clk_bulk_data 表的指针

描述

返回获取的时钟数量的正值,同时时钟引用存储在 clks 字段中的 clk_bulk_data 表中。如果没有,则返回 0,如果出现错误,则返回负值。

这个辅助函数允许驱动程序获取消费者的所有时钟,并在一个操作中启用它们并进行管理。当设备解除绑定时,clks 将自动禁用并释放。

struct clk *devm_clk_get(struct device *dev, const char *id)

查找并获取对时钟生产者的托管引用。

参数

struct device *dev

时钟“消费者”的设备

const char *id

时钟使用者 ID

上下文

可能会休眠。

返回

与时钟生产者对应的 struct clk,或者包含 errno 的有效 IS_ERR() 条件。该实现使用 **dev** 和 **id** 来确定时钟消费者,从而确定时钟生产者。(也就是说,**id** 可能是相同的字符串,但 clk_get 可能会根据 **dev** 返回不同的时钟生产者。)

描述

驱动程序必须假定时钟源既没有准备好也没有启用。

当设备从总线解除绑定时,时钟将自动释放。

struct clk *devm_clk_get_prepared(struct device *dev, const char *id)

devm_clk_get() + clk_prepare()

参数

struct device *dev

时钟“消费者”的设备

const char *id

时钟使用者 ID

上下文

可能会休眠。

返回

与时钟生产者对应的 struct clk,或者包含 errno 的有效 IS_ERR() 条件。该实现使用 **dev** 和 **id** 来确定时钟消费者,从而确定时钟生产者。(也就是说,**id** 可能是相同的字符串,但 clk_get 可能会根据 **dev** 返回不同的时钟生产者。)

描述

返回的 clk(如果有效)已准备好。但是,驱动程序必须假定时钟未启用。

当设备从总线解除绑定时,时钟将自动取消准备并释放。

struct clk *devm_clk_get_enabled(struct device *dev, const char *id)

devm_clk_get() + clk_prepare_enable()

参数

struct device *dev

时钟“消费者”的设备

const char *id

时钟使用者 ID

上下文

可能会休眠。

返回

与时钟生产者对应的 struct clk,或者包含 errno 的有效 IS_ERR() 条件。该实现使用 **dev** 和 **id** 来确定时钟消费者,从而确定时钟生产者。(也就是说,**id** 可能是相同的字符串,但 clk_get 可能会根据 **dev** 返回不同的时钟生产者。)

描述

返回的 clk(如果有效)已准备好并启用。

当设备从总线解除绑定时,时钟将自动禁用、取消准备并释放。

struct clk *devm_clk_get_optional(struct device *dev, const char *id)

查找并获取对可选时钟生产者的托管引用。

参数

struct device *dev

时钟“消费者”的设备

const char *id

时钟使用者 ID

上下文

可能会休眠。

返回

与时钟生产者对应的 struct clk,或者包含 errno 的有效 IS_ERR() 条件。该实现使用 **dev** 和 **id** 来确定时钟消费者,从而确定时钟生产者。如果找不到这样的 clk,则返回 NULL,作为虚拟 clk。这是与 devm_clk_get() 的唯一区别。

描述

驱动程序必须假定时钟源既没有准备好也没有启用。

当设备从总线解除绑定时,时钟将自动释放。

struct clk *devm_clk_get_optional_prepared(struct device *dev, const char *id)

devm_clk_get_optional() + clk_prepare()

参数

struct device *dev

时钟“消费者”的设备

const char *id

时钟使用者 ID

上下文

可能会休眠。

返回

与时钟生产者对应的 struct clk,或者包含 errno 的有效 IS_ERR() 条件。该实现使用 **dev** 和 **id** 来确定时钟消费者,从而确定时钟生产者。如果找不到这样的 clk,则返回 NULL,作为虚拟 clk。这是与 devm_clk_get_prepared() 的唯一区别。

描述

返回的 clk(如果有效)已准备好。但是,驱动程序必须假定时钟未启用。

当设备从总线解除绑定时,时钟将自动取消准备并释放。

struct clk *devm_clk_get_optional_enabled(struct device *dev, const char *id)

devm_clk_get_optional() + clk_prepare_enable()

参数

struct device *dev

时钟“消费者”的设备

const char *id

时钟使用者 ID

上下文

可能会休眠。

返回

与时钟生产者对应的 struct clk,或者包含 errno 的有效 IS_ERR() 条件。该实现使用 **dev** 和 **id** 来确定时钟消费者,从而确定时钟生产者。如果找不到这样的 clk,则返回 NULL,作为虚拟 clk。这是与 devm_clk_get_enabled() 的唯一区别。

描述

返回的 clk(如果有效)已准备好并启用。

当设备从总线解除绑定时,时钟将自动禁用、取消准备并释放。

struct clk *devm_clk_get_optional_enabled_with_rate(struct device *dev, const char *id, unsigned long rate)

devm_clk_get_optional() + clk_set_rate() + clk_prepare_enable()

参数

struct device *dev

时钟“消费者”的设备

const char *id

时钟使用者 ID

unsigned long rate

新的时钟频率

上下文

可能会休眠。

返回

与时钟生产者对应的 struct clk,或者包含 errno 的有效 IS_ERR() 条件。该实现使用 **dev** 和 **id** 来确定时钟消费者,从而确定时钟生产者。如果找不到这样的 clk,则返回 NULL,作为虚拟 clk。这是与 devm_clk_get_enabled() 的唯一区别。

描述

返回的 clk(如果有效)已准备好并启用,并且已设置频率。

当设备从总线解除绑定时,时钟将自动禁用、取消准备并释放。

struct clk *devm_get_clk_from_child(struct device *dev, struct device_node *np, const char *con_id)

从子节点查找并获取对时钟生产者的托管引用。

参数

struct device *dev

时钟“消费者”的设备

struct device_node *np

指向时钟消费者节点的指针

const char *con_id

时钟使用者 ID

描述

此函数解析时钟,并使用它们通过使用 **np** 和 **con_id** 从注册的时钟提供程序列表中查找 struct clk

当设备从总线解除绑定时,时钟将自动释放。

int clk_enable(struct clk *clk)

通知系统何时应该运行时钟源。

参数

struct clk *clk

时钟源

描述

如果时钟无法启用/禁用,则应返回成功。

可以从原子上下文中调用。

返回成功 (0) 或负的 errno。

int clk_bulk_enable(int num_clks, const struct clk_bulk_data *clks)

通知系统何时应该运行一组时钟。

参数

int num_clks

clk_bulk_data 的数量

const struct clk_bulk_data *clks

消费者的 clk_bulk_data 表

描述

可以从原子上下文中调用。

返回成功 (0) 或负的 errno。

void clk_disable(struct clk *clk)

通知系统何时不再需要时钟源。

参数

struct clk *clk

时钟源

描述

通知系统驱动程序不再需要时钟源,可以将其关闭。

可以从原子上下文中调用。

实现细节:如果时钟源在多个驱动程序之间共享,则必须通过相同数量的 clk_disable() 调用来平衡 clk_enable() 调用,才能禁用时钟源。

void clk_bulk_disable(int num_clks, const struct clk_bulk_data *clks)

通知系统何时不再需要一组时钟。

参数

int num_clks

clk_bulk_data 的数量

const struct clk_bulk_data *clks

消费者的 clk_bulk_data 表

描述

通知系统驱动程序不再需要一组时钟,可以将其关闭。

可以从原子上下文中调用。

实现细节:如果一组时钟在多个驱动程序之间共享,则必须通过相同数量的 clk_bulk_disable() 调用来平衡 clk_bulk_enable() 调用,才能禁用时钟源。

unsigned long clk_get_rate(struct clk *clk)

获取时钟源的当前时钟速率(以 Hz 为单位)。只有在启用时钟源后,此值才有效。

参数

struct clk *clk

时钟源

void clk_put(struct clk *clk)

“释放”时钟源

参数

struct clk *clk

时钟源

注意

驱动程序必须确保在此时钟源上进行的所有 clk_enable 调用都通过 clk_disable 调用进行平衡,然后再调用此函数。

描述

不应在中断上下文中调用 clk_put。

void clk_bulk_put(int num_clks, struct clk_bulk_data *clks)

“释放”时钟源

参数

int num_clks

clk_bulk_data 的数量

struct clk_bulk_data *clks

消费者的 clk_bulk_data 表

注意

驱动程序必须确保在此时钟源上进行的所有 clk_bulk_enable 调用都通过 clk_bulk_disable 调用进行平衡,然后再调用此函数。

描述

不应在中断上下文中调用 clk_bulk_put。

void clk_bulk_put_all(int num_clks, struct clk_bulk_data *clks)

“释放”所有时钟源

参数

int num_clks

clk_bulk_data 的数量

struct clk_bulk_data *clks

消费者的 clk_bulk_data 表

注意

驱动程序必须确保在此时钟源上进行的所有 clk_bulk_enable 调用都通过 clk_bulk_disable 调用进行平衡,然后再调用此函数。

描述

不应在中断上下文中调用 clk_bulk_put_all。

void devm_clk_put(struct device *dev, struct clk *clk)

“释放”一个托管的时钟源

参数

struct device *dev

用于获取时钟的设备

struct clk *clk

使用 devm_clk_get() 获取的时钟源

注意

驱动程序必须确保在此时钟源上进行的所有 clk_enable 调用都通过 clk_disable 调用进行平衡,然后再调用此函数。

描述

不应在中断上下文中调用 clk_put。

long clk_round_rate(struct clk *clk, unsigned long rate)

将速率调整为时钟可以提供的精确速率

参数

struct clk *clk

时钟源

unsigned long rate

所需的时钟速率,以 Hz 为单位

描述

这回答了“如果我将 rate 传递给 clk_set_rate(),我最终会得到什么时钟速率?”这个问题,而不会以任何方式更改硬件。换句话说

rate = clk_round_rate(clk, r);

clk_set_rate(clk, r); rate = clk_get_rate(clk);

是等效的,只是前者不会以任何方式修改时钟硬件。

返回以 Hz 为单位的舍入时钟速率,或负的 errno。

int clk_set_rate(struct clk *clk, unsigned long rate)

设置时钟源的时钟速率

参数

struct clk *clk

时钟源

unsigned long rate

所需的时钟速率,以 Hz 为单位

描述

更新速率从受影响的最顶层时钟开始,然后遍历树向下到达需要更新的最底层时钟。

返回成功 (0) 或负的 errno。

int clk_set_rate_exclusive(struct clk *clk, unsigned long rate)

设置时钟速率并声明对时钟源的独占权

参数

struct clk *clk

时钟源

unsigned long rate

所需的时钟速率,以 Hz 为单位

描述

此辅助函数允许驱动程序原子地设置生成器的速率,并声明对生成器速率控制的独占权。

它本质上是 clk_set_rate() 和 clk_rate_exclusite_get() 的组合。调用方必须使用对 clk_rate_exclusive_put() 的调用来平衡此调用

返回成功 (0) 或负的 errno。

bool clk_has_parent(const struct clk *clk, const struct clk *parent)

检查一个时钟是否是另一个时钟的可能父时钟

参数

const struct clk *clk

时钟源

const struct clk *parent

父时钟源

描述

此函数可用于需要检查时钟是否可以是另一个时钟的父时钟的驱动程序,而无需实际更改父时钟。

如果 parentclk 的可能父时钟,则返回 true,否则返回 false。

int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max)

为时钟源设置速率范围

参数

struct clk *clk

时钟源

unsigned long min

期望的最小时钟速率,单位为 Hz,包含边界值。

unsigned long max

期望的最大时钟速率,单位为 Hz,包含边界值。

描述

返回成功 (0) 或负的 errno。

int clk_set_min_rate(struct clk *clk, unsigned long rate)

为时钟源设置最小时钟速率

参数

struct clk *clk

时钟源

unsigned long rate

期望的最小时钟速率,单位为 Hz,包含边界值。

描述

返回成功 (0) 或负的 errno。

int clk_set_max_rate(struct clk *clk, unsigned long rate)

为时钟源设置最大时钟速率

参数

struct clk *clk

时钟源

unsigned long rate

期望的最大时钟速率,单位为 Hz,包含边界值。

描述

返回成功 (0) 或负的 errno。

int clk_set_parent(struct clk *clk, struct clk *parent)

为此时钟设置父时钟源

参数

struct clk *clk

时钟源

struct clk *parent

父时钟源

描述

返回成功 (0) 或负的 errno。

struct clk *clk_get_parent(struct clk *clk)

获取此时钟的父时钟源

参数

struct clk *clk

时钟源

描述

返回与父时钟源对应的 struct clk,或包含 errno 的有效 IS_ERR() 条件。

struct clk *clk_get_sys(const char *dev_id, const char *con_id)

根据设备名称获取时钟

参数

const char *dev_id

设备名称

const char *con_id

连接 ID

描述

返回与时钟生产者对应的 struct clk,或包含 errno 的有效 IS_ERR() 条件。 该实现使用 dev_idcon_id 来确定时钟消费者,从而确定时钟生产者。与 clk_get() 不同,此函数使用设备名称而不是设备本身进行标识。

驱动程序必须假设时钟源未启用。

不应从中断上下文内部调用 clk_get_sys。

int clk_save_context(void)

保存断电时的时钟上下文

参数

void

无参数

描述

为寄存器内容会丢失的电源状态保存时钟寄存器的上下文。发生在挂起代码的深处,因此不需要加锁。

void clk_restore_context(void)

在断电后恢复时钟上下文

参数

void

无参数

描述

这发生在所有时钟都启用时。发生在恢复代码的深处,因此不需要加锁。

int clk_drop_range(struct clk *clk)

重置该时钟上设置的任何范围

参数

struct clk *clk

时钟源

描述

返回成功 (0) 或负的 errno。

struct clk *clk_get_optional(struct device *dev, const char *id)

查找并获取对可选时钟生产者的引用。

参数

struct device *dev

时钟“消费者”的设备

const char *id

时钟使用者 ID

描述

行为与 clk_get() 相同,但没有时钟生产者的情况除外。在这种情况下,该函数不会返回 -ENOENT,而是返回 NULL。

同步原语

读-复制更新 (RCU)

bool same_state_synchronize_rcu(unsigned long oldstate1, unsigned long oldstate2)

两个旧状态值是否相同?

参数

unsigned long oldstate1

第一个旧状态值。

unsigned long oldstate2

第二个旧状态值。

描述

这两个旧状态值必须从 get_state_synchronize_rcu()start_poll_synchronize_rcu()get_completed_synchronize_rcu() 中获取。如果两个值相同,则返回 **true**,否则返回 **false**。 这允许通过旧状态值跟踪生命周期的结构将其值推送到列表头,从而允许这些结构稍小一些。

bool rcu_trace_implies_rcu_gp(void)

RCU 任务跟踪宽限期是否意味着 RCU 宽限期?

参数

void

无参数

描述

由于实现上的偶然性,RCU 任务跟踪宽限期也充当 RCU 宽限期。 但是,这种情况随时可能发生变化。 依赖此偶然性的代码必须调用此函数以验证此偶然性是否仍然存在。

已经警告过您!

cond_resched_tasks_rcu_qs

cond_resched_tasks_rcu_qs ()

向 RCU 报告潜在的静默状态

描述

此宏类似于 cond_resched(),但它被定义为向 RCU 任务报告潜在的静默状态,即使 cond_resched() 机制被关闭(正如一些人倡导抢占内核那样)。

rcu_softirq_qs_periodic

rcu_softirq_qs_periodic (old_ts)

报告 RCU 和 RCU 任务的静默状态

参数

old_ts

处理开始时的节拍数。

描述

此辅助函数用于长时间运行的软中断处理程序,例如网络中的 NAPI 线程。调用者应在软中断处理程序开始时初始化作为 old_ts 传入的变量。当频繁调用时,此宏将在之后每 100 毫秒调用一次 rcu_softirq_qs(),这将提供 RCU 和 RCU-Tasks 静止状态。请注意,此宏会修改其 old_ts 参数。

由于禁用软中断的代码区域充当 RCU 读取端临界区,因此应在启用软中断(和抢占)的情况下调用此宏。

当定义了 CONFIG_PREEMPT_RT 时,则不需要此宏。RT 内核更有可能调用 schedule() 调用并提供必要的静止状态。相比之下,仅调用 cond_resched() 不会达到相同的效果,因为 cond_resched() 不提供 RCU-Tasks 静止状态。

RCU_LOCKDEP_WARN

RCU_LOCKDEP_WARN (c, s)

如果满足指定条件,则发出锁依赖 splat

参数

c

要检查的条件

s

信息性消息

描述

此函数在检查 (c) 之前检查 debug_lockdep_rcu_enabled(),以防止由于锁依赖尚未初始化而导致早期启动 splat,并在检查 (c) 之后重新检查,以防止由于锁依赖被禁用而导致的误报 splat。有关更多详细信息,请参阅 commit 3066820034b5dd(“rcu:拒绝 RCU_LOCKDEP_WARN() 误报”) 。

lockdep_assert_in_rcu_read_lock

lockdep_assert_in_rcu_read_lock ()

如果没有被 rcu_read_lock() 保护,则发出警告

描述

如果启用了锁依赖并且没有 rcu_read_lock() 生效,则发出 splat。

lockdep_assert_in_rcu_read_lock_bh

lockdep_assert_in_rcu_read_lock_bh ()

如果没有被 rcu_read_lock_bh() 保护,则发出警告

描述

如果启用了锁依赖并且没有 rcu_read_lock_bh() 生效,则发出 splat。请注意,local_bh_disable() 及其相关函数在这里是不够的,而是需要实际的 rcu_read_lock_bh()

lockdep_assert_in_rcu_read_lock_sched

lockdep_assert_in_rcu_read_lock_sched ()

如果没有被 rcu_read_lock_sched() 保护,则发出警告

描述

如果启用了锁依赖并且没有 rcu_read_lock_sched() 生效,则发出 splat。请注意,preempt_disable() 及其相关函数在这里是不够的,而是需要实际的 rcu_read_lock_sched()

lockdep_assert_in_rcu_reader

lockdep_assert_in_rcu_reader ()

如果不在某种类型的 RCU 读取器中,则发出警告

描述

如果启用了锁依赖并且没有任何类型的 RCU 读取器生效,则发出 splat。请注意,受 preempt_disable、local_bh_disable() 和 local_irq_disable() 等保护的代码区域都符合 RCU 读取器的条件。

请注意,这永远不会在没有使用 PREEMPT_COUNT 构建的 PREEMPT_NONE 或 PREEMPT_VOLUNTARY 内核中触发。但是,如果您启用了锁依赖,您不妨也启用 PREEMPT_COUNT。

unrcu_pointer

unrcu_pointer (p)

将指针标记为不受 RCU 保护

参数

p

需要失去其 __rcu 属性的指针

描述

p 从 __rcu 指针转换为 __kernel 指针。这允许将 __rcu 指针与 xchg() 及其相关函数一起使用。

RCU_INITIALIZER

RCU_INITIALIZER (v)

静态初始化 RCU 保护的全局变量

参数

v

要静态初始化的值。

rcu_assign_pointer

rcu_assign_pointer (p, v)

赋值给 RCU 保护的指针

参数

p

要赋值的指针

v

要赋值(发布)的值

描述

将指定的值赋值给指定的 RCU 保护的指针,确保任何并发的 RCU 读取器都将看到任何先前的初始化。

在需要它们的体系结构上插入内存屏障(其中大多数体系结构需要),并且还防止编译器在指针赋值后重新排序初始化结构的代码。更重要的是,此调用记录了 RCU 读取端代码将取消引用的指针。

在某些特殊情况下,您可以使用 RCU_INIT_POINTER() 而不是 rcu_assign_pointer()RCU_INIT_POINTER() 速度更快一些,因为它不会限制 CPU 或编译器。也就是说,在您应该使用 rcu_assign_pointer() 时使用 RCU_INIT_POINTER() 是一件非常糟糕的事情,会导致无法诊断的内存损坏。因此,请小心。有关详细信息,请参阅 RCU_INIT_POINTER() 注释标头。

请注意,rcu_assign_pointer() 仅评估其每个参数一次,尽管外观如此。其中一个“额外”的评估位于 typeof() 中,另一个仅对 sparse (__CHECKER__) 可见,两者都不会实际执行该参数。与大多数 cpp 宏一样,此仅执行参数一次的属性非常重要,因此在更改 rcu_assign_pointer() 和它调用的其他宏时请务必小心。

rcu_replace_pointer

rcu_replace_pointer (rcu_ptr, ptr, c)

替换 RCU 指针,返回其旧值

参数

rcu_ptr

RCU 指针,返回其旧值

ptr

常规指针

c

取消引用将发生的锁依赖条件

描述

执行替换,其中 rcu_ptr 是 RCU 注释的指针,c 是传递给用于读取该指针的 rcu_dereference_protected() 调用的锁依赖参数。返回 rcu_ptr 的旧值,并将 rcu_ptr 设置为 ptr

rcu_access_pointer

rcu_access_pointer (p)

获取 RCU 指针,不进行解引用

参数

p

要读取的指针

描述

返回指定的 RCU 保护的指针的值,但省略在 RCU 读取端临界区中的锁依赖检查。当访问此指针的值但不取消引用指针时,这非常有用,例如,在测试 RCU 保护的指针是否为 NULL 时。虽然 rcu_access_pointer() 也可在更新端锁阻止指针的值更改的情况下使用,但您应该为此用例使用 rcu_dereference_protected()。在 RCU 读取端临界区内,几乎没有理由使用 rcu_access_pointer()

通常最好直接测试 rcu_access_pointer() 的返回值,以避免因以后不注意的更改而引入意外的解引用。换句话说,将 rcu_access_pointer() 的返回值赋值给局部变量会导致意外情况的发生。

当至少在一个宽限期之前删除了对指针的读取端访问时,也可以使用 rcu_access_pointer(),就像释放数据的 RCU 回调的上下文中,或者在 synchronize_rcu() 返回之后一样。在宽限期过后拆卸多链接结构时,这非常有用。但是,通常首选为此用例使用 rcu_dereference_protected()

rcu_dereference_check

rcu_dereference_check (p, c)

使用调试检查的 rcu_dereference

参数

p

读取(在解引用之前)的指针

c

取消引用将发生的条件

描述

执行 rcu_dereference(),但检查执行解引用的条件是否正确。通常,这些条件指示此时应持有的各种锁定条件。如果条件满足,则检查应返回 true。其中隐式包含了对处于 RCU 读取端临界区(rcu_read_lock())的检查。

例如:

bar = rcu_dereference_check(foo->bar, lockdep_is_held(foo->lock));

可以用来向 lockdep 指示,只有在持有 rcu_read_lock() 或持有替换 foo->bar 中 bar 结构所需的锁时,才能解引用 foo->bar。

请注意,条件列表也可能包含何时不需要持有锁的指示,例如在目标结构的初始化或销毁期间。

bar = rcu_dereference_check(foo->bar, lockdep_is_held(foo->lock) ||

atomic_read(foo->usage) == 0);

在需要内存屏障的架构(目前只有 Alpha)上插入内存屏障,防止编译器重复获取(以及合并获取),更重要的是,明确记录哪些指针受 RCU 保护,并检查该指针是否被标记为 __rcu。

rcu_dereference_bh_check

rcu_dereference_bh_check (p, c)

带有调试检查的 rcu_dereference_bh

参数

p

读取(在解引用之前)的指针

c

取消引用将发生的条件

描述

这是 rcu_dereference_check() 的 RCU-bh 对应版本。但是,请注意,从 v5.0 内核开始,普通的 RCU 宽限期除了等待由 rcu_read_lock()rcu_read_unlock() 标记的代码区域外,还会等待 local_bh_disable() 代码区域。这意味着 synchronize_rcu()、call_rcu 和相关函数不仅会考虑 rcu_read_lock(),还会考虑 rcu_read_lock_bh()

rcu_dereference_sched_check

rcu_dereference_sched_check (p, c)

带有调试检查的 rcu_dereference_sched

参数

p

读取(在解引用之前)的指针

c

取消引用将发生的条件

描述

这是 rcu_dereference_check() 的 RCU-sched 对应版本。但是,请注意,从 v5.0 内核开始,普通的 RCU 宽限期除了等待由 rcu_read_lock()rcu_read_unlock() 标记的代码区域外,还会等待 preempt_disable() 代码区域。这意味着 synchronize_rcu()、call_rcu 和相关函数不仅会考虑 rcu_read_lock(),还会考虑 rcu_read_lock_sched()

rcu_dereference_protected

rcu_dereference_protected (p, c)

当阻止更新时获取 RCU 指针

参数

p

读取(在解引用之前)的指针

c

取消引用将发生的条件

描述

返回指定的 RCU 保护指针的值,但省略 READ_ONCE()。这在更新端锁阻止指针值更改的情况下很有用。请注意,此原语并阻止编译器重复此引用或将其与其他引用组合,因此在没有适当的锁保护的情况下不应使用它。

此函数仅供更新端使用。仅受 rcu_read_lock() 保护时使用此函数会导致不频繁但非常糟糕的失败。

rcu_dereference

rcu_dereference (p)

获取 RCU 保护指针以进行解引用

参数

p

读取(在解引用之前)的指针

描述

这是 rcu_dereference_check() 的一个简单包装器。

rcu_dereference_bh

rcu_dereference_bh (p)

获取受 RCU-bh 保护的指针以进行解引用

参数

p

读取(在解引用之前)的指针

描述

rcu_dereference_check() 完成脏活。

rcu_dereference_sched

rcu_dereference_sched (p)

获取受 RCU-sched 保护的指针以进行解引用

参数

p

读取(在解引用之前)的指针

描述

rcu_dereference_check() 完成脏活。

rcu_pointer_handoff

rcu_pointer_handoff (p)

将指针从 RCU 移交给其他机制

参数

p

要移交的指针

描述

这只是一个恒等函数,但它记录了将指针从 RCU 移交给其他同步机制(例如,引用计数或锁定)的位置。在 C11 中,它会映射到 kill_dependency()。它可以按如下方式使用:

rcu_read_lock();
p = rcu_dereference(gp);
long_lived = is_long_lived(p);
if (long_lived) {
        if (!atomic_inc_not_zero(p->refcnt))
                long_lived = false;
        else
                p = rcu_pointer_handoff(p);
}
rcu_read_unlock();
void rcu_read_lock(void)

标记 RCU 读取端临界区的开始

参数

void

无参数

描述

当在一个 CPU 上调用 synchronize_rcu() 时,而其他 CPU 处于 RCU 读取端临界区内,则保证 synchronize_rcu() 会阻塞,直到所有其他 CPU 退出其临界区。类似地,如果在一个 CPU 上调用 call_rcu(),而其他 CPU 处于 RCU 读取端临界区内,则相应 RCU 回调的调用会延迟,直到所有其他 CPU 退出其临界区。

在 v5.0 及更高版本的内核中,synchronize_rcu()call_rcu() 还会等待禁用抢占的代码区域,包括禁用中断或软中断的代码区域。在 v5.0 之前的内核中,这些内核定义了 synchronize_sched(),只有 rcu_read_lock()rcu_read_unlock() 包围的代码才保证被等待。

但是,请注意,RCU 回调允许与新的 RCU 读取端临界区并发运行。发生这种情况的一种方式是通过以下事件序列:(1)CPU 0 进入 RCU 读取端临界区,(2)CPU 1 调用 call_rcu() 以注册 RCU 回调,(3)CPU 0 退出 RCU 读取端临界区,(4)CPU 2 进入 RCU 读取端临界区,(5)调用 RCU 回调。这是合法的,因为在调用相应的 RCU 回调之前,与 call_rcu() 并发运行(因此可能引用相应的 RCU 回调会释放的内容)的 RCU 读取端临界区已完成。

RCU 读取端临界区可以嵌套。任何延迟的操作都会延迟到最外层的 RCU 读取端临界区完成为止。

您可以通过遵循以下规则来避免阅读和理解下一段:不要在 rcu_read_lock() RCU 读取端临界区中放置任何会在 !PREEMPTION 内核中阻塞的内容。但如果您想了解完整的故事,请继续阅读!

在不可抢占的 RCU 实现(纯 TREE_RCU 和 TINY_RCU)中,在 RCU 读取端临界区中阻塞是非法的。在 CONFIG_PREEMPTION 内核构建中可抢占的 RCU 实现 (PREEMPT_RCU) 中,RCU 读取端临界区可能会被抢占,但显式阻塞是非法的。最后,在实时(带有 -rt 补丁集)内核构建中可抢占的 RCU 实现中,RCU 读取端临界区可能会被抢占,它们也可能会阻塞,但只有在获取受优先级继承约束的自旋锁时才会阻塞。

void rcu_read_unlock(void)

标记 RCU 读取端临界区的结束。

参数

void

无参数

描述

在几乎所有情况下,rcu_read_unlock() 都能避免死锁。在最近的内核中,已经将 synchronize_sched() 和 synchronize_rcu_bh() 合并到 synchronize_rcu() 中,这种避免死锁的能力也扩展到了调度器的运行队列和优先级继承自旋锁,这归功于当调用 rcu_read_unlock() 且禁用中断时执行的静止状态延迟。

有关更多信息,请参见 rcu_read_lock()

void rcu_read_lock_bh(void)

标记 RCU-bh 临界区的开始

参数

void

无参数

描述

这等效于 rcu_read_lock(),但也会禁用软中断。请注意,任何其他禁用软中断的操作也可以作为 RCU 读取侧临界区。但是,请注意,这种等效性仅适用于 v5.0 及更高版本。在 v5.0 之前,rcu_read_lock()rcu_read_lock_bh() 是不相关的。

请注意,rcu_read_lock_bh() 和匹配的 rcu_read_unlock_bh() 必须在相同的上下文中发生,例如,如果匹配的 rcu_read_lock_bh() 是从其他任务调用的,那么从一个任务调用 rcu_read_unlock_bh() 是非法的。

void rcu_read_unlock_bh(void)

标记仅限软中断的 RCU 临界区的结束

参数

void

无参数

描述

有关更多信息,请参见 rcu_read_lock_bh()

void rcu_read_lock_sched(void)

标记 RCU-sched 临界区的开始

参数

void

无参数

描述

这等效于 rcu_read_lock(),但也会禁用抢占。读取侧临界区也可以由任何其他禁用抢占的操作引入,包括 local_irq_disable() 及其类似函数。但是,请注意,与 rcu_read_lock() 的等效性仅适用于 v5.0 及更高版本。在 v5.0 之前,rcu_read_lock()rcu_read_lock_sched() 是不相关的。

请注意,rcu_read_lock_sched() 和匹配的 rcu_read_unlock_sched() 必须在相同的上下文中发生,例如,如果匹配的 rcu_read_lock_sched() 是从 NMI 处理程序调用的,那么从进程上下文调用 rcu_read_unlock_sched() 是非法的。

void rcu_read_unlock_sched(void)

标记 RCU 经典临界区的结束

参数

void

无参数

描述

有关更多信息,请参见 rcu_read_lock_sched()

RCU_INIT_POINTER

RCU_INIT_POINTER (p, v)

初始化 RCU 保护的指针

参数

p

要初始化的指针。

v

将指针初始化为的值。

描述

在读者不需要 CPU 或编译器的排序约束的特殊情况下,初始化 RCU 保护的指针。这些特殊情况是

  1. 使用 RCU_INIT_POINTER() 将指针设置为 NULL

  2. 调用者已采取任何必要的步骤来防止 RCU 读者同时访问此指针

  3. 引用的数据结构已在编译时或通过 rcu_assign_pointer() 向读者公开

    1. 从那时起,您没有对该结构进行任何读者可见的更改

    2. 从其新位置访问此结构的读者可以看到该结构的旧状态是可以接受的。(例如,更改是对统计计数器或其他不需要精确同步的状态的更改。)

未能遵循这些关于使用 RCU_INIT_POINTER() 的规则将导致难以诊断的内存损坏。例如,这些结构在崩溃转储中看起来是正常的,但任何并发的 RCU 读者都可能会看到引用的数据结构的预初始化值。因此,请非常小心地使用 RCU_INIT_POINTER()!!!

如果您正在创建一个由单个外部于结构的 RCU 保护指针访问的 RCU 保护的链接结构,那么您可以使用 RCU_INIT_POINTER() 初始化内部 RCU 保护的指针,但在完全初始化链接结构的读者可访问部分 *之后*,您必须使用 rcu_assign_pointer() 初始化外部于结构的指针。

请注意,与 rcu_assign_pointer() 不同,RCU_INIT_POINTER() 不为 CPU 或编译器提供任何排序保证。

RCU_POINTER_INITIALIZER

RCU_POINTER_INITIALIZER (p, v)

静态初始化 RCU 保护的指针

参数

p

要初始化的指针。

v

将指针初始化为的值。

描述

结构字段中 RCU 保护的指针的 GCC 风格的初始化。

kfree_rcu

kfree_rcu (ptr, rhf)

在宽限期后 kfree 一个对象。

参数

ptr

用于双参数调用的 kfree 的指针。

rhf

ptr 类型内的 struct rcu_head 的名称。

描述

许多 rcu 回调函数只是对基本结构调用 kfree()。这些函数很简单,但它们的大小会累积,此外,当它们在内核模块中使用时,该模块必须在模块卸载时调用高延迟的 rcu_barrier() 函数。

kfree_rcu() 函数处理此问题。 kfree_rcu() 不在嵌入的 rcu_head 结构中编码函数地址,而是编码 rcu_head 结构在基本结构中的偏移量。由于函数不允许在内核虚拟内存的低 4096 字节中使用,因此可以容纳最大 4095 字节的偏移量。如果偏移量大于 4095 字节,则会在 kvfree_rcu_arg_2() 中生成编译时错误。如果触发此错误,您可以回退使用 call_rcu(),或者重新排列结构以将 rcu_head 结构定位到前 4096 个字节中。

要释放的对象可以由 kmalloc()kmem_cache_alloc() 分配。

请注意,允许的偏移量将来可能会减少。

BUILD_BUG_ON 检查不得涉及任何函数调用,因此此处在宏中进行检查。

kfree_rcu_mightsleep

kfree_rcu_mightsleep (ptr)

在宽限期后 kfree 一个对象。

参数

ptr

用于单参数调用的 kfree 指针。

描述

当涉及到无头变体时,只传递一个参数,它只是一个指针,必须在宽限期后释放。因此,语义是

kfree_rcu_mightsleep(ptr);

其中 ptr 是指向由 kvfree() 释放的指针。

请注意,允许从必须遵循 might_sleep() 注解的上下文中使用无头释放方式。否则,请切换并将 rcu_head 结构嵌入到 ptr 的类型中。

void rcu_head_init(struct rcu_head *rhp)

rcu_head_after_call_rcu() 初始化 rcu_head

参数

struct rcu_head *rhp

要初始化的 rcu_head 结构。

描述

如果您打算调用 rcu_head_after_call_rcu() 来测试给定的 rcu_head 结构是否已传递给 call_rcu(),那么您还必须在分配该结构后立即调用此 rcu_head_init() 函数。对此函数的调用不得与对 call_rcu()rcu_head_after_call_rcu() 或回调调用的调用竞争。

bool rcu_head_after_call_rcu(struct rcu_head *rhp, rcu_callback_t f)

此 rcu_head 是否已传递给 call_rcu()

参数

struct rcu_head *rhp

要测试的 rcu_head 结构。

rcu_callback_t f

rhp 一起传递给 call_rcu() 的函数。

描述

如果 rhp 已传递给带有 funccall_rcu(),则返回 true,否则返回 false。在任何其他情况下都会发出警告,包括在宽限期后已调用 rhp 的情况。对此函数的调用不得与回调调用竞争。避免此类竞争的一种方法是将对 rcu_head_after_call_rcu() 的调用包含在 RCU 读取侧临界区中,该临界区包括读取包含 rhp 的结构的指针的读取侧获取。

void rcu_softirq_qs(void)

在软中断处理中提供一组 RCU 静止状态

参数

void

无参数

描述

标记 RCU、Tasks RCU 和 Tasks Trace RCU 的静止状态。这是一个在软中断基础设施中使用的专用函数,也可能在偶尔长时间运行的软中断处理程序中使用。

请注意,从 RCU 的角度来看,调用 rcu_softirq_qs() 等同于暂时完全启用抢占。例如,给定此代码

local_bh_disable();
do_something();
rcu_softirq_qs();  // A
do_something_else();
local_bh_enable();  // B

与 do_something() 调用同时开始的 synchronize_rcu() 调用将保证仅等待到执行到达语句 A。如果没有该 rcu_softirq_qs(),相同的 synchronize_rcu() 将改为保证等待到执行到达语句 B。

bool rcu_watching_snap_stopped_since(struct rcu_data *rdp, int snap)

自指定的 snap 以来,RCU 是否已停止监视给定的 CPU?

参数

struct rcu_data *rdp

对应于要检查 EQS 的 CPU 的 rcu_data。

int snap

CPU 不在 EQS 中时拍摄的 rcu_watching 快照。

描述

如果对应于 rdp 的 CPU 自 snap 以来在扩展静止状态中花费了一段时间,则返回 true。请注意,这不检查它是否/仍然/在 EQS 中,只是检查它自 snap 以来经历了一个 EQS。

这意味着用于循环等待 CPU 经历 EQS。

int rcu_is_cpu_rrupt_from_idle(void)

查看是否从空闲状态“中断”

参数

void

无参数

描述

如果当前 CPU 处于空闲状态并以第一级(非嵌套)中断或直接从空闲状态运行,则返回 true。

调用方必须至少禁用 IRQ。

void rcu_irq_exit_check_preempt(void)

验证是否可以进行调度

参数

void

无参数

void __rcu_irq_enter_check_tick(void)

如果 RCU 需要,则在 CPU 上启用调度器滴答。

参数

void

无参数

描述

当 CPU 从 nohz_full 用户空间执行进入内核时,调度器滴答通常不启用。毕竟,nohz_full 用户空间执行是 RCU 静止状态,并且在内核中执行的时间非常短。当然,除非它不是这样。并且不难导致大型系统在内核中循环数十秒甚至数分钟,这可能会导致许多问题,包括 RCU CPU 停顿警告。

因此,如果 nohz_full CPU 未能及时报告静止状态,则 RCU 宽限期 kthread 将设置该 CPU 的 ->rcu_urgent_qs 标志,期望下一个中断或异常将调用此函数,这将打开调度器滴答,这将使 RCU 能够检测该 CPU 的静止状态,例如,由于 CONFIG_PREEMPT=n 内核中的 cond_resched() 调用。一旦为此 CPU 报告了静止状态,滴答将被禁用。

当然,在精心调整的系统中,可能永远不会发生中断或异常。在这种情况下,RCU 宽限期 kthread 最终将导致发生中断或异常。但是,在不太受控制的环境中,此函数允许 RCU 在不产生其他无用中断的情况下获得所需的内容。

notrace bool rcu_is_watching(void)

当前 CPU 上是否允许 RCU 读取侧临界区?

参数

void

无参数

描述

如果 RCU 正在监视正在运行的 CPU,则返回 true,否则返回 falsetrue 返回表示此 CPU 可以安全地进入 RCU 读取侧临界区。

尽管从内核的大部分地方调用 rcu_is_watching() 将返回 true,但也有重要的例外情况。 例如,如果当前 CPU 处于其空闲循环的深处、在内核入口/出口代码中或处于离线状态,rcu_is_watching() 将返回 false

标记为 notrace,因为它可能被 ftrace 的内部函数调用,将其标记为 notrace 可以消除不必要的递归调用。

void call_rcu_hurry(struct rcu_head *head, rcu_callback_t func)

将 RCU 回调排队,以便在宽限期后调用,并在执行此操作时将所有延迟的回调(包括新的回调)刷新到主 ->cblist。

参数

struct rcu_head *head

用于将 RCU 更新排队的结构。

rcu_callback_t func

宽限期过后要调用的实际回调函数

描述

回调函数将在完整宽限期经过一段时间后被调用,换句话说,在所有预先存在的 RCU 读取端临界区都已完成之后。

如果您不希望回调在很长一段时间后被调用,请使用此 API 而不是 call_rcu(),这可能发生在没有内存压力的系统和负载较轻或大多处于空闲状态的系统上。此函数将导致回调比晚调用更快,但会以额外的功耗为代价。除此之外,此函数与 call_rcu() 相同,并重用其逻辑。 有关内存排序和其他功能的更多详细信息,请参阅 call_rcu()

void call_rcu(struct rcu_head *head, rcu_callback_t func)

将 RCU 回调排队,以便在宽限期后调用。 默认情况下,回调是“延迟的”,并且对主 ->cblist 隐藏,以防止过早开始宽限期。 如果您希望宽限期很快开始,请使用 call_rcu_hurry()

参数

struct rcu_head *head

用于将 RCU 更新排队的结构。

rcu_callback_t func

宽限期过后要调用的实际回调函数

描述

回调函数将在完整宽限期经过一段时间后被调用,换句话说,在所有预先存在的 RCU 读取端临界区都已完成之后。 但是,回调函数很可能与在调用 call_rcu() 之后启动的 RCU 读取端临界区并发执行。

RCU 读取端临界区由 rcu_read_lock()rcu_read_unlock() 分隔,并且可以嵌套。 此外,但仅在 v5.0 及更高版本中,禁用中断、抢占或软中断的代码区域也充当 RCU 读取端临界区。 这包括硬件中断处理程序、软中断处理程序和 NMI 处理程序。

请注意,所有 CPU 都必须同意宽限期超出所有预先存在的 RCU 读取端临界区。 在具有多个 CPU 的系统上,这意味着当调用“func()”时,保证每个 CPU 自其最后一个 RCU 读取端临界区结束以来都执行了完整的内存屏障,而该临界区的开始先于对 call_rcu() 的调用。 这也意味着每个执行 RCU 读取端临界区的 CPU,该临界区在“func()”开始之后继续,必须在 call_rcu() 之后但在该 RCU 读取端临界区开始之前执行了内存屏障。 请注意,这些保证包括处于脱机、空闲或在用户模式下执行的 CPU,以及在内核中执行的 CPU。

此外,如果 CPU A 调用了 call_rcu() 并且 CPU B 调用了生成的 RCU 回调函数“func()”,则保证 CPU A 和 CPU B 都在调用 call_rcu() 和调用 “func()” 之间的时间间隔内执行完整的内存屏障 —— 即使 CPU A 和 CPU B 是同一个 CPU(但同样只有在系统具有多个 CPU 的情况下)。

此处介绍了这些内存排序保证的实现:TREE_RCU 的宽限期内存排序之旅

struct kvfree_rcu_bulk_data

用于存储 kvfree_rcu() 指针的单个块

定义:

struct kvfree_rcu_bulk_data {
    struct list_head list;
    struct rcu_gp_oldstate gp_snap;
    unsigned long nr_records;
    void *records[] ;
};

成员

列表

列表节点。 所有块彼此链接

gp_snap

放置到此批处理中的对象的 RCU 状态快照

nr_records

数组中活动指针的数量

records

kvfree_rcu() 指针数组

struct kfree_rcu_cpu_work

单批 kfree_rcu() 请求

定义:

struct kfree_rcu_cpu_work {
    struct rcu_work rcu_work;
    struct rcu_head *head_free;
    struct rcu_gp_oldstate head_free_gp_snap;
    struct list_head bulk_head_free[FREE_N_CHANNELS];
    struct kfree_rcu_cpu *krcp;
};

成员

rcu_work

queue_rcu_work() 在宽限期后调用工作队列处理程序

head_free

等待宽限期的 kfree_rcu() 对象列表

head_free_gp_snap

用于检查尝试过早释放的宽限期快照。

bulk_head_free

等待宽限期的 kvfree_rcu() 对象的批量列表

krcp

指向 kfree_rcu_cpu 结构的指针

struct kfree_rcu_cpu

批量处理用于 RCU 宽限期的 kfree_rcu() 请求

定义:

struct kfree_rcu_cpu {
    struct rcu_head *head;
    unsigned long head_gp_snap;
    atomic_t head_count;
    struct list_head bulk_head[FREE_N_CHANNELS];
    atomic_t bulk_count[FREE_N_CHANNELS];
    struct kfree_rcu_cpu_work krw_arr[KFREE_N_BATCHES];
    raw_spinlock_t lock;
    struct delayed_work monitor_work;
    bool initialized;
    struct delayed_work page_cache_work;
    atomic_t backoff_page_cache_fill;
    atomic_t work_in_progress;
    struct hrtimer hrtimer;
    struct llist_head bkvcache;
    int nr_bkv_objs;
};

成员

head

尚未等待宽限期的 kfree_rcu() 对象列表

head_gp_snap

放置到“head”的对象的 RCU 状态快照

head_count

rcu_head 单个列表中的对象数量

bulk_head

尚未等待宽限期的 kvfree_rcu() 对象的批量列表

bulk_count

批量列表中的对象数量

krw_arr

等待宽限期的 kfree_rcu() 对象批次数组

lock

同步对此结构的访问

monitor_work

在 KFREE_DRAIN_JIFFIES 后将 head 升级为 head_free

initialized

rcu_work 字段已初始化

page_cache_work

当缓存为空时用于重新填充缓存的工作

backoff_page_cache_fill

延迟缓存重新填充

work_in_progress

指示 page_cache_work 正在运行

hrtimer

用于调度 page_cache_work 的 hrtimer

bkvcache

一个简单的缓存列表,其中包含用于重复使用的对象。 为了节省一些每 CPU 空间,该列表是单一的。 即使它是无锁的,也必须通过每 CPU 锁来保护访问。

nr_bkv_objs

bkvcache 中分配的对象数量。

描述

这是一个每 CPU 结构。 它未包含在 rcu_data 结构中的原因是允许从此 RCU 文件中提取此代码。 这种提取可以允许进一步优化与 slab 分配器的交互。

void kvfree_rcu_barrier(void)

等待所有正在进行的 kvfree_rcu() 完成。

参数

void

无参数

描述

请注意,`kvfree_rcu()` 的单参数调用有一个慢速路径,该路径会触发 synchronize_rcu(),然后释放一个指针。这会在函数返回之前完成。因此,对于任何会导致 kfree() 调用释放模块退出期间将被销毁的缓存的单参数调用,开发者有责任确保在调用 `kmem_cache_destroy()` 之前,所有此类调用都已返回。

void synchronize_rcu(void)

等待宽限期结束。

参数

void

无参数

描述

控制将在完整的宽限期结束后的一段时间返回给调用者,换句话说,在所有当前正在执行的 RCU 读取端临界区都已完成之后。但是请注意,从 synchronize_rcu() 返回后,调用者很可能与在 synchronize_rcu() 等待时开始的新的 RCU 读取端临界区并发执行。

RCU 读取端临界区由 rcu_read_lock()rcu_read_unlock() 分隔,并且可以嵌套。 此外,但仅在 v5.0 及更高版本中,禁用中断、抢占或软中断的代码区域也充当 RCU 读取端临界区。 这包括硬件中断处理程序、软中断处理程序和 NMI 处理程序。

请注意,此保证意味着进一步的内存排序保证。在具有多个 CPU 的系统上,当 synchronize_rcu() 返回时,保证每个 CPU 都已执行了一个完整的内存屏障,该屏障自其最后一个 RCU 读取端临界区结束以来,该临界区的开始早于对 synchronize_rcu() 的调用。此外,保证每个具有 RCU 读取端临界区的 CPU,该临界区延伸到 synchronize_rcu() 返回之后,都已在 synchronize_rcu() 开始之后和该 RCU 读取端临界区开始之前执行了一个完整的内存屏障。请注意,这些保证包括离线、空闲或在用户模式下执行的 CPU,以及在内核中执行的 CPU。

此外,如果 CPU A 调用了 synchronize_rcu(),该函数在 CPU B 上返回给其调用者,则保证 CPU A 和 CPU B 都在 synchronize_rcu() 的执行期间执行了一个完整的内存屏障——即使 CPU A 和 CPU B 是同一个 CPU(但同样仅在系统具有多个 CPU 的情况下)。

此处介绍了这些内存排序保证的实现:TREE_RCU 的宽限期内存排序之旅

void get_completed_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp)

返回一个完整的预完成轮询状态 Cookie

参数

struct rcu_gp_oldstate *rgosp

放置状态 Cookie 的位置

描述

将值存储到 rgosp 中,该值将始终被 poll_state_synchronize_rcu_full() 等函数视为其宽限期已完成的 Cookie。

unsigned long get_state_synchronize_rcu(void)

快照当前 RCU 状态

参数

void

无参数

描述

返回一个 Cookie,该 Cookie 由稍后对 cond_synchronize_rcu()poll_state_synchronize_rcu() 的调用使用,以确定在此期间是否经过了完整的宽限期。

void get_state_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp)

快照 RCU 状态,包括正常和加速状态

参数

struct rcu_gp_oldstate *rgosp

放置组合的正常/加速宽限期状态的位置

描述

将正常和加速的宽限期状态放置在 rgosp 中。此状态值可以传递给稍后对 cond_synchronize_rcu_full()poll_state_synchronize_rcu_full() 的调用,以确定在此期间是否经过了宽限期(无论是正常还是加速)。 `rcu_gp_oldstate` 结构占用的内存是 unsigned long 的两倍,但保证可以看到所有宽限期。相比之下,组合状态占用的内存更少,但有时可能无法考虑宽限期。

这不保证所需的宽限期实际上会开始。

unsigned long start_poll_synchronize_rcu(void)

快照并启动 RCU 宽限期

参数

void

无参数

描述

返回一个 Cookie,该 Cookie 由稍后对 cond_synchronize_rcu()poll_state_synchronize_rcu() 的调用使用,以确定在此期间是否经过了完整的宽限期。如果所需的宽限期尚未计划开始,则会通知 RCU 核心需要该宽限期。

void start_poll_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp)

获取完整快照并启动 RCU 宽限期

参数

struct rcu_gp_oldstate *rgosp

来自 get_state_synchronize_rcu_full()start_poll_synchronize_rcu_full() 的值

描述

将正常和加速的宽限期状态放置在 *rgos 中。此状态值可以传递给稍后对 cond_synchronize_rcu_full()poll_state_synchronize_rcu_full() 的调用,以确定在此期间是否经过了宽限期(无论是正常还是加速)。如果所需的宽限期尚未计划开始,则会通知 RCU 核心需要该宽限期。

bool poll_state_synchronize_rcu(unsigned long oldstate)

指定的 RCU 宽限期是否已完成?

参数

unsigned long oldstate

来自 get_state_synchronize_rcu()start_poll_synchronize_rcu() 的值

描述

如果自较早的调用(从中获取 oldstate)以来经过了完整的 RCU 宽限期,则返回 true,否则返回 false。如果返回 false,则调用者有责任稍后调用此函数,直到它返回 true。或者,调用者可以显式等待宽限期,例如,通过将 oldstate 传递给 cond_synchronize_rcu()cond_synchronize_rcu_expedited(),或者直接调用 synchronize_rcu()synchronize_rcu_expedited()

是的,此函数不考虑计数器回绕。但计数器回绕是无害的。如果计数器回绕,我们已经等待了超过十亿个宽限期(在 64 位系统上则更多!)。那些需要将旧状态值保持很长时间(即使在 32 位系统上也有数小时)的人应该偶尔检查它们,并刷新它们或设置一个标志,指示宽限期已完成。或者,他们可以使用 get_completed_synchronize_rcu() 来获取保证完成的宽限期状态。

此外,由于 oldstate 将普通宽限期和加速宽限期的状态压缩成一个 unsigned long,当 synchronize_rcu()synchronize_rcu_expedited() 并发运行时,它可能会错过一个宽限期。如果这是不可接受的,请改用这些轮询 API 的 _full() 变体。

此函数提供的内存排序保证与调用提供 oldstate 的函数时调用 synchronize_rcu() 所提供的相同,并且该调用在此函数结束时返回。

bool poll_state_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp)

指定的 RCU 宽限期是否已完成?

参数

struct rcu_gp_oldstate *rgosp

来自 get_state_synchronize_rcu_full()start_poll_synchronize_rcu_full() 的值

描述

如果自先前获取 *rgosp* 的调用以来,已过去一个完整的 RCU 宽限期,则返回 **true**,否则返回 **false**。如果返回 **false**,则调用者有责任稍后调用此函数,直到它返回 **true**。或者,调用者可以显式等待宽限期,例如,通过将 **rgosp** 传递给 cond_synchronize_rcu() 或直接调用 synchronize_rcu()

是的,此函数不考虑计数器回绕。但计数器回绕是无害的。如果计数器回绕,我们已经等待了超过十亿个宽限期(在 64 位系统上则更多!)。那些需要将 rcu_gp_oldstate 值保持很长时间(即使在 32 位系统上也有数小时)的人应该偶尔检查它们,并刷新它们或设置一个标志,指示宽限期已完成。或者,他们可以使用 get_completed_synchronize_rcu_full() 来获取保证完成的宽限期状态。

此函数提供的内存排序保证与调用提供 rgosp 的函数时调用 synchronize_rcu() 所提供的相同,并且该调用在此函数结束时返回。此保证要求检查根 rcu_node 结构的 ->gp_seq 字段,而不是 rcu_state 结构的字段。问题在于,刚刚结束的宽限期的回调可能在更新根 rcu_node 结构的 ->gp_seq 字段的时间和更新 rcu_state 结构的 ->gp_seq 字段的时间之间被调用。因此,如果单个 synchronize_rcu() 要使后续的 poll_state_synchronize_rcu_full() 返回 **true**,则需要轮询根 rcu_node 结构。

void cond_synchronize_rcu(unsigned long oldstate)

有条件地等待 RCU 宽限期

参数

unsigned long oldstate

来自 get_state_synchronize_rcu(), start_poll_synchronize_rcu(), 或 start_poll_synchronize_rcu_expedited() 的值

描述

如果自先前调用 get_state_synchronize_rcu()start_poll_synchronize_rcu() 以来,已过去一个完整的 RCU 宽限期,则直接返回。否则,调用 synchronize_rcu() 来等待一个完整的宽限期。

是的,此函数不考虑计数器回绕。但计数器回绕是无害的。如果计数器回绕,我们已经等待了超过 20 亿个宽限期(在 64 位系统上则更多!),因此等待几个额外的宽限期应该没什么问题。

此函数提供的内存排序保证与调用提供 oldstate 的函数时调用 synchronize_rcu() 所提供的相同,并且该调用在此函数结束时返回。

void cond_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp)

有条件地等待 RCU 宽限期

参数

struct rcu_gp_oldstate *rgosp

来自 get_state_synchronize_rcu_full(), start_poll_synchronize_rcu_full(), 或 start_poll_synchronize_rcu_expedited_full() 的值

描述

如果自调用 get_state_synchronize_rcu_full(), start_poll_synchronize_rcu_full(), 或 start_poll_synchronize_rcu_expedited_full() 获取 rgosp 以来,已过去一个完整的 RCU 宽限期,则直接返回。否则,调用 synchronize_rcu() 来等待一个完整的宽限期。

是的,此函数不考虑计数器回绕。但计数器回绕是无害的。如果计数器回绕,我们已经等待了超过 20 亿个宽限期(在 64 位系统上则更多!),因此等待几个额外的宽限期应该没什么问题。

此函数提供的内存排序保证与调用提供 rgosp 的函数时调用 synchronize_rcu() 所提供的相同,并且该调用在此函数结束时返回。

void rcu_barrier(void)

等待所有正在进行的 call_rcu() 回调完成。

参数

void

无参数

描述

请注意,此原语不一定会等待 RCU 宽限期完成。例如,如果系统中任何地方都没有排队等待的 RCU 回调,则 rcu_barrier() 有权立即返回,而无需等待任何内容,更不用说 RCU 宽限期了。

void rcu_barrier_throttled(void)

执行 rcu_barrier(),但限制为每秒一次

参数

void

无参数

描述

这可以被认为是围绕 rcu_barrier() 的保护栏,允许不受限制的用户空间使用,至少假设硬件的 try_cmpxchg() 是稳健的。使用此函数后,系统范围的 rcu_barrier() 调用最多每秒一次,这意味着调用者可能会不必要地等待一秒或三秒。

此功能旨在供测试套件使用,以避免因在启动下一个测试之前刷新上一个测试中的 RCU 回调而导致的 OOM(内存溢出)。有关详细信息,请参阅 rcutree.do_rcu_barrier 模块参数。

为什么不简单地使 rcu_barrier() 更具可扩展性?这可能是最终的解决方案,但目前我们还是保持简单。请注意,模块参数基础设施会对给定 .set() 函数的调用进行序列化,但如果将来可能出现并发 .set() 调用,我们已做好准备!

void synchronize_rcu_expedited(void)

强制 RCU 宽限期

参数

void

无参数

描述

等待 RCU 宽限期,但加快其速度。基本思路是向所有非空闲、非 nohz 的在线 CPU 发送 IPI(核间中断)。IPI 处理程序会检查 CPU 是否处于 RCU 临界区,如果是,则设置一个标志,使最外层的 rcu_read_unlock() 报告 RCU-preempt 的静止状态,或者请求调度程序帮助 RCU-sched。另一方面,如果 CPU 不在 RCU 读取端临界区,则 IPI 处理程序会立即报告静止状态。

尽管这比之前的加速实现有了很大的改进,但它仍然对实时工作负载不友好,因此不建议用于任何常见的代码。实际上,如果您在循环中使用 synchronize_rcu_expedited(),请重构代码以批量更新,然后使用单个 synchronize_rcu() 代替。

它具有与 synchronize_rcu() 相同的语义(但比它更激进)。

unsigned long start_poll_synchronize_rcu_expedited(void)

快照当前 RCU 状态并启动加速宽限期

参数

void

无参数

描述

返回一个 Cookie,传递给对 cond_synchronize_rcu()cond_synchronize_rcu_expedited()poll_state_synchronize_rcu() 的调用,允许它们确定在此期间是否经过了任何类型的宽限期。如果所需的加速宽限期尚未安排开始,则会启动该宽限期。

void start_poll_synchronize_rcu_expedited_full(struct rcu_gp_oldstate *rgosp)

获取完整快照并启动加速宽限期

参数

struct rcu_gp_oldstate *rgosp

放置宽限期状态快照的位置

描述

将正常和加速宽限期状态放入 rgosp 中。此状态值可以传递给稍后对 cond_synchronize_rcu_full()poll_state_synchronize_rcu_full() 的调用,以确定在此期间是否经过了宽限期(无论是正常还是加速)。如果所需的加速宽限期尚未安排开始,则会启动该宽限期。

void cond_synchronize_rcu_expedited(unsigned long oldstate)

有条件地等待加速 RCU 宽限期

参数

unsigned long oldstate

来自 get_state_synchronize_rcu(), start_poll_synchronize_rcu(), 或 start_poll_synchronize_rcu_expedited() 的值

描述

如果自先前调用 get_state_synchronize_rcu()start_poll_synchronize_rcu()start_poll_synchronize_rcu_expedited() 以来,任何类型的完整 RCU 宽限期已经过去,则直接返回。否则,调用 synchronize_rcu_expedited() 以等待完整的宽限期。

是的,此函数不考虑计数器回绕。但计数器回绕是无害的。如果计数器回绕,我们已经等待了超过 20 亿个宽限期(在 64 位系统上则更多!),因此等待几个额外的宽限期应该没什么问题。

此函数提供的内存排序保证与调用提供 oldstate 的函数时调用 synchronize_rcu() 所提供的相同,并且该调用在此函数结束时返回。

void cond_synchronize_rcu_expedited_full(struct rcu_gp_oldstate *rgosp)

有条件地等待加速 RCU 宽限期

参数

struct rcu_gp_oldstate *rgosp

来自 get_state_synchronize_rcu_full(), start_poll_synchronize_rcu_full(), 或 start_poll_synchronize_rcu_expedited_full() 的值

描述

如果自调用 get_state_synchronize_rcu_full()start_poll_synchronize_rcu_full()start_poll_synchronize_rcu_expedited_full()(从中获取 **rgosp**)以来,已经经过了完整的 RCU 宽限期,则直接返回。否则,调用 synchronize_rcu_expedited() 以等待完整的宽限期。

是的,此函数不考虑计数器回绕。但计数器回绕是无害的。如果计数器回绕,我们已经等待了超过 20 亿个宽限期(在 64 位系统上则更多!),因此等待几个额外的宽限期应该没什么问题。

此函数提供的内存排序保证与调用提供 rgosp 的函数时调用 synchronize_rcu() 所提供的相同,并且该调用在此函数结束时返回。

bool rcu_read_lock_held_common(bool *ret)

我们是否可能在 RCU-sched 读取端临界区中?

参数

bool *ret

如果无法依赖 lockdep,则做出最佳猜测

描述

如果必须忽略 lockdep,则返回 true,在这种情况下,*ret 包含下面描述的最佳猜测。否则返回 false,在这种情况下,*ret 不会告诉调用者任何信息,调用者应该改为咨询 lockdep。

如果选择了 CONFIG_DEBUG_LOCK_ALLOC,则当且仅当处于 RCU-sched 读取端临界区时,将 *ret 设置为非零值。在没有 CONFIG_DEBUG_LOCK_ALLOC 的情况下,除非可以证明相反情况,否则假设我们处于 RCU-sched 读取端临界区。请注意,禁用抢占(包括禁用 irq)也算作 RCU-sched 读取端临界区。这对于需要在其 RCU-sched 读取端临界区内调用的函数中的调试检查很有用。

检查 debug_lockdep_rcu_enabled() 以防止在启动期间和禁用 lockdep 时出现误报。

请注意,如果从 RCU 的角度来看,CPU 处于空闲循环中(即:我们在 ct_idle_enter() 和 ct_idle_exit() 之间的部分中),则即使 CPU 执行了 rcu_read_lock()rcu_read_lock_held() 也会将 *ret 设置为 false。这样做的原因是,RCU 会忽略处于该部分中的 CPU,将其视为处于扩展的静止状态,因此无论 CPU 调用什么 RCU 原语,该 CPU 实际上永远不会处于 RCU 读取端临界区。这是必需的 --- 我们需要在空闲时保持一个无 RCU 的窗口,CPU 可能会进入低功耗模式。这样,我们可以注意到其他 CPU 开始宽限期的扩展静止状态。否则,只要我们在空闲任务中运行,就会延迟任何宽限期。

同样,如果当前 CPU 处于离线状态,则避免声明持有 RCU 读取锁。

void rcu_async_hurry(void)

使未来的异步 RCU 回调不再延迟。

参数

void

无参数

描述

调用此函数后,将及时处理将来对 call_rcu() 的调用。

void rcu_async_relax(void)

使未来的异步 RCU 回调延迟。

参数

void

无参数

描述

调用此函数后,将以延迟方式处理将来对 call_rcu() 的调用。

void rcu_expedite_gp(void)

加速未来的 RCU 宽限期

参数

void

无参数

描述

在调用此函数之后,未来对 synchronize_rcu() 及其友函数的调用,其行为将如同调用了相应的 synchronize_rcu_expedited() 函数一样。

void rcu_unexpedite_gp(void)

取消之前 rcu_expedite_gp() 的调用

参数

void

无参数

描述

撤销之前对 rcu_expedite_gp() 的调用。如果所有之前对 rcu_expedite_gp() 的调用都被后续对 rcu_unexpedite_gp() 的调用撤销,并且如果未设置 rcu_expedited sysfs/boot 参数,那么所有后续对 synchronize_rcu() 及其友函数的调用将恢复其正常的非加速行为。

int rcu_read_lock_held(void)

我们可能处于 RCU 读取侧临界区吗?

参数

void

无参数

描述

如果选择了 CONFIG_DEBUG_LOCK_ALLOC,则当且仅当在 RCU 读取侧临界区时返回非零值。在没有 CONFIG_DEBUG_LOCK_ALLOC 的情况下,除非能够证明不是,否则此函数假定我们处于 RCU 读取侧临界区。这对于需要在 RCU 读取侧临界区内调用的函数中的调试检查非常有用。

检查 debug_lockdep_rcu_enabled() 以防止在启动时和禁用 lockdep 时出现误报。

请注意,rcu_read_lock() 和匹配的 rcu_read_unlock() 必须在同一个上下文中发生,例如,如果匹配的 rcu_read_lock() 是从 irq 处理程序中调用的,则在进程上下文中调用 rcu_read_unlock() 是非法的。

请注意,如果 CPU 从 RCU 的角度来看处于空闲或离线状态,则不允许使用 rcu_read_lock(),因此也要检查这些情况。

int rcu_read_lock_bh_held(void)

我们可能处于 RCU-bh 读取侧临界区吗?

参数

void

无参数

描述

检查底半部是否被禁用,这涵盖了 CONFIG_PROVE_RCU 和非 CONFIG_PROVE_RCU 的情况。请注意,如果有人使用 rcu_read_lock_bh(),然后稍后启用 BH,则 lockdep (如果启用) 将显示这种情况。这对于需要在 RCU 读取侧临界区内调用的函数中的调试检查非常有用。

检查 debug_lockdep_rcu_enabled() 以防止在启动时出现误报。

请注意,如果 CPU 从 RCU 的角度来看处于空闲或离线状态,则不允许使用 rcu_read_lock_bh(),因此也要检查这些情况。

void wakeme_after_rcu(struct rcu_head *head)

宽限期后唤醒任务的回调函数

参数

struct rcu_head *head

指向 rcu_synchronize 结构中的 rcu_head 成员的指针

描述

在宽限期结束后唤醒相应的任务。

void init_rcu_head_on_stack(struct rcu_head *head)

初始化堆栈上的 rcu_head 以用于 debugobjects

参数

struct rcu_head *head

指向要初始化的 rcu_head 结构的指针

描述

此函数通知 debugobjects,一个新的 rcu_head 结构已在堆栈上作为自动变量分配。静态定义或在堆上动态分配的 rcu_head 结构不需要此函数。对于 !CONFIG_DEBUG_OBJECTS_RCU_HEAD 内核构建,此函数不起作用。

void destroy_rcu_head_on_stack(struct rcu_head *head)

销毁堆栈上的 rcu_head 以用于 debugobjects

参数

struct rcu_head *head

指向要初始化的 rcu_head 结构的指针

描述

此函数通知 debugobjects,堆栈上的 rcu_head 结构即将超出作用域。与 init_rcu_head_on_stack() 一样,对于静态定义或在堆上动态分配的 rcu_head 结构,不需要此函数。与 init_rcu_head_on_stack() 一样,此函数对于 !CONFIG_DEBUG_OBJECTS_RCU_HEAD 内核构建不起作用。

unsigned long get_completed_synchronize_rcu(void)

返回预完成的轮询状态 cookie

参数

void

无参数

描述

返回一个值,该值将被 poll_state_synchronize_rcu() 等函数始终视为宽限期已完成的 cookie。

unsigned long get_completed_synchronize_srcu(void)

返回预完成的轮询状态 cookie

参数

void

无参数

描述

返回一个值,poll_state_synchronize_srcu() 将始终将其视为宽限期已完成的 cookie。

bool same_state_synchronize_srcu(unsigned long oldstate1, unsigned long oldstate2)

两个旧状态值是否相同?

参数

unsigned long oldstate1

第一个旧状态值。

unsigned long oldstate2

第二个旧状态值。

描述

这两个旧状态值必须从 get_state_synchronize_srcu()start_poll_synchronize_srcu()get_completed_synchronize_srcu() 中获取。如果两个值相同,则返回 **true**,否则返回 **false**。这允许通过旧状态值跟踪其生命周期的结构将这些值推送到列表头,从而允许这些结构稍微小一些。

int srcu_read_lock_held(const struct srcu_struct *ssp)

我们可能处于 SRCU 读取侧临界区吗?

参数

const struct srcu_struct *ssp

要检查的 srcu_struct 结构

描述

如果选择了 CONFIG_DEBUG_LOCK_ALLOC,则当且仅当在 SRCU 读取侧临界区时返回非零值。在没有 CONFIG_DEBUG_LOCK_ALLOC 的情况下,除非能够证明不是,否则此函数假定我们处于 SRCU 读取侧临界区。

检查 debug_lockdep_rcu_enabled() 以防止在启动时和禁用 lockdep 时出现误报。

请注意,SRCU 基于其自身的状态机,并且不依赖于正常的 RCU,它可以从 RCU 角度处于空闲循环或离线的 CPU 中调用。

srcu_dereference_check

srcu_dereference_check (p, ssp, c)

获取 SRCU 保护的指针,以便稍后取消引用

参数

p

要获取并保护以便稍后取消引用的指针

ssp

指向 srcu_struct 的指针,用于检查我们是否真的处于 SRCU 读取侧临界区。

c

检查更新侧使用的条件

描述

如果启用了 PROVE_RCU,则在 RCU 读取侧临界区之外调用此函数将导致 RCU-lockdep splat,除非 **c** 的计算结果为 1。**c** 参数通常是一个包含 lockdep_is_held() 调用的逻辑表达式。

srcu_dereference

srcu_dereference (p, ssp)

获取 SRCU 保护的指针,以便稍后取消引用

参数

p

要获取并保护以便稍后取消引用的指针

ssp

指向 srcu_struct 的指针,用于检查我们是否真的处于 SRCU 读取侧临界区。

描述

使 rcu_dereference_check() 完成脏活。如果启用了 PROVE_RCU,则在 RCU 读取侧临界区之外调用此函数将导致 RCU-lockdep splat。

srcu_dereference_notrace

srcu_dereference_notrace (p, ssp)

此处不进行跟踪且没有 lockdep 调用

参数

p

要获取并保护以便稍后取消引用的指针

ssp

指向 srcu_struct 的指针,用于检查我们是否真的处于 SRCU 读取侧临界区。

int srcu_read_lock(struct srcu_struct *ssp)

为受 SRCU 保护的结构注册一个新的读取者。

参数

struct srcu_struct *ssp

要在其中注册新读取者的 srcu_struct。

描述

进入 SRCU 读取端临界区。请注意,SRCU 读取端临界区可以嵌套。但是,无论直接还是间接,调用任何等待同一 srcu_struct 的 SRCU 宽限期的操作都是非法的。请注意,间接等待 SRCU 宽限期的一种方法是在调用 synchronize_srcu()synchronize_srcu_expedited() 时,获取其他地方持有的互斥锁。

srcu_read_lock() 返回的值必须原封不动地传递给匹配的 srcu_read_unlock()。请注意,srcu_read_lock() 和匹配的 srcu_read_unlock() 必须发生在同一上下文中,例如,如果在进程上下文中调用了匹配的 srcu_read_lock(),则在 irq 处理程序中调用 srcu_read_unlock() 是非法的。或者,从一个任务调用 srcu_read_unlock(),从另一个任务调用匹配的 srcu_read_lock() 也是非法的。

int srcu_read_lock_lite(struct srcu_struct *ssp)

为受 SRCU 保护的结构注册一个新的读取者。

参数

struct srcu_struct *ssp

要在其中注册新读取者的 srcu_struct。

描述

进入 SRCU 读取端临界区,但用于轻量级的无 smp_mb() 的读取器。有关详细信息,请参阅 srcu_read_lock()

如果曾经在 srcu_struct 结构上使用 srcu_read_lock_lite(),则无论之前、期间或之后,都不得使用其他任何风格。请注意,_lite srcu_struct 结构禁用了宽限期自动加速,因为自动加速的宽限期会调用 synchronize_rcu_expedited()、IPI 等。

请注意,只能从 RCU 正在监视的上下文中调用 srcu_read_lock_lite(),也就是说,从调用 rcu_read_lock() 合法的上下文中调用。否则,lockdep 会报错。

int srcu_read_lock_nmisafe(struct srcu_struct *ssp)

为受 SRCU 保护的结构注册一个新的读取者。

参数

struct srcu_struct *ssp

要在其中注册新读取者的 srcu_struct。

描述

进入 SRCU 读取端临界区,但以 NMI 安全的方式进行。有关详细信息,请参阅 srcu_read_lock()

如果曾经在 srcu_struct 结构上使用 srcu_read_lock_nmisafe(),则无论之前、期间或之后,都不得使用其他任何风格。

int srcu_down_read(struct srcu_struct *ssp)

为受 SRCU 保护的结构注册一个新的读取者。

参数

struct srcu_struct *ssp

要在其中注册新读取者的 srcu_struct。

描述

进入类似信号量的 SRCU 读取端临界区。请注意,SRCU 读取端临界区可以嵌套。但是,无论直接还是间接,调用任何等待同一 srcu_struct 的 SRCU 宽限期的操作都是非法的。请注意,间接等待 SRCU 宽限期的一种方法是在调用 synchronize_srcu()synchronize_srcu_expedited() 时,获取其他地方持有的互斥锁。但是,如果您希望 lockdep 帮助您理清这些内容,则应改用 srcu_read_lock()

srcu_down_read() 的类似信号量的性质意味着匹配的 srcu_up_read() 可以从其他一些上下文中调用,例如,从其他一些任务或从 irq 处理程序中调用。但是,不得从 NMI 处理程序中调用 srcu_down_read()srcu_up_read()

srcu_down_read() 的调用可以嵌套,类似于对 down_read() 的调用可以嵌套的方式。

void srcu_read_unlock(struct srcu_struct *ssp, int idx)

从受 SRCU 保护的结构中注销旧的读取器。

参数

struct srcu_struct *ssp

要在其中注销旧读取器的 srcu_struct。

int idx

来自相应的 srcu_read_lock() 的返回值。

描述

退出 SRCU 读取端临界区。

void srcu_read_unlock_lite(struct srcu_struct *ssp, int idx)

从受 SRCU 保护的结构中注销旧的读取器。

参数

struct srcu_struct *ssp

要在其中注销旧读取器的 srcu_struct。

int idx

来自相应的 srcu_read_lock() 的返回值。

描述

退出轻量级 SRCU 读取端临界区。

void srcu_read_unlock_nmisafe(struct srcu_struct *ssp, int idx)

从受 SRCU 保护的结构中注销旧的读取器。

参数

struct srcu_struct *ssp

要在其中注销旧读取器的 srcu_struct。

int idx

来自相应的 srcu_read_lock() 的返回值。

描述

退出 SRCU 读取端临界区,但以 NMI 安全的方式进行。

void srcu_up_read(struct srcu_struct *ssp, int idx)

从受 SRCU 保护的结构中注销旧的读取器。

参数

struct srcu_struct *ssp

要在其中注销旧读取器的 srcu_struct。

int idx

来自相应的 srcu_read_lock() 的返回值。

描述

退出 SRCU 读取端临界区,但不一定与匹配的 srcu_down_read() 来自同一上下文。

void smp_mb__after_srcu_read_unlock(void)

确保在 srcu_read_unlock 之后完全排序

参数

void

无参数

描述

将前面的 srcu_read_unlock 转换为双向内存屏障。

在 srcu_read_unlock 之后调用此函数,以保证在 smp_mb__after_srcu_read_unlock 之后发生的所有内存操作都会在前面的 srcu_read_unlock 之后发生。

void smp_mb__after_srcu_read_lock(void)

确保在 srcu_read_lock 之后完全排序

参数

void

无参数

描述

将前面的 srcu_read_lock 转换为双向内存屏障。

在 srcu_read_lock 之后调用此函数,以保证在 smp_mb__after_srcu_read_lock 之后发生的所有内存操作都会在前面的 srcu_read_lock 之后发生。

int init_srcu_struct(struct srcu_struct *ssp)

初始化睡眠 RCU 结构

参数

struct srcu_struct *ssp

要初始化的结构。

描述

在将给定的 srcu_struct 传递给任何其他函数之前,必须在给定的 srcu_struct 上调用此函数。每个 srcu_struct 代表一个单独的 SRCU 保护域。

bool srcu_readers_active(struct srcu_struct *ssp)

如果存在读者则返回 true,否则返回 false。

参数

struct srcu_struct *ssp

要统计活动读者的(持有 srcu_read_lock)的 srcu_struct。

描述

请注意,这不是一个原子操作,因此在活动 srcu_struct 上调用时可能会出现严重错误。即便如此,它在清理时可以用作错误检查。

void cleanup_srcu_struct(struct srcu_struct *ssp)

解构一个 sleep-RCU 结构。

参数

struct srcu_struct *ssp

要清理的结构。

描述

必须在使用通过 init_srcu_struct() 初始化的给定 srcu_struct 后调用此函数,否则会发生内存泄漏。

void call_srcu(struct srcu_struct *ssp, struct rcu_head *rhp, rcu_callback_t func)

将回调排队,以便在 SRCU 宽限期后调用。

参数

struct srcu_struct *ssp

将回调排队的 srcu_struct。

struct rcu_head *rhp

用于将 SRCU 回调排队的结构。

rcu_callback_t func

SRCU 宽限期过后要调用的函数。

描述

回调函数将在完整的 SRCU 宽限期结束后的一段时间调用,换句话说,在所有预先存在的 SRCU 读端临界区完成后调用。但是,回调函数很可能与在调用 call_srcu() 后启动的其他 SRCU 读端临界区并发执行。SRCU 读端临界区由 srcu_read_lock()srcu_read_unlock() 分隔,并且可以嵌套。

回调将从进程上下文中调用,但必须快速且不得阻塞。

void synchronize_srcu_expedited(struct srcu_struct *ssp)

强制 SRCU 宽限期。

参数

struct srcu_struct *ssp

要同步的 srcu_struct。

描述

等待 SRCU 宽限期过去,但在等待时更倾向于自旋而不是阻塞。

请注意,synchronize_srcu_expedited() 具有与 synchronize_srcu() 相同的死锁和内存排序属性。

void synchronize_srcu(struct srcu_struct *ssp)

等待先前的 SRCU 读端临界区完成。

参数

struct srcu_struct *ssp

要同步的 srcu_struct。

描述

等待两个索引的计数都变为零。为了避免 synchronize_srcu() 可能发生的饥饿,它首先等待索引=((->srcu_idx & 1) ^ 1) 的计数变为零,然后翻转 srcu_idx 并等待另一个索引的计数。

可以阻塞;必须从进程上下文中调用。

请注意,从相应的 SRCU 读端临界区调用 synchronize_srcu() 是非法的;这样做会导致死锁。但是,从某个其他 srcu_struct 的读端临界区调用一个 srcu_struct 上的 synchronize_srcu() 是完全合法的,只要生成的 srcu_struct 图是无环的即可。

synchronize_srcu() 隐含了内存排序约束。在具有多个 CPU 的系统上,当 synchronize_srcu() 返回时,保证每个 CPU 都已经执行了完整的内存屏障,其时间点是在它最后一个对应的 SRCU 读端临界区结束之后,并且该临界区的开始早于对 synchronize_srcu() 的调用。此外,保证每个具有扩展到从 synchronize_srcu() 返回后的 SRCU 读端临界区的 CPU 在 synchronize_srcu() 开始之后并且在该 SRCU 读端临界区开始之前执行了完整的内存屏障。请注意,这些保证包括处于脱机、空闲或在用户模式下执行的 CPU,以及在内核中执行的 CPU。

此外,如果 CPU A 调用了 synchronize_srcu(),该调用在 CPU B 上返回到其调用者,则保证 CPU A 和 CPU B 都在 synchronize_srcu() 的执行过程中执行了完整的内存屏障。即使 CPU A 和 CPU B 是同一个 CPU,此保证也适用,但前提是系统具有多个 CPU。

当然,这些内存排序保证仅在将相同的 srcu_struct 结构传递给 synchronize_srcu()srcu_read_lock()srcu_read_unlock() 时才适用。

这些内存排序保证的实现方式类似于 synchronize_rcu()

如果 SRCU 可能处于空闲状态(由 srcu_should_expedite() 确定),则加快第一个请求。经典 SRCU 提供了此语义,并且它的用户依赖于此语义,因此 TREE SRCU 也必须提供此语义。请注意,检测空闲状态是启发式的,并且会存在误报和漏报。

unsigned long get_state_synchronize_srcu(struct srcu_struct *ssp)

提供宽限期结束 Cookie。

参数

struct srcu_struct *ssp

为其提供 Cookie 的 srcu_struct。

描述

此函数返回一个 Cookie,该 Cookie 可以传递给 poll_state_synchronize_srcu(),如果在此期间经过了完整的宽限期,则该函数将返回 true。调用者有责任确保发生宽限期,例如,通过在从 get_state_synchronize_srcu() 返回后调用 call_srcu()

unsigned long start_poll_synchronize_srcu(struct srcu_struct *ssp)

提供 Cookie 并启动宽限期。

参数

struct srcu_struct *ssp

为其提供 Cookie 的 srcu_struct。

描述

此函数返回一个 cookie,该 cookie 可以传递给 poll_state_synchronize_srcu(),如果在此期间经过了完整的宽限期,则该函数将返回 true。与 get_state_synchronize_srcu() 不同,此函数还会确保启动任何需要的 SRCU 宽限期。这种便利性确实会带来 CPU 开销方面的代价。

bool poll_state_synchronize_srcu(struct srcu_struct *ssp, unsigned long cookie)

cookie 的宽限期是否已结束?

参数

struct srcu_struct *ssp

为其提供 Cookie 的 srcu_struct。

unsigned long cookie

get_state_synchronize_srcu()start_poll_synchronize_srcu() 返回的值。

描述

此函数接受从 get_state_synchronize_srcu()start_poll_synchronize_srcu() 返回的 cookie,如果自创建 cookie 以来经过了 SRCU 宽限期,则返回 true

由于 cookie 的大小是有限的,因此可能会发生环绕/溢出。这在 32 位系统上更为明显,其中 cookie 为 32 位,理论上,假设 25 微秒的加速 SRCU 宽限期,环绕可能在大约 14 小时内发生。但是,在 1 毫秒 SRCU 宽限期的情况下,更可能的溢出下限约为 24 天。当然,在 64 位系统中发生环绕需要地质时间跨度,即使对于加速 SRCU 宽限期,也需要超过七百万年。

对于 CONFIG_SMP=n 且 CONFIG_PREEMPTION=n 的系统(它们选择 Tiny SRCU),环绕/溢出问题更为严重。它使用 16 位 cookie,rcutorture 通常会在几分钟内环绕。如果这被证明是一个问题,则此计数器将扩展到与 Tree SRCU 相同的大小。

void srcu_barrier(struct srcu_struct *ssp)

等待所有正在进行的 call_srcu() 回调完成。

参数

struct srcu_struct *ssp

等待正在进行的回调的 srcu_struct。

unsigned long srcu_batches_completed(struct srcu_struct *ssp)

返回已完成的批次。

参数

struct srcu_struct *ssp

报告批次完成情况的 srcu_struct。

描述

报告与已过去的宽限期数量相关但不必完全相同的批次数量。

void hlist_bl_del_rcu(struct hlist_bl_node *n)

从哈希列表中删除条目,而无需重新初始化

参数

struct hlist_bl_node *n

要从哈希列表中删除的元素。

注意

入口处的 hlist_bl_unhashed() 在此之后不会返回 true,该条目处于未定义状态。它对于基于 RCU 的无锁遍历很有用。

描述

特别是,这意味着我们不能破坏可能仍用于遍历哈希列表的前向指针。

调用者必须采取任何必要的预防措施(例如持有适当的锁)以避免与在此同一列表上运行的另一个列表修改原语(例如 hlist_bl_add_head_rcu()hlist_bl_del_rcu())发生竞争。但是,与 _rcu 列表遍历原语(例如 hlist_bl_for_each_entry())同时运行是完全合法的。

void hlist_bl_add_head_rcu(struct hlist_bl_node *n, struct hlist_bl_head *h)

参数

struct hlist_bl_node *n

要添加到哈希列表的元素。

struct hlist_bl_head *h

要添加到的列表。

描述

将指定的元素添加到指定的 hlist_bl,同时允许竞争遍历。

调用者必须采取任何必要的预防措施(例如持有适当的锁)以避免与在此同一列表上运行的另一个列表修改原语(例如 hlist_bl_add_head_rcu()hlist_bl_del_rcu())发生竞争。但是,与 _rcu 列表遍历原语(例如 hlist_bl_for_each_entry_rcu())同时运行是完全合法的,该原语用于防止 Alpha CPU 上的内存一致性问题。无论 CPU 的类型如何,列表遍历原语都必须受 rcu_read_lock() 保护。

hlist_bl_for_each_entry_rcu

hlist_bl_for_each_entry_rcu (tpos, pos, head, member)

迭代给定类型的 rcu 列表

参数

tpos

用作循环游标的 type *。

pos

用作循环游标的 struct hlist_bl_node

head

您的列表的头部。

member

结构体中 hlist_bl_node 的名称。

list_tail_rcu

list_tail_rcu (head)

返回列表头部的 prev 指针

参数

head

列表的头

注意

这应该仅与列表头部一起使用,即使如此,也只有当 list_del() 和类似的原语也未在列表头部上使用时才应使用。

void list_add_rcu(struct list_head *new, struct list_head *head)

将新条目添加到受 rcu 保护的列表中

参数

struct list_head *new

要添加的新条目

struct list_head *head

要添加到其后的列表头

描述

在指定的头之后插入新条目。 这对于实现堆栈很有用。

调用者必须采取任何必要的预防措施(例如持有适当的锁)以避免与在此同一列表上运行的另一个列表修改原语(例如 list_add_rcu()list_del_rcu())发生竞争。但是,与 _rcu 列表遍历原语(例如 list_for_each_entry_rcu())同时运行是完全合法的。

void list_add_tail_rcu(struct list_head *new, struct list_head *head)

将新条目添加到受 rcu 保护的列表中

参数

struct list_head *new

要添加的新条目

struct list_head *head

要添加到其前的列表头

描述

在指定的头之前插入新条目。 这对于实现队列很有用。

调用者必须采取任何必要的预防措施(例如持有适当的锁)以避免与在此同一列表上运行的另一个列表修改原语(例如 list_add_tail_rcu()list_del_rcu())发生竞争。但是,与 _rcu 列表遍历原语(例如 list_for_each_entry_rcu())同时运行是完全合法的。

void list_del_rcu(struct list_head *entry)

从列表中删除条目,但不重新初始化。

参数

struct list_head *entry

要从列表中删除的元素。

注意

在此操作之后,在条目上调用list_empty()不会返回 true,该条目处于未定义状态。 它对于基于 RCU 的无锁遍历非常有用。

描述

特别是,这意味着我们不能破坏可能仍用于遍历列表的前向指针。

调用者必须采取必要的预防措施(例如持有适当的锁),以避免与在此同一列表上运行的其他列表修改原语(例如 list_del_rcu()list_add_rcu())发生竞争。 但是,与 _rcu 列表遍历原语(例如 list_for_each_entry_rcu())并发运行是完全合法的。

请注意,不允许调用者立即释放新删除的条目。 相反,必须使用 synchronize_rcu()call_rcu() 来延迟释放,直到 RCU 宽限期过去。

void hlist_del_init_rcu(struct hlist_node *n)

从哈希列表中删除条目并重新初始化。

参数

struct hlist_node *n

要从哈希列表中删除的元素。

注意

在此操作之后,节点上的 list_unhashed() 返回 true。 如果写入端必须知道列表条目是否仍然哈希或已经取消哈希,则它对于基于 RCU 的无锁读取遍历非常有用。

描述

特别是,这意味着我们不能破坏可能仍用于遍历哈希列表的前向指针,并且我们只能将 pprev 指针清零,以便在此操作之后 list_unhashed() 将返回 true。

调用者必须采取必要的预防措施(例如持有适当的锁),以避免与在此同一列表上运行的其他列表修改原语(例如 hlist_add_head_rcu()hlist_del_rcu())发生竞争。 但是,与 _rcu 列表遍历原语(例如 hlist_for_each_entry_rcu())并发运行是完全合法的。

void list_replace_rcu(struct list_head *old, struct list_head *new)

用新条目替换旧条目

参数

struct list_head *old

要替换的元素

struct list_head *new

要插入的新元素

描述

从并发读取器的角度来看,old 条目将被原子地替换为 new 条目。 调用者有责任与任何并发更新器同步。

注意

old 不应为空。

void __list_splice_init_rcu(struct list_head *list, struct list_head *prev, struct list_head *next, void (*sync)(void))

将受 RCU 保护的列表连接到现有列表。

参数

struct list_head *list

要拼接的受 RCU 保护的列表

struct list_head *prev

指向现有列表的最后一个元素

struct list_head *next

指向现有列表的第一个元素

void (*sync)(void)

synchronize_rcu、synchronize_rcu_expedited、...

描述

prevnext 指向的列表可以与此函数并发地进行 RCU 读取遍历。

请注意,此函数会阻塞。

重要说明:调用者必须采取一切必要的措施来防止对现有列表进行任何其他更新。 原则上,一旦 sync() 开始执行,就可以修改列表。 如果有必要,可以创建一个基于 call_rcu() 的替代版本。 但只有在真正需要时才这样做 -- RCU API 成员并不短缺。

void list_splice_init_rcu(struct list_head *list, struct list_head *head, void (*sync)(void))

将受 RCU 保护的列表拼接到一个现有列表中,专为堆栈设计。

参数

struct list_head *list

要拼接的受 RCU 保护的列表

struct list_head *head

要将第一个列表拼接到的现有列表中的位置

void (*sync)(void)

synchronize_rcu、synchronize_rcu_expedited、...

void list_splice_tail_init_rcu(struct list_head *list, struct list_head *head, void (*sync)(void))

将受 RCU 保护的列表拼接到一个现有列表中,专为队列设计。

参数

struct list_head *list

要拼接的受 RCU 保护的列表

struct list_head *head

要将第一个列表拼接到的现有列表中的位置

void (*sync)(void)

synchronize_rcu、synchronize_rcu_expedited、...

list_entry_rcu

list_entry_rcu (ptr, type, member)

获取此条目的结构

参数

ptr

struct list_head 指针。

type

嵌入此结构的结构类型。

member

结构中 list_head 的名称。

描述

此原语可以与 _rcu 列表修改原语(例如 list_add_rcu())安全地并发运行,只要它受到 rcu_read_lock() 的保护即可。

list_first_or_null_rcu

list_first_or_null_rcu (ptr, type, member)

从列表中获取第一个元素

参数

ptr

从中获取元素的列表头部。

type

嵌入此结构的结构类型。

member

结构中 list_head 的名称。

描述

请注意,如果列表为空,则返回 NULL。

此原语可以与 _rcu 列表修改原语(例如 list_add_rcu())安全地并发运行,只要它受到 rcu_read_lock() 的保护即可。

list_next_or_null_rcu

list_next_or_null_rcu (head, ptr, type, member)

从列表中获取下一个元素

参数

head

列表的头。

ptr

从中获取下一个元素的列表头。

type

嵌入此结构的结构类型。

member

结构中 list_head 的名称。

描述

请注意,如果 ptr 位于列表的末尾,则返回 NULL。

此原语可以与 _rcu 列表修改原语(例如 list_add_rcu())安全地并发运行,只要它受到 rcu_read_lock() 的保护即可。

list_for_each_entry_rcu

list_for_each_entry_rcu (pos, head, member, cond...)

迭代给定类型的 rcu 列表

参数

pos

用作循环游标的 type *。

head

您的列表的头部。

member

结构中 list_head 的名称。

cond...

如果从非 RCU 保护调用,则为可选的 lockdep 表达式。

描述

此列表遍历原语可以与 _rcu 列表修改原语(例如 list_add_rcu())安全地并发运行,只要遍历受 rcu_read_lock() 的保护即可。

list_for_each_entry_srcu

list_for_each_entry_srcu (pos, head, member, cond)

迭代给定类型的 rcu 列表

参数

pos

用作循环游标的 type *。

head

您的列表的头部。

member

结构中 list_head 的名称。

cond

用于遍历列表所需的锁的 lockdep 表达式。

描述

只要遍历操作受到 srcu_read_lock() 的保护,此列表遍历原语可以安全地与 _rcu 列表修改原语(如 list_add_rcu())并发运行。lockdep 表达式 srcu_read_lock_held() 可以作为读取端的 cond 参数传递。

list_entry_lockless

list_entry_lockless (ptr, type, member)

获取此条目的结构

参数

ptr

struct list_head 指针。

type

嵌入此结构的结构类型。

member

结构中 list_head 的名称。

描述

此原语可以安全地与 _rcu 列表修改原语(如 list_add_rcu())并发运行,但需要一些隐式的 RCU 读取端保护。一个例子是在禁用抢占且无法调用 lockdep 的特殊异常时间环境中运行。另一个例子是当项目被添加到列表中,但永远不会被删除时。

list_for_each_entry_lockless

list_for_each_entry_lockless (pos, head, member)

迭代给定类型的 rcu 列表

参数

pos

用作循环游标的 type *。

head

您的列表的头部。

member

结构体中 list_struct 的名称。

描述

此原语可以安全地与 _rcu 列表修改原语(如 list_add_rcu())并发运行,但需要一些隐式的 RCU 读取端保护。一个例子是在禁用抢占且无法调用 lockdep 的特殊异常时间环境中运行。另一个例子是当项目被添加到列表中,但永远不会被删除时。

list_for_each_entry_continue_rcu

list_for_each_entry_continue_rcu (pos, head, member)

继续遍历给定类型的列表

参数

pos

用作循环游标的 type *。

head

您的列表的头部。

member

结构中 list_head 的名称。

描述

继续迭代给定类型的列表,从当前位置之后继续,该位置必须在获取 RCU 读取锁时位于列表中。这通常需要您要么从同一 RCU 读取端临界区中列表的先前遍历中获取该节点,要么持有某种非 RCU 引用(例如引用计数)以保持节点处于活动状态 *并且* 在列表中。

此迭代器类似于 list_for_each_entry_from_rcu(),不同之处在于此迭代器从给定位置之后开始,而另一个迭代器从给定位置开始。

list_for_each_entry_from_rcu

list_for_each_entry_from_rcu (pos, head, member)

从当前点开始迭代列表

参数

pos

用作循环游标的 type *。

head

您的列表的头部。

member

结构体中 list_node 的名称。

描述

从给定位置开始迭代列表的尾部,该位置必须在获取 RCU 读取锁时位于列表中。这通常需要您要么从同一 RCU 读取端临界区中列表的先前遍历中获取该节点,要么持有某种非 RCU 引用(例如引用计数)以保持节点处于活动状态 *并且* 在列表中。

此迭代器类似于 list_for_each_entry_continue_rcu(),不同之处在于此迭代器从给定位置开始,而另一个迭代器从给定位置之后的位置开始。

void hlist_del_rcu(struct hlist_node *n)

从哈希列表中删除条目,而无需重新初始化

参数

struct hlist_node *n

要从哈希列表中删除的元素。

注意

在入口处的 list_unhashed() 在此之后不会返回 true,该条目处于未定义状态。它对于基于 RCU 的无锁遍历非常有用。

描述

特别是,这意味着我们不能破坏可能仍用于遍历哈希列表的前向指针。

调用者必须采取必要的预防措施(例如持有适当的锁)以避免与同一列表上运行的另一个列表修改原语(例如 hlist_add_head_rcu()hlist_del_rcu())发生竞争。但是,与 _rcu 列表遍历原语(例如 hlist_for_each_entry())并发运行是完全合法的。

void hlist_replace_rcu(struct hlist_node *old, struct hlist_node *new)

用新条目替换旧条目

参数

struct hlist_node *old

要替换的元素

struct hlist_node *new

要插入的新元素

描述

从并发读取器的角度来看,old 条目将被原子地替换为 new 条目。 调用者有责任与任何并发更新器同步。

void hlists_swap_heads_rcu(struct hlist_head *left, struct hlist_head *right)

交换 hlist 头指向的列表

参数

struct hlist_head *left

左侧的 hlist 头

struct hlist_head *right

右侧的 hlist 头

描述

列表的起始状态为 [left ][node1 ... ] 和

[right ][node2 ... ]

列表的最终状态为 [left ][node2 ... ]

[right ][node1 ... ]

void hlist_add_head_rcu(struct hlist_node *n, struct hlist_head *h)

参数

struct hlist_node *n

要添加到哈希列表的元素。

struct hlist_head *h

要添加到的列表。

描述

将指定的元素添加到指定的 hlist,同时允许竞争遍历。

调用者必须采取必要的预防措施(例如持有适当的锁)以避免与同一列表上运行的另一个列表修改原语(例如 hlist_add_head_rcu()hlist_del_rcu())发生竞争。但是,与 _rcu 列表遍历原语(例如 hlist_for_each_entry_rcu())并发运行是完全合法的,用于防止 Alpha CPU 上的内存一致性问题。无论 CPU 的类型如何,列表遍历原语都必须受到 rcu_read_lock() 的保护。

void hlist_add_tail_rcu(struct hlist_node *n, struct hlist_head *h)

参数

struct hlist_node *n

要添加到哈希列表的元素。

struct hlist_head *h

要添加到的列表。

描述

将指定的元素添加到指定的 hlist,同时允许竞争遍历。

调用者必须采取必要的预防措施(例如持有适当的锁)以避免与同一列表上运行的另一个列表修改原语(例如 hlist_add_head_rcu()hlist_del_rcu())发生竞争。但是,与 _rcu 列表遍历原语(例如 hlist_for_each_entry_rcu())并发运行是完全合法的,用于防止 Alpha CPU 上的内存一致性问题。无论 CPU 的类型如何,列表遍历原语都必须受到 rcu_read_lock() 的保护。

void hlist_add_before_rcu(struct hlist_node *n, struct hlist_node *next)

参数

struct hlist_node *n

要添加到哈希列表的新元素。

struct hlist_node *next

要将新元素添加在其之前的现有元素。

描述

将指定的元素添加到指定的 hlist 中的指定节点之前,同时允许竞争遍历。

调用者必须采取必要的预防措施(例如持有适当的锁)以避免与同一列表上运行的另一个列表修改原语(例如 hlist_add_head_rcu()hlist_del_rcu())发生竞争。但是,与 _rcu 列表遍历原语(例如 hlist_for_each_entry_rcu())并发运行是完全合法的,用于防止 Alpha CPU 上的内存一致性问题。

void hlist_add_behind_rcu(struct hlist_node *n, struct hlist_node *prev)

参数

struct hlist_node *n

要添加到哈希列表的新元素。

struct hlist_node *prev

新元素将添加在其后的现有元素。

描述

在允许并发遍历的情况下,将指定元素添加到指定节点的指定 hlist 之后。

调用者必须采取必要的预防措施(例如持有适当的锁)以避免与同一列表上运行的另一个列表修改原语(例如 hlist_add_head_rcu()hlist_del_rcu())发生竞争。但是,与 _rcu 列表遍历原语(例如 hlist_for_each_entry_rcu())并发运行是完全合法的,用于防止 Alpha CPU 上的内存一致性问题。

hlist_for_each_entry_rcu

hlist_for_each_entry_rcu (pos, head, member, cond...)

迭代给定类型的 rcu 列表

参数

pos

用作循环游标的 type *。

head

您的列表的头部。

member

结构体中 hlist_node 的名称。

cond...

如果从非 RCU 保护调用,则为可选的 lockdep 表达式。

描述

只要遍历操作受 rcu_read_lock() 保护,此列表遍历原语就可以安全地与 _rcu 列表修改原语(如 hlist_add_head_rcu())并发运行。

hlist_for_each_entry_srcu

hlist_for_each_entry_srcu (pos, head, member, cond)

迭代给定类型的 rcu 列表

参数

pos

用作循环游标的 type *。

head

您的列表的头部。

member

结构体中 hlist_node 的名称。

cond

用于遍历列表所需的锁的 lockdep 表达式。

描述

只要遍历操作受 srcu_read_lock() 保护,此列表遍历原语就可以安全地与 _rcu 列表修改原语(如 hlist_add_head_rcu())并发运行。 lockdep 表达式 srcu_read_lock_held() 可以作为读取端的 cond 参数传递。

hlist_for_each_entry_rcu_notrace

hlist_for_each_entry_rcu_notrace (pos, head, member)

迭代给定类型的 rcu 列表(用于跟踪)。

参数

pos

用作循环游标的 type *。

head

您的列表的头部。

member

结构体中 hlist_node 的名称。

描述

只要遍历操作受 rcu_read_lock() 保护,此列表遍历原语就可以安全地与 _rcu 列表修改原语(如 hlist_add_head_rcu())并发运行。

这与 hlist_for_each_entry_rcu() 相同,只是它不执行任何 RCU 调试或跟踪。

hlist_for_each_entry_rcu_bh

hlist_for_each_entry_rcu_bh (pos, head, member)

迭代给定类型的 rcu 列表

参数

pos

用作循环游标的 type *。

head

您的列表的头部。

member

结构体中 hlist_node 的名称。

描述

只要遍历操作受 rcu_read_lock() 保护,此列表遍历原语就可以安全地与 _rcu 列表修改原语(如 hlist_add_head_rcu())并发运行。

hlist_for_each_entry_continue_rcu

hlist_for_each_entry_continue_rcu (pos, member)

在当前点之后继续迭代 hlist

参数

pos

用作循环游标的 type *。

member

结构体中 hlist_node 的名称。

hlist_for_each_entry_continue_rcu_bh

hlist_for_each_entry_continue_rcu_bh (pos, member)

在当前点之后继续迭代 hlist

参数

pos

用作循环游标的 type *。

member

结构体中 hlist_node 的名称。

hlist_for_each_entry_from_rcu

hlist_for_each_entry_from_rcu (pos, member)

从当前点继续迭代 hlist

参数

pos

用作循环游标的 type *。

member

结构体中 hlist_node 的名称。

void hlist_nulls_del_init_rcu(struct hlist_nulls_node *n)

从哈希列表中删除条目并重新初始化。

参数

struct hlist_nulls_node *n

要从哈希列表中删除的元素。

注意

在此之后,节点上的 hlist_nulls_unhashed() 返回 true。如果写入方必须知道列表项是否仍然已哈希或已取消哈希,这对于基于 RCU 的无锁读取遍历非常有用。

描述

特别是,这意味着我们不能破坏可能仍用于遍历哈希列表的前向指针,并且我们只能将 pprev 指针清零,以便在此操作之后 list_unhashed() 将返回 true。

调用方必须采取必要的预防措施(例如持有适当的锁)以避免与在同一列表上运行的另一个列表修改原语(例如 hlist_nulls_add_head_rcu()hlist_nulls_del_rcu())发生冲突。但是,它可以与 _rcu 列表遍历原语(例如 hlist_nulls_for_each_entry_rcu())并发运行,这是完全合法的。

hlist_nulls_first_rcu

hlist_nulls_first_rcu (head)

返回哈希列表的第一个元素。

参数

head

列表的头部。

hlist_nulls_next_rcu

hlist_nulls_next_rcu (node)

返回 **node** 之后的列表元素。

参数

node

列表的元素。

void hlist_nulls_del_rcu(struct hlist_nulls_node *n)

从哈希列表中删除条目,而无需重新初始化

参数

struct hlist_nulls_node *n

要从哈希列表中删除的元素。

注意

条目上的 hlist_nulls_unhashed() 在此之后不返回 true,条目处于未定义状态。这对于基于 RCU 的无锁遍历非常有用。

描述

特别是,这意味着我们不能破坏可能仍用于遍历哈希列表的前向指针。

调用方必须采取必要的预防措施(例如持有适当的锁)以避免与在同一列表上运行的另一个列表修改原语(例如 hlist_nulls_add_head_rcu()hlist_nulls_del_rcu())发生冲突。但是,它可以与 _rcu 列表遍历原语(例如 hlist_nulls_for_each_entry())并发运行,这是完全合法的。

void hlist_nulls_add_head_rcu(struct hlist_nulls_node *n, struct hlist_nulls_head *h)

参数

struct hlist_nulls_node *n

要添加到哈希列表的元素。

struct hlist_nulls_head *h

要添加到的列表。

描述

在允许并发遍历的情况下,将指定的元素添加到指定的 hlist_nulls。

调用方必须采取必要的预防措施(例如持有适当的锁)以避免与在同一列表上运行的另一个列表修改原语(例如 hlist_nulls_add_head_rcu()hlist_nulls_del_rcu())发生冲突。但是,它可以与 _rcu 列表遍历原语(例如 hlist_nulls_for_each_entry_rcu())并发运行,用于防止 Alpha CPU 上的内存一致性问题。无论 CPU 的类型如何,列表遍历原语都必须受 rcu_read_lock() 保护。

void hlist_nulls_add_tail_rcu(struct hlist_nulls_node *n, struct hlist_nulls_head *h)

参数

struct hlist_nulls_node *n

要添加到哈希列表的元素。

struct hlist_nulls_head *h

要添加到的列表。

描述

在允许并发遍历的情况下,将指定的元素添加到指定的 hlist_nulls。

调用方必须采取必要的预防措施(例如持有适当的锁)以避免与在同一列表上运行的另一个列表修改原语(例如 hlist_nulls_add_head_rcu()hlist_nulls_del_rcu())发生冲突。但是,它可以与 _rcu 列表遍历原语(例如 hlist_nulls_for_each_entry_rcu())并发运行,用于防止 Alpha CPU 上的内存一致性问题。无论 CPU 的类型如何,列表遍历原语都必须受 rcu_read_lock() 保护。

hlist_nulls_for_each_entry_rcu

hlist_nulls_for_each_entry_rcu (tpos, pos, head, member)

迭代给定类型的 rcu 列表

参数

tpos

用作循环游标的 type *。

pos

用作循环游标的 struct hlist_nulls_node

head

列表的头部。

member

结构体中 hlist_nulls_node 的名称。

描述

需要使用 barrier() 来确保编译器不会缓存第一个元素 [1],因为此循环可以重新启动 [2] [1] Documentation/memory-barriers.txt 大约在第 1533 行 [2] 使用 RCU hlist_nulls 保护列表和对象 大约在第 146 行

hlist_nulls_for_each_entry_safe

hlist_nulls_for_each_entry_safe (tpos, pos, head, member)

遍历给定类型的列表,防止删除列表条目

参数

tpos

用作循环游标的 type *。

pos

用作循环游标的 struct hlist_nulls_node

head

列表的头部。

member

结构体中 hlist_nulls_node 的名称。

bool rcu_sync_is_idle(struct rcu_sync *rsp)

是否允许读取器使用他们的快速路径?

参数

struct rcu_sync *rsp

指向用于同步的 rcu_sync 结构的指针

描述

如果允许读取器使用他们的快速路径,则返回 true。必须在某种 RCU 读取端临界区中调用。

void rcu_sync_init(struct rcu_sync *rsp)

初始化 rcu_sync 结构

参数

struct rcu_sync *rsp

指向要初始化的 rcu_sync 结构的指针

void rcu_sync_func(struct rcu_head *rhp)

管理读者对快速路径访问的回调函数

参数

struct rcu_head *rhp

指向 rcu_sync 结构中用于同步的 rcu_head 的指针

描述

此函数由 rcu_sync_enter()rcu_sync_exit() 传递给 call_rcu() 函数,以便在 enter/exit 调用之后的宽限期后调用它。

如果它由 rcu_sync_enter() 调用,则表示所有读者都已切换到慢速路径。

如果它由 rcu_sync_exit() 调用,则会根据这段时间发生的事件采取行动,以便紧密间隔的 rcu_sync_enter()rcu_sync_exit() 对无需等待宽限期。

如果在宽限期结束之前调用了另一个 rcu_sync_enter(),则重置状态以允许下一个 rcu_sync_exit() 将读者返回到其快速路径(在宽限期之后)。 如果在宽限期结束之前调用了另一个 rcu_sync_enter() 及其匹配的 rcu_sync_exit(),则代表该 rcu_sync_exit() 重新调用 call_rcu()。 否则,将所有状态恢复为空闲,以便读者可以再次使用其快速路径。

void rcu_sync_enter(struct rcu_sync *rsp)

强制读者使用慢速路径

参数

struct rcu_sync *rsp

指向用于同步的 rcu_sync 结构的指针

描述

此函数由更新者使用,他们需要在更新期间让读者使用慢速路径。 此函数返回后,所有后续对 rcu_sync_is_idle() 的调用都将返回 false,这会告诉读者不要使用他们的快速路径。 稍后调用 rcu_sync_exit() 会重新启用读者的快速路径。

当单独调用时,rcu_sync_enter() 必须等待宽限期,但是,紧密间隔的 rcu_sync_enter() 调用可以通过 rcu_sync_enter()rcu_sync_exit()rcu_sync_func() 实现的状态机来优化宽限期等待。

void rcu_sync_exit(struct rcu_sync *rsp)

在宽限期后允许读者返回快速路径

参数

struct rcu_sync *rsp

指向用于同步的 rcu_sync 结构的指针

描述

此函数由已完成的更新者使用,因此现在允许读者在宽限期结束后使用他们的快速路径。 此宽限期完成后,所有后续对 rcu_sync_is_idle() 的调用都将返回 true,这会告诉读者他们可以再次使用他们的快速路径。

void rcu_sync_dtor(struct rcu_sync *rsp)

清理 rcu_sync 结构

参数

struct rcu_sync *rsp

指向要清理的 rcu_sync 结构的指针

struct rcu_tasks_percpu

类似于 Tasks-RCU 机制的每个 CPU 组件的定义。

定义:

struct rcu_tasks_percpu {
    struct rcu_segcblist cblist;
    raw_spinlock_t __private lock;
    unsigned long rtp_jiffies;
    unsigned long rtp_n_lock_retries;
    struct timer_list lazy_timer;
    unsigned int urgent_gp;
    struct work_struct rtp_work;
    struct irq_work rtp_irq_work;
    struct rcu_head barrier_q_head;
    struct list_head rtp_blkd_tasks;
    struct list_head rtp_exit_list;
    int cpu;
    int index;
    struct rcu_tasks *rtpp;
};

成员

cblist

回调列表。

lock

保护每个 CPU 回调列表的锁。

rtp_jiffies

用于统计的 Jiffies 计数器值。

rtp_n_lock_retries

粗略的锁竞争统计信息。

lazy_timer

用于取消延迟回调的计时器。

urgent_gp

额外的非延迟宽限期数。

rtp_work

用于调用回调的工作队列。

rtp_irq_work

用于延迟唤醒的 IRQ 工作队列。

barrier_q_head

用于屏障操作的 RCU 回调。

rtp_blkd_tasks

作为读者被阻塞的任务列表。

rtp_exit_list

do_exit() 后一部分中的任务列表。

cpu

与此条目对应的 CPU 编号。

index

此 CPU 在 rcu_tasks 结构的 rtpcp_array 中的索引。

rtpp

指向 rcu_tasks 结构的指针。

struct rcu_tasks

类似于 Tasks-RCU 机制的定义。

定义:

struct rcu_tasks {
    struct rcuwait cbs_wait;
    raw_spinlock_t cbs_gbl_lock;
    struct mutex tasks_gp_mutex;
    int gp_state;
    int gp_sleep;
    int init_fract;
    unsigned long gp_jiffies;
    unsigned long gp_start;
    unsigned long tasks_gp_seq;
    unsigned long n_ipis;
    unsigned long n_ipis_fails;
    struct task_struct *kthread_ptr;
    unsigned long lazy_jiffies;
    rcu_tasks_gp_func_t gp_func;
    pregp_func_t pregp_func;
    pertask_func_t pertask_func;
    postscan_func_t postscan_func;
    holdouts_func_t holdouts_func;
    postgp_func_t postgp_func;
    call_rcu_func_t call_func;
    unsigned int wait_state;
    struct rcu_tasks_percpu __percpu *rtpcpu;
    struct rcu_tasks_percpu **rtpcp_array;
    int percpu_enqueue_shift;
    int percpu_enqueue_lim;
    int percpu_dequeue_lim;
    unsigned long percpu_dequeue_gpseq;
    struct mutex barrier_q_mutex;
    atomic_t barrier_q_count;
    struct completion barrier_q_completion;
    unsigned long barrier_q_seq;
    unsigned long barrier_q_start;
    char *name;
    char *kname;
};

成员

cbs_wait

RCU 等待允许新的回调引起 kthread 的注意。

cbs_gbl_lock

保护回调列表的锁。

tasks_gp_mutex

保护宽限期的互斥锁,在启动中间的死区期间需要。

gp_state

宽限期最近的状态转换(调试)。

gp_sleep

每个宽限期的睡眠,以防止 CPU 密集型循环。

init_fract

初始退避睡眠间隔。

gp_jiffies

上次 gp_state 转换的时间。

gp_start

最近一次以 jiffies 为单位的宽限期开始时间。

tasks_gp_seq

自启动以来完成的宽限期数,位于高位。

n_ipis

发送以鼓励宽限期结束的 IPI 数。

n_ipis_fails

IPI 发送失败的次数。

kthread_ptr

此风格的宽限期/回调调用 kthread。

lazy_jiffies

允许回调延迟的 jiffies 数。

gp_func

此风格的宽限期等待函数。

pregp_func

此风格的预宽限期函数(可选)。

pertask_func

此风格的每个任务扫描函数(可选)。

postscan_func

此风格的任务后扫描函数(可选)。

holdouts_func

此风格的保留列表扫描函数(可选)。

postgp_func

此风格的后宽限期函数(可选)。

call_func

此风格的 call_rcu() 等效函数。

wait_state

用于同步宽限期等待的任务状态(默认 TASK_UNINTERRUPTIBLE)。

rtpcpu

此风格的 rcu_tasks_percpu 结构。

rtpcp_array

指向 cpu_possible_mask 中 CPU 的 rcu_tasks_percpu 结构的指针数组。

percpu_enqueue_shift

在排队回调时将 CPU ID 向下移动这么多。

percpu_enqueue_lim

用于排队的每个 CPU 回调队列的数量。

percpu_dequeue_lim

用于出队的每个 CPU 回调队列的数量。

percpu_dequeue_gpseq

RCU 宽限期编号,用于将入队限制传播到出队器。

barrier_q_mutex

序列化屏障操作。

barrier_q_count

正在等待的队列数。

barrier_q_completion

屏障等待/唤醒机制。

barrier_q_seq

屏障操作的序列号。

barrier_q_start

最近一次以 jiffies 为单位的屏障开始时间。

name

此风格的文本名称。

kname

此风格的 kthread 名称。

void call_rcu_tasks(struct rcu_head *rhp, rcu_callback_t func)

排队 RCU 以在基于任务的宽限期内调用

参数

struct rcu_head *rhp

用于将 RCU 更新排队的结构。

rcu_callback_t func

宽限期过后要调用的实际回调函数

描述

回调函数将在完整的宽限期结束后的一段时间内调用,换句话说,在所有当前正在执行的 RCU 读取端关键部分完成后调用。 call_rcu_tasks() 假设读取端关键部分在自愿上下文切换(不是抢占!)、cond_resched_tasks_rcu_qs()、进入空闲状态或转换为用户模式执行时结束。 因此,没有类似于 rcu_read_lock()rcu_read_unlock() 的读取端原语,因为此原语旨在确定所有任务都已通过安全状态,而不是用于数据结构同步。

有关内存排序保证的更多详细信息,请参阅 call_rcu() 的描述。

void synchronize_rcu_tasks(void)

等待 rcu-tasks 宽限期结束。

参数

void

无参数

描述

在完整的 rcu-tasks 宽限期结束后,即在所有当前正在执行的 rcu-tasks 读取端临界区结束后,控制权将返回给调用者。这些读取端临界区由对 schedule()、cond_resched_tasks_rcu_qs()、空闲执行、用户空间执行、对 synchronize_rcu_tasks() 的调用以及(理论上)cond_resched() 的调用分隔。

这是一个非常特殊的原语,仅用于跟踪和其他需要操作函数前导码和分析挂钩的少数情况。synchronize_rcu_tasks() 函数(尚未)不打算在多个 CPU 上大量使用。

有关内存排序保证的更多详细信息,请参阅 synchronize_rcu() 的描述。

void rcu_barrier_tasks(void)

等待正在进行的 call_rcu_tasks() 回调。

参数

void

无参数

描述

尽管当前实现保证会等待,但它没有义务等待,例如,如果没有挂起的回调。

void synchronize_rcu_tasks_rude(void)

等待粗暴的 rcu-tasks 宽限期

参数

void

无参数

描述

在粗暴的 rcu-tasks 宽限期结束后,即在所有当前正在执行的 rcu-tasks 读取端临界区结束后,控制权将返回给调用者。这些读取端临界区由对 schedule()、cond_resched_tasks_rcu_qs()、用户空间执行(这是一个可调度的上下文)以及(理论上)cond_resched() 的调用分隔。

这是一个非常特殊的原语,仅用于跟踪和其他需要操作函数前导码和分析挂钩的少数情况。synchronize_rcu_tasks_rude() 函数(尚未)不打算在多个 CPU 上大量使用。

有关内存排序保证的更多详细信息,请参阅 synchronize_rcu() 的描述。

void call_rcu_tasks_trace(struct rcu_head *rhp, rcu_callback_t func)

将回调排队到基于任务的跟踪宽限期

参数

struct rcu_head *rhp

用于将 RCU 更新排队的结构。

rcu_callback_t func

宽限期过后要调用的实际回调函数

描述

回调函数将在跟踪 rcu-tasks 宽限期结束后调用,即在所有当前正在执行的跟踪 rcu-tasks 读取端临界区完成后调用。这些读取端临界区由对 rcu_read_lock_trace()rcu_read_unlock_trace() 的调用分隔。

有关内存排序保证的更多详细信息,请参阅 call_rcu() 的描述。

void synchronize_rcu_tasks_trace(void)

等待跟踪 rcu-tasks 宽限期

参数

void

无参数

描述

在跟踪 rcu-tasks 宽限期结束后,即在所有当前正在执行的跟踪 rcu-tasks 读取端临界区结束后,控制权将返回给调用者。这些读取端临界区由对 rcu_read_lock_trace()rcu_read_unlock_trace() 的调用分隔。

这是一个非常特殊的原语,仅用于跟踪和其他需要操作函数前导码和分析挂钩的少数情况。synchronize_rcu_tasks_trace() 函数(尚未)不打算在多个 CPU 上大量使用。

有关内存排序保证的更多详细信息,请参阅 synchronize_rcu() 的描述。

void rcu_barrier_tasks_trace(void)

等待正在进行的 call_rcu_tasks_trace() 回调。

参数

void

无参数

描述

尽管当前实现保证会等待,但它没有义务等待,例如,如果没有挂起的回调。

void rcu_cpu_stall_reset(void)

重新启动当前宽限期的停顿警告超时

参数

void

无参数

描述

要执行来自调用者的重置请求,请禁用停顿检测,直到 3 个 fqs 循环过去。这是确保加载新的 jiffies 所必需的。从 fqs 循环执行应该是安全的,因为应该已经过去了足够的定时器中断和上下文切换。

调用者必须禁用硬中断。

int rcu_stall_chain_notifier_register(struct notifier_block *n)

添加 RCU CPU 停顿通知器

参数

struct notifier_block *n

要添加的条目。

描述

将 RCU CPU 停顿通知器添加到原子通知器链。传递给通知器的 **action** 将是 **RCU_STALL_NOTIFY_NORM** 或类似的值。**data** 将是停顿宽限期的持续时间(以 jiffies 为单位),强制转换为 void* 指针。

成功返回 0,错误返回 -EEXIST

int rcu_stall_chain_notifier_unregister(struct notifier_block *n)

删除 RCU CPU 停顿通知器

参数

struct notifier_block *n

要添加的条目。

描述

从原子通知器链中删除 RCU CPU 停顿通知器。

成功返回零,失败返回 -ENOENT

void rcu_read_lock_trace(void)

标记 RCU 跟踪读取端临界区的开始

参数

void

无参数

描述

当一个任务调用 synchronize_rcu_tasks_trace() 时,该任务保证会被阻塞,直到所有其他任务退出其读取端临界区。类似地,如果在一个任务中调用 call_rcu_trace(),而其他任务处于 RCU 读取端临界区内,则相应 RCU 回调的调用会延迟到所有其他任务退出其临界区之后。

有关更多详细信息,请参阅 rcu_read_lock() 的文档。

void rcu_read_unlock_trace(void)

标记 RCU 跟踪读取端临界区的结束

参数

void

无参数

描述

与前面调用的 rcu_read_lock_trace() 配对,并且允许嵌套。在没有匹配的 rcu_read_lock_trace() 时调用 rcu_read_unlock_trace() 是禁止的,并且会导致锁依赖性错误。

有关更多详细信息,请参阅 rcu_read_unlock() 的文档。

synchronize_rcu_mult

synchronize_rcu_mult (...)

并发等待多个宽限期

参数

...

要等待的不同宽限期的 call_rcu() 函数列表

描述

此宏并发等待多种类型的 RCU 宽限期。例如,synchronize_rcu_mult(call_rcu, call_rcu_tasks) 将等待并发的 RCU 和 RCU-tasks 宽限期。等待给定的 SRCU 域需要为该 SRCU 域的 call_srcu() 函数编写一个包装器函数,此包装器提供指向相应 srcu_struct 的指针。

请注意,应该使用 call_rcu_hurry() 而不是 call_rcu(),因为在构建时使用 CONFIG_RCU_LAZY=y 的内核中,调用 call_rcu() 和相应的 RCU 回调之间可能会有数秒的延迟。

第一个参数告诉 Tiny RCU 的 _wait_rcu_gp() 不要费心等待 RCU。这样做的原因是,可以调用 synchronize_rcu_mult() 的任何地方都已经是完整的宽限期。

void rcuref_init(rcuref_t *ref, unsigned int cnt)

使用给定的引用计数初始化一个 rcuref 引用计数。

参数

rcuref_t *ref

指向引用计数的指针。

unsigned int cnt

初始引用计数,通常为 “1”。

unsigned int rcuref_read(rcuref_t *ref)

读取 rcuref 的持有引用计数。

参数

rcuref_t *ref

指向引用计数的指针。

返回

持有的引用数量 (0 ... N)。

bool rcuref_get(rcuref_t *ref)

获取 rcuref 引用计数的一个引用。

参数

rcuref_t *ref

指向引用计数的指针。

描述

类似于 atomic_inc_not_zero(),但在 RCUREF_MAXREF 处饱和。

不提供内存排序,假设调用者已保证对象内存的稳定性 (RCU 等)。它确实提供了一个控制依赖性,从而对未来的存储进行排序。请参阅 lib/rcuref.c 中的文档。

返回

如果尝试获取引用失败,则为 False。当最后一个引用已被释放时会发生这种情况。

如果成功获取了引用,则为 True。

bool rcuref_put_rcusafe(rcuref_t *ref)
  • 释放 rcuref 引用计数的一个引用,RCU 安全。

参数

rcuref_t *ref

指向引用计数的指针。

描述

提供释放内存排序,以便在之前完成之前的加载和存储,并在成功时提供获取排序,以便 free() 必须在之后进行。

可以从上下文中调用,该上下文保证如果递减使最后一个引用被删除,并且慢速路径与并发的 get() 和 put() 对竞争,则不会发生宽限期,从而同时释放该对象。rcu_read_lock()’ed 和原子上下文符合条件。

返回

如果这是最后一个引用,并且没有未来可能的引用,则为 True。这会向调用者发出信号,表明它可以安全地释放受引用计数器保护的对象。

如果仍然有活动引用,或者 put() 与并发的 get()/put() 对竞争,则为 False。不允许调用者释放受保护的对象。

bool rcuref_put(rcuref_t *ref)
  • 释放 rcuref 引用计数的一个引用。

参数

rcuref_t *ref

指向引用计数的指针。

描述

可以从任何上下文中调用。

提供释放内存排序,以便在之前完成之前的加载和存储,并在成功时提供获取排序,以便 free() 必须在之后进行。

返回

如果这是最后一个引用,并且没有未来可能的引用,则为 True。这会向调用者发出信号,表明它可以安全地调度受引用计数器保护的对象,以便进行解构。

如果仍然有活动引用,或者 put() 与并发的 get()/put() 对竞争,则为 False。不允许调用者解构受保护的对象。

bool same_state_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp1, struct rcu_gp_oldstate *rgosp2)

两个旧状态值是否相同?

参数

struct rcu_gp_oldstate *rgosp1

第一个旧状态值。

struct rcu_gp_oldstate *rgosp2

第二个旧状态值。

描述

这两个旧状态值必须从 get_state_synchronize_rcu_full(), start_poll_synchronize_rcu_full(), 或 get_completed_synchronize_rcu_full() 中获取。如果两个值相同则返回 true,否则返回 false。这允许其生命周期由旧状态值跟踪的结构将这些值推送到列表头,从而使这些结构稍微小一些。

请注意,相等性是以按位为基础来判断的,因此一个在某个字段中具有已完成状态的 rcu_gp_oldstate 结构将与另一个在另一个字段中具有已完成状态的结构进行比较时不相等。毕竟,rcu_gp_oldstate 结构是不透明的,那么这种情况是如何首先发生的?