diff mbox series

[v1,5/7] fscrypt: reclaim pooled prepared keys under pressure

Message ID 4513ae7360866fee4bbc2f800f77d0b74a9fa3ec.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
While it is to be hoped that the pools of prepared keys are sized
appropriately, there is no way to guarantee that they will never have
pressure short of expanding the pool for every user -- and that defeats
the goal of not allocating a prepared key for every user. So, a
mechanism to 'steal' a prepared key from its current owner must be
added, to support another info using the prepared key.

This adds that stealing mechanism. Currently, on pressure, the head of
the active prepared key list is stolen; this selection mechanism is
likely suboptimal, and is improved in a later change.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/keysetup.c | 47 +++++++++++++++++++++++++++++++-------------
 1 file changed, 33 insertions(+), 14 deletions(-)
diff mbox series

Patch

diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index aed60280ad32..c12a26c81611 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -260,8 +260,8 @@  static void __fscrypt_return_key_to_pool(struct fscrypt_key_pool *pool,
 	/* Pairs with the acquire in get_pooled_key_owner() */
 	smp_store_release(&key->owner, NULL);
 	list_move(&key->pool_link, &pool->free_keys);
-	mutex_unlock(&pool->mutex);
 	mutex_unlock(&key->mutex);
+	mutex_unlock(&pool->mutex);
 }
 
 static void fscrypt_return_key_to_pool(struct fscrypt_info *ci)
@@ -269,17 +269,20 @@  static void fscrypt_return_key_to_pool(struct fscrypt_info *ci)
 	struct fscrypt_pooled_prepared_key *pooled_key;
 	const u8 mode_num = ci->ci_mode - fscrypt_modes;
 	struct fscrypt_master_key *mk = ci->ci_master_key;
+	struct fscrypt_key_pool *pool = &mk->mk_key_pools[mode_num];
 
 	mutex_lock(&pool->mutex);
 	/* Try to lock the key. If we don't, it's already been stolen. */
-	if (fscrypt_lock_pooled_key(ci) != 0)
+	if (fscrypt_lock_pooled_key(ci) != 0) {
+		mutex_unlock(&pool->mutex);
 		return;
+	}
 
 	pooled_key = container_of(ci->ci_enc_key,
 				  struct fscrypt_pooled_prepared_key,
 				  prep_key);
 
-	__fscrypt_return_key_to_pool(&mk->mk_key_pools[mode_num], pooled_key);
+	__fscrypt_return_key_to_pool(pool, pooled_key);
 }
 
 static int fscrypt_allocate_new_pooled_key(struct fscrypt_key_pool *pool,
@@ -302,13 +305,33 @@  static int fscrypt_allocate_new_pooled_key(struct fscrypt_key_pool *pool,
 	pooled_key->prep_key.type = FSCRYPT_KEY_POOLED;
 	mutex_init(&pooled_key->mutex);
 	INIT_LIST_HEAD(&pooled_key->pool_link);
+	mutex_lock(&pool->mutex);
 	mutex_lock(&pooled_key->mutex);
 	__fscrypt_return_key_to_pool(pool, pooled_key);
 	return 0;
 }
 
+static struct fscrypt_pooled_prepared_key *
+fscrypt_select_victim_key(struct fscrypt_key_pool *pool)
+{
+	return list_first_entry(&pool->active_keys,
+				struct fscrypt_pooled_prepared_key, pool_link);
+}
+
+static void fscrypt_steal_an_active_key(struct fscrypt_key_pool *pool)
+{
+	struct fscrypt_pooled_prepared_key *key;
+
+	key = fscrypt_select_victim_key(pool);
+	mutex_lock(&key->mutex);
+	/* Pairs with the acquire in get_pooled_key_owner() */
+	smp_store_release(&key->owner, NULL);
+	list_move(&key->pool_link, &pool->free_keys);
+	mutex_unlock(&key->mutex);
+}
+
 /*
- * Gets a key out of the free list, and locks it for use.
+ * Gets a key out of the free list, possibly stealing it along the way.
  */
 static int fscrypt_get_key_from_pool(struct fscrypt_key_pool *pool,
 				     struct fscrypt_info *ci)
@@ -316,10 +339,14 @@  static int fscrypt_get_key_from_pool(struct fscrypt_key_pool *pool,
 	struct fscrypt_pooled_prepared_key *key;
 
 	mutex_lock(&pool->mutex);
-	if (WARN_ON_ONCE(list_empty(&pool->free_keys))) {
+	if (list_empty(&pool->free_keys) && list_empty(&pool->active_keys)) {
 		mutex_unlock(&pool->mutex);
-		return -EBUSY;
+		return -EINVAL;
 	}
+
+	if (list_empty(&pool->free_keys))
+		fscrypt_steal_an_active_key(pool);
+
 	key = list_first_entry(&pool->free_keys,
 			       struct fscrypt_pooled_prepared_key, pool_link);
 
@@ -959,16 +986,8 @@  fscrypt_setup_encryption_info(struct inode *inode,
 		res = fscrypt_setup_file_key(crypt_info, mk);
 		if (res)
 			goto out;
-	} else {
-		const u8 mode_num = mode - fscrypt_modes;
-		struct fscrypt_key_pool *pool = &mk->mk_key_pools[mode_num];
-
-		res = fscrypt_allocate_new_pooled_key(pool, mode);
-		if (res)
-			return res;
 	}
 
-
 	/*
 	 * Derive a secret dirhash key for directories that need it. It
 	 * should be impossible to set flags such that a v1 policy sets