s390/ap: Introduce ap message buffer pool
authorHarald Freudenberger <freude@linux.ibm.com>
Thu, 24 Apr 2025 13:35:57 +0000 (15:35 +0200)
committerHeiko Carstens <hca@linux.ibm.com>
Wed, 30 Apr 2025 09:34:00 +0000 (11:34 +0200)
There is a need for a do-not-allocate-memory path through the AP bus
layer. The pkey layer may be triggered via the in-kernel interface
from a protected key crypto algorithm (namely PAES) to convert a
secure key into a protected key. This happens in a workqueue context,
so sleeping is allowed but memory allocations causing IO operations
are not permitted.

To accomplish this, an AP message memory pool with pre-allocated space
is established. When ap_init_apmsg() with use_mempool set to true is
called, instead of kmalloc() the ap message buffer is allocated from
the ap_msg_pool. This pool only holds a limited amount of buffers:
ap_msg_pool_min_items with the item size AP_DEFAULT_MAX_MSG_SIZE and
exactly one of these items (if available) is returned if
ap_init_apmsg() with the use_mempool arg set to true is called. When
this pool is exhausted and use_mempool is set true, ap_init_apmsg()
returns -ENOMEM without any attempt to allocate memory and the caller
has to deal with that.

Default values for this mempool of ap messages is:
 * Each buffer is 12KB (that is the default AP bus size
   and all the urgent messages should fit into this space).
 * Minimum items held in the pool is 8. This value is adjustable
   via module parameter ap.msgpool_min_items.

The zcrypt layer may use this flag to indicate to the ap bus that the
processing path for this message should not allocate memory but should
use pre-allocated memory buffer instead.  This is to prevent deadlocks
with crypto and io for example with encrypted swap volumes.

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-4-freude@linux.ibm.com
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
drivers/s390/crypto/ap_bus.c
drivers/s390/crypto/ap_bus.h
drivers/s390/crypto/zcrypt_api.c

index 8935b3ee3a331723e447b152394700568bfa8ce1..1e4784e9efd1ac7f8b036462262cd030bd12f8fa 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/module.h>
 #include <asm/uv.h>
 #include <asm/chsc.h>
+#include <linux/mempool.h>
 
 #include "ap_bus.h"
 #include "ap_debug.h"
@@ -102,6 +103,27 @@ static struct ap_config_info *const ap_qci_info_old = &qci[1];
  */
 debug_info_t *ap_dbf_info;
 
+/*
+ * There is a need for a do-not-allocate-memory path through the AP bus
+ * layer. The pkey layer may be triggered via the in-kernel interface from
+ * a protected key crypto algorithm (namely PAES) to convert a secure key
+ * into a protected key. This happens in a workqueue context, so sleeping
+ * is allowed but memory allocations causing IO operations are not permitted.
+ * To accomplish this, an AP message memory pool with pre-allocated space
+ * is established. When ap_init_apmsg() with use_mempool set to true is
+ * called, instead of kmalloc() the ap message buffer is allocated from
+ * the ap_msg_pool. This pool only holds a limited amount of buffers:
+ * ap_msg_pool_min_items with the item size AP_DEFAULT_MAX_MSG_SIZE and
+ * exactly one of these items (if available) is returned if ap_init_apmsg()
+ * with the use_mempool arg set to true is called. When this pool is exhausted
+ * and use_mempool is set true, ap_init_apmsg() returns -ENOMEM without
+ * any attempt to allocate memory and the caller has to deal with that.
+ */
+static mempool_t *ap_msg_pool;
+static unsigned int ap_msg_pool_min_items = 8;
+module_param_named(msgpool_min_items, ap_msg_pool_min_items, uint, 0440);
+MODULE_PARM_DESC(msgpool_min_items, "AP message pool minimal items");
+
 /*
  * AP bus rescan related things.
  */
@@ -550,11 +572,22 @@ static void ap_poll_thread_stop(void)
 /*
  * ap_init_apmsg() - Initialize ap_message.
  */
-int ap_init_apmsg(struct ap_message *ap_msg)
+int ap_init_apmsg(struct ap_message *ap_msg, bool use_mempool)
 {
-       unsigned int maxmsgsize = atomic_read(&ap_max_msg_size);
+       unsigned int maxmsgsize;
 
        memset(ap_msg, 0, sizeof(*ap_msg));
+
+       if (use_mempool) {
+               ap_msg->msg = mempool_alloc_preallocated(ap_msg_pool);
+               if (!ap_msg->msg)
+                       return -ENOMEM;
+               ap_msg->bufsize = AP_DEFAULT_MAX_MSG_SIZE;
+               ap_msg->flags |= AP_MSG_FLAG_MEMPOOL;
+               return 0;
+       }
+
+       maxmsgsize = atomic_read(&ap_max_msg_size);
        ap_msg->msg = kmalloc(maxmsgsize, GFP_KERNEL);
        if (!ap_msg->msg)
                return -ENOMEM;
@@ -569,7 +602,12 @@ EXPORT_SYMBOL(ap_init_apmsg);
  */
 void ap_release_apmsg(struct ap_message *ap_msg)
 {
-       kfree_sensitive(ap_msg->msg);
+       if (ap_msg->flags & AP_MSG_FLAG_MEMPOOL) {
+               memzero_explicit(ap_msg->msg, ap_msg->bufsize);
+               mempool_free(ap_msg->msg, ap_msg_pool);
+       } else {
+               kfree_sensitive(ap_msg->msg);
+       }
 }
 EXPORT_SYMBOL(ap_release_apmsg);
 
@@ -2457,6 +2495,14 @@ static int __init ap_module_init(void)
        /* init ap_queue hashtable */
        hash_init(ap_queues);
 
+       /* create ap msg buffer memory pool */
+       ap_msg_pool = mempool_create_kmalloc_pool(ap_msg_pool_min_items,
+                                                 AP_DEFAULT_MAX_MSG_SIZE);
+       if (!ap_msg_pool) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
        /* set up the AP permissions (ioctls, ap and aq masks) */
        ap_perms_init();
 
@@ -2503,6 +2549,7 @@ out_device:
 out_bus:
        bus_unregister(&ap_bus_type);
 out:
+       mempool_destroy(ap_msg_pool);
        ap_debug_exit();
        return rc;
 }
@@ -2513,6 +2560,7 @@ static void __exit ap_module_exit(void)
        ap_irq_exit();
        root_device_unregister(ap_root_device);
        bus_unregister(&ap_bus_type);
+       mempool_destroy(ap_msg_pool);
        ap_debug_exit();
 }
 
index 483231bcdea6f617b1bfbed7d2a365cb46365cc1..60c41d8116ad3ae4c0871b24c8808296142ed6bd 100644 (file)
@@ -236,8 +236,9 @@ struct ap_message {
 #define AP_MSG_FLAG_SPECIAL  0x0001    /* flag msg as 'special' with NQAP */
 #define AP_MSG_FLAG_USAGE    0x0002    /* CCA, EP11: usage (no admin) msg */
 #define AP_MSG_FLAG_ADMIN    0x0004    /* CCA, EP11: admin (=control) msg */
+#define AP_MSG_FLAG_MEMPOOL  0x0008 /* ap msg buffer allocated via mempool */
 
-int ap_init_apmsg(struct ap_message *ap_msg);
+int ap_init_apmsg(struct ap_message *ap_msg, bool use_mempool);
 void ap_release_apmsg(struct ap_message *ap_msg);
 
 enum ap_sm_wait ap_sm_event(struct ap_queue *aq, enum ap_sm_event event);
index 8c263a9b643db2944bb7f6e9a3e4d285fa2575e3..139bf2b135446e00a0958d81f5bbc68fe69e01dd 100644 (file)
@@ -648,7 +648,7 @@ static long zcrypt_rsa_modexpo(struct ap_perms *perms,
 
        trace_s390_zcrypt_req(mex, TP_ICARSAMODEXPO);
 
-       rc = ap_init_apmsg(&ap_msg);
+       rc = ap_init_apmsg(&ap_msg, false);
        if (rc)
                goto out;
 
@@ -753,7 +753,7 @@ static long zcrypt_rsa_crt(struct ap_perms *perms,
 
        trace_s390_zcrypt_req(crt, TP_ICARSACRT);
 
-       rc = ap_init_apmsg(&ap_msg);
+       rc = ap_init_apmsg(&ap_msg, false);
        if (rc)
                goto out;
 
@@ -861,7 +861,7 @@ static long _zcrypt_send_cprb(bool userspace, struct ap_perms *perms,
 
        xcrb->status = 0;
 
-       rc = ap_init_apmsg(&ap_msg);
+       rc = ap_init_apmsg(&ap_msg, false);
        if (rc)
                goto out;
 
@@ -1045,7 +1045,7 @@ static long _zcrypt_send_ep11_cprb(bool userspace, struct ap_perms *perms,
 
        trace_s390_zcrypt_req(xcrb, TP_ZSENDEP11CPRB);
 
-       rc = ap_init_apmsg(&ap_msg);
+       rc = ap_init_apmsg(&ap_msg, false);
        if (rc)
                goto out;
 
@@ -1219,7 +1219,7 @@ static long zcrypt_rng(char *buffer)
 
        trace_s390_zcrypt_req(buffer, TP_HWRNGCPRB);
 
-       rc = ap_init_apmsg(&ap_msg);
+       rc = ap_init_apmsg(&ap_msg, false);
        if (rc)
                goto out;
        rc = prep_rng_ap_msg(&ap_msg, &func_code, &domain);