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 + -
显示快捷键?