* 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"
#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;
}
/*
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;
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;
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;
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);
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 {
#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.
*/
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);
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.
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;
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.
*/
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);