[PATCH v4 06/24] s390/zcrypt: Introduce cprb mempool for cca misc functions

Harald Freudenberger
2025-04-09T14:03:17Z

Introduce a new module parameter "zcrypt_mempool_threshold"
for the zcrypt module. This parameter controls the minimal
amount of mempool items which are pre-allocated for urgent
requests/replies and will be used with the support for the
new xflag ZCRYPT_XFLAG_NOMEMALLOC. The default value of 5
shall provide enough memory items to support up to 5 requests
(and their associated reply) in parallel. The minimum value
is 1 and is checked and maybe adjusted in the module init().

If the mempool is depleted upon one cca misc functions is called
with the named xflag set, the function will fail with -ENOMEM
and the caller is responsible for taking further actions.

For CCA each mempool item is 16KB, as a CCA CPRB needs to
hold the request and the reply. The pool items only support
requests/replies with a limit of about 8KB.
So by default the CCA mempool consumes
5 * 16KB = 80KB

This is only part of an rework to support a new xflag
ZCRYPT_XFLAG_NOMEMALLOC but not yet complete.

Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
Reviewed-by: Holger Dengler <dengler@linux.ibm.com>
---
drivers/s390/crypto/zcrypt_api.c | 16 +++-
drivers/s390/crypto/zcrypt_api.h | 2 +
drivers/s390/crypto/zcrypt_ccamisc.c | 116 ++++++++++++++++++++-------
drivers/s390/crypto/zcrypt_ccamisc.h | 1 +
4 files changed, 104 insertions(+), 31 deletions(-)

diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
index f753c0403a18..888ab289bd10 100644
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -50,6 +50,10 @@ MODULE_DESCRIPTION("Cryptographic Coprocessor interface, " \
"Copyright IBM Corp. 2001, 2012");
MODULE_LICENSE("GPL");

+unsigned int zcrypt_mempool_threshold = 5;
+module_param_named(mempool_threshold, zcrypt_mempool_threshold, uint, 0440);
+MODULE_PARM_DESC(mempool_threshold, "CCA and EP11 request/reply mempool minimal items.");
+
/*
* zcrypt tracepoint functions
*/
@@ -2147,13 +2151,20 @@ int __init zcrypt_api_init(void)
{
int rc;

+ /* make sure the mempool threshold is >= 1 */
+ zcrypt_mempool_threshold = max_t(unsigned int, zcrypt_mempool_threshold, 1);
+
rc = zcrypt_debug_init();
if (rc)
goto out;

rc = zcdn_init();
if (rc)
- goto out;
+ goto out_zcdn_init_failed;
+
+ rc = zcrypt_ccamisc_init();
+ if (rc)
+ goto out_ccamisc_init_failed;

/* Register the request sprayer. */
rc = misc_register(&zcrypt_misc_device);
@@ -2166,7 +2177,10 @@ int __init zcrypt_api_init(void)
return 0;

out_misc_register_failed:
+ zcrypt_ccamisc_exit();
+out_ccamisc_init_failed:
zcdn_exit();
+out_zcdn_init_failed:
zcrypt_debug_exit();
out:
return rc;
diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h
index 94dffb01942f..84d636fd14a4 100644
--- a/drivers/s390/crypto/zcrypt_api.h
+++ b/drivers/s390/crypto/zcrypt_api.h
@@ -139,6 +139,8 @@ extern atomic_t zcrypt_rescan_req;
extern spinlock_t zcrypt_list_lock;
extern struct list_head zcrypt_card_list;

+extern unsigned int zcrypt_mempool_threshold;
+
#define for_each_zcrypt_card(_zc) \
list_for_each_entry(_zc, &zcrypt_card_list, list)

diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c
index 521baaea06ff..05085b40a55c 100644
--- a/drivers/s390/crypto/zcrypt_ccamisc.c
+++ b/drivers/s390/crypto/zcrypt_ccamisc.c
@@ -11,6 +11,7 @@
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt

#include <linux/init.h>
+#include <linux/mempool.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/random.h>
@@ -40,6 +41,16 @@ struct cca_info_list_entry {
static LIST_HEAD(cca_info_list);
static DEFINE_SPINLOCK(cca_info_list_lock);

+/*
+ * Cprb memory pool held for urgent cases where no memory
+ * can be allocated via kmalloc. This pool is only used
+ * when alloc_and_prep_cprbmem() is called with the xflag
+ * ZCRYPT_XFLAG_NOMEMALLOC. The cprb memory needs to hold
+ * space for request AND reply!
+ */
+#define CPRB_MEMPOOL_ITEM_SIZE (16 * 1024)
+static mempool_t *cprb_mempool;
+
/*
* Simple check if the token is a valid CCA secure AES data key
* token. If keybitsize is given, the bitsize of the key is
@@ -219,9 +230,10 @@ EXPORT_SYMBOL(cca_check_sececckeytoken);
static int alloc_and_prep_cprbmem(size_t paramblen,
u8 **p_cprb_mem,
struct CPRBX **p_req_cprb,
- struct CPRBX **p_rep_cprb)
+ struct CPRBX **p_rep_cprb,
+ u32 xflags)
{
- u8 *cprbmem;
+ u8 *cprbmem = NULL;
size_t cprbplusparamblen = sizeof(struct CPRBX) + paramblen;
struct CPRBX *preqcblk, *prepcblk;

@@ -229,7 +241,16 @@ static int alloc_and_prep_cprbmem(size_t paramblen,
* allocate consecutive memory for request CPRB, request param
* block, reply CPRB and reply param block
*/
- cprbmem = kcalloc(2, cprbplusparamblen, GFP_KERNEL);
+ if (xflags & ZCRYPT_XFLAG_NOMEMALLOC) {
+ size_t len = 2 * cprbplusparamblen;
+
+ if (cprb_mempool && len <= CPRB_MEMPOOL_ITEM_SIZE) {
+ cprbmem = mempool_alloc_preallocated(cprb_mempool);
+ memset(cprbmem, 0, len);
+ }
+ } else {
+ cprbmem = kcalloc(2, cprbplusparamblen, GFP_KERNEL);
+ }
if (!cprbmem)
return -ENOMEM;

@@ -261,11 +282,15 @@ static int alloc_and_prep_cprbmem(size_t paramblen,
* with zeros before freeing (useful if there was some
* clear key material in there).
*/
-static void free_cprbmem(void *mem, size_t paramblen, int scrub)
+static void free_cprbmem(void *mem, size_t paramblen, bool scrub, u32 xflags)
{
if (scrub)
memzero_explicit(mem, 2 * (sizeof(struct CPRBX) + paramblen));
- kfree(mem);
+
+ if (xflags & ZCRYPT_XFLAG_NOMEMALLOC)
+ mempool_free(mem, cprb_mempool);
+ else
+ kfree(mem);
}

/*
@@ -330,9 +355,11 @@ int cca_genseckey(u16 cardnr, u16 domain,
} keyblock;
} lv3;
} __packed * prepparm;
+ const u32 xflags = 0;

/* get already prepared memory for 2 cprbs with param block each */
- rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk);
+ rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem,
+ &preqcblk, &prepcblk, xflags);
if (rc)
return rc;

@@ -379,7 +406,7 @@ int cca_genseckey(u16 cardnr, u16 domain,
prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk);

/* forward xcrb with request CPRB and reply CPRB to zcrypt dd */
- rc = zcrypt_send_cprb(&xcrb, 0);
+ rc = zcrypt_send_cprb(&xcrb, xflags);
if (rc) {
ZCRYPT_DBF_ERR("%s zcrypt_send_cprb (cardnr=%d domain=%d) failed, errno %d\n",
__func__, (int)cardnr, (int)domain, rc);
@@ -424,7 +451,7 @@ int cca_genseckey(u16 cardnr, u16 domain,
memcpy(seckey, prepparm->lv3.keyblock.tok, SECKEYBLOBSIZE);

out:
- free_cprbmem(mem, PARMBSIZE, 0);
+ free_cprbmem(mem, PARMBSIZE, false, xflags);
return rc;
}
EXPORT_SYMBOL(cca_genseckey);
@@ -471,9 +498,11 @@ int cca_clr2seckey(u16 cardnr, u16 domain, u32 keybitsize,
} keyblock;
} lv3;
} __packed * prepparm;
+ const u32 xflags = 0;

/* get already prepared memory for 2 cprbs with param block each */
- rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk);
+ rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem,
+ &preqcblk, &prepcblk, xflags);
if (rc)
return rc;

@@ -517,7 +546,7 @@ int cca_clr2seckey(u16 cardnr, u16 domain, u32 keybitsize,
prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk);

/* forward xcrb with request CPRB and reply CPRB to zcrypt dd */
- rc = zcrypt_send_cprb(&xcrb, 0);
+ rc = zcrypt_send_cprb(&xcrb, xflags);
if (rc) {
ZCRYPT_DBF_ERR("%s zcrypt_send_cprb (cardnr=%d domain=%d) failed, rc=%d\n",
__func__, (int)cardnr, (int)domain, rc);
@@ -563,7 +592,7 @@ int cca_clr2seckey(u16 cardnr, u16 domain, u32 keybitsize,
memcpy(seckey, prepparm->lv3.keyblock.tok, SECKEYBLOBSIZE);

out:
- free_cprbmem(mem, PARMBSIZE, 1);
+ free_cprbmem(mem, PARMBSIZE, true, xflags);
return rc;
}
EXPORT_SYMBOL(cca_clr2seckey);
@@ -617,9 +646,11 @@ int cca_sec2protkey(u16 cardnr, u16 domain,
} ckb;
} lv3;
} __packed * prepparm;
+ const u32 xflags = 0;

/* get already prepared memory for 2 cprbs with param block each */
- rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk);
+ rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem,
+ &preqcblk, &prepcblk, xflags);
if (rc)
return rc;

@@ -644,7 +675,7 @@ int cca_sec2protkey(u16 cardnr, u16 domain,
prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk);

/* forward xcrb with request CPRB and reply CPRB to zcrypt dd */
- rc = zcrypt_send_cprb(&xcrb, 0);
+ rc = zcrypt_send_cprb(&xcrb, xflags);
if (rc) {
ZCRYPT_DBF_ERR("%s zcrypt_send_cprb (cardnr=%d domain=%d) failed, rc=%d\n",
__func__, (int)cardnr, (int)domain, rc);
@@ -712,7 +743,7 @@ int cca_sec2protkey(u16 cardnr, u16 domain,
*protkeylen = prepparm->lv3.ckb.len;

out:
- free_cprbmem(mem, PARMBSIZE, 0);
+ free_cprbmem(mem, PARMBSIZE, true, xflags);
return rc;
}
EXPORT_SYMBOL(cca_sec2protkey);
@@ -811,9 +842,11 @@ int cca_gencipherkey(u16 cardnr, u16 domain, u32 keybitsize, u32 keygenflags,
} kb;
} __packed * prepparm;
struct cipherkeytoken *t;
+ const u32 xflags = 0;

/* get already prepared memory for 2 cprbs with param block each */
- rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk);
+ rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem,
+ &preqcblk, &prepcblk, xflags);
if (rc)
return rc;

@@ -872,7 +905,7 @@ int cca_gencipherkey(u16 cardnr, u16 domain, u32 keybitsize, u32 keygenflags,
prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk);

/* forward xcrb with request CPRB and reply CPRB to zcrypt dd */
- rc = zcrypt_send_cprb(&xcrb, 0);
+ rc = zcrypt_send_cprb(&xcrb, xflags);
if (rc) {
ZCRYPT_DBF_ERR("%s zcrypt_send_cprb (cardnr=%d domain=%d) failed, rc=%d\n",
__func__, (int)cardnr, (int)domain, rc);
@@ -923,7 +956,7 @@ int cca_gencipherkey(u16 cardnr, u16 domain, u32 keybitsize, u32 keygenflags,
*keybufsize = t->len;

out:
- free_cprbmem(mem, PARMBSIZE, 0);
+ free_cprbmem(mem, PARMBSIZE, false, xflags);
return rc;
}
EXPORT_SYMBOL(cca_gencipherkey);
@@ -987,9 +1020,11 @@ static int _ip_cprb_helper(u16 cardnr, u16 domain,
} __packed * prepparm;
struct cipherkeytoken *t;
int complete = strncmp(rule_array_2, "COMPLETE", 8) ? 0 : 1;
+ const u32 xflags = 0;

/* get already prepared memory for 2 cprbs with param block each */
- rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk);
+ rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem,
+ &preqcblk, &prepcblk, xflags);
if (rc)
return rc;

@@ -1038,7 +1073,7 @@ static int _ip_cprb_helper(u16 cardnr, u16 domain,
prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk);

/* forward xcrb with request CPRB and reply CPRB to zcrypt dd */
- rc = zcrypt_send_cprb(&xcrb, 0);
+ rc = zcrypt_send_cprb(&xcrb, xflags);
if (rc) {
ZCRYPT_DBF_ERR("%s zcrypt_send_cprb (cardnr=%d domain=%d) failed, rc=%d\n",
__func__, (int)cardnr, (int)domain, rc);
@@ -1077,7 +1112,7 @@ static int _ip_cprb_helper(u16 cardnr, u16 domain,
*key_token_size = t->len;

out:
- free_cprbmem(mem, PARMBSIZE, 0);
+ free_cprbmem(mem, PARMBSIZE, false, xflags);
return rc;
}

@@ -1217,9 +1252,11 @@ int cca_cipher2protkey(u16 cardnr, u16 domain, const u8 *ckey,
} kb;
} __packed * prepparm;
int keytoklen = ((struct cipherkeytoken *)ckey)->len;
+ const u32 xflags = 0;

/* get already prepared memory for 2 cprbs with param block each */
- rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk);
+ rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem,
+ &preqcblk, &prepcblk, xflags);
if (rc)
return rc;

@@ -1249,7 +1286,7 @@ int cca_cipher2protkey(u16 cardnr, u16 domain, const u8 *ckey,
prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk);

/* forward xcrb with request CPRB and reply CPRB to zcrypt dd */
- rc = zcrypt_send_cprb(&xcrb, 0);
+ rc = zcrypt_send_cprb(&xcrb, xflags);
if (rc) {
ZCRYPT_DBF_ERR("%s zcrypt_send_cprb (cardnr=%d domain=%d) failed, rc=%d\n",
__func__, (int)cardnr, (int)domain, rc);
@@ -1323,7 +1360,7 @@ int cca_cipher2protkey(u16 cardnr, u16 domain, const u8 *ckey,
*protkeylen = prepparm->vud.ckb.keylen;

out:
- free_cprbmem(mem, PARMBSIZE, 0);
+ free_cprbmem(mem, PARMBSIZE, true, xflags);
return rc;
}
EXPORT_SYMBOL(cca_cipher2protkey);
@@ -1380,9 +1417,11 @@ int cca_ecc2protkey(u16 cardnr, u16 domain, const u8 *key,
/* followed by a key block */
} __packed * prepparm;
int keylen = ((struct eccprivkeytoken *)key)->len;
+ const u32 xflags = 0;

/* get already prepared memory for 2 cprbs with param block each */
- rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk);
+ rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem,
+ &preqcblk, &prepcblk, xflags);
if (rc)
return rc;

@@ -1412,7 +1451,7 @@ int cca_ecc2protkey(u16 cardnr, u16 domain, const u8 *key,
prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk);

/* forward xcrb with request CPRB and reply CPRB to zcrypt dd */
- rc = zcrypt_send_cprb(&xcrb, 0);
+ rc = zcrypt_send_cprb(&xcrb, xflags);
if (rc) {
ZCRYPT_DBF_ERR("%s zcrypt_send_cprb (cardnr=%d domain=%d) failed, rc=%d\n",
__func__, (int)cardnr, (int)domain, rc);
@@ -1470,7 +1509,7 @@ int cca_ecc2protkey(u16 cardnr, u16 domain, const u8 *key,
*protkeytype = PKEY_KEYTYPE_ECC;

out:
- free_cprbmem(mem, PARMBSIZE, 0);
+ free_cprbmem(mem, PARMBSIZE, true, xflags);
return rc;
}
EXPORT_SYMBOL(cca_ecc2protkey);
@@ -1503,9 +1542,11 @@ int cca_query_crypto_facility(u16 cardnr, u16 domain,
u8 subfunc_code[2];
u8 lvdata[];
} __packed * prepparm;
+ const u32 xflags = 0;

/* get already prepared memory for 2 cprbs with param block each */
- rc = alloc_and_prep_cprbmem(parmbsize, &mem, &preqcblk, &prepcblk);
+ rc = alloc_and_prep_cprbmem(parmbsize, &mem,
+ &preqcblk, &prepcblk, xflags);
if (rc)
return rc;

@@ -1526,7 +1567,7 @@ int cca_query_crypto_facility(u16 cardnr, u16 domain,
prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk);

/* forward xcrb with request CPRB and reply CPRB to zcrypt dd */
- rc = zcrypt_send_cprb(&xcrb, 0);
+ rc = zcrypt_send_cprb(&xcrb, xflags);
if (rc) {
ZCRYPT_DBF_ERR("%s zcrypt_send_cprb (cardnr=%d domain=%d) failed, rc=%d\n",
__func__, (int)cardnr, (int)domain, rc);
@@ -1573,7 +1614,7 @@ int cca_query_crypto_facility(u16 cardnr, u16 domain,
}

out:
- free_cprbmem(mem, parmbsize, 0);
+ free_cprbmem(mem, parmbsize, false, xflags);
return rc;
}
EXPORT_SYMBOL(cca_query_crypto_facility);
@@ -1959,7 +2000,22 @@ int cca_findcard2(u32 **apqns, u32 *nr_apqns, u16 cardnr, u16 domain,
}
EXPORT_SYMBOL(cca_findcard2);

-void __exit zcrypt_ccamisc_exit(void)
+int __init zcrypt_ccamisc_init(void)
+{
+ /* Pre-allocate a small memory pool for cca cprbs. */
+ cprb_mempool = mempool_create_kmalloc_pool(zcrypt_mempool_threshold,
+ CPRB_MEMPOOL_ITEM_SIZE);
+ if (!cprb_mempool) {
+ ZCRYPT_DBF_WARN("%s mempool_create(%d,%d) failed: %ld\n",
+ __func__, zcrypt_mempool_threshold,
+ CPRB_MEMPOOL_ITEM_SIZE, PTR_ERR(cprb_mempool));
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+void zcrypt_ccamisc_exit(void)
{
mkvp_cache_free();
+ mempool_destroy(cprb_mempool);
}
diff --git a/drivers/s390/crypto/zcrypt_ccamisc.h b/drivers/s390/crypto/zcrypt_ccamisc.h
index 26bdca702523..273edf2bb036 100644
--- a/drivers/s390/crypto/zcrypt_ccamisc.h
+++ b/drivers/s390/crypto/zcrypt_ccamisc.h
@@ -272,6 +272,7 @@ struct cca_info {
*/
int cca_get_info(u16 card, u16 dom, struct cca_info *ci, int verify);

+int zcrypt_ccamisc_init(void);
void zcrypt_ccamisc_exit(void);

#endif /* _ZCRYPT_CCAMISC_H_ */
--
2.43.0