/*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * %sccs.include.redist.c% * * @(#)lfs_syscalls.c 7.13 (Berkeley) 06/23/92 */ #include #include #include #include #include #include #include #include #include #include #include #include /* * lfs_markv: * * This will mark inodes and blocks dirty, so they are written into the log. * It will block until all the blocks have been written. The segment create * time passed in the block_info and inode_info structures is used to decide * if the data is valid for each block (in case some process dirtied a block * or inode that is being cleaned between the determination that a block is * live and the lfs_markv call). * * 0 on success * -1/errno is return on error. */ int lfs_markv(p, uap, retval) struct proc *p; struct args { fsid_t fsid; /* file system */ BLOCK_INFO *blkiov; /* block array */ int blkcnt; /* count of block array entries */ INODE_INFO *inoiov; /* inode array */ int inocnt; /* count of inode array entries */ } *uap; int *retval; { USES_VOP_BMAP; USES_VOP_BWRITE; USES_VOP_VGET; BLOCK_INFO *blkp; IFILE *ifp; INODE_INFO *inop; struct buf *bp; struct inode *ip; struct lfs *fs; struct mount *mntp; struct vnode *vp; void *start; ino_t lastino; daddr_t daddr; u_long bsize; int cnt, error; #ifdef VERBOSE printf("lfs_markv\n"); #endif if (error = suser(p->p_ucred, &p->p_acflag)) return (error); if ((mntp = getvfs(&uap->fsid)) == NULL) return (EINVAL); cnt = uap->blkcnt; start = malloc(cnt * sizeof(BLOCK_INFO), M_SEGMENT, M_WAITOK); if (error = copyin(uap->blkiov, start, cnt * sizeof(BLOCK_INFO))) { free(start, M_SEGMENT); return (error); } /* * Mark blocks/inodes dirty. Note that errors are mostly ignored. If * we can't get the info, the block is probably not all that useful, * and hopefully subsequent calls from the cleaner will fix everything. */ fs = VFSTOUFS(mntp)->um_lfs; bsize = fs->lfs_bsize; for (lastino = LFS_UNUSED_INUM, blkp = start; cnt--; ++blkp) { /* * Get the IFILE entry (only once) and see if the file still * exists. */ if (lastino != blkp->bi_inode) { lastino = blkp->bi_inode; LFS_IENTRY(ifp, fs, blkp->bi_inode, bp); daddr = ifp->if_daddr; brelse(bp); if (daddr == LFS_UNUSED_DADDR) continue; } /* * Get the vnode/inode. If the inode modification time is * earlier than the segment in which the block was found then * they have to be valid, skip other checks. */ if (LFS_VGET(mntp, blkp->bi_inode, &vp)) continue; ip = VTOI(vp); /* * If modify time later than segment create time, see if the * block has been replaced. */ if (ip->i_mtime.ts_sec > blkp->bi_segcreate && (VOP_BMAP(vp, blkp->bi_lbn, NULL, &daddr) || daddr != blkp->bi_daddr)) { vput(vp); continue; } /* Get the block (from core or the cleaner) and write it. */ bp = getblk(vp, blkp->bi_lbn, bsize); vput(vp); if (!(bp->b_flags & B_CACHE) && (error = copyin(blkp->bi_bp, bp->b_un.b_addr, bsize))) { brelse(bp); free(start, M_SEGMENT); return (error); } VOP_BWRITE(bp); } free(start, M_SEGMENT); cnt = uap->inocnt; start = malloc(cnt * sizeof(INODE_INFO), M_SEGMENT, M_WAITOK); if (error = copyin(uap->inoiov, start, cnt * sizeof(INODE_INFO))) { free(start, M_SEGMENT); return (error); } for (inop = start; cnt--; ++inop) { LFS_IENTRY(ifp, fs, inop->ii_inode, bp); daddr = ifp->if_daddr; brelse(bp); if (daddr != inop->ii_daddr) continue; /* * XXX * This is grossly inefficient since the cleaner just handed * us a copy of the inode and we're going to have to seek * to get our own. The fix requires creating a version of * lfs_vget that takes the copy and uses it instead of reading * from disk, if it's not already in the cache. */ if (!LFS_VGET(mntp, inop->ii_inode, &vp)) { VTOI(vp)->i_flag |= IMOD; vput(vp); } } free(start, M_SEGMENT); return (lfs_segwrite(mntp, 1)); } /* * lfs_bmapv: * * This will fill in the current disk address for arrays of blocks. * * 0 on success * -1/errno is return on error. */ int lfs_bmapv(p, uap, retval) struct proc *p; struct args { fsid_t fsid; /* file system */ BLOCK_INFO *blkiov; /* block array */ int blkcnt; /* count of block array entries */ } *uap; int *retval; { USES_VOP_BMAP; USES_VOP_VGET; BLOCK_INFO *blkp; struct mount *mntp; struct vnode *vp; void *start; daddr_t daddr; int cnt, error, step; #ifdef VERBOSE printf("lfs_bmapv\n"); #endif if (error = suser(p->p_ucred, &p->p_acflag)) return (error); if ((mntp = getvfs(&uap->fsid)) == NULL) return (EINVAL); cnt = uap->blkcnt; start = blkp = malloc(cnt * sizeof(BLOCK_INFO), M_SEGMENT, M_WAITOK); if (error = copyin(uap->blkiov, blkp, cnt * sizeof(BLOCK_INFO))) { free(blkp, M_SEGMENT); return (error); } for (step = cnt; step--; ++blkp) { if (LFS_VGET(mntp, blkp->bi_inode, &vp)) daddr = LFS_UNUSED_DADDR; else { if (VOP_BMAP(vp, blkp->bi_lbn, NULL, &daddr)) daddr = LFS_UNUSED_DADDR; vput(vp); } blkp->bi_daddr = daddr; } copyout(start, uap->blkiov, cnt * sizeof(BLOCK_INFO)); free(start, M_SEGMENT); return (0); } /* * lfs_segclean: * * Mark the segment clean. * * 0 on success * -1/errno is return on error. */ int lfs_segclean(p, uap, retval) struct proc *p; struct args { fsid_t fsid; /* file system */ u_long segment; /* segment number */ } *uap; int *retval; { CLEANERINFO *cip; SEGUSE *sup; struct buf *bp; struct mount *mntp; struct lfs *fs; int error; #ifdef VERBOSE printf("lfs_segclean\n"); #endif if (error = suser(p->p_ucred, &p->p_acflag)) return (error); if ((mntp = getvfs(&uap->fsid)) == NULL) return (EINVAL); fs = VFSTOUFS(mntp)->um_lfs; LFS_SEGENTRY(sup, fs, uap->segment, bp); sup->su_flags &= ~SEGUSE_DIRTY; sup->su_nbytes = 0; LFS_UBWRITE(bp); LFS_CLEANERINFO(cip, fs, bp); ++cip->clean; --cip->dirty; LFS_UBWRITE(bp); return (0); } /* * lfs_segwait: * * This will block until a segment in file system fsid is written. A timeout * in milliseconds may be specified which will awake the cleaner automatically. * An fsid of -1 means any file system, and a timeout of 0 means forever. * * 0 on success * 1 on timeout * -1/errno is return on error. */ int lfs_segwait(p, uap, retval) struct proc *p; struct args { fsid_t fsid; /* file system */ struct timeval *tv; /* timeout */ } *uap; int *retval; { extern int lfs_allclean_wakeup; struct mount *mntp; struct timeval atv; void *addr; u_long timeout; int error, s; #ifdef VERBOSE printf("lfs_segwait\n"); #endif if (error = suser(p->p_ucred, &p->p_acflag)) return (error); #ifdef WHEN_QUADS_WORK if (uap->fsid == (fsid_t)-1) addr = &lfs_allclean_wakeup; else { if ((mntp = getvfs(&uap->fsid)) == NULL) return (EINVAL); addr = &VFSTOUFS(mntp)->um_lfs->lfs_nextseg; } #else if ((mntp = getvfs(&uap->fsid)) == NULL) addr = &lfs_allclean_wakeup; else addr = &VFSTOUFS(mntp)->um_lfs->lfs_nextseg; #endif if (uap->tv) { if (error = copyin(uap->tv, &atv, sizeof(struct timeval))) return (error); if (itimerfix(&atv)) return (EINVAL); s = splhigh(); timevaladd(&atv, &time); timeout = hzto(&atv); splx(s); } else timeout = 0; error = tsleep(addr, PCATCH | PUSER, "segment", timeout); return (error == ERESTART ? EINTR : 0); }