diff --git a/sys/ufs/ufs/ufs_bmap.c b/sys/ufs/ufs/ufs_bmap.c --- a/sys/ufs/ufs/ufs_bmap.c +++ b/sys/ufs/ufs/ufs_bmap.c @@ -48,6 +48,7 @@ #include #include +#include #include #include @@ -59,6 +60,11 @@ static ufs_lbn_t lbn_count(struct ufsmount *, int); static int readindir(struct vnode *, ufs_lbn_t, ufs2_daddr_t, struct buf **); +static int ufs_bmap_use_unmapped = 1; + +SYSCTL_INT(_vfs_ufs, OID_AUTO, bmap_use_unmapped, CTLFLAG_RWTUN, + &ufs_bmap_use_unmapped, 0, "UFS bmap uses unmapped bufs"); + /* * Bmap converts the logical block number of a file to its physical block * number on the disk. The conversion is done by using the logical block @@ -102,12 +108,16 @@ struct buf *bp; struct mount *mp; struct ufsmount *ump; - int error; + struct inode *ip; + int error, gbflags; mp = vp->v_mount; ump = VFSTOUFS(mp); + ip = VTOI(vp); - bp = getblk(vp, lbn, mp->mnt_stat.f_iosize, 0, 0, 0); + gbflags = !I_IS_UFS1(ip) && PMAP_HAS_DMAP && ufs_bmap_use_unmapped ? + GB_UNMAPPED : 0; + bp = getblk(vp, lbn, mp->mnt_stat.f_iosize, 0, 0, gbflags); if ((bp->b_flags & B_CACHE) == 0) { KASSERT(daddr != 0, ("readindir: indirect block not in cache")); @@ -168,6 +178,11 @@ ufs_lbn_t metalbn; int error, num, maxrun = 0; int *nump; + ufs1_daddr_t *daddr1p; + ufs2_daddr_t pgbn, daddrppg, prevdaddr, *daddr2p; + int32_t daddrsz, boff, pgidx, pgoff; + void *pgaddr; + bool isseq; ap = NULL; ip = VTOI(vp); @@ -261,17 +276,68 @@ if (error != 0) return (error); - if (I_IS_UFS1(ip)) - daddr = ((ufs1_daddr_t *)bp->b_data)[ap->in_off]; - else - daddr = ((ufs2_daddr_t *)bp->b_data)[ap->in_off]; + daddrsz = I_IS_UFS1(ip) ? sizeof(ufs1_daddr_t) : sizeof(ufs2_daddr_t); + if (!buf_mapped(bp)) { + boff = ap->in_off * daddrsz; + pgidx = boff / PAGE_SIZE; + pgoff = (boff & PAGE_MASK) / daddrsz; + pgaddr = (void *)PHYS_TO_DMAP(VM_PAGE_TO_PHYS(bp->b_pages[pgidx])); + if (I_IS_UFS1(ip)) + daddr = ((ufs1_daddr_t *)pgaddr)[pgoff]; + else + daddr = ((ufs2_daddr_t *)pgaddr)[pgoff]; + } else { + if (I_IS_UFS1(ip)) + daddr = ((ufs1_daddr_t *)bp->b_data)[ap->in_off]; + else + daddr = ((ufs2_daddr_t *)bp->b_data)[ap->in_off]; + } + if ((error = UFS_CHECK_BLKNO(mp, ip->i_number, daddr, mp->mnt_stat.f_iosize)) != 0) { bqrelse(bp); return (error); } + if (num > 1 || daddr == 0 || runp == NULL) + continue; + + daddrppg = PAGE_SIZE / daddrsz; if (I_IS_UFS1(ip)) { - if (num == 1 && daddr && runp) { + if (!buf_mapped(bp)) { + prevdaddr = daddr; + isseq = true; + for (bn = ap->in_off + 1; + bn < MNINDIR(ump) && *runp < maxrun && isseq; ) { + boff = bn * daddrsz; + pgidx = boff / PAGE_SIZE; + pgoff = (boff & PAGE_MASK) / daddrsz; + KASSERT(pgidx >= 0 && pgidx < bp->b_npages, + ("pgidx %d vs b_npages %d", pgidx, bp->b_npages)); + pgaddr = (void *)PHYS_TO_DMAP(VM_PAGE_TO_PHYS(bp->b_pages[pgidx])); + daddr1p = (ufs1_daddr_t *)pgaddr; + for (pgbn = pgoff; + pgbn < daddrppg && *runp < maxrun && + (isseq = is_sequential(ump, prevdaddr, daddr1p[pgbn])); + prevdaddr = daddr1p[pgbn], ++pgbn, ++bn, ++*runp); + } + prevdaddr = daddr; + bn = ap->in_off; + if (runb && bn) { + isseq = true; + for (--bn; bn >= 0 && *runb < maxrun && isseq; ) { + boff = bn * daddrsz; + pgidx = boff / PAGE_SIZE; + pgoff = (boff & PAGE_MASK) / daddrsz; + KASSERT(pgidx >= 0 && pgidx < bp->b_npages, + ("pgidx %d vs b_npages %d", pgidx, bp->b_npages)); + pgaddr = (void *)PHYS_TO_DMAP(VM_PAGE_TO_PHYS(bp->b_pages[pgidx])); + daddr1p = (ufs1_daddr_t *)pgaddr; + for (pgbn = pgoff; pgbn >= 0 && *runb < maxrun && + (isseq = is_sequential(ump, daddr1p[pgbn], prevdaddr)); + prevdaddr = daddr1p[pgbn], --pgbn, --bn, ++*runb); + } + } + } else { for (bn = ap->in_off + 1; bn < MNINDIR(ump) && *runp < maxrun && is_sequential(ump, @@ -289,7 +355,43 @@ } continue; } - if (num == 1 && daddr && runp) { + + if (!buf_mapped(bp)) { + prevdaddr = daddr; + isseq = true; + for (bn = ap->in_off + 1; + bn < MNINDIR(ump) && *runp < maxrun && isseq; ) { + boff = bn * daddrsz; + pgidx = boff / PAGE_SIZE; + pgoff = (boff & PAGE_MASK) / daddrsz; + KASSERT(pgidx >= 0 && pgidx < bp->b_npages, + ("pgidx %d vs b_npages %d", pgidx, bp->b_npages)); + pgaddr = (void *)PHYS_TO_DMAP(VM_PAGE_TO_PHYS(bp->b_pages[pgidx])); + daddr2p = (ufs2_daddr_t *)pgaddr; + for (pgbn = pgoff; + pgbn < daddrppg && *runp < maxrun && + (isseq = is_sequential(ump, prevdaddr, daddr2p[pgbn])); + prevdaddr = daddr2p[pgbn], ++pgbn, ++bn, ++*runp); + } + prevdaddr = daddr; + bn = ap->in_off; + if (runb && bn) { + isseq = true; + for (--bn; bn >= 0 && *runb < maxrun && isseq; ) { + boff = bn * daddrsz; + pgidx = boff / PAGE_SIZE; + pgoff = (boff & PAGE_MASK) / daddrsz; + KASSERT(pgidx >= 0 && pgidx < bp->b_npages, + ("pgidx %d vs b_npages %d", pgidx, bp->b_npages)); + pgaddr = (void *)PHYS_TO_DMAP(VM_PAGE_TO_PHYS( + bp->b_pages[pgidx])); + daddr2p = (ufs2_daddr_t *)pgaddr; + for (pgbn = pgoff; pgbn >= 0 && *runb < maxrun && + (isseq = is_sequential(ump, daddr2p[pgbn], prevdaddr)); + prevdaddr = daddr2p[pgbn], --pgbn, --bn, ++*runb); + } + } + } else { for (bn = ap->in_off + 1; bn < MNINDIR(ump) && *runp < maxrun && is_sequential(ump,