delete VERBOSE #ifdef's
[unix-history] / usr / src / sys / ufs / lfs / lfs_balloc.c
index a92b727..643df17 100644 (file)
 /*
 /*
- * Copyright (c) 1982, 1986 Regents of the University of California.
- * All rights reserved.  The Berkeley software License Agreement
- * specifies the terms and conditions for redistribution.
+ * Copyright (c) 1989, 1991 Regents of the University of California.
+ * All rights reserved.
  *
  *
- *     @(#)lfs_balloc.c        7.1 (Berkeley) %G%
+ * %sccs.include.redist.c%
+ *
+ *     @(#)lfs_balloc.c        7.35 (Berkeley) %G%
  */
 
  */
 
-#include "param.h"
-#include "systm.h"
-#include "inode.h"
-#include "dir.h"
-#include "user.h"
-#include "buf.h"
-#include "proc.h"
-#include "fs.h"
+#include <sys/param.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/resourcevar.h>
+#include <sys/trace.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+int lfs_getlbns __P((struct vnode *, daddr_t, INDIR *, int *));
 
 /*
 
 /*
- * Bmap defines the structure of file system storage
- * by returning the physical block number on a device given the
- * inode and the logical block number in a file.
- * When convenient, it also leaves the physical
- * block number of the next block of the file in rablock
- * for use in read-ahead.
+ * Bmap converts a the logical block number of a file to its physical block
+ * number on the disk. The conversion is done by using the logical block
+ * number to index into the array of block pointers described by the dinode.
  */
  */
-/*VARARGS3*/
-daddr_t
-bmap(ip, bn, rwflg, size)
-       register struct inode *ip;
-       daddr_t bn;
-       int rwflg;
-       int size;       /* supplied only when rwflg == B_WRITE */
+int
+lfs_bmap(ap)
+       struct vop_bmap_args /* {
+               struct vnode *a_vp;
+               daddr_t  a_bn;
+               struct vnode **a_vpp;
+               daddr_t *a_bnp;
+       } */ *ap;
 {
 {
-       register int i;
-       int osize, nsize;
-       struct buf *bp, *nbp;
-       struct fs *fs;
-       int j, sh;
-       daddr_t nb, lbn, *bap, pref, blkpref();
-
-       if (bn < 0) {
-               u.u_error = EFBIG;
-               return ((daddr_t)0);
-       }
-       fs = ip->i_fs;
-       rablock = 0;
-       rasize = 0;             /* conservative */
-
        /*
        /*
-        * If the next write will extend the file into a new block,
-        * and the file is currently composed of a fragment
-        * this fragment has to be extended to be a full block.
+        * Check for underlying vnode requests and ensure that logical
+        * to physical mapping is requested.
         */
         */
-       nb = lblkno(fs, ip->i_size);
-       if (rwflg == B_WRITE && nb < NDADDR && nb < bn) {
-               osize = blksize(fs, ip, nb);
-               if (osize < fs->fs_bsize && osize > 0) {
-                       bp = realloccg(ip, ip->i_db[nb],
-                               blkpref(ip, nb, (int)nb, &ip->i_db[0]),
-                               osize, (int)fs->fs_bsize);
-                       if (bp == NULL)
-                               return ((daddr_t)-1);
-                       ip->i_size = (nb + 1) * fs->fs_bsize;
-                       ip->i_db[nb] = dbtofsb(fs, bp->b_blkno);
-                       ip->i_flag |= IUPD|ICHG;
-                       bdwrite(bp);
-               }
+       if (ap->a_vpp != NULL)
+               *ap->a_vpp = VTOI(ap->a_vp)->i_devvp;
+       if (ap->a_bnp == NULL)
+               return (0);
+
+       return (lfs_bmaparray(ap->a_vp, ap->a_bn, ap->a_bnp, NULL, NULL));
+}
+
+/*
+ * LFS has a different version of bmap from FFS because of a naming conflict.
+ * In FFS, meta blocks are given real disk addresses at allocation time, and
+ * are linked into the device vnode, using a logical block number which is
+ * the same as the physical block number.  This can't be done by LFS because
+ * blocks aren't given disk addresses until they're written, so there's no
+ * way to distinguish the meta-data blocks for one file from any other file.
+ * This means that meta-data blocks have to be on the vnode for the file so
+ * they can be found, and have to have "names" different from the standard
+ * data blocks.  To do this, we divide the name space into positive and
+ * negative block numbers, and give the meta-data blocks negative logical
+ * numbers.  Indirect blocks are addressed by the negative address of the
+ * first data block to which they point.  Double indirect blocks are addressed
+ * by one less than the address of the first indirect block to which they
+ * point.  Triple indirect blocks are addressed by one less than the address
+ * of the first double indirect block to which they point.
+ */
+int
+lfs_bmaparray(vp, bn, bnp, ap, nump)
+       struct vnode *vp;
+       register daddr_t bn;
+       daddr_t *bnp;
+       INDIR *ap;
+       int *nump;
+{
+       register struct inode *ip;
+       struct buf *bp;
+       struct lfs *fs;
+       struct vnode *devvp;
+       INDIR a[NIADDR], *xap;
+       daddr_t *bap, daddr;
+       long metalbn;
+       int error, num, off;
+       struct vop_strategy_args vop_strategy_a;
+
+       ip = VTOI(vp);
+#ifdef DIAGNOSTIC
+       if (ap != NULL && nump == NULL || ap == NULL && nump != NULL)
+               panic("lfs_bmaparray: invalid arguments");
+#endif
+
+       xap = ap == NULL ? a : ap;
+       if (!nump)
+               nump = &num;
+       if (error = lfs_getlbns(vp, bn, xap, nump))
+               return (error);
+
+       num = *nump;
+       if (num == 0) {
+               *bnp = ip->i_db[bn];
+               if (*bnp == 0)
+                       *bnp = UNASSIGNED;
+               return (0);
        }
        }
-       /*
-        * The first NDADDR blocks are direct blocks
-        */
-       if (bn < NDADDR) {
-               nb = ip->i_db[bn];
-               if (rwflg == B_READ) {
-                       if (nb == 0)
-                               return ((daddr_t)-1);
-                       goto gotit;
-               }
-               if (nb == 0 || ip->i_size < (bn + 1) * fs->fs_bsize) {
-                       if (nb != 0) {
-                               /* consider need to reallocate a frag */
-                               osize = fragroundup(fs, blkoff(fs, ip->i_size));
-                               nsize = fragroundup(fs, size);
-                               if (nsize <= osize)
-                                       goto gotit;
-                               bp = realloccg(ip, nb,
-                                       blkpref(ip, bn, (int)bn, &ip->i_db[0]),
-                                       osize, nsize);
-                       } else {
-                               if (ip->i_size < (bn + 1) * fs->fs_bsize)
-                                       nsize = fragroundup(fs, size);
-                               else
-                                       nsize = fs->fs_bsize;
-                               bp = alloc(ip,
-                                       blkpref(ip, bn, (int)bn, &ip->i_db[0]),
-                                       nsize);
+
+
+       /* Get disk address out of indirect block array */
+       daddr = ip->i_ib[xap->in_off];
+
+       /* Fetch through the indirect blocks. */
+       fs = ip->i_lfs;
+       devvp = VFSTOUFS(vp->v_mount)->um_devvp;
+
+       for (bp = NULL, ++xap; daddr && --num; ++xap) {
+               /* If looking for a meta-block, break out when we find it. */
+               metalbn = xap->in_lbn;
+               if (metalbn == bn)
+                       break;
+
+               /*
+                * Read in the appropriate indirect block.  LFS can't do a
+                * bread because bread knows that FFS will hand it the device
+                * vnode, not the file vnode, so the b_dev and b_blkno would
+                * be wrong.
+                *
+                * XXX
+                * This REALLY needs to be fixed, at the very least it needs
+                * to be rethought when the buffer cache goes away.  When it's
+                * fixed, change lfs_bmaparray and lfs_getlbns to take an ip,
+                * not a vp.
+                */
+               if (bp)
+                       brelse(bp);
+               bp = getblk(vp, metalbn, fs->lfs_bsize);
+               if (bp->b_flags & (B_DONE | B_DELWRI)) {
+                       trace(TR_BREADHIT, pack(vp, size), metalbn);
+               } else {
+                       trace(TR_BREADMISS, pack(vp, size), metalbn);
+                       bp->b_blkno = daddr;
+                       bp->b_flags |= B_READ;
+                       bp->b_dev = devvp->v_rdev;
+                       /*
+                        * Call a strategy VOP by hand.
+                        */
+                       vop_strategy_a.a_desc = VDESC(vop_strategy);
+                       vop_strategy_a.a_bp=bp;
+                       VOCALL(devvp->v_op, VOFFSET(vop_strategy), \
+                              &vop_strategy_a);
+                       curproc->p_stats->p_ru.ru_inblock++;    /* XXX */
+                       if (error = biowait(bp)) {
+                               brelse(bp);
+                               return (error);
                        }
                        }
-                       if (bp == NULL)
-                               return ((daddr_t)-1);
-                       nb = dbtofsb(fs, bp->b_blkno);
-                       if ((ip->i_mode&IFMT) == IFDIR)
-                               /*
-                                * Write directory blocks synchronously
-                                * so they never appear with garbage in
-                                * them on the disk.
-                                */
-                               bwrite(bp);
-                       else
-                               bdwrite(bp);
-                       ip->i_db[bn] = nb;
-                       ip->i_flag |= IUPD|ICHG;
-               }
-gotit:
-               if (bn < NDADDR - 1) {
-                       rablock = fsbtodb(fs, ip->i_db[bn + 1]);
-                       rasize = blksize(fs, ip, bn + 1);
                }
                }
-               return (nb);
+               daddr = bp->b_un.b_daddr[xap->in_off];
        }
        }
+       if (bp)
+               brelse(bp);
 
 
-       /*
-        * Determine how many levels of indirection.
+       *bnp = daddr == 0 ? UNASSIGNED : daddr;
+       return (0);
+}
+
+/*
+ * Create an array of logical block number/offset pairs which represent the
+ * path of indirect blocks required to access a data block.  The first "pair"
+ * contains the logical block number of the appropriate single, double or
+ * triple indirect block and the offset into the inode indirect block array.
+ * Note, the logical block number of the inode single/double/triple indirect
+ * block appears twice in the array, once with the offset into the i_ib and
+ * once with the offset into the page itself.
+ */
+int
+lfs_getlbns(vp, bn, ap, nump)
+       struct vnode *vp;
+       register daddr_t bn;
+       INDIR *ap;
+       int *nump;
+{
+       struct lfs *fs;
+       long metalbn, realbn;
+       int j, numlevels, off, sh;
+
+       if (nump)
+               *nump = 0;
+       numlevels = 0;
+       realbn = bn;
+       if ((long)bn < 0)
+               bn = -(long)bn;
+
+       /* The first NDADDR blocks are direct blocks. */
+       if (bn < NDADDR)
+               return (0);
+
+       /* 
+        * Determine the number of levels of indirection.  After this loop
+        * is done, sh indicates the number of data blocks possible at the
+        * given level of indirection, and NIADDR - j is the number of levels
+        * of indirection needed to locate the requested block.
         */
         */
-       pref = 0;
-       sh = 1;
-       lbn = bn;
        bn -= NDADDR;
        bn -= NDADDR;
-       for (j = NIADDR; j>0; j--) {
+       fs = VTOI(vp)->i_lfs;
+       sh = 1;
+       for (j = NIADDR; j > 0; j--) {
                sh *= NINDIR(fs);
                if (bn < sh)
                        break;
                bn -= sh;
        }
                sh *= NINDIR(fs);
                if (bn < sh)
                        break;
                bn -= sh;
        }
-       if (j == 0) {
-               u.u_error = EFBIG;
-               return ((daddr_t)0);
-       }
+       if (j == 0)
+               return (EFBIG);
 
 
-       /*
-        * fetch the first indirect block
-        */
-       nb = ip->i_ib[NIADDR - j];
-       if (nb == 0) {
-               if (rwflg == B_READ)
-                       return ((daddr_t)-1);
-               pref = blkpref(ip, lbn, 0, (daddr_t *)0);
-               bp = alloc(ip, pref, (int)fs->fs_bsize);
-               if (bp == NULL)
-                       return ((daddr_t)-1);
-               nb = dbtofsb(fs, bp->b_blkno);
-               /*
-                * Write synchronously so that indirect blocks
-                * never point at garbage.
-                */
-               bwrite(bp);
-               ip->i_ib[NIADDR - j] = nb;
-               ip->i_flag |= IUPD|ICHG;
-       }
+       /* Calculate the address of the first meta-block. */
+       if (realbn >= 0)
+               metalbn = -(realbn - bn + NIADDR - j);
+       else
+               metalbn = -(-realbn - bn + NIADDR - j);
 
 
-       /*
-        * fetch through the indirect blocks
+       /* 
+        * At each iteration, off is the offset into the bap array which is
+        * an array of disk addresses at the current level of indirection.
+        * The logical block number and the offset in that block are stored
+        * into the argument array.
         */
         */
+       ++numlevels;
+       ap->in_lbn = metalbn;
+       ap->in_off = off = NIADDR - j;
+       ap++;
        for (; j <= NIADDR; j++) {
        for (; j <= NIADDR; j++) {
-               bp = bread(ip->i_dev, fsbtodb(fs, nb), (int)fs->fs_bsize);
-               if (bp->b_flags & B_ERROR) {
-                       brelse(bp);
-                       return ((daddr_t)0);
-               }
-               bap = bp->b_un.b_daddr;
+               /* If searching for a meta-data block, quit when found. */
+               if (metalbn == realbn)
+                       break;
+
                sh /= NINDIR(fs);
                sh /= NINDIR(fs);
-               i = (bn / sh) % NINDIR(fs);
-               nb = bap[i];
-               if (nb == 0) {
-                       if (rwflg==B_READ) {
-                               brelse(bp);
-                               return ((daddr_t)-1);
-                       }
-                       if (pref == 0)
-                               if (j < NIADDR)
-                                       pref = blkpref(ip, lbn, 0,
-                                               (daddr_t *)0);
-                               else
-                                       pref = blkpref(ip, lbn, i, &bap[0]);
-                       nbp = alloc(ip, pref, (int)fs->fs_bsize);
-                       if (nbp == NULL) {
-                               brelse(bp);
-                               return ((daddr_t)-1);
-                       }
-                       nb = dbtofsb(fs, nbp->b_blkno);
-                       if (j < NIADDR || (ip->i_mode&IFMT) == IFDIR)
-                               /*
-                                * Write synchronously so indirect blocks
-                                * never point at garbage and blocks
-                                * in directories never contain garbage.
-                                */
-                               bwrite(nbp);
-                       else
-                               bdwrite(nbp);
-                       bap[i] = nb;
-                       bdwrite(bp);
-               } else
-                       brelse(bp);
+               off = (bn / sh) % NINDIR(fs);
+
+               ++numlevels;
+               ap->in_lbn = metalbn;
+               ap->in_off = off;
+               ++ap;
+
+               metalbn -= -1 + off * sh;
        }
        }
+       if (nump)
+               *nump = numlevels;
+       return (0);
+}
 
 
-       /*
-        * calculate read-ahead.
+int
+lfs_balloc(vp, iosize, lbn, bpp)
+       struct vnode *vp;
+       u_long iosize;
+       daddr_t lbn;
+       struct buf **bpp;
+{
+       struct buf *bp;
+       struct inode *ip;
+       struct lfs *fs;
+       daddr_t daddr;
+       int error, newblock;
+
+       ip = VTOI(vp);
+       fs = ip->i_lfs;
+
+       /* 
+        * Three cases: it's a block beyond the end of file, it's a block in
+        * the file that may or may not have been assigned a disk address or
+        * we're writing an entire block.  Note, if the daddr is unassigned,
+        * the block might still have existed in the cache.  If it did, make
+        * sure we don't count it as a new block or zero out its contents.
         */
         */
-       if (i < NINDIR(fs) - 1) {
-               rablock = fsbtodb(fs, bap[i+1]);
-               rasize = fs->fs_bsize;
+       newblock = ip->i_size <= lbn << fs->lfs_bshift;
+       if (!newblock && (error = VOP_BMAP(vp, lbn, NULL, &daddr)))
+               return (error);
+
+       if (newblock || daddr == UNASSIGNED || iosize == fs->lfs_bsize) {
+               *bpp = bp = getblk(vp, lbn, fs->lfs_bsize);
+               if (newblock ||
+                   daddr == UNASSIGNED && !(bp->b_flags & B_CACHE)) {
+                       ip->i_blocks += btodb(fs->lfs_bsize);
+                       fs->lfs_bfree -= btodb(fs->lfs_bsize);
+                       if (iosize != fs->lfs_bsize)
+                               clrbuf(bp);
+               }
+               return (0);
        }
        }
-       return (nb);
+       return (bread(vp, lbn, fs->lfs_bsize, NOCRED, bpp));
+
 }
 }