Commit e75003a7 authored by Heiko Carstens's avatar Heiko Carstens
Browse files

Merge branch 'zcrypt-no-alloc'

Harald Freudenberger says:

====================
This series of patches has the goal to open up a do-not-allocate
memory path from the callers of the pkey in-kernel api down to
the crypto cards and back.

The asynch in-kernel cipher implementations (and the s390 PAES
cipher implementations are one of them) may be called in a
context where memory allocations which trigger IO is not acceptable.

So this patch series reworks the AP bus code, the zcrypt layer,
the pkey layer and the pkey handlers to respect this situation
by processing a new parameter xflags (execution hints flags).
There is a flag PKEY_XFLAG_NOMEMALLOC which tells the code to
not allocate memory which may lead to IO operations.

To reach this goal, the actual code changes have been differed.
The zcrypt misc functions which need memory for cprb build
use a pre allocated memory pool for this purpose. The findcard()
functions have one temp memory area preallocated and protected
with a mutex. Some smaller data is not allocated any more but went
to the stack instead. The AP bus also uses a pre-allocated
memory pool for building AP message requests.

Note that the PAES implementation still needs to get reworked
to run the protected key derivation in a real asynchronous way.
However, this rework of AP bus, zcrypt and pkey is the base work
required before reconsidering the PAES implementation.

The patch series starts bottom (AP bus) and goes up the call
chain (PKEY). At any time in the patch stack it should compile.
For easier review I tried to have one logic code change by
each patch and thus keep the patches "small".
====================

Link: https://lore.kernel.org/r/20250424133619.16495-1-freude@linux.ibm.com


Signed-off-by: default avatarHeiko Carstens <hca@linux.ibm.com>
parents 7cf58880 f6884295
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -182,14 +182,14 @@ static inline int __paes_keyblob2pkey(const u8 *key, unsigned int keylen,
{
	int i, rc = -EIO;

	/* try three times in case of busy card */
	/* try three times in case of busy card or no mem */
	for (i = 0; rc && i < 3; i++) {
		if (rc == -EBUSY && in_task()) {
		if ((rc == -EBUSY || rc == -ENOMEM) && in_task()) {
			if (msleep_interruptible(1000))
				return -EINTR;
		}
		rc = pkey_key2protkey(key, keylen, pk->protkey, &pk->len,
				      &pk->type);
				      &pk->type, PKEY_XFLAG_NOMEMALLOC);
	}

	return rc;
+14 −1
Original line number Diff line number Diff line
@@ -20,9 +20,22 @@
 * @param key pointer to a buffer containing the key blob
 * @param keylen size of the key blob in bytes
 * @param protkey pointer to buffer receiving the protected key
 * @param xflags additional execution flags (see PKEY_XFLAG_* definitions below)
 *	  As of now the only supported flag is PKEY_XFLAG_NOMEMALLOC.
 * @return 0 on success, negative errno value on failure
 */
int pkey_key2protkey(const u8 *key, u32 keylen,
		     u8 *protkey, u32 *protkeylen, u32 *protkeytype);
		     u8 *protkey, u32 *protkeylen, u32 *protkeytype,
		     u32 xflags);

/*
 * If this flag is given in the xflags parameter, the pkey implementation
 * is not allowed to allocate memory but instead should fall back to use
 * preallocated memory or simple fail with -ENOMEM.
 * This flag is for protected key derive within a cipher or similar
 * which must not allocate memory which would cause io operations - see
 * also the CRYPTO_ALG_ALLOCATES_MEMORY flag in crypto.h.
 */
#define PKEY_XFLAG_NOMEMALLOC 0x0001

#endif /* _KAPI_PKEY_H */
+3 −2
Original line number Diff line number Diff line
@@ -616,7 +616,8 @@ static inline int uv_remove_shared(unsigned long addr)
	return share(addr, UVC_CMD_REMOVE_SHARED_ACCESS);
}

int uv_get_secret_metadata(const u8 secret_id[UV_SECRET_ID_LEN],
int uv_find_secret(const u8 secret_id[UV_SECRET_ID_LEN],
		   struct uv_secret_list *list,
		   struct uv_secret_list_item_hdr *secret);
int uv_retrieve_secret(u16 secret_idx, u8 *buf, size_t buf_size);

+13 −34
Original line number Diff line number Diff line
@@ -782,7 +782,12 @@ static int __init uv_sysfs_init(void)
device_initcall(uv_sysfs_init);

/*
 * Find the secret with the secret_id in the provided list.
 * Locate a secret in the list by its id.
 * @secret_id: search pattern.
 * @list: ephemeral buffer space
 * @secret: output data, containing the secret's metadata.
 *
 * Search for a secret with the given secret_id in the Ultravisor secret store.
 *
 * Context: might sleep.
 */
@@ -803,10 +808,13 @@ static int find_secret_in_page(const u8 secret_id[UV_SECRET_ID_LEN],

/*
 * Do the actual search for `uv_get_secret_metadata`.
 * @secret_id: search pattern.
 * @list: ephemeral buffer space
 * @secret: output data, containing the secret's metadata.
 *
 * Context: might sleep.
 */
static int find_secret(const u8 secret_id[UV_SECRET_ID_LEN],
int uv_find_secret(const u8 secret_id[UV_SECRET_ID_LEN],
		   struct uv_secret_list *list,
		   struct uv_secret_list_item_hdr *secret)
{
@@ -830,36 +838,7 @@ static int find_secret(const u8 secret_id[UV_SECRET_ID_LEN],

	return -ENOENT;
}

/**
 * uv_get_secret_metadata() - get secret metadata for a given secret id.
 * @secret_id: search pattern.
 * @secret: output data, containing the secret's metadata.
 *
 * Search for a secret with the given secret_id in the Ultravisor secret store.
 *
 * Context: might sleep.
 *
 * Return:
 * * %0:	- Found entry; secret->idx and secret->type are valid.
 * * %ENOENT	- No entry found.
 * * %ENODEV:	- Not supported: UV not available or command not available.
 * * %EIO:	- Other unexpected UV error.
 */
int uv_get_secret_metadata(const u8 secret_id[UV_SECRET_ID_LEN],
			   struct uv_secret_list_item_hdr *secret)
{
	struct uv_secret_list *buf;
	int rc;

	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
	if (!buf)
		return -ENOMEM;
	rc = find_secret(secret_id, buf, secret);
	kfree(buf);
	return rc;
}
EXPORT_SYMBOL_GPL(uv_get_secret_metadata);
EXPORT_SYMBOL_GPL(uv_find_secret);

/**
 * uv_retrieve_secret() - get the secret value for the secret index.
+74 −0
Original line number Diff line number Diff line
@@ -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.
 */
@@ -547,6 +569,48 @@ static void ap_poll_thread_stop(void)
#define is_card_dev(x) ((x)->parent == ap_root_device)
#define is_queue_dev(x) ((x)->parent != ap_root_device)

/*
 * ap_init_apmsg() - Initialize ap_message.
 */
int ap_init_apmsg(struct ap_message *ap_msg, u32 flags)
{
	unsigned int maxmsgsize;

	memset(ap_msg, 0, sizeof(*ap_msg));
	ap_msg->flags = flags;

	if (flags & AP_MSG_FLAG_MEMPOOL) {
		ap_msg->msg = mempool_alloc_preallocated(ap_msg_pool);
		if (!ap_msg->msg)
			return -ENOMEM;
		ap_msg->bufsize = AP_DEFAULT_MAX_MSG_SIZE;
		return 0;
	}

	maxmsgsize = atomic_read(&ap_max_msg_size);
	ap_msg->msg = kmalloc(maxmsgsize, GFP_KERNEL);
	if (!ap_msg->msg)
		return -ENOMEM;
	ap_msg->bufsize = maxmsgsize;

	return 0;
}
EXPORT_SYMBOL(ap_init_apmsg);

/*
 * ap_release_apmsg() - Release ap_message.
 */
void ap_release_apmsg(struct ap_message *ap_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);

/**
 * ap_bus_match()
 * @dev: Pointer to device
@@ -2431,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();

@@ -2477,6 +2549,7 @@ static int __init ap_module_init(void)
out_bus:
	bus_unregister(&ap_bus_type);
out:
	mempool_destroy(ap_msg_pool);
	ap_debug_exit();
	return rc;
}
@@ -2487,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();
}

Loading