104c7da70SRuslan Ermilov /*- 23728855aSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 33728855aSPedro F. Giffuni * 404c7da70SRuslan Ermilov * Copyright (c) 2006 Ruslan Ermilov <ru@FreeBSD.org> 504c7da70SRuslan Ermilov * All rights reserved. 604c7da70SRuslan Ermilov * 704c7da70SRuslan Ermilov * Redistribution and use in source and binary forms, with or without 804c7da70SRuslan Ermilov * modification, are permitted provided that the following conditions 904c7da70SRuslan Ermilov * are met: 1004c7da70SRuslan Ermilov * 1. Redistributions of source code must retain the above copyright 1104c7da70SRuslan Ermilov * notice, this list of conditions and the following disclaimer. 1204c7da70SRuslan Ermilov * 2. Redistributions in binary form must reproduce the above copyright 1304c7da70SRuslan Ermilov * notice, this list of conditions and the following disclaimer in the 1404c7da70SRuslan Ermilov * documentation and/or other materials provided with the distribution. 1504c7da70SRuslan Ermilov * 1604c7da70SRuslan Ermilov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1704c7da70SRuslan Ermilov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1804c7da70SRuslan Ermilov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1904c7da70SRuslan Ermilov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2004c7da70SRuslan Ermilov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2104c7da70SRuslan Ermilov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2204c7da70SRuslan Ermilov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2304c7da70SRuslan Ermilov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2404c7da70SRuslan Ermilov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2504c7da70SRuslan Ermilov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2604c7da70SRuslan Ermilov * SUCH DAMAGE. 2704c7da70SRuslan Ermilov */ 2804c7da70SRuslan Ermilov 2904c7da70SRuslan Ermilov #include <sys/cdefs.h> 3004c7da70SRuslan Ermilov __FBSDID("$FreeBSD$"); 3104c7da70SRuslan Ermilov 3204c7da70SRuslan Ermilov #include <sys/param.h> 3304c7da70SRuslan Ermilov #include <sys/systm.h> 3404c7da70SRuslan Ermilov #include <sys/kernel.h> 3504c7da70SRuslan Ermilov #include <sys/module.h> 3604c7da70SRuslan Ermilov #include <sys/lock.h> 3704c7da70SRuslan Ermilov #include <sys/mutex.h> 3804c7da70SRuslan Ermilov #include <sys/bio.h> 3904c7da70SRuslan Ermilov #include <sys/sysctl.h> 4004c7da70SRuslan Ermilov #include <sys/malloc.h> 4104c7da70SRuslan Ermilov #include <sys/queue.h> 425d807a0eSAndrey V. Elsukov #include <sys/sbuf.h> 4304c7da70SRuslan Ermilov #include <sys/time.h> 4404c7da70SRuslan Ermilov #include <vm/uma.h> 4504c7da70SRuslan Ermilov #include <geom/geom.h> 46ac03832eSConrad Meyer #include <geom/geom_dbg.h> 4704c7da70SRuslan Ermilov #include <geom/cache/g_cache.h> 4804c7da70SRuslan Ermilov 49cb08c2ccSAlexander Leidinger FEATURE(geom_cache, "GEOM cache module"); 50cb08c2ccSAlexander Leidinger 5104c7da70SRuslan Ermilov static MALLOC_DEFINE(M_GCACHE, "gcache_data", "GEOM_CACHE Data"); 5204c7da70SRuslan Ermilov 5304c7da70SRuslan Ermilov SYSCTL_DECL(_kern_geom); 54*7029da5cSPawel Biernacki static SYSCTL_NODE(_kern_geom, OID_AUTO, cache, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 556472ac3dSEd Schouten "GEOM_CACHE stuff"); 5604c7da70SRuslan Ermilov static u_int g_cache_debug = 0; 5704c7da70SRuslan Ermilov SYSCTL_UINT(_kern_geom_cache, OID_AUTO, debug, CTLFLAG_RW, &g_cache_debug, 0, 5804c7da70SRuslan Ermilov "Debug level"); 5904c7da70SRuslan Ermilov static u_int g_cache_enable = 1; 6004c7da70SRuslan Ermilov SYSCTL_UINT(_kern_geom_cache, OID_AUTO, enable, CTLFLAG_RW, &g_cache_enable, 0, 6104c7da70SRuslan Ermilov ""); 6204c7da70SRuslan Ermilov static u_int g_cache_timeout = 10; 6304c7da70SRuslan Ermilov SYSCTL_UINT(_kern_geom_cache, OID_AUTO, timeout, CTLFLAG_RW, &g_cache_timeout, 6404c7da70SRuslan Ermilov 0, ""); 6504c7da70SRuslan Ermilov static u_int g_cache_idletime = 5; 6604c7da70SRuslan Ermilov SYSCTL_UINT(_kern_geom_cache, OID_AUTO, idletime, CTLFLAG_RW, &g_cache_idletime, 6704c7da70SRuslan Ermilov 0, ""); 6804c7da70SRuslan Ermilov static u_int g_cache_used_lo = 5; 6904c7da70SRuslan Ermilov static u_int g_cache_used_hi = 20; 7004c7da70SRuslan Ermilov static int 7104c7da70SRuslan Ermilov sysctl_handle_pct(SYSCTL_HANDLER_ARGS) 7204c7da70SRuslan Ermilov { 736cec74b2SJustin Hibbits u_int val = *(u_int *)arg1; 7404c7da70SRuslan Ermilov int error; 7504c7da70SRuslan Ermilov 76041b706bSDavid Malone error = sysctl_handle_int(oidp, &val, 0, req); 7704c7da70SRuslan Ermilov if (error || !req->newptr) 7804c7da70SRuslan Ermilov return (error); 7986e26cb1SWarner Losh if (val > 100) 8004c7da70SRuslan Ermilov return (EINVAL); 8104c7da70SRuslan Ermilov if ((arg1 == &g_cache_used_lo && val > g_cache_used_hi) || 8204c7da70SRuslan Ermilov (arg1 == &g_cache_used_hi && g_cache_used_lo > val)) 8304c7da70SRuslan Ermilov return (EINVAL); 8404c7da70SRuslan Ermilov *(u_int *)arg1 = val; 8504c7da70SRuslan Ermilov return (0); 8604c7da70SRuslan Ermilov } 87*7029da5cSPawel Biernacki SYSCTL_PROC(_kern_geom_cache, OID_AUTO, used_lo, 88*7029da5cSPawel Biernacki CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, &g_cache_used_lo, 0, 89*7029da5cSPawel Biernacki sysctl_handle_pct, "IU", 90*7029da5cSPawel Biernacki ""); 91*7029da5cSPawel Biernacki SYSCTL_PROC(_kern_geom_cache, OID_AUTO, used_hi, 92*7029da5cSPawel Biernacki CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, &g_cache_used_hi, 0, 93*7029da5cSPawel Biernacki sysctl_handle_pct, "IU", 94*7029da5cSPawel Biernacki ""); 9504c7da70SRuslan Ermilov 9604c7da70SRuslan Ermilov 9704c7da70SRuslan Ermilov static int g_cache_destroy(struct g_cache_softc *sc, boolean_t force); 9804c7da70SRuslan Ermilov static g_ctl_destroy_geom_t g_cache_destroy_geom; 9904c7da70SRuslan Ermilov 10004c7da70SRuslan Ermilov static g_taste_t g_cache_taste; 10104c7da70SRuslan Ermilov static g_ctl_req_t g_cache_config; 10204c7da70SRuslan Ermilov static g_dumpconf_t g_cache_dumpconf; 10304c7da70SRuslan Ermilov 10404c7da70SRuslan Ermilov struct g_class g_cache_class = { 10504c7da70SRuslan Ermilov .name = G_CACHE_CLASS_NAME, 10604c7da70SRuslan Ermilov .version = G_VERSION, 10704c7da70SRuslan Ermilov .ctlreq = g_cache_config, 10804c7da70SRuslan Ermilov .taste = g_cache_taste, 10904c7da70SRuslan Ermilov .destroy_geom = g_cache_destroy_geom 11004c7da70SRuslan Ermilov }; 11104c7da70SRuslan Ermilov 11204c7da70SRuslan Ermilov #define OFF2BNO(off, sc) ((off) >> (sc)->sc_bshift) 11304c7da70SRuslan Ermilov #define BNO2OFF(bno, sc) ((bno) << (sc)->sc_bshift) 11404c7da70SRuslan Ermilov 11504c7da70SRuslan Ermilov 11604c7da70SRuslan Ermilov static struct g_cache_desc * 11704c7da70SRuslan Ermilov g_cache_alloc(struct g_cache_softc *sc) 11804c7da70SRuslan Ermilov { 11904c7da70SRuslan Ermilov struct g_cache_desc *dp; 12004c7da70SRuslan Ermilov 12104c7da70SRuslan Ermilov mtx_assert(&sc->sc_mtx, MA_OWNED); 12204c7da70SRuslan Ermilov 12304c7da70SRuslan Ermilov if (!TAILQ_EMPTY(&sc->sc_usedlist)) { 12404c7da70SRuslan Ermilov dp = TAILQ_FIRST(&sc->sc_usedlist); 12504c7da70SRuslan Ermilov TAILQ_REMOVE(&sc->sc_usedlist, dp, d_used); 12604c7da70SRuslan Ermilov sc->sc_nused--; 12704c7da70SRuslan Ermilov dp->d_flags = 0; 12804c7da70SRuslan Ermilov LIST_REMOVE(dp, d_next); 12904c7da70SRuslan Ermilov return (dp); 13004c7da70SRuslan Ermilov } 13104c7da70SRuslan Ermilov if (sc->sc_nent > sc->sc_maxent) { 13204c7da70SRuslan Ermilov sc->sc_cachefull++; 13304c7da70SRuslan Ermilov return (NULL); 13404c7da70SRuslan Ermilov } 13504c7da70SRuslan Ermilov dp = malloc(sizeof(*dp), M_GCACHE, M_NOWAIT | M_ZERO); 13604c7da70SRuslan Ermilov if (dp == NULL) 13704c7da70SRuslan Ermilov return (NULL); 13804c7da70SRuslan Ermilov dp->d_data = uma_zalloc(sc->sc_zone, M_NOWAIT); 13904c7da70SRuslan Ermilov if (dp->d_data == NULL) { 14004c7da70SRuslan Ermilov free(dp, M_GCACHE); 14104c7da70SRuslan Ermilov return (NULL); 14204c7da70SRuslan Ermilov } 14304c7da70SRuslan Ermilov sc->sc_nent++; 14404c7da70SRuslan Ermilov return (dp); 14504c7da70SRuslan Ermilov } 14604c7da70SRuslan Ermilov 14704c7da70SRuslan Ermilov static void 14804c7da70SRuslan Ermilov g_cache_free(struct g_cache_softc *sc, struct g_cache_desc *dp) 14904c7da70SRuslan Ermilov { 15004c7da70SRuslan Ermilov 15104c7da70SRuslan Ermilov mtx_assert(&sc->sc_mtx, MA_OWNED); 15204c7da70SRuslan Ermilov 15304c7da70SRuslan Ermilov uma_zfree(sc->sc_zone, dp->d_data); 15404c7da70SRuslan Ermilov free(dp, M_GCACHE); 15504c7da70SRuslan Ermilov sc->sc_nent--; 15604c7da70SRuslan Ermilov } 15704c7da70SRuslan Ermilov 15804c7da70SRuslan Ermilov static void 15904c7da70SRuslan Ermilov g_cache_free_used(struct g_cache_softc *sc) 16004c7da70SRuslan Ermilov { 16104c7da70SRuslan Ermilov struct g_cache_desc *dp; 16204c7da70SRuslan Ermilov u_int n; 16304c7da70SRuslan Ermilov 16404c7da70SRuslan Ermilov mtx_assert(&sc->sc_mtx, MA_OWNED); 16504c7da70SRuslan Ermilov 16604c7da70SRuslan Ermilov n = g_cache_used_lo * sc->sc_maxent / 100; 16704c7da70SRuslan Ermilov while (sc->sc_nused > n) { 16804c7da70SRuslan Ermilov KASSERT(!TAILQ_EMPTY(&sc->sc_usedlist), ("used list empty")); 16904c7da70SRuslan Ermilov dp = TAILQ_FIRST(&sc->sc_usedlist); 17004c7da70SRuslan Ermilov TAILQ_REMOVE(&sc->sc_usedlist, dp, d_used); 17104c7da70SRuslan Ermilov sc->sc_nused--; 17204c7da70SRuslan Ermilov LIST_REMOVE(dp, d_next); 17304c7da70SRuslan Ermilov g_cache_free(sc, dp); 17404c7da70SRuslan Ermilov } 17504c7da70SRuslan Ermilov } 17604c7da70SRuslan Ermilov 17704c7da70SRuslan Ermilov static void 17804c7da70SRuslan Ermilov g_cache_deliver(struct g_cache_softc *sc, struct bio *bp, 17904c7da70SRuslan Ermilov struct g_cache_desc *dp, int error) 18004c7da70SRuslan Ermilov { 18104c7da70SRuslan Ermilov off_t off1, off, len; 18204c7da70SRuslan Ermilov 18304c7da70SRuslan Ermilov mtx_assert(&sc->sc_mtx, MA_OWNED); 18404c7da70SRuslan Ermilov KASSERT(OFF2BNO(bp->bio_offset, sc) <= dp->d_bno, ("wrong entry")); 18504c7da70SRuslan Ermilov KASSERT(OFF2BNO(bp->bio_offset + bp->bio_length - 1, sc) >= 18604c7da70SRuslan Ermilov dp->d_bno, ("wrong entry")); 18704c7da70SRuslan Ermilov 18804c7da70SRuslan Ermilov off1 = BNO2OFF(dp->d_bno, sc); 18904c7da70SRuslan Ermilov off = MAX(bp->bio_offset, off1); 19004c7da70SRuslan Ermilov len = MIN(bp->bio_offset + bp->bio_length, off1 + sc->sc_bsize) - off; 19104c7da70SRuslan Ermilov 19204c7da70SRuslan Ermilov if (bp->bio_error == 0) 19304c7da70SRuslan Ermilov bp->bio_error = error; 19404c7da70SRuslan Ermilov if (bp->bio_error == 0) { 19504c7da70SRuslan Ermilov bcopy(dp->d_data + (off - off1), 19604c7da70SRuslan Ermilov bp->bio_data + (off - bp->bio_offset), len); 19704c7da70SRuslan Ermilov } 19804c7da70SRuslan Ermilov bp->bio_completed += len; 19904c7da70SRuslan Ermilov KASSERT(bp->bio_completed <= bp->bio_length, ("extra data")); 20004c7da70SRuslan Ermilov if (bp->bio_completed == bp->bio_length) { 20104c7da70SRuslan Ermilov if (bp->bio_error != 0) 20204c7da70SRuslan Ermilov bp->bio_completed = 0; 20304c7da70SRuslan Ermilov g_io_deliver(bp, bp->bio_error); 20404c7da70SRuslan Ermilov } 20504c7da70SRuslan Ermilov 20604c7da70SRuslan Ermilov if (dp->d_flags & D_FLAG_USED) { 20704c7da70SRuslan Ermilov TAILQ_REMOVE(&sc->sc_usedlist, dp, d_used); 20804c7da70SRuslan Ermilov TAILQ_INSERT_TAIL(&sc->sc_usedlist, dp, d_used); 20904c7da70SRuslan Ermilov } else if (OFF2BNO(off + len, sc) > dp->d_bno) { 21004c7da70SRuslan Ermilov TAILQ_INSERT_TAIL(&sc->sc_usedlist, dp, d_used); 21104c7da70SRuslan Ermilov sc->sc_nused++; 21204c7da70SRuslan Ermilov dp->d_flags |= D_FLAG_USED; 21304c7da70SRuslan Ermilov } 21404c7da70SRuslan Ermilov dp->d_atime = time_uptime; 21504c7da70SRuslan Ermilov } 21604c7da70SRuslan Ermilov 21704c7da70SRuslan Ermilov static void 21804c7da70SRuslan Ermilov g_cache_done(struct bio *bp) 21904c7da70SRuslan Ermilov { 22004c7da70SRuslan Ermilov struct g_cache_softc *sc; 22104c7da70SRuslan Ermilov struct g_cache_desc *dp; 22204c7da70SRuslan Ermilov struct bio *bp2, *tmpbp; 22304c7da70SRuslan Ermilov 22404c7da70SRuslan Ermilov sc = bp->bio_from->geom->softc; 22504c7da70SRuslan Ermilov KASSERT(G_CACHE_DESC1(bp) == sc, ("corrupt bio_caller in g_cache_done()")); 22604c7da70SRuslan Ermilov dp = G_CACHE_DESC2(bp); 22704c7da70SRuslan Ermilov mtx_lock(&sc->sc_mtx); 22804c7da70SRuslan Ermilov bp2 = dp->d_biolist; 22904c7da70SRuslan Ermilov while (bp2 != NULL) { 23004c7da70SRuslan Ermilov KASSERT(G_CACHE_NEXT_BIO1(bp2) == sc, ("corrupt bio_driver in g_cache_done()")); 23104c7da70SRuslan Ermilov tmpbp = G_CACHE_NEXT_BIO2(bp2); 23204c7da70SRuslan Ermilov g_cache_deliver(sc, bp2, dp, bp->bio_error); 23304c7da70SRuslan Ermilov bp2 = tmpbp; 23404c7da70SRuslan Ermilov } 23504c7da70SRuslan Ermilov dp->d_biolist = NULL; 23604c7da70SRuslan Ermilov if (dp->d_flags & D_FLAG_INVALID) { 23704c7da70SRuslan Ermilov sc->sc_invalid--; 23804c7da70SRuslan Ermilov g_cache_free(sc, dp); 23904c7da70SRuslan Ermilov } else if (bp->bio_error) { 24004c7da70SRuslan Ermilov LIST_REMOVE(dp, d_next); 24104c7da70SRuslan Ermilov if (dp->d_flags & D_FLAG_USED) { 24204c7da70SRuslan Ermilov TAILQ_REMOVE(&sc->sc_usedlist, dp, d_used); 24304c7da70SRuslan Ermilov sc->sc_nused--; 24404c7da70SRuslan Ermilov } 24504c7da70SRuslan Ermilov g_cache_free(sc, dp); 24604c7da70SRuslan Ermilov } 24704c7da70SRuslan Ermilov mtx_unlock(&sc->sc_mtx); 24804c7da70SRuslan Ermilov g_destroy_bio(bp); 24904c7da70SRuslan Ermilov } 25004c7da70SRuslan Ermilov 25104c7da70SRuslan Ermilov static struct g_cache_desc * 25204c7da70SRuslan Ermilov g_cache_lookup(struct g_cache_softc *sc, off_t bno) 25304c7da70SRuslan Ermilov { 25404c7da70SRuslan Ermilov struct g_cache_desc *dp; 25504c7da70SRuslan Ermilov 25604c7da70SRuslan Ermilov mtx_assert(&sc->sc_mtx, MA_OWNED); 25704c7da70SRuslan Ermilov 25804c7da70SRuslan Ermilov LIST_FOREACH(dp, &sc->sc_desclist[G_CACHE_BUCKET(bno)], d_next) 25904c7da70SRuslan Ermilov if (dp->d_bno == bno) 26004c7da70SRuslan Ermilov return (dp); 26104c7da70SRuslan Ermilov return (NULL); 26204c7da70SRuslan Ermilov } 26304c7da70SRuslan Ermilov 26404c7da70SRuslan Ermilov static int 26504c7da70SRuslan Ermilov g_cache_read(struct g_cache_softc *sc, struct bio *bp) 26604c7da70SRuslan Ermilov { 26704c7da70SRuslan Ermilov struct bio *cbp; 26804c7da70SRuslan Ermilov struct g_cache_desc *dp; 26904c7da70SRuslan Ermilov 27004c7da70SRuslan Ermilov mtx_lock(&sc->sc_mtx); 27104c7da70SRuslan Ermilov dp = g_cache_lookup(sc, 27204c7da70SRuslan Ermilov OFF2BNO(bp->bio_offset + bp->bio_completed, sc)); 27304c7da70SRuslan Ermilov if (dp != NULL) { 27404c7da70SRuslan Ermilov /* Add to waiters list or deliver. */ 27504c7da70SRuslan Ermilov sc->sc_cachehits++; 27604c7da70SRuslan Ermilov if (dp->d_biolist != NULL) { 27704c7da70SRuslan Ermilov G_CACHE_NEXT_BIO1(bp) = sc; 27804c7da70SRuslan Ermilov G_CACHE_NEXT_BIO2(bp) = dp->d_biolist; 27904c7da70SRuslan Ermilov dp->d_biolist = bp; 28004c7da70SRuslan Ermilov } else 28104c7da70SRuslan Ermilov g_cache_deliver(sc, bp, dp, 0); 28204c7da70SRuslan Ermilov mtx_unlock(&sc->sc_mtx); 28304c7da70SRuslan Ermilov return (0); 28404c7da70SRuslan Ermilov } 28504c7da70SRuslan Ermilov 28604c7da70SRuslan Ermilov /* Cache miss. Allocate entry and schedule bio. */ 28704c7da70SRuslan Ermilov sc->sc_cachemisses++; 28804c7da70SRuslan Ermilov dp = g_cache_alloc(sc); 28904c7da70SRuslan Ermilov if (dp == NULL) { 29004c7da70SRuslan Ermilov mtx_unlock(&sc->sc_mtx); 29104c7da70SRuslan Ermilov return (ENOMEM); 29204c7da70SRuslan Ermilov } 29304c7da70SRuslan Ermilov cbp = g_clone_bio(bp); 29404c7da70SRuslan Ermilov if (cbp == NULL) { 29504c7da70SRuslan Ermilov g_cache_free(sc, dp); 29604c7da70SRuslan Ermilov mtx_unlock(&sc->sc_mtx); 29704c7da70SRuslan Ermilov return (ENOMEM); 29804c7da70SRuslan Ermilov } 29904c7da70SRuslan Ermilov 30004c7da70SRuslan Ermilov dp->d_bno = OFF2BNO(bp->bio_offset + bp->bio_completed, sc); 30104c7da70SRuslan Ermilov G_CACHE_NEXT_BIO1(bp) = sc; 30204c7da70SRuslan Ermilov G_CACHE_NEXT_BIO2(bp) = NULL; 30304c7da70SRuslan Ermilov dp->d_biolist = bp; 30404c7da70SRuslan Ermilov LIST_INSERT_HEAD(&sc->sc_desclist[G_CACHE_BUCKET(dp->d_bno)], 30504c7da70SRuslan Ermilov dp, d_next); 30604c7da70SRuslan Ermilov mtx_unlock(&sc->sc_mtx); 30704c7da70SRuslan Ermilov 30804c7da70SRuslan Ermilov G_CACHE_DESC1(cbp) = sc; 30904c7da70SRuslan Ermilov G_CACHE_DESC2(cbp) = dp; 31004c7da70SRuslan Ermilov cbp->bio_done = g_cache_done; 31104c7da70SRuslan Ermilov cbp->bio_offset = BNO2OFF(dp->d_bno, sc); 31204c7da70SRuslan Ermilov cbp->bio_data = dp->d_data; 31304c7da70SRuslan Ermilov cbp->bio_length = sc->sc_bsize; 31404c7da70SRuslan Ermilov g_io_request(cbp, LIST_FIRST(&bp->bio_to->geom->consumer)); 31504c7da70SRuslan Ermilov return (0); 31604c7da70SRuslan Ermilov } 31704c7da70SRuslan Ermilov 31804c7da70SRuslan Ermilov static void 31904c7da70SRuslan Ermilov g_cache_invalidate(struct g_cache_softc *sc, struct bio *bp) 32004c7da70SRuslan Ermilov { 32104c7da70SRuslan Ermilov struct g_cache_desc *dp; 32204c7da70SRuslan Ermilov off_t bno, lim; 32304c7da70SRuslan Ermilov 32404c7da70SRuslan Ermilov mtx_lock(&sc->sc_mtx); 32504c7da70SRuslan Ermilov bno = OFF2BNO(bp->bio_offset, sc); 32604c7da70SRuslan Ermilov lim = OFF2BNO(bp->bio_offset + bp->bio_length - 1, sc); 32704c7da70SRuslan Ermilov do { 32804c7da70SRuslan Ermilov if ((dp = g_cache_lookup(sc, bno)) != NULL) { 32904c7da70SRuslan Ermilov LIST_REMOVE(dp, d_next); 33004c7da70SRuslan Ermilov if (dp->d_flags & D_FLAG_USED) { 33104c7da70SRuslan Ermilov TAILQ_REMOVE(&sc->sc_usedlist, dp, d_used); 33204c7da70SRuslan Ermilov sc->sc_nused--; 33304c7da70SRuslan Ermilov } 33404c7da70SRuslan Ermilov if (dp->d_biolist == NULL) 33504c7da70SRuslan Ermilov g_cache_free(sc, dp); 33604c7da70SRuslan Ermilov else { 33704c7da70SRuslan Ermilov dp->d_flags = D_FLAG_INVALID; 33804c7da70SRuslan Ermilov sc->sc_invalid++; 33904c7da70SRuslan Ermilov } 34004c7da70SRuslan Ermilov } 34104c7da70SRuslan Ermilov bno++; 34204c7da70SRuslan Ermilov } while (bno <= lim); 34304c7da70SRuslan Ermilov mtx_unlock(&sc->sc_mtx); 34404c7da70SRuslan Ermilov } 34504c7da70SRuslan Ermilov 34604c7da70SRuslan Ermilov static void 34704c7da70SRuslan Ermilov g_cache_start(struct bio *bp) 34804c7da70SRuslan Ermilov { 34904c7da70SRuslan Ermilov struct g_cache_softc *sc; 35004c7da70SRuslan Ermilov struct g_geom *gp; 35104c7da70SRuslan Ermilov struct g_cache_desc *dp; 35204c7da70SRuslan Ermilov struct bio *cbp; 35304c7da70SRuslan Ermilov 35404c7da70SRuslan Ermilov gp = bp->bio_to->geom; 35504c7da70SRuslan Ermilov sc = gp->softc; 35604c7da70SRuslan Ermilov G_CACHE_LOGREQ(bp, "Request received."); 35704c7da70SRuslan Ermilov switch (bp->bio_cmd) { 35804c7da70SRuslan Ermilov case BIO_READ: 35904c7da70SRuslan Ermilov sc->sc_reads++; 36004c7da70SRuslan Ermilov sc->sc_readbytes += bp->bio_length; 36104c7da70SRuslan Ermilov if (!g_cache_enable) 36204c7da70SRuslan Ermilov break; 36304c7da70SRuslan Ermilov if (bp->bio_offset + bp->bio_length > sc->sc_tail) 36404c7da70SRuslan Ermilov break; 36504c7da70SRuslan Ermilov if (OFF2BNO(bp->bio_offset, sc) == 36604c7da70SRuslan Ermilov OFF2BNO(bp->bio_offset + bp->bio_length - 1, sc)) { 36704c7da70SRuslan Ermilov sc->sc_cachereads++; 36804c7da70SRuslan Ermilov sc->sc_cachereadbytes += bp->bio_length; 36904c7da70SRuslan Ermilov if (g_cache_read(sc, bp) == 0) 37004c7da70SRuslan Ermilov return; 37104c7da70SRuslan Ermilov sc->sc_cachereads--; 37204c7da70SRuslan Ermilov sc->sc_cachereadbytes -= bp->bio_length; 37304c7da70SRuslan Ermilov break; 37404c7da70SRuslan Ermilov } else if (OFF2BNO(bp->bio_offset, sc) + 1 == 37504c7da70SRuslan Ermilov OFF2BNO(bp->bio_offset + bp->bio_length - 1, sc)) { 37604c7da70SRuslan Ermilov mtx_lock(&sc->sc_mtx); 37704c7da70SRuslan Ermilov dp = g_cache_lookup(sc, OFF2BNO(bp->bio_offset, sc)); 37804c7da70SRuslan Ermilov if (dp == NULL || dp->d_biolist != NULL) { 37904c7da70SRuslan Ermilov mtx_unlock(&sc->sc_mtx); 38004c7da70SRuslan Ermilov break; 38104c7da70SRuslan Ermilov } 38204c7da70SRuslan Ermilov sc->sc_cachereads++; 38304c7da70SRuslan Ermilov sc->sc_cachereadbytes += bp->bio_length; 38404c7da70SRuslan Ermilov g_cache_deliver(sc, bp, dp, 0); 38504c7da70SRuslan Ermilov mtx_unlock(&sc->sc_mtx); 38604c7da70SRuslan Ermilov if (g_cache_read(sc, bp) == 0) 38704c7da70SRuslan Ermilov return; 38804c7da70SRuslan Ermilov sc->sc_cachereads--; 38904c7da70SRuslan Ermilov sc->sc_cachereadbytes -= bp->bio_length; 39004c7da70SRuslan Ermilov break; 39104c7da70SRuslan Ermilov } 39204c7da70SRuslan Ermilov break; 39304c7da70SRuslan Ermilov case BIO_WRITE: 39404c7da70SRuslan Ermilov sc->sc_writes++; 39504c7da70SRuslan Ermilov sc->sc_wrotebytes += bp->bio_length; 39604c7da70SRuslan Ermilov g_cache_invalidate(sc, bp); 39704c7da70SRuslan Ermilov break; 39804c7da70SRuslan Ermilov } 39904c7da70SRuslan Ermilov cbp = g_clone_bio(bp); 40004c7da70SRuslan Ermilov if (cbp == NULL) { 40104c7da70SRuslan Ermilov g_io_deliver(bp, ENOMEM); 40204c7da70SRuslan Ermilov return; 40304c7da70SRuslan Ermilov } 40404c7da70SRuslan Ermilov cbp->bio_done = g_std_done; 40504c7da70SRuslan Ermilov G_CACHE_LOGREQ(cbp, "Sending request."); 40604c7da70SRuslan Ermilov g_io_request(cbp, LIST_FIRST(&gp->consumer)); 40704c7da70SRuslan Ermilov } 40804c7da70SRuslan Ermilov 40904c7da70SRuslan Ermilov static void 41004c7da70SRuslan Ermilov g_cache_go(void *arg) 41104c7da70SRuslan Ermilov { 41204c7da70SRuslan Ermilov struct g_cache_softc *sc = arg; 41304c7da70SRuslan Ermilov struct g_cache_desc *dp; 41404c7da70SRuslan Ermilov int i; 41504c7da70SRuslan Ermilov 41604c7da70SRuslan Ermilov mtx_assert(&sc->sc_mtx, MA_OWNED); 41704c7da70SRuslan Ermilov 41804c7da70SRuslan Ermilov /* Forcibly mark idle ready entries as used. */ 41904c7da70SRuslan Ermilov for (i = 0; i < G_CACHE_BUCKETS; i++) { 42004c7da70SRuslan Ermilov LIST_FOREACH(dp, &sc->sc_desclist[i], d_next) { 42104c7da70SRuslan Ermilov if (dp->d_flags & D_FLAG_USED || 42204c7da70SRuslan Ermilov dp->d_biolist != NULL || 42304c7da70SRuslan Ermilov time_uptime - dp->d_atime < g_cache_idletime) 42404c7da70SRuslan Ermilov continue; 42504c7da70SRuslan Ermilov TAILQ_INSERT_TAIL(&sc->sc_usedlist, dp, d_used); 42604c7da70SRuslan Ermilov sc->sc_nused++; 42704c7da70SRuslan Ermilov dp->d_flags |= D_FLAG_USED; 42804c7da70SRuslan Ermilov } 42904c7da70SRuslan Ermilov } 43004c7da70SRuslan Ermilov 43104c7da70SRuslan Ermilov /* Keep the number of used entries low. */ 43204c7da70SRuslan Ermilov if (sc->sc_nused > g_cache_used_hi * sc->sc_maxent / 100) 43304c7da70SRuslan Ermilov g_cache_free_used(sc); 43404c7da70SRuslan Ermilov 43504c7da70SRuslan Ermilov callout_reset(&sc->sc_callout, g_cache_timeout * hz, g_cache_go, sc); 43604c7da70SRuslan Ermilov } 43704c7da70SRuslan Ermilov 43804c7da70SRuslan Ermilov static int 43904c7da70SRuslan Ermilov g_cache_access(struct g_provider *pp, int dr, int dw, int de) 44004c7da70SRuslan Ermilov { 44104c7da70SRuslan Ermilov struct g_geom *gp; 44204c7da70SRuslan Ermilov struct g_consumer *cp; 44304c7da70SRuslan Ermilov int error; 44404c7da70SRuslan Ermilov 44504c7da70SRuslan Ermilov gp = pp->geom; 44604c7da70SRuslan Ermilov cp = LIST_FIRST(&gp->consumer); 44704c7da70SRuslan Ermilov error = g_access(cp, dr, dw, de); 44804c7da70SRuslan Ermilov 44904c7da70SRuslan Ermilov return (error); 45004c7da70SRuslan Ermilov } 45104c7da70SRuslan Ermilov 45204c7da70SRuslan Ermilov static void 45304c7da70SRuslan Ermilov g_cache_orphan(struct g_consumer *cp) 45404c7da70SRuslan Ermilov { 45504c7da70SRuslan Ermilov 45604c7da70SRuslan Ermilov g_topology_assert(); 45704c7da70SRuslan Ermilov g_cache_destroy(cp->geom->softc, 1); 45804c7da70SRuslan Ermilov } 45904c7da70SRuslan Ermilov 46004c7da70SRuslan Ermilov static struct g_cache_softc * 46104c7da70SRuslan Ermilov g_cache_find_device(struct g_class *mp, const char *name) 46204c7da70SRuslan Ermilov { 46304c7da70SRuslan Ermilov struct g_geom *gp; 46404c7da70SRuslan Ermilov 46504c7da70SRuslan Ermilov LIST_FOREACH(gp, &mp->geom, geom) { 46604c7da70SRuslan Ermilov if (strcmp(gp->name, name) == 0) 46704c7da70SRuslan Ermilov return (gp->softc); 46804c7da70SRuslan Ermilov } 46904c7da70SRuslan Ermilov return (NULL); 47004c7da70SRuslan Ermilov } 47104c7da70SRuslan Ermilov 47204c7da70SRuslan Ermilov static struct g_geom * 47304c7da70SRuslan Ermilov g_cache_create(struct g_class *mp, struct g_provider *pp, 47404c7da70SRuslan Ermilov const struct g_cache_metadata *md, u_int type) 47504c7da70SRuslan Ermilov { 47604c7da70SRuslan Ermilov struct g_cache_softc *sc; 47704c7da70SRuslan Ermilov struct g_geom *gp; 47804c7da70SRuslan Ermilov struct g_provider *newpp; 47904c7da70SRuslan Ermilov struct g_consumer *cp; 48004c7da70SRuslan Ermilov u_int bshift; 48104c7da70SRuslan Ermilov int i; 48204c7da70SRuslan Ermilov 48304c7da70SRuslan Ermilov g_topology_assert(); 48404c7da70SRuslan Ermilov 48504c7da70SRuslan Ermilov gp = NULL; 48604c7da70SRuslan Ermilov newpp = NULL; 48704c7da70SRuslan Ermilov cp = NULL; 48804c7da70SRuslan Ermilov 48904c7da70SRuslan Ermilov G_CACHE_DEBUG(1, "Creating device %s.", md->md_name); 49004c7da70SRuslan Ermilov 49104c7da70SRuslan Ermilov /* Cache size is minimum 100. */ 49204c7da70SRuslan Ermilov if (md->md_size < 100) { 49304c7da70SRuslan Ermilov G_CACHE_DEBUG(0, "Invalid size for device %s.", md->md_name); 49404c7da70SRuslan Ermilov return (NULL); 49504c7da70SRuslan Ermilov } 49604c7da70SRuslan Ermilov 49704c7da70SRuslan Ermilov /* Block size restrictions. */ 49804c7da70SRuslan Ermilov bshift = ffs(md->md_bsize) - 1; 49904c7da70SRuslan Ermilov if (md->md_bsize == 0 || md->md_bsize > MAXPHYS || 50004c7da70SRuslan Ermilov md->md_bsize != 1 << bshift || 50104c7da70SRuslan Ermilov (md->md_bsize % pp->sectorsize) != 0) { 50204c7da70SRuslan Ermilov G_CACHE_DEBUG(0, "Invalid blocksize for provider %s.", pp->name); 50304c7da70SRuslan Ermilov return (NULL); 50404c7da70SRuslan Ermilov } 50504c7da70SRuslan Ermilov 50604c7da70SRuslan Ermilov /* Check for duplicate unit. */ 50704c7da70SRuslan Ermilov if (g_cache_find_device(mp, (const char *)&md->md_name) != NULL) { 50804c7da70SRuslan Ermilov G_CACHE_DEBUG(0, "Provider %s already exists.", md->md_name); 50904c7da70SRuslan Ermilov return (NULL); 51004c7da70SRuslan Ermilov } 51104c7da70SRuslan Ermilov 51202c62349SJaakko Heinonen gp = g_new_geomf(mp, "%s", md->md_name); 51304c7da70SRuslan Ermilov sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO); 51404c7da70SRuslan Ermilov sc->sc_type = type; 51504c7da70SRuslan Ermilov sc->sc_bshift = bshift; 51604c7da70SRuslan Ermilov sc->sc_bsize = 1 << bshift; 51704c7da70SRuslan Ermilov sc->sc_zone = uma_zcreate("gcache", sc->sc_bsize, NULL, NULL, NULL, NULL, 51804c7da70SRuslan Ermilov UMA_ALIGN_PTR, 0); 51904c7da70SRuslan Ermilov mtx_init(&sc->sc_mtx, "GEOM CACHE mutex", NULL, MTX_DEF); 52004c7da70SRuslan Ermilov for (i = 0; i < G_CACHE_BUCKETS; i++) 52104c7da70SRuslan Ermilov LIST_INIT(&sc->sc_desclist[i]); 52204c7da70SRuslan Ermilov TAILQ_INIT(&sc->sc_usedlist); 52304c7da70SRuslan Ermilov sc->sc_maxent = md->md_size; 52404c7da70SRuslan Ermilov callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0); 52504c7da70SRuslan Ermilov gp->softc = sc; 52604c7da70SRuslan Ermilov sc->sc_geom = gp; 52704c7da70SRuslan Ermilov gp->start = g_cache_start; 52804c7da70SRuslan Ermilov gp->orphan = g_cache_orphan; 52904c7da70SRuslan Ermilov gp->access = g_cache_access; 53004c7da70SRuslan Ermilov gp->dumpconf = g_cache_dumpconf; 53104c7da70SRuslan Ermilov 53204c7da70SRuslan Ermilov newpp = g_new_providerf(gp, "cache/%s", gp->name); 53304c7da70SRuslan Ermilov newpp->sectorsize = pp->sectorsize; 53404c7da70SRuslan Ermilov newpp->mediasize = pp->mediasize; 53504c7da70SRuslan Ermilov if (type == G_CACHE_TYPE_AUTOMATIC) 53604c7da70SRuslan Ermilov newpp->mediasize -= pp->sectorsize; 53704c7da70SRuslan Ermilov sc->sc_tail = BNO2OFF(OFF2BNO(newpp->mediasize, sc), sc); 53804c7da70SRuslan Ermilov 53904c7da70SRuslan Ermilov cp = g_new_consumer(gp); 54004c7da70SRuslan Ermilov if (g_attach(cp, pp) != 0) { 54104c7da70SRuslan Ermilov G_CACHE_DEBUG(0, "Cannot attach to provider %s.", pp->name); 542e62dffbfSAndrey V. Elsukov g_destroy_consumer(cp); 543e62dffbfSAndrey V. Elsukov g_destroy_provider(newpp); 544e62dffbfSAndrey V. Elsukov mtx_destroy(&sc->sc_mtx); 545e62dffbfSAndrey V. Elsukov g_free(sc); 546e62dffbfSAndrey V. Elsukov g_destroy_geom(gp); 547e62dffbfSAndrey V. Elsukov return (NULL); 54804c7da70SRuslan Ermilov } 54904c7da70SRuslan Ermilov 55004c7da70SRuslan Ermilov g_error_provider(newpp, 0); 55104c7da70SRuslan Ermilov G_CACHE_DEBUG(0, "Device %s created.", gp->name); 55204c7da70SRuslan Ermilov callout_reset(&sc->sc_callout, g_cache_timeout * hz, g_cache_go, sc); 55304c7da70SRuslan Ermilov return (gp); 55404c7da70SRuslan Ermilov } 55504c7da70SRuslan Ermilov 55604c7da70SRuslan Ermilov static int 55704c7da70SRuslan Ermilov g_cache_destroy(struct g_cache_softc *sc, boolean_t force) 55804c7da70SRuslan Ermilov { 55904c7da70SRuslan Ermilov struct g_geom *gp; 56004c7da70SRuslan Ermilov struct g_provider *pp; 56104c7da70SRuslan Ermilov struct g_cache_desc *dp, *dp2; 56204c7da70SRuslan Ermilov int i; 56304c7da70SRuslan Ermilov 56404c7da70SRuslan Ermilov g_topology_assert(); 56504c7da70SRuslan Ermilov if (sc == NULL) 56604c7da70SRuslan Ermilov return (ENXIO); 56704c7da70SRuslan Ermilov gp = sc->sc_geom; 56804c7da70SRuslan Ermilov pp = LIST_FIRST(&gp->provider); 56904c7da70SRuslan Ermilov if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { 57004c7da70SRuslan Ermilov if (force) { 57104c7da70SRuslan Ermilov G_CACHE_DEBUG(0, "Device %s is still open, so it " 57204c7da70SRuslan Ermilov "can't be definitely removed.", pp->name); 57304c7da70SRuslan Ermilov } else { 57404c7da70SRuslan Ermilov G_CACHE_DEBUG(1, "Device %s is still open (r%dw%de%d).", 57504c7da70SRuslan Ermilov pp->name, pp->acr, pp->acw, pp->ace); 57604c7da70SRuslan Ermilov return (EBUSY); 57704c7da70SRuslan Ermilov } 57804c7da70SRuslan Ermilov } else { 57904c7da70SRuslan Ermilov G_CACHE_DEBUG(0, "Device %s removed.", gp->name); 58004c7da70SRuslan Ermilov } 58104c7da70SRuslan Ermilov callout_drain(&sc->sc_callout); 58204c7da70SRuslan Ermilov mtx_lock(&sc->sc_mtx); 58304c7da70SRuslan Ermilov for (i = 0; i < G_CACHE_BUCKETS; i++) { 58404c7da70SRuslan Ermilov dp = LIST_FIRST(&sc->sc_desclist[i]); 58504c7da70SRuslan Ermilov while (dp != NULL) { 58604c7da70SRuslan Ermilov dp2 = LIST_NEXT(dp, d_next); 58704c7da70SRuslan Ermilov g_cache_free(sc, dp); 58804c7da70SRuslan Ermilov dp = dp2; 58904c7da70SRuslan Ermilov } 59004c7da70SRuslan Ermilov } 59104c7da70SRuslan Ermilov mtx_unlock(&sc->sc_mtx); 59204c7da70SRuslan Ermilov mtx_destroy(&sc->sc_mtx); 59304c7da70SRuslan Ermilov uma_zdestroy(sc->sc_zone); 59404c7da70SRuslan Ermilov g_free(sc); 59504c7da70SRuslan Ermilov gp->softc = NULL; 59604c7da70SRuslan Ermilov g_wither_geom(gp, ENXIO); 59704c7da70SRuslan Ermilov 59804c7da70SRuslan Ermilov return (0); 59904c7da70SRuslan Ermilov } 60004c7da70SRuslan Ermilov 60104c7da70SRuslan Ermilov static int 60204c7da70SRuslan Ermilov g_cache_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp) 60304c7da70SRuslan Ermilov { 60404c7da70SRuslan Ermilov 60504c7da70SRuslan Ermilov return (g_cache_destroy(gp->softc, 0)); 60604c7da70SRuslan Ermilov } 60704c7da70SRuslan Ermilov 60804c7da70SRuslan Ermilov static int 60904c7da70SRuslan Ermilov g_cache_read_metadata(struct g_consumer *cp, struct g_cache_metadata *md) 61004c7da70SRuslan Ermilov { 61104c7da70SRuslan Ermilov struct g_provider *pp; 61204c7da70SRuslan Ermilov u_char *buf; 61304c7da70SRuslan Ermilov int error; 61404c7da70SRuslan Ermilov 61504c7da70SRuslan Ermilov g_topology_assert(); 61604c7da70SRuslan Ermilov 61704c7da70SRuslan Ermilov error = g_access(cp, 1, 0, 0); 61804c7da70SRuslan Ermilov if (error != 0) 61904c7da70SRuslan Ermilov return (error); 62004c7da70SRuslan Ermilov pp = cp->provider; 62104c7da70SRuslan Ermilov g_topology_unlock(); 62204c7da70SRuslan Ermilov buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, 62304c7da70SRuslan Ermilov &error); 62404c7da70SRuslan Ermilov g_topology_lock(); 62504c7da70SRuslan Ermilov g_access(cp, -1, 0, 0); 62604c7da70SRuslan Ermilov if (buf == NULL) 62704c7da70SRuslan Ermilov return (error); 62804c7da70SRuslan Ermilov 62904c7da70SRuslan Ermilov /* Decode metadata. */ 63004c7da70SRuslan Ermilov cache_metadata_decode(buf, md); 63104c7da70SRuslan Ermilov g_free(buf); 63204c7da70SRuslan Ermilov 63304c7da70SRuslan Ermilov return (0); 63404c7da70SRuslan Ermilov } 63504c7da70SRuslan Ermilov 63604c7da70SRuslan Ermilov static int 63704c7da70SRuslan Ermilov g_cache_write_metadata(struct g_consumer *cp, struct g_cache_metadata *md) 63804c7da70SRuslan Ermilov { 63904c7da70SRuslan Ermilov struct g_provider *pp; 64004c7da70SRuslan Ermilov u_char *buf; 64104c7da70SRuslan Ermilov int error; 64204c7da70SRuslan Ermilov 64304c7da70SRuslan Ermilov g_topology_assert(); 64404c7da70SRuslan Ermilov 64504c7da70SRuslan Ermilov error = g_access(cp, 0, 1, 0); 64604c7da70SRuslan Ermilov if (error != 0) 64704c7da70SRuslan Ermilov return (error); 64804c7da70SRuslan Ermilov pp = cp->provider; 64904c7da70SRuslan Ermilov buf = malloc((size_t)pp->sectorsize, M_GCACHE, M_WAITOK | M_ZERO); 65004c7da70SRuslan Ermilov cache_metadata_encode(md, buf); 65104c7da70SRuslan Ermilov g_topology_unlock(); 65204c7da70SRuslan Ermilov error = g_write_data(cp, pp->mediasize - pp->sectorsize, buf, pp->sectorsize); 65304c7da70SRuslan Ermilov g_topology_lock(); 65404c7da70SRuslan Ermilov g_access(cp, 0, -1, 0); 65504c7da70SRuslan Ermilov free(buf, M_GCACHE); 65604c7da70SRuslan Ermilov 65704c7da70SRuslan Ermilov return (error); 65804c7da70SRuslan Ermilov } 65904c7da70SRuslan Ermilov 66004c7da70SRuslan Ermilov static struct g_geom * 66104c7da70SRuslan Ermilov g_cache_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 66204c7da70SRuslan Ermilov { 66304c7da70SRuslan Ermilov struct g_cache_metadata md; 66404c7da70SRuslan Ermilov struct g_consumer *cp; 66504c7da70SRuslan Ermilov struct g_geom *gp; 66604c7da70SRuslan Ermilov int error; 66704c7da70SRuslan Ermilov 66804c7da70SRuslan Ermilov g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name); 66904c7da70SRuslan Ermilov g_topology_assert(); 67004c7da70SRuslan Ermilov 67104c7da70SRuslan Ermilov G_CACHE_DEBUG(3, "Tasting %s.", pp->name); 67204c7da70SRuslan Ermilov 67304c7da70SRuslan Ermilov gp = g_new_geomf(mp, "cache:taste"); 67404c7da70SRuslan Ermilov gp->start = g_cache_start; 67504c7da70SRuslan Ermilov gp->orphan = g_cache_orphan; 67604c7da70SRuslan Ermilov gp->access = g_cache_access; 67704c7da70SRuslan Ermilov cp = g_new_consumer(gp); 67804c7da70SRuslan Ermilov g_attach(cp, pp); 67904c7da70SRuslan Ermilov error = g_cache_read_metadata(cp, &md); 68004c7da70SRuslan Ermilov g_detach(cp); 68104c7da70SRuslan Ermilov g_destroy_consumer(cp); 68204c7da70SRuslan Ermilov g_destroy_geom(gp); 68304c7da70SRuslan Ermilov if (error != 0) 68404c7da70SRuslan Ermilov return (NULL); 68504c7da70SRuslan Ermilov 68604c7da70SRuslan Ermilov if (strcmp(md.md_magic, G_CACHE_MAGIC) != 0) 68704c7da70SRuslan Ermilov return (NULL); 68804c7da70SRuslan Ermilov if (md.md_version > G_CACHE_VERSION) { 68904c7da70SRuslan Ermilov printf("geom_cache.ko module is too old to handle %s.\n", 69004c7da70SRuslan Ermilov pp->name); 69104c7da70SRuslan Ermilov return (NULL); 69204c7da70SRuslan Ermilov } 69304c7da70SRuslan Ermilov if (md.md_provsize != pp->mediasize) 69404c7da70SRuslan Ermilov return (NULL); 69504c7da70SRuslan Ermilov 69604c7da70SRuslan Ermilov gp = g_cache_create(mp, pp, &md, G_CACHE_TYPE_AUTOMATIC); 69704c7da70SRuslan Ermilov if (gp == NULL) { 69804c7da70SRuslan Ermilov G_CACHE_DEBUG(0, "Can't create %s.", md.md_name); 69904c7da70SRuslan Ermilov return (NULL); 70004c7da70SRuslan Ermilov } 70104c7da70SRuslan Ermilov return (gp); 70204c7da70SRuslan Ermilov } 70304c7da70SRuslan Ermilov 70404c7da70SRuslan Ermilov static void 70504c7da70SRuslan Ermilov g_cache_ctl_create(struct gctl_req *req, struct g_class *mp) 70604c7da70SRuslan Ermilov { 70704c7da70SRuslan Ermilov struct g_cache_metadata md; 70804c7da70SRuslan Ermilov struct g_provider *pp; 70904c7da70SRuslan Ermilov struct g_geom *gp; 71004c7da70SRuslan Ermilov intmax_t *bsize, *size; 71104c7da70SRuslan Ermilov const char *name; 71204c7da70SRuslan Ermilov int *nargs; 71304c7da70SRuslan Ermilov 71404c7da70SRuslan Ermilov g_topology_assert(); 71504c7da70SRuslan Ermilov 71604c7da70SRuslan Ermilov nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 71704c7da70SRuslan Ermilov if (nargs == NULL) { 71804c7da70SRuslan Ermilov gctl_error(req, "No '%s' argument", "nargs"); 71904c7da70SRuslan Ermilov return; 72004c7da70SRuslan Ermilov } 72104c7da70SRuslan Ermilov if (*nargs != 2) { 72204c7da70SRuslan Ermilov gctl_error(req, "Invalid number of arguments."); 72304c7da70SRuslan Ermilov return; 72404c7da70SRuslan Ermilov } 72504c7da70SRuslan Ermilov 72604c7da70SRuslan Ermilov strlcpy(md.md_magic, G_CACHE_MAGIC, sizeof(md.md_magic)); 72704c7da70SRuslan Ermilov md.md_version = G_CACHE_VERSION; 72804c7da70SRuslan Ermilov name = gctl_get_asciiparam(req, "arg0"); 72904c7da70SRuslan Ermilov if (name == NULL) { 73004c7da70SRuslan Ermilov gctl_error(req, "No 'arg0' argument"); 73104c7da70SRuslan Ermilov return; 73204c7da70SRuslan Ermilov } 73304c7da70SRuslan Ermilov strlcpy(md.md_name, name, sizeof(md.md_name)); 73404c7da70SRuslan Ermilov 73504c7da70SRuslan Ermilov size = gctl_get_paraml(req, "size", sizeof(*size)); 73604c7da70SRuslan Ermilov if (size == NULL) { 73704c7da70SRuslan Ermilov gctl_error(req, "No '%s' argument", "size"); 73804c7da70SRuslan Ermilov return; 73904c7da70SRuslan Ermilov } 74004c7da70SRuslan Ermilov if ((u_int)*size < 100) { 74104c7da70SRuslan Ermilov gctl_error(req, "Invalid '%s' argument", "size"); 74204c7da70SRuslan Ermilov return; 74304c7da70SRuslan Ermilov } 74404c7da70SRuslan Ermilov md.md_size = (u_int)*size; 74504c7da70SRuslan Ermilov 74604c7da70SRuslan Ermilov bsize = gctl_get_paraml(req, "blocksize", sizeof(*bsize)); 74704c7da70SRuslan Ermilov if (bsize == NULL) { 74804c7da70SRuslan Ermilov gctl_error(req, "No '%s' argument", "blocksize"); 74904c7da70SRuslan Ermilov return; 75004c7da70SRuslan Ermilov } 75104c7da70SRuslan Ermilov if (*bsize < 0) { 75204c7da70SRuslan Ermilov gctl_error(req, "Invalid '%s' argument", "blocksize"); 75304c7da70SRuslan Ermilov return; 75404c7da70SRuslan Ermilov } 75504c7da70SRuslan Ermilov md.md_bsize = (u_int)*bsize; 75604c7da70SRuslan Ermilov 75704c7da70SRuslan Ermilov /* This field is not important here. */ 75804c7da70SRuslan Ermilov md.md_provsize = 0; 75904c7da70SRuslan Ermilov 76004c7da70SRuslan Ermilov name = gctl_get_asciiparam(req, "arg1"); 76104c7da70SRuslan Ermilov if (name == NULL) { 76204c7da70SRuslan Ermilov gctl_error(req, "No 'arg1' argument"); 76304c7da70SRuslan Ermilov return; 76404c7da70SRuslan Ermilov } 76504c7da70SRuslan Ermilov if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 76604c7da70SRuslan Ermilov name += strlen("/dev/"); 76704c7da70SRuslan Ermilov pp = g_provider_by_name(name); 76804c7da70SRuslan Ermilov if (pp == NULL) { 76904c7da70SRuslan Ermilov G_CACHE_DEBUG(1, "Provider %s is invalid.", name); 77004c7da70SRuslan Ermilov gctl_error(req, "Provider %s is invalid.", name); 77104c7da70SRuslan Ermilov return; 77204c7da70SRuslan Ermilov } 77304c7da70SRuslan Ermilov gp = g_cache_create(mp, pp, &md, G_CACHE_TYPE_MANUAL); 77404c7da70SRuslan Ermilov if (gp == NULL) { 77504c7da70SRuslan Ermilov gctl_error(req, "Can't create %s.", md.md_name); 77604c7da70SRuslan Ermilov return; 77704c7da70SRuslan Ermilov } 77804c7da70SRuslan Ermilov } 77904c7da70SRuslan Ermilov 78004c7da70SRuslan Ermilov static void 78104c7da70SRuslan Ermilov g_cache_ctl_configure(struct gctl_req *req, struct g_class *mp) 78204c7da70SRuslan Ermilov { 78304c7da70SRuslan Ermilov struct g_cache_metadata md; 78404c7da70SRuslan Ermilov struct g_cache_softc *sc; 78504c7da70SRuslan Ermilov struct g_consumer *cp; 78604c7da70SRuslan Ermilov intmax_t *bsize, *size; 78704c7da70SRuslan Ermilov const char *name; 78804c7da70SRuslan Ermilov int error, *nargs; 78904c7da70SRuslan Ermilov 79004c7da70SRuslan Ermilov g_topology_assert(); 79104c7da70SRuslan Ermilov 79204c7da70SRuslan Ermilov nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 79304c7da70SRuslan Ermilov if (nargs == NULL) { 79404c7da70SRuslan Ermilov gctl_error(req, "No '%s' argument", "nargs"); 79504c7da70SRuslan Ermilov return; 79604c7da70SRuslan Ermilov } 79704c7da70SRuslan Ermilov if (*nargs != 1) { 79804c7da70SRuslan Ermilov gctl_error(req, "Missing device."); 79904c7da70SRuslan Ermilov return; 80004c7da70SRuslan Ermilov } 80104c7da70SRuslan Ermilov 80204c7da70SRuslan Ermilov name = gctl_get_asciiparam(req, "arg0"); 80304c7da70SRuslan Ermilov if (name == NULL) { 80404c7da70SRuslan Ermilov gctl_error(req, "No 'arg0' argument"); 80504c7da70SRuslan Ermilov return; 80604c7da70SRuslan Ermilov } 80704c7da70SRuslan Ermilov sc = g_cache_find_device(mp, name); 80804c7da70SRuslan Ermilov if (sc == NULL) { 80904c7da70SRuslan Ermilov G_CACHE_DEBUG(1, "Device %s is invalid.", name); 81004c7da70SRuslan Ermilov gctl_error(req, "Device %s is invalid.", name); 81104c7da70SRuslan Ermilov return; 81204c7da70SRuslan Ermilov } 81304c7da70SRuslan Ermilov 81404c7da70SRuslan Ermilov size = gctl_get_paraml(req, "size", sizeof(*size)); 81504c7da70SRuslan Ermilov if (size == NULL) { 81604c7da70SRuslan Ermilov gctl_error(req, "No '%s' argument", "size"); 81704c7da70SRuslan Ermilov return; 81804c7da70SRuslan Ermilov } 81904c7da70SRuslan Ermilov if ((u_int)*size != 0 && (u_int)*size < 100) { 82004c7da70SRuslan Ermilov gctl_error(req, "Invalid '%s' argument", "size"); 82104c7da70SRuslan Ermilov return; 82204c7da70SRuslan Ermilov } 82304c7da70SRuslan Ermilov if ((u_int)*size != 0) 82404c7da70SRuslan Ermilov sc->sc_maxent = (u_int)*size; 82504c7da70SRuslan Ermilov 82604c7da70SRuslan Ermilov bsize = gctl_get_paraml(req, "blocksize", sizeof(*bsize)); 82704c7da70SRuslan Ermilov if (bsize == NULL) { 82804c7da70SRuslan Ermilov gctl_error(req, "No '%s' argument", "blocksize"); 82904c7da70SRuslan Ermilov return; 83004c7da70SRuslan Ermilov } 83104c7da70SRuslan Ermilov if (*bsize < 0) { 83204c7da70SRuslan Ermilov gctl_error(req, "Invalid '%s' argument", "blocksize"); 83304c7da70SRuslan Ermilov return; 83404c7da70SRuslan Ermilov } 83504c7da70SRuslan Ermilov 83604c7da70SRuslan Ermilov if (sc->sc_type != G_CACHE_TYPE_AUTOMATIC) 83704c7da70SRuslan Ermilov return; 83804c7da70SRuslan Ermilov 83904c7da70SRuslan Ermilov strlcpy(md.md_name, name, sizeof(md.md_name)); 84004c7da70SRuslan Ermilov strlcpy(md.md_magic, G_CACHE_MAGIC, sizeof(md.md_magic)); 84104c7da70SRuslan Ermilov md.md_version = G_CACHE_VERSION; 84204c7da70SRuslan Ermilov if ((u_int)*size != 0) 84304c7da70SRuslan Ermilov md.md_size = (u_int)*size; 84404c7da70SRuslan Ermilov else 84504c7da70SRuslan Ermilov md.md_size = sc->sc_maxent; 84604c7da70SRuslan Ermilov if ((u_int)*bsize != 0) 84704c7da70SRuslan Ermilov md.md_bsize = (u_int)*bsize; 84804c7da70SRuslan Ermilov else 84904c7da70SRuslan Ermilov md.md_bsize = sc->sc_bsize; 85004c7da70SRuslan Ermilov cp = LIST_FIRST(&sc->sc_geom->consumer); 85104c7da70SRuslan Ermilov md.md_provsize = cp->provider->mediasize; 85204c7da70SRuslan Ermilov error = g_cache_write_metadata(cp, &md); 85304c7da70SRuslan Ermilov if (error == 0) 85404c7da70SRuslan Ermilov G_CACHE_DEBUG(2, "Metadata on %s updated.", cp->provider->name); 85504c7da70SRuslan Ermilov else 85604c7da70SRuslan Ermilov G_CACHE_DEBUG(0, "Cannot update metadata on %s (error=%d).", 85704c7da70SRuslan Ermilov cp->provider->name, error); 85804c7da70SRuslan Ermilov } 85904c7da70SRuslan Ermilov 86004c7da70SRuslan Ermilov static void 86104c7da70SRuslan Ermilov g_cache_ctl_destroy(struct gctl_req *req, struct g_class *mp) 86204c7da70SRuslan Ermilov { 86304c7da70SRuslan Ermilov int *nargs, *force, error, i; 86404c7da70SRuslan Ermilov struct g_cache_softc *sc; 86504c7da70SRuslan Ermilov const char *name; 86604c7da70SRuslan Ermilov char param[16]; 86704c7da70SRuslan Ermilov 86804c7da70SRuslan Ermilov g_topology_assert(); 86904c7da70SRuslan Ermilov 87004c7da70SRuslan Ermilov nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 87104c7da70SRuslan Ermilov if (nargs == NULL) { 87204c7da70SRuslan Ermilov gctl_error(req, "No '%s' argument", "nargs"); 87304c7da70SRuslan Ermilov return; 87404c7da70SRuslan Ermilov } 87504c7da70SRuslan Ermilov if (*nargs <= 0) { 87604c7da70SRuslan Ermilov gctl_error(req, "Missing device(s)."); 87704c7da70SRuslan Ermilov return; 87804c7da70SRuslan Ermilov } 87904c7da70SRuslan Ermilov force = gctl_get_paraml(req, "force", sizeof(*force)); 88004c7da70SRuslan Ermilov if (force == NULL) { 88104c7da70SRuslan Ermilov gctl_error(req, "No 'force' argument"); 88204c7da70SRuslan Ermilov return; 88304c7da70SRuslan Ermilov } 88404c7da70SRuslan Ermilov 88504c7da70SRuslan Ermilov for (i = 0; i < *nargs; i++) { 88604c7da70SRuslan Ermilov snprintf(param, sizeof(param), "arg%d", i); 88704c7da70SRuslan Ermilov name = gctl_get_asciiparam(req, param); 88804c7da70SRuslan Ermilov if (name == NULL) { 88904c7da70SRuslan Ermilov gctl_error(req, "No 'arg%d' argument", i); 89004c7da70SRuslan Ermilov return; 89104c7da70SRuslan Ermilov } 89204c7da70SRuslan Ermilov sc = g_cache_find_device(mp, name); 89304c7da70SRuslan Ermilov if (sc == NULL) { 89404c7da70SRuslan Ermilov G_CACHE_DEBUG(1, "Device %s is invalid.", name); 89504c7da70SRuslan Ermilov gctl_error(req, "Device %s is invalid.", name); 89604c7da70SRuslan Ermilov return; 89704c7da70SRuslan Ermilov } 89804c7da70SRuslan Ermilov error = g_cache_destroy(sc, *force); 89904c7da70SRuslan Ermilov if (error != 0) { 90004c7da70SRuslan Ermilov gctl_error(req, "Cannot destroy device %s (error=%d).", 90104c7da70SRuslan Ermilov sc->sc_name, error); 90204c7da70SRuslan Ermilov return; 90304c7da70SRuslan Ermilov } 90404c7da70SRuslan Ermilov } 90504c7da70SRuslan Ermilov } 90604c7da70SRuslan Ermilov 90704c7da70SRuslan Ermilov static void 90804c7da70SRuslan Ermilov g_cache_ctl_reset(struct gctl_req *req, struct g_class *mp) 90904c7da70SRuslan Ermilov { 91004c7da70SRuslan Ermilov struct g_cache_softc *sc; 91104c7da70SRuslan Ermilov const char *name; 91204c7da70SRuslan Ermilov char param[16]; 91304c7da70SRuslan Ermilov int i, *nargs; 91404c7da70SRuslan Ermilov 91504c7da70SRuslan Ermilov g_topology_assert(); 91604c7da70SRuslan Ermilov 91704c7da70SRuslan Ermilov nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 91804c7da70SRuslan Ermilov if (nargs == NULL) { 91904c7da70SRuslan Ermilov gctl_error(req, "No '%s' argument", "nargs"); 92004c7da70SRuslan Ermilov return; 92104c7da70SRuslan Ermilov } 92204c7da70SRuslan Ermilov if (*nargs <= 0) { 92304c7da70SRuslan Ermilov gctl_error(req, "Missing device(s)."); 92404c7da70SRuslan Ermilov return; 92504c7da70SRuslan Ermilov } 92604c7da70SRuslan Ermilov 92704c7da70SRuslan Ermilov for (i = 0; i < *nargs; i++) { 92804c7da70SRuslan Ermilov snprintf(param, sizeof(param), "arg%d", i); 92904c7da70SRuslan Ermilov name = gctl_get_asciiparam(req, param); 93004c7da70SRuslan Ermilov if (name == NULL) { 93104c7da70SRuslan Ermilov gctl_error(req, "No 'arg%d' argument", i); 93204c7da70SRuslan Ermilov return; 93304c7da70SRuslan Ermilov } 93404c7da70SRuslan Ermilov sc = g_cache_find_device(mp, name); 93504c7da70SRuslan Ermilov if (sc == NULL) { 93604c7da70SRuslan Ermilov G_CACHE_DEBUG(1, "Device %s is invalid.", name); 93704c7da70SRuslan Ermilov gctl_error(req, "Device %s is invalid.", name); 93804c7da70SRuslan Ermilov return; 93904c7da70SRuslan Ermilov } 94004c7da70SRuslan Ermilov sc->sc_reads = 0; 94104c7da70SRuslan Ermilov sc->sc_readbytes = 0; 94204c7da70SRuslan Ermilov sc->sc_cachereads = 0; 94304c7da70SRuslan Ermilov sc->sc_cachereadbytes = 0; 94404c7da70SRuslan Ermilov sc->sc_cachehits = 0; 94504c7da70SRuslan Ermilov sc->sc_cachemisses = 0; 94604c7da70SRuslan Ermilov sc->sc_cachefull = 0; 94704c7da70SRuslan Ermilov sc->sc_writes = 0; 94804c7da70SRuslan Ermilov sc->sc_wrotebytes = 0; 94904c7da70SRuslan Ermilov } 95004c7da70SRuslan Ermilov } 95104c7da70SRuslan Ermilov 95204c7da70SRuslan Ermilov static void 95304c7da70SRuslan Ermilov g_cache_config(struct gctl_req *req, struct g_class *mp, const char *verb) 95404c7da70SRuslan Ermilov { 95504c7da70SRuslan Ermilov uint32_t *version; 95604c7da70SRuslan Ermilov 95704c7da70SRuslan Ermilov g_topology_assert(); 95804c7da70SRuslan Ermilov 95904c7da70SRuslan Ermilov version = gctl_get_paraml(req, "version", sizeof(*version)); 96004c7da70SRuslan Ermilov if (version == NULL) { 96104c7da70SRuslan Ermilov gctl_error(req, "No '%s' argument.", "version"); 96204c7da70SRuslan Ermilov return; 96304c7da70SRuslan Ermilov } 96404c7da70SRuslan Ermilov if (*version != G_CACHE_VERSION) { 96504c7da70SRuslan Ermilov gctl_error(req, "Userland and kernel parts are out of sync."); 96604c7da70SRuslan Ermilov return; 96704c7da70SRuslan Ermilov } 96804c7da70SRuslan Ermilov 96904c7da70SRuslan Ermilov if (strcmp(verb, "create") == 0) { 97004c7da70SRuslan Ermilov g_cache_ctl_create(req, mp); 97104c7da70SRuslan Ermilov return; 97204c7da70SRuslan Ermilov } else if (strcmp(verb, "configure") == 0) { 97304c7da70SRuslan Ermilov g_cache_ctl_configure(req, mp); 97404c7da70SRuslan Ermilov return; 97504c7da70SRuslan Ermilov } else if (strcmp(verb, "destroy") == 0 || 97604c7da70SRuslan Ermilov strcmp(verb, "stop") == 0) { 97704c7da70SRuslan Ermilov g_cache_ctl_destroy(req, mp); 97804c7da70SRuslan Ermilov return; 97904c7da70SRuslan Ermilov } else if (strcmp(verb, "reset") == 0) { 98004c7da70SRuslan Ermilov g_cache_ctl_reset(req, mp); 98104c7da70SRuslan Ermilov return; 98204c7da70SRuslan Ermilov } 98304c7da70SRuslan Ermilov 98404c7da70SRuslan Ermilov gctl_error(req, "Unknown verb."); 98504c7da70SRuslan Ermilov } 98604c7da70SRuslan Ermilov 98704c7da70SRuslan Ermilov static void 98804c7da70SRuslan Ermilov g_cache_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, 98904c7da70SRuslan Ermilov struct g_consumer *cp, struct g_provider *pp) 99004c7da70SRuslan Ermilov { 99104c7da70SRuslan Ermilov struct g_cache_softc *sc; 99204c7da70SRuslan Ermilov 99304c7da70SRuslan Ermilov if (pp != NULL || cp != NULL) 99404c7da70SRuslan Ermilov return; 99504c7da70SRuslan Ermilov sc = gp->softc; 99604c7da70SRuslan Ermilov sbuf_printf(sb, "%s<Size>%u</Size>\n", indent, sc->sc_maxent); 99704c7da70SRuslan Ermilov sbuf_printf(sb, "%s<BlockSize>%u</BlockSize>\n", indent, sc->sc_bsize); 99804c7da70SRuslan Ermilov sbuf_printf(sb, "%s<TailOffset>%ju</TailOffset>\n", indent, 99904c7da70SRuslan Ermilov (uintmax_t)sc->sc_tail); 100004c7da70SRuslan Ermilov sbuf_printf(sb, "%s<Entries>%u</Entries>\n", indent, sc->sc_nent); 100104c7da70SRuslan Ermilov sbuf_printf(sb, "%s<UsedEntries>%u</UsedEntries>\n", indent, 100204c7da70SRuslan Ermilov sc->sc_nused); 100304c7da70SRuslan Ermilov sbuf_printf(sb, "%s<InvalidEntries>%u</InvalidEntries>\n", indent, 100404c7da70SRuslan Ermilov sc->sc_invalid); 100504c7da70SRuslan Ermilov sbuf_printf(sb, "%s<Reads>%ju</Reads>\n", indent, sc->sc_reads); 100604c7da70SRuslan Ermilov sbuf_printf(sb, "%s<ReadBytes>%ju</ReadBytes>\n", indent, 100704c7da70SRuslan Ermilov sc->sc_readbytes); 100804c7da70SRuslan Ermilov sbuf_printf(sb, "%s<CacheReads>%ju</CacheReads>\n", indent, 100904c7da70SRuslan Ermilov sc->sc_cachereads); 101004c7da70SRuslan Ermilov sbuf_printf(sb, "%s<CacheReadBytes>%ju</CacheReadBytes>\n", indent, 101104c7da70SRuslan Ermilov sc->sc_cachereadbytes); 101204c7da70SRuslan Ermilov sbuf_printf(sb, "%s<CacheHits>%ju</CacheHits>\n", indent, 101304c7da70SRuslan Ermilov sc->sc_cachehits); 101404c7da70SRuslan Ermilov sbuf_printf(sb, "%s<CacheMisses>%ju</CacheMisses>\n", indent, 101504c7da70SRuslan Ermilov sc->sc_cachemisses); 101604c7da70SRuslan Ermilov sbuf_printf(sb, "%s<CacheFull>%ju</CacheFull>\n", indent, 101704c7da70SRuslan Ermilov sc->sc_cachefull); 101804c7da70SRuslan Ermilov sbuf_printf(sb, "%s<Writes>%ju</Writes>\n", indent, sc->sc_writes); 101904c7da70SRuslan Ermilov sbuf_printf(sb, "%s<WroteBytes>%ju</WroteBytes>\n", indent, 102004c7da70SRuslan Ermilov sc->sc_wrotebytes); 102104c7da70SRuslan Ermilov } 102204c7da70SRuslan Ermilov 102304c7da70SRuslan Ermilov DECLARE_GEOM_CLASS(g_cache_class, g_cache); 102474d6c131SKyle Evans MODULE_VERSION(geom_cache, 0); 1025