/* * Read a cluster from the snapshotted block device to the cache. */ static int fss_read_cluster(struct fss_softc *sc, u_int32_t cl) { int error, todo, offset, len; daddr_t dblk; struct buf *bp, *mbp; struct fss_cache *scp, *scl; /* * Get a free cache slot. */ scl = sc->sc_cache+sc->sc_cache_size; mutex_enter(&sc->sc_slock); restart: if (isset(sc->sc_copied, cl) || !FSS_ISVALID(sc)) { mutex_exit(&sc->sc_slock); return 0; } for (scp = sc->sc_cache; scp < scl; scp++) if (scp->fc_cluster == cl) { if (scp->fc_type == FSS_CACHE_VALID) { mutex_exit(&sc->sc_slock); return 0; } else if (scp->fc_type == FSS_CACHE_BUSY) { cv_wait(&scp->fc_state_cv, &sc->sc_slock); goto restart; } } for (scp = sc->sc_cache; scp < scl; scp++) if (scp->fc_type == FSS_CACHE_FREE) { scp->fc_type = FSS_CACHE_BUSY; scp->fc_cluster = cl; break; } if (scp >= scl) { cv_wait(&sc->sc_cache_cv, &sc->sc_slock); goto restart; } mutex_exit(&sc->sc_slock); /* * Start the read. */ dblk = btodb(FSS_CLTOB(sc, cl)); if (cl == sc->sc_clcount-1) { todo = sc->sc_clresid; memset((char *)scp->fc_data + todo, 0, FSS_CLSIZE(sc) - todo); } else todo = FSS_CLSIZE(sc); offset = 0; mbp = getiobuf(NULL, true); mbp->b_bufsize = todo; mbp->b_data = scp->fc_data; mbp->b_resid = mbp->b_bcount = todo; mbp->b_flags = B_READ; mbp->b_cflags = BC_BUSY; mbp->b_dev = sc->sc_bdev; while (todo > 0) { len = todo; if (len > MAXPHYS) len = MAXPHYS; if (btodb(FSS_CLTOB(sc, cl)) == dblk && len == todo) bp = mbp; else { bp = getiobuf(NULL, true); nestiobuf_setup(mbp, bp, offset, len); } bp->b_lblkno = 0; bp->b_blkno = dblk; bdev_strategy(bp); dblk += btodb(len); offset += len; todo -= len; } error = biowait(mbp); putiobuf(mbp); mutex_enter(&sc->sc_slock); scp->fc_type = (error ? FSS_CACHE_FREE : FSS_CACHE_VALID); cv_broadcast(&scp->fc_state_cv); if (error == 0) { setbit(sc->sc_copied, scp->fc_cluster); cv_signal(&sc->sc_work_cv); } mutex_exit(&sc->sc_slock); return error; }
/* * Handes the read/write request given in 'bp' using the vnode's VOP_BMAP * and VOP_STRATEGY operations. * * 'obp' is a pointer to the original request fed to the vnd device. */ static void handle_with_strategy(struct vnd_softc *vnd, const struct buf *obp, struct buf *bp) { int bsize, error, flags, skipped; size_t resid, sz; off_t bn, offset; struct vnode *vp; flags = obp->b_flags; if (!(flags & B_READ)) { vp = bp->b_vp; mutex_enter(vp->v_interlock); vp->v_numoutput++; mutex_exit(vp->v_interlock); } /* convert to a byte offset within the file. */ bn = obp->b_rawblkno * vnd->sc_dkdev.dk_label->d_secsize; bsize = vnd->sc_vp->v_mount->mnt_stat.f_iosize; skipped = 0; /* * Break the request into bsize pieces and feed them * sequentially using VOP_BMAP/VOP_STRATEGY. * We do it this way to keep from flooding NFS servers if we * are connected to an NFS file. This places the burden on * the client rather than the server. */ error = 0; bp->b_resid = bp->b_bcount; for (offset = 0, resid = bp->b_resid; resid; resid -= sz, offset += sz) { struct buf *nbp; daddr_t nbn; int off, nra; nra = 0; vn_lock(vnd->sc_vp, LK_EXCLUSIVE | LK_RETRY); error = VOP_BMAP(vnd->sc_vp, bn / bsize, &vp, &nbn, &nra); VOP_UNLOCK(vnd->sc_vp); if (error == 0 && (long)nbn == -1) error = EIO; /* * If there was an error or a hole in the file...punt. * Note that we may have to wait for any operations * that we have already fired off before releasing * the buffer. * * XXX we could deal with holes here but it would be * a hassle (in the write case). */ if (error) { skipped += resid; break; } #ifdef DEBUG if (!dovndcluster) nra = 0; #endif off = bn % bsize; sz = MIN(((off_t)1 + nra) * bsize - off, resid); #ifdef DEBUG if (vnddebug & VDB_IO) printf("vndstrategy: vp %p/%p bn 0x%qx/0x%" PRIx64 " sz 0x%zx\n", vnd->sc_vp, vp, (long long)bn, nbn, sz); #endif nbp = getiobuf(vp, true); nestiobuf_setup(bp, nbp, offset, sz); nbp->b_blkno = nbn + btodb(off); #if 0 /* XXX #ifdef DEBUG */ if (vnddebug & VDB_IO) printf("vndstart(%ld): bp %p vp %p blkno " "0x%" PRIx64 " flags %x addr %p cnt 0x%x\n", (long) (vnd-vnd_softc), &nbp->vb_buf, nbp->vb_buf.b_vp, nbp->vb_buf.b_blkno, nbp->vb_buf.b_flags, nbp->vb_buf.b_data, nbp->vb_buf.b_bcount); #endif VOP_STRATEGY(vp, nbp); bn += sz; } nestiobuf_done(bp, skipped, error); }