Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/ufs Change the way LFS does block accounting, from tryin...



details:   https://anonhg.NetBSD.org/src/rev/89690133a385
branches:  trunk
changeset: 485791:89690133a385
user:      perseant <perseant%NetBSD.org@localhost>
date:      Fri May 05 20:59:20 2000 +0000

description:
Change the way LFS does block accounting, from trying to infer from the
buffer cache flags, to marking the inode and/or indirect blocks with a
special disk address UNWRITTEN==-2 when a block is accounted for.  (This
address is never written to disk, but only used in-core.  This is essentially
the same method of block accounting as on the UBC branch, where the buffer
headers don't exist.)  Make sure that truncation is handled properly,
especially in the case of holey files.

Fixes PR#9994.

diffstat:

 sys/ufs/lfs/lfs.h         |    6 +-
 sys/ufs/lfs/lfs_balloc.c  |   78 ++++++++++++++++++++++++-----
 sys/ufs/lfs/lfs_bio.c     |   15 +++++-
 sys/ufs/lfs/lfs_inode.c   |  120 ++++++++++++++++++++++++---------------------
 sys/ufs/lfs/lfs_segment.c |   96 ++++++++++++++++++++++++++++++++----
 sys/ufs/lfs/lfs_subr.c    |    4 +-
 sys/ufs/ufs/ufs_vnops.c   |    6 +-
 7 files changed, 235 insertions(+), 90 deletions(-)

diffs (truncated from 663 to 300 lines):

diff -r c1c23cb83657 -r 89690133a385 sys/ufs/lfs/lfs.h
--- a/sys/ufs/lfs/lfs.h Fri May 05 20:59:17 2000 +0000
+++ b/sys/ufs/lfs/lfs.h Fri May 05 20:59:20 2000 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: lfs.h,v 1.20 2000/01/19 00:03:04 perseant Exp $        */
+/*     $NetBSD: lfs.h,v 1.21 2000/05/05 20:59:21 perseant Exp $        */
 
 /*-
  * Copyright (c) 1999 The NetBSD Foundation, Inc.
@@ -313,6 +313,7 @@
        daddr_t   lfs_sbactive;         /* disk address of in-progress sb write */
 #endif
        struct vnode *lfs_flushvp;      /* vnode being flushed */
+       struct vnode *lfs_unlockvp;     /* being inactivated in lfs_segunlock */
        u_int32_t lfs_diropwait;        /* # procs waiting on dirop flush */
        struct lock lfs_freelock;
 };
@@ -333,8 +334,9 @@
 #define        D_INDIR(fs)     (S_INDIR(fs) - NINDIR(fs) - 1)
 #define        T_INDIR(fs)     (D_INDIR(fs) - NINDIR(fs) * NINDIR(fs) - 1)
 
-/* Unassigned disk address. */
+/* Unassigned disk addresses. */
 #define        UNASSIGNED      -1
+#define UNWRITTEN       -2
 
 /* Unused logical block number */
 #define LFS_UNUSED_LBN -1
diff -r c1c23cb83657 -r 89690133a385 sys/ufs/lfs/lfs_balloc.c
--- a/sys/ufs/lfs/lfs_balloc.c  Fri May 05 20:59:17 2000 +0000
+++ b/sys/ufs/lfs/lfs_balloc.c  Fri May 05 20:59:20 2000 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: lfs_balloc.c,v 1.15 2000/04/23 21:10:26 perseant Exp $ */
+/*     $NetBSD: lfs_balloc.c,v 1.16 2000/05/05 20:59:21 perseant Exp $ */
 
 /*-
  * Copyright (c) 1999 The NetBSD Foundation, Inc.
@@ -99,6 +99,19 @@
 
 int lfs_fragextend __P((struct vnode *, int, int, ufs_daddr_t, struct buf **));
 
+/*
+ * Allocate a block, and to inode and filesystem block accounting for it
+ * and for any indirect blocks the may need to be created in order for
+ * this block to be created.
+ *
+ * Blocks which have never been accounted for (i.e., which "do not exist")
+ * have disk address 0, which is translated by ufs_bmap to the special value
+ * UNASSIGNED == -1, as in the historical UFS.
+ * 
+ * Blocks which have been accounted for but which have not yet been written
+ * to disk are given the new special disk address UNWRITTEN == -2, so that
+ * they can be differentiated from completely new blocks.
+ */
 int
 lfs_balloc(v)
        void *v;
@@ -118,7 +131,7 @@
        struct buf *ibp, *bp;
        struct inode *ip;
        struct lfs *fs;
-       struct indir indirs[NIADDR+2];
+       struct indir indirs[NIADDR+2], *idp;
        ufs_daddr_t     daddr, lastblock;
        int bb;         /* number of disk blocks in a block disk blocks */
        int error, frags, i, nsize, osize, num;
@@ -171,28 +184,45 @@
        }
        
        bb = VFSTOUFS(vp->v_mount)->um_seqinc;
-       if (daddr == UNASSIGNED)
+       if (daddr == UNASSIGNED) {
+               if (num > 0 && ip->i_ffs_ib[indirs[0].in_off] == 0) {
+                       ip->i_ffs_ib[indirs[0].in_off] = UNWRITTEN;
+               }
                /* May need to allocate indirect blocks */
-               for (i = 1; i < num; ++i)
+               for (i = 1; i < num; ++i) {
+                       ibp = getblk(vp, indirs[i].in_lbn, fs->lfs_bsize, 0,0);
                        if (!indirs[i].in_exists) {
-                               ibp = getblk(vp, indirs[i].in_lbn, fs->lfs_bsize,
-                                            0, 0);
-                               if ((ibp->b_flags & (B_DONE | B_DELWRI))) 
+#ifdef DIAGNOSTIC
+                               if ((ibp->b_flags & (B_DONE | B_DELWRI)))
                                        panic ("Indirect block should not exist");
+#endif
 
                                if (!ISSPACE(fs, bb, curproc->p_ucred)){
                                        ibp->b_flags |= B_INVAL;
                                        brelse(ibp);
+                                       /* XXX might leave some UNWRITTENs hanging, do this better */
                                        return(ENOSPC);
                                } else {
                                        ip->i_ffs_blocks += bb;
                                        ip->i_lfs->lfs_bfree -= bb;
                                        clrbuf(ibp);
-                                       if ((error = VOP_BWRITE(ibp)))
-                                               return(error);
+                                       ((ufs_daddr_t *)ibp->b_data)[indirs[i].in_off] = UNWRITTEN;
+                                       ibp->b_blkno = UNWRITTEN;
+                               }
+                       } else {
+                               /*
+                                * This block exists, but the next one may not.
+                                * If that is the case mark it UNWRITTEN to
+                                * keep the accounting straight.
+                                */
+                               if ( ((ufs_daddr_t *)ibp->b_data)[indirs[i].in_off] == 0) {
+                                       ((ufs_daddr_t *)ibp->b_data)[indirs[i].in_off] = UNWRITTEN;
                                }
                        }
-       
+                       if ((error = VOP_BWRITE(ibp)))
+                               return(error);
+               }
+       }       
        /*
         * If the block we are writing is a direct block, it's the last
         * block in the file, and offset + iosize is less than a full
@@ -239,11 +269,10 @@
         * for free space and update the inode number of blocks.
         *
         * We can tell a truly new block because (1) ufs_bmaparray
-        * will say it is UNASSIGNED; and (2) it will not be marked
-        * with B_DELWRI.  (It might be marked B_DONE, if it was
-        * read into the cache before it existed on disk.)
+        * will say it is UNASSIGNED.  Once we allocate it we will assign
+        * it the disk address UNWRITTEN.
         */
-       if ((!(bp->b_flags & B_DELWRI)) && daddr == UNASSIGNED) {
+       if (daddr == UNASSIGNED) {
                if (!ISSPACE(fs, bb, curproc->p_ucred)) {
                        bp->b_flags |= B_INVAL;
                        brelse(bp);
@@ -253,6 +282,27 @@
                        ip->i_lfs->lfs_bfree -= bb;
                        if (iosize != fs->lfs_bsize)
                                clrbuf(bp);
+
+                       /* Note the new address */
+                       bp->b_blkno = UNWRITTEN;
+                       
+                       switch (num) {
+                           case 0:
+                               ip->i_ffs_db[lbn] = UNWRITTEN;
+                               break;
+                           case 1:
+                               ip->i_ffs_ib[indirs[0].in_off] = UNWRITTEN;
+                               break;
+                           default:
+                               idp = &indirs[num - 1];
+                               if (bread(vp, idp->in_lbn, fs->lfs_bsize,
+                                         NOCRED, &ibp))
+                                       panic("lfs_balloc: bread bno %d",
+                                             idp->in_lbn);
+                               ((ufs_daddr_t *)ibp->b_data)[idp->in_off] =
+                                       UNWRITTEN;
+                               VOP_BWRITE(ibp);
+                       }
                }
        } else if (!(bp->b_flags & (B_DONE|B_DELWRI))) {
                /*
diff -r c1c23cb83657 -r 89690133a385 sys/ufs/lfs/lfs_bio.c
--- a/sys/ufs/lfs/lfs_bio.c     Fri May 05 20:59:17 2000 +0000
+++ b/sys/ufs/lfs/lfs_bio.c     Fri May 05 20:59:20 2000 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: lfs_bio.c,v 1.17 2000/03/30 12:41:13 augustss Exp $    */
+/*     $NetBSD: lfs_bio.c,v 1.18 2000/05/05 20:59:21 perseant Exp $    */
 
 /*-
  * Copyright (c) 1999 The NetBSD Foundation, Inc.
@@ -393,6 +393,19 @@
 #endif
                error = tsleep(&locked_queue_count, PCATCH | PUSER,
                               "buffers", hz * LFS_BUFWAIT);
+               /*
+                * lfs_flush might not flush all the buffers, if some of the
+                * inodes were locked.  Try flushing again to keep us from
+                * blocking indefinitely.
+                */
+               if (locked_queue_count > LFS_MAX_BUFS
+                   || locked_queue_bytes > LFS_MAX_BYTES)
+               {
+                       ++fs->lfs_writer;
+                       lfs_flush(fs, flags);
+                       if(--fs->lfs_writer==0)
+                               wakeup(&fs->lfs_dirops);
+               }
        }
        return (error);
 }
diff -r c1c23cb83657 -r 89690133a385 sys/ufs/lfs/lfs_inode.c
--- a/sys/ufs/lfs/lfs_inode.c   Fri May 05 20:59:17 2000 +0000
+++ b/sys/ufs/lfs/lfs_inode.c   Fri May 05 20:59:20 2000 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: lfs_inode.c,v 1.34 2000/04/24 19:07:16 perseant Exp $  */
+/*     $NetBSD: lfs_inode.c,v 1.35 2000/05/05 20:59:21 perseant Exp $  */
 
 /*-
  * Copyright (c) 1999 The NetBSD Foundation, Inc.
@@ -95,6 +95,8 @@
 #include <ufs/lfs/lfs_extern.h>
 
 static int lfs_vinvalbuf __P((struct vnode *, struct ucred *, struct proc *, ufs_daddr_t));
+extern int locked_queue_count;
+extern long locked_queue_bytes;
 
 /* Search a block for a specific dinode. */
 struct dinode *
@@ -184,7 +186,7 @@
 }
 
 /* Update segment usage information when removing a block. */
-#define UPDATE_SEGUSE \
+#define UPDATE_SEGUSE do { \
        if (lastseg != -1) { \
                LFS_SEGENTRY(sup, fs, lastseg, sup_bp); \
                if (num > sup->su_nbytes) { \
@@ -195,19 +197,22 @@
                } else \
                sup->su_nbytes -= num; \
                e1 = VOP_BWRITE(sup_bp); \
-               fragsreleased += numfrags(fs, num); \
-       }
+       } \
+       fragsreleased += numfrags(fs, num); \
+} while(0)
 
-#define SEGDEC(S) { \
-       if (daddr != 0) { \
+#define SEGDEC(S) do { \
+       if (daddr > 0) { \
                if (lastseg != (seg = datosn(fs, daddr))) { \
                        UPDATE_SEGUSE; \
                        num = (S); \
                        lastseg = seg; \
                } else \
                        num += (S); \
+       } else if (daddr == UNWRITTEN) { \
+               fragsreleased += numfrags(fs,(S)); \
        } \
-}
+} while(0)
 
 /*
  * Truncate the inode ip to at most length size.  Update segment usage
@@ -239,7 +244,7 @@
        SEGUSE *sup;
        ufs_daddr_t daddr, lastblock, lbn, olastblock;
        ufs_daddr_t oldsize_lastblock, oldsize_newlast, newsize;
-       long off, a_released, fragsreleased, i_released;
+       long off, a_released, fragsreleased;
        int e1, e2, depth, lastseg, num, offset, seg, freesize, s;
        
        if (length < 0)
@@ -288,15 +293,18 @@
        uvm_vnp_setsize(vp, length);
 
        /*
-        * Make sure no writes happen while we're truncating.
-        * Otherwise, blocks which are accounted for on the inode
-        * *and* which have been created for cleaning can coexist,
-        * and cause us to overcount, and panic below.
+        * Make sure no writes to this inode can happen while we're
+        * truncating.  Otherwise, blocks which are accounted for on the
+        * inode *and* which have been created for cleaning can coexist,
+        * and cause an overcounting.
         *
-        * XXX KS - too restrictive?  Maybe only when cleaning?
+        * (We don't need to *hold* the seglock, though, because we already
+        * hold the inode lock; draining the seglock is sufficient.)
         */
-       while(fs->lfs_seglock && fs->lfs_lockpid != ap->a_p->p_pid) {
-               tsleep(&fs->lfs_seglock, (PRIBIO+1), "lfs_truncate", 0);
+       if (vp != fs->lfs_unlockvp) {
+               while(fs->lfs_seglock) {
+                       tsleep(&fs->lfs_seglock, PRIBIO+1, "lfs_truncate", 0);
+               }
        }
        
        /*
@@ -339,12 +347,13 @@
                bzero((char *)bp->b_data + offset, (u_int)(newsize - offset));
 #ifdef DEBUG
                if(bp->b_flags & B_CALL)
-                   panic("Can't allocbuf malloced buffer!");
+                       panic("Can't allocbuf malloced buffer!");
                else
 #endif
                        allocbuf(bp, newsize);
-               if(oldsize_newlast > newsize)
+               if(bp->b_blkno != UNASSIGNED && oldsize_newlast > newsize) {
                        ip->i_ffs_blocks -= btodb(oldsize_newlast - newsize);
+               }
                if ((e1 = VOP_BWRITE(bp)) != 0) {
                        printf("lfs_truncate: bwrite: %d\n",e1);
                        return (e1);
@@ -393,15 +402,15 @@
                                        panic("lfs_truncate: bread bno %d",



Home | Main Index | Thread Index | Old Index