s390/zcrypt: Introduce cprb mempool for ep11 misc functions
authorHarald Freudenberger <freude@linux.ibm.com>
Thu, 24 Apr 2025 13:36:01 +0000 (15:36 +0200)
committerHeiko Carstens <hca@linux.ibm.com>
Wed, 30 Apr 2025 09:34:01 +0000 (11:34 +0200)
Introduce a cprb mempool for the zcrypt ep11 misc functions
(zcrypt_ep11misc.*) do some preparation rework to support
a do-not-allocate path through some zcrypt ep11 misc functions.

The mempool is controlled by the zcrypt module parameter
"mempool_threshold" which shall control the minimal amount
of memory items for CCA and EP11.

The mempool shall support "mempool_threshold" requests/replies
in parallel which means for EP11 to hold a send and receive
buffer memory per request. Each of this cprb space items is
limited to 8 KB. So by default the mempool consumes
  5 * 2 * 8KB = 80KB

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

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>
Link: https://lore.kernel.org/r/20250424133619.16495-8-freude@linux.ibm.com
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
drivers/s390/crypto/zcrypt_api.c
drivers/s390/crypto/zcrypt_ep11misc.c
drivers/s390/crypto/zcrypt_ep11misc.h

index 57d819966dad3f7e32068e536eba98d8936b94e2..88bf719b4c58ffdf149b4d57f2073769ca00f990 100644 (file)
@@ -2166,6 +2166,10 @@ int __init zcrypt_api_init(void)
        if (rc)
                goto out_ccamisc_init_failed;
 
+       rc = zcrypt_ep11misc_init();
+       if (rc)
+               goto out_ep11misc_init_failed;
+
        /* Register the request sprayer. */
        rc = misc_register(&zcrypt_misc_device);
        if (rc < 0)
@@ -2177,6 +2181,8 @@ int __init zcrypt_api_init(void)
        return 0;
 
 out_misc_register_failed:
+       zcrypt_ep11misc_exit();
+out_ep11misc_init_failed:
        zcrypt_ccamisc_exit();
 out_ccamisc_init_failed:
        zcdn_exit();
index b60e262bcaa384be1679b34328cde2a6d787c399..048582851f253e4a59773f9c7f3d85d6750cc92b 100644 (file)
 #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>
+#include <linux/slab.h>
 #include <asm/zcrypt.h>
 #include <asm/pkey.h>
 #include <crypto/aes.h>
 static const u8 def_iv[16] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
                               0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
 
+/*
+ * Cprb memory pool held for urgent cases where no memory
+ * can be allocated via kmalloc. This pool is only used when
+ * alloc_cprbmem() is called with the xflag ZCRYPT_XFLAG_NOMEMALLOC.
+ */
+#define CPRB_MEMPOOL_ITEM_SIZE (8 * 1024)
+static mempool_t *cprb_mempool;
+
 /* ep11 card info cache */
 struct card_list_entry {
        struct list_head list;
@@ -411,14 +420,20 @@ EXPORT_SYMBOL(ep11_check_aes_key);
 /*
  * Allocate and prepare ep11 cprb plus additional payload.
  */
-static inline struct ep11_cprb *alloc_cprb(size_t payload_len)
+static void *alloc_cprbmem(size_t payload_len, u32 xflags)
 {
        size_t len = sizeof(struct ep11_cprb) + payload_len;
-       struct ep11_cprb *cprb;
+       struct ep11_cprb *cprb = NULL;
 
-       cprb = kzalloc(len, GFP_KERNEL);
+       if (xflags & ZCRYPT_XFLAG_NOMEMALLOC) {
+               if (len <= CPRB_MEMPOOL_ITEM_SIZE)
+                       cprb = mempool_alloc_preallocated(cprb_mempool);
+       } else {
+               cprb = kmalloc(len, GFP_KERNEL);
+       }
        if (!cprb)
                return NULL;
+       memset(cprb, 0, len);
 
        cprb->cprb_len = sizeof(struct ep11_cprb);
        cprb->cprb_ver_id = 0x04;
@@ -429,6 +444,20 @@ static inline struct ep11_cprb *alloc_cprb(size_t payload_len)
        return cprb;
 }
 
+/*
+ * Free ep11 cprb buffer space.
+ */
+static void free_cprbmem(void *mem, size_t payload_len, bool scrub, u32 xflags)
+{
+       if (mem && scrub)
+               memzero_explicit(mem, sizeof(struct ep11_cprb) + payload_len);
+
+       if (xflags & ZCRYPT_XFLAG_NOMEMALLOC)
+               mempool_free(mem, cprb_mempool);
+       else
+               kfree(mem);
+}
+
 /*
  * Some helper functions related to ASN1 encoding.
  * Limited to length info <= 2 byte.
@@ -489,6 +518,7 @@ static inline void prep_urb(struct ep11_urb *u,
                            struct ep11_cprb *req, size_t req_len,
                            struct ep11_cprb *rep, size_t rep_len)
 {
+       memset(u, 0, sizeof(*u));
        u->targets = (u8 __user *)t;
        u->targets_num = nt;
        u->req = (u8 __user *)req;
@@ -605,11 +635,12 @@ static int ep11_query_info(u16 cardnr, u16 domain, u32 query_type,
        } __packed * rep_pl;
        struct ep11_cprb *req = NULL, *rep = NULL;
        struct ep11_target_dev target;
-       struct ep11_urb *urb = NULL;
+       struct ep11_urb urb;
        int api = EP11_API_V1, rc = -ENOMEM;
+       const u32 xflags = 0;
 
        /* request cprb and payload */
-       req = alloc_cprb(sizeof(struct ep11_info_req_pl));
+       req = alloc_cprbmem(sizeof(struct ep11_info_req_pl), xflags);
        if (!req)
                goto out;
        req_pl = (struct ep11_info_req_pl *)(((u8 *)req) + sizeof(*req));
@@ -621,22 +652,19 @@ static int ep11_query_info(u16 cardnr, u16 domain, u32 query_type,
        req_pl->query_subtype_len = sizeof(u32);
 
        /* reply cprb and payload */
-       rep = alloc_cprb(sizeof(struct ep11_info_rep_pl) + buflen);
+       rep = alloc_cprbmem(sizeof(struct ep11_info_rep_pl) + buflen, xflags);
        if (!rep)
                goto out;
        rep_pl = (struct ep11_info_rep_pl *)(((u8 *)rep) + sizeof(*rep));
 
        /* urb and target */
-       urb = kmalloc(sizeof(*urb), GFP_KERNEL);
-       if (!urb)
-               goto out;
        target.ap_id = cardnr;
        target.dom_id = domain;
-       prep_urb(urb, &target, 1,
+       prep_urb(&urb, &target, 1,
                 req, sizeof(*req) + sizeof(*req_pl),
                 rep, sizeof(*rep) + sizeof(*rep_pl) + buflen);
 
-       rc = zcrypt_send_ep11_cprb(urb, 0);
+       rc = zcrypt_send_ep11_cprb(&urb, xflags);
        if (rc) {
                ZCRYPT_DBF_ERR("%s zcrypt_send_ep11_cprb(card=%d dom=%d) failed, rc=%d\n",
                               __func__, (int)cardnr, (int)domain, rc);
@@ -667,9 +695,8 @@ static int ep11_query_info(u16 cardnr, u16 domain, u32 query_type,
        memcpy(buf, ((u8 *)rep_pl) + sizeof(*rep_pl), rep_pl->data_len);
 
 out:
-       kfree(req);
-       kfree(rep);
-       kfree(urb);
+       free_cprbmem(req, 0, false, xflags);
+       free_cprbmem(rep, 0, false, xflags);
        return rc;
 }
 
@@ -823,9 +850,10 @@ static int _ep11_genaeskey(u16 card, u16 domain,
        struct ep11_cprb *req = NULL, *rep = NULL;
        size_t req_pl_size, pinblob_size = 0;
        struct ep11_target_dev target;
-       struct ep11_urb *urb = NULL;
+       struct ep11_urb urb;
        int api, rc = -ENOMEM;
        u8 *p;
+       const u32 xflags = 0;
 
        switch (keybitsize) {
        case 128:
@@ -851,7 +879,7 @@ static int _ep11_genaeskey(u16 card, u16 domain,
                pinblob_size = EP11_PINBLOB_V1_BYTES;
        }
        req_pl_size = sizeof(struct keygen_req_pl) + ASN1TAGLEN(pinblob_size);
-       req = alloc_cprb(req_pl_size);
+       req = alloc_cprbmem(req_pl_size, xflags);
        if (!req)
                goto out;
        req_pl = (struct keygen_req_pl *)(((u8 *)req) + sizeof(*req));
@@ -877,22 +905,19 @@ static int _ep11_genaeskey(u16 card, u16 domain,
        *p++ = pinblob_size;
 
        /* reply cprb and payload */
-       rep = alloc_cprb(sizeof(struct keygen_rep_pl));
+       rep = alloc_cprbmem(sizeof(struct keygen_rep_pl), xflags);
        if (!rep)
                goto out;
        rep_pl = (struct keygen_rep_pl *)(((u8 *)rep) + sizeof(*rep));
 
        /* urb and target */
-       urb = kmalloc(sizeof(*urb), GFP_KERNEL);
-       if (!urb)
-               goto out;
        target.ap_id = card;
        target.dom_id = domain;
-       prep_urb(urb, &target, 1,
+       prep_urb(&urb, &target, 1,
                 req, sizeof(*req) + req_pl_size,
                 rep, sizeof(*rep) + sizeof(*rep_pl));
 
-       rc = zcrypt_send_ep11_cprb(urb, 0);
+       rc = zcrypt_send_ep11_cprb(&urb, xflags);
        if (rc) {
                ZCRYPT_DBF_ERR("%s zcrypt_send_ep11_cprb(card=%d dom=%d) failed, rc=%d\n",
                               __func__, (int)card, (int)domain, rc);
@@ -925,9 +950,8 @@ static int _ep11_genaeskey(u16 card, u16 domain,
        *keybufsize = rep_pl->data_len;
 
 out:
-       kfree(req);
-       kfree(rep);
-       kfree(urb);
+       free_cprbmem(req, 0, false, xflags);
+       free_cprbmem(rep, sizeof(struct keygen_rep_pl), true, xflags);
        return rc;
 }
 
@@ -1000,10 +1024,11 @@ static int ep11_cryptsingle(u16 card, u16 domain,
        } __packed * rep_pl;
        struct ep11_cprb *req = NULL, *rep = NULL;
        struct ep11_target_dev target;
-       struct ep11_urb *urb = NULL;
-       size_t req_pl_size, rep_pl_size;
+       struct ep11_urb urb;
+       size_t req_pl_size, rep_pl_size = 0;
        int n, api = EP11_API_V1, rc = -ENOMEM;
        u8 *p;
+       const u32 xflags = 0;
 
        /* the simple asn1 coding used has length limits */
        if (keysize > 0xFFFF || inbufsize > 0xFFFF)
@@ -1012,7 +1037,7 @@ static int ep11_cryptsingle(u16 card, u16 domain,
        /* request cprb and payload */
        req_pl_size = sizeof(struct crypt_req_pl) + (iv ? 16 : 0)
                + ASN1TAGLEN(keysize) + ASN1TAGLEN(inbufsize);
-       req = alloc_cprb(req_pl_size);
+       req = alloc_cprbmem(req_pl_size, xflags);
        if (!req)
                goto out;
        req_pl = (struct crypt_req_pl *)(((u8 *)req) + sizeof(*req));
@@ -1034,22 +1059,19 @@ static int ep11_cryptsingle(u16 card, u16 domain,
 
        /* reply cprb and payload, assume out data size <= in data size + 32 */
        rep_pl_size = sizeof(struct crypt_rep_pl) + ASN1TAGLEN(inbufsize + 32);
-       rep = alloc_cprb(rep_pl_size);
+       rep = alloc_cprbmem(rep_pl_size, xflags);
        if (!rep)
                goto out;
        rep_pl = (struct crypt_rep_pl *)(((u8 *)rep) + sizeof(*rep));
 
        /* urb and target */
-       urb = kmalloc(sizeof(*urb), GFP_KERNEL);
-       if (!urb)
-               goto out;
        target.ap_id = card;
        target.dom_id = domain;
-       prep_urb(urb, &target, 1,
+       prep_urb(&urb, &target, 1,
                 req, sizeof(*req) + req_pl_size,
                 rep, sizeof(*rep) + rep_pl_size);
 
-       rc = zcrypt_send_ep11_cprb(urb, 0);
+       rc = zcrypt_send_ep11_cprb(&urb, xflags);
        if (rc) {
                ZCRYPT_DBF_ERR("%s zcrypt_send_ep11_cprb(card=%d dom=%d) failed, rc=%d\n",
                               __func__, (int)card, (int)domain, rc);
@@ -1095,9 +1117,8 @@ static int ep11_cryptsingle(u16 card, u16 domain,
        *outbufsize = n;
 
 out:
-       kfree(req);
-       kfree(rep);
-       kfree(urb);
+       free_cprbmem(req, req_pl_size, true, xflags);
+       free_cprbmem(rep, rep_pl_size, true, xflags);
        return rc;
 }
 
@@ -1143,9 +1164,10 @@ static int _ep11_unwrapkey(u16 card, u16 domain,
        struct ep11_cprb *req = NULL, *rep = NULL;
        size_t req_pl_size, pinblob_size = 0;
        struct ep11_target_dev target;
-       struct ep11_urb *urb = NULL;
+       struct ep11_urb urb;
        int api, rc = -ENOMEM;
        u8 *p;
+       const u32 xflags = 0;
 
        /* request cprb and payload */
        api = (!keygenflags || keygenflags & 0x00200000) ?
@@ -1161,7 +1183,7 @@ static int _ep11_unwrapkey(u16 card, u16 domain,
        req_pl_size = sizeof(struct uw_req_pl) + (iv ? 16 : 0)
                + ASN1TAGLEN(keksize) + ASN1TAGLEN(0)
                + ASN1TAGLEN(pinblob_size) + ASN1TAGLEN(enckeysize);
-       req = alloc_cprb(req_pl_size);
+       req = alloc_cprbmem(req_pl_size, xflags);
        if (!req)
                goto out;
        req_pl = (struct uw_req_pl *)(((u8 *)req) + sizeof(*req));
@@ -1197,22 +1219,19 @@ static int _ep11_unwrapkey(u16 card, u16 domain,
        p += asn1tag_write(p, 0x04, enckey, enckeysize);
 
        /* reply cprb and payload */
-       rep = alloc_cprb(sizeof(struct uw_rep_pl));
+       rep = alloc_cprbmem(sizeof(struct uw_rep_pl), xflags);
        if (!rep)
                goto out;
        rep_pl = (struct uw_rep_pl *)(((u8 *)rep) + sizeof(*rep));
 
        /* urb and target */
-       urb = kmalloc(sizeof(*urb), GFP_KERNEL);
-       if (!urb)
-               goto out;
        target.ap_id = card;
        target.dom_id = domain;
-       prep_urb(urb, &target, 1,
+       prep_urb(&urb, &target, 1,
                 req, sizeof(*req) + req_pl_size,
                 rep, sizeof(*rep) + sizeof(*rep_pl));
 
-       rc = zcrypt_send_ep11_cprb(urb, 0);
+       rc = zcrypt_send_ep11_cprb(&urb, xflags);
        if (rc) {
                ZCRYPT_DBF_ERR("%s zcrypt_send_ep11_cprb(card=%d dom=%d) failed, rc=%d\n",
                               __func__, (int)card, (int)domain, rc);
@@ -1245,9 +1264,8 @@ static int _ep11_unwrapkey(u16 card, u16 domain,
        *keybufsize = rep_pl->data_len;
 
 out:
-       kfree(req);
-       kfree(rep);
-       kfree(urb);
+       free_cprbmem(req, req_pl_size, true, xflags);
+       free_cprbmem(rep, sizeof(struct uw_rep_pl), true, xflags);
        return rc;
 }
 
@@ -1319,15 +1337,16 @@ static int _ep11_wrapkey(u16 card, u16 domain,
        } __packed * rep_pl;
        struct ep11_cprb *req = NULL, *rep = NULL;
        struct ep11_target_dev target;
-       struct ep11_urb *urb = NULL;
+       struct ep11_urb urb;
        size_t req_pl_size;
        int api, rc = -ENOMEM;
        u8 *p;
+       const u32 xflags = 0;
 
        /* request cprb and payload */
        req_pl_size = sizeof(struct wk_req_pl) + (iv ? 16 : 0)
                + ASN1TAGLEN(keysize) + 4;
-       req = alloc_cprb(req_pl_size);
+       req = alloc_cprbmem(req_pl_size, xflags);
        if (!req)
                goto out;
        if (!mech || mech == 0x80060001)
@@ -1357,22 +1376,19 @@ static int _ep11_wrapkey(u16 card, u16 domain,
        *p++ = 0;
 
        /* reply cprb and payload */
-       rep = alloc_cprb(sizeof(struct wk_rep_pl));
+       rep = alloc_cprbmem(sizeof(struct wk_rep_pl), xflags);
        if (!rep)
                goto out;
        rep_pl = (struct wk_rep_pl *)(((u8 *)rep) + sizeof(*rep));
 
        /* urb and target */
-       urb = kmalloc(sizeof(*urb), GFP_KERNEL);
-       if (!urb)
-               goto out;
        target.ap_id = card;
        target.dom_id = domain;
-       prep_urb(urb, &target, 1,
+       prep_urb(&urb, &target, 1,
                 req, sizeof(*req) + req_pl_size,
                 rep, sizeof(*rep) + sizeof(*rep_pl));
 
-       rc = zcrypt_send_ep11_cprb(urb, 0);
+       rc = zcrypt_send_ep11_cprb(&urb, xflags);
        if (rc) {
                ZCRYPT_DBF_ERR("%s zcrypt_send_ep11_cprb(card=%d dom=%d) failed, rc=%d\n",
                               __func__, (int)card, (int)domain, rc);
@@ -1405,9 +1421,8 @@ static int _ep11_wrapkey(u16 card, u16 domain,
        *datasize = rep_pl->data_len;
 
 out:
-       kfree(req);
-       kfree(rep);
-       kfree(urb);
+       free_cprbmem(req, req_pl_size, true, xflags);
+       free_cprbmem(rep, sizeof(struct wk_rep_pl), true, xflags);
        return rc;
 }
 
@@ -1459,7 +1474,7 @@ int ep11_clr2keyblob(u16 card, u16 domain, u32 keybitsize, u32 keygenflags,
                            encbuf, encbuflen, 0, def_iv,
                            keybitsize, 0, keybuf, keybufsize, keytype);
        if (rc) {
-               ZCRYPT_DBF_ERR("%s importing key value as new key failed,, rc=%d\n",
+               ZCRYPT_DBF_ERR("%s importing key value as new key failed, rc=%d\n",
                               __func__, rc);
                goto out;
        }
@@ -1658,7 +1673,19 @@ int ep11_findcard2(u32 **apqns, u32 *nr_apqns, u16 cardnr, u16 domain,
 }
 EXPORT_SYMBOL(ep11_findcard2);
 
-void __exit zcrypt_ep11misc_exit(void)
+int __init zcrypt_ep11misc_init(void)
+{
+       /* Pre-allocate a small memory pool for ep11 cprbs. */
+       cprb_mempool = mempool_create_kmalloc_pool(2 * zcrypt_mempool_threshold,
+                                                  CPRB_MEMPOOL_ITEM_SIZE);
+       if (!cprb_mempool)
+               return -ENOMEM;
+
+       return 0;
+}
+
+void zcrypt_ep11misc_exit(void)
 {
        card_cache_free();
+       mempool_destroy(cprb_mempool);
 }
index 9f1bdffdec6898fc8b40961fdd4a057e481bf2cc..a4b98eca8431df0af2050556b22f3cd717115092 100644 (file)
@@ -152,6 +152,7 @@ int ep11_findcard2(u32 **apqns, u32 *nr_apqns, u16 cardnr, u16 domain,
 int ep11_kblob2protkey(u16 card, u16 dom, const u8 *key, u32 keylen,
                       u8 *protkey, u32 *protkeylen, u32 *protkeytype);
 
+int zcrypt_ep11misc_init(void);
 void zcrypt_ep11misc_exit(void);
 
 #endif /* _ZCRYPT_EP11MISC_H_ */