svn commit: r220922 - in head/sys: geom/eli modules/geom/geom_eli

Pawel Jakub Dawidek pjd at FreeBSD.org
Thu Apr 21 13:31:43 UTC 2011


Author: pjd
Date: Thu Apr 21 13:31:43 2011
New Revision: 220922
URL: http://svn.freebsd.org/changeset/base/220922

Log:
  Instead of allocating memory for all the keys at device attach,
  create reasonably large cache for the keys that is filled when
  needed. The previous version was problematic for very large providers
  (hundreds of terabytes or serval petabytes). Every terabyte of data
  needs around 256kB for keys. Make the default cache limit big enough
  to fit all the keys needed for 4TB providers, which will eat at most
  1MB of memory.
  
  MFC after:	2 weeks

Added:
  head/sys/geom/eli/g_eli_key_cache.c   (contents, props changed)
Modified:
  head/sys/geom/eli/g_eli.c
  head/sys/geom/eli/g_eli.h
  head/sys/geom/eli/g_eli_ctl.c
  head/sys/geom/eli/g_eli_integrity.c
  head/sys/geom/eli/g_eli_key.c
  head/sys/geom/eli/g_eli_privacy.c
  head/sys/modules/geom/geom_eli/Makefile

Modified: head/sys/geom/eli/g_eli.c
==============================================================================
--- head/sys/geom/eli/g_eli.c	Thu Apr 21 12:38:12 2011	(r220921)
+++ head/sys/geom/eli/g_eli.c	Thu Apr 21 13:31:43 2011	(r220922)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2005-2010 Pawel Jakub Dawidek <pjd at FreeBSD.org>
+ * Copyright (c) 2005-2011 Pawel Jakub Dawidek <pawel at dawidek.net>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -329,7 +329,7 @@ g_eli_newsession(struct g_eli_worker *wr
 	crie.cri_klen = sc->sc_ekeylen;
 	if (sc->sc_ealgo == CRYPTO_AES_XTS)
 		crie.cri_klen <<= 1;
-	crie.cri_key = sc->sc_ekeys[0];
+	crie.cri_key = sc->sc_ekey;
 	if (sc->sc_flags & G_ELI_FLAG_AUTH) {
 		bzero(&cria, sizeof(cria));
 		cria.cri_alg = sc->sc_aalgo;
@@ -522,34 +522,6 @@ again:
 }
 
 /*
- * Select encryption key. If G_ELI_FLAG_SINGLE_KEY is present we only have one
- * key available for all the data. If the flag is not present select the key
- * based on data offset.
- */
-uint8_t *
-g_eli_crypto_key(struct g_eli_softc *sc, off_t offset, size_t blocksize)
-{
-	u_int nkey;
-
-	if (sc->sc_nekeys == 1)
-		return (sc->sc_ekeys[0]);
-
-	KASSERT(sc->sc_nekeys > 1, ("%s: sc_nekeys=%u", __func__,
-	    sc->sc_nekeys));
-	KASSERT((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) == 0,
-	    ("%s: SINGLE_KEY flag set, but sc_nekeys=%u", __func__,
-	    sc->sc_nekeys));
-
-	/* We switch key every 2^G_ELI_KEY_SHIFT blocks. */
-	nkey = (offset >> G_ELI_KEY_SHIFT) / blocksize;
-
-	KASSERT(nkey < sc->sc_nekeys, ("%s: nkey=%u >= sc_nekeys=%u", __func__,
-	    nkey, sc->sc_nekeys));
-
-	return (sc->sc_ekeys[nkey]);
-}
-
-/*
  * Here we generate IV. It is unique for every sector.
  */
 void
@@ -766,6 +738,7 @@ g_eli_create(struct gctl_req *req, struc
 
 	bioq_init(&sc->sc_queue);
 	mtx_init(&sc->sc_queue_mtx, "geli:queue", NULL, MTX_DEF);
+	mtx_init(&sc->sc_ekeys_lock, "geli:ekeys", NULL, MTX_DEF);
 
 	pp = NULL;
 	cp = g_new_consumer(gp);
@@ -909,11 +882,7 @@ failed:
 	}
 	g_destroy_consumer(cp);
 	g_destroy_geom(gp);
-	if (sc->sc_ekeys != NULL) {
-		bzero(sc->sc_ekeys,
-		    sc->sc_nekeys * (sizeof(uint8_t *) + G_ELI_DATAKEYLEN));
-		free(sc->sc_ekeys, M_ELI);
-	}
+	g_eli_key_destroy(sc);
 	bzero(sc, sizeof(*sc));
 	free(sc, M_ELI);
 	return (NULL);
@@ -953,12 +922,7 @@ g_eli_destroy(struct g_eli_softc *sc, bo
 	}
 	mtx_destroy(&sc->sc_queue_mtx);
 	gp->softc = NULL;
-	if (sc->sc_ekeys != NULL) {
-		/* The sc_ekeys field can be NULL is device is suspended. */
-		bzero(sc->sc_ekeys,
-		    sc->sc_nekeys * (sizeof(uint8_t *) + G_ELI_DATAKEYLEN));
-		free(sc->sc_ekeys, M_ELI);
-	}
+	g_eli_key_destroy(sc);
 	bzero(sc, sizeof(*sc));
 	free(sc, M_ELI);
 
@@ -1191,6 +1155,11 @@ g_eli_dumpconf(struct sbuf *sb, const ch
 		return;
 	if (pp != NULL || cp != NULL)
 		return;	/* Nothing here. */
+
+	sbuf_printf(sb, "%s<KeysTotal>%ju</KeysTotal>", indent,
+	    (uintmax_t)sc->sc_ekeys_total);
+	sbuf_printf(sb, "%s<KeysAllocated>%ju</KeysAllocated>", indent,
+	    (uintmax_t)sc->sc_ekeys_allocated);
 	sbuf_printf(sb, "%s<Flags>", indent);
 	if (sc->sc_flags == 0)
 		sbuf_printf(sb, "NONE");

Modified: head/sys/geom/eli/g_eli.h
==============================================================================
--- head/sys/geom/eli/g_eli.h	Thu Apr 21 12:38:12 2011	(r220921)
+++ head/sys/geom/eli/g_eli.h	Thu Apr 21 13:31:43 2011	(r220922)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2005-2010 Pawel Jakub Dawidek <pjd at FreeBSD.org>
+ * Copyright (c) 2005-2011 Pawel Jakub Dawidek <pawel at dawidek.net>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -37,6 +37,10 @@
 #ifdef _KERNEL
 #include <sys/bio.h>
 #include <sys/libkern.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
 #include <geom/geom.h>
 #else
 #include <stdio.h>
@@ -150,31 +154,35 @@ struct g_eli_worker {
 };
 
 struct g_eli_softc {
-	struct g_geom	 *sc_geom;
-	u_int		  sc_crypto;
-	uint8_t		  sc_mkey[G_ELI_DATAIVKEYLEN];
-	uint8_t		**sc_ekeys;
-	u_int		  sc_nekeys;
-	u_int		  sc_ealgo;
-	u_int		  sc_ekeylen;
-	uint8_t		  sc_akey[G_ELI_AUTHKEYLEN];
-	u_int		  sc_aalgo;
-	u_int		  sc_akeylen;
-	u_int		  sc_alen;
-	SHA256_CTX	  sc_akeyctx;
-	uint8_t		  sc_ivkey[G_ELI_IVKEYLEN];
-	SHA256_CTX	  sc_ivctx;
-	int		  sc_nkey;
-	uint32_t	  sc_flags;
-	int		  sc_inflight;
-	off_t		  sc_mediasize;
-	size_t		  sc_sectorsize;
-	u_int		  sc_bytes_per_sector;
-	u_int		  sc_data_per_sector;
+	struct g_geom	*sc_geom;
+	u_int		 sc_crypto;
+	uint8_t		 sc_mkey[G_ELI_DATAIVKEYLEN];
+	uint8_t		 sc_ekey[G_ELI_DATAKEYLEN];
+	TAILQ_HEAD(, g_eli_key) sc_ekeys_queue;
+	RB_HEAD(g_eli_key_tree, g_eli_key) sc_ekeys_tree;
+	struct mtx	 sc_ekeys_lock;
+	uint64_t	 sc_ekeys_total;
+	uint64_t	 sc_ekeys_allocated;
+	u_int		 sc_ealgo;
+	u_int		 sc_ekeylen;
+	uint8_t		 sc_akey[G_ELI_AUTHKEYLEN];
+	u_int		 sc_aalgo;
+	u_int		 sc_akeylen;
+	u_int		 sc_alen;
+	SHA256_CTX	 sc_akeyctx;
+	uint8_t		 sc_ivkey[G_ELI_IVKEYLEN];
+	SHA256_CTX	 sc_ivctx;
+	int		 sc_nkey;
+	uint32_t	 sc_flags;
+	int		 sc_inflight;
+	off_t		 sc_mediasize;
+	size_t		 sc_sectorsize;
+	u_int		 sc_bytes_per_sector;
+	u_int		 sc_data_per_sector;
 
 	/* Only for software cryptography. */
 	struct bio_queue_head sc_queue;
-	struct mtx	  sc_queue_mtx;
+	struct mtx	 sc_queue_mtx;
 	LIST_HEAD(, g_eli_worker) sc_workers;
 };
 #define	sc_name		 sc_geom->name
@@ -539,4 +547,11 @@ void g_eli_crypto_hmac_update(struct hma
 void g_eli_crypto_hmac_final(struct hmac_ctx *ctx, uint8_t *md, size_t mdsize);
 void g_eli_crypto_hmac(const uint8_t *hkey, size_t hkeysize,
     const uint8_t *data, size_t datasize, uint8_t *md, size_t mdsize);
+
+#ifdef _KERNEL
+void g_eli_key_init(struct g_eli_softc *sc);
+void g_eli_key_destroy(struct g_eli_softc *sc);
+uint8_t *g_eli_key_hold(struct g_eli_softc *sc, off_t offset, size_t blocksize);
+void g_eli_key_drop(struct g_eli_softc *sc, uint8_t *rawkey);
+#endif
 #endif	/* !_G_ELI_H_ */

Modified: head/sys/geom/eli/g_eli_ctl.c
==============================================================================
--- head/sys/geom/eli/g_eli_ctl.c	Thu Apr 21 12:38:12 2011	(r220921)
+++ head/sys/geom/eli/g_eli_ctl.c	Thu Apr 21 13:31:43 2011	(r220922)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2005-2010 Pawel Jakub Dawidek <pjd at FreeBSD.org>
+ * Copyright (c) 2005-2011 Pawel Jakub Dawidek <pawel at dawidek.net>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -739,10 +739,7 @@ g_eli_suspend_one(struct g_eli_softc *sc
 	 * Clear sensitive data on suspend, they will be recovered on resume.
 	 */
 	bzero(sc->sc_mkey, sizeof(sc->sc_mkey));
-	bzero(sc->sc_ekeys,
-	    sc->sc_nekeys * (sizeof(uint8_t *) + G_ELI_DATAKEYLEN));
-	free(sc->sc_ekeys, M_ELI);
-	sc->sc_ekeys = NULL;
+	g_eli_key_destroy(sc);
 	bzero(sc->sc_akey, sizeof(sc->sc_akey));
 	bzero(&sc->sc_akeyctx, sizeof(sc->sc_akeyctx));
 	bzero(sc->sc_ivkey, sizeof(sc->sc_ivkey));

Modified: head/sys/geom/eli/g_eli_integrity.c
==============================================================================
--- head/sys/geom/eli/g_eli_integrity.c	Thu Apr 21 12:38:12 2011	(r220921)
+++ head/sys/geom/eli/g_eli_integrity.c	Thu Apr 21 13:31:43 2011	(r220922)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2005-2010 Pawel Jakub Dawidek <pjd at FreeBSD.org>
+ * Copyright (c) 2005-2011 Pawel Jakub Dawidek <pawel at dawidek.net>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -148,12 +148,13 @@ g_eli_auth_read_done(struct cryptop *crp
 		if (bp->bio_error == 0)
 			bp->bio_error = crp->crp_etype;
 	}
+	sc = bp->bio_to->geom->softc;
+	g_eli_key_drop(sc, crp->crp_desc->crd_key);
 	/*
 	 * Do we have all sectors already?
 	 */
 	if (bp->bio_inbed < bp->bio_children)
 		return (0);
-	sc = bp->bio_to->geom->softc;
 	if (bp->bio_error == 0) {
 		u_int i, lsec, nsec, data_secsize, decr_secsize, encr_secsize;
 		u_char *srcdata, *dstdata, *auth;
@@ -272,12 +273,13 @@ g_eli_auth_write_done(struct cryptop *cr
 		if (bp->bio_error == 0)
 			bp->bio_error = crp->crp_etype;
 	}
+	sc = bp->bio_to->geom->softc;
+	g_eli_key_drop(sc, crp->crp_desc->crd_key);
 	/*
 	 * All sectors are already encrypted?
 	 */
 	if (bp->bio_inbed < bp->bio_children)
 		return (0);
-	sc = bp->bio_to->geom->softc;
 	if (bp->bio_error != 0) {
 		G_ELI_LOGREQ(0, bp, "Crypto WRITE request failed (error=%d).",
 		    bp->bio_error);
@@ -514,7 +516,7 @@ g_eli_auth_run(struct g_eli_worker *wr, 
 		if (bp->bio_cmd == BIO_WRITE)
 			crde->crd_flags |= CRD_F_ENCRYPT;
 		crde->crd_alg = sc->sc_ealgo;
-		crde->crd_key = g_eli_crypto_key(sc, dstoff, encr_secsize);
+		crde->crd_key = g_eli_key_hold(sc, dstoff, encr_secsize);
 		crde->crd_klen = sc->sc_ekeylen;
 		if (sc->sc_ealgo == CRYPTO_AES_XTS)
 			crde->crd_klen <<= 1;

Modified: head/sys/geom/eli/g_eli_key.c
==============================================================================
--- head/sys/geom/eli/g_eli_key.c	Thu Apr 21 12:38:12 2011	(r220921)
+++ head/sys/geom/eli/g_eli_key.c	Thu Apr 21 13:31:43 2011	(r220922)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2005-2010 Pawel Jakub Dawidek <pjd at FreeBSD.org>
+ * Copyright (c) 2005-2011 Pawel Jakub Dawidek <pawel at dawidek.net>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -181,46 +181,6 @@ g_eli_mkey_encrypt(unsigned algo, const 
 }
 
 #ifdef _KERNEL
-static void
-g_eli_ekeys_generate(struct g_eli_softc *sc)
-{
-	uint8_t *keys;
-	u_int kno;
-	off_t mediasize;
-	size_t blocksize;
-	struct {
-		char magic[4];
-		uint8_t keyno[8];
-	} __packed hmacdata;
-
-	KASSERT((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) == 0,
-	    ("%s: G_ELI_FLAG_SINGLE_KEY flag present", __func__));
-
-	if ((sc->sc_flags & G_ELI_FLAG_AUTH) != 0) {
-		struct g_provider *pp;
-
-		pp = LIST_FIRST(&sc->sc_geom->consumer)->provider;
-		mediasize = pp->mediasize;
-		blocksize = pp->sectorsize;
-	} else {
-		mediasize = sc->sc_mediasize;
-		blocksize = sc->sc_sectorsize;
-	}
-	sc->sc_nekeys = ((mediasize - 1) >> G_ELI_KEY_SHIFT) / blocksize + 1;
-	sc->sc_ekeys =
-	    malloc(sc->sc_nekeys * (sizeof(uint8_t *) + G_ELI_DATAKEYLEN),
-	    M_ELI, M_WAITOK);
-	keys = (uint8_t *)(sc->sc_ekeys + sc->sc_nekeys);
-	bcopy("ekey", hmacdata.magic, 4);
-	for (kno = 0; kno < sc->sc_nekeys; kno++, keys += G_ELI_DATAKEYLEN) {
-		sc->sc_ekeys[kno] = keys;
-		le64enc(hmacdata.keyno, (uint64_t)kno);
-		g_eli_crypto_hmac(sc->sc_mkey, G_ELI_MAXKEYLEN,
-		    (uint8_t *)&hmacdata, sizeof(hmacdata),
-		    sc->sc_ekeys[kno], 0);
-	}
-}
-
 /*
  * When doing encryption only, copy IV key and encryption key.
  * When doing encryption and authentication, copy IV key, generate encryption
@@ -246,24 +206,8 @@ g_eli_mkey_propagate(struct g_eli_softc 
 		arc4rand(sc->sc_akey, sizeof(sc->sc_akey), 0);
 	}
 
-	if ((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) != 0) {
-		sc->sc_nekeys = 1;
-		sc->sc_ekeys = malloc(sc->sc_nekeys *
-		    (sizeof(uint8_t *) + G_ELI_DATAKEYLEN), M_ELI, M_WAITOK);
-		sc->sc_ekeys[0] = (uint8_t *)(sc->sc_ekeys + sc->sc_nekeys);
-		if ((sc->sc_flags & G_ELI_FLAG_AUTH) == 0)
-			bcopy(mkey, sc->sc_ekeys[0], G_ELI_DATAKEYLEN);
-		else {
-			/*
-			 * The encryption key is: ekey = HMAC_SHA512(Master-Key, 0x10)
-			 */
-			g_eli_crypto_hmac(mkey, G_ELI_MAXKEYLEN, "\x10", 1,
-			    sc->sc_ekeys[0], 0);
-		}
-	} else {
-		/* Generate all encryption keys. */
-		g_eli_ekeys_generate(sc);
-	}
+	/* Initialize encryption keys. */
+	g_eli_key_init(sc);
 
 	if (sc->sc_flags & G_ELI_FLAG_AUTH) {
 		/*

Added: head/sys/geom/eli/g_eli_key_cache.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/geom/eli/g_eli_key_cache.c	Thu Apr 21 13:31:43 2011	(r220922)
@@ -0,0 +1,319 @@
+/*-
+ * Copyright (c) 2011 Pawel Jakub Dawidek <pawel at dawidek.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/tree.h>
+
+#include <geom/geom.h>
+
+#include <geom/eli/g_eli.h>
+
+MALLOC_DECLARE(M_ELI);
+
+SYSCTL_DECL(_kern_geom_eli);
+/*
+ * The default limit (8192 keys) will allow to cache all keys for 4TB
+ * provider with 512 bytes sectors and will take around 1MB of memory.
+ */
+static u_int g_eli_key_cache_limit = 8192;
+TUNABLE_INT("kern.geom.eli.key_cache_limit", &g_eli_key_cache_limit);
+SYSCTL_UINT(_kern_geom_eli, OID_AUTO, key_cache_limit, CTLFLAG_RDTUN,
+    &g_eli_key_cache_limit, 0, "Maximum number of encryption keys to cache");
+static uint64_t g_eli_key_cache_hits;
+SYSCTL_UQUAD(_kern_geom_eli, OID_AUTO, key_cache_hits, CTLFLAG_RW,
+    &g_eli_key_cache_hits, 0, "Key cache hits");
+static uint64_t g_eli_key_cache_misses;
+SYSCTL_UQUAD(_kern_geom_eli, OID_AUTO, key_cache_misses, CTLFLAG_RW,
+    &g_eli_key_cache_misses, 0, "Key cache misses");
+
+struct g_eli_key {
+	/* Key value, must be first in the structure. */
+	uint8_t		gek_key[G_ELI_DATAKEYLEN];
+	/* Key number. */
+	uint64_t	gek_keyno;
+	/* Reference counter. */
+	int		gek_count;
+	/* Keeps keys sorted by most recent use. */
+	TAILQ_ENTRY(g_eli_key) gek_next;
+	/* Keeps keys sorted by number. */
+	RB_ENTRY(g_eli_key) gek_link;
+};
+
+static int
+g_eli_key_cmp(const struct g_eli_key *a, const struct g_eli_key *b)
+{
+
+	if (a->gek_keyno > b->gek_keyno)
+		return (1);
+	else if (a->gek_keyno < b->gek_keyno)
+		return (-1);
+	return (0);
+}
+
+RB_PROTOTYPE(g_eli_key_tree, g_eli_key, gek_link, g_eli_key_cmp);
+RB_GENERATE(g_eli_key_tree, g_eli_key, gek_link, g_eli_key_cmp);
+
+static void
+g_eli_key_fill(struct g_eli_softc *sc, struct g_eli_key *key, uint64_t keyno)
+{
+	struct {
+		char magic[4];
+		uint8_t keyno[8];
+	} __packed hmacdata;
+
+	bcopy("ekey", hmacdata.magic, 4);
+	le64enc(hmacdata.keyno, keyno);
+	g_eli_crypto_hmac(sc->sc_mkey, G_ELI_MAXKEYLEN, (uint8_t *)&hmacdata,
+	    sizeof(hmacdata), key->gek_key, 0);
+	key->gek_keyno = keyno;
+	key->gek_count = 0;
+}
+
+static struct g_eli_key *
+g_eli_key_allocate(struct g_eli_softc *sc, uint64_t keyno)
+{
+	struct g_eli_key *key, *ekey, keysearch;
+
+	mtx_assert(&sc->sc_ekeys_lock, MA_OWNED);
+	mtx_unlock(&sc->sc_ekeys_lock);
+
+	key = malloc(sizeof(*key), M_ELI, M_WAITOK);
+	g_eli_key_fill(sc, key, keyno);
+
+	mtx_lock(&sc->sc_ekeys_lock);
+	/*
+	 * Recheck if the key wasn't added while we weren't holding the lock.
+	 */
+	keysearch.gek_keyno = keyno;
+	ekey = RB_FIND(g_eli_key_tree, &sc->sc_ekeys_tree, &keysearch);
+	if (ekey != NULL) {
+		bzero(key, sizeof(*key));
+		key = ekey;
+		TAILQ_REMOVE(&sc->sc_ekeys_queue, key, gek_next);
+	} else {
+		RB_INSERT(g_eli_key_tree, &sc->sc_ekeys_tree, key);
+		sc->sc_ekeys_allocated++;
+	}
+	TAILQ_INSERT_TAIL(&sc->sc_ekeys_queue, key, gek_next);
+
+	return (key);
+}
+
+static struct g_eli_key *
+g_eli_key_find_last(struct g_eli_softc *sc)
+{
+	struct g_eli_key *key;
+
+	mtx_assert(&sc->sc_ekeys_lock, MA_OWNED);
+
+	TAILQ_FOREACH(key, &sc->sc_ekeys_queue, gek_next) {
+		if (key->gek_count == 0)
+			break;
+	}
+
+	return (key);
+}
+
+static void
+g_eli_key_replace(struct g_eli_softc *sc, struct g_eli_key *key, uint64_t keyno)
+{
+
+	mtx_assert(&sc->sc_ekeys_lock, MA_OWNED);
+
+	RB_REMOVE(g_eli_key_tree, &sc->sc_ekeys_tree, key);
+	TAILQ_REMOVE(&sc->sc_ekeys_queue, key, gek_next);
+
+	KASSERT(key->gek_count == 0, ("gek_count=%d", key->gek_count));
+
+	g_eli_key_fill(sc, key, keyno);
+
+	RB_INSERT(g_eli_key_tree, &sc->sc_ekeys_tree, key);
+	TAILQ_INSERT_TAIL(&sc->sc_ekeys_queue, key, gek_next);
+}
+
+static void
+g_eli_key_remove(struct g_eli_softc *sc, struct g_eli_key *key)
+{
+
+	mtx_assert(&sc->sc_ekeys_lock, MA_OWNED);
+
+	KASSERT(key->gek_count == 0, ("gek_count=%d", key->gek_count));
+
+	RB_REMOVE(g_eli_key_tree, &sc->sc_ekeys_tree, key);
+	TAILQ_REMOVE(&sc->sc_ekeys_queue, key, gek_next);
+	sc->sc_ekeys_allocated--;
+	bzero(key, sizeof(*key));
+	free(key, M_ELI);
+}
+
+void
+g_eli_key_init(struct g_eli_softc *sc)
+{
+
+	mtx_lock(&sc->sc_ekeys_lock);
+	if ((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) != 0) {
+		uint8_t *mkey;
+
+		mkey = sc->sc_mkey + sizeof(sc->sc_ivkey);
+
+		sc->sc_ekeys_total = 1;
+		sc->sc_ekeys_allocated = 0;
+		if ((sc->sc_flags & G_ELI_FLAG_AUTH) == 0)
+			bcopy(mkey, sc->sc_ekey, G_ELI_DATAKEYLEN);
+		else {
+			/*
+			 * The encryption key is: ekey = HMAC_SHA512(Master-Key, 0x10)
+			 */
+			g_eli_crypto_hmac(mkey, G_ELI_MAXKEYLEN, "\x10", 1,
+			    sc->sc_ekey, 0);
+		}
+	} else {
+		off_t mediasize;
+		size_t blocksize;
+
+		if ((sc->sc_flags & G_ELI_FLAG_AUTH) != 0) {
+			struct g_provider *pp;
+
+			pp = LIST_FIRST(&sc->sc_geom->consumer)->provider;
+			mediasize = pp->mediasize;
+			blocksize = pp->sectorsize;
+		} else {
+			mediasize = sc->sc_mediasize;
+			blocksize = sc->sc_sectorsize;
+		}
+		sc->sc_ekeys_total =
+		    ((mediasize - 1) >> G_ELI_KEY_SHIFT) / blocksize + 1;
+		sc->sc_ekeys_allocated = 0;
+		TAILQ_INIT(&sc->sc_ekeys_queue);
+		RB_INIT(&sc->sc_ekeys_tree);
+	}
+	mtx_unlock(&sc->sc_ekeys_lock);
+}
+
+void
+g_eli_key_destroy(struct g_eli_softc *sc)
+{
+
+	mtx_lock(&sc->sc_ekeys_lock);
+	if ((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) != 0) {
+		bzero(sc->sc_ekey, sizeof(sc->sc_ekey));
+	} else {
+		struct g_eli_key *key;
+
+		while ((key = TAILQ_FIRST(&sc->sc_ekeys_queue)) != NULL)
+			g_eli_key_remove(sc, key);
+		TAILQ_INIT(&sc->sc_ekeys_queue);
+		RB_INIT(&sc->sc_ekeys_tree);
+	}
+	mtx_unlock(&sc->sc_ekeys_lock);
+}
+
+/*
+ * Select encryption key. If G_ELI_FLAG_SINGLE_KEY is present we only have one
+ * key available for all the data. If the flag is not present select the key
+ * based on data offset.
+ */
+uint8_t *
+g_eli_key_hold(struct g_eli_softc *sc, off_t offset, size_t blocksize)
+{
+	struct g_eli_key *key, keysearch;
+	uint64_t keyno;
+
+	if ((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) != 0)
+		return (sc->sc_ekey);
+
+	KASSERT(sc->sc_ekeys_total > 1, ("%s: sc_ekeys_total=%ju", __func__,
+	    (uintmax_t)sc->sc_ekeys_total));
+	KASSERT((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) == 0,
+	    ("%s: SINGLE_KEY flag set, but sc_ekeys_total=%ju", __func__,
+	    (uintmax_t)sc->sc_ekeys_total));
+
+	/* We switch key every 2^G_ELI_KEY_SHIFT blocks. */
+	keyno = (offset >> G_ELI_KEY_SHIFT) / blocksize;
+
+	KASSERT(keyno < sc->sc_ekeys_total,
+	    ("%s: keyno=%ju >= sc_ekeys_total=%ju",
+	    __func__, (uintmax_t)keyno, (uintmax_t)sc->sc_ekeys_total));
+
+	keysearch.gek_keyno = keyno;
+
+	mtx_lock(&sc->sc_ekeys_lock);
+	key = RB_FIND(g_eli_key_tree, &sc->sc_ekeys_tree, &keysearch);
+	if (key != NULL) {
+		g_eli_key_cache_hits++;
+		TAILQ_REMOVE(&sc->sc_ekeys_queue, key, gek_next);
+		TAILQ_INSERT_TAIL(&sc->sc_ekeys_queue, key, gek_next);
+	} else {
+		/*
+		 * No key in cache, find the least recently unreferenced key
+		 * or allocate one if we haven't reached our limit yet.
+		 */
+		if (sc->sc_ekeys_allocated < g_eli_key_cache_limit) {
+			key = g_eli_key_allocate(sc, keyno);
+		} else {
+			g_eli_key_cache_misses++;
+			key = g_eli_key_find_last(sc);
+			if (key != NULL) {
+				g_eli_key_replace(sc, key, keyno);
+			} else {
+				/* All keys are referenced? Allocate one. */
+				key = g_eli_key_allocate(sc, keyno);
+			}
+		}
+	}
+	key->gek_count++;
+	mtx_unlock(&sc->sc_ekeys_lock);
+
+	return (key->gek_key);
+}
+
+void
+g_eli_key_drop(struct g_eli_softc *sc, uint8_t *rawkey)
+{
+	struct g_eli_key *key = (struct g_eli_key *)rawkey;
+
+	if ((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) != 0)
+		return;
+
+	mtx_lock(&sc->sc_ekeys_lock);
+	KASSERT(key->gek_count > 0, ("key->gek_count=%d", key->gek_count));
+	key->gek_count--;
+	while (sc->sc_ekeys_allocated > g_eli_key_cache_limit) {
+		key = g_eli_key_find_last(sc);
+		if (key == NULL)
+			break;
+		g_eli_key_remove(sc, key);
+	}
+	mtx_unlock(&sc->sc_ekeys_lock);
+}

Modified: head/sys/geom/eli/g_eli_privacy.c
==============================================================================
--- head/sys/geom/eli/g_eli_privacy.c	Thu Apr 21 12:38:12 2011	(r220921)
+++ head/sys/geom/eli/g_eli_privacy.c	Thu Apr 21 13:31:43 2011	(r220922)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2005-2010 Pawel Jakub Dawidek <pjd at FreeBSD.org>
+ * Copyright (c) 2005-2011 Pawel Jakub Dawidek <pawel at dawidek.net>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -87,6 +87,8 @@ g_eli_crypto_read_done(struct cryptop *c
 		if (bp->bio_error == 0)
 			bp->bio_error = crp->crp_etype;
 	}
+	sc = bp->bio_to->geom->softc;
+	g_eli_key_drop(sc, crp->crp_desc->crd_key);
 	/*
 	 * Do we have all sectors already?
 	 */
@@ -102,7 +104,6 @@ g_eli_crypto_read_done(struct cryptop *c
 	/*
 	 * Read is finished, send it up.
 	 */
-	sc = bp->bio_to->geom->softc;
 	g_io_deliver(bp, bp->bio_error);
 	atomic_subtract_int(&sc->sc_inflight, 1);
 	return (0);
@@ -136,6 +137,9 @@ g_eli_crypto_write_done(struct cryptop *
 		if (bp->bio_error == 0)
 			bp->bio_error = crp->crp_etype;
 	}
+	gp = bp->bio_to->geom;
+	sc = gp->softc;
+	g_eli_key_drop(sc, crp->crp_desc->crd_key);
 	/*
 	 * All sectors are already encrypted?
 	 */
@@ -145,14 +149,12 @@ g_eli_crypto_write_done(struct cryptop *
 	bp->bio_children = 1;
 	cbp = bp->bio_driver1;
 	bp->bio_driver1 = NULL;
-	gp = bp->bio_to->geom;
 	if (bp->bio_error != 0) {
 		G_ELI_LOGREQ(0, bp, "Crypto WRITE request failed (error=%d).",
 		    bp->bio_error);
 		free(bp->bio_driver2, M_ELI);
 		bp->bio_driver2 = NULL;
 		g_destroy_bio(cbp);
-		sc = gp->softc;
 		g_io_deliver(bp, bp->bio_error);
 		atomic_subtract_int(&sc->sc_inflight, 1);
 		return (0);
@@ -307,12 +309,12 @@ g_eli_crypto_run(struct g_eli_worker *wr
 		crd->crd_skip = 0;
 		crd->crd_len = secsize;
 		crd->crd_flags = CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT;
-		if (sc->sc_nekeys > 1)
+		if ((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) == 0)
 			crd->crd_flags |= CRD_F_KEY_EXPLICIT;
 		if (bp->bio_cmd == BIO_WRITE)
 			crd->crd_flags |= CRD_F_ENCRYPT;
 		crd->crd_alg = sc->sc_ealgo;
-		crd->crd_key = g_eli_crypto_key(sc, dstoff, secsize);
+		crd->crd_key = g_eli_key_hold(sc, dstoff, secsize);
 		crd->crd_klen = sc->sc_ekeylen;
 		if (sc->sc_ealgo == CRYPTO_AES_XTS)
 			crd->crd_klen <<= 1;

Modified: head/sys/modules/geom/geom_eli/Makefile
==============================================================================
--- head/sys/modules/geom/geom_eli/Makefile	Thu Apr 21 12:38:12 2011	(r220921)
+++ head/sys/modules/geom/geom_eli/Makefile	Thu Apr 21 13:31:43 2011	(r220922)
@@ -8,6 +8,7 @@ SRCS+=	g_eli_crypto.c
 SRCS+=	g_eli_ctl.c
 SRCS+=	g_eli_integrity.c
 SRCS+=	g_eli_key.c
+SRCS+=	g_eli_key_cache.c
 SRCS+=	g_eli_privacy.c
 SRCS+=	pkcs5v2.c
 SRCS+=	vnode_if.h


More information about the svn-src-head mailing list