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