英语

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 和包括 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

要测试的列表

描述

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

注意

如果没有同步,使用 list_empty_careful() 只能在可以对列表条目执行的唯一活动是 list_del_init() 的情况下是安全的。例如,如果另一个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。您应该将已知位于 head 上的元素传递给 entrylist 应该是一个空列表或一个您不介意丢失其数据的列表。

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 中的一个条目,可以是 head 本身

描述

此辅助函数将 **head** 的初始部分(直到但不包括 **entry**)从 **head** 移动到 **list**。 你应该传入一个你确定在 **head** 上的元素 **entry**。**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

类型 * 指向游标

member

结构体中 list_head 的名称。

list_next_entry_circular

list_next_entry_circular (pos, head, member)

获取链表中的下一个元素

参数

pos

类型 * 指向游标。

head

从中获取元素的链表头。

member

结构体中 list_head 的名称。

描述

如果 pos 是最后一个元素,则回绕(返回第一个元素)。请注意,预期链表不为空。

list_prev_entry

list_prev_entry (pos, member)

获取链表中的上一个元素

参数

pos

类型 * 指向游标

member

结构体中 list_head 的名称。

list_prev_entry_circular

list_prev_entry_circular (pos, head, member)

获取链表中的上一个元素

参数

pos

类型 * 指向游标。

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

类型 * 指向游标

head

链表的头部。

member

结构体中 list_head 的名称。

list_for_each_entry

list_for_each_entry (pos, head, member)

迭代给定类型的链表

参数

pos

用作循环游标的 类型 *。

head

链表的头部。

member

结构体中 list_head 的名称。

list_for_each_entry_reverse

list_for_each_entry_reverse (pos, head, member)

向后迭代给定类型的链表。

参数

pos

用作循环游标的 类型 *。

head

链表的头部。

member

结构体中 list_head 的名称。

list_prepare_entry

list_prepare_entry (pos, head, member)

准备一个 pos 条目以用于 list_for_each_entry_continue()

参数

pos

用作起始点的 类型 *

head

列表的头部

member

结构体中 list_head 的名称。

描述

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

list_for_each_entry_continue

list_for_each_entry_continue (pos, head, member)

继续迭代给定类型的链表

参数

pos

用作循环游标的 类型 *。

head

链表的头部。

member

结构体中 list_head 的名称。

描述

继续迭代给定类型的链表,在当前位置之后继续。

list_for_each_entry_continue_reverse

list_for_each_entry_continue_reverse (pos, head, member)

从给定的点向后迭代

参数

pos

用作循环游标的 类型 *。

head

链表的头部。

member

结构体中 list_head 的名称。

描述

开始向后迭代给定类型的链表,在当前位置之后继续。

list_for_each_entry_from

list_for_each_entry_from (pos, head, member)

从当前点迭代给定类型的链表

参数

pos

用作循环游标的 类型 *。

head

链表的头部。

member

结构体中 list_head 的名称。

描述

迭代给定类型的链表,从当前位置继续。

list_for_each_entry_from_reverse

list_for_each_entry_from_reverse (pos, head, member)

从当前点向后迭代给定类型的链表

参数

pos

用作循环游标的 类型 *。

head

链表的头部。

member

结构体中 list_head 的名称。

描述

向后迭代给定类型的链表,从当前位置继续。

list_for_each_entry_safe

list_for_each_entry_safe (pos, n, head, member)

迭代给定类型的链表,防止删除链表条目

参数

pos

用作循环游标的 类型 *。

n

另一个 类型 * 用作临时存储

head

链表的头部。

member

结构体中 list_head 的名称。

list_for_each_entry_safe_continue

list_for_each_entry_safe_continue (pos, n, head, member)

继续链表迭代,防止删除

参数

pos

用作循环游标的 类型 *。

n

另一个 类型 * 用作临时存储

head

链表的头部。

member

结构体中 list_head 的名称。

描述

迭代给定类型的链表,在当前点之后继续,防止删除链表条目。

list_for_each_entry_safe_from

list_for_each_entry_safe_from (pos, n, head, member)

从当前点迭代链表,防止删除

参数

pos

用作循环游标的 类型 *。

n

另一个 类型 * 用作临时存储

head

链表的头部。

member

结构体中 list_head 的名称。

描述

迭代给定类型的链表,从当前点开始,防止删除链表条目。

list_for_each_entry_safe_reverse

list_for_each_entry_safe_reverse (pos, n, head, member)

向后迭代链表,防止删除

参数

pos

用作循环游标的 类型 *。

n

另一个 类型 * 用作临时存储

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

用作循环游标的 类型 *。

head

链表的头部。

member

结构体中 hlist_node 的名称。

hlist_for_each_entry_continue

hlist_for_each_entry_continue (pos, member)

在当前点之后继续迭代 hlist

参数

pos

用作循环游标的 类型 *。

member

结构体中 hlist_node 的名称。

hlist_for_each_entry_from

hlist_for_each_entry_from (pos, member)

从当前点继续迭代 hlist

参数

pos

用作循环游标的 类型 *。

member

结构体中 hlist_node 的名称。

hlist_for_each_entry_safe

hlist_for_each_entry_safe (pos, n, head, member)

迭代给定类型的链表,防止删除链表条目

参数

pos

用作循环游标的 类型 *。

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_str, va_list args)

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

参数

char *buf

要将结果放入的缓冲区

size_t size

缓冲区的大小,包括尾随空空格

const char *fmt_str

要使用的格式字符串

va_list args

格式字符串的参数

描述

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

  • ``n`` 不受支持

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

有关更广泛的描述,请参阅 pointer() 或 如何正确获取 printk 格式说明符

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

返回值是为给定输入生成的字符数,不包括尾随的“0”,按照 ISO C99。如果您想获得写入 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_str, va_list args)

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

参数

u32 *bin_buf

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

size_t size

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

const char *fmt_str

要使用的格式字符串

va_list args

格式字符串的参数

描述

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

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

注意

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

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

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

参数

char *buf

要将结果放入的缓冲区

size_t size

缓冲区的大小,包括尾随空空格

const char *fmt_str

要使用的格式字符串

const u32 *bin_buf

格式化字符串的二进制参数

描述

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

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

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

返回值是为给定输入生成的字符数,不包括尾随的“0”,按照 ISO C99。如果您想获得写入 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。如果基数给出为 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。如果基数给出为 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。如果基数给出为 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。如果基数给出为 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。如果基数给出为 0,则字符串的基数将自动使用传统语义进行检测 - 如果它以 0x 开头,则该数字将被解析为十六进制(不区分大小写),如果它以 0 开头,它将被解析为八进制数。否则它将被解析为十进制。

unsigned int *res

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

描述

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

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

将字符串转换为整数

参数

const char *s

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

unsigned int base

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

int *res

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

描述

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

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

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

参数

const char *s

输入字符串

bool *res

结果

描述

当第一个字符是 ‘YyTt1NnFf0’ 或 [oO][NnFf](表示“on”和“off”)时,此例程返回 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_NPESCAPE_NAESCAPE_NAP 之一时,ESCAPE_APPEND 将有助于将附加字符传递给转义字符。

一个值得注意的警告是,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 元素。

返回

如果匹配,则为 arraystring 的索引,否则为 -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

要匹配的字符串

描述

返回 arraystr 的索引或 -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)

memcpy 实现,没有 FORTIFY 边界检查

参数

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,则对 q 的复制将在 size 字节后停止,并且 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 中的最大可用字节数

描述

p 处的 NUL-终止 字符串之后追加 NUL-终止 字符串 q,但不会写入超出 avail 字节的总数,可能会截断来自 q 的副本。 只有当 NUL 已经存在于 pavail 字节中时,p 才会保持 NUL-终止。 如果是这样,则从 q 复制的生成的字节数最多为 “avail - strlen(p) - 1”。

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

返回 _would_ 已包含在 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 开头的指针)。此接口被认为是不安全的,因为它不对输入执行边界检查。 因此,不建议使用它。 相反,提供其定义是为了防止编译器将其他 libcalls 降低到 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_ts 的数量,而不是字节数。

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_ts 的数量,而不是字节数。

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_ts 的数量,而不是字节数。

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

要从哪里复制字符串

...

目标缓冲区的大小

描述

将字符串复制到目标缓冲区中,或尽可能多地复制。如果字符串缓冲区重叠,则行为未定义。除非目标缓冲区的大小为零,否则始终以 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 结尾的字符串。

请注意,**dest** 和 **src** 的大小必须在编译时已知。

memtostr_pad

memtostr_pad (dest, src)

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

参数

dest

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

src

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

描述

这是 strncpy() 用法的替代方法,其中源不是以 NUL 结尾的字符串。

请注意,**dest** 和 **src** 的大小必须在编译时已知。

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()

返回

  • 如果 **str** 以 **prefix** 开头,则返回 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 字符串

参数

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)

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

参数

const char *s

要字符串化的数据

size_t len

数据的大小

gfp_t gfp

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

返回

带有 NUL 终止符的 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

要复制的最大字节数,包括尾随的 NUL。

返回

s 的新分配副本,或者在出错情况下为 ERR_PTR()

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

从用户空间复制内存区域并进行 NUL 终止

参数

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 以指示是否有人在等待解锁。

返回

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

位图操作

位图提供一个位数组,使用一个无符号长整型数组实现。 给定位图中有效位的数量_不需要_是 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” 的数组,该数组具有足够的无符号长整型来包含从 0 到 “bits” - 1 的所有位位置。

在位图是单个无符号长整型的情况下,可用的位图操作及其大致含义如下

当 `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()` 在无符号长整型区域上操作,也就是说,位图后面的位直到无符号长整型边界也会被清零或填充。考虑使用 `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 时,才设置 dst 的第 n 位;或者,如果对于任何 m 使得 first <= n < nbits,并且 m = n + cut,则 src 的第 m 位被设置。

用图片表示,例如对于大端 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 位数组的内容复制到位图

参数

unsigned long *bitmap

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

const u64 *buf

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

unsigned int nbits

bitmap 中的位数

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

将位图的内容复制到 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 (长度为 nbits) 中位置 pos 处的位映射到它是哪个设置位的序号。 如果未设置或如果 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

每个位图中的位数

描述

设置 dst 的第 n 位,当且仅当存在一些 m,使得 relmap 的第 n 位被设置,orig 的第 m 位被设置,并且 relmap 的第 n 位也是 relmap 的第 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 中第十二个被打开的位。 在上面的例子中,relmap 中只有十个位被打开(30..39),因此 orig 中的第 11 位被设置对 dst 没有影响。

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

假设 relmap 有这十个位被设置

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);

然后,下表显示了对于各种 origdst 的各种值将会是什么。 我列出了每个设置位的从零开始的位置。 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

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

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() 以了解位图收集的详细操作。 TL;DR:bitmap_gather() 可以被视为 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() 以了解位图分散的详细操作。 TL;DR:bitmap_scatter() 可以被视为 bitmap_gather() 操作的“反向”。

假设 scattered 是使用 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 为底的对数)

描述

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

返回

已分配区域在位图中的位偏移量,或者失败时为 -errno。

BITMAP_FROM_U64

BITMAP_FROM_U64 (n)

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

参数

n

u64 值

描述

Linux 位图在内部是无符号长整型数组,即在 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 位于第二个字的第 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。

描述

如果像 nbits 那样实现了 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 完全可解析,则通常为空终止符)。

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 是错误指针,则返回 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,因此速度明显更快。

比较函数必须遵守特定的数学属性,以确保正确和稳定的排序: - 反对称性:cmp_func(a, b) 必须返回与 cmp_func(b, a) 相反的符号。 - 传递性:如果 cmp_func(a, b) <= 0 且 cmp_func(b, c) <= 0,则 cmp_func(a, c) <= 0。

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

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

对元素数组进行排序,带有 cond_resched

参数

void *base

指向要排序的数据的指针

size_t num

元素数量

size_t size

每个元素的大小

cmp_r_func_t cmp_func

指向比较函数的指针

swap_r_func_t swap_func

指向交换函数的指针或 NULL

const void *priv

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

描述

与 sort_r 相同,但对于较大的数组,首选此函数,因为它会定期执行 cond_resched()。

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(a, b) 必须返回与 cmp(b, a) 相反的符号。 - 传递性:如果 cmp(a, b) <= 0 且 cmp(b, c) <= 0,则 cmp(a, c) <= 0。

这与两种样式的 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) 的列表。

当 count 达到 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:大小为 2^k 的 0 个挂起列表;大小 < 2^k 的 x 个挂起列表 1: 01x:大小为 2^k 的 0 个挂起列表;大小 < 2^k 的 2^(k-1) + x 个挂起列表 2: x10x:大小为 2^k 的 0 个挂起列表;大小 < 2^k 的 2^k + x 个挂起列表 3: x11x:大小为 2^k 的 1 个挂起列表;大小 < 2^k 的 2^(k-1) + x 个挂起列表 4: y00x:大小为 2^k 的 1 个挂起列表;大小 < 2^k 的 2^k + x 个挂起列表 5: y01x:大小为 2^k 的 2 个挂起列表;大小 < 2^k 的 2^(k-1) + x 个挂起列表(合并并循环回到状态 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)

注册一个 textsearch 模块

参数

struct ts_ops *ops

操作查找表

描述

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

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

int textsearch_unregister(struct ts_ops *ops)

注销一个 textsearch 模块

参数

struct ts_ops *ops

操作查找表

描述

文本搜索模块必须调用此函数来声明它们已消失,例如当模块被卸载时。ops 参数必须与注册期间的参数相同。

成功时返回 0,如果未找到匹配的 textsearch 注册,则返回 -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

要添加的量

描述

将 **var** 递增 **offset**,并进行回绕。返回 **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

要减去的量

描述

将 **var** 递减 **offset**,并进行回绕。返回 **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**”是负数。

  • 如果存在任何符号位,“**a** << **s**”会在“**\*d**”中设置符号位。

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

overflows_type

overflows_type (n, T)

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

参数

n

要检查的源常量值或变量

T

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

描述

比较 **x** 表达式,以确定它是否可以安全地放入 **T** 中类型的存储中。 **x** 和 **T** 可以具有不同的类型。如果 **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。左值必须是 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。左值必须是 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)。左值必须是 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

灵活数组成员的名称。

count

数组中的元素数。

描述

计算 **p** 结构的末尾处 **count** 个 **member** 元素的灵活数组的大小。

返回

所需的字节数,如果溢出,则为 SIZE_MAX。

struct_size

struct_size (p, member, count)

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

参数

p

指向结构的指针。

member

数组成员的名称。

count

数组中的元素数。

描述

计算 **p** 结构后跟 **count** 个 **member** 元素数组所需的内存大小。

返回

所需的字节数,如果溢出,则为 SIZE_MAX。

struct_size_t

struct_size_t (type, member, count)

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

参数

type

结构类型名称。

member

数组成员的名称。

count

数组中的元素数。

描述

计算 **type** 结构后跟 **count** 个 **member** 元素数组所需的内存大小。如果可能,最好使用 struct_size(),以保持计算与类型 **type** 的特定实例变量相关联。

返回

所需的字节数,如果溢出,则为 SIZE_MAX。

__DEFINE_FLEX

__DEFINE_FLEX (type, name, member, count, trailer...)

用于 DEFINE_FLEX() 系列的辅助宏。使调用方宏能够传递任意尾随表达式

参数

type

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

name

要定义的变量的名称。

member

数组成员的名称。

count

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

trailer...

用于属性和/或初始值设定项的尾随表达式。

_DEFINE_FLEX

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

用于 DEFINE_FLEX() 系列的辅助宏。使调用方宏能够传递(不同的)initializer。

参数

type

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

name

要定义的变量的名称。

member

数组成员的名称。

count

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

initializer...

初始化表达式(例如,至少传递 = { })。

DEFINE_RAW_FLEX

DEFINE_RAW_FLEX (type, name, member, count)

定义一个具有尾随可变数组的结构体的栈上实例,当它没有 __counted_by 注释时。

参数

type

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

name

要定义的变量的名称。

member

数组成员的名称。

count

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

描述

定义一个清零的、具有尾随可变数组的 **type** 结构体的栈上实例。之后使用 __struct_size( **name** ) 来获取其编译时大小。使用 __member_size( **name->member** ) 来获取 **name** 成员的编译时大小。使用 STACK_FLEX_ARRAY_SIZE( **name** , **member** ) 来获取数组 **member** 中元素的编译时数量。

DEFINE_FLEX

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

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

参数

TYPE

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

NAME

要定义的变量的名称。

MEMBER

数组成员的名称。

COUNTER

__counted_by 成员的名称。

COUNT

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

描述

定义一个清零的、具有尾随可变数组的 **TYPE** 结构体的栈上实例。之后使用 __struct_size( **NAME** ) 来获取其编译时大小。使用 __member_size( **NAME->member** ) 来获取 **NAME** 成员的编译时大小。使用 STACK_FLEX_ARRAY_SIZE( **name** , **member** ) 来获取数组 **member** 中元素的编译时数量。

STACK_FLEX_ARRAY_SIZE

STACK_FLEX_ARRAY_SIZE (name, array)

用于 DEFINE_FLEX() 系列的辅助宏。返回 **array** 中的元素数量。

参数

name

DEFINE_RAW_FLEX()/DEFINE_FLEX() 中定义的变量的名称。

array

数组成员的名称。

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, const u8 *p, size_t len)

计算数据缓冲区的 CRC-16

参数

u16 crc

先前的 CRC 值

const u8 *p

数据指针

size_t len

缓冲区中的字节数

描述

返回更新的 CRC 值。

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) 成正比。

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 被除数(将被更新)

base

uint32_t 除数

描述

摘要:uint32_t remainder = n % base; n = n / base;

返回

(uint32_t)remainder

注意

宏参数 n 会被多次计算,小心副作用!

u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder)

带 32 位除数的无符号 64 位除法,并返回余数

参数

u64 dividend

无符号 64 位被除数

u32 divisor

无符号 32 位除数

u32 *remainder

指向无符号 32 位余数的指针

返回

设置 *remainder,然后返回 被除数 / 除数

描述

通常由 32 位架构提供,以提供优化的 64 位除法。

s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder)

带 32 位除数的有符号 64 位除法,并返回余数

参数

s64 dividend

有符号 64 位被除数

s32 divisor

有符号 32 位除数

s32 *remainder

指向有符号 32 位余数的指针

返回

设置 *remainder,然后返回 被除数 / 除数

u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder)

带 64 位除数的无符号 64 位除法,并返回余数

参数

u64 dividend

无符号 64 位被除数

u64 divisor

无符号 64 位除数

u64 *remainder

指向无符号 64 位余数的指针

返回

设置 *remainder,然后返回 被除数 / 除数

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)

带 32 位除数的无符号 64 位除法

参数

u64 dividend

无符号 64 位被除数

u32 divisor

无符号 32 位除数

描述

这是最常见的 64 位除法,如果可能,应该使用它,因为许多 32 位架构可以比完整的 64 位除法更好地优化这个变体。

返回

被除数 / 除数

s64 div_s64(s64 dividend, s32 divisor)

带 32 位除数的有符号 64 位除法

参数

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)

带 32 位除数的无符号 64 位除法,向上舍入

参数

ll

无符号 64 位被除数

d

无符号 32 位除数

描述

将无符号 64 位被除数除以无符号 32 位除数并向上舍入。

返回

被除数 / 除数,向上舍入

DIV64_U64_ROUND_CLOSEST

DIV64_U64_ROUND_CLOSEST (dividend, divisor)

带 64 位除数的无符号 64 位除法,舍入到最接近的整数

参数

dividend

无符号 64 位被除数

divisor

无符号 64 位除数

描述

将无符号 64 位被除数除以无符号 64 位除数并舍入到最接近的整数。

返回

被除数 / 除数,舍入到最接近的整数

DIV_U64_ROUND_CLOSEST

DIV_U64_ROUND_CLOSEST (dividend, divisor)

带 32 位除数的无符号 64 位除法,舍入到最接近的整数

参数

dividend

无符号 64 位被除数

divisor

无符号 32 位除数

描述

将无符号 64 位被除数除以无符号 32 位除数并舍入到最接近的整数。

返回

被除数 / 除数,舍入到最接近的整数

DIV_S64_ROUND_CLOSEST

DIV_S64_ROUND_CLOSEST (dividend, divisor)

带 32 位除数的有符号 64 位除法,舍入到最接近的整数

参数

dividend

有符号 64 位被除数

divisor

有符号 32 位除数

描述

将有符号 64 位被除数除以有符号 32 位除数并舍入到最接近的整数。

返回

被除数 / 除数,舍入到最接近的整数

u64 roundup_u64(u64 x, u32 y)

将 64 位值向上舍入到下一个指定的 32 位倍数

参数

u64 x

要向上舍入的值

u32 y

要向上舍入到的 32 位倍数

描述

x 舍入到 y 的下一个倍数。对于 32 位 x 值,请参阅 roundup 和更快的 round_up(),适用于 2 的幂。

返回

向上舍入的值。

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 扩展到 lowmem,因此在成功添加/删除内存后,将调用此回调例程以重新计算 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 *)

show 例程。

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 ids 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 perm 结构

描述

在此函数被调用之前,会持有 ipc_ids.rwsem(作为写入者)和此 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 perm 结构

描述

在此函数被调用之前,会持有 ipc_ids.rwsem(作为写入者)和此 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 perm 结构

描述

在此函数被调用之前,会持有 ipc_ids.rwsem(作为写入者)和此 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 或 <linux/stat.h> 中的 S_...UGO

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)

在 ipc ids idr 中查找一个 id,并返回关联的 ipc 对象。

参数

struct ipc_ids *ids

ipc 标识符集

int id

要查找的 ipc id

描述

在 RCU 临界区内调用。退出时 ipc 对象锁定。

struct kern_ipc_perm *ipc_obtain_object_check(struct ipc_ids *ids, int id)

ipc_obtain_object_idr() 类似,但也检查 ipc 对象序列号。

参数

struct ipc_ids *ids

ipc 标识符集

int id

要查找的 ipc id

描述

在 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 元素的类型

size

fifo 中的元素数量,这必须是 2 的幂

INIT_KFIFO

INIT_KFIFO (fifo)

初始化由 DECLARE_KFIFO 声明的 fifo

参数

fifo

声明的 fifo 数据类型的名称

DEFINE_KFIFO

DEFINE_KFIFO (fifo, type, size)

定义并初始化一个 fifo 的宏

参数

fifo

声明的 fifo 数据类型的名称

type

fifo 元素的类型

size

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 的地址

count

要跳过的数据计数

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 的指针

size

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

要使用的预先分配的缓冲区

size

内部缓冲区的大小,这必须是 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**) with 返回的计数)。

请注意,只有单个并发读取器和单个并发写入器,您不需要额外的锁定即可使用这些宏。

kfifo_out_linear_ptr

kfifo_out_linear_ptr (fifo, ptr, n)

获取指向可用数据的指针

参数

fifo

要使用的 fifo 的地址

ptr

指向要存储指向尾部的指针的数据的指针

n

要指向的元素的最大数量

描述

kfifo_out_linear() 类似,此宏获取 FIFO 缓冲区中可用数据的指针,并返回可用元素的数量。它返回到可用数据结尾或缓冲区结尾的可用计数。因此,它可以用于线性数据处理(如 memcpy() of **ptr** with 返回的计数)。

请注意,只有单个并发读取器和单个并发写入器,您不需要额外的锁定即可使用这些宏。

继电器接口支持

继电器接口支持旨在为工具和设施提供一种有效的机制,以便将大量数据从内核空间传递到用户空间。

继电器接口

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

要创建的文件的基本名称

struct dentry *parent

父目录的目录项,根目录或缓冲区的 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

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 应该是新消耗的子缓冲区的数量,而不是总共消耗的数量。

注意:如果通道模式为“overwrite”,则内核客户端无需调用此函数。

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)
  • 将通道缓冲区 mmap 到进程地址空间

参数

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)

relay 文件的 poll 文件操作

参数

struct file *filp

文件

poll_table *wait

poll 表

Poll 实现。

int relay_file_release(struct inode *inode, struct file *filp)

relay 文件的 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 的特定于体系结构的版本来处理需要特殊权限的 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

如果找到资源,则返回 ptr

描述

如果找到资源,则返回 0,并且 **res 会被覆盖为 [**start**..**end**] 范围内的资源部分; 如果未找到资源,则返回 -ENODEV。 对于无效参数,返回 -EINVAL。

调用者必须指定 **start**、**end**、**flags** 和 **desc**(可以是 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。 如果发生冲突,并且冲突资源完全适合新资源的范围,则会插入新资源,并且冲突资源将成为新资源的子资源。

此函数适用于资源生产者,例如 FW 模块和总线驱动程序。

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

描述

检查指定的区域是否部分重叠或完全覆盖由 **flags** 和 **desc**(可选,使用 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

要满足的范围和对齐方式约束

描述

在满足范围和对齐方式 **constraints** 的资源树中,在 **root** 下查找空闲空间。

返回

  • 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。

此函数适用于资源生产者,例如 FW 模块和总线驱动程序。

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 内存,而 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,如果失败,则记录一个错误。

被调用者应该在等效的二次方边界上提供二次方大小。

驱动程序必须存储返回值以传递给 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)

分配一个组合任务blob

参数

struct task_struct *task

需要blob的任务

描述

为所有模块分配任务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)

在初始化期间分配一个组合任务blob

参数

struct task_struct *task

需要blob的任务

描述

为所有模块分配任务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,如果复制错误,则返回 -EFAULT,如果无法分配内存,则返回 -ENOMEM。

int security_binder_set_context_mgr(const struct cred *mgr)

检查是否可以成为 binder ctx mgr

参数

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** 调用到 **to** 的 binder 事务调用。

返回

如果授予权限,则返回 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 file *file)

检查是否允许 binder 文件传输

参数

const struct cred *from

发送进程

const struct cred *to

接收进程

const struct file *file

正在传输的文件

描述

检查是否允许 **from** 将 **file** 传输到 **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 期间的 set_security 或 apply_creds 挂钩中执行进程跟踪检查,如果在 binprm_security_ops 的 bprm_set_creds 挂钩中的 execve 期间的跟踪检查期间进程正在被跟踪并且其安全属性将被 execve 更改。

返回

如果授予权限,则返回 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** 进程的 **effective**、**inheritable** 和 **permitted** 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 集合

描述

为当前进程设置 **effective**、**inheritable** 和 **permitted** 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)

检查是否允许此 fs 的 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 都返回正值,则将使用 cap_sys_admin 设置调用 __vm_enough_memory()。如果至少有一个 LSM 返回 0 或负值,则将使用 cap_sys_admin 清除调用 __vm_enough_memory()。

返回

如果 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,请更新 LSM 的 bprm->cred->security 部分,使其成为 commit_creds 需要为新程序安装的部分。此 hook 也可以选择性地检查权限(例如,用于安全域之间的转换)。如果应设置 AT_SECURE 以请求 libc 启用安全模式,则此 hook 必须将 bprm->secureexec 设置为 1。bprm 包含 linux_binprm 结构。

如果使用 AT_EXECVE_CHECK 标志调用 execveat(2),则会设置 bprm->is_check。即使执行永远不会真正发生并且将始终删除 bprm,结果也必须与没有此标志的结果相同。

此 hook 不得更改 current->cred,仅更改 bprm->cred

返回

如果 hook 成功并且授予权限,则返回 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 以反映该更改。 这是在找到将要执行的二进制文件而没有解释器之后调用的。 这可确保凭据不会来自二进制文件需要重新打开的脚本,该脚本在重新打开时可能最终成为完全不同的文件。 此 hook 也可以选择性地检查权限(例如,用于安全域之间的转换)。 如果应设置 AT_SECURE 以请求 libc 启用安全模式,则此 hook 必须将 bprm->secureexec 设置为 1。此 hook 必须将应该从 current->personality 清除的任何个性标志添加到 bprm->per_clearbprm 包含 linux_binprm 结构。

返回

如果 hook 成功并且授予权限,则返回 0。

int security_bprm_check(struct linux_binprm *bprm)

调节二进制处理程序搜索

参数

struct linux_binprm *bprm

二进制程序信息

描述

此 hook 调节二进制处理程序搜索将要开始的点。它允许检查在前面的 creds_for_exec 调用中设置的 bprm->cred->security 值。argv 列表和 envp 列表可以在 bprm 中可靠地使用。在单个 execve 期间,可能会多次调用此 hook。bprm 包含 linux_binprm 结构。

返回

如果 hook 成功并且授予权限,则返回 0。

void security_bprm_committing_creds(const struct linux_binprm *bprm)

在 exec() 期间为进程安装凭据

参数

const struct linux_binprm *bprm

二进制程序信息

描述

准备安装由 execve 操作转换的进程的新安全属性,该属性基于 current->cred 指向的旧凭据和 bprm_creds_for_exec hook 在 bprm->cred 中设置的信息。bprm 指向 linux_binprm 结构。此 hook 是对进程执行状态更改的好地方,例如关闭当属性更改时不再授予访问权限的打开的文件描述符。这将在 commit_creds() 之前立即调用。

void security_bprm_committed_creds(const struct linux_binprm *bprm)

在 exec() 期间完成凭据安装后进行清理

参数

const struct linux_binprm *bprm

二进制程序信息

描述

完成安装由 execve 操作转换的进程的新安全属性后进行清理。此时,新凭据已设置为 current->credbprm 指向 linux_binprm 结构。此 hook 是对进程执行状态更改的好地方,例如清除不可继承的信号状态。这将在 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

用于子挂载/重新挂载的 dentry 引用

描述

填充新 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)

检查是否允许访问 fs 统计信息

参数

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)

检查旋转 rootfs 的权限

参数

const struct path *old_path

当前 rootfs 的新位置

const struct path *new_path

新 rootfs 的位置

描述

在旋转根文件系统之前检查权限。

返回

如果授予权限,则返回 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

文件路径类型

描述

在由 **mask** 定义的事件上,在 **path** 处的对象上,其类型由 **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() 中使用 inode 状态的 LSM 都应该只在 inode_free_security_rcu() LSM 钩子回调中释放该状态。

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 设置 incore 安全字段,并返回安全模块是否允许创建 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

文件

描述

在截断 path 指示的文件之前检查权限。请注意,也可以基于已打开的文件来检查截断权限,使用 security_file_truncate() 钩子。

返回

如果授予权限,则返回 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)

更新新 tmpfile 的 inode 安全性

参数

struct mnt_idmap *idmap

挂载的 idmap

struct inode *inode

新 tmpfile 的 inode

描述

创建 tmpfile 后更新 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 步行模式下为真

描述

在查找路径名时,检查跟随符号链接的权限。 如果 rcu 为真,则 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 层执行额外的 capability 检查。

通常,我们在执行各种 LSM 钩子实现之前强制执行 capability 检查,但是如果 LSM 想要避免此 capability 检查,它可以注册一个 'inode_xattr_skipcap' 钩子,并为它想要避免 capability 检查的 xattrs 返回值 1,让 LSM 完全负责强制执行特定 xattr 的访问控制。 如果所有启用的 LSM 都不注册 'inode_xattr_skipcap' 钩子,或者返回 0(默认返回值),则仍会执行 capability 检查。 如果未注册任何 'inode_xattr_skipcap' 钩子,则会执行 capability 检查。

返回

如果授予权限,则返回 0。

int security_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, const char *acl_name, struct posix_acl *kacl)

检查是否允许设置 posix acls

参数

struct mnt_idmap *idmap

挂载的 idmap

struct dentry *dentry

文件

const char *acl_name

acl 名称

struct posix_acl *kacl

acl 结构

描述

在设置 posix acls 之前检查权限,kacl 中的 posix acls 由 acl_name 标识。

返回

如果授予权限,则返回 0。

void security_inode_post_set_acl(struct dentry *dentry, const char *acl_name, struct posix_acl *kacl)

从 posix acls 设置更新 inode 安全性

参数

struct dentry *dentry

文件

const char *acl_name

acl 名称

struct posix_acl *kacl

acl 结构

描述

成功在 dentry 上设置 posix acls 后,更新 inode 安全数据。 kacl 中的 posix acls 由 acl_name 标识。

int security_inode_get_acl(struct mnt_idmap *idmap, struct dentry *dentry, const char *acl_name)

检查是否允许读取 posix acls

参数

struct mnt_idmap *idmap

挂载的 idmap

struct dentry *dentry

文件

const char *acl_name

acl 名称

描述

在获取 osix acls 之前检查权限,posix acls 由 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 acls 之前检查权限,posix acls 由 acl_name 标识。

返回

如果授予权限,则返回 0。

void security_inode_post_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry, const char *acl_name)

rm posix acls 后更新 inode 安全性

参数

struct mnt_idmap *idmap

挂载的 idmap

struct dentry *dentry

文件

const char *acl_name

acl 名称

描述

成功从 idmap 中的 dentry 中删除 posix acls 后,更新 inode 安全数据。 posix acls 由 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 名称

描述

在获取由 name 标识的 dentry 的扩展属性之前检查权限。

返回

如果授予权限,则返回 0。

int security_inode_listxattr(struct dentry *dentry)

检查是否允许列出 xattrs

参数

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 层执行额外的 capability 检查。

通常,我们在执行各种 LSM 钩子实现之前强制执行 capability 检查,但是如果 LSM 想要避免此 capability 检查,它可以注册一个 'inode_xattr_skipcap' 钩子,并为它想要避免 capability 检查的 xattrs 返回值 1,让 LSM 完全负责强制执行特定 xattr 的访问控制。 如果所有启用的 LSM 都不注册 'inode_xattr_skipcap' 钩子,或者返回 0(默认返回值),则仍会执行 capability 检查。 如果未注册任何 'inode_xattr_skipcap' 钩子,则会执行 capability 检查。

返回

如果授予权限,则返回 0。

void security_inode_post_removexattr(struct dentry *dentry, const char *name)

在 removexattr op 之后更新 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()

返回

如果发生错误导致 inode 更改操作中止,则返回 <0,如果

不需要调用 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** 为 **inode** 设置与 **name** 关联的安全标签。 **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

描述

保存 open-time 权限检查状态,以便稍后在 file_permission 上使用,如果自 inode_permission 以来有任何更改,则重新检查访问。

我们可以通过检查 file->f_flags & __FMODE_EXEC 来检查文件是否为执行而打开(例如 execve(2) 调用),无论是直接还是间接(例如 ELF 的 ld.so)。

返回

如果授予权限,则返回 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)

释放凭证的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

新的资源限制

描述

在将进程 presource 的资源限制设置为 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

描述

根据关联任务的安全属性设置 inode 的安全属性,例如,对于 /proc/pid inodes。

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。首次创建该结构体时,security 字段初始化为 NULL。

返回

如果操作成功且权限被授予,则返回 0。

void security_msg_queue_free(struct kern_ipc_perm *msq)

释放一个 sysv ipc 消息队列 LSM blob

参数

struct kern_ipc_perm *msq

sysv ipc 权限结构

描述

释放消息队列的 security 字段 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 安全字段。首次创建该结构体时,security 字段初始化为 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 安全字段。首次创建该结构体时,security 字段初始化为 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

返回

成功时返回写入的字节数,否则返回负值。

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。

保存信息并检查是否允许netlink发送

参数

struct sock *sk

发送套接字

struct sk_buff *skb

netlink消息

描述

保存netlink消息的安全信息,以便在处理消息时可以执行权限检查。可以使用netlink_skb_parms结构的eff_cap字段保存安全信息。还可以用于提供对消息传输的细粒度控制。

返回

如果成功保存信息并允许传输消息,则返回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

套接字

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

套接字

struct sockaddr *address

请求的绑定地址

int addrlen

地址的长度

描述

在执行套接字协议层绑定操作并将套接字 sock 绑定到 address 参数中指定的地址之前,检查权限。

返回

如果授予权限,则返回 0。

int security_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen)

检查是否允许套接字连接操作

参数

struct socket *sock

套接字

struct sockaddr *address

远程连接点的地址

int addrlen

地址的长度

描述

在套接字协议层连接操作尝试将套接字 sock 连接到远程地址 address 之前,检查权限。

返回

如果授予权限,则返回 0。

int security_socket_listen(struct socket *sock, int backlog)

检查是否允许套接字侦听

参数

struct socket *sock

套接字

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

套接字

描述

在读取套接字对象的本地地址(名称)之前检查权限。

返回

如果授予权限,则返回 0。

int security_socket_getpeername(struct socket *sock)

检查是否允许读取对等方的地址

参数

struct socket *sock

套接字

描述

在读取套接字对象的远程地址(名称)之前检查权限。

返回

如果授予权限,则返回 0。

int security_socket_getsockopt(struct socket *sock, int level, int optname)

检查是否允许读取套接字选项

参数

struct socket *sock

套接字

int level

选项的协议级别

int optname

选项名称

描述

在检索与套接字 sock 关联的选项之前检查权限。

返回

如果授予权限,则返回 0。

int security_socket_setsockopt(struct socket *sock, int level, int optname)

检查是否允许设置套接字选项

参数

struct socket *sock

套接字

int level

选项的协议级别

int optname

选项名称

描述

在设置与套接字 sock 关联的选项之前检查权限。

返回

如果授予权限,则返回 0。

int security_socket_shutdown(struct socket *sock, int how)

检查是否允许关闭套接字

参数

struct socket *sock

套接字

int how

指示如何处理发送和接收的标志

描述

检查是否允许关闭套接字 sock 上连接的全部或部分。

返回

如果授予权限,则返回 0。

int security_socket_getpeersec_stream(struct socket *sock, sockptr_t optval, sockptr_t optlen, unsigned int len)

获取远程对等方标签

参数

struct socket *sock

套接字

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 设置新的 sock LSM 状态

参数

struct sock *newsk

新的 sock

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 时,该安全字段初始化为 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

密钥

const struct cred *cred

凭证

unsigned long flags

分配标志

描述

允许分配密钥并分配安全数据。请注意,此时尚未为密钥分配序列号。

返回

如果授予权限,则返回 0,否则返回 -ve 错误。

void security_key_free(struct key *key)

释放内核密钥 LSM blob

参数

struct 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

密钥

char **buffer

安全标签缓冲区

描述

获取附加到密钥的安全上下文的文本表示形式,以便于处理 KEYCTL_GETSECURITY。此函数分配 NUL 终止字符串的存储空间,调用者应释放它。

返回

返回 buffer 的长度(包括终止 NUL),如果发生错误则返回 -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。

失败。

int security_bpf(int cmd, union bpf_attr *attr, unsigned int size, bool kernel)

检查是否允许 bpf 系统调用操作

参数

int cmd

命令

union bpf_attr *attr

bpf 属性

unsigned int size

size

bool kernel

调用是否源自内核

描述

在将属性复制到内核后,对所有 bpf 系统调用执行初始检查。实际的安全模块可以实现自己的规则来检查它们需要的特定命令。

返回

如果授予权限,则返回 0。

int security_bpf_map(struct bpf_map *map, fmode_t fmode)

检查是否允许访问 bpf map

参数

struct bpf_map *map

bpf map

fmode_t fmode

模式

描述

在内核为 eBPF map 生成并返回文件描述符时进行检查。

返回

如果授予权限,则返回 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, bool kernel)

检查是否允许创建 BPF map

参数

struct bpf_map *map

BPF map 对象

union bpf_attr *attr

用于创建 BPF map 的 BPF 系统调用属性

struct bpf_token *token

用于授予用户访问权限的 BPF 令牌

bool kernel

调用是否源自内核

描述

在内核创建新的 BPF map 时进行检查。这也是为需要它们的 LSM 分配 LSM blob 的地方。

返回

成功时返回 0,失败时返回错误。

int security_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr, struct bpf_token *token, bool kernel)

检查是否允许加载 BPF 程序

参数

struct bpf_prog *prog

BPF 程序对象

union bpf_attr *attr

用于创建 BPF 程序的 BPF 系统调用属性

struct bpf_token *token

用于授予用户访问 BPF 子系统权限的 BPF 令牌

bool kernel

调用是否源自内核

描述

当内核加载 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 相关 capability

参数

const struct bpf_token *token

BPF 令牌对象

int cap

请求由 BPF 令牌委托的 capability

描述

当内核决定提供的 BPF 令牌是否应允许委托请求的 BPF 相关 capability 时进行检查。

返回

成功时返回 0,失败时返回错误。

void security_bpf_map_free(struct bpf_map *map)

释放 bpf map 的 LSM blob

参数

struct bpf_map *map

bpf map

描述

清理存储在 bpf map 中的安全信息。

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(int type)

检查是否允许 perf event open

参数

int type

事件类型

描述

检查是否允许 type 类型的 perf_event_open 系统调用。

返回

如果授予权限,则返回 0。

int security_perf_event_alloc(struct perf_event *event)

分配 perf event LSM blob

参数

struct perf_event *event

perf event

描述

分配并保存 perf_event 安全信息。

返回

成功时返回 0,失败时返回错误。

void security_perf_event_free(struct perf_event *event)

释放 perf event LSM blob

参数

struct perf_event *event

perf event

描述

释放(free)perf_event 安全信息。

int security_perf_event_read(struct perf_event *event)

检查是否允许读取 perf event 标签

参数

struct perf_event *event

perf event

描述

如果允许,读取 perf_event 安全信息。

返回

如果授予权限,则返回 0。

int security_perf_event_write(struct perf_event *event)

检查是否允许写入 perf event 标签

参数

struct perf_event *event

perf event

描述

如果允许,写入 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。

int security_uring_allowed(void)

检查是否允许 io_uring_setup()

参数

void

无参数

描述

检查是否允许当前任务调用 io_uring_setup()。

返回

如果授予权限,则返回 0。

void security_initramfs_populated(void)

通知 LSM 已加载 initramfs

参数

void

无参数

描述

告知 LSM 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

指向调用者稍后想要访问的内容的指针。inode.i_private 指针将在 open() 调用中指向此值。

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 operations。

描述

此函数在 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)

操作的通用过滤器助手(syscall/uring/etc)

参数

struct task_struct *tsk

关联的任务

struct audit_context *ctx

审计上下文

struct list_head *list

审计过滤器列表

struct audit_names *name

audit_name (可以为 NULL)

unsigned long op

当前 syscall/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 中的所有字段都将重置为初始状态,字段持有的所有引用都将被删除,并且私有内存将被释放。当此函数返回时,audit_context 将适合重用,只要传递的上下文不是 NULL 或虚拟上下文。

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

audit_context

int success

指示操作是否成功的 true/false 值

long code

操作返回代码

描述

如果实际返回代码稍后将由特定于 arch 的信号处理程序修复,我们需要修复审计日志中的返回代码。

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)

在 syscall 进入时填写审计记录

参数

int major

主要的 syscall 类型(函数)

unsigned long a1

额外的 syscall 寄存器 1

unsigned long a2

额外的 syscall 寄存器 2

unsigned long a3

额外的 syscall 寄存器 3

unsigned long a4

额外的 syscall 寄存器 4

描述

在 syscall 进入时填写审计上下文。只有在创建任务时创建了审计上下文,并且状态或过滤器要求构建审计上下文时才会发生这种情况。如果来自每个任务的过滤器或来自每个 syscall 的过滤器的状态为 AUDIT_STATE_RECORD,则该记录将在 syscall 退出时写入(否则,只有在内核的另一部分请求写入该记录时才会写入)。

void __audit_syscall_exit(int success, long return_code)

在系统调用后释放审计上下文

参数

int success

syscall 的成功值

long return_code

syscall 的返回值

描述

在系统调用后进行清理。如果审计上下文已被标记为可审计(由于过滤器的 AUDIT_STATE_RECORD 状态,或者由于内核的另一部分写入了审计消息),则写出 syscall 信息。在所有情况下,释放从 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 用于存储审计上下文中记录的时间

unsigned int *serial

在审计上下文中记录的序列值

描述

还会将上下文设置为可审计。

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 上下文时返回 NULL,错误时返回 < 0。

int audit_signal_info_syscall(struct task_struct *t)

为 syscalls 记录 signal 信息

参数

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

旧凭据

描述

只需检查 proc 是否已具有文件给出的 capabilities,如果没有,则存储权限提升信息,以便在 syscall 结束时进行后续审计

-Eric

void __audit_log_capset(const struct cred *new, const struct cred *old)

存储有关 capset syscall 参数的信息

参数

const struct cred *new

新凭据

const struct cred *old

旧 (当前) 凭据

描述

记录 userspace 发送给 sys_capset 的参数,以便在适用时由审计系统进行后续打印

void audit_core_dumps(long signr)

记录有关异常结束的进程的信息

参数

long signr

信号值

描述

如果进程以 core dump 结束,则可能存在一些可疑情况,我们应该记录该事件以进行调查。

void audit_seccomp(unsigned long syscall, long signr, int code)

记录有关 seccomp 操作的信息

参数

unsigned long syscall

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 审计消息序列 (serial) 号

void *data

payload 数据

size_t datasz

payload 数据的大小

int audit_list_rules_send(struct sk_buff *request_skb, int seq)

列出审计规则

参数

struct sk_buff *request_skb

我们正在回复的请求的 skb(用于定位回复)

int seq

netlink 审计消息序列 (serial) 号

int parent_len(const char *path)

查找路径名的父部分长度

参数

const char *path

要确定长度的路径名

int audit_compare_dname_path(const struct qstr *dname, const char *path, int parentlen)

将给定的 dentry 名称与给定路径中的最后一个组件进行比较。 返回 0 表示匹配。

参数

const struct qstr *dname

我们正在比较的 dentry 名称

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,不做修改。

unsigned int bio_add_max_vecs(void *kaddr, unsigned int len)

将数据添加到 bio 所需的 bio_vecs 的数量

参数

void *kaddr

要添加的内核虚拟地址

unsigned int len

要添加的字节长度

描述

计算最坏情况下,需要分配多少 bio_vecs 才能将 [kaddr:len] 中的内核虚拟地址范围添加到 bio。

bool bio_is_zone_append(struct bio *bio)

这是一个 zone append bio 吗?

参数

struct bio *bio

要检查的 bio

描述

检查 bio 是否为 zone append 操作。核心块层代码和 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 *rq, void *kbuf, unsigned int len, gfp_t gfp_mask)

为直通请求将内核数据映射到请求。

参数

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

要应用的限制

描述

将 **lim** 中的限制应用于 q,这些限制是从 queue_limits_start_update() 获取并由调用者更新的。调用者必须冻结队列或确保没有其他方式的未完成 I/O。

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

int queue_limits_commit_update_frozen(struct request_queue *q, struct queue_limits *lim)

提交队列限制的原子更新

参数

struct request_queue *q

要更新的队列

struct queue_limits *lim

要应用的限制

描述

将 **lim** 中的限制应用于 q,这些限制是从 queue_limits_start_update() 获取并由调用者使用新值更新的。在更新之前冻结队列,并在之后解冻队列。

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

int queue_limits_set(struct request_queue *q, struct queue_limits *lim)

将队列限制应用于队列

参数

struct request_queue *q

要更新的队列

struct queue_limits *lim

要应用的限制

描述

将 **lim** 中的限制应用于 q,这些限制是新初始化的。要更新现有限制,请改用 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

要发出刷新的 blockdev

描述

为有问题的块设备发出刷新。

int blkdev_issue_discard(struct block_device *bdev, sector_t sector, sector_t nr_sects, gfp_t gfp_mask)

排队丢弃

参数

struct block_device *bdev

要发出丢弃的 blockdev

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

要发出的 blockdev

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

要写入的 blockdev

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 cmd

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 重新映射操作添加跟踪信息

参数

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)

为请求重新映射操作添加跟踪信息

参数

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 也将在磁盘之前被释放。

上下文

可以睡眠

unsigned int bdev_count_inflight(struct block_device *part)

获取块设备正在进行的 IO 数量。

参数

struct block_device *part

块设备。

描述

Inflight 在这里意味着已开始 IO 记帐,对于基于 bio 的块设备,从 bdev_start_io_acct() 开始,对于基于 rq 的块设备,从 blk_account_io_start() 开始。

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)

将磁盘标记为死亡

参数

struct gendisk *disk

要标记为死亡的磁盘

描述

将磁盘标记为死亡(例如,意外移除)并且不接受任何新的 I/O 到该磁盘。

void del_gendisk(struct gendisk *disk)

移除 gendisk

参数

struct gendisk *disk

要移除的 struct gendisk

描述

移除 gendisk 及其所有相关资源。这将删除与 gendisk 关联的分区,并注销关联的 request_queue。

这是对相应 __device_add_disk() 调用的计数器。

struct gendisk 的最终移除发生在它的引用计数达到 0 时,使用 put_disk(),如果使用了 __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 磁盘,在处理 probe 错误时(即在调用 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_validate_blocksize(struct block_device *bdev, int block_size)

检查此块大小是否可接受

参数

struct block_device *bdev

要检查的块设备

int block_size

要检查的块大小

描述

对于不使用缓冲区头或块设备页面缓存的块设备用户,请确保此块大小可以与该设备一起使用。

返回

成功时返回零,失败时返回负错误代码。

int bdev_freeze(struct block_device *bdev)

锁定文件系统并强制其进入一致状态

参数

struct block_device *bdev

要锁定的块设备

描述

如果在此设备上找到超级块,我们将获取其 s_umount 信号量,以确保在快照创建完成之前没有人卸载。引用计数器 (bd_fsfreeze_count) 保证只有最后一个解冻进程才能在同时到达多个冻结请求时实际解冻冻结的文件系统。它在 bdev_freeze() 中计数增加,并在 bdev_thaw() 中计数减少。当它变为 0 时,thaw_bdev() 将实际解冻。

返回

成功时返回零,失败时返回负错误代码。

int bdev_thaw(struct block_device *bdev)

解锁文件系统

参数

struct block_device *bdev

要解锁的块设备

描述

bdev_freeze() 之后解锁文件系统并将其标记为再次可写。

返回

成功时返回零,失败时返回负错误代码。

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)

将块设备标记为 dead

参数

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,则此函数将尝试保留一个具有给定主设备号的设备,并在成功时返回零。

失败时返回 -ve 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

此设备范围的名称

描述

注销并销毁占用由 majorbaseminorcount 描述的区域的 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 之前不会释放父对象。应在 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 将保持不变,并且它们的 fops 仍然可以调用,即使在 cdev_del 返回之后也是如此。

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 的 blocking_notifier_head

节点

链表指针

描述

通知器代码维护一个 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 的新速率

描述

对于 pre-notifier,old_rate 是 clk 在此速率更改之前的速率,new_rate 是速率将来的值。对于 post-notifier,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() 条件。该实现使用 **dev** 和 **id** 来确定时钟消费者,从而确定时钟生产者。(换句话说,**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() 条件。该实现使用 **dev** 和 **clk_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,失败时返回 errno。

此辅助函数允许驱动程序在一个操作中获取多个 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() 相同,除非没有时钟生产者。在这种情况下,函数不会返回 -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() 条件。该实现使用 devid 来确定时钟消费者,从而确定时钟生产者。(也就是说,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() 条件。该实现使用 devid 来确定时钟消费者,从而确定时钟生产者。(也就是说,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() 条件。该实现使用 devid 来确定时钟消费者,从而确定时钟生产者。(也就是说,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() 条件。该实现使用 devid 来确定时钟消费者,从而确定时钟生产者。如果未找到此类 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() 条件。该实现使用 devid 来确定时钟消费者,从而确定时钟生产者。如果未找到此类 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() 条件。该实现使用 devid 来确定时钟消费者,从而确定时钟生产者。如果未找到此类 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() 条件。该实现使用 devid 来确定时钟消费者,从而确定时钟生产者。如果未找到此类 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

描述

此函数解析时钟,并使用它们通过使用 npcon_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)

通知系统何时应运行这组 clk。

参数

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_enable() 调用必须与相同数量的 clk_disable() 调用保持平衡,才能禁用时钟源。

void clk_bulk_disable(int num_clks, const struct clk_bulk_data *clks)

通知系统何时不再需要这组 clk。

参数

int num_clks

clk_bulk_data 的数量

const struct clk_bulk_data *clks

消费者的 clk_bulk_data 表

描述

通知系统驱动程序不再需要一组 clk,可以关闭。

可以从原子上下文中调用。

实现细节:如果这组 clk 在多个驱动程序之间共享,则 clk_bulk_enable() 调用必须与相同数量的 clk_bulk_disable() 调用保持平衡,才能禁用时钟源。

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

父时钟源

描述

驱动程序可以使用此函数来检查一个时钟是否可以成为另一个时钟的父时钟,而无需实际更改父时钟。

如果 **parent** 是 **clk** 的可能父时钟,则返回 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_id** 和 **con_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(),不同之处在于,即使 cond_resched() 机制已关闭,它也被定义为向 RCU 任务报告潜在的静止状态,正如某些 PREEMPTION 内核所倡导的那样。

rcu_softirq_qs_periodic

rcu_softirq_qs_periodic (old_ts)

报告 RCU 和 RCU 任务静止状态

参数

old_ts

处理开始时的节拍数。

描述

此帮助程序用于长时间运行的 softirq 处理程序,例如网络中的 NAPI 线程。调用者应在 softirq 处理程序的开头初始化作为 **old_ts** 传递的变量。频繁调用时,此宏将在此后每 100 毫秒调用一次 rcu_softirq_qs(),这将提供 RCU 和 RCU 任务静止状态。请注意,此宏会修改其 old_ts 参数。

由于已禁用 softirq 的代码区域充当 RCU 读取端临界区,因此应在启用 softirq(和抢占)的情况下调用此宏。

当定义了 CONFIG_PREEMPT_RT 时,不需要此宏。RT 内核将有更多机会调用 schedule() 调用并提供必要的静止状态。相比之下,仅调用 cond_resched() 不会达到相同的效果,因为 cond_resched() 不提供 RCU 任务静止状态。

RCU_LOCKDEP_WARN

RCU_LOCKDEP_WARN (c, s)

如果满足指定的条件,则发出 lockdep splat

参数

c

要检查的条件

s

信息性消息

描述

这会在检查 (c) 之前检查 debug_lockdep_rcu_enabled(),以防止由于 lockdep 尚未初始化而导致的早期引导 splat,并在检查 (c) 之后重新检查它,以防止由于 lockdep 被禁用而导致的误报 splat。有关更多详细信息,请参见 提交 3066820034b5dd(“rcu:拒绝 RCU_LOCKDEP_WARN() 误报”)。

lockdep_assert_in_rcu_read_lock

lockdep_assert_in_rcu_read_lock ()

如果未受 rcu_read_lock() 保护,则发出警告

描述

如果启用了 lockdep 并且没有 rcu_read_lock() 生效,则发出 splat。

lockdep_assert_in_rcu_read_lock_bh

lockdep_assert_in_rcu_read_lock_bh ()

如果未受 rcu_read_lock_bh() 保护,则发出警告

描述

如果启用了 lockdep 并且没有生效的 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() 保护,则发出警告。

描述

如果启用了 lockdep 并且没有生效的 rcu_read_lock_sched(),则会触发 splat。请注意,preempt_disable() 及其同类函数在这里是不够的,而是需要实际的 rcu_read_lock_sched()

lockdep_assert_in_rcu_reader

lockdep_assert_in_rcu_reader ()

如果不在某种类型的 RCU 读取器中,则发出警告。

描述

如果启用了 lockdep 并且没有任何类型的 RCU 读取器生效,则会触发 splat。请注意,受 preempt_disable、local_bh_disable() 和 local_irq_disable() 等保护的代码区域都符合 RCU 读取器的条件。

请注意,这永远不会在未同时使用 PREEMPT_COUNT 构建的 PREEMPT_NONE 或 PREEMPT_VOLUNTARY 内核中触发。但是,如果您启用了 lockdep,那么最好也启用 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

取消引用将发生的 lockdep 条件。

描述

执行替换,其中 rcu_ptr 是 RCU 注释的指针,而 c 是传递给用于读取该指针的 rcu_dereference_protected() 调用的 lockdep 参数。将返回 rcu_ptr 的旧值,并且 rcu_ptr 将设置为 ptr

rcu_access_pointer

rcu_access_pointer (p)

获取 RCU 指针,不进行取消引用。

参数

p

要读取的指针

描述

返回指定 RCU 保护的指针的值,但省略 RCU 读取端临界区中的 lockdep 检查。当访问此指针的值,但不取消引用指针时,这很有用,例如,当针对 NULL 测试 RCU 保护的指针时。虽然 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 内核开始,vanilla 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 内核开始,vanilla 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 位于 RCU 读取端临界区中时在一个 CPU 上调用 synchronize_rcu(),则保证 synchronize_rcu() 会阻塞,直到所有其他 CPU 退出其临界区之后。类似地,如果在其他 CPU 位于 RCU 读取端临界区中时在一个 CPU 上调用 call_rcu(),则相应的 RCU 回调的调用会延迟到所有其他 CPU 退出其临界区之后。

synchronize_rcu()call_rcu() 都还会等待禁用抢占的代码区域,包括禁用中断或软中断的代码区域。

但是,请注意,允许 RCU 回调与新的 RCU 读取端临界区并发运行。发生这种情况的一种方式是通过以下事件序列:(1)CPU 0 进入 RCU 读取端临界区,(2)CPU 1 调用 call_rcu() 以注册 RCU 回调,(3)CPU 0 退出 RCU 读取端临界区,(4)CPU 2 进入 RCU 读取端临界区,(5)调用 RCU 回调。这是合法的,因为与 call_rcu() 并发运行的 RCU 读取端临界区(因此可能引用相应的 RCU 回调会释放的某些内容)在调用相应的 RCU 回调之前已完成。

RCU 读取端临界区可以嵌套。任何延迟操作都将延迟到最外层的 RCU 读取端临界区完成。

您可以通过遵循以下规则来避免阅读和理解下一段:不要将任何会在 !PREEMPTION 内核中阻塞的内容放入 rcu_read_lock() RCU 读取端临界区中。但是,如果您想要完整的故事,请继续阅读!

在不可抢占的 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() 都不会死锁。由于当禁用中断时调用 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)

标记软中断(softirq)专用 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-classic 临界区的结束

参数

void

无参数

描述

有关更多信息,请参见 rcu_read_lock_sched()

RCU_INIT_POINTER

RCU_INIT_POINTER (p, v)

初始化一个 RCU 保护的指针

参数

p

要初始化的指针。

v

指针要初始化的值。

描述

在特殊情况下初始化 RCU 保护的指针,在这些情况下,读取者不需要 CPU 或编译器的排序约束。 这些特殊情况是

  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() 函数处理此问题。 为了使通用回调函数处理 rcu_head 的不同偏移量,回调需要确定被释放对象的起始地址,该起始地址可以是大型 kmalloc 或 vmalloc 分配。 为了允许简单地将指针向下对齐到页边界,只能容纳最大 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() 注释的上下文中以无头方式释放。 否则,请切换并在 ptr 类型中嵌入 rcu_head 结构。

void rcu_head_init(struct rcu_head *rhp)

初始化 rcu_head 以用于 rcu_head_after_call_rcu()

参数

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 已使用 func 传递给 call_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 rcu_set_gpwrap_lag(unsigned long lag_gps)

设置 RCU GP 序列溢出滞后值。

参数

unsigned long lag_gps

将溢出滞后设置为此多个宽限期的计数器值,rcutorture 使用该值快速强制 gpwrap 情况。 lag_gps = 0 表示我们将其重置回启动时间值。

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() 的逻辑。有关内存排序和其他功能的更多详细信息,请参阅 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 读取端临界区由 rcu_read_lock()rcu_read_unlock() 分隔,并且可以嵌套。此外,但仅在 v5.0 及更高版本中,禁用中断、抢占或软中断的代码区域也充当 RCU 读取端临界区。这包括硬件中断处理程序、软中断处理程序和 NMI 处理程序。

请注意,所有 CPU 必须同意宽限期已扩展到所有先前存在的 RCU 读取端临界区之外。在具有多个 CPU 的系统上,这意味着当调用“func()”时,保证每个 CPU 自其最后一个 RCU 读取端临界区结束以来都已执行完整的内存屏障,该 RCU 读取端临界区的开始先于调用 call_rcu()。这也意味着每个执行 RCU 读取端临界区的 CPU,该临界区继续超出“func()”的开始,必须在 call_rcu() 之后但在该 RCU 读取端临界区的开始之前执行内存屏障。请注意,这些保证包括脱机、空闲或以用户模式执行的 CPU,以及在内核中执行的 CPU。

此外,如果 CPU A 调用了 call_rcu(),并且 CPU B 调用了 resulting RCU 回调函数“func()”,则保证 CPU A 和 CPU B 在调用 call_rcu() 和调用“func()”之间的时间间隔内执行完整的内存屏障——即使 CPU A 和 CPU B 是同一个 CPU(但同样只有在系统有多个 CPU 的情况下)。

此处描述了这些内存排序保证的实现:TREE_RCU 的宽限期内存排序之旅

特别是对于 call_rcu()(与其他 call_rcu*() 函数相反),在以 CONFIG_RCU_LAZY=y 构建的内核中,call_rcu() 可能会延迟数秒,然后才开始对应回调所需的宽限期。这种延迟可以显著提高低利用率电池供电设备的能效。为了避免这种延迟,在对延迟敏感的内核代码中,请使用 call_rcu_hurry()

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 读取端临界区结束以来都已执行完整的内存屏障,该 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()

是的,此函数不考虑计数器回绕。但是计数器回绕是无害的。如果计数器回绕,我们已经等待超过 10 亿个宽限期(在 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()

是的,此函数不考虑计数器回绕。但是计数器回绕是无害的。如果计数器回绕,我们已经等待超过 10 亿个宽限期(在 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(),这意味着调用者可能会不必要地等待一到三秒。

这旨在供测试套件使用,以避免 OOM,通过在启动下一个测试之前刷新先前测试中的 RCU 回调。有关更多信息,请参见 rcutree.do_rcu_barrier 模块参数。

为什么不简单地使 rcu_barrier() 更具可伸缩性?这可能是最终的端点,但暂时让我们保持简单。请注意,模块参数基础结构序列化对给定 .set() 函数的调用,但是如果可能同时调用 .set(),我们已经准备好了!

void synchronize_rcu_expedited(void)

暴力 RCU 宽限期

参数

void

无参数

描述

等待 RCU 宽限期,但加快它。基本思想是 IPI 所有非空闲、非nohz 的在线 CPU。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 时出现误报。

请注意,如果 CPU 从 RCU 的角度来看处于空闲循环中(即:我们位于 ct_idle_enter() 和 ct_idle_exit() 之间的部分中),则即使 CPU 执行了 rcu_read_lock()rcu_read_lock_held() 也会将 *ret 设置为 false。这样做的原因是 RCU 忽略处于此类部分的 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_unexpedite_gp() 撤消了所有先前对 rcu_expedite_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() 必须发生在同一上下文中,例如,如果在 irq 处理程序中调用了匹配的 rcu_read_lock(),则在进程上下文中调用 rcu_read_unlock() 是非法的。

请注意,如果 CPU 从 RCU 的角度来看是空闲的或离线的,则不允许使用 rcu_read_lock(),因此也要检查这些情况。

int rcu_read_lock_bh_held(void)

我们是否可能位于 RCU-bh 读取侧临界区?

参数

void

无参数

描述

检查是否禁用了 bottom half,这涵盖了 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)

初始化用于 debugobjects 的堆栈上的 rcu_head

参数

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)

销毁用于 debugobjects 的堆栈上的 rcu_head

参数

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,它可以从 CPU 调用,该 CPU 从 RCU 的角度来看处于空闲循环或离线状态。

srcu_dereference_check

srcu_dereference_check (p, ssp, c)

获取 SRCU 保护的指针以供以后解引用

参数

p

要获取并保护以供以后解引用的指针

ssp

指向 srcu_struct 的指针,用于检查我们是否真的在 SRCU 读取端临界区中。

c

要检查的更新端使用的条件

描述

如果启用了 PROVE_RCU,则在 RCU 读取端临界区之外调用此函数将导致 RCU 锁依赖错误,除非 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 锁依赖错误。

srcu_dereference_notrace

srcu_dereference_notrace (p, ssp)

此处没有跟踪,也没有锁依赖调用

参数

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() 也是非法的。

struct srcu_ctr __percpu *srcu_read_lock_fast(struct srcu_struct *ssp)

为受 SRCU 保护的结构注册一个新的读取器。

参数

struct srcu_struct *ssp

要在其中注册新读取器的 srcu_struct。

描述

进入 SRCU 读取端临界区,但适用于轻量级的无 smp_mb() 读取器。有关更多信息,请参见 srcu_read_lock()

如果在 srcu_struct 结构上使用了 srcu_read_lock_fast(),则无论之前、期间还是之后,都不能使用其他任何风格。请注意,对于 _fast srcu_struct 结构,宽限期自动加速已禁用,因为自动加速的宽限期会调用 synchronize_rcu_expedited()、IPI 以及所有内容。

请注意,只能从 RCU 正在监视的上下文中调用 srcu_read_lock_fast(),也就是说,从调用 rcu_read_lock() 合法的上下文中。否则,lockdep 将会报错。

struct srcu_ctr __percpu *srcu_down_read_fast(struct srcu_struct *ssp)

为受 SRCU 保护的结构注册一个新的读取器。

参数

struct srcu_struct *ssp

要在其中注册新读取器的 srcu_struct。

描述

进入类似信号量的 SRCU 读取端临界区,但适用于轻量级的无 smp_mb() 读取器。有关更多信息,请参见 srcu_read_lock_fast()srcu_down_read()

srcu_down_read_fast()srcu_read_lock_fast() 可以并发使用相同的 srcu_struct。

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() 的类似信号量的性质意味着可以从其他一些上下文(例如,从其他一些任务或从 irq 处理程序)调用匹配的 srcu_up_read()。但是,既不能从 NMI 处理程序调用 srcu_down_read(),也不能从 NMI 处理程序调用 srcu_up_read()

srcu_down_read() 的调用可以嵌套,类似于对 down_read() 的调用可以嵌套的方式。srcu_down_read()srcu_read_lock() 可以并发使用相同的 srcu_struct。

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_fast(struct srcu_struct *ssp, struct srcu_ctr __percpu *scp)

从受 SRCU 保护的结构中注销旧的读取器。

参数

struct srcu_struct *ssp

要在其中注销旧的读取器的 srcu_struct。

struct srcu_ctr __percpu *scp

来自相应 srcu_read_lock_fast() 的返回值。

描述

退出轻量级 SRCU 读取端临界区。

void srcu_up_read_fast(struct srcu_struct *ssp, struct srcu_ctr __percpu *scp)

从受 SRCU 保护的结构中注销旧的读取器。

参数

struct srcu_struct *ssp

要在其中注销旧的读取器的 srcu_struct。

struct srcu_ctr __percpu *scp

来自相应 srcu_read_lock_fast() 的返回值。

描述

退出 SRCU 读取端临界区,但不一定与匹配的 srcu_down_read_fast() 位于同一上下文中。

void srcu_read_unlock_lite(struct srcu_struct *ssp, int idx)

从受 SRCU 保护的结构中注销旧的读取器。

参数

struct srcu_struct *ssp

要在其中注销旧的读取器的 srcu_struct。

int idx

来自相应 srcu_read_lock_lite() 的返回值。

描述

退出轻量级 SRCU 读取端临界区。

void srcu_read_unlock_nmisafe(struct srcu_struct *ssp, int idx)

从受 SRCU 保护的结构中注销旧的读取器。

参数

struct srcu_struct *ssp

要在其中注销旧的读取器的 srcu_struct。

int idx

来自相应 srcu_read_lock_nmisafe() 的返回值。

描述

退出 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)

解构一个睡眠-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() 分隔,并且可能是嵌套的。

回调将从进程上下文中调用,但禁用了 bh。因此,回调函数必须快速且不得阻塞。

有关内存排序保证的更多详细信息,请参见 call_rcu() 的描述。

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() 可能发生的饥饿,它首先等待 index=!(ssp->srcu_ctrp - ssp->sda->srcu_ctrs[0]) 的计数降至零,然后翻转 ->srcu_ctrp 并等待另一个索引的计数。

可以阻塞;必须从进程上下文中调用。

请注意,从相应的 SRCU 读取侧临界区调用 synchronize_srcu() 是非法的;这样做会导致死锁。但是,从某个其他 srcu_struct 的读取侧临界区调用一个 srcu_struct 上的 synchronize_srcu() 是完全合法的,只要生成的 srcu_struct 图是无环的。

synchronize_srcu() 隐含了内存排序约束。在具有多个 CPU 的系统上,当 synchronize_srcu() 返回时,保证每个 CPU 都执行了一个完整的内存屏障,该内存屏障自从其最后一个相应的 SRCU 读取侧临界区结束以来,该临界区的开始先于对 synchronize_srcu() 的调用。此外,具有 SRCU 读取侧临界区的每个 CPU,该临界区超出 synchronize_srcu() 的返回,都保证在 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() 确定),则加速第一个请求。此语义由 Classic SRCU 提供,并被其用户所依赖,因此 TREE SRCU 也必须提供此语义。请注意,检测空闲状态是启发式的,并且容易出现误报和漏报。

unsigned long get_state_synchronize_srcu(struct srcu_struct *ssp)

提供一个宽限期结束 Cookie

参数

struct srcu_struct *ssp

要提供 Cookie 的 srcu_struct。

描述

此函数返回一个 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,可以将其传递给 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 宽限期,也超过 700 万年。

对于 CONFIG_SMP=n 系统(也具有 CONFIG_PREEMPTION=n)而言,包装/溢出问题更为严重,该系统选择 Tiny SRCU。这使用一个 16 位的 Cookie,rcutorture 通常在几分钟内包装该 Cookie。如果这证明是一个问题,则此计数器将扩展到与 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

用作循环游标的 类型 *。

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

要从列表中删除的元素。

注意

在此之后,entry 上的 list_empty() 不会返回 true,该条目处于未定义状态。 它对于基于 RCU 的无锁遍历很有用。

描述

特别是,这意味着我们不能污染可能仍用于遍历列表的前向指针。

调用者必须采取必要的预防措施(例如持有适当的锁),以避免与同一链表上运行的其他链表修改原语(例如 list_del_rcu()list_add_rcu())发生竞争。但是,与 _rcu 链表遍历原语(例如 list_for_each_entry_rcu())并发运行是完全合法的。

请注意,不允许调用者立即释放新删除的条目。相反,必须使用 synchronize_rcu()call_rcu() 来延迟释放,直到 RCU 宽限期过去。

void list_bidir_del_rcu(struct list_head *entry)

从链表中删除条目,无需重新初始化

参数

struct list_head *entry

要从列表中删除的元素。

描述

list_del_rcu() 相比,不会污染 prev 指针,因此允许通过 list_bidir_prev_rcu() 进行向后遍历。

调用者必须采取必要的预防措施(例如持有适当的锁),以避免与同一链表上运行的其他链表修改原语(例如 list_bidir_del_rcu()list_add_rcu())发生竞争。但是,与 _rcu 链表遍历原语(例如 list_for_each_entry_rcu())并发运行是完全合法的。

请注意,list_del_rcu()list_bidir_del_rcu() 不得在同一链表上使用。

请注意,不允许调用者立即释放新删除的条目。相反,必须使用 synchronize_rcu()call_rcu() 来延迟释放,直到 RCU 宽限期过去。

注意

在此之后,entry 上的 list_empty() 不会返回 true,因为该条目处于特殊的未定义状态,该状态允许基于 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, ...

描述

由 **prev** 和 **next** 指向的列表可以与此函数并发地进行 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_read_lock() 保护,此原语就可以与 _rcu 链表修改原语(例如 list_add_rcu())安全地并发运行。

list_first_or_null_rcu

list_first_or_null_rcu (ptr, type, member)

从链表中获取第一个元素

参数

ptr

从中获取元素的链表头。

type

嵌入此结构的结构体类型。

member

结构体中 list_head 的名称。

描述

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

只要由 rcu_read_lock() 保护,此原语就可以与 _rcu 链表修改原语(例如 list_add_rcu())安全地并发运行。

list_next_or_null_rcu

list_next_or_null_rcu (head, ptr, type, member)

从列表中获取下一个元素

参数

head

列表的头部。

ptr

从中获取下一个元素的链表头部。

type

嵌入此结构的结构体类型。

member

结构体中 list_head 的名称。

描述

请注意,如果 ptr 位于列表末尾,则返回 NULL。

只要由 rcu_read_lock() 保护,此原语就可以与 _rcu 链表修改原语(例如 list_add_rcu())安全地并发运行。

list_for_each_entry_rcu

list_for_each_entry_rcu (pos, head, member, cond...)

迭代给定类型的 rcu 列表

参数

pos

用作循环游标的 类型 *。

head

链表的头部。

member

结构体中 list_head 的名称。

cond...

如果从非 RCU 保护调用,则为可选的 lockdep 表达式。

描述

只要由 rcu_read_lock() 保护,此链表遍历原语就可以与 _rcu 链表修改原语(例如 list_add_rcu())安全地并发运行。

list_for_each_entry_srcu

list_for_each_entry_srcu (pos, head, member, cond)

迭代给定类型的 rcu 列表

参数

pos

用作循环游标的 类型 *。

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

用作循环游标的 类型 *。

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

用作循环游标的 类型 *。

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

用作循环游标的 类型 *。

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

要从哈希列表中删除的元素。

注意

在此之后,entry 上的 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 head 指向的列表

参数

struct hlist_head *left

左侧的 hlist head

struct hlist_head *right

右侧的 hlist head

描述

列表初始状态为 [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

用作循环游标的 类型 *。

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

用作循环游标的 类型 *。

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

用作循环游标的 类型 *。

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

用作循环游标的 类型 *。

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

用作循环游标的 类型 *。

member

结构体中 hlist_node 的名称。

hlist_for_each_entry_continue_rcu_bh

hlist_for_each_entry_continue_rcu_bh (pos, member)

在当前点之后继续迭代 hlist

参数

pos

用作循环游标的 类型 *。

member

结构体中 hlist_node 的名称。

hlist_for_each_entry_from_rcu

hlist_for_each_entry_from_rcu (pos, member)

从当前点继续迭代 hlist

参数

pos

用作循环游标的 类型 *。

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 之后的列表元素。

参数

节点

列表的元素。

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

用作循环游标的 类型 *。

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

用作循环游标的 类型 *。

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 循环执行应该是安全的,因为应该已经过去了足够的定时器中断和上下文切换。

调用者必须禁用硬 IRQ。

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() 由一个任务调用时,则保证该任务会阻塞,直到所有其他任务退出其读取端关键部分。 同样,如果在其他任务位于 RCU 读取端关键部分内时在一个任务上调用 call_rcu_trace(),则对应 RCU 回调的调用将延迟到所有其他任务退出其关键部分之后。

有关更多详细信息,请参见 rcu_read_lock() 的文档。

void rcu_read_unlock_trace(void)

标记 RCU 跟踪读取端关键部分的结束

参数

void

无参数

描述

与先前对 rcu_read_lock_trace() 的调用配对,并允许嵌套。如果在没有匹配的 rcu_read_lock_trace() 的情况下调用 rcu_read_unlock_trace() 是禁止的,并且会导致 lockdep 抱怨。

更多细节,请参考 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)。 值 0 并不表示可以安全地调度受此引用计数器保护的对象进行解构。 如果您想知道引用计数器是否已标记为 DEAD(由 rcuref_put() 发出信号),请使用 rcuread_is_dead()。

bool rcuref_is_dead(rcuref_t *ref)

检查 rcuref 是否已被标记为 dead

参数

rcuref_t *ref

指向引用计数的指针

返回

如果对象已被标记为 DEAD,则为 True。 这表示先前调用 rcuref_put() 在此引用计数器上返回 true,这意味着可以安全地调度受保护对象进行解构。 否则,返回 false。

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

第二个旧状态值。

描述

这两个 old-state 值必须从 get_state_synchronize_rcu_full()start_poll_synchronize_rcu_full()get_completed_synchronize_rcu_full() 中获得。 如果这两个值相同,则返回 true,否则返回 false。 这允许生命周期由 old-state 值跟踪的结构将这些值推送到列表标头,从而使这些结构稍微小一些。

请注意,等式是按位判断的,因此在一个字段中具有已完成状态的 rcu_gp_oldstate 结构将与在另一个字段中具有已完成状态的结构进行比较而不相等。 毕竟,rcu_gp_oldstate 结构是不透明的,那么这种情况最初是如何发生的呢?