[PATCH 6/9] dm crypt: support diskcipher

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch supports dm-crypt to use diskcipher in a specific ivmode
(disk or fmp).
Dm-crypt allocates diskcipher and sets the key on it.
Then, dm-crypt sets diskcipher into BIO and submits the BIO without
any additional data encryption.

Cc: Alasdair Kergon <agk@xxxxxxxxxx>
Cc: Mike Snitzer <snitzer@xxxxxxxxxx>
Cc: dm-devel@xxxxxxxxxx
Signed-off-by: Boojin Kim <boojin.kim@xxxxxxxxxxx>
---
 drivers/md/dm-crypt.c | 112
++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 103 insertions(+), 9 deletions(-)

diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 9f8b654..271cfcc 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -37,6 +37,7 @@
 #include <keys/user-type.h>
 
 #include <linux/device-mapper.h>
+#include <crypto/diskcipher.h>
 
 #define DM_MSG_PREFIX "crypt"
 
@@ -130,6 +131,8 @@ enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID,
 enum cipher_flags {
 	CRYPT_MODE_INTEGRITY_AEAD,	/* Use authenticated mode for cihper
*/
 	CRYPT_IV_LARGE_SECTORS,		/* Calculate IV from sector_size,
not 512B sectors */
+	CRYPT_MODE_DISKCIPHER,
+	CRYPT_MODE_SKCIPHER,
 };
 
 /*
@@ -170,6 +173,7 @@ struct crypt_config {
 	union {
 		struct crypto_skcipher **tfms;
 		struct crypto_aead **tfms_aead;
+		struct crypto_diskcipher **tfms_diskc;
 	} cipher_tfm;
 	unsigned tfms_count;
 	unsigned long cipher_flags;
@@ -955,6 +959,17 @@ static bool crypt_integrity_hmac(struct crypt_config
*cc)
 	return crypt_integrity_aead(cc) && cc->key_mac_size;
 }
 
+static bool crypt_mode_diskcipher(struct crypt_config *cc)
+{
+	return test_bit(CRYPT_MODE_DISKCIPHER, &cc->cipher_flags);
+}
+
+static bool crypt_mode_skcipher(struct crypt_config *cc)
+{
+	return test_bit(CRYPT_MODE_SKCIPHER, &cc->cipher_flags);
+}
+
+
 /* Get sg containing data */
 static struct scatterlist *crypt_get_sg_data(struct crypt_config *cc,
 					     struct scatterlist *sg)
@@ -1573,13 +1588,13 @@ static void crypt_endio(struct bio *clone)
 	/*
 	 * free the processed pages
 	 */
-	if (rw == WRITE)
+	if ((rw == WRITE) && !crypt_mode_diskcipher(cc))
 		crypt_free_buffer_pages(cc, clone);
 
 	error = clone->bi_status;
 	bio_put(clone);
 
-	if (rw == READ && !error) {
+	if (rw == READ && !error && !crypt_mode_diskcipher(cc)) {
 		kcryptd_queue_crypt(io);
 		return;
 	}
@@ -1618,6 +1633,11 @@ static int kcryptd_io_read(struct dm_crypt_io *io,
gfp_t gfp)
 	crypt_inc_pending(io);
 
 	clone_init(io, clone);
+
+	if (crypt_mode_diskcipher(cc))
+		crypto_diskcipher_set(clone,
+			cc->cipher_tfm.tfms_diskc[0], NULL, 0);
+
 	clone->bi_iter.bi_sector = cc->start + io->sector;
 
 	if (dm_crypt_integrity_io_alloc(io, clone)) {
@@ -1907,10 +1927,29 @@ static void crypt_free_tfms_skcipher(struct
crypt_config *cc)
 	cc->cipher_tfm.tfms = NULL;
 }
 
+static void crypt_free_tfms_diskcipher(struct crypt_config *cc)
+{
+	if (!crypt_mode_diskcipher(cc))
+		return;
+
+	if (cc->cipher_tfm.tfms_diskc[0] &&
+		!IS_ERR(cc->cipher_tfm.tfms_diskc[0])) {
+		crypto_diskcipher_clearkey(cc->cipher_tfm.tfms_diskc[0]);
+		crypto_free_diskcipher(cc->cipher_tfm.tfms_diskc[0]);
+		cc->cipher_tfm.tfms_diskc[0] = NULL;
+	}
+
+	kfree(cc->cipher_tfm.tfms_diskc);
+	cc->cipher_tfm.tfms_diskc = NULL;
+}
+
+
 static void crypt_free_tfms(struct crypt_config *cc)
 {
 	if (crypt_integrity_aead(cc))
 		crypt_free_tfms_aead(cc);
+	else if (crypt_mode_diskcipher(cc))
+		crypt_free_tfms_diskcipher(cc);
 	else
 		crypt_free_tfms_skcipher(cc);
 }
@@ -1934,6 +1973,7 @@ static int crypt_alloc_tfms_skcipher(struct
crypt_config *cc, char *ciphermode)
 			return err;
 		}
 	}
+	set_bit(CRYPT_MODE_SKCIPHER, &cc->cipher_flags);
 
 	/*
 	 * dm-crypt performance can vary greatly depending on which crypto
@@ -1965,10 +2005,34 @@ static int crypt_alloc_tfms_aead(struct crypt_config
*cc, char *ciphermode)
 	return 0;
 }
 
+static int crypt_alloc_tfms_diskcipher(struct crypt_config *cc,
+				char *ciphermode)
+{
+	int err;
+
+	cc->cipher_tfm.tfms = kmalloc(sizeof(struct crypto_aead *),
GFP_KERNEL);
+	if (!cc->cipher_tfm.tfms)
+		return -ENOMEM;
+
+	cc->cipher_tfm.tfms_diskc[0] =
+	    crypto_alloc_diskcipher(ciphermode, 0, 0, 1);
+	if (IS_ERR(cc->cipher_tfm.tfms_diskc[0])) {
+		err = PTR_ERR(cc->cipher_tfm.tfms_diskc[0]);
+		crypt_free_tfms(cc);
+		pr_err("%s: no diskcipher with %s\n", __func__, ciphermode);
+		return err;
+	}
+	pr_info("%s is done with %s\n", __func__, ciphermode);
+
+	return 0;
+}
+
 static int crypt_alloc_tfms(struct crypt_config *cc, char *ciphermode)
 {
 	if (crypt_integrity_aead(cc))
 		return crypt_alloc_tfms_aead(cc, ciphermode);
+	else if (crypt_mode_diskcipher(cc))
+		return crypt_alloc_tfms_diskcipher(cc, ciphermode);
 	else
 		return crypt_alloc_tfms_skcipher(cc, ciphermode);
 }
@@ -2030,6 +2094,11 @@ static int crypt_setkey(struct crypt_config *cc)
 			r = crypto_aead_setkey(cc->cipher_tfm.tfms_aead[i],
 					       cc->key + (i * subkey_size),
 					       subkey_size);
+		else if (crypt_mode_diskcipher(cc))
+			r = crypto_diskcipher_setkey(
+
cc->cipher_tfm.tfms_diskc[i],
+						cc->key + (i * subkey_size),
+						subkey_size, 1);
 		else
 			r = crypto_skcipher_setkey(cc->cipher_tfm.tfms[i],
 						   cc->key + (i *
subkey_size),
@@ -2510,7 +2579,7 @@ static int crypt_ctr_cipher_new(struct dm_target *ti,
char *cipher_in, char *key
 			return -ENOMEM;
 		}
 		cc->iv_size = crypto_aead_ivsize(any_tfm_aead(cc));
-	} else
+	} else if (crypt_mode_skcipher(cc))
 		cc->iv_size = crypto_skcipher_ivsize(any_tfm(cc));
 
 	ret = crypt_ctr_blkdev_cipher(cc);
@@ -2560,6 +2629,9 @@ static int crypt_ctr_cipher_old(struct dm_target *ti,
char *cipher_in, char *key
 	chainmode = strsep(&tmp, "-");
 	*ivmode = strsep(&tmp, ":");
 	*ivopts = tmp;
+	if (*ivmode)
+		if (!strcmp(*ivmode, "disk") || !strcmp(*ivmode, "fmp"))
+			set_bit(CRYPT_MODE_DISKCIPHER, &cc->cipher_flags);
 
 	/*
 	 * For compatibility with the original dm-crypt mapping format, if
@@ -2621,9 +2693,11 @@ static int crypt_ctr_cipher(struct dm_target *ti,
char *cipher_in, char *key)
 		return ret;
 
 	/* Initialize IV */
-	ret = crypt_ctr_ivmode(ti, ivmode);
-	if (ret < 0)
-		return ret;
+	if (!crypt_mode_diskcipher(cc)) {
+		ret = crypt_ctr_ivmode(ti, ivmode);
+		if (ret < 0)
+			return ret;
+	}
 
 	/* Initialize and set key */
 	ret = crypt_set_key(cc, key);
@@ -2654,6 +2728,11 @@ static int crypt_ctr_cipher(struct dm_target *ti,
char *cipher_in, char *key)
 	if (cc->key_string)
 		memset(cc->key, 0, cc->key_size * sizeof(u8));
 
+	pr_info("%s with ivmode:%s, ivopts:%s, aead:%d, diskcipher:%d(%p),
skcipher:%d\n",
+			__func__, ivmode, ivopts, crypt_integrity_aead(cc),
+			crypt_mode_diskcipher(cc),
cc->cipher_tfm.tfms_diskc[0],
+			crypt_mode_skcipher(cc));
+
 	return ret;
 }
 
@@ -2788,11 +2867,15 @@ static int crypt_ctr(struct dm_target *ti, unsigned
int argc, char **argv)
 	ret = crypt_ctr_cipher(ti, argv[0], argv[1]);
 	if (ret < 0)
 		goto bad;
-
 	if (crypt_integrity_aead(cc)) {
 		cc->dmreq_start = sizeof(struct aead_request);
 		cc->dmreq_start += crypto_aead_reqsize(any_tfm_aead(cc));
 		align_mask = crypto_aead_alignmask(any_tfm_aead(cc));
+	} else if (crypt_mode_diskcipher(cc)) {
+		cc->per_bio_data_size = ti->per_io_data_size =
+			ALIGN(sizeof(struct dm_crypt_io),
+			ARCH_KMALLOC_MINALIGN);
+		goto get_bio;
 	} else {
 		cc->dmreq_start = sizeof(struct skcipher_request);
 		cc->dmreq_start += crypto_skcipher_reqsize(any_tfm(cc));
@@ -2836,6 +2919,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned
int argc, char **argv)
 		goto bad;
 	}
 
+get_bio:
 	ret = bioset_init(&cc->bs, MIN_IOS, 0, BIOSET_NEED_BVECS);
 	if (ret) {
 		ti->error = "Cannot allocate crypt bioset";
@@ -2893,6 +2977,12 @@ static int crypt_ctr(struct dm_target *ti, unsigned
int argc, char **argv)
 		goto bad;
 	}
 
+	if (crypt_mode_diskcipher(cc)) {
+		cc->crypt_queue = NULL;
+		cc->write_thread = NULL;
+		goto out;
+	}
+
 	if (test_bit(DM_CRYPT_SAME_CPU, &cc->flags))
 		cc->crypt_queue = alloc_workqueue("kcryptd/%s",
 						  WQ_HIGHPRI |
WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM,
@@ -2918,6 +3008,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned
int argc, char **argv)
 	}
 	wake_up_process(cc->write_thread);
 
+out:
 	ti->num_flush_bios = 1;
 
 	return 0;
@@ -2981,10 +3072,10 @@ static int crypt_map(struct dm_target *ti, struct
bio *bio)
 
 	if (crypt_integrity_aead(cc))
 		io->ctx.r.req_aead = (struct aead_request *)(io + 1);
-	else
+	else if (crypt_mode_skcipher(cc))
 		io->ctx.r.req = (struct skcipher_request *)(io + 1);
 
-	if (bio_data_dir(io->base_bio) == READ) {
+	if ((bio_data_dir(io->base_bio) == READ) ||
crypt_mode_diskcipher(cc)) {
 		if (kcryptd_io_read(io, GFP_NOWAIT))
 			kcryptd_queue_read(io);
 	} else
@@ -3143,6 +3234,9 @@ static void crypt_io_hints(struct dm_target *ti,
struct queue_limits *limits)
 	limits->physical_block_size =
 		max_t(unsigned, limits->physical_block_size,
cc->sector_size);
 	limits->io_min = max_t(unsigned, limits->io_min, cc->sector_size);
+
+	if (crypt_mode_diskcipher(cc))
+		limits->logical_block_size = PAGE_SIZE;
 }
 
 static struct target_type crypt_target = {
-- 
2.7.4




[Index of Archives]     [Linux RAID]     [Linux SCSI]     [Linux ATA RAID]     [IDE]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Device Mapper]

  Powered by Linux