xref: /freebsd/sys/geom/eli/g_eli_key_cache.c (revision 5bd8adc7)
11e09ff3dSPawel Jakub Dawidek /*-
21e09ff3dSPawel Jakub Dawidek  * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
31e09ff3dSPawel Jakub Dawidek  * All rights reserved.
41e09ff3dSPawel Jakub Dawidek  *
51e09ff3dSPawel Jakub Dawidek  * Redistribution and use in source and binary forms, with or without
61e09ff3dSPawel Jakub Dawidek  * modification, are permitted provided that the following conditions
71e09ff3dSPawel Jakub Dawidek  * are met:
81e09ff3dSPawel Jakub Dawidek  * 1. Redistributions of source code must retain the above copyright
91e09ff3dSPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer.
101e09ff3dSPawel Jakub Dawidek  * 2. Redistributions in binary form must reproduce the above copyright
111e09ff3dSPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer in the
121e09ff3dSPawel Jakub Dawidek  *    documentation and/or other materials provided with the distribution.
131e09ff3dSPawel Jakub Dawidek  *
141e09ff3dSPawel Jakub Dawidek  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
151e09ff3dSPawel Jakub Dawidek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
161e09ff3dSPawel Jakub Dawidek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
171e09ff3dSPawel Jakub Dawidek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
181e09ff3dSPawel Jakub Dawidek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
191e09ff3dSPawel Jakub Dawidek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
201e09ff3dSPawel Jakub Dawidek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
211e09ff3dSPawel Jakub Dawidek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
221e09ff3dSPawel Jakub Dawidek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231e09ff3dSPawel Jakub Dawidek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241e09ff3dSPawel Jakub Dawidek  * SUCH DAMAGE.
251e09ff3dSPawel Jakub Dawidek  */
261e09ff3dSPawel Jakub Dawidek 
271e09ff3dSPawel Jakub Dawidek #include <sys/cdefs.h>
281e09ff3dSPawel Jakub Dawidek __FBSDID("$FreeBSD$");
291e09ff3dSPawel Jakub Dawidek 
301e09ff3dSPawel Jakub Dawidek #include <sys/param.h>
311e09ff3dSPawel Jakub Dawidek #include <sys/kernel.h>
321e09ff3dSPawel Jakub Dawidek #include <sys/malloc.h>
331e09ff3dSPawel Jakub Dawidek #include <sys/queue.h>
341e09ff3dSPawel Jakub Dawidek #include <sys/sysctl.h>
351e09ff3dSPawel Jakub Dawidek #include <sys/systm.h>
361e09ff3dSPawel Jakub Dawidek #include <sys/tree.h>
371e09ff3dSPawel Jakub Dawidek 
381e09ff3dSPawel Jakub Dawidek #include <geom/geom.h>
391e09ff3dSPawel Jakub Dawidek 
401e09ff3dSPawel Jakub Dawidek #include <geom/eli/g_eli.h>
411e09ff3dSPawel Jakub Dawidek 
421e09ff3dSPawel Jakub Dawidek MALLOC_DECLARE(M_ELI);
431e09ff3dSPawel Jakub Dawidek 
441e09ff3dSPawel Jakub Dawidek SYSCTL_DECL(_kern_geom_eli);
451e09ff3dSPawel Jakub Dawidek /*
461e09ff3dSPawel Jakub Dawidek  * The default limit (8192 keys) will allow to cache all keys for 4TB
471e09ff3dSPawel Jakub Dawidek  * provider with 512 bytes sectors and will take around 1MB of memory.
481e09ff3dSPawel Jakub Dawidek  */
491e09ff3dSPawel Jakub Dawidek static u_int g_eli_key_cache_limit = 8192;
501e09ff3dSPawel Jakub Dawidek TUNABLE_INT("kern.geom.eli.key_cache_limit", &g_eli_key_cache_limit);
511e09ff3dSPawel Jakub Dawidek SYSCTL_UINT(_kern_geom_eli, OID_AUTO, key_cache_limit, CTLFLAG_RDTUN,
521e09ff3dSPawel Jakub Dawidek     &g_eli_key_cache_limit, 0, "Maximum number of encryption keys to cache");
531e09ff3dSPawel Jakub Dawidek static uint64_t g_eli_key_cache_hits;
541e09ff3dSPawel Jakub Dawidek SYSCTL_UQUAD(_kern_geom_eli, OID_AUTO, key_cache_hits, CTLFLAG_RW,
551e09ff3dSPawel Jakub Dawidek     &g_eli_key_cache_hits, 0, "Key cache hits");
561e09ff3dSPawel Jakub Dawidek static uint64_t g_eli_key_cache_misses;
571e09ff3dSPawel Jakub Dawidek SYSCTL_UQUAD(_kern_geom_eli, OID_AUTO, key_cache_misses, CTLFLAG_RW,
581e09ff3dSPawel Jakub Dawidek     &g_eli_key_cache_misses, 0, "Key cache misses");
591e09ff3dSPawel Jakub Dawidek 
601e09ff3dSPawel Jakub Dawidek struct g_eli_key {
611e09ff3dSPawel Jakub Dawidek 	/* Key value, must be first in the structure. */
621e09ff3dSPawel Jakub Dawidek 	uint8_t		gek_key[G_ELI_DATAKEYLEN];
631e09ff3dSPawel Jakub Dawidek 	/* Key number. */
641e09ff3dSPawel Jakub Dawidek 	uint64_t	gek_keyno;
651e09ff3dSPawel Jakub Dawidek 	/* Reference counter. */
661e09ff3dSPawel Jakub Dawidek 	int		gek_count;
671e09ff3dSPawel Jakub Dawidek 	/* Keeps keys sorted by most recent use. */
681e09ff3dSPawel Jakub Dawidek 	TAILQ_ENTRY(g_eli_key) gek_next;
691e09ff3dSPawel Jakub Dawidek 	/* Keeps keys sorted by number. */
701e09ff3dSPawel Jakub Dawidek 	RB_ENTRY(g_eli_key) gek_link;
711e09ff3dSPawel Jakub Dawidek };
721e09ff3dSPawel Jakub Dawidek 
731e09ff3dSPawel Jakub Dawidek static int
741e09ff3dSPawel Jakub Dawidek g_eli_key_cmp(const struct g_eli_key *a, const struct g_eli_key *b)
751e09ff3dSPawel Jakub Dawidek {
761e09ff3dSPawel Jakub Dawidek 
771e09ff3dSPawel Jakub Dawidek 	if (a->gek_keyno > b->gek_keyno)
781e09ff3dSPawel Jakub Dawidek 		return (1);
791e09ff3dSPawel Jakub Dawidek 	else if (a->gek_keyno < b->gek_keyno)
801e09ff3dSPawel Jakub Dawidek 		return (-1);
811e09ff3dSPawel Jakub Dawidek 	return (0);
821e09ff3dSPawel Jakub Dawidek }
831e09ff3dSPawel Jakub Dawidek 
841e09ff3dSPawel Jakub Dawidek RB_PROTOTYPE(g_eli_key_tree, g_eli_key, gek_link, g_eli_key_cmp);
851e09ff3dSPawel Jakub Dawidek RB_GENERATE(g_eli_key_tree, g_eli_key, gek_link, g_eli_key_cmp);
861e09ff3dSPawel Jakub Dawidek 
871e09ff3dSPawel Jakub Dawidek static void
881e09ff3dSPawel Jakub Dawidek g_eli_key_fill(struct g_eli_softc *sc, struct g_eli_key *key, uint64_t keyno)
891e09ff3dSPawel Jakub Dawidek {
901e09ff3dSPawel Jakub Dawidek 	struct {
911e09ff3dSPawel Jakub Dawidek 		char magic[4];
921e09ff3dSPawel Jakub Dawidek 		uint8_t keyno[8];
931e09ff3dSPawel Jakub Dawidek 	} __packed hmacdata;
941e09ff3dSPawel Jakub Dawidek 
951e09ff3dSPawel Jakub Dawidek 	bcopy("ekey", hmacdata.magic, 4);
961e09ff3dSPawel Jakub Dawidek 	le64enc(hmacdata.keyno, keyno);
971e09ff3dSPawel Jakub Dawidek 	g_eli_crypto_hmac(sc->sc_mkey, G_ELI_MAXKEYLEN, (uint8_t *)&hmacdata,
981e09ff3dSPawel Jakub Dawidek 	    sizeof(hmacdata), key->gek_key, 0);
991e09ff3dSPawel Jakub Dawidek 	key->gek_keyno = keyno;
1001e09ff3dSPawel Jakub Dawidek 	key->gek_count = 0;
1011e09ff3dSPawel Jakub Dawidek }
1021e09ff3dSPawel Jakub Dawidek 
1031e09ff3dSPawel Jakub Dawidek static struct g_eli_key *
1041e09ff3dSPawel Jakub Dawidek g_eli_key_allocate(struct g_eli_softc *sc, uint64_t keyno)
1051e09ff3dSPawel Jakub Dawidek {
1061e09ff3dSPawel Jakub Dawidek 	struct g_eli_key *key, *ekey, keysearch;
1071e09ff3dSPawel Jakub Dawidek 
1081e09ff3dSPawel Jakub Dawidek 	mtx_assert(&sc->sc_ekeys_lock, MA_OWNED);
1091e09ff3dSPawel Jakub Dawidek 	mtx_unlock(&sc->sc_ekeys_lock);
1101e09ff3dSPawel Jakub Dawidek 
1111e09ff3dSPawel Jakub Dawidek 	key = malloc(sizeof(*key), M_ELI, M_WAITOK);
1121e09ff3dSPawel Jakub Dawidek 	g_eli_key_fill(sc, key, keyno);
1131e09ff3dSPawel Jakub Dawidek 
1141e09ff3dSPawel Jakub Dawidek 	mtx_lock(&sc->sc_ekeys_lock);
1151e09ff3dSPawel Jakub Dawidek 	/*
1161e09ff3dSPawel Jakub Dawidek 	 * Recheck if the key wasn't added while we weren't holding the lock.
1171e09ff3dSPawel Jakub Dawidek 	 */
1181e09ff3dSPawel Jakub Dawidek 	keysearch.gek_keyno = keyno;
1191e09ff3dSPawel Jakub Dawidek 	ekey = RB_FIND(g_eli_key_tree, &sc->sc_ekeys_tree, &keysearch);
1201e09ff3dSPawel Jakub Dawidek 	if (ekey != NULL) {
1211e09ff3dSPawel Jakub Dawidek 		bzero(key, sizeof(*key));
1221e09ff3dSPawel Jakub Dawidek 		key = ekey;
1231e09ff3dSPawel Jakub Dawidek 		TAILQ_REMOVE(&sc->sc_ekeys_queue, key, gek_next);
1241e09ff3dSPawel Jakub Dawidek 	} else {
1251e09ff3dSPawel Jakub Dawidek 		RB_INSERT(g_eli_key_tree, &sc->sc_ekeys_tree, key);
1261e09ff3dSPawel Jakub Dawidek 		sc->sc_ekeys_allocated++;
1271e09ff3dSPawel Jakub Dawidek 	}
1281e09ff3dSPawel Jakub Dawidek 	TAILQ_INSERT_TAIL(&sc->sc_ekeys_queue, key, gek_next);
1291e09ff3dSPawel Jakub Dawidek 
1301e09ff3dSPawel Jakub Dawidek 	return (key);
1311e09ff3dSPawel Jakub Dawidek }
1321e09ff3dSPawel Jakub Dawidek 
1331e09ff3dSPawel Jakub Dawidek static struct g_eli_key *
1341e09ff3dSPawel Jakub Dawidek g_eli_key_find_last(struct g_eli_softc *sc)
1351e09ff3dSPawel Jakub Dawidek {
1361e09ff3dSPawel Jakub Dawidek 	struct g_eli_key *key;
1371e09ff3dSPawel Jakub Dawidek 
1381e09ff3dSPawel Jakub Dawidek 	mtx_assert(&sc->sc_ekeys_lock, MA_OWNED);
1391e09ff3dSPawel Jakub Dawidek 
1401e09ff3dSPawel Jakub Dawidek 	TAILQ_FOREACH(key, &sc->sc_ekeys_queue, gek_next) {
1411e09ff3dSPawel Jakub Dawidek 		if (key->gek_count == 0)
1421e09ff3dSPawel Jakub Dawidek 			break;
1431e09ff3dSPawel Jakub Dawidek 	}
1441e09ff3dSPawel Jakub Dawidek 
1451e09ff3dSPawel Jakub Dawidek 	return (key);
1461e09ff3dSPawel Jakub Dawidek }
1471e09ff3dSPawel Jakub Dawidek 
1481e09ff3dSPawel Jakub Dawidek static void
1491e09ff3dSPawel Jakub Dawidek g_eli_key_replace(struct g_eli_softc *sc, struct g_eli_key *key, uint64_t keyno)
1501e09ff3dSPawel Jakub Dawidek {
1511e09ff3dSPawel Jakub Dawidek 
1521e09ff3dSPawel Jakub Dawidek 	mtx_assert(&sc->sc_ekeys_lock, MA_OWNED);
1531e09ff3dSPawel Jakub Dawidek 
1541e09ff3dSPawel Jakub Dawidek 	RB_REMOVE(g_eli_key_tree, &sc->sc_ekeys_tree, key);
1551e09ff3dSPawel Jakub Dawidek 	TAILQ_REMOVE(&sc->sc_ekeys_queue, key, gek_next);
1561e09ff3dSPawel Jakub Dawidek 
1571e09ff3dSPawel Jakub Dawidek 	KASSERT(key->gek_count == 0, ("gek_count=%d", key->gek_count));
1581e09ff3dSPawel Jakub Dawidek 
1591e09ff3dSPawel Jakub Dawidek 	g_eli_key_fill(sc, key, keyno);
1601e09ff3dSPawel Jakub Dawidek 
1611e09ff3dSPawel Jakub Dawidek 	RB_INSERT(g_eli_key_tree, &sc->sc_ekeys_tree, key);
1621e09ff3dSPawel Jakub Dawidek 	TAILQ_INSERT_TAIL(&sc->sc_ekeys_queue, key, gek_next);
1631e09ff3dSPawel Jakub Dawidek }
1641e09ff3dSPawel Jakub Dawidek 
1651e09ff3dSPawel Jakub Dawidek static void
1661e09ff3dSPawel Jakub Dawidek g_eli_key_remove(struct g_eli_softc *sc, struct g_eli_key *key)
1671e09ff3dSPawel Jakub Dawidek {
1681e09ff3dSPawel Jakub Dawidek 
1691e09ff3dSPawel Jakub Dawidek 	mtx_assert(&sc->sc_ekeys_lock, MA_OWNED);
1701e09ff3dSPawel Jakub Dawidek 
1711e09ff3dSPawel Jakub Dawidek 	KASSERT(key->gek_count == 0, ("gek_count=%d", key->gek_count));
1721e09ff3dSPawel Jakub Dawidek 
1731e09ff3dSPawel Jakub Dawidek 	RB_REMOVE(g_eli_key_tree, &sc->sc_ekeys_tree, key);
1741e09ff3dSPawel Jakub Dawidek 	TAILQ_REMOVE(&sc->sc_ekeys_queue, key, gek_next);
1751e09ff3dSPawel Jakub Dawidek 	sc->sc_ekeys_allocated--;
1761e09ff3dSPawel Jakub Dawidek 	bzero(key, sizeof(*key));
1771e09ff3dSPawel Jakub Dawidek 	free(key, M_ELI);
1781e09ff3dSPawel Jakub Dawidek }
1791e09ff3dSPawel Jakub Dawidek 
1801e09ff3dSPawel Jakub Dawidek void
1811e09ff3dSPawel Jakub Dawidek g_eli_key_init(struct g_eli_softc *sc)
1821e09ff3dSPawel Jakub Dawidek {
1831e09ff3dSPawel Jakub Dawidek 
1841e09ff3dSPawel Jakub Dawidek 	mtx_lock(&sc->sc_ekeys_lock);
1851e09ff3dSPawel Jakub Dawidek 	if ((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) != 0) {
1861e09ff3dSPawel Jakub Dawidek 		uint8_t *mkey;
1871e09ff3dSPawel Jakub Dawidek 
1881e09ff3dSPawel Jakub Dawidek 		mkey = sc->sc_mkey + sizeof(sc->sc_ivkey);
1891e09ff3dSPawel Jakub Dawidek 
1901e09ff3dSPawel Jakub Dawidek 		sc->sc_ekeys_total = 1;
1911e09ff3dSPawel Jakub Dawidek 		sc->sc_ekeys_allocated = 0;
1921e09ff3dSPawel Jakub Dawidek 		if ((sc->sc_flags & G_ELI_FLAG_AUTH) == 0)
1931e09ff3dSPawel Jakub Dawidek 			bcopy(mkey, sc->sc_ekey, G_ELI_DATAKEYLEN);
1941e09ff3dSPawel Jakub Dawidek 		else {
1951e09ff3dSPawel Jakub Dawidek 			/*
1961e09ff3dSPawel Jakub Dawidek 			 * The encryption key is: ekey = HMAC_SHA512(Master-Key, 0x10)
1971e09ff3dSPawel Jakub Dawidek 			 */
1981e09ff3dSPawel Jakub Dawidek 			g_eli_crypto_hmac(mkey, G_ELI_MAXKEYLEN, "\x10", 1,
1991e09ff3dSPawel Jakub Dawidek 			    sc->sc_ekey, 0);
2001e09ff3dSPawel Jakub Dawidek 		}
2011e09ff3dSPawel Jakub Dawidek 	} else {
2021e09ff3dSPawel Jakub Dawidek 		off_t mediasize;
2031e09ff3dSPawel Jakub Dawidek 		size_t blocksize;
2041e09ff3dSPawel Jakub Dawidek 
2051e09ff3dSPawel Jakub Dawidek 		if ((sc->sc_flags & G_ELI_FLAG_AUTH) != 0) {
2061e09ff3dSPawel Jakub Dawidek 			struct g_provider *pp;
2071e09ff3dSPawel Jakub Dawidek 
2081e09ff3dSPawel Jakub Dawidek 			pp = LIST_FIRST(&sc->sc_geom->consumer)->provider;
2091e09ff3dSPawel Jakub Dawidek 			mediasize = pp->mediasize;
2101e09ff3dSPawel Jakub Dawidek 			blocksize = pp->sectorsize;
2111e09ff3dSPawel Jakub Dawidek 		} else {
2121e09ff3dSPawel Jakub Dawidek 			mediasize = sc->sc_mediasize;
2131e09ff3dSPawel Jakub Dawidek 			blocksize = sc->sc_sectorsize;
2141e09ff3dSPawel Jakub Dawidek 		}
2151e09ff3dSPawel Jakub Dawidek 		sc->sc_ekeys_total =
2161e09ff3dSPawel Jakub Dawidek 		    ((mediasize - 1) >> G_ELI_KEY_SHIFT) / blocksize + 1;
2171e09ff3dSPawel Jakub Dawidek 		sc->sc_ekeys_allocated = 0;
2181e09ff3dSPawel Jakub Dawidek 		TAILQ_INIT(&sc->sc_ekeys_queue);
2191e09ff3dSPawel Jakub Dawidek 		RB_INIT(&sc->sc_ekeys_tree);
2205bd8adc7SPawel Jakub Dawidek 		if (sc->sc_ekeys_total <= g_eli_key_cache_limit) {
2215bd8adc7SPawel Jakub Dawidek 			uint64_t keyno;
2225bd8adc7SPawel Jakub Dawidek 
2235bd8adc7SPawel Jakub Dawidek 			for (keyno = 0; keyno < sc->sc_ekeys_total; keyno++)
2245bd8adc7SPawel Jakub Dawidek 				(void)g_eli_key_allocate(sc, keyno);
2255bd8adc7SPawel Jakub Dawidek 			KASSERT(sc->sc_ekeys_total == sc->sc_ekeys_allocated,
2265bd8adc7SPawel Jakub Dawidek 			    ("sc_ekeys_total=%ju != sc_ekeys_allocated=%ju",
2275bd8adc7SPawel Jakub Dawidek 			    (uintmax_t)sc->sc_ekeys_total,
2285bd8adc7SPawel Jakub Dawidek 			    (uintmax_t)sc->sc_ekeys_allocated));
2295bd8adc7SPawel Jakub Dawidek 		}
2301e09ff3dSPawel Jakub Dawidek 	}
2311e09ff3dSPawel Jakub Dawidek 	mtx_unlock(&sc->sc_ekeys_lock);
2321e09ff3dSPawel Jakub Dawidek }
2331e09ff3dSPawel Jakub Dawidek 
2341e09ff3dSPawel Jakub Dawidek void
2351e09ff3dSPawel Jakub Dawidek g_eli_key_destroy(struct g_eli_softc *sc)
2361e09ff3dSPawel Jakub Dawidek {
2371e09ff3dSPawel Jakub Dawidek 
2381e09ff3dSPawel Jakub Dawidek 	mtx_lock(&sc->sc_ekeys_lock);
2391e09ff3dSPawel Jakub Dawidek 	if ((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) != 0) {
2401e09ff3dSPawel Jakub Dawidek 		bzero(sc->sc_ekey, sizeof(sc->sc_ekey));
2411e09ff3dSPawel Jakub Dawidek 	} else {
2421e09ff3dSPawel Jakub Dawidek 		struct g_eli_key *key;
2431e09ff3dSPawel Jakub Dawidek 
2441e09ff3dSPawel Jakub Dawidek 		while ((key = TAILQ_FIRST(&sc->sc_ekeys_queue)) != NULL)
2451e09ff3dSPawel Jakub Dawidek 			g_eli_key_remove(sc, key);
2461e09ff3dSPawel Jakub Dawidek 		TAILQ_INIT(&sc->sc_ekeys_queue);
2471e09ff3dSPawel Jakub Dawidek 		RB_INIT(&sc->sc_ekeys_tree);
2481e09ff3dSPawel Jakub Dawidek 	}
2491e09ff3dSPawel Jakub Dawidek 	mtx_unlock(&sc->sc_ekeys_lock);
2501e09ff3dSPawel Jakub Dawidek }
2511e09ff3dSPawel Jakub Dawidek 
2521e09ff3dSPawel Jakub Dawidek /*
2531e09ff3dSPawel Jakub Dawidek  * Select encryption key. If G_ELI_FLAG_SINGLE_KEY is present we only have one
2541e09ff3dSPawel Jakub Dawidek  * key available for all the data. If the flag is not present select the key
2551e09ff3dSPawel Jakub Dawidek  * based on data offset.
2561e09ff3dSPawel Jakub Dawidek  */
2571e09ff3dSPawel Jakub Dawidek uint8_t *
2581e09ff3dSPawel Jakub Dawidek g_eli_key_hold(struct g_eli_softc *sc, off_t offset, size_t blocksize)
2591e09ff3dSPawel Jakub Dawidek {
2601e09ff3dSPawel Jakub Dawidek 	struct g_eli_key *key, keysearch;
2611e09ff3dSPawel Jakub Dawidek 	uint64_t keyno;
2621e09ff3dSPawel Jakub Dawidek 
2631e09ff3dSPawel Jakub Dawidek 	if ((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) != 0)
2641e09ff3dSPawel Jakub Dawidek 		return (sc->sc_ekey);
2651e09ff3dSPawel Jakub Dawidek 
2661e09ff3dSPawel Jakub Dawidek 	KASSERT(sc->sc_ekeys_total > 1, ("%s: sc_ekeys_total=%ju", __func__,
2671e09ff3dSPawel Jakub Dawidek 	    (uintmax_t)sc->sc_ekeys_total));
2681e09ff3dSPawel Jakub Dawidek 	KASSERT((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) == 0,
2691e09ff3dSPawel Jakub Dawidek 	    ("%s: SINGLE_KEY flag set, but sc_ekeys_total=%ju", __func__,
2701e09ff3dSPawel Jakub Dawidek 	    (uintmax_t)sc->sc_ekeys_total));
2711e09ff3dSPawel Jakub Dawidek 
2721e09ff3dSPawel Jakub Dawidek 	/* We switch key every 2^G_ELI_KEY_SHIFT blocks. */
2731e09ff3dSPawel Jakub Dawidek 	keyno = (offset >> G_ELI_KEY_SHIFT) / blocksize;
2741e09ff3dSPawel Jakub Dawidek 
2751e09ff3dSPawel Jakub Dawidek 	KASSERT(keyno < sc->sc_ekeys_total,
2761e09ff3dSPawel Jakub Dawidek 	    ("%s: keyno=%ju >= sc_ekeys_total=%ju",
2771e09ff3dSPawel Jakub Dawidek 	    __func__, (uintmax_t)keyno, (uintmax_t)sc->sc_ekeys_total));
2781e09ff3dSPawel Jakub Dawidek 
2791e09ff3dSPawel Jakub Dawidek 	keysearch.gek_keyno = keyno;
2801e09ff3dSPawel Jakub Dawidek 
2815bd8adc7SPawel Jakub Dawidek 	if (sc->sc_ekeys_total == sc->sc_ekeys_allocated) {
2825bd8adc7SPawel Jakub Dawidek 		/* We have all the keys, so avoid some overhead. */
2835bd8adc7SPawel Jakub Dawidek 		key = RB_FIND(g_eli_key_tree, &sc->sc_ekeys_tree, &keysearch);
2845bd8adc7SPawel Jakub Dawidek 		KASSERT(key != NULL, ("No key %ju found.", (uintmax_t)keyno));
2855bd8adc7SPawel Jakub Dawidek 		return (key->gek_key);
2865bd8adc7SPawel Jakub Dawidek 	}
2875bd8adc7SPawel Jakub Dawidek 
2881e09ff3dSPawel Jakub Dawidek 	mtx_lock(&sc->sc_ekeys_lock);
2891e09ff3dSPawel Jakub Dawidek 	key = RB_FIND(g_eli_key_tree, &sc->sc_ekeys_tree, &keysearch);
2901e09ff3dSPawel Jakub Dawidek 	if (key != NULL) {
2911e09ff3dSPawel Jakub Dawidek 		g_eli_key_cache_hits++;
2921e09ff3dSPawel Jakub Dawidek 		TAILQ_REMOVE(&sc->sc_ekeys_queue, key, gek_next);
2931e09ff3dSPawel Jakub Dawidek 		TAILQ_INSERT_TAIL(&sc->sc_ekeys_queue, key, gek_next);
2941e09ff3dSPawel Jakub Dawidek 	} else {
2951e09ff3dSPawel Jakub Dawidek 		/*
2961e09ff3dSPawel Jakub Dawidek 		 * No key in cache, find the least recently unreferenced key
2971e09ff3dSPawel Jakub Dawidek 		 * or allocate one if we haven't reached our limit yet.
2981e09ff3dSPawel Jakub Dawidek 		 */
2991e09ff3dSPawel Jakub Dawidek 		if (sc->sc_ekeys_allocated < g_eli_key_cache_limit) {
3001e09ff3dSPawel Jakub Dawidek 			key = g_eli_key_allocate(sc, keyno);
3011e09ff3dSPawel Jakub Dawidek 		} else {
3021e09ff3dSPawel Jakub Dawidek 			g_eli_key_cache_misses++;
3031e09ff3dSPawel Jakub Dawidek 			key = g_eli_key_find_last(sc);
3041e09ff3dSPawel Jakub Dawidek 			if (key != NULL) {
3051e09ff3dSPawel Jakub Dawidek 				g_eli_key_replace(sc, key, keyno);
3061e09ff3dSPawel Jakub Dawidek 			} else {
3071e09ff3dSPawel Jakub Dawidek 				/* All keys are referenced? Allocate one. */
3081e09ff3dSPawel Jakub Dawidek 				key = g_eli_key_allocate(sc, keyno);
3091e09ff3dSPawel Jakub Dawidek 			}
3101e09ff3dSPawel Jakub Dawidek 		}
3111e09ff3dSPawel Jakub Dawidek 	}
3121e09ff3dSPawel Jakub Dawidek 	key->gek_count++;
3131e09ff3dSPawel Jakub Dawidek 	mtx_unlock(&sc->sc_ekeys_lock);
3141e09ff3dSPawel Jakub Dawidek 
3151e09ff3dSPawel Jakub Dawidek 	return (key->gek_key);
3161e09ff3dSPawel Jakub Dawidek }
3171e09ff3dSPawel Jakub Dawidek 
3181e09ff3dSPawel Jakub Dawidek void
3191e09ff3dSPawel Jakub Dawidek g_eli_key_drop(struct g_eli_softc *sc, uint8_t *rawkey)
3201e09ff3dSPawel Jakub Dawidek {
3211e09ff3dSPawel Jakub Dawidek 	struct g_eli_key *key = (struct g_eli_key *)rawkey;
3221e09ff3dSPawel Jakub Dawidek 
3231e09ff3dSPawel Jakub Dawidek 	if ((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) != 0)
3241e09ff3dSPawel Jakub Dawidek 		return;
3251e09ff3dSPawel Jakub Dawidek 
3265bd8adc7SPawel Jakub Dawidek 	if (sc->sc_ekeys_total == sc->sc_ekeys_allocated)
3275bd8adc7SPawel Jakub Dawidek 		return;
3285bd8adc7SPawel Jakub Dawidek 
3291e09ff3dSPawel Jakub Dawidek 	mtx_lock(&sc->sc_ekeys_lock);
3301e09ff3dSPawel Jakub Dawidek 	KASSERT(key->gek_count > 0, ("key->gek_count=%d", key->gek_count));
3311e09ff3dSPawel Jakub Dawidek 	key->gek_count--;
3321e09ff3dSPawel Jakub Dawidek 	while (sc->sc_ekeys_allocated > g_eli_key_cache_limit) {
3331e09ff3dSPawel Jakub Dawidek 		key = g_eli_key_find_last(sc);
3341e09ff3dSPawel Jakub Dawidek 		if (key == NULL)
3351e09ff3dSPawel Jakub Dawidek 			break;
3361e09ff3dSPawel Jakub Dawidek 		g_eli_key_remove(sc, key);
3371e09ff3dSPawel Jakub Dawidek 	}
3381e09ff3dSPawel Jakub Dawidek 	mtx_unlock(&sc->sc_ekeys_lock);
3391e09ff3dSPawel Jakub Dawidek }
340