.. SPDX-License-Identifier: GPL-2.0 .. include:: ../disclaimer-zh_CN.rst :Original: Documentation/core-api/padata.rst :翻译: å¸å»¶è…¾ Yanteng Si <siyanteng@loongson.cn> .. _cn_core_api_padata.rst: ================== padata并行执行机制 ================== :日期: 2020å¹´5月 Padata是一ç§æœºåˆ¶ï¼Œå†…æ ¸å¯ä»¥é€šè¿‡æ¤æœºåˆ¶å°†å·¥ä½œåˆ†æ•£åˆ°å¤šä¸ªCPU上并行完æˆï¼ŒåŒæ—¶ å¯ä»¥é€‰æ‹©ä¿æŒå®ƒä»¬çš„顺åºã€‚ 它最åˆæ˜¯ä¸ºIPsecå¼€å‘的,它需è¦åœ¨ä¸å¯¹è¿™äº›æ•°æ®åŒ…é‡æ–°æŽ’åºçš„å…¶å‰æ下,为大é‡çš„æ•° æ®åŒ…è¿›è¡ŒåŠ å¯†å’Œè§£å¯†ã€‚è¿™æ˜¯ç›®å‰padataçš„åºåˆ—化作业支æŒçš„唯一用途。 Padata还支æŒå¤šçº¿ç¨‹ä½œä¸šï¼Œå°†ä½œä¸šå¹³å‡åˆ†å‰²ï¼ŒåŒæ—¶åœ¨çº¿ç¨‹ä¹‹é—´è¿›è¡Œè´Ÿè½½å‡è¡¡å’Œå调。 执行åºåˆ—化作业 ============== åˆå§‹åŒ– ------ 使用padata执行åºåˆ—化作业的第一æ¥æ˜¯å»ºç«‹ä¸€ä¸ªpadata_instanceç»“æž„ä½“ï¼Œä»¥å…¨é¢ æŽ§åˆ¶ä½œä¸šçš„è¿è¡Œæ–¹å¼:: #include <linux/padata.h> struct padata_instance *padata_alloc(const char *name); 'name'å³æ ‡è¯†äº†è¿™ä¸ªå®žä¾‹ã€‚ 然åŽï¼Œé€šè¿‡åˆ†é…一个padata_shellæ¥å®Œæˆpadataçš„åˆå§‹åŒ–:: struct padata_shell *padata_alloc_shell(struct padata_instance *pinst); 一个padata_shell用于å‘padataæ交一个作业,并å…è®¸ä¸€ç³»åˆ—è¿™æ ·çš„ä½œä¸šè¢«ç‹¬ç«‹åœ° åºåˆ—化。一个padata_instanceå¯ä»¥æœ‰ä¸€ä¸ªæˆ–多个padata_shell与之相关è”,æ¯ä¸ª 都å…许一系列独立的作业。 修改cpumasks ------------ 用于è¿è¡Œä½œä¸šçš„CPUå¯ä»¥é€šè¿‡ä¸¤ç§æ–¹å¼æ”¹å˜ï¼Œé€šè¿‡padata_set_cpumask()编程或通 过sysfs。å‰è€…的定义是:: int padata_set_cpumask(struct padata_instance *pinst, int cpumask_type, cpumask_var_t cpumask); 这里cpumask_type是PADATA_CPU_PARALLEL(并行)或PADATA_CPU_SERIAL(串行)之一,其ä¸å¹¶ è¡Œcpumaskæ述了哪些处ç†å™¨å°†è¢«ç”¨æ¥å¹¶è¡Œæ‰§è¡Œæ交给这个实例的作业,串行cpumask 定义了哪些处ç†å™¨è¢«å…许用作串行化回调处ç†å™¨ã€‚ cpumask指定了è¦ä½¿ç”¨çš„æ–°cpumask。 一个实例的cpumaskså¯èƒ½æœ‰sysfs文件。例如,pcrypt的文件在 /sys/kernel/pcrypt/<instance-name>。在一个实例的目录ä¸ï¼Œæœ‰ä¸¤ä¸ªæ–‡ä»¶ï¼Œparallel_cpumask å’Œserial_cpumask,任何一个cpumask都å¯ä»¥é€šè¿‡åœ¨æ–‡ä»¶ä¸å›žæ˜¾ï¼ˆecho)一个bitmask æ¥æ”¹å˜ï¼Œæ¯”如说:: echo f > /sys/kernel/pcrypt/pencrypt/parallel_cpumask 读å–å…¶ä¸ä¸€ä¸ªæ–‡ä»¶ä¼šæ˜¾ç¤ºç”¨æˆ·æ供的cpumask,它å¯èƒ½ä¸Žâ€œå¯ç”¨â€çš„cpumaskä¸åŒã€‚ Padata内部维护ç€ä¸¤å¯¹cpumask,用户æ供的cpumask和“å¯ç”¨çš„â€cpumask(æ¯ä¸€å¯¹ç”±ä¸€ä¸ª 并行和一个串行cpumask组æˆ)。用户æ供的cpumasks在实例分é…时默认为所有å¯èƒ½çš„CPU, 并且å¯ä»¥å¦‚上所述进行更改。å¯ç”¨çš„cpumasks总是用户æ供的cpumasks的一个å集,åªåŒ… å«ç”¨æˆ·æ供的掩ç ä¸çš„在线CPU;这些是padata实际使用的cpumasksã€‚å› æ¤ï¼Œå‘padataæ 供一个包å«ç¦»çº¿CPUçš„cpumask是åˆæ³•çš„。一旦用户æ供的cpumaskä¸çš„一个离线CPU上线, padata就会使用它。 改å˜CPU掩ç çš„æ“作代价很高,所以ä¸åº”频ç¹æ›´æ”¹ã€‚ è¿è¡Œä¸€ä¸ªä½œä¸š ------------- 实际上å‘padata实例æ交工作需è¦åˆ›å»ºä¸€ä¸ªpadata_priv结构体,它代表一个作业:: struct padata_priv { /* Other stuff here... */ void (*parallel)(struct padata_priv *padata); void (*serial)(struct padata_priv *padata); }; è¿™ä¸ªç»“æž„ä½“å‡ ä¹Žè‚¯å®šä¼šè¢«åµŒå…¥åˆ°ä¸€äº›é’ˆå¯¹è¦åšçš„工作的大结构体ä¸ã€‚它的大部分å—段对 padataæ¥è¯´æ˜¯ç§æœ‰çš„,但是这个结构在åˆå§‹åŒ–时应该被清零,并且应该æä¾›parallel()å’Œ serial()函数。在完æˆå·¥ä½œçš„过程ä¸ï¼Œè¿™äº›å‡½æ•°å°†è¢«è°ƒç”¨ï¼Œæˆ‘们马上就会é‡åˆ°ã€‚ 工作的æ交是通过:: int padata_do_parallel(struct padata_shell *ps, struct padata_priv *padata, int *cb_cpu); pså’Œpadata结构体必须如上所述进行设置;cb_cpu指å‘作业完æˆåŽç”¨äºŽæœ€ç»ˆå›žè°ƒçš„首选CPUï¼› 它必须在当å‰å®žä¾‹çš„CPU掩ç ä¸ï¼ˆå¦‚æžœä¸æ˜¯ï¼Œcb_cpu指针将被更新为指å‘实际选择的CPU)。 padata_do_parallel()的返回值在æˆåŠŸæ—¶ä¸º0,表示工作æ£åœ¨è¿›è¡Œä¸ã€‚-EBUSYæ„味ç€æœ‰äºº 在其他地方æ£åœ¨æžä¹±å®žä¾‹çš„CPU掩ç ,而当cb_cpuä¸åœ¨ä¸²è¡Œcpumaskä¸ã€å¹¶è¡Œæˆ–串行cpumasks ä¸æ— 在线CPU,或实例åœæ¢æ—¶ï¼Œåˆ™ä¼šå‡ºçŽ°-EINVALå馈。 æ¯ä¸ªæ交给padata_do_parallel()的作业将ä¾æ¬¡ä¼ 递给一个CPU上的上述parallel()函数 的一个调用,所以真æ£çš„并行是通过æ交多个作业æ¥å®žçŽ°çš„。parallel()在è¿è¡Œæ—¶ç¦ç”¨è½¯ 件ä¸æ–ï¼Œå› æ¤ä¸èƒ½ç¡çœ 。parallel()函数把获得的padata_privç»“æž„ä½“æŒ‡é’ˆä½œä¸ºå…¶å”¯ä¸€çš„å‚ æ•°ï¼›å…³äºŽå®žé™…è¦åšçš„工作的信æ¯å¯èƒ½æ˜¯é€šè¿‡ä½¿ç”¨container_of()找到å°è£…结构体æ¥èŽ·å¾—的。 请注æ„,parallel()没有返回值;padataå系统å‡å®šparallel()将从æ¤æ—¶å¼€å§‹è´Ÿè´£è¿™é¡¹å·¥ 作。作业ä¸éœ€è¦åœ¨è¿™æ¬¡è°ƒç”¨ä¸å®Œæˆï¼Œä½†æ˜¯ï¼Œå¦‚æžœparallel()留下了未完æˆçš„工作,它应该准 备在å‰ä¸€ä¸ªä½œä¸šå®Œæˆä¹‹å‰ï¼Œè¢«ä»¥æ–°çš„作业å†æ¬¡è°ƒç”¨ åºåˆ—化作业 ---------- 当一个作业完æˆæ—¶ï¼Œparallel()(或任何实际完æˆè¯¥å·¥ä½œçš„函数)应该通过调用通知padataæ¤ äº‹:: void padata_do_serial(struct padata_priv *padata); 在未æ¥çš„æŸä¸ªæ—¶åˆ»ï¼Œpadata_do_serial()将触å‘对padata_priv结构体ä¸serial()函数的调 用。这个调用将å‘生在最åˆè¦æ±‚调用padata_do_parallel()çš„CPU上;它也是在本地软件ä¸æ– 被ç¦ç”¨çš„情况下è¿è¡Œçš„。 请注æ„,这个调用å¯èƒ½ä¼šè¢«æŽ¨è¿Ÿä¸€æ®µæ—¶é—´ï¼Œå› 为padata代ç 会努力确ä¿ä½œä¸šæŒ‰ç…§æ交的顺åºå®Œ æˆã€‚ é”€æ¯ ---- 清ç†ä¸€ä¸ªpadata实例时,å¯ä»¥é¢„è§çš„是调用两个free函数,这两个函数对应于分é…的逆过程:: void padata_free_shell(struct padata_shell *ps); void padata_free(struct padata_instance *pinst); 用户有责任确ä¿åœ¨è°ƒç”¨ä¸Šè¿°ä»»ä½•ä¸€é¡¹ä¹‹å‰ï¼Œæ‰€æœ‰æœªå®Œæˆçš„工作都已完æˆã€‚ è¿è¡Œå¤šçº¿ç¨‹ä½œä¸š ============== 一个多线程作业有一个主线程和零个或多个辅助线程,主线程å‚与作业,然åŽç‰å¾…所有辅助线 程完æˆã€‚padata将作业分割æˆç§°ä¸ºchunkçš„å•å…ƒï¼Œå…¶ä¸chunk是一个线程在一次调用线程函数 ä¸å®Œæˆçš„作业片段。 用户必须åšä¸‰ä»¶äº‹æ¥è¿è¡Œä¸€ä¸ªå¤šçº¿ç¨‹ä½œä¸šã€‚首先,通过定义一个padata_mt_job结构体æ¥æè¿° 作业,这在接å£éƒ¨åˆ†æœ‰è§£é‡Šã€‚这包括一个指å‘线程函数的指针,padataæ¯æ¬¡å°†ä½œä¸šå—分é…给线 程时都会调用这个函数。然åŽï¼Œå®šä¹‰çº¿ç¨‹å‡½æ•°ï¼Œå®ƒæŽ¥å—三个å‚数: ``start`` 〠``end`` å’Œ ``arg`` , å…¶ä¸å‰ä¸¤ä¸ªå‚æ•°é™å®šäº†çº¿ç¨‹æ“作的范围,最åŽä¸€ä¸ªæ˜¯æŒ‡å‘作业共享状æ€çš„指针,如果有的è¯ã€‚ 准备好共享状æ€ï¼Œå®ƒé€šå¸¸è¢«åˆ†é…åœ¨ä¸»çº¿ç¨‹çš„å †æ ˆä¸ã€‚最åŽï¼Œè°ƒç”¨padata_do_multithreaded(), 它将在作业完æˆåŽè¿”回。 æŽ¥å£ ==== 该APIåœ¨ä»¥ä¸‹å†…æ ¸ä»£ç ä¸: include/linux/padata.h kernel/padata.c