diff options
author | mckusick <mckusick@FreeBSD.org> | 2001-03-21 04:09:01 +0000 |
---|---|---|
committer | mckusick <mckusick@FreeBSD.org> | 2001-03-21 04:09:01 +0000 |
commit | 69603157dedb4757ce880ddfc5157558441cf985 (patch) | |
tree | 6378d524f9c6f5c077153375f319c0f93a7682cf /sys | |
parent | 39275d892cc0291136bb750a2f1191137371de42 (diff) | |
download | FreeBSD-src-69603157dedb4757ce880ddfc5157558441cf985.zip FreeBSD-src-69603157dedb4757ce880ddfc5157558441cf985.tar.gz |
Add kernel support for running fsck on active filesystems.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/ufs/ffs/ffs_alloc.c | 233 | ||||
-rw-r--r-- | sys/ufs/ffs/ffs_extern.h | 17 | ||||
-rw-r--r-- | sys/ufs/ffs/ffs_softdep.c | 35 | ||||
-rw-r--r-- | sys/ufs/ffs/ffs_vfsops.c | 6 | ||||
-rw-r--r-- | sys/ufs/ffs/fs.h | 22 | ||||
-rw-r--r-- | sys/ufs/ufs/ufs_inode.c | 8 |
6 files changed, 278 insertions, 43 deletions
diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c index cea935b..9166083 100644 --- a/sys/ufs/ffs/ffs_alloc.c +++ b/sys/ufs/ffs/ffs_alloc.c @@ -41,6 +41,7 @@ #include <sys/bio.h> #include <sys/buf.h> #include <sys/conf.h> +#include <sys/file.h> #include <sys/proc.h> #include <sys/vnode.h> #include <sys/mount.h> @@ -327,8 +328,6 @@ nospace: return (ENOSPC); } -SYSCTL_NODE(_vfs, OID_AUTO, ffs, CTLFLAG_RW, 0, "FFS filesystem"); - /* * Reallocate a sequence of blocks into a contiguous sequence of blocks. * @@ -343,11 +342,14 @@ SYSCTL_NODE(_vfs, OID_AUTO, ffs, CTLFLAG_RW, 0, "FFS filesystem"); * Note that the error return is not reflected back to the user. Rather * the previous block allocation will be used. */ + +SYSCTL_NODE(_vfs, OID_AUTO, ffs, CTLFLAG_RW, 0, "FFS filesystem"); + static int doasyncfree = 1; -SYSCTL_INT(_vfs_ffs, FFS_ASYNCFREE, doasyncfree, CTLFLAG_RW, &doasyncfree, 0, ""); +SYSCTL_INT(_vfs_ffs, OID_AUTO, doasyncfree, CTLFLAG_RW, &doasyncfree, 0, ""); static int doreallocblks = 1; -SYSCTL_INT(_vfs_ffs, FFS_REALLOCBLKS, doreallocblks, CTLFLAG_RW, &doreallocblks, 0, ""); +SYSCTL_INT(_vfs_ffs, OID_AUTO, doreallocblks, CTLFLAG_RW, &doreallocblks, 0, ""); #ifdef DEBUG static volatile int prtrealloc = 0; @@ -612,7 +614,7 @@ ffs_valloc(pvp, mode, cred, vpp) ip->i_mode, (u_long)ip->i_number, fs->fs_fsmnt); panic("ffs_valloc: dup alloc"); } - if (ip->i_blocks) { /* XXX */ + if (ip->i_blocks && (fs->fs_flags & FS_UNCLEAN) == 0) { /* XXX */ printf("free inode %s/%lu had %ld blocks\n", fs->fs_fsmnt, (u_long)ino, (long)ip->i_blocks); ip->i_blocks = 0; @@ -1478,7 +1480,7 @@ ffs_checkblk(ip, bno, size) * Free an inode. */ int -ffs_vfree( pvp, ino, mode) +ffs_vfree(pvp, ino, mode) struct vnode *pvp; ino_t ino; int mode; @@ -1487,27 +1489,25 @@ ffs_vfree( pvp, ino, mode) softdep_freefile(pvp, ino, mode); return (0); } - return (ffs_freefile(pvp, ino, mode)); + return (ffs_freefile(VTOI(pvp), ino, mode)); } /* * Do the actual free operation. * The specified inode is placed back in the free map. */ - int - ffs_freefile( pvp, ino, mode) - struct vnode *pvp; +int +ffs_freefile(pip, ino, mode) + struct inode *pip; ino_t ino; int mode; { register struct fs *fs; register struct cg *cgp; - register struct inode *pip; struct buf *bp; int error, cg; u_int8_t *inosused; - pip = VTOI(pvp); fs = pip->i_fs; if ((u_int)ino >= fs->fs_ipg * fs->fs_ncg) panic("ffs_vfree: range: dev = (%d,%d), ino = %d, fs = %s", @@ -1529,8 +1529,8 @@ ffs_vfree( pvp, ino, mode) inosused = cg_inosused(cgp); ino %= fs->fs_ipg; if (isclr(inosused, ino)) { - printf("dev = %s, ino = %lu, fs = %s\n", - devtoname(pip->i_dev), (u_long)ino, fs->fs_fsmnt); + printf("dev = %s, ino = %lu, fs = %s\n", devtoname(pip->i_dev), + (u_long)ino + cg * fs->fs_ipg, fs->fs_fsmnt); if (fs->fs_ronly == 0) panic("ffs_vfree: freeing free inode"); } @@ -1726,3 +1726,208 @@ ffs_fserr(fs, uid, cp) log(LOG_ERR, "pid %d (%s), uid %d on %s: %s\n", p ? p->p_pid : -1, p ? p->p_comm : "-", uid, fs->fs_fsmnt, cp); } + +/* + * This function provides the capability for the fsck program to + * update an active filesystem. Six operations are provided: + * + * adjrefcnt(inode, amt) - adjusts the reference count on the + * specified inode by the specified amount. Under normal + * operation the count should always go down. Decrementing + * the count to zero will cause the inode to be freed. + * adjblkcnt(inode, amt) - adjust the number of blocks used to + * by the specifed amount. + * freedirs(inode, count) - directory inodes [inode..inode + count - 1] + * are marked as free. Inodes should never have to be marked + * as in use. + * freefiles(inode, count) - file inodes [inode..inode + count - 1] + * are marked as free. Inodes should never have to be marked + * as in use. + * freeblks(blockno, size) - blocks [blockno..blockno + size - 1] + * are marked as free. Blocks should never have to be marked + * as in use. + * setflags(flags, set/clear) - the fs_flags field has the specified + * flags set (second parameter +1) or cleared (second parameter -1). + */ + +static int sysctl_ffs_fsck __P((SYSCTL_HANDLER_ARGS)); + +SYSCTL_PROC(_vfs_ffs, FFS_ADJ_REFCNT, adjrefcnt, CTLFLAG_WR|CTLTYPE_STRUCT, + 0, 0, sysctl_ffs_fsck, "S,fsck", "Adjust Inode Reference Count"); + +SYSCTL_NODE(_vfs_ffs, FFS_ADJ_BLKCNT, adjblkcnt, CTLFLAG_WR, + sysctl_ffs_fsck, "Adjust Inode Used Blocks Count"); + +SYSCTL_NODE(_vfs_ffs, FFS_DIR_FREE, freedirs, CTLFLAG_WR, + sysctl_ffs_fsck, "Free Range of Directory Inodes"); + +SYSCTL_NODE(_vfs_ffs, FFS_FILE_FREE, freefiles, CTLFLAG_WR, + sysctl_ffs_fsck, "Free Range of File Inodes"); + +SYSCTL_NODE(_vfs_ffs, FFS_BLK_FREE, freeblks, CTLFLAG_WR, + sysctl_ffs_fsck, "Free Range of Blocks"); + +SYSCTL_NODE(_vfs_ffs, FFS_SET_FLAGS, setflags, CTLFLAG_WR, + sysctl_ffs_fsck, "Change Filesystem Flags"); + +#ifdef DEBUG +static int fsckcmds = 0; +SYSCTL_INT(_debug, OID_AUTO, fsckcmds, CTLFLAG_RW, &fsckcmds, 0, ""); +#endif /* DEBUG */ + +static int +sysctl_ffs_fsck(SYSCTL_HANDLER_ARGS) +{ + struct fsck_cmd cmd; + struct inode tip; + struct ufsmount *ump; + struct vnode *vp; + struct inode *ip; + struct mount *mp; + struct fs *fs; + ufs_daddr_t blkno; + long blkcnt, blksize; + struct file *fp; + int filetype, error; + + if (req->newlen > sizeof cmd) + return (EBADRPC); + if ((error = SYSCTL_IN(req, &cmd, sizeof cmd)) != 0) + return (error); + if (cmd.version != FFS_CMD_VERSION) + return (ERPCMISMATCH); + if ((error = getvnode(curproc->p_fd, cmd.handle, &fp)) != 0) + return (error); + mp = ((struct vnode *)fp->f_data)->v_mount; + if (mp->mnt_flag & MNT_RDONLY) + return (EROFS); + ump = VFSTOUFS(mp); + fs = ump->um_fs; + filetype = IFREG; + + switch (oidp->oid_number) { + + case FFS_SET_FLAGS: +#ifdef DEBUG + if (fsckcmds) + printf("%s: %s flags\n", mp->mnt_stat.f_mntonname, + cmd.size > 0 ? "set" : "clear"); +#endif /* DEBUG */ + if (cmd.size > 0) + fs->fs_flags |= (long)cmd.value; + else + fs->fs_flags &= ~(long)cmd.value; + break; + + case FFS_ADJ_REFCNT: +#ifdef DEBUG + if (fsckcmds) { + printf("%s: adjust inode %d count by %ld\n", + mp->mnt_stat.f_mntonname, (ino_t)cmd.value, + cmd.size); + } +#endif /* DEBUG */ + if ((error = VFS_VGET(mp, (ino_t)cmd.value, &vp)) != 0) + return (error); + ip = VTOI(vp); + ip->i_nlink += cmd.size; + ip->i_effnlink += cmd.size; + ip->i_flag |= IN_CHANGE; + if (DOINGSOFTDEP(vp)) + softdep_change_linkcnt(ip); + vput(vp); + break; + + case FFS_ADJ_BLKCNT: +#ifdef DEBUG + if (fsckcmds) { + printf("%s: adjust inode %d block count by %ld\n", + mp->mnt_stat.f_mntonname, (ino_t)cmd.value, + cmd.size); + } +#endif /* DEBUG */ + if ((error = VFS_VGET(mp, (ino_t)cmd.value, &vp)) != 0) + return (error); + ip = VTOI(vp); + ip->i_blocks += cmd.size; + ip->i_flag |= IN_CHANGE; + vput(vp); + break; + + case FFS_DIR_FREE: + filetype = IFDIR; + /* fall through */ + + case FFS_FILE_FREE: +#ifdef DEBUG + if (fsckcmds) { + if (cmd.size == 1) + printf("%s: free %s inode %d\n", + mp->mnt_stat.f_mntonname, + filetype == IFDIR ? "directory" : "file", + (ino_t)cmd.value); + else + printf("%s: free %s inodes %d-%d\n", + mp->mnt_stat.f_mntonname, + filetype == IFDIR ? "directory" : "file", + (ino_t)cmd.value); + (ino_t)cmd.value + cmd.size - 1); + } +#endif /* DEBUG */ + tip.i_devvp = ump->um_devvp; + tip.i_dev = ump->um_dev; + tip.i_fs = fs; + while (cmd.size > 0) { + if ((error = ffs_freefile(&tip, cmd.value, filetype))) + return (error); + cmd.size -= 1; + cmd.value += 1; + } + break; + + case FFS_BLK_FREE: +#ifdef DEBUG + if (fsckcmds) { + if (cmd.size == 1) + printf("%s: free block %d\n", + mp->mnt_stat.f_mntonname, + (ufs_daddr_t)cmd.value); + else + printf("%s: free blocks %d-%ld\n", + mp->mnt_stat.f_mntonname, + (ufs_daddr_t)cmd.value, + (ufs_daddr_t)cmd.value + cmd.size - 1); + } +#endif /* DEBUG */ + tip.i_number = ROOTINO; + tip.i_devvp = ump->um_devvp; + tip.i_dev = ump->um_dev; + tip.i_fs = fs; + tip.i_size = cmd.size * fs->fs_fsize; + tip.i_uid = 0; + tip.i_vnode = NULL; + blkno = (ufs_daddr_t)cmd.value; + blkcnt = cmd.size; + blksize = fs->fs_frag - (blkno % fs->fs_frag); + while (blkcnt > 0) { + if (blksize > blkcnt) + blksize = blkcnt; + ffs_blkfree(&tip, blkno, blksize * fs->fs_fsize); + blkno += blksize; + blkcnt -= blksize; + blksize = fs->fs_frag; + } + break; + + default: +#ifdef DEBUG + if (fsckcmds) { + printf("Invalid request %d from fsck\n", + oidp->oid_number); + } +#endif /* DEBUG */ + return(EINVAL); + + } + return (0); +} diff --git a/sys/ufs/ffs/ffs_extern.h b/sys/ufs/ffs/ffs_extern.h index a755980..4c8e22f 100644 --- a/sys/ufs/ffs/ffs_extern.h +++ b/sys/ufs/ffs/ffs_extern.h @@ -37,21 +37,6 @@ #ifndef _UFS_FFS_EXTERN_H #define _UFS_FFS_EXTERN_H -/* - * Sysctl values for the fast filesystem. - */ -#define FFS_REALLOCBLKS 3 /* block reallocation enabled */ -#define FFS_ASYNCFREE 4 /* asynchronous block freeing enabled */ -#define FFS_MAXID 5 /* number of valid ffs ids */ - -#define FFS_NAMES { \ - { 0, 0 }, \ - { 0, 0 }, \ - { 0, 0 }, \ - { "doreallocblks", CTLTYPE_INT }, \ - { "doasyncfree", CTLTYPE_INT }, \ -} - struct buf; struct fid; struct fs; @@ -80,7 +65,7 @@ void ffs_clrblock __P((struct fs *, u_char *, ufs_daddr_t)); int ffs_fhtovp __P((struct mount *, struct fid *, struct vnode **)); int ffs_flushfiles __P((struct mount *, int, struct proc *)); void ffs_fragacct __P((struct fs *, int, int32_t [], int)); -int ffs_freefile __P(( struct vnode *, ino_t, int )); +int ffs_freefile __P((struct inode *, ino_t, int )); int ffs_isblock __P((struct fs *, u_char *, ufs_daddr_t)); int ffs_isfreeblock __P((struct fs *, unsigned char *, ufs_daddr_t)); int ffs_mountfs __P((struct vnode *, struct mount *, struct proc *, diff --git a/sys/ufs/ffs/ffs_softdep.c b/sys/ufs/ffs/ffs_softdep.c index 3c4ed20..e8399a6 100644 --- a/sys/ufs/ffs/ffs_softdep.c +++ b/sys/ufs/ffs/ffs_softdep.c @@ -171,7 +171,7 @@ static void free_allocdirect __P((struct allocdirectlst *, struct allocdirect *, int)); static int check_inode_unwritten __P((struct inodedep *)); static int free_inodedep __P((struct inodedep *)); -static void handle_workitem_freeblocks __P((struct freeblks *)); +static void handle_workitem_freeblocks __P((struct freeblks *, int)); static void merge_inode_lists __P((struct inodedep *)); static void setup_allocindir_phase2 __P((struct buf *, struct inode *, struct allocindir *)); @@ -684,7 +684,7 @@ process_worklist_item(matchmnt, flags) "process_worklist_item"); if (mp == matchmnt) matchcnt += 1; - handle_workitem_freeblocks(WK_FREEBLKS(wk)); + handle_workitem_freeblocks(WK_FREEBLKS(wk), flags & LK_NOWAIT); break; case D_FREEFRAG: @@ -1113,7 +1113,7 @@ softdep_mount(devvp, mp, fs, cred) } #ifdef DEBUG if (bcmp(&cstotal, &fs->fs_cstotal, sizeof cstotal)) - printf("ffs_mountfs: superblock updated for soft updates\n"); + printf("%s: superblock summary recomputed\n", fs->fs_fsmnt); #endif bcopy(&cstotal, &fs->fs_cstotal, sizeof cstotal); return (0); @@ -1819,7 +1819,7 @@ softdep_setup_freeblocks(ip, length) * the dependencies. */ if (!delay) - handle_workitem_freeblocks(freeblks); + handle_workitem_freeblocks(freeblks, 0); } /* @@ -2082,10 +2082,12 @@ free_inodedep(inodedep) * performed in this function. */ static void -handle_workitem_freeblocks(freeblks) +handle_workitem_freeblocks(freeblks, flags) struct freeblks *freeblks; + int flags; { - struct inode tip; + struct inode tip, *ip; + struct vnode *vp; ufs_daddr_t bn; struct fs *fs; int i, level, bsize; @@ -2130,13 +2132,27 @@ handle_workitem_freeblocks(freeblks) ffs_blkfree(&tip, bn, bsize); blocksreleased += btodb(bsize); } + /* + * If we still have not finished background cleanup, then check + * to see if the block count needs to be adjusted. + */ + if (freeblks->fb_chkcnt != blocksreleased && + (fs->fs_flags & FS_UNCLEAN) != 0 && (flags & LK_NOWAIT) == 0 && + VFS_VGET(freeblks->fb_mnt, freeblks->fb_previousinum, &vp) == 0) { + ip = VTOI(vp); + ip->i_blocks += freeblks->fb_chkcnt - blocksreleased; + ip->i_flag |= IN_CHANGE; + vput(vp); + } #ifdef DIAGNOSTIC - if (freeblks->fb_chkcnt != blocksreleased) + if (freeblks->fb_chkcnt != blocksreleased && + ((fs->fs_flags & FS_UNCLEAN) == 0 || (flags & LK_NOWAIT) != 0)) printf("handle_workitem_freeblocks: block count"); if (allerror) softdep_error("handle_workitem_freeblks", allerror); #endif /* DIAGNOSTIC */ + WORKITEM_FREE(freeblks, D_FREEBLKS); } @@ -2876,7 +2892,6 @@ handle_workitem_freefile(freefile) struct freefile *freefile; { struct fs *fs; - struct vnode vp; struct inode tip; struct inodedep *idp; int error; @@ -2892,9 +2907,7 @@ handle_workitem_freefile(freefile) tip.i_devvp = freefile->fx_devvp; tip.i_dev = freefile->fx_devvp->v_rdev; tip.i_fs = fs; - tip.i_vnode = &vp; - vp.v_data = &tip; - if ((error = ffs_freefile(&vp, freefile->fx_oldinum, freefile->fx_mode)) != 0) + if ((error = ffs_freefile(&tip, freefile->fx_oldinum, freefile->fx_mode)) != 0) softdep_error("handle_workitem_freefile", error); WORKITEM_FREE(freefile, D_FREEFILE); } diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c index bac00b9..9803a22 100644 --- a/sys/ufs/ffs/ffs_vfsops.c +++ b/sys/ufs/ffs/ffs_vfsops.c @@ -223,7 +223,8 @@ ffs_mount(mp, path, data, ndp, p) fs->fs_flags &= ~FS_UNCLEAN; if (fs->fs_clean == 0) { fs->fs_flags |= FS_UNCLEAN; - if (mp->mnt_flag & MNT_FORCE) { + if ((mp->mnt_flag & MNT_FORCE) || + (fs->fs_flags & FS_DOSOFTDEP)) { printf("WARNING: %s was not %s\n", fs->fs_fsmnt, "properly dismounted"); } else { @@ -584,7 +585,8 @@ ffs_mountfs(devvp, mp, p, malloctype) fs->fs_flags &= ~FS_UNCLEAN; if (fs->fs_clean == 0) { fs->fs_flags |= FS_UNCLEAN; - if (ronly || (mp->mnt_flag & MNT_FORCE)) { + if (ronly || (mp->mnt_flag & MNT_FORCE) || + (fs->fs_flags & FS_DOSOFTDEP)) { printf( "WARNING: %s was not properly dismounted\n", fs->fs_fsmnt); diff --git a/sys/ufs/ffs/fs.h b/sys/ufs/ffs/fs.h index 53d497c..4083c14 100644 --- a/sys/ufs/ffs/fs.h +++ b/sys/ufs/ffs/fs.h @@ -166,6 +166,28 @@ #define BLK_SNAP ((ufs_daddr_t)(2)) /* + * Sysctl values for the fast filesystem. + */ +#define FFS_ADJ_REFCNT 1 /* adjust inode reference count */ +#define FFS_ADJ_BLKCNT 2 /* adjust inode used block count */ +#define FFS_BLK_FREE 3 /* free range of blocks in map */ +#define FFS_DIR_FREE 4 /* free specified dir inodes in map */ +#define FFS_FILE_FREE 5 /* free specified file inodes in map */ +#define FFS_SET_FLAGS 6 /* set filesystem flags */ +#define FFS_MAXID 7 /* number of valid ffs ids */ + +/* + * Command structure passed in to the filesystem to adjust filesystem values. + */ +#define FFS_CMD_VERSION 0x05181979 /* version ID */ +struct fsck_cmd { + int version; /* version of command structure */ + int handle; /* reference to filesystem to be changed */ + off_t value; /* inode or block number to be affected */ + long size; /* amount or range to be adjusted */ +}; + +/* * Per cylinder group information; summarized in blocks allocated * from first cylinder group data blocks. These blocks have to be * read in from fs_csaddr (size fs_cssize) in addition to the diff --git a/sys/ufs/ufs/ufs_inode.c b/sys/ufs/ufs/ufs_inode.c index 96f0649..d4ed8b3 100644 --- a/sys/ufs/ufs/ufs_inode.c +++ b/sys/ufs/ufs/ufs_inode.c @@ -87,10 +87,18 @@ ufs_inactive(ap) ufs_extattr_vnode_inactive(ap->a_vp, ap->a_p); #endif error = UFS_TRUNCATE(vp, (off_t)0, 0, NOCRED, p); + /* + * 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(). + */ ip->i_rdev = 0; mode = ip->i_mode; ip->i_mode = 0; ip->i_flag |= IN_CHANGE | IN_UPDATE; + if (DOINGSOFTDEP(vp)) + softdep_change_linkcnt(ip); UFS_VFREE(vp, ip->i_number, mode); } if (ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) { |