diff mbox series

[v1,6/7] fscrypt: add facility to shrink a pool of keys

Message ID 0ec2076fa7ebc3b44aaaab3a0ffb703e853dce28.1681871298.git.sweettea-kernel@dorminy.me (mailing list archive)
State Superseded
Headers show
Series fscrypt: add pooled prepared keys facility | expand

Commit Message

Sweet Tea Dorminy April 19, 2023, 2:42 a.m. UTC
One expected mechanism to size the pools for extent-based encryption is
to allocate one new pooled prepared key when each inode using such is
opened, and to free it when that inode is closed. This provides roughly
equally many active prepared keys as the non-extent-based scheme, one
prepared key per 'leaf' inode for contents encryption.

This change introduces the ability to shrink a prepared key pool by one
key, including deferring freeing until the next prepared key is freed
back to the pool. This avoids issues with stealing a prepared key from
an info and then freeing it while the old info still references it.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/fscrypt_private.h |  4 ++++
 fs/crypto/keysetup.c        | 43 ++++++++++++++++++++++++++++++-------
 2 files changed, 39 insertions(+), 8 deletions(-)
diff mbox series

Patch

diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index 2737545ce4a6..cc6e4a2a942c 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -467,6 +467,10 @@  struct fscrypt_key_pool {
 	struct list_head active_keys;
 	/* Inactive keys available for use */
 	struct list_head free_keys;
+	/* Count of keys currently managed */
+	size_t count;
+	/* Count of keys desired. Oft equal to count, but can be less. */
+	size_t desired;
 };
 
 /*
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index c12a26c81611..57c343b78abc 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -254,6 +254,22 @@  void fscrypt_unlock_key_if_pooled(struct fscrypt_info *ci)
 	mutex_unlock(&pooled_key->mutex);
 }
 
+/*
+ * Free one key from the free list.
+ */
+static void __fscrypt_free_one_free_key(struct fscrypt_key_pool *pool)
+{
+	struct fscrypt_pooled_prepared_key *tmp;
+
+	tmp = list_first_entry(&pool->free_keys,
+			       struct fscrypt_pooled_prepared_key,
+			       pool_link);
+	fscrypt_destroy_prepared_key(NULL, &tmp->prep_key);
+	list_del_init(&tmp->pool_link);
+	kfree(tmp);
+	pool->count--;
+}
+
 static void __fscrypt_return_key_to_pool(struct fscrypt_key_pool *pool,
 					 struct fscrypt_pooled_prepared_key *key)
 {
@@ -261,6 +277,8 @@  static void __fscrypt_return_key_to_pool(struct fscrypt_key_pool *pool,
 	smp_store_release(&key->owner, NULL);
 	list_move(&key->pool_link, &pool->free_keys);
 	mutex_unlock(&key->mutex);
+	if (pool->count > pool->desired)
+		__fscrypt_free_one_free_key(pool);
 	mutex_unlock(&pool->mutex);
 }
 
@@ -306,6 +324,8 @@  static int fscrypt_allocate_new_pooled_key(struct fscrypt_key_pool *pool,
 	mutex_init(&pooled_key->mutex);
 	INIT_LIST_HEAD(&pooled_key->pool_link);
 	mutex_lock(&pool->mutex);
+	pool->count++;
+	pool->desired++;
 	mutex_lock(&pooled_key->mutex);
 	__fscrypt_return_key_to_pool(pool, pooled_key);
 	return 0;
@@ -359,6 +379,18 @@  static int fscrypt_get_key_from_pool(struct fscrypt_key_pool *pool,
 	return 0;
 }
 
+/*
+ * Shrink the pool by one key.
+ */
+static void fscrypt_shrink_key_pool(struct fscrypt_key_pool *pool)
+{
+	mutex_lock(&pool->mutex);
+	pool->desired--;
+	if (!list_empty(&pool->free_keys))
+		__fscrypt_free_one_free_key(pool);
+	mutex_unlock(&pool->mutex);
+}
+
 /*
  * Do initial setup for a particular key pool, allocated as part of an array
  */
@@ -396,14 +428,9 @@  void fscrypt_destroy_key_pool(struct fscrypt_key_pool *pool)
 		list_del_init(&tmp->pool_link);
 		kfree(tmp);
 	}
-	while (!list_empty(&pool->free_keys)) {
-		tmp = list_first_entry(&pool->free_keys,
-				       struct fscrypt_pooled_prepared_key,
-				       pool_link);
-		fscrypt_destroy_prepared_key(NULL, &tmp->prep_key);
-		list_del_init(&tmp->pool_link);
-		kfree(tmp);
-	}
+	while (!list_empty(&pool->free_keys))
+		__fscrypt_free_one_free_key(pool);
+
 	mutex_unlock(&pool->mutex);
 }