diff --git a/block/blk-core.c b/block/blk-core.c
index 27fb1357ad4b..80fed8c794d7 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -474,6 +474,10 @@ struct request_queue *blk_alloc_queue(int node_id, bool alloc_srcu)
 				PERCPU_REF_INIT_ATOMIC, GFP_KERNEL))
 		goto fail_stats;
 
+#ifdef CONFIG_BLK_INLINE_ENCRYPTION
+	atomic_set(&q->blk_key_use_count, 0);
+#endif
+
 	blk_queue_dma_alignment(q, 511);
 	blk_set_default_limits(&q->limits);
 	q->nr_requests = BLKDEV_DEFAULT_RQ;
diff --git a/block/blk-crypto-internal.h b/block/blk-crypto-internal.h
index e6818ffaddbf..33a162697e53 100644
--- a/block/blk-crypto-internal.h
+++ b/block/blk-crypto-internal.h
@@ -65,6 +65,11 @@ static inline bool blk_crypto_rq_is_encrypted(struct request *rq)
 	return rq->crypt_ctx;
 }
 
+bool blk_crypto_bio_set_default_ctx(struct bio *bio);
+
+int blk_crypto_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
+		     void __user *argp);
+
 #else /* CONFIG_BLK_INLINE_ENCRYPTION */
 
 static inline int blk_crypto_sysfs_register(struct request_queue *q)
@@ -105,6 +110,17 @@ static inline bool blk_crypto_rq_is_encrypted(struct request *rq)
 	return false;
 }
 
+static bool blk_crypto_bio_set_default_ctx(struct bio *bio)
+{
+	return false;
+}
+
+static inline int blk_crypto_ioctl(struct block_device *bdev, fmode_t mode,
+				   unsigned int cmd, void __user *argp)
+{
+	return -ENOTTY;
+}
+
 #endif /* CONFIG_BLK_INLINE_ENCRYPTION */
 
 void __bio_crypt_advance(struct bio *bio, unsigned int bytes);
@@ -134,7 +150,8 @@ static inline void bio_crypt_do_front_merge(struct request *rq,
 bool __blk_crypto_bio_prep(struct bio **bio_ptr);
 static inline bool blk_crypto_bio_prep(struct bio **bio_ptr)
 {
-	if (bio_has_crypt_ctx(*bio_ptr))
+	if (bio_has_crypt_ctx(*bio_ptr) ||
+	    blk_crypto_bio_set_default_ctx(*bio_ptr))
 		return __blk_crypto_bio_prep(bio_ptr);
 	return true;
 }
diff --git a/block/blk-crypto-profile.c b/block/blk-crypto-profile.c
index 96c511967386..4ca75520b466 100644
--- a/block/blk-crypto-profile.c
+++ b/block/blk-crypto-profile.c
@@ -459,6 +459,7 @@ bool blk_crypto_register(struct blk_crypto_profile *profile,
 		return false;
 	}
 	q->crypto_profile = profile;
+	q->blk_key = NULL;
 	return true;
 }
 EXPORT_SYMBOL_GPL(blk_crypto_register);
diff --git a/block/blk-crypto.c b/block/blk-crypto.c
index a496aaef85ba..39129d8b120f 100644
--- a/block/blk-crypto.c
+++ b/block/blk-crypto.c
@@ -98,20 +98,30 @@ void bio_crypt_set_ctx(struct bio *bio, const struct blk_crypto_key *key,
 	memcpy(bc->bc_dun, dun, sizeof(bc->bc_dun));
 
 	bio->bi_crypt_context = bc;
+	if (bc->bc_key->q)
+		atomic_inc(&bc->bc_key->q->blk_key_use_count);
 }
 
 void __bio_crypt_free_ctx(struct bio *bio)
 {
+	struct request_queue *q = bio->bi_crypt_context->bc_key->q;
+
+	if (q)
+		atomic_dec(&q->blk_key_use_count);
 	mempool_free(bio->bi_crypt_context, bio_crypt_ctx_pool);
 	bio->bi_crypt_context = NULL;
 }
 
 int __bio_crypt_clone(struct bio *dst, struct bio *src, gfp_t gfp_mask)
 {
+	struct request_queue *q = src->bi_crypt_context->bc_key->q;
+
 	dst->bi_crypt_context = mempool_alloc(bio_crypt_ctx_pool, gfp_mask);
 	if (!dst->bi_crypt_context)
 		return -ENOMEM;
 	*dst->bi_crypt_context = *src->bi_crypt_context;
+	if (q)
+		atomic_inc(&q->blk_key_use_count);
 	return 0;
 }
 
@@ -413,3 +423,149 @@ int blk_crypto_evict_key(struct request_queue *q,
 	return blk_crypto_fallback_evict_key(key);
 }
 EXPORT_SYMBOL_GPL(blk_crypto_evict_key);
+
+bool blk_crypto_bio_set_default_ctx(struct bio *bio)
+{
+	struct request_queue *q = bdev_get_queue(bio->bi_bdev);
+	u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE];
+
+	if (!q->blk_key)
+		return false;
+
+	if (bio_op(bio) != REQ_OP_READ && bio_op(bio) != REQ_OP_WRITE)
+		return false;
+
+	if (!bio_sectors(bio))
+		return false;
+
+	memset(dun, 0, BLK_CRYPTO_MAX_IV_SIZE);
+	dun[0] = bio->bi_iter.bi_sector >>
+			(q->blk_key->data_unit_size_bits - SECTOR_SHIFT);
+	bio_crypt_set_ctx(bio, q->blk_key, dun, GFP_KERNEL);
+
+	return true;
+}
+
+static int blk_crypto_destroy_default_key(struct request_queue *q)
+{
+	int ret;
+
+	if (!q->blk_key)
+		return 0;
+
+	blk_mq_freeze_queue(q);
+	blk_mq_quiesce_queue(q);
+	if (atomic_read(&q->blk_key_use_count)) {
+		ret = -EBUSY;
+	} else {
+		ret = blk_crypto_evict_key(q, q->blk_key);
+		if (!ret) {
+			kfree_sensitive(q->blk_key);
+			q->blk_key = NULL;
+		}
+	}
+	blk_mq_unquiesce_queue(q);
+	blk_mq_unfreeze_queue(q);
+
+	return ret;
+}
+
+static int blk_crypto_ioctl_create_key(struct request_queue *q,
+				       void __user *argp)
+{
+	struct blk_crypto_set_key_arg arg;
+	u8 raw_key[BLK_CRYPTO_MAX_KEY_SIZE];
+	struct blk_crypto_key *blk_key;
+	int ret;
+
+	if (copy_from_user(&arg, argp, sizeof(arg)))
+		return -EFAULT;
+
+	if (memchr_inv(arg.reserved, 0, sizeof(arg.reserved)))
+		return -EINVAL;
+
+	if (!arg.raw_key_size)
+		return blk_crypto_destroy_default_key(q);
+
+	if (q->blk_key) {
+		pr_err("Crypto key is already configured\n");
+		return -EBUSY;
+	}
+
+	if (arg.raw_key_size > sizeof(raw_key))
+		return -EINVAL;
+
+	if (arg.data_unit_size > queue_logical_block_size(q)) {
+		pr_err("Data unit size is bigger than logical block size\n");
+		return -EINVAL;
+	}
+
+	if (copy_from_user(raw_key, u64_to_user_ptr(arg.raw_key_ptr),
+			   arg.raw_key_size)) {
+		ret = -EFAULT;
+		goto err;
+	}
+
+	blk_key = kzalloc(sizeof(*blk_key), GFP_KERNEL);
+	if (!blk_key) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	ret = blk_crypto_init_key(blk_key, raw_key, arg.crypto_mode,
+				  sizeof(u64), arg.data_unit_size);
+	if (ret) {
+		pr_err("Failed to init inline encryption key\n");
+		goto key_err;
+	}
+
+	/* Block key size is taken from crypto mode */
+	if (arg.raw_key_size != blk_key->size) {
+		ret = -EINVAL;
+		goto key_err;
+	}
+
+	ret = blk_crypto_start_using_key(blk_key, q);
+	if (ret) {
+		pr_err("Failed to use inline encryption key\n");
+		goto key_err;
+	}
+	blk_key->q = q;
+	q->blk_key = blk_key;
+
+	memzero_explicit(raw_key, sizeof(raw_key));
+
+	return 0;
+
+key_err:
+	kfree_sensitive(blk_key);
+err:
+	memzero_explicit(raw_key, sizeof(raw_key));
+	return ret;
+}
+
+int blk_crypto_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
+		     void __user *argp)
+{
+	struct request_queue *q = bdev_get_queue(bdev);
+	int ret;
+
+	if (!q->crypto_profile)
+		return -EOPNOTSUPP;
+
+	switch (cmd) {
+	case BLKCRYPTOSETKEY:
+		if (mode & FMODE_EXCL)
+			return blk_crypto_ioctl_create_key(q, argp);
+
+		if (IS_ERR(blkdev_get_by_dev(bdev->bd_dev, mode | FMODE_EXCL,
+					     &bdev)))
+			return -EBUSY;
+		ret = blk_crypto_ioctl_create_key(q, argp);
+		blkdev_put(bdev, mode | FMODE_EXCL);
+
+		return ret;
+	default:
+		return -ENOTTY;
+	}
+}
diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c
index 9b905e9443e4..2d28b17393db 100644
--- a/block/blk-sysfs.c
+++ b/block/blk-sysfs.c
@@ -765,6 +765,13 @@ static void blk_release_queue(struct kobject *kobj)
 
 	might_sleep();
 
+#ifdef CONFIG_BLK_INLINE_ENCRYPTION
+	if (q->blk_key) {
+		blk_crypto_evict_key(q, q->blk_key);
+		kfree_sensitive(q->blk_key);
+	}
+#endif
+
 	percpu_ref_exit(&q->q_usage_counter);
 
 	if (q->poll_stat)
diff --git a/block/ioctl.c b/block/ioctl.c
index 46949f1b0dba..b5358819de6a 100644
--- a/block/ioctl.c
+++ b/block/ioctl.c
@@ -12,6 +12,7 @@
 #include <linux/pr.h>
 #include <linux/uaccess.h>
 #include "blk.h"
+#include "blk-crypto-internal.h"
 
 static int blkpg_do_ioctl(struct block_device *bdev,
 			  struct blkpg_partition __user *upart, int op)
@@ -532,6 +533,8 @@ static int blkdev_common_ioctl(struct block_device *bdev, fmode_t mode,
 	case BLKTRACESTOP:
 	case BLKTRACETEARDOWN:
 		return blk_trace_ioctl(bdev, cmd, argp);
+	case BLKCRYPTOSETKEY:
+		return blk_crypto_ioctl(bdev, mode, cmd, argp);
 	case IOC_PR_REGISTER:
 		return blkdev_pr_register(bdev, argp);
 	case IOC_PR_RESERVE:
diff --git a/include/linux/blk-crypto.h b/include/linux/blk-crypto.h
index 69b24fe92cbf..cea421c9374f 100644
--- a/include/linux/blk-crypto.h
+++ b/include/linux/blk-crypto.h
@@ -7,14 +7,7 @@
 #define __LINUX_BLK_CRYPTO_H
 
 #include <linux/types.h>
-
-enum blk_crypto_mode_num {
-	BLK_ENCRYPTION_MODE_INVALID,
-	BLK_ENCRYPTION_MODE_AES_256_XTS,
-	BLK_ENCRYPTION_MODE_AES_128_CBC_ESSIV,
-	BLK_ENCRYPTION_MODE_ADIANTUM,
-	BLK_ENCRYPTION_MODE_MAX,
-};
+#include <uapi/linux/blk-crypto.h>
 
 #define BLK_CRYPTO_MAX_KEY_SIZE		64
 /**
@@ -39,6 +32,7 @@ struct blk_crypto_config {
  * @data_unit_size_bits: log2 of data_unit_size
  * @size: size of this key in bytes (determined by @crypto_cfg.crypto_mode)
  * @raw: the raw bytes of this key.  Only the first @size bytes are used.
+ * @q: if set, this key is used as a default key for this queue.
  *
  * A blk_crypto_key is immutable once created, and many bios can reference it at
  * the same time.  It must not be freed until all bios using it have completed
@@ -49,6 +43,7 @@ struct blk_crypto_key {
 	unsigned int data_unit_size_bits;
 	unsigned int size;
 	u8 raw[BLK_CRYPTO_MAX_KEY_SIZE];
+	struct request_queue *q;
 };
 
 #define BLK_CRYPTO_MAX_IV_SIZE		32
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index dd552479a15c..b45b13ea2f7d 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -429,6 +429,8 @@ struct request_queue {
 #ifdef CONFIG_BLK_INLINE_ENCRYPTION
 	struct blk_crypto_profile *crypto_profile;
 	struct kobject *crypto_kobject;
+	struct blk_crypto_key *blk_key;
+	atomic_t blk_key_use_count;
 #endif
 
 	unsigned int		rq_timeout;
diff --git a/include/uapi/linux/blk-crypto.h b/include/uapi/linux/blk-crypto.h
new file mode 100644
index 000000000000..5a65ebaf6038
--- /dev/null
+++ b/include/uapi/linux/blk-crypto.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+
+#ifndef __UAPI_LINUX_BLK_CRYPTO_H
+#define __UAPI_LINUX_BLK_CRYPTO_H
+
+enum blk_crypto_mode_num {
+	BLK_ENCRYPTION_MODE_INVALID,
+	BLK_ENCRYPTION_MODE_AES_256_XTS,
+	BLK_ENCRYPTION_MODE_AES_128_CBC_ESSIV,
+	BLK_ENCRYPTION_MODE_ADIANTUM,
+	BLK_ENCRYPTION_MODE_MAX,
+};
+
+#endif /* __UAPI_LINUX_BLK_CRYPTO_H */
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index bdf7b404b3e7..398a77895e96 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -121,6 +121,14 @@ struct fsxattr {
 	unsigned char	fsx_pad[8];
 };
 
+struct blk_crypto_set_key_arg {
+	__u32 crypto_mode;
+	__u64 raw_key_ptr;
+	__u32 raw_key_size;
+	__u32 data_unit_size;
+	__u32 reserved[9];	/* must be zero */
+};
+
 /*
  * Flags for the fsx_xflags field
  */
@@ -185,6 +193,7 @@ struct fsxattr {
 #define BLKROTATIONAL _IO(0x12,126)
 #define BLKZEROOUT _IO(0x12,127)
 #define BLKGETDISKSEQ _IOR(0x12,128,__u64)
+#define BLKCRYPTOSETKEY _IOW(0x12, 129, struct blk_crypto_set_key_arg)
 /*
  * A jump here: 130-136 are reserved for zoned block devices
  * (see uapi/linux/blkzoned.h)
