Subject: Re: [PATCH v2 1/5] crypto: Introduce crypto_pool

From: Jakub Kicinski <[email-masked]>
Date: Fri, 6 Jan 2023 17:53:26 -0800

On Tue,  3 Jan 2023 18:42:53 +0000 Dmitry Safonov wrote:
> Introduce a per-CPU pool of async crypto requests that can be used
> in bh-disabled contexts (designed with net RX/TX softirqs as users in
> mind). Allocation can sleep and is a slow-path.
> Initial implementation has only ahash as a backend and a fix-sized array
> of possible algorithms used in parallel.
> 
> Signed-off-by: Dmitry Safonov <[email-masked]>

> +config CRYPTO_POOL
> +	tristate "Per-CPU crypto pool"
> +	default n
> +	help
> +	  Per-CPU pool of crypto requests ready for usage in atomic contexts.

Let's make it a hidden symbol? It seems like a low-level library
which gets select'ed, so no point bothering users with questions.

config CRYPTO_POOL
	tristate

that's it.

> +static int crypto_pool_scratch_alloc(void)

This isn't called by anything in this patch..
crypto_pool_alloc_ahash() should call it I'm guessing?

> +{
> +	int cpu;
> +
> +	lockdep_assert_held(&cpool_mutex);
> +
> +	for_each_possible_cpu(cpu) {
> +		void *scratch = per_cpu(crypto_pool_scratch, cpu);
> +
> +		if (scratch)
> +			continue;
> +
> +		scratch = kmalloc_node(scratch_size, GFP_KERNEL,
> +				       cpu_to_node(cpu));
> +		if (!scratch)
> +			return -ENOMEM;
> +		per_cpu(crypto_pool_scratch, cpu) = scratch;
> +	}
> +	return 0;
> +}

> +out_free:
> +	if (!IS_ERR_OR_NULL(hash) && e->needs_key)
> +		crypto_free_ahash(hash);
> +
> +	for_each_possible_cpu(cpu) {
> +		if (*per_cpu_ptr(e->req, cpu) == NULL)
> +			break;
> +		hash = crypto_ahash_reqtfm(*per_cpu_ptr(e->req, cpu));

Could you use a local variable here instead of @hash?
That way you won't need the two separate free_ahash()
one before and one after the loop..

> +		ahash_request_free(*per_cpu_ptr(e->req, cpu));

I think using @req here would be beneficial as well :S

> +		if (e->needs_key) {
> +			crypto_free_ahash(hash);
> +			hash = NULL;
> +		}
> +	}
> +
> +	if (hash)
> +		crypto_free_ahash(hash);

This error handling is tricky as hell, please just add a separate
variable to hold the 

> +out_free_req:
> +	free_percpu(e->req);
> +out_free_alg:
> +	kfree(e->alg);
> +	e->alg = NULL;
> +	return ret;
> +}
> +
> +/**
> + * crypto_pool_alloc_ahash - allocates pool for ahash requests
> + * @alg: name of async hash algorithm
> + */
> +int crypto_pool_alloc_ahash(const char *alg)
> +{
> +	int i, ret;
> +
> +	/* slow-path */
> +	mutex_lock(&cpool_mutex);
> +
> +	for (i = 0; i < cpool_populated; i++) {
> +		if (cpool[i].alg && !strcmp(cpool[i].alg, alg)) {
> +			if (kref_read(&cpool[i].kref) > 0) {

In the current design we can as well resurrect a pool waiting to 
be destroyed, right? Just reinit the ref and we're good.

Otherwise the read() + get() looks quite suspicious to a reader.

> +				kref_get(&cpool[i].kref);
> +				ret = i;
> +				goto out;
> +			} else {
> +				break;
> +			}
> +		}
> +	}
> +
> +	for (i = 0; i < cpool_populated; i++) {
> +		if (!cpool[i].alg)
> +			break;
> +	}
> +	if (i >= CPOOL_SIZE) {
> +		ret = -ENOSPC;
> +		goto out;
> +	}
> +
> +	ret = __cpool_alloc_ahash(&cpool[i], alg);
> +	if (!ret) {
> +		ret = i;
> +		if (i == cpool_populated)
> +			cpool_populated++;
> +	}
> +out:
> +	mutex_unlock(&cpool_mutex);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(crypto_pool_alloc_ahash);

> +/**
> + * crypto_pool_add - increases number of users (refcounter) for a pool
> + * @id: crypto_pool that was previously allocated by crypto_pool_alloc_ahash()
> + */
> +void crypto_pool_add(unsigned int id)
> +{
> +	if (WARN_ON_ONCE(id > cpool_populated || !cpool[id].alg))
> +		return;
> +	kref_get(&cpool[id].kref);
> +}
> +EXPORT_SYMBOL_GPL(crypto_pool_add);
> +
> +/**
> + * crypto_pool_get - disable bh and start using crypto_pool
> + * @id: crypto_pool that was previously allocated by crypto_pool_alloc_ahash()
> + * @c: returned crypto_pool for usage (uninitialized on failure)
> + */
> +int crypto_pool_get(unsigned int id, struct crypto_pool *c)

Is there a precedent somewhere for the _add() and _get() semantics
you're using here? I don't think I've seen _add() for taking a
reference, maybe _get() -> start(), _add() -> _get()?

> +{
> +	struct crypto_pool_ahash *ret = (struct crypto_pool_ahash *)c;
> +
> +	local_bh_disable();
> +	if (WARN_ON_ONCE(id > cpool_populated || !cpool[id].alg)) {
> +		local_bh_enable();
> +		return -EINVAL;
> +	}
> +	ret->req = *this_cpu_ptr(cpool[id].req);
> +	ret->base.scratch = this_cpu_read(crypto_pool_scratch);
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(crypto_pool_get);