lfs_segment.c

来自「早期freebsd实现」· C语言 代码 · 共 1,112 行 · 第 1/2 页

C
1,112
字号
/* * Copyright (c) 1991, 1993 *	The Regents of the University of California.  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. * 3. All advertising materials mentioning features or use of this software *    must display the following acknowledgement: *	This product includes software developed by the University of *	California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors *    may be used to endorse or promote products derived from this software *    without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * *	@(#)lfs_segment.c	8.5 (Berkeley) 1/4/94 */#include <sys/param.h>#include <sys/systm.h>#include <sys/namei.h>#include <sys/kernel.h>#include <sys/resourcevar.h>#include <sys/file.h>#include <sys/stat.h>#include <sys/buf.h>#include <sys/proc.h>#include <sys/conf.h>#include <sys/vnode.h>#include <sys/malloc.h>#include <sys/mount.h>#include <miscfs/specfs/specdev.h>#include <miscfs/fifofs/fifo.h>#include <ufs/ufs/quota.h>#include <ufs/ufs/inode.h>#include <ufs/ufs/dir.h>#include <ufs/ufs/ufsmount.h>#include <ufs/ufs/ufs_extern.h>#include <ufs/lfs/lfs.h>#include <ufs/lfs/lfs_extern.h>extern int count_lock_queue __P((void));#define MAX_ACTIVE	10/* * Determine if it's OK to start a partial in this segment, or if we need * to go on to a new segment. */#define	LFS_PARTIAL_FITS(fs) \	((fs)->lfs_dbpseg - ((fs)->lfs_offset - (fs)->lfs_curseg) > \	1 << (fs)->lfs_fsbtodb)void	 lfs_callback __P((struct buf *));void	 lfs_gather __P((struct lfs *, struct segment *,	     struct vnode *, int (*) __P((struct lfs *, struct buf *))));int	 lfs_gatherblock __P((struct segment *, struct buf *, int *));void	 lfs_iset __P((struct inode *, daddr_t, time_t));int	 lfs_match_data __P((struct lfs *, struct buf *));int	 lfs_match_dindir __P((struct lfs *, struct buf *));int	 lfs_match_indir __P((struct lfs *, struct buf *));int	 lfs_match_tindir __P((struct lfs *, struct buf *));void	 lfs_newseg __P((struct lfs *));void	 lfs_shellsort __P((struct buf **, daddr_t *, register int));void	 lfs_supercallback __P((struct buf *));void	 lfs_updatemeta __P((struct segment *));int	 lfs_vref __P((struct vnode *));void	 lfs_vunref __P((struct vnode *));void	 lfs_writefile __P((struct lfs *, struct segment *, struct vnode *));int	 lfs_writeinode __P((struct lfs *, struct segment *, struct inode *));int	 lfs_writeseg __P((struct lfs *, struct segment *));void	 lfs_writesuper __P((struct lfs *));void	 lfs_writevnodes __P((struct lfs *fs, struct mount *mp,	    struct segment *sp, int dirops));int	lfs_allclean_wakeup;		/* Cleaner wakeup address. *//* Statistics Counters */#define DOSTATSstruct lfs_stats lfs_stats;/* op values to lfs_writevnodes */#define	VN_REG	0#define	VN_DIROP	1#define	VN_EMPTY	2/* * Ifile and meta data blocks are not marked busy, so segment writes MUST be * single threaded.  Currently, there are two paths into lfs_segwrite, sync() * and getnewbuf().  They both mark the file system busy.  Lfs_vflush() * explicitly marks the file system busy.  So lfs_segwrite is safe.  I think. */intlfs_vflush(vp)	struct vnode *vp;{	struct inode *ip;	struct lfs *fs;	struct segment *sp;	fs = VFSTOUFS(vp->v_mount)->um_lfs;	if (fs->lfs_nactive > MAX_ACTIVE)		return(lfs_segwrite(vp->v_mount, SEGM_SYNC|SEGM_CKP));	lfs_seglock(fs, SEGM_SYNC);	sp = fs->lfs_sp;	ip = VTOI(vp);	if (vp->v_dirtyblkhd.lh_first == NULL)		lfs_writevnodes(fs, vp->v_mount, sp, VN_EMPTY);	do {		do {			if (vp->v_dirtyblkhd.lh_first != NULL)				lfs_writefile(fs, sp, vp);		} while (lfs_writeinode(fs, sp, ip));	} while (lfs_writeseg(fs, sp) && ip->i_number == LFS_IFILE_INUM);#ifdef DOSTATS	++lfs_stats.nwrites;	if (sp->seg_flags & SEGM_SYNC)		++lfs_stats.nsync_writes;	if (sp->seg_flags & SEGM_CKP)		++lfs_stats.ncheckpoints;#endif	lfs_segunlock(fs);	return (0);}voidlfs_writevnodes(fs, mp, sp, op)	struct lfs *fs;	struct mount *mp;	struct segment *sp;	int op;{	struct inode *ip;	struct vnode *vp;loop:	for (vp = mp->mnt_vnodelist.lh_first;	     vp != NULL;	     vp = vp->v_mntvnodes.le_next) {		/*		 * If the vnode that we are about to sync is no longer		 * associated with this mount point, start over.		 */		if (vp->v_mount != mp)			goto loop;		/* XXX ignore dirops for now		if (op == VN_DIROP && !(vp->v_flag & VDIROP) ||		    op != VN_DIROP && (vp->v_flag & VDIROP))			continue;		*/		if (op == VN_EMPTY && vp->v_dirtyblkhd.lh_first)			continue;		if (vp->v_type == VNON)			continue;		if (lfs_vref(vp))			continue;		/*		 * Write the inode/file if dirty and it's not the		 * the IFILE.		 */		ip = VTOI(vp);		if ((ip->i_flag &		    (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE) ||		    vp->v_dirtyblkhd.lh_first != NULL) &&		    ip->i_number != LFS_IFILE_INUM) {			if (vp->v_dirtyblkhd.lh_first != NULL)				lfs_writefile(fs, sp, vp);			(void) lfs_writeinode(fs, sp, ip);		}		vp->v_flag &= ~VDIROP;		lfs_vunref(vp);	}}intlfs_segwrite(mp, flags)	struct mount *mp;	int flags;			/* Do a checkpoint. */{	struct buf *bp;	struct inode *ip;	struct lfs *fs;	struct segment *sp;	struct vnode *vp;	SEGUSE *segusep;	daddr_t ibno;	CLEANERINFO *cip;	int clean, do_ckp, error, i;	fs = VFSTOUFS(mp)->um_lfs; 	/* 	 * If we have fewer than 2 clean segments, wait until cleaner	 * writes. 	 */	do {		LFS_CLEANERINFO(cip, fs, bp);		clean = cip->clean;		brelse(bp);		if (clean <= 2) {			printf ("segs clean: %d\n", clean);			wakeup(&lfs_allclean_wakeup);			if (error = tsleep(&fs->lfs_avail, PRIBIO + 1,			    "lfs writer", 0))				return (error);		}	} while (clean <= 2 );	/*	 * Allocate a segment structure and enough space to hold pointers to	 * the maximum possible number of buffers which can be described in a	 * single summary block.	 */	do_ckp = flags & SEGM_CKP || fs->lfs_nactive > MAX_ACTIVE;	lfs_seglock(fs, flags | (do_ckp ? SEGM_CKP : 0));	sp = fs->lfs_sp;	lfs_writevnodes(fs, mp, sp, VN_REG);	/* XXX ignore ordering of dirops for now */	/* XXX	fs->lfs_writer = 1;	if (fs->lfs_dirops && (error =	    tsleep(&fs->lfs_writer, PRIBIO + 1, "lfs writer", 0))) {		free(sp->bpp, M_SEGMENT);		free(sp, M_SEGMENT); 		fs->lfs_writer = 0;		return (error);	}	lfs_writevnodes(fs, mp, sp, VN_DIROP);	*/	/*	 * If we are doing a checkpoint, mark everything since the	 * last checkpoint as no longer ACTIVE.	 */	if (do_ckp)		for (ibno = fs->lfs_cleansz + fs->lfs_segtabsz;		     --ibno >= fs->lfs_cleansz; ) {			if (bread(fs->lfs_ivnode, ibno, fs->lfs_bsize,			    NOCRED, &bp))				panic("lfs: ifile read");			segusep = (SEGUSE *)bp->b_data;			for (i = fs->lfs_sepb; i--; segusep++)				segusep->su_flags &= ~SEGUSE_ACTIVE;							error = VOP_BWRITE(bp);		}	if (do_ckp || fs->lfs_doifile) {redo:		vp = fs->lfs_ivnode;		while (vget(vp, 1));		ip = VTOI(vp);		if (vp->v_dirtyblkhd.lh_first != NULL)			lfs_writefile(fs, sp, vp);		(void)lfs_writeinode(fs, sp, ip);		vput(vp);		if (lfs_writeseg(fs, sp) && do_ckp)			goto redo;	} else		(void) lfs_writeseg(fs, sp);	/*	 * If the I/O count is non-zero, sleep until it reaches zero.  At the	 * moment, the user's process hangs around so we can sleep.	 */	/* XXX ignore dirops for now	fs->lfs_writer = 0;	fs->lfs_doifile = 0;	wakeup(&fs->lfs_dirops);	*/#ifdef DOSTATS	++lfs_stats.nwrites;	if (sp->seg_flags & SEGM_SYNC)		++lfs_stats.nsync_writes;	if (sp->seg_flags & SEGM_CKP)		++lfs_stats.ncheckpoints;#endif	lfs_segunlock(fs);	return (0);}/* * Write the dirty blocks associated with a vnode. */voidlfs_writefile(fs, sp, vp)	struct lfs *fs;	struct segment *sp;	struct vnode *vp;{	struct buf *bp;	struct finfo *fip;	IFILE *ifp;	if (sp->seg_bytes_left < fs->lfs_bsize ||	    sp->sum_bytes_left < sizeof(struct finfo))		(void) lfs_writeseg(fs, sp);	sp->sum_bytes_left -= sizeof(struct finfo) - sizeof(daddr_t);	++((SEGSUM *)(sp->segsum))->ss_nfinfo;	fip = sp->fip;	fip->fi_nblocks = 0;	fip->fi_ino = VTOI(vp)->i_number;	LFS_IENTRY(ifp, fs, fip->fi_ino, bp);	fip->fi_version = ifp->if_version;	brelse(bp);	/*	 * It may not be necessary to write the meta-data blocks at this point,	 * as the roll-forward recovery code should be able to reconstruct the	 * list.	 */	lfs_gather(fs, sp, vp, lfs_match_data);	lfs_gather(fs, sp, vp, lfs_match_indir);	lfs_gather(fs, sp, vp, lfs_match_dindir);#ifdef TRIPLE	lfs_gather(fs, sp, vp, lfs_match_tindir);#endif	fip = sp->fip;	if (fip->fi_nblocks != 0) {		sp->fip =		    (struct finfo *)((caddr_t)fip + sizeof(struct finfo) +		    sizeof(daddr_t) * (fip->fi_nblocks - 1));		sp->start_lbp = &sp->fip->fi_blocks[0];	} else {		sp->sum_bytes_left += sizeof(struct finfo) - sizeof(daddr_t);		--((SEGSUM *)(sp->segsum))->ss_nfinfo;	}}intlfs_writeinode(fs, sp, ip)	struct lfs *fs;	struct segment *sp;	struct inode *ip;{	struct buf *bp, *ibp;	IFILE *ifp;	SEGUSE *sup;	daddr_t daddr;	ino_t ino;	int error, i, ndx;	int redo_ifile = 0;	if (!(ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)))		return(0);	/* Allocate a new inode block if necessary. */	if (sp->ibp == NULL) {		/* Allocate a new segment if necessary. */		if (sp->seg_bytes_left < fs->lfs_bsize ||		    sp->sum_bytes_left < sizeof(daddr_t))			(void) lfs_writeseg(fs, sp);		/* Get next inode block. */		daddr = fs->lfs_offset;		fs->lfs_offset += fsbtodb(fs, 1);		sp->ibp = *sp->cbpp++ =		    lfs_newbuf(VTOI(fs->lfs_ivnode)->i_devvp, daddr,		    fs->lfs_bsize);		/* Zero out inode numbers */		for (i = 0; i < INOPB(fs); ++i)			((struct dinode *)sp->ibp->b_data)[i].di_inumber = 0;		++sp->start_bpp;		fs->lfs_avail -= fsbtodb(fs, 1);		/* Set remaining space counters. */		sp->seg_bytes_left -= fs->lfs_bsize;		sp->sum_bytes_left -= sizeof(daddr_t);		ndx = LFS_SUMMARY_SIZE / sizeof(daddr_t) -		    sp->ninodes / INOPB(fs) - 1;		((daddr_t *)(sp->segsum))[ndx] = daddr;	}	/* Update the inode times and copy the inode onto the inode page. */	if (ip->i_flag & IN_MODIFIED)		--fs->lfs_uinodes;	ITIMES(ip, &time, &time);	ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);	bp = sp->ibp;	((struct dinode *)bp->b_data)[sp->ninodes % INOPB(fs)] = ip->i_din;	/* Increment inode count in segment summary block. */	++((SEGSUM *)(sp->segsum))->ss_ninos;	/* If this page is full, set flag to allocate a new page. */	if (++sp->ninodes % INOPB(fs) == 0)		sp->ibp = NULL;	/*	 * If updating the ifile, update the super-block.  Update the disk	 * address and access times for this inode in the ifile.	 */	ino = ip->i_number;	if (ino == LFS_IFILE_INUM) {		daddr = fs->lfs_idaddr;		fs->lfs_idaddr = bp->b_blkno;	} else {		LFS_IENTRY(ifp, fs, ino, ibp);		daddr = ifp->if_daddr;		ifp->if_daddr = bp->b_blkno;		error = VOP_BWRITE(ibp);	}	/*	 * No need to update segment usage if there was no former inode address	 * or if the last inode address is in the current partial segment.	 */	if (daddr != LFS_UNUSED_DADDR && 	    !(daddr >= fs->lfs_lastpseg && daddr <= bp->b_blkno)) {		LFS_SEGENTRY(sup, fs, datosn(fs, daddr), bp);#ifdef DIAGNOSTIC		if (sup->su_nbytes < sizeof(struct dinode)) {			/* XXX -- Change to a panic. */			printf("lfs: negative bytes (segment %d)\n",			    datosn(fs, daddr));			panic("negative bytes");		}#endif		sup->su_nbytes -= sizeof(struct dinode);		redo_ifile =		    (ino == LFS_IFILE_INUM && !(bp->b_flags & B_GATHERED));		error = VOP_BWRITE(bp);	}	return (redo_ifile);}intlfs_gatherblock(sp, bp, sptr)	struct segment *sp;	struct buf *bp;	int *sptr;{	struct lfs *fs;	int version;	/*	 * If full, finish this segment.  We may be doing I/O, so	 * release and reacquire the splbio().	 */#ifdef DIAGNOSTIC	if (sp->vp == NULL)		panic ("lfs_gatherblock: Null vp in segment");#endif	fs = sp->fs;	if (sp->sum_bytes_left < sizeof(daddr_t) ||	    sp->seg_bytes_left < fs->lfs_bsize) {		if (sptr)			splx(*sptr);		lfs_updatemeta(sp);		version = sp->fip->fi_version;		(void) lfs_writeseg(fs, sp);		sp->fip->fi_version = version;		sp->fip->fi_ino = VTOI(sp->vp)->i_number;		/* Add the current file to the segment summary. */		++((SEGSUM *)(sp->segsum))->ss_nfinfo;		sp->sum_bytes_left -= 		    sizeof(struct finfo) - sizeof(daddr_t);		if (sptr)			*sptr = splbio();		return(1);	}	/* Insert into the buffer list, update the FINFO block. */	bp->b_flags |= B_GATHERED;	*sp->cbpp++ = bp;	sp->fip->fi_blocks[sp->fip->fi_nblocks++] = bp->b_lblkno;	sp->sum_bytes_left -= sizeof(daddr_t);	sp->seg_bytes_left -= fs->lfs_bsize;	return(0);}voidlfs_gather(fs, sp, vp, match)	struct lfs *fs;	struct segment *sp;	struct vnode *vp;	int (*match) __P((struct lfs *, struct buf *));{	struct buf *bp;	int s;	sp->vp = vp;	s = splbio();loop:	for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = bp->b_vnbufs.le_next) {		if (bp->b_flags & B_BUSY || !match(fs, bp) ||		    bp->b_flags & B_GATHERED)			continue;#ifdef DIAGNOSTIC		if (!(bp->b_flags & B_DELWRI))			panic("lfs_gather: bp not B_DELWRI");		if (!(bp->b_flags & B_LOCKED))			panic("lfs_gather: bp not B_LOCKED");#endif		if (lfs_gatherblock(sp, bp, &s))			goto loop;	}	splx(s);	lfs_updatemeta(sp);	sp->vp = NULL;}/* * Update the metadata that points to the blocks listed in the FINFO * array. */voidlfs_updatemeta(sp)	struct segment *sp;{	SEGUSE *sup;	struct buf *bp;	struct lfs *fs;	struct vnode *vp;	struct indir a[NIADDR + 2], *ap;	struct inode *ip;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?