summaryrefslogtreecommitdiffstats
path: root/sys/ufs/ffs
diff options
context:
space:
mode:
authormckusick <mckusick@FreeBSD.org>2001-03-21 04:09:01 +0000
committermckusick <mckusick@FreeBSD.org>2001-03-21 04:09:01 +0000
commit69603157dedb4757ce880ddfc5157558441cf985 (patch)
tree6378d524f9c6f5c077153375f319c0f93a7682cf /sys/ufs/ffs
parent39275d892cc0291136bb750a2f1191137371de42 (diff)
downloadFreeBSD-src-69603157dedb4757ce880ddfc5157558441cf985.zip
FreeBSD-src-69603157dedb4757ce880ddfc5157558441cf985.tar.gz
Add kernel support for running fsck on active filesystems.
Diffstat (limited to 'sys/ufs/ffs')
-rw-r--r--sys/ufs/ffs/ffs_alloc.c233
-rw-r--r--sys/ufs/ffs/ffs_extern.h17
-rw-r--r--sys/ufs/ffs/ffs_softdep.c35
-rw-r--r--sys/ufs/ffs/ffs_vfsops.c6
-rw-r--r--sys/ufs/ffs/fs.h22
5 files changed, 270 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
OpenPOWER on IntegriCloud