DEVEL15-death-to-longc-procs-20060804
[openafs.git] / src / afs / afs_buffer.c
index f80b719..44c64d9 100644 (file)
@@ -19,7 +19,7 @@ RCSID
 #include "h/param.h"
 #include "h/types.h"
 #include "h/time.h"
-#if    defined(AFS_AIX31_ENV) || defined(AFS_DEC_ENV)
+#if    defined(AFS_AIX31_ENV) 
 #include "h/limits.h"
 #endif
 #if    !defined(AFS_AIX_ENV) && !defined(AFS_SUN5_ENV) && !defined(AFS_SGI_ENV) && !defined(AFS_LINUX20_ENV)
@@ -43,7 +43,6 @@ RCSID
 #include "afs/dir.h"
 
 #include "afs/afs_stats.h"
-#include "afs/longc_procs.h"
 #include "afs/afs.h"
 
 #ifndef        BUF_TIME_MAX
@@ -63,7 +62,7 @@ RCSID
 /* page hash table size - this is pretty intertwined with pHash */
 #define PHSIZE (PHPAGEMASK + PHFIDMASK + 1)
 /* the pHash macro */
-#define pHash(fid,page) ((((afs_int32)((fid)[0])) & PHFIDMASK) \
+#define pHash(fid,page) ((((afs_int32)(fid)) & PHFIDMASK) \
                         | (page & PHPAGEMASK))
 
 #ifdef dirty
@@ -88,7 +87,7 @@ static int nbuffers;
 static afs_int32 timecounter;
 
 /* Prototypes for static routines */
-static struct buffer *afs_newslot(afs_inode_t * afid, afs_int32 apage,
+static struct buffer *afs_newslot(struct dcache *adc, afs_int32 apage,
                                  register struct buffer *lp);
 
 static int dinit_flag = 0;
@@ -98,7 +97,7 @@ DInit(int abuffers)
     /* Initialize the venus buffer system. */
     register int i;
     register struct buffer *tb;
-#if AFS_USEBUFFERS
+#if defined(AFS_USEBUFFERS)
     struct buf *tub;           /* unix buffer for allocation */
 #endif
 
@@ -106,14 +105,14 @@ DInit(int abuffers)
     if (dinit_flag)
        return;
     dinit_flag = 1;
-#if AFS_USEBUFFERS
+#if defined(AFS_USEBUFFERS)
     /* round up to next multiple of NPB, since we allocate multiple pages per chunk */
     abuffers = ((abuffers - 1) | (NPB - 1)) + 1;
 #endif
     LOCK_INIT(&afs_bufferLock, "afs_bufferLock");
     Buffers =
        (struct buffer *)afs_osi_Alloc(abuffers * sizeof(struct buffer));
-#if !AFS_USEBUFFERS
+#if !defined(AFS_USEBUFFERS)
     BufferData = (char *)afs_osi_Alloc(abuffers * AFS_BUFFER_PAGESIZE);
 #endif
     timecounter = 1;
@@ -121,7 +120,7 @@ DInit(int abuffers)
     for (i = 0; i < PHSIZE; i++)
        phTable[i] = 0;
     for (i = 0; i < abuffers; i++) {
-#if AFS_USEBUFFERS
+#if defined(AFS_USEBUFFERS)
        if ((i & (NPB - 1)) == 0) {
            /* time to allocate a fresh buffer */
            tub = geteblk(AFS_BUFFER_PAGESIZE * NPB);
@@ -130,10 +129,11 @@ DInit(int abuffers)
 #endif
        /* Fill in each buffer with an empty indication. */
        tb = &Buffers[i];
-       dirp_Zap(tb->fid);
+       tb->fid = NULLIDX;
+       afs_reset_inode(&tb->inode);
        tb->accesstime = 0;
        tb->lockers = 0;
-#if AFS_USEBUFFERS
+#if defined(AFS_USEBUFFERS)
        if ((i & (NPB - 1)) == 0)
            tb->bufp = tub;
        else
@@ -149,8 +149,8 @@ DInit(int abuffers)
     return;
 }
 
-char *
-DRead(register afs_inode_t * fid, register int page)
+void *
+DRead(register struct dcache *adc, register int page)
 {
     /* Read a page from the disk. */
     register struct buffer *tb, *tb2;
@@ -160,7 +160,7 @@ DRead(register afs_inode_t * fid, register int page)
     AFS_STATCNT(DRead);
     MObtainWriteLock(&afs_bufferLock, 256);
 
-#define bufmatch(tb) (tb->page == page && dirp_Eq(tb->fid, fid))
+#define bufmatch(tb) (tb->page == page && tb->fid == adc->index)
 #define buf_Front(head,parent,p) {(parent)->hashNext = (p)->hashNext; (p)->hashNext= *(head);*(head)=(p);}
 
     /* this apparently-complicated-looking code is simply an example of
@@ -169,7 +169,7 @@ DRead(register afs_inode_t * fid, register int page)
      * of larger code size.  This could be simplified by better use of
      * macros. 
      */
-    if ((tb = phTable[pHash(fid, page)])) {
+    if ((tb = phTable[pHash(adc->index, page)])) {
        if (bufmatch(tb)) {
            MObtainWriteLock(&tb->lock, 257);
            ReleaseWriteLock(&afs_bufferLock);
@@ -180,7 +180,7 @@ DRead(register afs_inode_t * fid, register int page)
            return tb->data;
        } else {
            register struct buffer **bufhead;
-           bufhead = &(phTable[pHash(fid, page)]);
+           bufhead = &(phTable[pHash(adc->index, page)]);
            while ((tb2 = tb->hashNext)) {
                if (bufmatch(tb2)) {
                    buf_Front(bufhead, tb, tb2);
@@ -216,7 +216,7 @@ DRead(register afs_inode_t * fid, register int page)
      * is at least the oldest buffer on one particular hash chain, so it's 
      * a pretty good place to start looking for the truly oldest buffer.
      */
-    tb = afs_newslot(fid, page, (tb ? tb : tb2));
+    tb = afs_newslot(adc, page, (tb ? tb : tb2));
     if (!tb) {
        MReleaseWriteLock(&afs_bufferLock);
        return NULL;
@@ -224,20 +224,21 @@ DRead(register afs_inode_t * fid, register int page)
     MObtainWriteLock(&tb->lock, 260);
     MReleaseWriteLock(&afs_bufferLock);
     tb->lockers++;
-    tfile = afs_CFileOpen(fid[0]);
-    if (page * AFS_BUFFER_PAGESIZE >= tfile->size) {
-       dirp_Zap(tb->fid);
+    if (page * AFS_BUFFER_PAGESIZE >= adc->f.chunkBytes) {
+       tb->fid = NULLIDX;
+       afs_reset_inode(&tb->inode);
        tb->lockers--;
        MReleaseWriteLock(&tb->lock);
-       afs_CFileClose(tfile);
        return NULL;
     }
+    tfile = afs_CFileOpen(&adc->f.inode);
     code =
        afs_CFileRead(tfile, tb->page * AFS_BUFFER_PAGESIZE, tb->data,
                      AFS_BUFFER_PAGESIZE);
     afs_CFileClose(tfile);
     if (code < AFS_BUFFER_PAGESIZE) {
-       dirp_Zap(tb->fid);
+       tb->fid = NULLIDX;
+       afs_reset_inode(&tb->inode);
        tb->lockers--;
        MReleaseWriteLock(&tb->lock);
        return NULL;
@@ -274,7 +275,7 @@ FixupBucket(register struct buffer *ap)
 
 /* lp is pointer to a fairly-old buffer */
 static struct buffer *
-afs_newslot(afs_inode_t * afid, afs_int32 apage, register struct buffer *lp)
+afs_newslot(struct dcache *adc, afs_int32 apage, register struct buffer *lp)
 {
     /* Find a usable buffer slot */
     register afs_int32 i;
@@ -341,7 +342,8 @@ afs_newslot(afs_inode_t * afid, afs_int32 apage, register struct buffer *lp)
     }
 
     if (lp->dirty) {
-       tfile = afs_CFileOpen(lp->fid[0]);
+       /* see DFlush for rationale for not getting and locking the dcache */
+        tfile = afs_CFileOpen(&lp->inode);
        afs_CFileWrite(tfile, lp->page * AFS_BUFFER_PAGESIZE, lp->data,
                       AFS_BUFFER_PAGESIZE);
        lp->dirty = 0;
@@ -350,7 +352,8 @@ afs_newslot(afs_inode_t * afid, afs_int32 apage, register struct buffer *lp)
     }
 
     /* Now fill in the header. */
-    dirp_Cpy(lp->fid, afid);   /* set this */
+    lp->fid = adc->index;
+    afs_copy_inode(&lp->inode, &adc->f.inode);
     lp->page = apage;
     lp->accesstime = timecounter++;
     FixupBucket(lp);           /* move to the right hash bucket */
@@ -364,14 +367,14 @@ DRelease(register struct buffer *bp, int flag)
     /* Release a buffer, specifying whether or not the buffer has been
      * modified by the locker. */
     register int index;
-#if AFS_USEBUFFERS
+#if defined(AFS_USEBUFFERS)
     register struct buffer *tp;
 #endif
 
     AFS_STATCNT(DRelease);
     if (!bp)
        return;
-#if AFS_USEBUFFERS
+#if defined(AFS_USEBUFFERS)
     /* look for buffer by scanning Unix buffers for appropriate address */
     tp = Buffers;
     for (index = 0; index < nbuffers; index += NPB, tp += NPB) {
@@ -400,12 +403,12 @@ DVOffset(register void *ap)
     /* Return the byte within a file represented by a buffer pointer. */
     register struct buffer *bp;
     register int index;
-#if AFS_USEBUFFERS
+#if defined(AFS_USEBUFFERS)
     register struct buffer *tp;
 #endif
     AFS_STATCNT(DVOffset);
     bp = ap;
-#if AFS_USEBUFFERS
+#if defined(AFS_USEBUFFERS)
     /* look for buffer by scanning Unix buffers for appropriate address */
     tp = Buffers;
     for (index = 0; index < nbuffers; index += NPB, tp += NPB) {
@@ -426,14 +429,19 @@ DVOffset(register void *ap)
     return AFS_BUFFER_PAGESIZE * bp->page + (int)(((char *)ap) - bp->data);
 }
 
-/* 1/1/91 - I've modified the hash function to take the page as well
+/*! 
+ * Zap one dcache entry: destroy one FID's buffers.
+ *
+ * 1/1/91 - I've modified the hash function to take the page as well
  * as the *fid, so that lookup will be a bit faster.  That presents some
  * difficulties for Zap, which now has to have some knowledge of the nature
  * of the hash function.  Oh well.  This should use the list traversal 
  * method of DRead...
+ *
+ * \param adc The dcache entry to be zapped.
  */
 void
-DZap(afs_inode_t * fid)
+DZap(struct dcache *adc)
 {
     register int i;
     /* Destroy all buffers pertaining to a particular fid. */
@@ -443,23 +451,59 @@ DZap(afs_inode_t * fid)
     MObtainReadLock(&afs_bufferLock);
 
     for (i = 0; i <= PHPAGEMASK; i++)
-       for (tb = phTable[pHash(fid, i)]; tb; tb = tb->hashNext)
-           if (dirp_Eq(tb->fid, fid)) {
+       for (tb = phTable[pHash(adc->index, i)]; tb; tb = tb->hashNext)
+           if (tb->fid == adc->index) {
                MObtainWriteLock(&tb->lock, 262);
-               dirp_Zap(tb->fid);
+               tb->fid = NULLIDX;
+               afs_reset_inode(&tb->inode);
                tb->dirty = 0;
                MReleaseWriteLock(&tb->lock);
            }
     MReleaseReadLock(&afs_bufferLock);
 }
 
+static void
+DFlushBuffer(struct buffer *ab) {
+    struct osi_file *tfile;
+    
+    tfile = afs_CFileOpen(&ab->inode);
+    afs_CFileWrite(tfile, ab->page * AFS_BUFFER_PAGESIZE,
+                  ab->data, AFS_BUFFER_PAGESIZE);
+    ab->dirty = 0;     /* Clear the dirty flag */
+    afs_CFileClose(tfile);
+}
+
+void
+DFlushDCache(struct dcache *adc) 
+{
+    int i;
+    struct buffer *tb;
+
+    ObtainReadLock(&afs_bufferLock);
+
+    for (i = 0; i <= PHPAGEMASK; i++)
+        for (tb = phTable[pHash(adc->index, i)]; tb; tb = tb->hashNext)
+           if (tb->fid == adc->index) {
+               ObtainWriteLock(&tb->lock, 701);
+               tb->lockers++;
+               ReleaseReadLock(&afs_bufferLock);
+               if (tb->dirty) {
+                   DFlushBuffer(tb);
+               }
+               tb->lockers--;
+               ReleaseWriteLock(&tb->lock);
+               ObtainReadLock(&afs_bufferLock);
+           }
+
+    ReleaseReadLock(&afs_bufferLock);
+}
+
 void
 DFlush(void)
 {
     /* Flush all the modified buffers. */
     register int i;
     register struct buffer *tb;
-    struct osi_file *tfile;
 
     AFS_STATCNT(DFlush);
     tb = Buffers;
@@ -470,11 +514,18 @@ DFlush(void)
            tb->lockers++;
            MReleaseReadLock(&afs_bufferLock);
            if (tb->dirty) {
-               tfile = afs_CFileOpen(tb->fid[0]);
-               afs_CFileWrite(tfile, tb->page * AFS_BUFFER_PAGESIZE,
-                              tb->data, AFS_BUFFER_PAGESIZE);
-               tb->dirty = 0;  /* Clear the dirty flag */
-               afs_CFileClose(tfile);
+               /* it seems safe to do this I/O without having the dcache
+                * locked, since the only things that will update the data in
+                * a directory are the buffer package, which holds the relevant
+                * tb->lock while doing the write, or afs_GetDCache, which 
+                * DZap's the directory while holding the dcache lock.
+                * It is not possible to lock the dcache or even call
+                * afs_GetDSlot to map the index to the dcache since the dir
+                * package's caller has some dcache object locked already (so
+                * we cannot lock afs_xdcache). In addition, we cannot obtain
+                * a dcache lock while holding the tb->lock of the same file
+                * since that can deadlock with DRead/DNew */
+               DFlushBuffer(tb);
            }
            tb->lockers--;
            MReleaseWriteLock(&tb->lock);
@@ -484,17 +535,26 @@ DFlush(void)
     MReleaseReadLock(&afs_bufferLock);
 }
 
-char *
-DNew(register afs_inode_t * fid, register int page)
+void *
+DNew(register struct dcache *adc, register int page)
 {
     /* Same as read, only do *not* even try to read the page, since it probably doesn't exist. */
     register struct buffer *tb;
     AFS_STATCNT(DNew);
     MObtainWriteLock(&afs_bufferLock, 264);
-    if ((tb = afs_newslot(fid, page, NULL)) == 0) {
+    if ((tb = afs_newslot(adc, page, NULL)) == 0) {
        MReleaseWriteLock(&afs_bufferLock);
        return 0;
     }
+    /* extend the chunk, if needed */
+    /* Do it now, not in DFlush or afs_newslot when the data is written out,
+     * since now our caller has adc->lock writelocked, and we can't acquire
+     * that lock (or even map from a fid to a dcache) in afs_newslot or
+     * DFlush due to lock hierarchy issues */
+    if ((page + 1) * AFS_BUFFER_PAGESIZE > adc->f.chunkBytes) {
+       afs_AdjustSize(adc, (page + 1) * AFS_BUFFER_PAGESIZE);
+       afs_WriteDCache(adc, 1);
+    }
     MObtainWriteLock(&tb->lock, 265);
     MReleaseWriteLock(&afs_bufferLock);
     tb->lockers++;
@@ -505,18 +565,17 @@ DNew(register afs_inode_t * fid, register int page)
 void
 shutdown_bufferpackage(void)
 {
-#if AFS_USEBUFFERS
+#if defined(AFS_USEBUFFERS)
     register struct buffer *tp;
 #endif
     int i;
-    extern int afs_cold_shutdown;
 
     AFS_STATCNT(shutdown_bufferpackage);
     /* Free all allocated Buffers and associated buffer pages */
     DFlush();
     if (afs_cold_shutdown) {
        dinit_flag = 0;
-#if !AFS_USEBUFFERS
+#if !defined(AFS_USEBUFFERS)
        afs_osi_Free(BufferData, nbuffers * AFS_BUFFER_PAGESIZE);
 #else
        tp = Buffers;