diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c index c6217434bd84..c8989ac1117e 100644 --- a/sys/kern/vfs_subr.c +++ b/sys/kern/vfs_subr.c @@ -2530,6 +2530,8 @@ vn_printf(struct vnode *vp, const char *fmt, ...) strcat(buf, "|VV_TEXT"); if (vp->v_vflag & VV_SYSTEM) strcat(buf, "|VV_SYSTEM"); + if (vp->v_vflag & VV_DELETED) + strcat(buf, "|VV_DELETED"); if (vp->v_iflag & VI_DOOMED) strcat(buf, "|VI_DOOMED"); if (vp->v_iflag & VI_FREE) diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h index ed027a7c37be..2bd0971a635f 100644 --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -253,6 +253,7 @@ struct xvnode { #define VV_SYSTEM 0x0080 /* vnode being used by kernel */ #define VV_PROCDEP 0x0100 /* vnode is process dependent */ #define VV_NOKNOTE 0x0200 /* don't activate knotes on this vnode */ +#define VV_DELETED 0x0400 /* should be removed */ /* * Vnode attributes. A field value of VNOVAL represents a field whose value diff --git a/sys/ufs/ffs/ffs_extern.h b/sys/ufs/ffs/ffs_extern.h index 21704bd9e142..98f0f919e6c7 100644 --- a/sys/ufs/ffs/ffs_extern.h +++ b/sys/ufs/ffs/ffs_extern.h @@ -72,6 +72,7 @@ int ffs_mountroot(void); int ffs_reallocblks(struct vop_reallocblks_args *); int ffs_realloccg(struct inode *, ufs2_daddr_t, ufs2_daddr_t, ufs2_daddr_t, int, int, struct ucred *, struct buf **); +int ffs_sbupdate(struct ufsmount *, int, int); void ffs_setblock(struct fs *, u_char *, ufs1_daddr_t); int ffs_snapblkfree(struct fs *, struct vnode *, ufs2_daddr_t, long, ino_t); void ffs_snapremove(struct vnode *vp); diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c index 05df8384e81d..b9ccf3a205a7 100644 --- a/sys/ufs/ffs/ffs_vfsops.c +++ b/sys/ufs/ffs/ffs_vfsops.c @@ -54,6 +54,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -71,7 +72,6 @@ __FBSDID("$FreeBSD$"); static uma_zone_t uma_inode, uma_ufs1, uma_ufs2; -static int ffs_sbupdate(struct ufsmount *, int, int); static int ffs_reload(struct mount *, struct thread *); static int ffs_mountfs(struct vnode *, struct mount *, struct thread *); static void ffs_oldfscompat_read(struct fs *, struct ufsmount *, @@ -696,6 +696,35 @@ ffs_mountfs(devvp, mp, td) fs->fs_pendingblocks = 0; fs->fs_pendinginodes = 0; } + if ((fs->fs_flags & FS_GJOURNAL) != 0) { +#ifdef UFS_GJOURNAL + /* + * Get journal provider name. + */ + size = 1024; + mp->mnt_gjprovider = malloc(size, M_UFSMNT, M_WAITOK); + if (g_io_getattr("GJOURNAL::provider", cp, &size, + mp->mnt_gjprovider) == 0) { + mp->mnt_gjprovider = realloc(mp->mnt_gjprovider, size, + M_UFSMNT, M_WAITOK); + MNT_ILOCK(mp); + mp->mnt_flag |= MNT_GJOURNAL; + MNT_IUNLOCK(mp); + } else { + printf( +"WARNING: %s: GJOURNAL flag on fs but no gjournal provider below\n", + mp->mnt_stat.f_mntonname); + free(mp->mnt_gjprovider, M_UFSMNT); + mp->mnt_gjprovider = NULL; + } +#else + printf( +"WARNING: %s: GJOURNAL flag on fs but no UFS_GJOURNAL support\n", + mp->mnt_stat.f_mntonname); +#endif + } else { + mp->mnt_gjprovider = NULL; + } ump = malloc(sizeof *ump, M_UFSMNT, M_WAITOK | M_ZERO); ump->um_cp = cp; ump->um_bo = &devvp->v_bufobj; @@ -871,6 +900,10 @@ out: } if (ump) { mtx_destroy(UFS_MTX(ump)); + if (mp->mnt_gjprovider != NULL) { + free(mp->mnt_gjprovider, M_UFSMNT); + mp->mnt_gjprovider = NULL; + } free(ump->um_fs, M_UFSMNT); free(ump, M_UFSMNT); mp->mnt_data = (qaddr_t)0; @@ -1030,6 +1063,10 @@ ffs_unmount(mp, mntflags, td) PICKUP_GIANT(); vrele(ump->um_devvp); mtx_destroy(UFS_MTX(ump)); + if (mp->mnt_gjprovider != NULL) { + free(mp->mnt_gjprovider, M_UFSMNT); + mp->mnt_gjprovider = NULL; + } free(fs->fs_csp, M_UFSMNT); free(fs, M_UFSMNT); free(ump, M_UFSMNT); @@ -1514,7 +1551,7 @@ ffs_uninit(vfsp) /* * Write a superblock and associated information back to disk. */ -static int +int ffs_sbupdate(mp, waitfor, suspended) struct ufsmount *mp; int waitfor; diff --git a/sys/ufs/ffs/fs.h b/sys/ufs/ffs/fs.h index 572996ec5935..9aaf0409835c 100644 --- a/sys/ufs/ffs/fs.h +++ b/sys/ufs/ffs/fs.h @@ -323,7 +323,8 @@ struct fs { u_int *fs_active; /* (u) used by snapshots to track fs */ int32_t fs_old_cpc; /* cyl per cycle in postbl */ int32_t fs_maxbsize; /* maximum blocking factor permitted */ - int64_t fs_sparecon64[17]; /* old rotation block list head */ + int64_t fs_unrefs; /* number of unreferenced inodes */ + int64_t fs_sparecon64[16]; /* old rotation block list head */ int64_t fs_sblockloc; /* byte offset of standard superblock */ struct csum_total fs_cstotal; /* (u) cylinder summary information */ ufs_time_t fs_time; /* last time written */ @@ -406,6 +407,7 @@ CTASSERT(sizeof(struct fs) == 1376); #define FS_INDEXDIRS 0x08 /* kernel supports indexed directories */ #define FS_ACLS 0x10 /* file system has ACLs enabled */ #define FS_MULTILABEL 0x20 /* file system is MAC multi-label */ +#define FS_GJOURNAL 0x40 /* gjournaled file system */ #define FS_FLAGS_UPDATED 0x80 /* flags have been moved to new location */ /* @@ -475,7 +477,8 @@ struct cg { int32_t cg_nclusterblks; /* number of clusters this cg */ int32_t cg_niblk; /* number of inode blocks this cg */ int32_t cg_initediblk; /* last initialized inode */ - int32_t cg_sparecon32[3]; /* reserved for future use */ + int32_t cg_unrefs; /* number of unreferenced inodes */ + int32_t cg_sparecon32[2]; /* reserved for future use */ ufs_time_t cg_time; /* time last written */ int64_t cg_sparecon64[3]; /* reserved for future use */ u_int8_t cg_space[1]; /* space for cylinder group maps */ diff --git a/sys/ufs/ufs/gjournal.h b/sys/ufs/ufs/gjournal.h new file mode 100644 index 000000000000..f4036b5dfa7a --- /dev/null +++ b/sys/ufs/ufs/gjournal.h @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 2005-2006 Pawel Jakub Dawidek + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _UFS_UFS_GJOURNAL_H_ +#define _UFS_UFS_GJOURNAL_H_ + +/* + * GEOM journal function prototypes. + */ +void ufs_gjournal_orphan(struct vnode *fvp); +void ufs_gjournal_close(struct vnode *vp); +#endif /* !_UFS_UFS_GJOURNAL_H_ */ diff --git a/sys/ufs/ufs/ufs_gjournal.c b/sys/ufs/ufs/ufs_gjournal.c new file mode 100644 index 000000000000..7b98a6d7e4d3 --- /dev/null +++ b/sys/ufs/ufs/ufs_gjournal.c @@ -0,0 +1,150 @@ +/*- + * Copyright (c) 2005-2006 Pawel Jakub Dawidek + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_ufs.h" + +#ifdef UFS_GJOURNAL + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +/* + * Change the number of unreferenced inodes. + */ +static int +ufs_gjournal_modref(struct vnode *vp, int count) +{ + struct cg *cgp; + struct buf *bp; + ufs2_daddr_t cgbno; + int error, cg; + struct cdev *dev; + struct inode *ip; + struct ufsmount *ump; + struct fs *fs; + struct vnode *devvp; + ino_t ino; + + ip = VTOI(vp); + ump = ip->i_ump; + fs = ip->i_fs; + devvp = ip->i_devvp; + ino = ip->i_number; + + cg = ino_to_cg(fs, ino); + if (devvp->v_type != VCHR) { + /* devvp is a snapshot */ + dev = VTOI(devvp)->i_devvp->v_rdev; + cgbno = fragstoblks(fs, cgtod(fs, cg)); + } else { + /* devvp is a normal disk device */ + dev = devvp->v_rdev; + cgbno = fsbtodb(fs, cgtod(fs, cg)); + } + if ((u_int)ino >= fs->fs_ipg * fs->fs_ncg) + panic("ffs_freefile: range: dev = %s, ino = %lu, fs = %s", + devtoname(dev), (u_long)ino, fs->fs_fsmnt); + if ((error = bread(devvp, cgbno, (int)fs->fs_cgsize, NOCRED, &bp))) { + brelse(bp); + return (error); + } + cgp = (struct cg *)bp->b_data; + if (!cg_chkmagic(cgp)) { + brelse(bp); + return (0); + } + bp->b_xflags |= BX_BKGRDWRITE; + cgp->cg_unrefs += count; + UFS_LOCK(ump); + fs->fs_unrefs += count; + fs->fs_fmod = 1; + ACTIVECLEAR(fs, cg); + UFS_UNLOCK(ump); + bdwrite(bp); + return (0); +} + +void +ufs_gjournal_orphan(struct vnode *fvp) +{ + struct mount *mp; + struct inode *ip; + + mp = fvp->v_mount; + if (mp->mnt_gjprovider == NULL) + return; + VI_LOCK(fvp); + if (fvp->v_usecount < 2 || (fvp->v_vflag & VV_DELETED)) { + VI_UNLOCK(fvp); + return; + } + ip = VTOI(fvp); + if ((fvp->v_type == VDIR && ip->i_nlink > 2) || + (fvp->v_type != VDIR && ip->i_nlink > 1)) { + VI_UNLOCK(fvp); + return; + } + fvp->v_vflag |= VV_DELETED; + VI_UNLOCK(fvp); + + ufs_gjournal_modref(fvp, 1); +} + +void +ufs_gjournal_close(struct vnode *vp) +{ + struct mount *mp; + struct inode *ip; + + mp = vp->v_mount; + if (mp->mnt_gjprovider == NULL) + return; + if (!(vp->v_vflag & VV_DELETED)) + return; + ip = VTOI(vp); + if (ip->i_nlink > 0) + return; + ufs_gjournal_modref(vp, -1); +} + +#endif /* UFS_GJOURNAL */ diff --git a/sys/ufs/ufs/ufs_inode.c b/sys/ufs/ufs/ufs_inode.c index 638e23b95a68..86b1608b4033 100644 --- a/sys/ufs/ufs/ufs_inode.c +++ b/sys/ufs/ufs/ufs_inode.c @@ -57,6 +57,9 @@ __FBSDID("$FreeBSD$"); #include #include #endif +#ifdef UFS_GJOURNAL +#include +#endif /* * Last reference to an inode. If necessary, write or delete it. @@ -83,6 +86,9 @@ ufs_inactive(ap) */ if (ip->i_mode == 0) goto out; +#ifdef UFS_GJOURNAL + ufs_gjournal_close(vp); +#endif if ((ip->i_effnlink == 0 && DOINGSOFTDEP(vp)) || (ip->i_nlink <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0)) { diff --git a/sys/ufs/ufs/ufs_vnops.c b/sys/ufs/ufs/ufs_vnops.c index 1495937276b9..4f964491cdf5 100644 --- a/sys/ufs/ufs/ufs_vnops.c +++ b/sys/ufs/ufs/ufs_vnops.c @@ -83,6 +83,9 @@ __FBSDID("$FreeBSD$"); #ifdef UFS_DIRHASH #include #endif +#ifdef UFS_GJOURNAL +#include +#endif #include @@ -803,6 +806,9 @@ ufs_remove(ap) error = EPERM; goto out; } +#ifdef UFS_GJOURNAL + ufs_gjournal_orphan(vp); +#endif error = ufs_dirremove(dvp, ip, ap->a_cnp->cn_flags, 0); if (ip->i_nlink <= 0) vp->v_vflag |= VV_NOSYNC; @@ -1709,6 +1715,9 @@ ufs_rmdir(ap) error = EINVAL; goto out; } +#ifdef UFS_GJOURNAL + ufs_gjournal_orphan(vp); +#endif /* * Delete reference to directory before purging * inode. If we crash in between, the directory