1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2004 Poul-Henning Kamp 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/bio.h> 35 #include <sys/kernel.h> 36 #include <sys/lock.h> 37 #include <sys/malloc.h> 38 #include <sys/mutex.h> 39 #include <sys/sbuf.h> 40 #include <sys/vnode.h> 41 #include <sys/mount.h> 42 43 #include <geom/geom.h> 44 #include <geom/geom_vfs.h> 45 46 /* 47 * subroutines for use by filesystems. 48 * 49 * XXX: should maybe live somewhere else ? 50 */ 51 #include <sys/buf.h> 52 53 struct g_vfs_softc { 54 struct mtx sc_mtx; 55 struct bufobj *sc_bo; 56 struct g_event *sc_event; 57 int sc_active; 58 int sc_orphaned; 59 int sc_enxio_active; 60 }; 61 62 static struct buf_ops __g_vfs_bufops = { 63 .bop_name = "GEOM_VFS", 64 .bop_write = bufwrite, 65 .bop_strategy = g_vfs_strategy, 66 .bop_sync = bufsync, 67 .bop_bdflush = bufbdflush 68 }; 69 70 struct buf_ops *g_vfs_bufops = &__g_vfs_bufops; 71 72 static g_orphan_t g_vfs_orphan; 73 74 static struct g_class g_vfs_class = { 75 .name = "VFS", 76 .version = G_VERSION, 77 .orphan = g_vfs_orphan, 78 }; 79 80 DECLARE_GEOM_CLASS(g_vfs_class, g_vfs); 81 82 static void 83 g_vfs_destroy(void *arg, int flags __unused) 84 { 85 struct g_consumer *cp; 86 87 g_topology_assert(); 88 cp = arg; 89 if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0) 90 g_access(cp, -cp->acr, -cp->acw, -cp->ace); 91 g_detach(cp); 92 if (cp->geom->softc == NULL) 93 g_wither_geom(cp->geom, ENXIO); 94 } 95 96 static void 97 g_vfs_done(struct bio *bip) 98 { 99 struct g_consumer *cp; 100 struct g_event *event; 101 struct g_vfs_softc *sc; 102 struct buf *bp; 103 int destroy; 104 struct mount *mp; 105 struct vnode *vp; 106 struct cdev *cdevp; 107 108 /* 109 * Collect statistics on synchronous and asynchronous read 110 * and write counts for disks that have associated filesystems. 111 */ 112 bp = bip->bio_caller2; 113 vp = bp->b_vp; 114 if (vp != NULL) { 115 /* 116 * If not a disk vnode, use its associated mount point 117 * otherwise use the mountpoint associated with the disk. 118 */ 119 VI_LOCK(vp); 120 if (vp->v_type != VCHR || 121 (cdevp = vp->v_rdev) == NULL || 122 cdevp->si_devsw == NULL || 123 (cdevp->si_devsw->d_flags & D_DISK) == 0) 124 mp = vp->v_mount; 125 else 126 mp = cdevp->si_mountpt; 127 if (mp != NULL) { 128 if (bp->b_iocmd == BIO_READ) { 129 if (LK_HOLDER(bp->b_lock.lk_lock) == LK_KERNPROC) 130 mp->mnt_stat.f_asyncreads++; 131 else 132 mp->mnt_stat.f_syncreads++; 133 } else if (bp->b_iocmd == BIO_WRITE) { 134 if (LK_HOLDER(bp->b_lock.lk_lock) == LK_KERNPROC) 135 mp->mnt_stat.f_asyncwrites++; 136 else 137 mp->mnt_stat.f_syncwrites++; 138 } 139 } 140 VI_UNLOCK(vp); 141 } 142 143 cp = bip->bio_from; 144 sc = cp->geom->softc; 145 if (bip->bio_error != 0 && bip->bio_error != EOPNOTSUPP) { 146 if ((bp->b_xflags & BX_CVTENXIO) != 0) 147 sc->sc_enxio_active = 1; 148 if (sc->sc_enxio_active) 149 bip->bio_error = ENXIO; 150 g_print_bio("g_vfs_done():", bip, "error = %d", 151 bip->bio_error); 152 } 153 bp->b_error = bip->bio_error; 154 bp->b_ioflags = bip->bio_flags; 155 if (bip->bio_error) 156 bp->b_ioflags |= BIO_ERROR; 157 bp->b_resid = bp->b_bcount - bip->bio_completed; 158 g_destroy_bio(bip); 159 160 mtx_lock(&sc->sc_mtx); 161 destroy = ((--sc->sc_active) == 0 && sc->sc_orphaned); 162 if (destroy) { 163 event = sc->sc_event; 164 sc->sc_event = NULL; 165 } else 166 event = NULL; 167 mtx_unlock(&sc->sc_mtx); 168 if (destroy) 169 g_post_event_ep(g_vfs_destroy, cp, event, NULL); 170 171 bufdone(bp); 172 } 173 174 void 175 g_vfs_strategy(struct bufobj *bo, struct buf *bp) 176 { 177 struct g_vfs_softc *sc; 178 struct g_consumer *cp; 179 struct bio *bip; 180 181 cp = bo->bo_private; 182 sc = cp->geom->softc; 183 184 /* 185 * If the provider has orphaned us, just return ENXIO. 186 */ 187 mtx_lock(&sc->sc_mtx); 188 if (sc->sc_orphaned || sc->sc_enxio_active) { 189 mtx_unlock(&sc->sc_mtx); 190 bp->b_error = ENXIO; 191 bp->b_ioflags |= BIO_ERROR; 192 bufdone(bp); 193 return; 194 } 195 sc->sc_active++; 196 mtx_unlock(&sc->sc_mtx); 197 198 bip = g_alloc_bio(); 199 bip->bio_cmd = bp->b_iocmd; 200 bip->bio_offset = bp->b_iooffset; 201 bip->bio_length = bp->b_bcount; 202 bdata2bio(bp, bip); 203 if ((bp->b_flags & B_BARRIER) != 0) { 204 bip->bio_flags |= BIO_ORDERED; 205 bp->b_flags &= ~B_BARRIER; 206 } 207 if (bp->b_iocmd == BIO_SPEEDUP) 208 bip->bio_flags |= bp->b_ioflags; 209 bip->bio_done = g_vfs_done; 210 bip->bio_caller2 = bp; 211 #if defined(BUF_TRACKING) || defined(FULL_BUF_TRACKING) 212 buf_track(bp, __func__); 213 bip->bio_track_bp = bp; 214 #endif 215 g_io_request(bip, cp); 216 } 217 218 static void 219 g_vfs_orphan(struct g_consumer *cp) 220 { 221 struct g_geom *gp; 222 struct g_event *event; 223 struct g_vfs_softc *sc; 224 int destroy; 225 226 g_topology_assert(); 227 228 gp = cp->geom; 229 g_trace(G_T_TOPOLOGY, "g_vfs_orphan(%p(%s))", cp, gp->name); 230 sc = gp->softc; 231 if (sc == NULL) 232 return; 233 event = g_alloc_event(M_WAITOK); 234 mtx_lock(&sc->sc_mtx); 235 KASSERT(sc->sc_event == NULL, ("g_vfs %p already has an event", sc)); 236 sc->sc_orphaned = 1; 237 destroy = (sc->sc_active == 0); 238 if (!destroy) { 239 sc->sc_event = event; 240 event = NULL; 241 } 242 mtx_unlock(&sc->sc_mtx); 243 if (destroy) { 244 g_free(event); 245 g_vfs_destroy(cp, 0); 246 } 247 248 /* 249 * Do not destroy the geom. Filesystem will do that during unmount. 250 */ 251 } 252 253 int 254 g_vfs_open(struct vnode *vp, struct g_consumer **cpp, const char *fsname, int wr) 255 { 256 struct g_geom *gp; 257 struct g_provider *pp; 258 struct g_consumer *cp; 259 struct g_vfs_softc *sc; 260 struct bufobj *bo; 261 int error; 262 263 g_topology_assert(); 264 265 *cpp = NULL; 266 bo = &vp->v_bufobj; 267 if (bo->bo_private != vp) 268 return (EBUSY); 269 270 pp = g_dev_getprovider(vp->v_rdev); 271 if (pp == NULL) 272 return (ENOENT); 273 gp = g_new_geomf(&g_vfs_class, "%s.%s", fsname, pp->name); 274 sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO); 275 mtx_init(&sc->sc_mtx, "g_vfs", NULL, MTX_DEF); 276 sc->sc_bo = bo; 277 gp->softc = sc; 278 cp = g_new_consumer(gp); 279 error = g_attach(cp, pp); 280 if (error) { 281 g_wither_geom(gp, ENXIO); 282 return (error); 283 } 284 error = g_access(cp, 1, wr, wr); 285 if (error) { 286 g_wither_geom(gp, ENXIO); 287 return (error); 288 } 289 vnode_create_vobject(vp, pp->mediasize, curthread); 290 *cpp = cp; 291 cp->private = vp; 292 cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE; 293 bo->bo_ops = g_vfs_bufops; 294 bo->bo_private = cp; 295 bo->bo_bsize = pp->sectorsize; 296 297 return (error); 298 } 299 300 void 301 g_vfs_close(struct g_consumer *cp) 302 { 303 struct g_geom *gp; 304 struct g_vfs_softc *sc; 305 306 g_topology_assert(); 307 308 gp = cp->geom; 309 sc = gp->softc; 310 bufobj_invalbuf(sc->sc_bo, V_SAVE, 0, 0); 311 sc->sc_bo->bo_private = cp->private; 312 gp->softc = NULL; 313 mtx_destroy(&sc->sc_mtx); 314 if (!sc->sc_orphaned || cp->provider == NULL) 315 g_wither_geom_close(gp, ENXIO); 316 KASSERT(sc->sc_event == NULL, ("g_vfs %p event is non-NULL", sc)); 317 g_free(sc); 318 } 319