/* * Last reference to an inode. If necessary, write or delete it. */ int ufs_inactive(void *v) { struct vop_inactive_args *ap = v; struct vnode *vp = ap->a_vp; struct inode *ip = VTOI(vp); struct proc *p = ap->a_p; mode_t mode; int error = 0; #ifdef DIAGNOSTIC extern int prtactive; if (prtactive && vp->v_usecount != 0) vprint("ffs_inactive: pushing active", vp); #endif /* * Ignore inodes related to stale file handles. */ if (ip->i_din1 == NULL || DIP(ip, mode) == 0) goto out; if (DIP(ip, nlink) <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { if (getinoquota(ip) == 0) (void)ufs_quota_free_inode(ip, NOCRED); error = UFS_TRUNCATE(ip, (off_t)0, IO_EXT | IO_NORMAL, NOCRED); DIP_ASSIGN(ip, rdev, 0); mode = DIP(ip, mode); DIP_ASSIGN(ip, mode, 0); ip->i_flag |= IN_CHANGE | IN_UPDATE; /* * Setting the mode to zero needs to wait for the inode to be * written just as does a change to the link count. So, rather * than creating a new entry point to do the same thing, we * just use softdep_change_linkcnt(). Also, we can't let * softdep co-opt us to help on its worklist, as we may end up * trying to recycle vnodes and getting to this same point a * couple of times, blowing the kernel stack. However, this * could be optimized by checking if we are coming from * vrele(), vput() or vclean() (by checking for VXLOCK) and * just avoiding the co-opt to happen in the last case. */ if (DOINGSOFTDEP(vp)) softdep_change_linkcnt(ip, 1); UFS_INODE_FREE(ip, ip->i_number, mode); } if (ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) { UFS_UPDATE(ip, 0); } out: VOP_UNLOCK(vp, 0, p); /* * If we are done with the inode, reclaim it * so that it can be reused immediately. */ if (ip->i_din1 == NULL || DIP(ip, mode) == 0) vrecycle(vp, p); return (error); }
/* * Last reference to an inode. If necessary, write or delete it. */ int ufs_inactive(void *v) { struct vop_inactive_args *ap = v; struct vnode *vp = ap->a_vp; struct inode *ip = VTOI(vp); struct fs *fs = ip->i_fs; struct proc *p = curproc; mode_t mode; int error = 0, logged = 0, truncate_error = 0; #ifdef DIAGNOSTIC extern int prtactive; if (prtactive && vp->v_usecount != 0) vprint("ufs_inactive: pushing active", vp); #endif UFS_WAPBL_JUNLOCK_ASSERT(vp->v_mount); /* * Ignore inodes related to stale file handles. */ if (ip->i_din1 == NULL || DIP(ip, mode) == 0) goto out; if (DIP(ip, nlink) <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { error = UFS_WAPBL_BEGIN(vp->v_mount); if (error) goto out; logged = 1; if (getinoquota(ip) == 0) (void)ufs_quota_free_inode(ip, NOCRED); if (DIP(ip, size) != 0 && vp->v_mount->mnt_wapbl) { /* * When journaling, only truncate one indirect block at * a time. */ uint64_t incr = MNINDIR(ip->i_ump) << fs->fs_bshift; uint64_t base = NDADDR << fs->fs_bshift; while (!error && DIP(ip, size) > base + incr) { /* * round down to next full indirect block * boundary. */ uint64_t nsize = base + ((DIP(ip, size) - base - 1) & ~(incr - 1)); error = UFS_TRUNCATE(ip, nsize, 0, NOCRED); if (error) break; UFS_WAPBL_END(vp->v_mount); error = UFS_WAPBL_BEGIN(vp->v_mount); if (error) goto out; } } if (error == 0) { truncate_error = UFS_TRUNCATE(ip, (off_t)0, 0, NOCRED); /* XXX pedro: remove me */ if (truncate_error) printf("UFS_TRUNCATE()=%d\n", truncate_error); } DIP_ASSIGN(ip, rdev, 0); mode = DIP(ip, mode); DIP_ASSIGN(ip, mode, 0); ip->i_flag |= IN_CHANGE | IN_UPDATE; /* * Setting the mode to zero needs to wait for the inode to be * written just as does a change to the link count. So, rather * than creating a new entry point to do the same thing, we * just use softdep_change_linkcnt(). Also, we can't let * softdep co-opt us to help on its worklist, as we may end up * trying to recycle vnodes and getting to this same point a * couple of times, blowing the kernel stack. However, this * could be optimized by checking if we are coming from * vrele(), vput() or vclean() (by checking for VXLOCK) and * just avoiding the co-opt to happen in the last case. */ if (DOINGSOFTDEP(vp)) softdep_change_linkcnt(ip, 1); UFS_INODE_FREE(ip, ip->i_number, mode); } if (ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) { if (!logged++) { int err; err = UFS_WAPBL_BEGIN(vp->v_mount); if (err) { error = err; goto out; } } UFS_UPDATE(ip, 0); } if (logged) UFS_WAPBL_END(vp->v_mount); out: VOP_UNLOCK(vp, 0); /* * If we are done with the inode, reclaim it * so that it can be reused immediately. */ if (error == 0 && truncate_error == 0 && (ip->i_din1 == NULL || DIP(ip, mode) == 0)) vrecycle(vp, p); return (truncate_error ? truncate_error : error); }