PREFACE
Linux密碼學算法可分成兩層
- User space layer
- Kernel space layer
- 在user space上想要使用密碼學算法,只要安裝并且執(zhí)行openssl這類套件即可.但是加解密的軟件運算在user space上實作耗時又費力,不太適用于嵌入式設備.所以我們可以透過kernel space/硬件的協(xié)助來對加解密的性能進行優(yōu)化,并降低CPU運算負擔.
此篇文章介紹Linux Kernel space密碼學算法實作流程為主,并沒有涵蓋user space中純應用程序的操作,但不會細談各個加解密算法,因為網絡已有太多精彩的內容了。
在Linux user space application上使用kernel space密碼學算法,詳細運作流程可分成下列三個主要步驟:
1. Kernel space (Kernel Space Cryptographic Implementation)
在Kernel space密碼學算法實作上,主要分成軟件以及硬件運算
- 軟件運算(Software calculation) 由CPU進行密碼學算法運算,不需額外硬件,但很耗費CPU性能.Linux Kernel原始碼位于crypto subsystem底下
- 硬件加速(Hardware component) 由硬件輔助進行密碼學算法運算(offloading),不需耗費CPU性能,但需要額外硬件.
SoC Component–許多ARM SoC廠商都會將硬件加解密元件放入SoC中,Linux Kernel原始碼多位于drivers/crypto底下.且設計必須遵照Linux crypto framework,不能私下修改。
TPM –專門針對保護密鑰與密碼運算而設計的一個高安全性的硬件安全芯片,Linux Kernel原始碼位于drivers/char/tpm底下。
另外像Intel有推出CPU instructions–Intel? AES NI [9].這或許也算硬件加速的一種.
2. Crypto API–User space interface
主要的功能是提供界面,讓user space可存取kernel space.目前主流為cryptodev以及af_alg
- CRYPTODEV [12] 不在Linux Kernel中,需要額外下載,編譯并掛載kernel module
- 使用ioctl界面 從OpenBSD Cryptographic Framework移值過來 OpenSSL早期即支持cryptodev
- AF_ALG Linux Kernel 2.6.38開始納入,原始碼位于crypto/af_alg.c
- 使用netlink界面 OpenSSL v1.1.0開始支持AF_ALG (note:除此之外,OpenSSL v1.1.0加入ChaCha20 & Poly1305加解密算法并且移除SSv2)
cryptodev官網上表示使用cryptodev性能較AF_ALG好,但根據(jù)[17]的實驗,性能其實差異不大.
個人認為新開發(fā)的程序可以考慮使用AF_ALG.畢竟AF_ALG在mainline Kernel中–穩(wěn)定性,兼容性以及維護性都會比較好.
3. User space密碼學函式庫(Cryptography libraries)[7]
以下為較常見的User space密碼學函式庫[19],
- OpenSSL
- wolfSSL
- GnuTLS
個人推薦OpenSSL.除了牌子老,使用者眾外.OpenSSL也被Linux Foundation下Core Infrastructure Initiative所資助。
OpenSSL提供AF_ALG以及cryptodev的engine,可透過engine來存取Crypto API.但這邊要注意的是,Debian中OpenSSL套件預設關閉AF_ALG以及cryptodev選項.所以直接執(zhí)行會使用user space的密碼學算法實作.若想要使用kernel space的密碼學算法實作,需下載原始碼下來設定并重新編譯.
- 開啟OpenSSL AF_ALG engine步驟
- 修改debian/rules,在CONFARGS最后面加入enable-afalgeng
- 開啟OpenSSL cryptodev engine步驟
- 1.下載cryptodev后,將crypto/cryptodev.h [21]復制一份到OpenSSL/crypto底下
- 2.修改debian/rules,在CONFARGS最前面加入-DHAVE_CRYPTODEV -DUSE_CRYPTDEV_DIGESTS 編譯完的OpenSSL即可存取Kernel space密碼學算法.
PART ONE--Crypto Subsystem of Linux Kernel
介紹由應用層所發(fā)出的crypto(cryptography)request,透過system call將request傳送到Linux kernel端,并經由crypto subsystem將request轉發(fā)給硬件算法引擎(hardware crypto engine)的流程。
概覽
Crypto subsystem是Linux系統(tǒng)中負責處理crypto request的子系統(tǒng),除了包含流程控制機制之外,另一個重要特色就是提供算法實作的抽象層,讓各家廠商能夠依據(jù)需求去客制化實作方式。
其中一個常見例子就是廠商 在硬件構架中加入用以加速特定算法運算效率的硬件算法引擎 ,并且透過crypto subsystem將驅動硬件算法引擎的流程整合進Linux系統(tǒng)中,供其他kernel module或是應用層使用。
一般芯片廠商都會這么玩,至少在我接觸的廠商中,因為集成使用openssl軟件庫去計算的時候會影響整個產品的性能,一般都會使用硬件來替換軟件實現(xiàn),之前我也發(fā)的關于硬件IP的就是用來做運算的IP。
以下以openSSL library如何將crypto request傳送到kernel crypto subsystem為例子:
image from: Linux Kernel
cryptodev Engine
在Linux系統(tǒng)中,想要實現(xiàn)應用層與硬件裝置的溝通,第一個想到的就是透過character/block device driver,讓應用程序開啟表示此硬件裝置的抽象層,并且藉由讀寫行為與硬件裝置進行互動。
而Cryptodev-linux就是負責此角色,它提供中間層的服務,接收由應用層傳送過來的crypto request,再呼叫Linux kernel crypto Subsystem的crypto API將request轉發(fā)給特定的硬件算法引擎。
Cryptodev-linux為miscellaneous device類型的kernel module,預設路徑是/dev/crypto,使用ioctl file operation cryptodev_ioctl來接受應用端所傳遞過來的數(shù)據(jù)。
1// https://github.com/cryptodev-linux/cryptodev-linux/blob/master/ioctl.c
2
3static const struct file_operations cryptodev_fops = {
4 .owner = THIS_MODULE,
5 .open = cryptodev_open,
6 .release = cryptodev_release,
7 .unlocked_ioctl = cryptodev_ioctl,
8#ifdef CONFIG_COMPAT
9 .compat_ioctl = cryptodev_compat_ioctl,
10#endif /* CONFIG_COMPAT */
11 .poll = cryptodev_poll,
12};
13
14static struct miscdevice cryptodev = {
15 .minor = MISC_DYNAMIC_MINOR,
16 .name = "crypto",
17 .fops = &cryptodev_fops,
18 .mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH,
19};
20
21static int __init
22cryptodev_register(void)
23{
24 int rc;
25
26 rc = misc_register(&cryptodev);
27 if (unlikely(rc)) {
28 pr_err(PFX "registration of /dev/crypto failedn");
29 return rc;
30 }
31
32 return 0;
33}
應用端則是使用cryptodev.h定義好的struct crypt_op或是struct crypt_auth_op來組成指定crypto request,并呼叫ioctl system call將request送給Cryptodev-linux。
1// https://github.com/cryptodev-linux/cryptodev-linux/blob/master/crypto/cryptodev.h
2
3struct crypt_auth_op {
4 __u32 ses; /* session identifier */
5 __u16 op; /* COP_ENCRYPT or COP_DECRYPT */
6 __u16 flags; /* see COP_FLAG_AEAD_* */
7 __u32 len; /* length of source data */
8 __u32 auth_len; /* length of auth data */
9 __u8 __user *auth_src; /* authenticated-only data */
10
11 /* The current implementation is more efficient if data are
12 * encrypted in-place (src==dst). */
13 __u8 __user *src; /* data to be encrypted and authenticated */
14 __u8 __user *dst; /* pointer to output data. Must have
15 * space for tag. For TLS this should be at least
16 * len + tag_size + block_size for padding */
17
18 __u8 __user *tag; /* where the tag will be copied to. TLS mode
19 * doesn't use that as tag is copied to dst.
20 * SRTP mode copies tag there. */
21 __u32 tag_len; /* the length of the tag. Use zero for digest size or max tag. */
22
23 /* initialization vector for encryption operations */
24 __u8 __user *iv;
25 __u32 iv_len;
26};
Sample code for Cryptodev-linux ioctl:
1// setup data for your crypto request
2 cryp.ses = ctx- >sess.ses;
3 cryp.iv = (void*)iv;
4 cryp.op = COP_DECRYPT;
5 cryp.auth_len = auth_size;
6 cryp.auth_src = (void*)auth;
7 cryp.len = size;
8 cryp.src = (void*)ciphertext;
9 cryp.dst = ciphertext;
10 cryp.flags = COP_FLAG_AEAD_TLS_TYPE;
11
12 // call ioctl to pass a crypto request to `/dev/crypto`
13 if (ioctl(ctx- >cfd, CIOCAUTHCRYPT, &cryp)) {
14 perror("ioctl(CIOCAUTHCRYPT)");
15 return -1;
16 }
另外,Cryptodev-linux也提供session機制,每個crypto request對應到一個session,而session管理當前crypto request的狀態(tài)。
例如,目前session在initialized的狀態(tài),則表示此crypto request可執(zhí)行encrypt,透過此方式來確保crypto request會在正確的流程下運作。
Linux Kernel Crypto Subsystem
Crypto request會透過kernel crypto API傳到kernel crypto subsystem中。以下為簡略的crypto API調用流程:
Transformation Object & Transformation Implementation
首先Crypto subsystem有兩個重要元素:
- transformation object
- transformation implementation。
transformation object在API中會簡寫為tfm,又被稱作cipher handler;
而transformation implementation則是transformation object底層的實作內容,又被稱作crypto algo ,以之前例子來說就是crypto engine的算法實作。
之所以要區(qū)分成object和implementation,最主要的原因是有可能多個object會使用同一個implementation。
舉例來說,A和B使用者都要使用hmac-sha256算法,因此會新建立A和B兩個transformation object并包含A和B各自擁有的key值,但這兩個object有可能會使用同一個transformation implementation來呼叫同一個crypto engine進行算法運算。
TFM: The transformation object (TFM) is an instance of a transformation implementation. There can be multiple transformation objects associated with a single transformation implementation. Each of those transformation objects is held by a crypto API consumer or another transformation. https://www.kernel.org/doc/html/latest/crypto/intro.html
1struct crypto_tfm {
2 u32 crt_flags;
3 int node;
4 void (*exit)(struct crypto_tfm *tfm);
5 struct crypto_alg *__crt_alg; // crypto algorithm or transformation implementation
6 void *__crt_ctx[] CRYPTO_MINALIGN_ATTR;
7};
- 當有crypto request進來,會先根據(jù)request中指定的算法名稱,從已注冊的crypto algorithm list中取出適合的crypto algorithm,并新建立transformation object。
- 之后,transformation object會再被組成crypto subsystem所用的cipher request。cipher request有可能共享同一個transformation object,舉例來說,hmac-sha256的transformation object包含了transformation implementation和一個key值,而這個transformation object可以使用在多個cipher request的messsage上進行hash算法(不同plaintext使用同一把key進行運算)。
- 當cipher request完成相關設值之后,接著實際調用transformation object的transformation implementation執(zhí)行算法運算。
此時會出現(xiàn)一個問題,就是當短時間有多個request進來時,我們該如何依序地處理request?
這點crypto subsystem也設計了方便的struct crypto_engine,crypto engine提供了queue管理機制,讓多個request能夠順序地轉發(fā)給對應的crypto engine。
當然如果我們有額外的需求,也可以自己實作其他機制來管理,不一定要使用crypto engine。
1struct crypto_engine {
2 char name[ENGINE_NAME_LEN];
3 bool idling;
4 bool busy;
5 bool running;
6
7 bool retry_support;
8
9 struct list_head list;
10 spinlock_t queue_lock;
11 struct crypto_queue queue;
12 struct device *dev;
13
14 bool rt;
15
16 // implement these three functions to trigger your hardware crypto engine
17 int (*prepare_crypt_hardware)(struct crypto_engine *engine);
18 int (*unprepare_crypt_hardware)(struct crypto_engine *engine);
19 int (*do_batch_requests)(struct crypto_engine *engine);
20
21 struct kthread_worker *kworker;
22 struct kthread_work pump_requests;
23
24 void *priv_data;
25 struct crypto_async_request *cur_req;
26};
Register an Crypto Algorithm (Transformation Implementation)
介紹完crypt API流程后,可以知道要新增transformation implementation到crypto subsystem,最重要的就是注冊transformation implementation到crypto algorithm list中。
而Crypto API提供了相關注冊API,以stm32-cryp為例:
1struct skcipher_alg {
2 int (*setkey)(struct crypto_skcipher *tfm, const u8 *key,
3 unsigned int keylen);
4 int (*encrypt)(struct skcipher_request *req);
5 int (*decrypt)(struct skcipher_request *req);
6 int (*init)(struct crypto_skcipher *tfm);
7 void (*exit)(struct crypto_skcipher *tfm);
8
9 unsigned int min_keysize;
10 unsigned int max_keysize;
11 unsigned int ivsize;
12 unsigned int chunksize;
13 unsigned int walksize;
14
15 struct crypto_alg base;
16};
17
18static struct skcipher_alg crypto_algs[] = {
19{
20 .base.cra_name = "ecb(aes)",
21 .base.cra_driver_name = "stm32-ecb-aes",
22 .base.cra_priority = 200,
23 .base.cra_flags = CRYPTO_ALG_ASYNC,
24 .base.cra_blocksize = AES_BLOCK_SIZE,
25 .base.cra_ctxsize = sizeof(struct stm32_cryp_ctx),
26 .base.cra_alignmask = 0xf,
27 .base.cra_module = THIS_MODULE,
28
29 .init = stm32_cryp_init_tfm,
30 .min_keysize = AES_MIN_KEY_SIZE,
31 .max_keysize = AES_MAX_KEY_SIZE,
32 .setkey = stm32_cryp_aes_setkey,
33 .encrypt = stm32_cryp_aes_ecb_encrypt,
34 .decrypt = stm32_cryp_aes_ecb_decrypt,
35},
36}
上述建立含有算法實作的transformation implementation后,接著呼叫注冊API:
1ret = crypto_register_skciphers(crypto_algs, ARRAY_SIZE(crypto_algs));
2if (ret) {
3 dev_err(dev, "Could not register algsn");
4 goto err_algs;
5}
即可完成注冊。
另外,代碼中提及的structure member中, cra_priority代表各transformation implementation的優(yōu)先程度 ,舉例來說,AES-ECB有注冊軟件和硬件兩種不同的transformation implementation,優(yōu)先程度較高的會先被采用。
cra_priority
Priority of this transformation implementation. In case multiple transformations with same cra_name are available to the Crypto API, the kernel will use the one with highest cra_priority.
PART TWO--Crypto Subsystem of Linux Kernel - Asynchronous & Synchronous
在crypto subsystem中,crypto API分成asynchronous(異步)和synchronous(同步)兩種機制。
最早版本的crypto API其實只有synchronous crypto API,但隨著要處理的數(shù)據(jù)量增加,運算和數(shù)據(jù)傳輸時間也可能大幅拉長,此時synchronous crypto API有可能讓處理流程陷入較長時間的等待,因此后來引入了asynchronous crypto API,供使用者依據(jù)自己的使用場景來選擇適合的機制。
而asynchronous與synchronous crypto API在命名設計上有所區(qū)別,asynchronous會在前綴多加一個a字,反之synchronous則是s字,以hash為例:
1// asynchronous API
2int crypto_ahash_digest(struct ahash_request *req);
3
4// synchronous API
5int crypto_shash_digest(struct shash_desc *desc, const u8 *data,
6 unsigned int len, u8 *out);
除了命名之外,由于兩種機制的處理流程不同,因此所需的參數(shù)也會有所不同。
以下同樣以hash crypto algorithm為例子,說明synchronous和asynchronous crypto API的差異和使用情境。
Synchronous hash API
Synchronous hash API中有一個重要的參數(shù)struct shash_desc * desc,它是state handler,用以保存運算流程中所需的狀態(tài)數(shù)值。
例如,在API的呼叫流程中,crypto_shash_update()可以被多次呼叫,讓使用者可以放入多組需要進行運算的message,而當crypto engine運算完一組message后,可能有些中間狀態(tài)是需要被保存起來的,這些狀態(tài)數(shù)值就會放在state handler中。
1struct shash_desc {
2 struct crypto_shash *tfm;
3
4 // store required state for crypto engine
5 void *__ctx[] __aligned(ARCH_SLAB_MINALIGN);
6};
因此當使用者在呼叫API前,會需要自己分配一塊足夠大小的內存,以讓crypto engine能夠存放這些狀態(tài) 。在transformation implementation中會設定好crypto engine所需的狀態(tài)儲存空間大小,使用者只需要呼叫特定API即可取得。
1unsigned int size;
2struct crypto_shash *hash; // transformation object or called cipher handler
3struct shash_desc *desc; // state handler
4
5hash = crypto_alloc_shash(name, 0, 0); // create a transformation object
6
7// get a required desc size for crypto engine via `crypto_shash_descsize` API
8size = sizeof(struct shash_desc) + crypto_shash_descsize(hash);
9desc = kmalloc(size, GFP_KERNEL);
建立好shash_desc之后,接著執(zhí)行初始化的API,
這個API主要會呼叫transformation implementation的init function,用意是讓對應的crypto engine能夠進行初始化或是重置等,以準備接下來的運算行為。
1int rc;2rc = crypto_shash_init(desc);3// error handling
初始化完成后,就可以將呼叫update API來對指定的message進行hash運算。
1rc = crypto_shash_update(desc, message, message_len);2// error handling
最後,呼叫 final 來取得 hash 結果。
1 u8 result[DIGEST_SIZE];2 rc = crypto_shash_final(desc, result);3 // error handling
基本上synchronous API使用方式跟一般應用端的crypto library很相似,只要順序的呼叫對應流程的API,并且針對返回的結果進行error handling即可。
Synchronous crypto API用起來雖然直觀,但是卻不適用于一些場景,除了一開始有提到synchronous機制會造成block之外,另一個可能的問題是,當需要處理的數(shù)據(jù)為不連續(xù)內存區(qū)段時,synchronous crypto API就不是這么好用。
可以看到之前所提及的例子,其中crypto_shash_update的輸入參數(shù)message為一段連續(xù)內存的buffer,假設目前有好幾段數(shù)據(jù),那就必須要呼叫crypto_shash_update多次,才能夠傳入所有的數(shù)據(jù)。
Asynchronous hash API
Asynchronous crypto API提供了非同步的機制和引入【struct scatterlist】[1],來改善上述所提到的問題。
1struct ahash_request {
2 struct crypto_async_request base;
3
4 unsigned int nbytes;
5 struct scatterlist *src;
6 u8 *result;
7
8 /* This field may only be used by the ahash API code. */
9 void *priv;
10
11 void *__ctx[] CRYPTO_MINALIGN_ATTR;
12};
13
14int crypto_ahash_digest(struct ahash_request *req);
從API中可以看到參數(shù)一率改成struct ahash_request,ahash_request結構中包含重要的成員struct scatterlist * ,struct scatterlist用來描述一段連續(xù)physical memory區(qū)段,而它可以是chain的形式,這也意味著能夠將多個physical memory區(qū)段串連成一個list。
1typedef void (*crypto_completion_t)(struct crypto_async_request *req, int err);
2
3struct crypto_async_request {
4 struct list_head list;
5 crypto_completion_t complete;
6 void *data;
7 struct crypto_tfm *tfm;
8
9 u32 flags;
10};
另外,struct crypto_async_request則是包含一個callback function crypto_completion_t,當運算完成之后,則會透過此callback來通知使用者接續(xù)處理完成的流程。
由于是asynchronous非同步機制,因此crypto engine在處理request時,行為和流程也和synchronous同步機制有蠻大的差異,其中常見的實作方式加入request queue來管理多個request,當使用者呼叫update API發(fā)送request時,則會將request加入到queue中,并直接回傳處理中(-EINPROGRESS)的狀態(tài)信息。
以下為簡單的asynchronous hash API使用例子:
1const u32 result_len = 16;
2struct crypto_ahash *tfm;
3struct ahash_request *req;
4u8 *result;
5
6result = kmalloc(result_len, GFP_NOFS);
7
8tfm = crypto_alloc_ahash(0, 0, CRYPTO_ALG_ASYNC);
9req = ahash_request_alloc(tfm, GFP_NOFS);
10// set callback function
11ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, callback_fn, NULL);
12// set input data
13ahash_request_set_crypt(req, sc, NULL, 32);
14
15err = crypto_ahash_init(req);
16
17err = crypto_ahash_update(req);
18if (err == -EINPROGRESS)
19{
20 //
21}
22
23err = crypto_ahash_final(req);
24if (err == -EINPROGRESS)
25{
26 //
27}
其他注意事項:雖然命為asynchronous hash API,但實際上對應到的crypto engine實作方式不一定就都會以非同步的方式來處理,具體流程還是要依照各家廠商的實作內容為主。
如果使用者使用asynchronous hash API,但是實際上對應的transformation implementation卻是synchronous型態(tài),crypto subsystem會主動進行相關的數(shù)據(jù)轉換,因此也是可以正常運作的。
結論
簡單介紹asynchronous和synchronous crypto API以及與crypto engine的溝通流程,表面上看起來asynchronous機制更有彈性,不過對于廠商來說,實際上要實作哪種機制可能會受到硬件或是其他實作層面的限制,因此還是要多方參考后才能知道哪種方式比較好。
PART THREE--Crypto Subsystem of Linux Kernel - Asynchronous Request Handling Mechanism
由于在crypto subsystem中預期多個crypto request可以同時向同一個crypto engine發(fā)出請求,因此crypto engine driver必須實作對應機制,使其有能力能應付此情況。
此外,結合在前一節(jié)有提到crypto subsystem的asynchronous crypto API流程,較常見的實作方式就是crypto queue搭配worker,額外開一個kernel thread來與crypto engine進行溝通,并讓crypto request按照FIFO順序處理,而本文主要針對此設計方式,說明整個運作流程和細節(jié)。
Overview
條件
假設hardware crypto engine一次只能處理一個request,將request根據(jù)需求設置好register之后,啟動crypto engine進行運算,運算完結果后才能換下一個request。
當運算出結果后,crypto engine會舉起status interrupt,通知外部已運算完成。
多個Request有可能同時對hardware crypto engine發(fā)出請求。
一個完整的crypto request流程包含三個API call: Init→Update→Final,F(xiàn)inal結果回傳后,則此crypto request將會被釋放,不再使用。
IDEA
建立一個全局的crypto request list,將進來的request依序排到list當中。
建立一個worker(kernel thread)和對應的work queue來與hardware crypto engine進行溝通。worker的任務除了從crypto request list中取出request來處理之外,也可能會包含crypto engine的初始化和資源釋放等工作。
注冊interrupt handler,當status interrupt舉起時,呼叫user自定義的completion callback function來完成最后的流程。如果當前是執(zhí)行最后的final API call且request有自定義的resource需要被釋放,則會在呼叫完callback function后執(zhí)行。
verison
Linux kernel version: v5.17.3
Crypto Queue
Linux kernel有實作通用型的crypto queue structure以及對應的操作API:
1struct crypto_queue {
2 struct list_head list;
3 struct list_head *backlog;
4
5 unsigned int qlen;
6 unsigned int max_qlen;
7};
8
9void crypto_init_queue(struct crypto_queue *queue, unsigned int max_qlen);
10
11int crypto_enqueue_request(struct crypto_queue *queue, struct crypto_async_request *request);
12void crypto_enqueue_request_head(struct crypto_queue *queue, struct crypto_async_request *request);
13
14struct crypto_async_request *crypto_dequeue_request(struct crypto_queue *queue);
15
16static inline unsigned int crypto_queue_len(struct crypto_queue *queue);
在大多數(shù)情況下,我們可以直接利用此structure來實現(xiàn)crypto request list,不過根據(jù)我們上述的場景,request list可能會被多個request同時操作,因此要再加上lock機制保護。
1struct cherie_crypto_engine {
2 struct device *dev;
3
4 struct crypto_queue queue;
5 struct kthread_worker *kworker;
6 struct kthread_work do_requests;
7 spinlock_t queue_lock;
8
9 struct crypto_async_request *current_req;
10};
11
12static int cherie_request_enqueue(struct ahash_request *req)
13{
14 int ret;
15 unsigned long flags;
16 struct cherie_crypto_engine *engine = get_engine();
17
18 spin_lock_irqsave(&engine- >queue_lock, flags);
19 ret = crypto_enqueue_request(&engine- >queue, &req- >base);
20 spin_unlock_irqrestore(&engine- >queue_lock, flags);
21 return ret;
22}
Worker & Worker Queue
Worker是唯一可以操作crypto engine的kernel thread,以確保crypto engine一次只會執(zhí)行一個任務。同樣地,我們也利用Linux kernel本身提供的worker API來實現(xiàn):
1struct kthread_worker *kthread_create_worker(unsigned int flags, const char namefmt[], ...);
2bool kthread_queue_work(struct kthread_worker *worker, struct kthread_work *work);
至于work可能會包含幾個項目:
- crypto engine初始化。
- 從crypto request list取出request,并依據(jù)request的信息進行crypto engine相關register的讀寫操作。
- crypto engine資源的釋放。(例如當前沒有request要處理時,可以先釋放相關資源)
1static void cherie_work(struct kthread_work *work)
2{
3 unsigned long flags;
4 struct cherie_request_state *state;
5 struct ahash_request *req;
6 struct crypto_async_request *async_req;
7 struct cherie_crypto_engine *engine = get_engine();
8
9 spin_lock_irqsave(&engine- >queue_lock, flags);
10
11 if (!engine- >initialize)
12 {
13 // do initialization
14 }
15
16 // we can't fetch the next request if the current request isn't done.
17 if (engine- >current_req)
18 {
19 spin_unlock_irqrestore(&engine- >queue_lock, flags);
20 return;
21 }
22
23 async_req = crypto_dequeue_request(&engine- >queue);
24 spin_unlock_irqrestore(&engine- >queue_lock, flags);
25
26 if (!async_req)
27 return;
28
29 req = ahash_request_cast(async_req);
30 state = ahash_request_ctx(req);
31
32 switch (state- >algo_op)
33 {
34 case ALGO_UPDATE:
35 cherie_do_request_update(req);
36 break;
37 case ALGO_FINAL:
38 cherie_do_request_final(req);
39 break;
40 default:
41 break;
42 }
43}
Status Interrupt Handling
由于是asynchronous request機制,因此在crypto engine計算完成、舉起status interrupt signal之后,透過bottom half方式呼叫user定義的completion callback function來結束此階段的API call。
1static irqreturn_t cherie_crypto_engine_irq_thread_fn(int irq, void *arg)
2{
3 unsigned long flags;
4 struct cherie_crypto_engine *engine = get_engine();
5
6 spin_lock_irqsave(&engine- >queue_lock, flags);
7
8 if (engine- >current_req)
9 {
10 engine- >current_req- >complete(engine- >current_req, 0);
11 engine- >current_req = NULL;
12 }
13 spin_unlock_irqrestore(&engine- >queue_lock, flags);
14 // add a work to process the next request
15 kthread_queue_work(ctx- >kworker, &ctx- >do_requests);
16
17 return IRQ_HANDLED;
18}
19
20static int cherie_crypto_engine_probe(struct platform_device *pdev)
21{
22 int irq, ret;
23 irq = platform_get_irq(pdev, 0);
24 if (irq < 0)
25 return irq;
26
27 ret = devm_request_threaded_irq(dev, irq, cherie_crypto_engine_irq_handler,
28 cherie_crypto_engine_irq_thread_fn, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
29 dev_name(dev), ctx);
30}
Implement Crypto API
最后實作Crypto API,也就是在overview中有提到的transformation implementation。
通常來說,asynchronous request回傳的status code有三種:
- 0代表成功、
- -EINPROGRESS代表正在處理中、
- 剩下的代表其他error code。
如果我們在實作API時,回傳了-EINPROGRESS,那一定要在后續(xù)的流程中呼叫user program的callback function,不然有可能user program就會陷入一直等待callback的循環(huán)中。
舉例來說,我們在update API function中,當request被加入到queue后,即回傳-EINPROGRESS狀態(tài)給user program:
1static int cherie_crypto_engine_update(struct ahash_request *req)
2{
3 int ret;
4 struct cherie_crypto_engine *engine = get_engine();
5 struct cherie_request_state *state = ahash_request_ctx(req);
6
7 state- >algo_op = ALGO_UPDATE;
8 ret = cherie_crypto_request_enqueue(req);
9 kthread_queue_work(ctx- >kworker, &ctx- >do_requests);
10 return ret; // ret is -EINPROGRESS if the request was added to queue.
11}
那么就要確保在worker執(zhí)行任務過程中會呼叫request→complete callback function,好讓user program知道他可以繼續(xù)執(zhí)行下一個API call。
結論
本篇主要是說明在Linux kernel的crypto subsystem之下,運用crypto queue來處理多個request同時對一個hardware crypto engine的情境,并且搭配worker來實現(xiàn)asynchronous request流程。
其中比較需要考察的是由于worker是唯一能與crypto engine互動的thread,因此worker需要處理的任務和順序,是需要根據(jù)Crypto API流程和crypto engine本身的功能而設計的。
當然,如果不想這么麻煩的話,在Linux kernel v4.9.0以上的版本,有提供抽象層【crypto/engine.h】[2],它包含上述所提及的queue和worker的機制以及流程控制等,可以讓硬件供應商更方便的將crypto engine整合到Linux kernel中。
-
芯片
+關注
關注
455文章
50714瀏覽量
423139 -
soc
+關注
關注
38文章
4161瀏覽量
218164 -
Linux
+關注
關注
87文章
11292瀏覽量
209328 -
Linux系統(tǒng)
+關注
關注
4文章
593瀏覽量
27392 -
SoC芯片
+關注
關注
1文章
610瀏覽量
34905 -
OpenSSL
+關注
關注
0文章
21瀏覽量
8677 -
加解密
+關注
關注
0文章
18瀏覽量
6517 -
Hash算法
+關注
關注
0文章
43瀏覽量
7382
發(fā)布評論請先 登錄
相關推薦
評論