diff options
Diffstat (limited to 'Documentation/translations/zh_CN/core-api')
11 files changed, 763 insertions, 71 deletions
diff --git a/Documentation/translations/zh_CN/core-api/cachetlb.rst b/Documentation/translations/zh_CN/core-api/cachetlb.rst index 6fee45fe5e80..b4a76ec75daa 100644 --- a/Documentation/translations/zh_CN/core-api/cachetlb.rst +++ b/Documentation/translations/zh_CN/core-api/cachetlb.rst @@ -5,6 +5,7 @@ :翻译: 司延腾 Yanteng Si <siyanteng@loongson.cn> + 周彬彬 Binbin Zhou <zhoubinbin@loongson.cn> :校译: @@ -278,6 +279,11 @@ HyperSparc cpu就是这样一个具有这种属性的cpu。 CPU上,因为它将cpu存储到页面上,使其变脏。同样,请看 sparc64关于如何处理这个问题的例子。 + ``void flush_dcache_folio(struct folio *folio)`` + + 该函数的调用情形与flush_dcache_page()相同。它允许架构针对刷新整个 + folio页面进行优化,而不是一次刷新一页。 + ``void copy_to_user_page(struct vm_area_struct *vma, struct page *page, unsigned long user_vaddr, void *dst, void *src, int len)`` ``void copy_from_user_page(struct vm_area_struct *vma, struct page *page, diff --git a/Documentation/translations/zh_CN/core-api/cpu_hotplug.rst b/Documentation/translations/zh_CN/core-api/cpu_hotplug.rst index 85a264287426..4772a900c37a 100644 --- a/Documentation/translations/zh_CN/core-api/cpu_hotplug.rst +++ b/Documentation/translations/zh_CN/core-api/cpu_hotplug.rst @@ -4,6 +4,7 @@ :翻译: 司延腾 Yanteng Si <siyanteng@loongson.cn> + 周彬彬 Binbin Zhou <zhoubinbin@loongson.cn> :校译: @@ -15,12 +16,13 @@ 内核中的CPU热拔插 ================= -:时间: 2016年12月 +:时间: 2021年9月 :作者: Sebastian Andrzej Siewior <bigeasy@linutronix.de>, - Rusty Russell <rusty@rustcorp.com.au>, - Srivatsa Vaddagiri <vatsa@in.ibm.com>, - Ashok Raj <ashok.raj@intel.com>, - Joel Schopp <jschopp@austin.ibm.com> + Rusty Russell <rusty@rustcorp.com.au>, + Srivatsa Vaddagiri <vatsa@in.ibm.com>, + Ashok Raj <ashok.raj@intel.com>, + Joel Schopp <jschopp@austin.ibm.com>, + Thomas Gleixner <tglx@linutronix.de> 简介 ==== @@ -139,7 +141,7 @@ CPU的热拔插协作 下线情况 -------- -一旦CPU被逻辑关闭,注册的热插拔状态的清除回调将被调用,从 ``CPUHP_ONLINE`` 开始,在 +一旦CPU被逻辑关闭,注册的热插拔状态的清除回调将被调用,从 ``CPUHP_ONLINE`` 开始,到 ``CPUHP_OFFLINE`` 状态结束。这包括: * 如果任务因暂停操作而被冻结,那么 *cpuhp_tasks_frozen* 将被设置为true。 @@ -154,82 +156,399 @@ CPU的热拔插协作 * 一旦所有的服务被迁移,内核会调用一个特定的例程 ``__cpu_disable()`` 来进行特定的清 理。 -使用热插拔API -------------- +CPU热插拔API +============ + +CPU热拔插状态机 +--------------- + +CPU热插拔使用一个从CPUHP_OFFLINE到CPUHP_ONLINE的线性状态空间的普通状态机。每个状态都 +有一个startup和teardown的回调。 + +当一个CPU上线时,将按顺序调用startup回调,直到达到CPUHP_ONLINE状态。当设置状态的回调 +或将实例添加到多实例状态时,也可以调用它们。 + +当一个CPU下线时,将按相反的顺序依次调用teardown回调,直到达到CPUHP_OFFLINE状态。当删 +除状态的回调或从多实例状态中删除实例时,也可以调用它们。 + +如果某个使用场景只需要一个方向的热插拔操作回调(CPU上线或CPU下线),则在设置状态时, +可以将另一个不需要的回调设置为NULL。 + +状态空间被划分成三个阶段: + +* PREPARE阶段 + + PREPARE阶段涵盖了从CPUHP_OFFLINE到CPUHP_BRINGUP_CPU之间的状态空间。 + + 在该阶段中,startup回调在CPU上线操作启动CPU之前被调用,teardown回调在CPU下线操作使 + CPU功能失效之后被调用。 + + 这些回调是在控制CPU上调用的,因为它们显然不能在热插拔的CPU上运行,此时热插拔的CPU要 + 么还没有启动,要么已经功能失效。 + + startup回调用于设置CPU成功上线所需要的资源。teardown回调用于释放资源或在热插拔的CPU + 功能失效后,将待处理的工作转移到在线的CPU上。 + + 允许startup回调失败。如果回调失败,CPU上线操作被中止,CPU将再次被降到之前的状态(通 + 常是CPUHP_OFFLINE)。 + + 本阶段中的teardown回调不允许失败。 + +* STARTING阶段 + + STARTING阶段涵盖了CPUHP_BRINGUP_CPU + 1到CPUHP_AP_ONLINE之间的状态空间。 + + 该阶段中的startup回调是在早期CPU设置代码中的CPU上线操作期间,禁用中断的情况下在热拔 + 插的CPU上被调用。teardown回调是在CPU完全关闭前不久的CPU下线操作期间,禁用中断的情况 + 下在热拔插的CPU上被调用。 + + 该阶段中的回调不允许失败。 + + 回调用于低级别的硬件初始化/关机和核心子系统。 + +* ONLINE阶段 + + ONLINE阶段涵盖了CPUHP_AP_ONLINE + 1到CPUHP_ONLINE之间的状态空间。 + + 该阶段中的startup回调是在CPU上线时在热插拔的CPU上调用的。teardown回调是在CPU下线操 + 作时在热插拔CPU上调用的。 + + 回调是在每个CPU热插拔线程的上下文中调用的,该线程绑定在热插拔的CPU上。回调是在启用 + 中断和抢占的情况下调用的。 + + 允许回调失败。如果回调失败,CPU热插拔操作被中止,CPU将恢复到之前的状态。 + +CPU 上线/下线操作 +----------------- + +一个成功的上线操作如下:: + + [CPUHP_OFFLINE] + [CPUHP_OFFLINE + 1]->startup() -> 成功 + [CPUHP_OFFLINE + 2]->startup() -> 成功 + [CPUHP_OFFLINE + 3] -> 略过,因为startup == NULL + ... + [CPUHP_BRINGUP_CPU]->startup() -> 成功 + === PREPARE阶段结束 + [CPUHP_BRINGUP_CPU + 1]->startup() -> 成功 + ... + [CPUHP_AP_ONLINE]->startup() -> 成功 + === STARTUP阶段结束 + [CPUHP_AP_ONLINE + 1]->startup() -> 成功 + ... + [CPUHP_ONLINE - 1]->startup() -> 成功 + [CPUHP_ONLINE] + +一个成功的下线操作如下:: + + [CPUHP_ONLINE] + [CPUHP_ONLINE - 1]->teardown() -> 成功 + ... + [CPUHP_AP_ONLINE + 1]->teardown() -> 成功 + === STARTUP阶段开始 + [CPUHP_AP_ONLINE]->teardown() -> 成功 + ... + [CPUHP_BRINGUP_ONLINE - 1]->teardown() + ... + === PREPARE阶段开始 + [CPUHP_BRINGUP_CPU]->teardown() + [CPUHP_OFFLINE + 3]->teardown() + [CPUHP_OFFLINE + 2] -> 略过,因为teardown == NULL + [CPUHP_OFFLINE + 1]->teardown() + [CPUHP_OFFLINE] + +一个失败的上线操作如下:: + + [CPUHP_OFFLINE] + [CPUHP_OFFLINE + 1]->startup() -> 成功 + [CPUHP_OFFLINE + 2]->startup() -> 成功 + [CPUHP_OFFLINE + 3] -> 略过,因为startup == NULL + ... + [CPUHP_BRINGUP_CPU]->startup() -> 成功 + === PREPARE阶段结束 + [CPUHP_BRINGUP_CPU + 1]->startup() -> 成功 + ... + [CPUHP_AP_ONLINE]->startup() -> 成功 + === STARTUP阶段结束 + [CPUHP_AP_ONLINE + 1]->startup() -> 成功 + --- + [CPUHP_AP_ONLINE + N]->startup() -> 失败 + [CPUHP_AP_ONLINE + (N - 1)]->teardown() + ... + [CPUHP_AP_ONLINE + 1]->teardown() + === STARTUP阶段开始 + [CPUHP_AP_ONLINE]->teardown() + ... + [CPUHP_BRINGUP_ONLINE - 1]->teardown() + ... + === PREPARE阶段开始 + [CPUHP_BRINGUP_CPU]->teardown() + [CPUHP_OFFLINE + 3]->teardown() + [CPUHP_OFFLINE + 2] -> 略过,因为teardown == NULL + [CPUHP_OFFLINE + 1]->teardown() + [CPUHP_OFFLINE] + +一个失败的下线操作如下:: + + [CPUHP_ONLINE] + [CPUHP_ONLINE - 1]->teardown() -> 成功 + ... + [CPUHP_ONLINE - N]->teardown() -> 失败 + [CPUHP_ONLINE - (N - 1)]->startup() + ... + [CPUHP_ONLINE - 1]->startup() + [CPUHP_ONLINE] + +递归失败不能被合理地处理。 +请看下面的例子,由于下线操作失败而导致的递归失败:: + + [CPUHP_ONLINE] + [CPUHP_ONLINE - 1]->teardown() -> 成功 + ... + [CPUHP_ONLINE - N]->teardown() -> 失败 + [CPUHP_ONLINE - (N - 1)]->startup() -> 成功 + [CPUHP_ONLINE - (N - 2)]->startup() -> 失败 + +CPU热插拔状态机在此停止,且不再尝试回滚,因为这可能会导致死循环:: + + [CPUHP_ONLINE - (N - 1)]->teardown() -> 成功 + [CPUHP_ONLINE - N]->teardown() -> 失败 + [CPUHP_ONLINE - (N - 1)]->startup() -> 成功 + [CPUHP_ONLINE - (N - 2)]->startup() -> 失败 + [CPUHP_ONLINE - (N - 1)]->teardown() -> 成功 + [CPUHP_ONLINE - N]->teardown() -> 失败 + +周而复始,不断重复。在这种情况下,CPU留在该状态中:: + + [CPUHP_ONLINE - (N - 1)] + +这至少可以让系统取得进展,让用户有机会进行调试,甚至解决这个问题。 + +分配一个状态 +------------ + +有两种方式分配一个CPU热插拔状态: + +* 静态分配 + + 当子系统或驱动程序有相对于其他CPU热插拔状态的排序要求时,必须使用静态分配。例如, + 在CPU上线操作期间,PERF核心startup回调必须在PERF驱动startup回调之前被调用。在CPU + 下线操作中,驱动teardown回调必须在核心teardown回调之前调用。静态分配的状态由 + cpuhp_state枚举中的常量描述,可以在include/linux/cpuhotplug.h中找到。 + + 在适当的位置将状态插入枚举中,这样就满足了排序要求。状态常量必须被用于状态的设置 + 和移除。 + + 当状态回调不是在运行时设置的,并且是kernel/cpu.c中CPU热插拔状态数组初始化的一部分 + 时,也需要静态分配。 + +* 动态分配 + + 当对状态回调没有排序要求时,动态分配是首选方法。状态编号由setup函数分配,并在成功 + 后返回给调用者。 + + 只有PREPARE和ONLINE阶段提供了一个动态分配范围。STARTING阶段则没有,因为该部分的大多 + 数回调都有明确的排序要求。 + +CPU热插拔状态的设置 +------------------- + +核心代码提供了以下函数用来设置状态: + +* cpuhp_setup_state(state, name, startup, teardown) +* cpuhp_setup_state_nocalls(state, name, startup, teardown) +* cpuhp_setup_state_cpuslocked(state, name, startup, teardown) +* cpuhp_setup_state_nocalls_cpuslocked(state, name, startup, teardown) + +对于一个驱动程序或子系统有多个实例,并且每个实例都需要调用相同的CPU hotplug状态回 +调的情况,CPU hotplug核心提供多实例支持。与驱动程序特定的实例列表相比,其优势在于 +与实例相关的函数完全针对CPU hotplug操作进行序列化,并在添加和删除时提供状态回调的 +自动调用。要设置这样一个多实例状态,可以使用以下函数: + +* cpuhp_setup_state_multi(state, name, startup, teardown) + +@state参数要么是静态分配的状态,要么是动态分配状态(PUHP_PREPARE_DYN,CPUHP_ONLINE_DYN) +的常量之一, 具体取决于应该分配动态状态的状态阶段(PREPARE,ONLINE)。 + +@name参数用于sysfs输出和检测。命名惯例是"subsys:mode"或"subsys/driver:mode", +例如 "perf:mode"或"perf/x86:mode"。常见的mode名称有: + +======== ============================================ +prepare 对应PREPARE阶段中的状态 + +dead 对应PREPARE阶段中不提供startup回调的状态 + +starting 对应STARTING阶段中的状态 + +dying 对应STARTING阶段中不提供startup回调的状态 + +online 对应ONLINE阶段中的状态 + +offline 对应ONLINE阶段中不提供startup回调的状态 +======== ============================================ + +由于@name参数只用于sysfs和检测,如果其他mode描述符比常见的描述符更好地描述状态的性质, +也可以使用。 + +@name参数的示例:"perf/online", "perf/x86:prepare", "RCU/tree:dying", "sched/waitempty" + +@startup参数是一个指向回调的函数指针,在CPU上线操作时被调用。若应用不需要startup +回调,则将该指针设为NULL。 + +@teardown参数是一个指向回调的函数指针,在CPU下线操作时调用。若应用不需要teardown +回调,则将该指针设为NULL。 + +这些函数在处理已注册回调的方式上有所不同: + + * cpuhp_setup_state_nocalls(), cpuhp_setup_state_nocalls_cpuslocked()和 + cpuhp_setup_state_multi()只注册回调。 + + * cpuhp_setup_state()和cpuhp_setup_state_cpuslocked()注册回调,并对当前状态大于新 + 安装状态的所有在线CPU调用@startup回调(如果不是NULL)。根据状态阶段,回调要么在 + 当前的CPU上调用(PREPARE阶段),要么在CPU的热插拔线程中调用每个在线CPU(ONLINE阶段)。 + + 如果CPU N的回调失败,那么CPU 0...N-1的teardown回调被调用以回滚操作。状态设置失败, + 状态的回调没有被注册,在动态分配的情况下,分配的状态被释放。 + +状态设置和回调调用是针对CPU热拔插操作进行序列化的。如果设置函数必须从CPU热插拔的读 +锁定区域调用,那么必须使用_cpuslocked()变体。这些函数不能在CPU热拔插回调中使用。 + +函数返回值: + ======== ========================================================== + 0 静态分配的状态设置成功 + + >0 动态分配的状态设置成功 + + 返回的数值是被分配的状态编号。如果状态回调后来必须被移除, + 例如模块移除,那么这个数值必须由调用者保存,并作为状态移 + 除函数的@state参数。对于多实例状态,动态分配的状态编号也 + 需要作为实例添加/删除操作的@state参数。 + + <0 操作失败 + ======== ========================================================== + +移除CPU热拔插状态 +----------------- + +为了移除一个之前设置好的状态,提供了如下函数: + +* cpuhp_remove_state(state) +* cpuhp_remove_state_nocalls(state) +* cpuhp_remove_state_nocalls_cpuslocked(state) +* cpuhp_remove_multi_state(state) + +@state参数要么是静态分配的状态,要么是由cpuhp_setup_state*()在动态范围内分配 +的状态编号。如果状态在动态范围内,则状态编号被释放,可再次进行动态分配。 + +这些函数在处理已注册回调的方式上有所不同: + + * cpuhp_remove_state_nocalls(), cpuhp_remove_state_nocalls_cpuslocked() + 和 cpuhp_remove_multi_state()只删除回调。 + + * cpuhp_remove_state()删除回调,并调用所有当前状态大于被删除状态的在线CPU的 + teardown回调(如果不是NULL)。根据状态阶段,回调要么在当前的CPU上调用 + (PREPARE阶段),要么在CPU的热插拔线程中调用每个在线CPU(ONLINE阶段)。 + + 为了完成移除工作,teardown回调不能失败。 + +状态移除和回调调用是针对CPU热拔插操作进行序列化的。如果移除函数必须从CPU hotplug +读取锁定区域调用,那么必须使用_cpuslocked()变体。这些函数不能从CPU热插拔的回调中使用。 + +如果一个多实例的状态被移除,那么调用者必须先移除所有的实例。 + +多实例状态实例管理 +------------------ + +一旦多实例状态被建立,实例就可以被添加到状态中: -一旦一个CPU下线或上线,就有可能收到通知。这对某些需要根据可用CPU数量执行某种设置或清 -理功能的驱动程序来说可能很重要:: + * cpuhp_state_add_instance(state, node) + * cpuhp_state_add_instance_nocalls(state, node) - #include <linux/cpuhotplug.h> +@state参数是一个静态分配的状态或由cpuhp_setup_state_multi()在动态范围内分配的状 +态编号。 - ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "X/Y:online", - Y_online, Y_prepare_down); +@node参数是一个指向hlist_node的指针,它被嵌入到实例的数据结构中。这个指针被交给 +多实例状态的回调,可以被回调用来通过container_of()检索到实例。 -*X* 是子系统, *Y* 是特定的驱动程序。 *Y_online* 回调将在所有在线CPU的注册过程中被调用。 -如果在线回调期间发生错误, *Y_prepare_down* 回调将在所有之前调用过在线回调的CPU上调 -用。注册完成后,一旦有CPU上线, *Y_online* 回调将被调用,当CPU关闭时, *Y_prepare_down* -将被调用。所有之前在 *Y_online* 中分配的资源都应该在 *Y_prepare_down* 中释放。如果在 -注册过程中发生错误,返回值 *ret* 为负值。否则会返回一个正值,其中包含动态分配状态 -( *CPUHP_AP_ONLINE_DYN* )的分配热拔插。对于预定义的状态,它将返回0。 +这些函数在处理已注册回调的方式上有所不同: -该回调可以通过调用 ``cpuhp_remove_state()`` 来删除。如果是动态分配的状态 -( *CPUHP_AP_ONLINE_DYN* ),则使用返回的状态。在移除热插拔状态的过程中,将调用拆解回调。 + * cpuhp_state_add_instance_nocalls()只将实例添加到多实例状态的节点列表中。 -多个实例 -~~~~~~~~ + * cpuhp_state_add_instance()为所有当前状态大于@state的在线CPU添加实例并调用与 + @state相关的startup回调(如果不是NULL)。该回调只对将要添加的实例进行调用。 + 根据状态阶段,回调要么在当前的CPU上调用(PREPARE阶段),要么在CPU的热插拔线 + 程中调用每个在线CPU(ONLINE阶段)。 -如果一个驱动程序有多个实例,并且每个实例都需要独立执行回调,那么很可能应该使用 -``multi-state`` 。首先需要注册一个多状态的状态:: + 如果CPU N的回调失败,那么CPU 0 ... N-1的teardown回调被调用以回滚操作,该函数 + 失败,实例不会被添加到多实例状态的节点列表中。 - ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "X/Y:online, - Y_online, Y_prepare_down); - Y_hp_online = ret; +要从状态的节点列表中删除一个实例,可以使用这些函数: -``cpuhp_setup_state_multi()`` 的行为与 ``cpuhp_setup_state()`` 类似,只是它 -为多状态准备了回调,但不调用回调。这是一个一次性的设置。 -一旦分配了一个新的实例,你需要注册这个新实例:: + * cpuhp_state_remove_instance(state, node) + * cpuhp_state_remove_instance_nocalls(state, node) - ret = cpuhp_state_add_instance(Y_hp_online, &d->node); +参数与上述cpuhp_state_add_instance*()变体相同。 -这个函数将把这个实例添加到你先前分配的 ``Y_hp_online`` 状态,并在所有在线的 -CPU上调用先前注册的回调( ``Y_online`` )。 *node* 元素是你的每个实例数据结构 -中的一个 ``struct hlist_node`` 成员。 +这些函数在处理已注册回调的方式上有所不同: -在移除该实例时:: + * cpuhp_state_remove_instance_nocalls()只从状态的节点列表中删除实例。 - cpuhp_state_remove_instance(Y_hp_online, &d->node) + * cpuhp_state_remove_instance()删除实例并调用与@state相关的回调(如果不是NULL), + 用于所有当前状态大于@state的在线CPU。 该回调只对将要被移除的实例进行调用。 + 根据状态阶段,回调要么在当前的CPU上调用(PREPARE阶段),要么在CPU的热插拔 + 线程中调用每个在线CPU(ONLINE阶段)。 -应该被调用,这将在所有在线CPU上调用拆分回调。 + 为了完成移除工作,teardown回调不能失败。 -手动设置 -~~~~~~~~ +节点列表的添加/删除操作和回调调用是针对CPU热拔插操作进行序列化。这些函数不能在 +CPU hotplug回调和CPU hotplug读取锁定区域内使用。 -通常情况下,在注册或移除状态时调用setup和teamdown回调是很方便的,因为通常在CPU上线 -(下线)和驱动的初始设置(关闭)时需要执行该操作。然而,每个注册和删除功能也有一个 -_nocalls的后缀,如果不希望调用回调,则不调用所提供的回调。在手动设置(或关闭)期间, -应该使用 ``get_online_cpus()`` 和 ``put_online_cpus()`` 函数来抑制CPU热插拔操作。 +样例 +---- +在STARTING阶段设置和取消静态分配的状态,以获取上线和下线操作的通知:: -事件的顺序 ----------- + ret = cpuhp_setup_state(CPUHP_SUBSYS_STARTING, "subsys:starting", subsys_cpu_starting, subsys_cpu_dying); + if (ret < 0) + return ret; + .... + cpuhp_remove_state(CPUHP_SUBSYS_STARTING); -热插拔状态被定义在 ``include/linux/cpuhotplug.h``: +在ONLINE阶段设置和取消动态分配的状态,以获取下线操作的通知:: -* ``CPUHP_OFFLINE`` ... ``CPUHP_AP_OFFLINE`` 状态是在CPU启动前调用的。 + state = cpuhp_setup_state(CPUHP_ONLINE_DYN, "subsys:offline", NULL, subsys_cpu_offline); + if (state < 0) + return state; + .... + cpuhp_remove_state(state); -* ``CPUHP_AP_OFFLINE`` ... ``CPUHP_AP_ONLINE`` 状态是在CPU被启动后被调用的。 - 中断是关闭的,调度程序还没有在这个CPU上活动。从 ``CPUHP_AP_OFFLINE`` 开始, - 回调被调用到目标CPU上。 +在ONLINE阶段设置和取消动态分配的状态,以获取有关上线操作的通知,而无需调用回调:: -* ``CPUHP_AP_ONLINE_DYN`` 和 ``CPUHP_AP_ONLINE_DYN_END`` 之间的状态被保留 - 给动态分配。 + state = cpuhp_setup_state_nocalls(CPUHP_ONLINE_DYN, "subsys:online", subsys_cpu_online, NULL); + if (state < 0) + return state; + .... + cpuhp_remove_state_nocalls(state); -* 这些状态在CPU关闭时以相反的顺序调用,从 ``CPUHP_ONLINE`` 开始,在 ``CPUHP_OFFLINE`` - 停止。这里的回调是在将被关闭的CPU上调用的,直到 ``CPUHP_AP_OFFLINE`` 。 +在ONLINE阶段设置、使用和取消动态分配的多实例状态,以获得上线和下线操作的通知:: -通过 ``CPUHP_AP_ONLINE_DYN`` 动态分配的状态通常已经足够了。然而,如果在启动或关闭 -期间需要更早的调用,那么应该获得一个显式状态。如果热拔插事件需要相对于另一个热拔插事 -件的特定排序,也可能需要一个显式状态。 + state = cpuhp_setup_state_multi(CPUHP_ONLINE_DYN, "subsys:online", subsys_cpu_online, subsys_cpu_offline); + if (state < 0) + return state; + .... + ret = cpuhp_state_add_instance(state, &inst1->node); + if (ret) + return ret; + .... + ret = cpuhp_state_add_instance(state, &inst2->node); + if (ret) + return ret; + .... + cpuhp_remove_instance(state, &inst1->node); + .... + cpuhp_remove_instance(state, &inst2->node); + .... + remove_multi_state(state); 测试热拔插状态 ============== diff --git a/Documentation/translations/zh_CN/core-api/index.rst b/Documentation/translations/zh_CN/core-api/index.rst index 26d9913fc8b6..7ca44629860c 100644 --- a/Documentation/translations/zh_CN/core-api/index.rst +++ b/Documentation/translations/zh_CN/core-api/index.rst @@ -28,6 +28,7 @@ printk-basics printk-formats workqueue + watch_queue symbol-namespaces 数据结构和低级实用程序 diff --git a/Documentation/translations/zh_CN/core-api/irq/irq-domain.rst b/Documentation/translations/zh_CN/core-api/irq/irq-domain.rst index 7d077742f758..9174fce12c1b 100644 --- a/Documentation/translations/zh_CN/core-api/irq/irq-domain.rst +++ b/Documentation/translations/zh_CN/core-api/irq/irq-domain.rst @@ -5,6 +5,7 @@ :翻译: 司延腾 Yanteng Si <siyanteng@loongson.cn> + 周彬彬 Binbin Zhou <zhoubinbin@loongson.cn> .. _cn_irq-domain.rst: @@ -52,8 +53,18 @@ irq_domain和一个hwirq号作为参数。 如果hwirq的映射还不存在, 一个新的Linux irq_desc,将其与hwirq关联起来,并调用.map()回调,这样驱动 程序就可以执行任何必要的硬件设置。 -当接收到一个中断时,应该使用irq_find_mapping()函数从hwirq号中找到 -Linux IRQ号。 +一旦建立了映射,可以通过多种方法检索或使用它: + +- irq_resolve_mapping()返回一个指向给定域和hwirq号的irq_desc结构指针, + 如果没有映射则返回NULL。 + +- irq_find_mapping()返回给定域和hwirq的Linux IRQ号,如果没有映射则返回0。 + +- irq_linear_revmap()现与irq_find_mapping()相同,已被废弃。 + +- generic_handle_domain_irq()处理一个由域和hwirq号描述的中断。 + +请注意,irq域的查找必须发生在与RCU读临界区兼容的上下文中。 在调用irq_find_mapping()之前,至少要调用一次irq_create_mapping()函数, 以免描述符不能被分配。 @@ -119,7 +130,8 @@ irq_domain_add_tree()和irq_domain_create_tree()在功能上是等价的,除 Linux IRQ号编入硬件本身,这样就不需要映射了。 调用irq_create_direct_mapping() 会分配一个Linux IRQ号,并调用.map()回调,这样驱动就可以将Linux IRQ号编入硬件中。 -大多数驱动程序不能使用这个映射。 +大多数驱动程序无法使用此映射,现在它由CONFIG_IRQ_DOMAIN_NOMAP选项控制。 +请不要引入此API的新用户。 传统映射类型 ------------ @@ -128,7 +140,6 @@ Linux IRQ号编入硬件本身,这样就不需要映射了。 调用irq_create irq_domain_add_simple() irq_domain_add_legacy() - irq_domain_add_legacy_isa() irq_domain_create_simple() irq_domain_create_legacy() @@ -137,6 +148,9 @@ Linux IRQ号编入硬件本身,这样就不需要映射了。 调用irq_create 一组用于IRQ号的定义(#define),这些定义被传递给struct设备注册。 在这种情况下, 不能动态分配Linux IRQ号,应该使用传统映射。 +顾名思义,\*_legacy()系列函数已被废弃,只是为了方便对古老平台的支持而存在。 +不应该增加新的用户。当\*_simple()系列函数的使用导致遗留行为时,他们也是如此。 + 传统映射假设已经为控制器分配了一个连续的IRQ号范围,并且可以通过向hwirq号添加一 个固定的偏移来计算IRQ号,反之亦然。 缺点是需要中断控制器管理IRQ分配,并且需要为每 个hwirq分配一个irq_desc,即使它没有被使用。 diff --git a/Documentation/translations/zh_CN/core-api/kernel-api.rst b/Documentation/translations/zh_CN/core-api/kernel-api.rst index 962d31d019d7..c22662679065 100644 --- a/Documentation/translations/zh_CN/core-api/kernel-api.rst +++ b/Documentation/translations/zh_CN/core-api/kernel-api.rst @@ -5,6 +5,7 @@ :翻译: 司延腾 Yanteng Si <siyanteng@loongson.cn> + 周彬彬 Binbin Zhou <zhoubinbin@loongson.cn> .. _cn_kernel-api.rst: @@ -282,6 +283,8 @@ kernel/acct.c 该API在以下内核代码中: +include/linux/bio.h + block/blk-core.c block/blk-core.c diff --git a/Documentation/translations/zh_CN/core-api/mm-api.rst b/Documentation/translations/zh_CN/core-api/mm-api.rst index 0ea43dc67953..a732b0eebf16 100644 --- a/Documentation/translations/zh_CN/core-api/mm-api.rst +++ b/Documentation/translations/zh_CN/core-api/mm-api.rst @@ -5,6 +5,7 @@ :翻译: 司延腾 Yanteng Si <siyanteng@loongson.cn> + 周彬彬 Binbin Zhou <zhoubinbin@loongson.cn> :校译: @@ -66,12 +67,24 @@ mm/vmalloc.c 该API在以下内核代码中: -mm/readahead.c +文件映射 +-------- mm/filemap.c +预读 +---- + +mm/readahead.c + +回写 +---- + mm/page-writeback.c +截断 +---- + mm/truncate.c include/linux/pagemap.h @@ -105,6 +118,14 @@ mm/mempolicy.c include/linux/mm_types.h +include/linux/mm_inline.h + +include/linux/page-flags.h + include/linux/mm.h +include/linux/page_ref.h + include/linux/mmzone.h + +mm/util.c diff --git a/Documentation/translations/zh_CN/core-api/printk-basics.rst b/Documentation/translations/zh_CN/core-api/printk-basics.rst index d574de3167c8..59c6efb3fc41 100644 --- a/Documentation/translations/zh_CN/core-api/printk-basics.rst +++ b/Documentation/translations/zh_CN/core-api/printk-basics.rst @@ -6,6 +6,7 @@ :翻译: 司延腾 Yanteng Si <siyanteng@loongson.cn> + 周彬彬 Binbin Zhou <zhoubinbin@loongson.cn> .. _cn_printk-basics.rst: @@ -107,6 +108,4 @@ pr_debug()和pr_devel(),除非定义了 ``DEBUG`` (或者在pr_debug()的情 该API在以下内核代码中: -kernel/printk/printk.c - include/linux/printk.h diff --git a/Documentation/translations/zh_CN/core-api/printk-formats.rst b/Documentation/translations/zh_CN/core-api/printk-formats.rst index ce39c788cf5a..bd36d35eba4e 100644 --- a/Documentation/translations/zh_CN/core-api/printk-formats.rst +++ b/Documentation/translations/zh_CN/core-api/printk-formats.rst @@ -5,6 +5,7 @@ :翻译: 司延腾 Yanteng Si <siyanteng@loongson.cn> + 周彬彬 Binbin Zhou <zhoubinbin@loongson.cn> .. _cn_printk-formats.rst: @@ -548,7 +549,7 @@ nodemask_pr_args()来方便打印cpumask和nodemask。 :: - %pGp referenced|uptodate|lru|active|private|node=0|zone=2|lastcpupid=0x1fffff + %pGp 0x17ffffc0002036(referenced|uptodate|lru|active|private|node=0|zone=2|lastcpupid=0x1fffff) %pGg GFP_USER|GFP_DMA32|GFP_NOWARN %pGv read|exec|mayread|maywrite|mayexec|denywrite diff --git a/Documentation/translations/zh_CN/core-api/watch_queue.rst b/Documentation/translations/zh_CN/core-api/watch_queue.rst new file mode 100644 index 000000000000..23b17ae2e4e2 --- /dev/null +++ b/Documentation/translations/zh_CN/core-api/watch_queue.rst @@ -0,0 +1,313 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/core-api/watch_queue.rst + +:翻译: + +周彬彬 Binbin Zhou <zhoubinbin@loongson.cn> + +:校译: + +司延腾 Yanteng Si <siyanteng@loongson.cn> +吴想成 Wu Xiangcheng <bobwxc@email.cn> + + +============ +通用通知机制 +============ + +通用通知机制是建立在标准管道驱动之上的,它可以有效地将来自内核的通知消息拼接到用 +户空间打开的管道中。这可以与以下方面结合使用:: + + * Key/keyring 通知 + +通知缓冲区可以通过以下方式启用: + + “General setup”/“General notification queue” + (CONFIG_WATCH_QUEUE) + +文档包含以下章节: + +.. contents:: :local: + + +概述 +==== + +该设施以一种特殊模式打开的管道形式出现,管道的内部环形缓冲区用于保存内核生成的消 +息。然后通过read()读出这些消息。在此类管道上禁用拼接以及类似的操作,因为它们希望 +在某些情况下将其添加的内容还原到环中-这可能最终会与通知消息重叠。 + +管道的所有者必须告诉内核它想通过该管道观察哪些源。只有连接到该管道上的源才会将消 +息插入其中。请注意,一个源可能绑定到多个管道,并同时将消息插入到所有管道中。 + +还可以将过滤器放置在管道上,以便在不感兴趣时可以忽略某些源类型和子事件。 + +如果环中没有可用的插槽,或者没有预分配的消息缓冲区可用,则将丢弃消息。在这两种情 +况下,read()都会在读取缓冲区中当前的最后一条消息后,将WATCH_META_LOSS_NOTIFICATION +插入到输出缓冲区中。 + +请注意,当生成一个通知时,内核不会等待消费者收集它,而是继续执行。这意味着可以在 +持有自旋锁的同时生成通知,并且还可以保护内核不被用户空间故障无限期地阻碍。 + + +消息结构 +======== + +通知消息由一个简短的头部开始:: + + struct watch_notification { + __u32 type:24; + __u32 subtype:8; + __u32 info; + }; + +“type”表示通知记录的来源,“subtype”表示该来源的记录类型(见下文观测源章节)。该类 +型也可以是“WATCH_TYPE_META”。这是一个由观测队列本身在内部生成的特殊记录类型。有两 +个子类型: + + * WATCH_META_REMOVAL_NOTIFICATION + * WATCH_META_LOSS_NOTIFICATION + +第一个表示安装了观察的对象已被删除或销毁,第二个表示某些消息已丢失。 + +“info”表示一系列东西,包括: + + * 消息的长度,以字节为单位,包括头(带有WATCH_INFO_LENGTH的掩码,并按 + WATCH_INFO_LENGTH__SHIFT移位)。这表示记录的大小,可能在8到127字节之间。 + + * 观测ID(带有WATCH_INFO_ID掩码,并按WATCH_INFO_ID__SHIFT移位)。这表示观测的主 + 叫ID,可能在0到255之间。多个观测组可以共享一个队列,这提供了一种区分它们的方法。 + + * 特定类型的字段(WATCH_INFO_TYPE_INFO)。这是由通知生产者设置的,以指示类型和 + 子类型的某些特定含义。 + +除长度外,信息中的所有内容都可以用于过滤。 + +头部后面可以有补充信息。此格式是由类型和子类型决定的。 + + +观测列表(通知源)API +===================== + +“观测列表“是订阅通知源的观测者的列表。列表可以附加到对象(比如键或超级块),也可 +以是全局的(比如对于设备事件)。从用户空间的角度来看,一个非全局的观测列表通常是 +通过引用它所属的对象来引用的(比如使用KEYCTL_NOTIFY并给它一个密钥序列号来观测特定 +的密钥)。 + +为了管理观测列表,提供了以下函数: + + * :: + + void init_watch_list(struct watch_list *wlist, + void (*release_watch)(struct watch *wlist)); + + 初始化一个观测列表。 如果 ``release_watch`` 不是NULL,那么这表示当watch_list对 + 象被销毁时,应该调用函数来丢弃观测列表对被观测对象的任何引用。 + + * ``void remove_watch_list(struct watch_list *wlist);`` + + 这将删除订阅watch_list的所有观测,并释放它们,然后销毁watch_list对象本身。 + + +观测队列(通知输出)API +======================= + +“观测队列”是由应用程序分配的用以记录通知的缓冲区,其工作原理完全隐藏在管道设备驱 +动中,但必须获得对它的引用才能设置观测。可以通过以下方式进行管理: + + * ``struct watch_queue *get_watch_queue(int fd);`` + + 由于观测队列在内核中通过实现缓冲区的管道的文件描述符表示,用户空间必须通过系 + 统调用传递该文件描述符,这可以用于从系统调用中查找指向观测队列的不透明指针。 + + * ``void put_watch_queue(struct watch_queue *wqueue);`` + + 该函数用以丢弃从 ``get_watch_queue()`` 获得的引用。 + + +观测订阅API +=========== + +“观测”是观测列表上的订阅,表示观测队列,从而表示应写入通知记录的缓冲区。观测队列 +对象还可以携带该对象的过滤规则,由用户空间设置。watch结构体的某些部分可以由驱动程 +序设置:: + + struct watch { + union { + u32 info_id; /* 在info字段中进行OR运算的ID */ + ... + }; + void *private; /* 被观测对象的私有数据 */ + u64 id; /* 内部标识符 */ + ... + }; + +``info_id`` 值是从用户空间获得并按WATCH_INFO_ID__SHIFT移位的8位数字。当通知写入关 +联的观测队列缓冲区时,这将与struct watch_notification::info的WATCH_INFO_ID字段进 +行或运算。 + +``private`` 字段是与watch_list相关联的驱动程序数据,并由 ``watch_list::release_watch()`` +函数清除。 + +``id`` 字段是源的ID。使用不同ID发布的通知将被忽略。 + +提供以下函数来管理观测: + + * ``void init_watch(struct watch *watch, struct watch_queue *wqueue);`` + + 初始化一个观测对象,把它的指针设置到观察队列中,使用适当的限制来避免死锁。 + + * ``int add_watch_to_object(struct watch *watch, struct watch_list *wlist);`` + + 将观测订阅到观测列表(通知源)。watch结构体中的driver-settable字段必须在调用 + 它之前设置。 + + * :: + + int remove_watch_from_object(struct watch_list *wlist, + struct watch_queue *wqueue, + u64 id, false); + + 从观测列表中删除一个观测,该观测必须与指定的观测队列(``wqueue``)和对象标识 + 符(``id``)匹配。通知(``WATCH_META_REMOVAL_NOTIFICATION``)被发送到观测队列 + 表示该观测已被删除。 + + * ``int remove_watch_from_object(struct watch_list *wlist, NULL, 0, true);`` + + 从观测列表中删除所有观测。预计这将被称为销毁前的准备工作,届时新的观测将无法 + 访问观测列表。通知(``WATCH_META_REMOVAL_NOTIFICATION``)被发送到每个订阅观测 + 的观测队列,以表明该观测已被删除。 + + +通知发布API +=========== + +要将通知发布到观测列表以便订阅的观测可以看到,应使用以下函数:: + + void post_watch_notification(struct watch_list *wlist, + struct watch_notification *n, + const struct cred *cred, + u64 id); + +应预先设置通知格式,并应传入一个指向头部(``n``)的指针。通知可能大于此值,并且缓 +冲槽为单位的大小在 ``n->info & WATCH_INFO_LENGTH`` 中注明。 + +``cred`` 结构体表示源(对象)的证书,并传递给LSM,例如SELinux,以允许或禁止根据该队 +列(对象)的证书在每个单独队列中记录注释。 + +``id`` 是源对象ID(如密钥上的序列号)。只有设置相同ID的观测才能看到这个通知。 + + +观测源 +====== + +任何特定的缓冲区都可以从多个源获取信息。 这些源包括: + + * WATCH_TYPE_KEY_NOTIFY + + 这种类型的通知表示密钥和密钥环的变化,包括密钥环内容或密钥属性的变化。 + + 更多信息请参见Documentation/security/keys/core.rst。 + + +事件过滤 +======== + +当创建观测队列后,我们可以应用一组过滤器以限制接收的事件:: + + struct watch_notification_filter filter = { + ... + }; + ioctl(fd, IOC_WATCH_QUEUE_SET_FILTER, &filter) + +过滤器的描述的类型变量是:: + + struct watch_notification_filter { + __u32 nr_filters; + __u32 __reserved; + struct watch_notification_type_filter filters[]; + }; + +其中“nr_filters”表示filters[]数组中过滤器的数量,而“__reserved”应为0。 +“filter”数组有以下类型的元素:: + + struct watch_notification_type_filter { + __u32 type; + __u32 info_filter; + __u32 info_mask; + __u32 subtype_filter[8]; + }; + +其中: + + * ``type`` 是过滤的事件类型,应类似于“WATCH_TYPE_KEY_NOTIFY”。 + + * ``info_filter`` 与 ``info_mask`` 充当通知记录的信息字段的过滤器,只有在以下情 + 况,通知才会写入缓冲区:: + + (watch.info & info_mask) == info_filter + + 例如,这可以用于忽略不在一个挂载树上的观测点的事件。 + + * ``subtype_filter`` 是一个位掩码,表示感兴趣的子类型。subtype_filter[0]的 + bit[0]对应子类型0,bit[1]对应子类型1,以此类推。 + +若ioctl()的参数为NULL,则过滤器将被移除,并且来自观测源的所有事件都将通过。 + + +用户空间代码示例 +================ + +缓冲区的创建如下所示:: + + pipe2(fds, O_TMPFILE); + ioctl(fds[1], IOC_WATCH_QUEUE_SET_SIZE, 256); + +它可以被设置成接收密钥环变化的通知:: + + keyctl(KEYCTL_WATCH_KEY, KEY_SPEC_SESSION_KEYRING, fds[1], 0x01); + +然后,这些通知可以被如下方式所使用:: + + static void consumer(int rfd, struct watch_queue_buffer *buf) + { + unsigned char buffer[128]; + ssize_t buf_len; + + while (buf_len = read(rfd, buffer, sizeof(buffer)), + buf_len > 0 + ) { + void *p = buffer; + void *end = buffer + buf_len; + while (p < end) { + union { + struct watch_notification n; + unsigned char buf1[128]; + } n; + size_t largest, len; + + largest = end - p; + if (largest > 128) + largest = 128; + memcpy(&n, p, largest); + + len = (n->info & WATCH_INFO_LENGTH) >> + WATCH_INFO_LENGTH__SHIFT; + if (len == 0 || len > largest) + return; + + switch (n.n.type) { + case WATCH_TYPE_META: + got_meta(&n.n); + case WATCH_TYPE_KEY_NOTIFY: + saw_key_change(&n.n); + break; + } + + p += len; + } + } + } diff --git a/Documentation/translations/zh_CN/core-api/workqueue.rst b/Documentation/translations/zh_CN/core-api/workqueue.rst index e372fa5cf101..f6567cf9d3fb 100644 --- a/Documentation/translations/zh_CN/core-api/workqueue.rst +++ b/Documentation/translations/zh_CN/core-api/workqueue.rst @@ -6,6 +6,7 @@ :翻译: 司延腾 Yanteng Si <siyanteng@loongson.cn> + 周彬彬 Binbin Zhou <zhoubinbin@loongson.cn> .. _cn_workqueue.rst: @@ -178,10 +179,6 @@ workqueue将自动创建与属性相匹配的后备工作者池。调节并发 这个标志对于未绑定的wq来说是没有意义的。 -请注意,标志 ``WQ_NON_REENTRANT`` 不再存在,因为现在所有的工作 -队列都是不可逆的——任何工作项都保证在任何时间内最多被整个系统的一 -个工作者执行。 - ``max_active`` -------------- @@ -328,6 +325,22 @@ And with cmwq with ``@max_active`` >= 3, :: 工作项函数在堆栈追踪中应该是微不足道的。 +不可重入条件 +============ + +工作队列保证,如果在工作项排队后满足以下条件,则工作项不能重入: + + + 1. 工作函数没有被改变。 + 2. 没有人将该工作项排到另一个工作队列中。 + 3. 该工作项尚未被重新启动。 + +换言之,如果上述条件成立,则保证在任何给定时间最多由一个系统范围内的工作程序执行 +该工作项。 + +请注意,在self函数中将工作项重新排队(到同一队列)不会破坏这些条件,因此可以安全 +地执行此操作。否则在破坏工作函数内部的条件时需要小心。 + 内核内联文档参考 ================ diff --git a/Documentation/translations/zh_CN/core-api/xarray.rst b/Documentation/translations/zh_CN/core-api/xarray.rst index ff2d9bcb7c34..fb19324966ce 100644 --- a/Documentation/translations/zh_CN/core-api/xarray.rst +++ b/Documentation/translations/zh_CN/core-api/xarray.rst @@ -6,6 +6,7 @@ :翻译: 司延腾 Yanteng Si <siyanteng@loongson.cn> + 周彬彬 Binbin Zhou <zhoubinbin@loongson.cn> :校译: @@ -254,7 +255,8 @@ __xa_set_mark() 和 __xa_clear_mark() 函数也适用于你查找一个条目并 高级API是基于xa_state的。这是一个不透明的数据结构,你使用XA_STATE()宏在堆栈中声明。这个宏初始化了 xa_state,准备开始在XArray上移动。它被用作一个游标来保持在XArray中的位置,并让你把各种操作组合在一 -起,而不必每次都从头开始。 +起,而不必每次都从头开始。xa_state的内容受rcu_read_lock()或xas_lock()的保护。如果需要删除保护状态 +和树的这些锁中的任何一个,你必须调用xas_pause()以便将来的调用不会依赖于状态中未受保护的部分。 xa_state也被用来存储错误(store errors)。你可以调用xas_error()来检索错误。所有的操作在进行之前都 会检查xa_state是否处于错误状态,所以你没有必要在每次调用之后检查错误;你可以连续进行多次调用,只在 |