1cbae918bSYanteng Si.. SPDX-License-Identifier: GPL-2.0 2cbae918bSYanteng Si 3cbae918bSYanteng Si.. include:: ../disclaimer-zh_CN.rst 4cbae918bSYanteng Si 5cbae918bSYanteng Si:Original: Documentation/core-api/padata.rst 6*8137a49eSYanteng Si 7*8137a49eSYanteng Si:翻译: 8*8137a49eSYanteng Si 9*8137a49eSYanteng Si 司延腾 Yanteng Si <siyanteng@loongson.cn> 10cbae918bSYanteng Si 11cbae918bSYanteng Si.. _cn_core_api_padata.rst: 12cbae918bSYanteng Si 13cbae918bSYanteng Si================== 14cbae918bSYanteng Sipadata并行执行机制 15cbae918bSYanteng Si================== 16cbae918bSYanteng Si 17cbae918bSYanteng Si:日期: 2020年5月 18cbae918bSYanteng Si 19cbae918bSYanteng SiPadata是一种机制,内核可以通过此机制将工作分散到多个CPU上并行完成,同时 20cbae918bSYanteng Si可以选择保持它们的顺序。 21cbae918bSYanteng Si 22cbae918bSYanteng Si它最初是为IPsec开发的,它需要在不对这些数据包重新排序的其前提下,为大量的数 23cbae918bSYanteng Si据包进行加密和解密。这是目前padata的序列化作业支持的唯一用途。 24cbae918bSYanteng Si 25cbae918bSYanteng SiPadata还支持多线程作业,将作业平均分割,同时在线程之间进行负载均衡和协调。 26cbae918bSYanteng Si 27cbae918bSYanteng Si执行序列化作业 28cbae918bSYanteng Si============== 29cbae918bSYanteng Si 30cbae918bSYanteng Si初始化 31cbae918bSYanteng Si------ 32cbae918bSYanteng Si 33cbae918bSYanteng Si使用padata执行序列化作业的第一步是建立一个padata_instance结构体,以全面 34cbae918bSYanteng Si控制作业的运行方式:: 35cbae918bSYanteng Si 36cbae918bSYanteng Si #include <linux/padata.h> 37cbae918bSYanteng Si 38cbae918bSYanteng Si struct padata_instance *padata_alloc(const char *name); 39cbae918bSYanteng Si 40cbae918bSYanteng Si'name'即标识了这个实例。 41cbae918bSYanteng Si 42cbae918bSYanteng Si然后,通过分配一个padata_shell来完成padata的初始化:: 43cbae918bSYanteng Si 44cbae918bSYanteng Si struct padata_shell *padata_alloc_shell(struct padata_instance *pinst); 45cbae918bSYanteng Si 46cbae918bSYanteng Si一个padata_shell用于向padata提交一个作业,并允许一系列这样的作业被独立地 47cbae918bSYanteng Si序列化。一个padata_instance可以有一个或多个padata_shell与之相关联,每个 48cbae918bSYanteng Si都允许一系列独立的作业。 49cbae918bSYanteng Si 50cbae918bSYanteng Si修改cpumasks 51cbae918bSYanteng Si------------ 52cbae918bSYanteng Si 53cbae918bSYanteng Si用于运行作业的CPU可以通过两种方式改变,通过padata_set_cpumask()编程或通 54cbae918bSYanteng Si过sysfs。前者的定义是:: 55cbae918bSYanteng Si 56cbae918bSYanteng Si int padata_set_cpumask(struct padata_instance *pinst, int cpumask_type, 57cbae918bSYanteng Si cpumask_var_t cpumask); 58cbae918bSYanteng Si 59cbae918bSYanteng Si这里cpumask_type是PADATA_CPU_PARALLEL(并行)或PADATA_CPU_SERIAL(串行)之一,其中并 60cbae918bSYanteng Si行cpumask描述了哪些处理器将被用来并行执行提交给这个实例的作业,串行cpumask 61cbae918bSYanteng Si定义了哪些处理器被允许用作串行化回调处理器。 cpumask指定了要使用的新cpumask。 62cbae918bSYanteng Si 63cbae918bSYanteng Si一个实例的cpumasks可能有sysfs文件。例如,pcrypt的文件在 64cbae918bSYanteng Si/sys/kernel/pcrypt/<instance-name>。在一个实例的目录中,有两个文件,parallel_cpumask 65cbae918bSYanteng Si和serial_cpumask,任何一个cpumask都可以通过在文件中回显(echo)一个bitmask 66cbae918bSYanteng Si来改变,比如说:: 67cbae918bSYanteng Si 68cbae918bSYanteng Si echo f > /sys/kernel/pcrypt/pencrypt/parallel_cpumask 69cbae918bSYanteng Si 70cbae918bSYanteng Si读取其中一个文件会显示用户提供的cpumask,它可能与“可用”的cpumask不同。 71cbae918bSYanteng Si 72cbae918bSYanteng SiPadata内部维护着两对cpumask,用户提供的cpumask和“可用的”cpumask(每一对由一个 73cbae918bSYanteng Si并行和一个串行cpumask组成)。用户提供的cpumasks在实例分配时默认为所有可能的CPU, 74cbae918bSYanteng Si并且可以如上所述进行更改。可用的cpumasks总是用户提供的cpumasks的一个子集,只包 75cbae918bSYanteng Si含用户提供的掩码中的在线CPU;这些是padata实际使用的cpumasks。因此,向padata提 76cbae918bSYanteng Si供一个包含离线CPU的cpumask是合法的。一旦用户提供的cpumask中的一个离线CPU上线, 77cbae918bSYanteng Sipadata就会使用它。 78cbae918bSYanteng Si 79cbae918bSYanteng Si改变CPU掩码的操作代价很高,所以不应频繁更改。 80cbae918bSYanteng Si 81cbae918bSYanteng Si运行一个作业 82cbae918bSYanteng Si------------- 83cbae918bSYanteng Si 84cbae918bSYanteng Si实际上向padata实例提交工作需要创建一个padata_priv结构体,它代表一个作业:: 85cbae918bSYanteng Si 86cbae918bSYanteng Si struct padata_priv { 87cbae918bSYanteng Si /* Other stuff here... */ 88cbae918bSYanteng Si void (*parallel)(struct padata_priv *padata); 89cbae918bSYanteng Si void (*serial)(struct padata_priv *padata); 90cbae918bSYanteng Si }; 91cbae918bSYanteng Si 92cbae918bSYanteng Si这个结构体几乎肯定会被嵌入到一些针对要做的工作的大结构体中。它的大部分字段对 93cbae918bSYanteng Sipadata来说是私有的,但是这个结构在初始化时应该被清零,并且应该提供parallel()和 94cbae918bSYanteng Siserial()函数。在完成工作的过程中,这些函数将被调用,我们马上就会遇到。 95cbae918bSYanteng Si 96cbae918bSYanteng Si工作的提交是通过:: 97cbae918bSYanteng Si 98cbae918bSYanteng Si int padata_do_parallel(struct padata_shell *ps, 99cbae918bSYanteng Si struct padata_priv *padata, int *cb_cpu); 100cbae918bSYanteng Si 101cbae918bSYanteng Sips和padata结构体必须如上所述进行设置;cb_cpu指向作业完成后用于最终回调的首选CPU; 102cbae918bSYanteng Si它必须在当前实例的CPU掩码中(如果不是,cb_cpu指针将被更新为指向实际选择的CPU)。 103cbae918bSYanteng Sipadata_do_parallel()的返回值在成功时为0,表示工作正在进行中。-EBUSY意味着有人 104cbae918bSYanteng Si在其他地方正在搞乱实例的CPU掩码,而当cb_cpu不在串行cpumask中、并行或串行cpumasks 105cbae918bSYanteng Si中无在线CPU,或实例停止时,则会出现-EINVAL反馈。 106cbae918bSYanteng Si 107cbae918bSYanteng Si每个提交给padata_do_parallel()的作业将依次传递给一个CPU上的上述parallel()函数 108cbae918bSYanteng Si的一个调用,所以真正的并行是通过提交多个作业来实现的。parallel()在运行时禁用软 109cbae918bSYanteng Si件中断,因此不能睡眠。parallel()函数把获得的padata_priv结构体指针作为其唯一的参 110cbae918bSYanteng Si数;关于实际要做的工作的信息可能是通过使用container_of()找到封装结构体来获得的。 111cbae918bSYanteng Si 112cbae918bSYanteng Si请注意,parallel()没有返回值;padata子系统假定parallel()将从此时开始负责这项工 113cbae918bSYanteng Si作。作业不需要在这次调用中完成,但是,如果parallel()留下了未完成的工作,它应该准 114cbae918bSYanteng Si备在前一个作业完成之前,被以新的作业再次调用 115cbae918bSYanteng Si 116cbae918bSYanteng Si序列化作业 117cbae918bSYanteng Si---------- 118cbae918bSYanteng Si 119cbae918bSYanteng Si当一个作业完成时,parallel()(或任何实际完成该工作的函数)应该通过调用通知padata此 120cbae918bSYanteng Si事:: 121cbae918bSYanteng Si 122cbae918bSYanteng Si void padata_do_serial(struct padata_priv *padata); 123cbae918bSYanteng Si 124cbae918bSYanteng Si在未来的某个时刻,padata_do_serial()将触发对padata_priv结构体中serial()函数的调 125cbae918bSYanteng Si用。这个调用将发生在最初要求调用padata_do_parallel()的CPU上;它也是在本地软件中断 126cbae918bSYanteng Si被禁用的情况下运行的。 127cbae918bSYanteng Si请注意,这个调用可能会被推迟一段时间,因为padata代码会努力确保作业按照提交的顺序完 128cbae918bSYanteng Si成。 129cbae918bSYanteng Si 130cbae918bSYanteng Si销毁 131cbae918bSYanteng Si---- 132cbae918bSYanteng Si 133cbae918bSYanteng Si清理一个padata实例时,可以预见的是调用两个free函数,这两个函数对应于分配的逆过程:: 134cbae918bSYanteng Si 135cbae918bSYanteng Si void padata_free_shell(struct padata_shell *ps); 136cbae918bSYanteng Si void padata_free(struct padata_instance *pinst); 137cbae918bSYanteng Si 138cbae918bSYanteng Si用户有责任确保在调用上述任何一项之前,所有未完成的工作都已完成。 139cbae918bSYanteng Si 140cbae918bSYanteng Si运行多线程作业 141cbae918bSYanteng Si============== 142cbae918bSYanteng Si 143cbae918bSYanteng Si一个多线程作业有一个主线程和零个或多个辅助线程,主线程参与作业,然后等待所有辅助线 144cbae918bSYanteng Si程完成。padata将作业分割成称为chunk的单元,其中chunk是一个线程在一次调用线程函数 145cbae918bSYanteng Si中完成的作业片段。 146cbae918bSYanteng Si 147cbae918bSYanteng Si用户必须做三件事来运行一个多线程作业。首先,通过定义一个padata_mt_job结构体来描述 148cbae918bSYanteng Si作业,这在接口部分有解释。这包括一个指向线程函数的指针,padata每次将作业块分配给线 149cbae918bSYanteng Si程时都会调用这个函数。然后,定义线程函数,它接受三个参数: ``start`` 、 ``end`` 和 ``arg`` , 150cbae918bSYanteng Si其中前两个参数限定了线程操作的范围,最后一个是指向作业共享状态的指针,如果有的话。 151cbae918bSYanteng Si准备好共享状态,它通常被分配在主线程的堆栈中。最后,调用padata_do_multithreaded(), 152cbae918bSYanteng Si它将在作业完成后返回。 153cbae918bSYanteng Si 154cbae918bSYanteng Si接口 155cbae918bSYanteng Si==== 156cbae918bSYanteng Si 157cbae918bSYanteng Si该API在以下内核代码中: 158cbae918bSYanteng Si 159cbae918bSYanteng Siinclude/linux/padata.h 160cbae918bSYanteng Si 161cbae918bSYanteng Sikernel/padata.c 162