From 37cd97e033305b9c4607e13444dbe39315b03535 Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Wed, 16 May 2018 00:11:01 +0900 Subject: [PATCH] [COMMON] crypto: update diskcipher free and debug Change-Id: I43447d93662c4c8978cab634441a146f7e45c13b Signed-off-by: Boojin Kim --- crypto/Kconfig | 7 ++ crypto/diskcipher.c | 200 ++++++++++++++++++++++++++---------- include/crypto/diskcipher.h | 38 ++++++- 3 files changed, 187 insertions(+), 58 deletions(-) diff --git a/crypto/Kconfig b/crypto/Kconfig index ad583228e0cd..7617ef8c139e 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -1634,6 +1634,13 @@ config CRYPTO_DISKCIPHER Diskcipher support the crypt operation of the block host device that has inline crypto engine. +config CRYPTO_DISKCIPHER_DEBUG + bool "Diskcipher debug support" + default n + depends on CRYPTO_DISKCIPHER + help + support Diskcipher debug + comment "Compression" config CRYPTO_DEFLATE diff --git a/crypto/diskcipher.c b/crypto/diskcipher.c index 78f1f0394d9f..91a6b536bbad 100644 --- a/crypto/diskcipher.c +++ b/crypto/diskcipher.c @@ -16,35 +16,48 @@ #include #include #include +#include #include "internal.h" -#ifdef EANBLE_DISKCIPHER_DEBUG + +#ifdef CONFIG_CRYPTO_DISKCIPHER_DEBUG #include #include +#include +#include #define DUMP_MAX 20 -enum diskcipher_api { - DISKC_API_ALLOC, DISKC_API_FREE, DISKC_API_SET, - DISKC_API_GET, DISKC_API_CRYPT, DISKC_API_CLEAR, DISKC_API_MAX, -}; - struct dump_err { struct page *page; struct bio bio; struct fmp_crypto_info ci; - enum diskcipher_api api; + enum diskcipher_dbg api; }; struct diskc_debug_info { struct dump_err dump[DUMP_MAX]; int err; - u32 cnt[DISKC_API_MAX][2]; + int cnt[DISKC_USER_MAX][2]; }; static struct diskc_debug_info diskc_dbg; +void crypto_diskcipher_debug(enum diskcipher_dbg api, int bi_opf) +{ + int idx = 0; + struct diskc_debug_info *dbg = &diskc_dbg; + + if (api <= DISKC_API_MAX) + dbg->cnt[api][bi_opf]++; + else { + if (bi_opf & REQ_CRYPT) + idx = 1; + dbg->cnt[api][idx]++; + } +} + static void print_err(void) { int i, j; @@ -80,7 +93,7 @@ static void print_err(void) } } -static void dump_err(struct crypto_diskcipher *ci, enum diskcipher_api api, +static void dump_err(struct crypto_diskcipher *ci, enum diskcipher_dbg api, struct bio *bio, struct page *page) { struct diskc_debug_info *dbg = &diskc_dbg; @@ -101,68 +114,61 @@ static void dump_err(struct crypto_diskcipher *ci, enum diskcipher_api api, dbg->err++; } -static inline void disckipher_log(enum diskcipher_api api, int ret, - struct crypto_diskcipher *ci) -{ - struct diskc_debug_info *dbg = &diskc_dbg; - - dbg->cnt[api][0]++; - if (ret) { - dbg->cnt[api][1]++; - if (ci) - dump_err(ci, api, NULL, NULL); - } -} - static void disckipher_log_show(struct seq_file *m) { int i; - char name[DISKC_API_MAX][8] - = {"alloc", "free", "set", "get", "crypt", "clear"}; struct diskc_debug_info *dbg = &diskc_dbg; - - for (i = 0; i < DISKC_API_MAX; i++) - seq_printf(m, "%s\t: %6u(err:%u)\n", - name[i], dbg->cnt[i][0], dbg->cnt[i][1]); + char name[DISKC_USER_MAX][20] + = {"alloc", "free", "freereq", "setkey", "set", "get", + "crypt", "clear", "null", "page-io", "readpage", "dio", + "blk_write", "zeropage", "bufferhead", + "dmcrypt", "merge", "diskc_check_err", "fs_dec_warn", + "fs_enc_warn", "diskc_merge_dio", "diskc_freereq_warn", + "diskc_freewq_warn", "disk_crypt_warn"}; + + for (i = 0; i < DISKC_USER_MAX; i++) + if (dbg->cnt[i][0] || dbg->cnt[i][1]) + seq_printf(m, "%s\t: %6u(err:%u)\n", + name[i], dbg->cnt[i][0], dbg->cnt[i][1]); if (dbg->err) print_err(); } /* check diskcipher for FBE */ -void crypto_diskcipher_check(struct bio *bio, struct page *page) +#define DISKC_FS_ENCRYPT_DEBUG +void crypto_diskcipher_check(struct bio *bio) { -#ifdef FBE_DEBUG +#ifdef DISKC_FS_ENCRYPT_DEBUG int ret = 0; struct crypto_diskcipher *ci = NULL; + struct inode *inode = NULL; + struct page *page = bio->bi_io_vec[0].bv_page; if (page && !PageAnon(page) && bio) if (page->mapping) if (page->mapping->host) if (page->mapping->host->i_crypt_info) { - ci = page->mapping->host->i_crypt_info->ci_dtfm; + inode = page->mapping->host; + ci = fscrypt_get_diskcipher(page->mapping->host); if (ci && (bio->bi_aux_private != ci) && (!(bio->bi_flags & REQ_OP_DISCARD))) { dump_err(ci, DISKC_API_GET, bio, page); ret = 1; + crypto_diskcipher_debug(DISKC_CHECK_ERR, 0); + } + if (!inode->i_crypt_info || !ci) { + ret = 1; + crypto_diskcipher_debug(DISKC_CHECK_ERR, 1); } } - disckipher_log(DISKC_API_GET, ret, ci); + crypto_diskcipher_debug(DISKC_API_GET, ret); #endif } #else -enum diskcipher_api { - DISKC_API_ALLOC, DISKC_API_FREE, DISKC_API_SET, - DISKC_API_GET, DISKC_API_CRYPT, DISKC_API_CLEAR, DISKC_API_MAX, -}; - #define disckipher_log_show(a) do { } while (0) -#define disckipher_log(a, b, c) do { } while (0) #endif -#define DISKC_NAME "-disk" -#define DISKC_NAME_SIZE (5) - struct crypto_diskcipher *crypto_diskcipher_get(struct bio *bio) { if (!bio || !virt_addr_valid(bio)) { @@ -176,15 +182,21 @@ struct crypto_diskcipher *crypto_diskcipher_get(struct bio *bio) } void crypto_diskcipher_set(struct bio *bio, - struct crypto_diskcipher *diskcipher) + struct crypto_diskcipher *tfm) { - if (bio && diskcipher) { + if (bio && tfm) { bio->bi_opf |= (REQ_CRYPT | REQ_AUX_PRIV); - bio->bi_aux_private = diskcipher; + bio->bi_aux_private = tfm; } - disckipher_log(DISKC_API_SET, 0, NULL); + crypto_diskcipher_debug(DISKC_API_SET, 0); } +/* debug freerq */ +enum diskc_status { + DISKC_ST_INIT, + DISKC_ST_FREE_REQ, + DISKC_ST_FREE, +}; int crypto_diskcipher_setkey(struct crypto_diskcipher *tfm, const char *in_key, unsigned int key_len, bool persistent) { @@ -195,6 +207,8 @@ int crypto_diskcipher_setkey(struct crypto_diskcipher *tfm, const char *in_key, pr_err("%s: doesn't exist cra", __func__); return -EINVAL; } + + crypto_diskcipher_debug(DISKC_API_SETKEY, 0); return cra->setkey(base, in_key, key_len, persistent); } @@ -215,6 +229,7 @@ int crypto_diskcipher_set_crypt(struct crypto_diskcipher *tfm, void *req) int ret = 0; struct crypto_tfm *base = crypto_diskcipher_tfm(tfm); struct diskcipher_alg *cra = crypto_diskcipher_alg(base->__crt_alg); + struct diskcipher_freectrl *fctrl = &cra->freectrl; if (!cra) { pr_err("%s: doesn't exist cra", __func__); @@ -222,12 +237,24 @@ int crypto_diskcipher_set_crypt(struct crypto_diskcipher *tfm, void *req) goto out; } + if (atomic_read(&tfm->status) == DISKC_ST_FREE) { + pr_err("%s: tfm is free\n", __func__); + crypto_diskcipher_debug(DISKC_CRYPT_WARN, 0); + return -EINVAL; + } + ret = cra->crypt(base, req); + + if (!list_empty(&fctrl->freelist)) { + if (!atomic_read(&fctrl->freewq_active)) { + atomic_set(&fctrl->freewq_active, 1); + schedule_delayed_work(&fctrl->freewq, 0); + } + } out: if (ret) pr_err("%s fails ret:%d, cra:%p\n", __func__, ret, cra); - pr_debug("%s done\n", __func__); - disckipher_log(DISKC_API_CRYPT, ret, tfm); + crypto_diskcipher_debug(DISKC_API_CRYPT, ret); return ret; } @@ -248,8 +275,7 @@ int crypto_diskcipher_clear_crypt(struct crypto_diskcipher *tfm, void *req) pr_err("%s fails", __func__); out: - pr_debug("%s done\n", __func__); - disckipher_log(DISKC_API_CLEAR, ret, tfm); + crypto_diskcipher_debug(DISKC_API_CLEAR, ret); return ret; } @@ -275,16 +301,73 @@ int diskcipher_do_crypt(struct crypto_diskcipher *tfm, pr_err("%s fails ret:%d", __func__, ret); out: - pr_debug("%s done\n", __func__); return ret; } #endif -static int crypto_diskcipher_init_tfm(struct crypto_tfm *tfm) +static int crypto_diskcipher_init_tfm(struct crypto_tfm *base) { + struct crypto_diskcipher *tfm = __crypto_diskcipher_cast(base); + + tfm->req_jiffies = 0; + atomic_set(&tfm->status, DISKC_ST_INIT); return 0; } +static void free_workq_func(struct work_struct *work) +{ + struct diskcipher_alg *cra = + container_of(work, struct diskcipher_alg, freectrl.freewq.work); + struct diskcipher_freectrl *fctrl = &cra->freectrl; + struct crypto_diskcipher *_tfm, *tmp; + unsigned long cur_jiffies = jiffies; + struct list_head poss_free_list; + unsigned long flags; + + INIT_LIST_HEAD(&poss_free_list); + + /* pickup freelist */ + spin_lock_irqsave(&fctrl->freelist_lock, flags); + list_for_each_entry_safe(_tfm, tmp, &fctrl->freelist, node) { + if (jiffies_to_msecs(cur_jiffies - _tfm->req_jiffies) > fctrl->max_io_ms) + list_move_tail(&_tfm->node, &poss_free_list); + } + spin_unlock_irqrestore(&fctrl->freelist_lock, flags); + + list_for_each_entry_safe(_tfm, tmp, &poss_free_list, node) { + if (atomic_read (&_tfm->status) != DISKC_ST_FREE_REQ) + crypto_diskcipher_debug(DISKC_FREE_WQ_WARN, 0); + crypto_free_diskcipher(_tfm); + } + + if (!list_empty(&fctrl->freelist)) + schedule_delayed_work(&fctrl->freewq, msecs_to_jiffies(fctrl->max_io_ms)); + else + atomic_set(&fctrl->freewq_active, 0); +} + +void crypto_free_req_diskcipher(struct crypto_diskcipher *tfm) +{ + struct crypto_tfm *base = crypto_diskcipher_tfm(tfm); + struct diskcipher_alg *cra = crypto_diskcipher_alg(base->__crt_alg); + struct diskcipher_freectrl *fctrl = &cra->freectrl; + unsigned long flags; + + if (atomic_read(&tfm->status) != DISKC_ST_INIT) { + crypto_diskcipher_debug(DISKC_FREE_REQ_WARN, 0); + pr_warn("%s: already submit status:%d\n", __func__, atomic_read(&tfm->status)); + return; + } + + atomic_set(&tfm->status, DISKC_ST_FREE_REQ); + INIT_LIST_HEAD(&tfm->node); + tfm->req_jiffies = jiffies; + spin_lock_irqsave(&fctrl->freelist_lock, flags); + list_move_tail(&tfm->node, &fctrl->freelist); + spin_unlock_irqrestore(&fctrl->freelist_lock, flags); + crypto_diskcipher_debug(DISKC_API_FREEREQ, 0); +} + unsigned int crypto_diskcipher_extsize(struct crypto_alg *alg) { return alg->cra_ctxsize + @@ -309,10 +392,13 @@ static const struct crypto_type crypto_diskcipher_type = { .tfmsize = offsetof(struct crypto_diskcipher, base), }; +#define DISKC_NAME "-disk" +#define DISKC_NAME_SIZE (5) +#define DISKCIPHER_MAX_IO_MS (1000) struct crypto_diskcipher *crypto_alloc_diskcipher(const char *alg_name, u32 type, u32 mask, bool force) { - disckipher_log(DISKC_API_ALLOC, 0, NULL); + crypto_diskcipher_debug(DISKC_API_ALLOC, 0); if (force) { if (strlen(alg_name) + DISKC_NAME_SIZE < CRYPTO_MAX_ALG_NAME) { char diskc_name[CRYPTO_MAX_ALG_NAME]; @@ -325,20 +411,26 @@ struct crypto_diskcipher *crypto_alloc_diskcipher(const char *alg_name, } else { return crypto_alloc_tfm(alg_name, &crypto_diskcipher_type, type, mask); } - return NULL; } void crypto_free_diskcipher(struct crypto_diskcipher *tfm) { - disckipher_log(DISKC_API_FREE, 0, NULL); + crypto_diskcipher_debug(DISKC_API_FREE, 0); + atomic_set(&tfm->status, DISKC_ST_FREE); crypto_destroy_tfm(tfm, crypto_diskcipher_tfm(tfm)); } int crypto_register_diskcipher(struct diskcipher_alg *alg) { struct crypto_alg *base = &alg->base; + struct diskcipher_freectrl *fctrl = &alg->freectrl; + INIT_LIST_HEAD(&fctrl->freelist); + INIT_DELAYED_WORK(&fctrl->freewq, free_workq_func); + spin_lock_init(&fctrl->freelist_lock); + if (!fctrl->max_io_ms) + fctrl->max_io_ms = DISKCIPHER_MAX_IO_MS; base->cra_type = &crypto_diskcipher_type; base->cra_flags = CRYPTO_ALG_TYPE_DISKCIPHER; return crypto_register_alg(base); diff --git a/include/crypto/diskcipher.h b/include/crypto/diskcipher.h index 2369b8b52030..6a98c7ffb8cf 100644 --- a/include/crypto/diskcipher.h +++ b/include/crypto/diskcipher.h @@ -18,6 +18,10 @@ struct diskcipher_alg; struct crypto_diskcipher { u32 algo; unsigned int ivsize; + /* for crypto_free_req_diskcipher */ + unsigned long req_jiffies; + struct list_head node; + atomic_t status; struct crypto_tfm base; }; @@ -44,6 +48,15 @@ struct diskcipher_test_request { * And pass the crypto information to disk host device via bio. * Crypt operation executes on inline crypto on disk host device. */ + +struct diskcipher_freectrl { + spinlock_t freelist_lock; + struct list_head freelist; + struct delayed_work freewq; + atomic_t freewq_active; + u32 max_io_ms; +}; + struct diskcipher_alg { int (*setkey)(struct crypto_tfm *tfm, const char *key, u32 keylen, bool persistent); @@ -55,6 +68,7 @@ struct diskcipher_alg { struct diskcipher_test_request *req); #endif struct crypto_alg base; + struct diskcipher_freectrl freectrl; }; static inline unsigned int crypto_diskcipher_ivsize(struct crypto_diskcipher *tfm) @@ -109,6 +123,7 @@ struct crypto_diskcipher *crypto_alloc_diskcipher(const char *alg_name, * @tfm: cipher handle to be freed */ void crypto_free_diskcipher(struct crypto_diskcipher *tfm); +void crypto_free_req_diskcipher(struct crypto_diskcipher *tfm); /** * crypto_diskcipher_get() - get diskcipher from bio @@ -207,16 +222,31 @@ static inline void diskcipher_request_set_crypt( req->enc = enc; } #endif -#define EANBLE_DISKCIPHER_DEBUG -#ifdef EANBLE_DISKCIPHER_DEBUG -void crypto_diskcipher_check(struct bio *bio, struct page *page); + +enum diskcipher_dbg { + DISKC_API_ALLOC, DISKC_API_FREE, DISKC_API_FREEREQ, DISKC_API_SETKEY, + DISKC_API_SET, DISKC_API_GET, DISKC_API_CRYPT, DISKC_API_CLEAR, + DISKC_API_MAX, FS_PAGEIO, FS_READP, FS_DIO, FS_BLOCK_WRITE, + FS_ZEROPAGE, BLK_BH, DMCRYPT, DISKC_MERGE, DISKC_CHECK_ERR, + FS_DEC_WARN, FS_ENC_WARN, DISKC_MERGE_DIO, DISKC_FREE_REQ_WARN, + DISKC_FREE_WQ_WARN, DISKC_CRYPT_WARN, DISKC_USER_MAX +}; +#ifdef CONFIG_CRYPTO_DISKCIPHER_DEBUG +void crypto_diskcipher_debug(enum diskcipher_dbg dbg, int idx); +void crypto_diskcipher_check(struct bio *bio); +#else +#define crypto_diskcipher_check(a) ((void)0) +#define crypto_diskcipher_debug(a, b) ((void)0) #endif #else -#define crypto_alloc_diskcipher(a, b, c, d) ((void *)NULL) +#define crypto_alloc_diskcipher(a, b, c, d) ((void *)-EINVAL) #define crypto_free_diskcipher(a) ((void)0) +#define crypto_free_req_diskcipher(a) ((void)0) #define crypto_diskcipher_get(a) ((void *)NULL) #define crypto_diskcipher_set(a, b) ((void)0) #define crypto_diskcipher_clearkey(a) ((void)0) #define crypto_diskcipher_setkey(a, b, c, d) (-1) +#define crypto_diskcipher_check(a) ((void)0) +#define crypto_diskcipher_debug(a, b) ((void)0) #endif #endif /* _DISKCIPHER_H_ */ -- 2.20.1