From owner-svn-src-all@FreeBSD.ORG Thu Apr 21 13:31:43 2011 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id C02731065754; Thu, 21 Apr 2011 13:31:43 +0000 (UTC) (envelope-from pjd@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id AE4398FC0C; Thu, 21 Apr 2011 13:31:43 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id p3LDVhDS098277; Thu, 21 Apr 2011 13:31:43 GMT (envelope-from pjd@svn.freebsd.org) Received: (from pjd@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id p3LDVhTi098268; Thu, 21 Apr 2011 13:31:43 GMT (envelope-from pjd@svn.freebsd.org) Message-Id: <201104211331.p3LDVhTi098268@svn.freebsd.org> From: Pawel Jakub Dawidek Date: Thu, 21 Apr 2011 13:31:43 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r220922 - in head/sys: geom/eli modules/geom/geom_eli X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 21 Apr 2011 13:31:43 -0000 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 + * Copyright (c) 2005-2011 Pawel Jakub Dawidek * 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%ju", indent, + (uintmax_t)sc->sc_ekeys_total); + sbuf_printf(sb, "%s%ju", indent, + (uintmax_t)sc->sc_ekeys_allocated); sbuf_printf(sb, "%s", 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 + * Copyright (c) 2005-2011 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,6 +37,10 @@ #ifdef _KERNEL #include #include +#include +#include +#include +#include #include #else #include @@ -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 + * Copyright (c) 2005-2011 Pawel Jakub Dawidek * 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 + * Copyright (c) 2005-2011 Pawel Jakub Dawidek * 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 + * Copyright (c) 2005-2011 Pawel Jakub Dawidek * 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 + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +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 + * Copyright (c) 2005-2011 Pawel Jakub Dawidek * 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