X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/7188ac27426632d20adaac4191443dfefebf5789..5d96a9ad203aa299a3522e738eb6c3146e23d17f:/usr/src/sys/ufs/lfs/lfs_inode.c diff --git a/usr/src/sys/ufs/lfs/lfs_inode.c b/usr/src/sys/ufs/lfs/lfs_inode.c index 980b7262d2..6b2920177a 100644 --- a/usr/src/sys/ufs/lfs/lfs_inode.c +++ b/usr/src/sys/ufs/lfs/lfs_inode.c @@ -14,7 +14,7 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * @(#)lfs_inode.c 7.6 (Berkeley) %G% + * @(#)lfs_inode.c 7.22 (Berkeley) %G% */ #include "param.h" @@ -41,55 +41,29 @@ #define INOHASH(dev,ino) (((unsigned)((dev)+(ino)))%INOHSZ) #endif -#define INSFREE(ip) {\ - if (ifreeh) { \ - *ifreet = (ip); \ - (ip)->i_freeb = ifreet; \ - } else { \ - ifreeh = (ip); \ - (ip)->i_freeb = &ifreeh; \ - } \ - (ip)->i_freef = NULL; \ - ifreet = &(ip)->i_freef; \ -} - -union ihead { /* inode LRU cache, Chris Maltby */ +union ihead { union ihead *ih_head[2]; struct inode *ih_chain[2]; } ihead[INOHSZ]; -struct inode *ifreeh, **ifreet, *bdevlisth; +int prtactive; /* 1 => print out reclaim of active vnodes */ /* - * Initialize hash links for inodes - * and build inode free list. + * Initialize hash links for inodes. */ -ihinit() +ufs_init() { register int i; - register struct inode *ip = inode; - register union ihead *ih = ihead; + register union ihead *ih = ihead; +#ifndef lint + if (VN_MAXPRIVATE < sizeof(struct inode)) + panic("ihinit: too small"); +#endif /* not lint */ for (i = INOHSZ; --i >= 0; ih++) { ih->ih_head[0] = ih; ih->ih_head[1] = ih; } - ifreeh = ip; - ifreet = &ip->i_freef; - ip->i_freeb = &ifreeh; - ip->i_forw = ip; - ip->i_back = ip; - ITOV(ip)->v_data = (qaddr_t)ip; - for (i = ninode; --i > 0; ) { - ++ip; - ip->i_forw = ip; - ip->i_back = ip; - ITOV(ip)->v_data = (qaddr_t)ip; - *ifreet = ip; - ip->i_freeb = ifreet; - ifreet = &ip->i_freef; - } - ip->i_freef = NULL; } /* @@ -110,323 +84,131 @@ iget(xp, ino, ipp) dev_t dev = xp->i_dev; struct mount *mntp = ITOV(xp)->v_mount; register struct fs *fs = VFSTOUFS(mntp)->um_fs; + extern struct vnodeops ufs_vnodeops, spec_inodeops; register struct inode *ip, *iq; register struct vnode *vp; - struct inode *nip; + struct vnode *nvp; struct buf *bp; - struct dinode tdip, *dp; + struct dinode *dp; union ihead *ih; int error; -loop: ih = &ihead[INOHASH(dev, ino)]; - for (ip = ih->ih_chain[0]; ip != (struct inode *)ih; ip = ip->i_forw) - if (ino == ip->i_number && dev == ip->i_dev) { - /* - * Following is essentially an inline expanded - * copy of igrab(), expanded inline for speed, - * and so that the test for a mounted on inode - * can be deferred until after we are sure that - * the inode isn't busy. - */ - if ((ip->i_flag&ILOCKED) != 0) { - ip->i_flag |= IWANT; - sleep((caddr_t)ip, PINOD); - goto loop; - } - vp = ITOV(ip); - if (vp->v_count == 0) { /* ino on free list */ - if (iq = ip->i_freef) - iq->i_freeb = ip->i_freeb; - else - ifreet = ip->i_freeb; - *ip->i_freeb = iq; - ip->i_freef = NULL; - ip->i_freeb = NULL; - } - ip->i_flag |= ILOCKED; - vp->v_count++; - *ipp = ip; - return(0); +loop: + for (ip = ih->ih_chain[0]; ip != (struct inode *)ih; ip = ip->i_forw) { + if (ino != ip->i_number || dev != ip->i_dev) + continue; + if ((ip->i_flag&ILOCKED) != 0) { + ip->i_flag |= IWANT; + sleep((caddr_t)ip, PINOD); + goto loop; } - if (error = getnewino(dev, ino, &nip)) { + if (vget(ITOV(ip))) + goto loop; + *ipp = ip; + return(0); + } + /* + * Allocate a new inode. + */ + if (error = getnewvnode(VT_UFS, mntp, &ufs_vnodeops, &nvp)) { *ipp = 0; return (error); } - ip = nip; + ip = VTOI(nvp); + ip->i_vnode = nvp; + ip->i_flag = 0; + ip->i_devvp = 0; + ip->i_lastr = 0; + ip->i_mode = 0; +#ifdef QUOTA + ip->i_dquot = NODQUOT; +#endif + /* + * Put it onto its hash chain and lock it so that other requests for + * this inode will block if they arrive while we are sleeping waiting + * for old data structures to be purged or for the contents of the + * disk portion of this inode to be read. + */ + ip->i_dev = dev; + ip->i_number = ino; + insque(ip, ih); + ILOCK(ip); /* * Read in the disk contents for the inode. */ if (error = bread(VFSTOUFS(mntp)->um_devvp, fsbtodb(fs, itod(fs, ino)), - (int)fs->fs_bsize, &bp)) { + (int)fs->fs_bsize, NOCRED, &bp)) { /* - * The inode doesn't contain anything useful, so it would - * be misleading to leave it on its hash chain. Iput() will - * take care of putting it back on the free list. We also - * lose its inumber, just in case. + * Unlock and discard unneeded inode. */ - remque(ip); - ip->i_forw = ip; - ip->i_back = ip; - ip->i_number = 0; - INSFREE(ip); - ip->i_flag = 0; + iput(ip); brelse(bp); *ipp = 0; - return(error); + return (error); } - /* - * Check to see if the new inode represents a block device - * for which we already have an inode (either because of - * bdevvp() or because of a different inode representing - * the same block device). If such an alias exists, put the - * just allocated inode back on the free list, and replace - * the contents of the existing inode with the contents of - * the new inode. - */ dp = bp->b_un.b_dino; dp += itoo(fs, ino); - if ((dp->di_mode & IFMT) != IFBLK) { - ip->i_ic = dp->di_ic; - brelse(bp); - } else { -again: - for (iq = bdevlisth; iq; iq = iq->i_devlst) { - if (dp->di_rdev != ITOV(iq)->v_rdev) - continue; - igrab(iq); - if (dp->di_rdev != ITOV(iq)->v_rdev) { - iput(iq); - goto again; - } + ip->i_din = *dp; + brelse(bp); + /* + * Initialize the associated vnode + */ + vp = ITOV(ip); + vp->v_type = IFTOVT(ip->i_mode); + if (vp->v_type == VCHR || vp->v_type == VBLK) { + vp->v_op = &spec_inodeops; + if (nvp = checkalias(vp, ip->i_rdev, mntp)) { /* - * Discard unneeded inode. + * Reinitialize aliased inode. */ - remque(ip); - ip->i_forw = ip; - ip->i_back = ip; - ip->i_number = 0; - INSFREE(ip); - ip->i_flag = 0; + vp = nvp; + iq = VTOI(vp); + iq->i_vnode = vp; + iq->i_lastr = 0; + iq->i_flag = 0; + ILOCK(iq); + iq->i_din = ip->i_din; + iq->i_dev = dev; + iq->i_number = ino; + insque(iq, ih); /* - * Reinitialize aliased inode. - * We must release the buffer that we just read - * before doing the iupdat() to avoid a possible - * deadlock with updating an inode in the same - * disk block. + * Discard unneeded vnode */ + ip->i_mode = 0; + iput(ip); ip = iq; - vp = ITOV(iq); - tdip.di_ic = dp->di_ic; - brelse(bp); - error = iupdat(ip, &time, &time, 1); - ip->i_ic = tdip.di_ic; - remque(ip); - insque(ip, ih); - ip->i_dev = dev; - ip->i_number = ino; - if (ip->i_devvp) { - vrele(ip->i_devvp); - ip->i_devvp = 0; - } - cache_purge(vp); - break; - } - if (iq == 0) { - ip->i_ic = dp->di_ic; - brelse(bp); - ip->i_devlst = bdevlisth; - bdevlisth = ip; } } + if (ino == ROOTINO) + vp->v_flag |= VROOT; /* * Finish inode initialization. */ ip->i_fs = fs; ip->i_devvp = VFSTOUFS(mntp)->um_devvp; - ip->i_devvp->v_count++; - /* - * Initialize the associated vnode - */ - vp = ITOV(ip); - vinit(vp, mntp, IFTOVT(ip->i_mode), &ufs_vnodeops); - if (vp->v_type == VCHR || vp->v_type == VBLK) { - vp->v_rdev = ip->i_rdev; - vp->v_op = &blk_vnodeops; - } - if (ino == ROOTINO) - vp->v_flag |= VROOT; + VREF(ip->i_devvp); #ifdef QUOTA if (ip->i_mode != 0) ip->i_dquot = inoquota(ip); #endif - *ipp = ip; - return (0); -} - -/* - * Allocate a new inode. - * - * Put it onto its hash chain and lock it so that other requests for - * this inode will block if they arrive while we are sleeping waiting - * for old data structures to be purged or for the contents of the disk - * portion of this inode to be read. - */ -getnewino(dev, ino, ipp) - dev_t dev; - ino_t ino; - struct inode **ipp; -{ - union ihead *ih; - register struct inode *ip, *iq; - register struct vnode *vp; - /* - * Remove the next inode from the free list. + * Set up a generation number for this inode if it does not + * already have one. This should only happen on old filesystems. */ - if ((ip = ifreeh) == NULL) { - tablefull("inode"); - *ipp = 0; - return(ENFILE); - } - vp = ITOV(ip); - if (vp->v_count) - panic("free inode isn't"); - if (iq = ip->i_freef) - iq->i_freeb = &ifreeh; - ifreeh = iq; - ip->i_freef = NULL; - ip->i_freeb = NULL; - /* - * Now to take inode off the hash chain it was on - * (initially, or after an iflush, it is on a "hash chain" - * consisting entirely of itself, and pointed to by no-one) - * and put it on the chain for its new (ino, dev) pair. - */ - remque(ip); - ip->i_dev = dev; - ip->i_number = ino; - if (dev != NODEV) { - ih = &ihead[INOHASH(dev, ino)]; - insque(ip, ih); - } - ip->i_flag = ILOCKED; - ip->i_lastr = 0; -#endif SECSIZE - /* - * Purge old data structures associated with the inode. - */ - cache_purge(vp); - if (ip->i_devvp) { - vrele(ip->i_devvp); - ip->i_devvp = 0; - } -#ifdef QUOTA - dqrele(ip->i_dquot); - ip->i_dquot = NODQUOT; -#endif - if (vp->v_type == VBLK) { - if (bdevlisth == ip) { - bdevlisth = ip->i_devlst; - } else { - for (iq = bdevlisth; iq; iq = iq->i_devlst) { - if (iq->i_devlst != ip) - continue; - iq->i_devlst = ip->i_devlst; - break; - } - if (iq == NULL) - panic("missing bdev"); - } + if (ip->i_gen == 0) { + if (++nextgennumber < (u_long)time.tv_sec) + nextgennumber = time.tv_sec; + ip->i_gen = nextgennumber; + if ((vp->v_mount->m_flag & M_RDONLY) == 0) + ip->i_flag |= IMOD; } *ipp = ip; return (0); } /* - * Convert a pointer to an inode into a reference to an inode. - * - * This is basically the internal piece of iget (after the - * inode pointer is located) but without the test for mounted - * filesystems. It is caller's responsibility to check that - * the inode pointer is valid. - */ -igrab(ip) - register struct inode *ip; -{ - register struct vnode *vp = ITOV(ip); - - while ((ip->i_flag&ILOCKED) != 0) { - ip->i_flag |= IWANT; - sleep((caddr_t)ip, PINOD); - } - if (vp->v_count == 0) { /* ino on free list */ - register struct inode *iq; - - if (iq = ip->i_freef) - iq->i_freeb = ip->i_freeb; - else - ifreet = ip->i_freeb; - *ip->i_freeb = iq; - ip->i_freef = NULL; - ip->i_freeb = NULL; - } - vp->v_count++; - ip->i_flag |= ILOCKED; -} - -/* - * Create a vnode for a block device. - * Used for root filesystem, argdev, and swap areas. - */ -bdevvp(dev, vpp) - dev_t dev; - struct vnode **vpp; -{ - register struct inode *ip; - register struct vnode *vp; - struct inode *nip; - int error; - - /* - * Check for the existence of an existing vnode. - */ -again: - for (ip = bdevlisth; ip; ip = ip->i_devlst) { - vp = ITOV(ip); - if (dev != vp->v_rdev) - continue; - igrab(ip); - if (dev != vp->v_rdev) { - iput(ip); - goto again; - } - IUNLOCK(ip); - *vpp = vp; - return (0); - } - if (error = getnewino(NODEV, (ino_t)0, &nip)) { - *vpp = 0; - return (error); - } - ip = nip; - ip->i_fs = 0; - ip->i_devlst = bdevlisth; - bdevlisth = ip; - vp = ITOV(ip); - vinit(vp, 0, VBLK, &blk_vnodeops); - vp->v_rdev = dev; - IUNLOCK(ip); - *vpp = vp; - return (0); -} - -/* - * Decrement reference count of - * an inode structure. - * On the last reference, - * write the inode out and if necessary, - * truncate and deallocate the file. + * Unlock and decrement the reference count of an inode structure. */ iput(ip) register struct inode *ip; @@ -438,17 +220,28 @@ iput(ip) vrele(ITOV(ip)); } - +/* + * Last reference to an inode, write the inode out and if necessary, + * truncate and deallocate the file. + */ ufs_inactive(vp) struct vnode *vp; { register struct inode *ip = VTOI(vp); - int mode, error; + int mode, error = 0; - if (ITOV(ip)->v_count != 0) - panic("ufs_inactive: not inactive"); - ip->i_flag |= ILOCKED; - if (ip->i_nlink <= 0 && (ITOV(ip)->v_mount->m_flag&M_RDONLY) == 0) { + if (prtactive && vp->v_count != 0) + printf("ufs_inactive: pushing active ino %d dev 0x%x\n", + ip->i_number, ip->i_dev); + /* + * Get rid of inodes related to stale file handles. + */ + if (ip->i_mode == 0) { + vgone(vp); + return (0); + } + ILOCK(ip); + if (ip->i_nlink <= 0 && (vp->v_mount->m_flag & M_RDONLY) == 0) { error = itrunc(ip, (u_long)0); mode = ip->i_mode; ip->i_mode = 0; @@ -465,22 +258,52 @@ ufs_inactive(vp) IUNLOCK(ip); ip->i_flag = 0; /* - * Put the inode on the end of the free list. - * Possibly in some cases it would be better to - * put the inode at the head of the free list, - * (eg: where i_mode == 0 || i_number == 0). + * If we are done with the inode, reclaim it + * so that it can be reused immediately. */ - INSFREE(ip); + if (vp->v_count == 0 && ip->i_mode == 0) + vgone(vp); return (error); } /* - * Check accessed and update flags on - * an inode structure. - * If any is on, update the inode - * with the current time. - * If waitfor is given, then must insure - * i/o order so wait for write to complete. + * Reclaim an inode so that it can be used for other purposes. + */ +ufs_reclaim(vp) + register struct vnode *vp; +{ + register struct inode *ip = VTOI(vp); + + if (prtactive && vp->v_count != 0) + printf("ufs_reclaim: pushing active ino %d dev 0x%x\n", + ip->i_number, ip->i_dev); + /* + * Remove the inode from its hash chain. + */ + remque(ip); + ip->i_forw = ip; + ip->i_back = ip; + /* + * Purge old data structures associated with the inode. + */ + cache_purge(vp); + if (ip->i_devvp) { + vrele(ip->i_devvp); + ip->i_devvp = 0; + } +#ifdef QUOTA + dqrele(ip->i_dquot); + ip->i_dquot = NODQUOT; +#endif + ip->i_flag = 0; + return (0); +} + +/* + * Check accessed and update flags on an inode structure. + * If any is on, update the inode with the current time. + * If waitfor is given, then must ensure I/O order, + * so wait for write to complete. */ iupdat(ip, ta, tm, waitfor) register struct inode *ip; @@ -498,7 +321,7 @@ iupdat(ip, ta, tm, waitfor) if (vp->v_mount->m_flag & M_RDONLY) return (0); error = bread(ip->i_devvp, fsbtodb(fs, itod(fs, ip->i_number)), - (int)fs->fs_bsize, &bp); + (int)fs->fs_bsize, NOCRED, &bp); if (error) { brelse(bp); return (error); @@ -511,7 +334,7 @@ iupdat(ip, ta, tm, waitfor) ip->i_ctime = time.tv_sec; ip->i_flag &= ~(IUPD|IACC|ICHG|IMOD); dp = bp->b_un.b_dino + itoo(fs, ip->i_number); - dp->di_ic = ip->i_ic; + *dp = ip->i_din; if (waitfor) { return (bwrite(bp)); } else { @@ -524,10 +347,8 @@ iupdat(ip, ta, tm, waitfor) #define DOUBLE 1 /* index of double indirect block */ #define TRIPLE 2 /* index of triple indirect block */ /* - * Truncate the inode ip to at most - * length size. Free affected disk - * blocks -- the blocks of the file - * are removed in reverse order. + * Truncate the inode ip to at most length size. Free affected disk + * blocks -- the blocks of the file are removed in reverse order. * * NB: triple indirect blocks are untested. */ @@ -585,7 +406,7 @@ itrunc(oip, length) size = blksize(fs, oip, lbn); count = howmany(size, CLBYTES); munhash(oip->i_devvp, bn + i * CLBYTES / DEV_BSIZE); - error = bread(oip->i_devvp, bn, size, &bp); + error = bread(oip->i_devvp, bn, size, NOCRED, &bp); if (error) { oip->i_size = osize; brelse(bp); @@ -612,7 +433,7 @@ itrunc(oip, length) for (i = NDADDR - 1; i > lastblock; i--) oip->i_db[i] = 0; oip->i_flag |= ICHG|IUPD; - allerror = syncip(oip); + allerror = syncip(oip, MNT_WAIT); /* * Indirect blocks first. @@ -746,7 +567,8 @@ indirtrunc(ip, bn, lastbn, level, countp) bp = bread(ip->i_dev, fsbtodb(fs, bn), (int)fs->fs_bsize, fs->fs_dbsize); #else SECSIZE - error = bread(ip->i_devvp, fsbtodb(fs, bn), (int)fs->fs_bsize, &bp); + error = bread(ip->i_devvp, fsbtodb(fs, bn), (int)fs->fs_bsize, + NOCRED, &bp); if (error) { brelse(bp); *countp = 0; @@ -798,57 +620,6 @@ indirtrunc(ip, bn, lastbn, level, countp) return (allerror); } -/* - * Remove any inodes in the inode cache belonging to dev. - * - * There should not be any active ones, return error if any are found - * (nb: this is a user error, not a system err). - */ -#ifdef QUOTA -iflush(dev, iq) - dev_t dev; - struct inode *iq; -#else -iflush(dev) - dev_t dev; -#endif -{ - register struct inode *ip; - - for (ip = inode; ip < inodeNINODE; ip++) { -#ifdef QUOTA - if (ip != iq && ip->i_dev == dev) -#else - if (ip->i_dev == dev) -#endif - if (ITOV(ip)->v_count) - return (EBUSY); - else { - remque(ip); - ip->i_forw = ip; - ip->i_back = ip; - /* - * as v_count == 0, the inode was on the free - * list already, just leave it there, it will - * fall off the bottom eventually. We could - * perhaps move it to the head of the free - * list, but as umounts are done so - * infrequently, we would gain very little, - * while making the code bigger. - */ -#ifdef QUOTA - dqrele(ip->i_dquot); - ip->i_dquot = NODQUOT; -#endif - if (ip->i_devvp) { - vrele(ip->i_devvp); - ip->i_devvp = 0; - } - } - } - return (0); -} - /* * Lock an inode. If its already locked, set the WANT bit and sleep. */ @@ -894,12 +665,10 @@ iaccess(ip, mode, cred) struct ucred *cred; { register gid_t *gp; - register struct vnode *vp = ITOV(ip); int i; /* - * If you're the super-user, - * you always get access. + * If you're the super-user, you always get access. */ if (cred->cr_uid == 0) return (0);