📄 ffs_alloc.c
字号:
/* * Copyright (c) 1982, 1986, 1989, 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. * * @(#)ffs_alloc.c 8.8 (Berkeley) 2/21/94 */#include <sys/param.h>#include <sys/systm.h>#include <sys/buf.h>#include <sys/proc.h>#include <sys/vnode.h>#include <sys/mount.h>#include <sys/kernel.h>#include <sys/syslog.h>#include <vm/vm.h>#include <ufs/ufs/quota.h>#include <ufs/ufs/inode.h>#include <ufs/ffs/fs.h>#include <ufs/ffs/ffs_extern.h>extern u_long nextgennumber;static daddr_t ffs_alloccg __P((struct inode *, int, daddr_t, int));static daddr_t ffs_alloccgblk __P((struct fs *, struct cg *, daddr_t));static daddr_t ffs_clusteralloc __P((struct inode *, int, daddr_t, int));static ino_t ffs_dirpref __P((struct fs *));static daddr_t ffs_fragextend __P((struct inode *, int, long, int, int));static void ffs_fserr __P((struct fs *, u_int, char *));static u_long ffs_hashalloc __P((struct inode *, int, long, int, u_long (*)()));static ino_t ffs_nodealloccg __P((struct inode *, int, daddr_t, int));static daddr_t ffs_mapsearch __P((struct fs *, struct cg *, daddr_t, int));/* * Allocate a block in the file system. * * The size of the requested block is given, which must be some * multiple of fs_fsize and <= fs_bsize. * A preference may be optionally specified. If a preference is given * the following hierarchy is used to allocate a block: * 1) allocate the requested block. * 2) allocate a rotationally optimal block in the same cylinder. * 3) allocate a block in the same cylinder group. * 4) quadradically rehash into other cylinder groups, until an * available block is located. * If no block preference is given the following heirarchy is used * to allocate a block: * 1) allocate a block in the cylinder group that contains the * inode for the file. * 2) quadradically rehash into other cylinder groups, until an * available block is located. */ffs_alloc(ip, lbn, bpref, size, cred, bnp) register struct inode *ip; daddr_t lbn, bpref; int size; struct ucred *cred; daddr_t *bnp;{ register struct fs *fs; daddr_t bno; int cg, error; *bnp = 0; fs = ip->i_fs;#ifdef DIAGNOSTIC if ((u_int)size > fs->fs_bsize || fragoff(fs, size) != 0) { printf("dev = 0x%x, bsize = %d, size = %d, fs = %s\n", ip->i_dev, fs->fs_bsize, size, fs->fs_fsmnt); panic("ffs_alloc: bad size"); } if (cred == NOCRED) panic("ffs_alloc: missing credential\n");#endif /* DIAGNOSTIC */ if (size == fs->fs_bsize && fs->fs_cstotal.cs_nbfree == 0) goto nospace; if (cred->cr_uid != 0 && freespace(fs, fs->fs_minfree) <= 0) goto nospace;#ifdef QUOTA if (error = chkdq(ip, (long)btodb(size), cred, 0)) return (error);#endif if (bpref >= fs->fs_size) bpref = 0; if (bpref == 0) cg = ino_to_cg(fs, ip->i_number); else cg = dtog(fs, bpref); bno = (daddr_t)ffs_hashalloc(ip, cg, (long)bpref, size, (u_long (*)())ffs_alloccg); if (bno > 0) { ip->i_blocks += btodb(size); ip->i_flag |= IN_CHANGE | IN_UPDATE; *bnp = bno; return (0); }#ifdef QUOTA /* * Restore user's disk quota because allocation failed. */ (void) chkdq(ip, (long)-btodb(size), cred, FORCE);#endifnospace: ffs_fserr(fs, cred->cr_uid, "file system full"); uprintf("\n%s: write failed, file system is full\n", fs->fs_fsmnt); return (ENOSPC);}/* * Reallocate a fragment to a bigger size * * The number and size of the old block is given, and a preference * and new size is also specified. The allocator attempts to extend * the original block. Failing that, the regular block allocator is * invoked to get an appropriate block. */ffs_realloccg(ip, lbprev, bpref, osize, nsize, cred, bpp) register struct inode *ip; daddr_t lbprev; daddr_t bpref; int osize, nsize; struct ucred *cred; struct buf **bpp;{ register struct fs *fs; struct buf *bp; int cg, request, error; daddr_t bprev, bno; *bpp = 0; fs = ip->i_fs;#ifdef DIAGNOSTIC if ((u_int)osize > fs->fs_bsize || fragoff(fs, osize) != 0 || (u_int)nsize > fs->fs_bsize || fragoff(fs, nsize) != 0) { printf( "dev = 0x%x, bsize = %d, osize = %d, nsize = %d, fs = %s\n", ip->i_dev, fs->fs_bsize, osize, nsize, fs->fs_fsmnt); panic("ffs_realloccg: bad size"); } if (cred == NOCRED) panic("ffs_realloccg: missing credential\n");#endif /* DIAGNOSTIC */ if (cred->cr_uid != 0 && freespace(fs, fs->fs_minfree) <= 0) goto nospace; if ((bprev = ip->i_db[lbprev]) == 0) { printf("dev = 0x%x, bsize = %d, bprev = %d, fs = %s\n", ip->i_dev, fs->fs_bsize, bprev, fs->fs_fsmnt); panic("ffs_realloccg: bad bprev"); } /* * Allocate the extra space in the buffer. */ if (error = bread(ITOV(ip), lbprev, osize, NOCRED, &bp)) { brelse(bp); return (error); }#ifdef QUOTA if (error = chkdq(ip, (long)btodb(nsize - osize), cred, 0)) { brelse(bp); return (error); }#endif /* * Check for extension in the existing location. */ cg = dtog(fs, bprev); if (bno = ffs_fragextend(ip, cg, (long)bprev, osize, nsize)) { if (bp->b_blkno != fsbtodb(fs, bno)) panic("bad blockno"); ip->i_blocks += btodb(nsize - osize); ip->i_flag |= IN_CHANGE | IN_UPDATE; allocbuf(bp, nsize); bp->b_flags |= B_DONE; bzero((char *)bp->b_data + osize, (u_int)nsize - osize); *bpp = bp; return (0); } /* * Allocate a new disk location. */ if (bpref >= fs->fs_size) bpref = 0; switch ((int)fs->fs_optim) { case FS_OPTSPACE: /* * Allocate an exact sized fragment. Although this makes * best use of space, we will waste time relocating it if * the file continues to grow. If the fragmentation is * less than half of the minimum free reserve, we choose * to begin optimizing for time. */ request = nsize; if (fs->fs_minfree < 5 || fs->fs_cstotal.cs_nffree > fs->fs_dsize * fs->fs_minfree / (2 * 100)) break; log(LOG_NOTICE, "%s: optimization changed from SPACE to TIME\n", fs->fs_fsmnt); fs->fs_optim = FS_OPTTIME; break; case FS_OPTTIME: /* * At this point we have discovered a file that is trying to * grow a small fragment to a larger fragment. To save time, * we allocate a full sized block, then free the unused portion. * If the file continues to grow, the `ffs_fragextend' call * above will be able to grow it in place without further * copying. If aberrant programs cause disk fragmentation to * grow within 2% of the free reserve, we choose to begin * optimizing for space. */ request = fs->fs_bsize; if (fs->fs_cstotal.cs_nffree < fs->fs_dsize * (fs->fs_minfree - 2) / 100) break; log(LOG_NOTICE, "%s: optimization changed from TIME to SPACE\n", fs->fs_fsmnt); fs->fs_optim = FS_OPTSPACE; break; default: printf("dev = 0x%x, optim = %d, fs = %s\n", ip->i_dev, fs->fs_optim, fs->fs_fsmnt); panic("ffs_realloccg: bad optim"); /* NOTREACHED */ } bno = (daddr_t)ffs_hashalloc(ip, cg, (long)bpref, request, (u_long (*)())ffs_alloccg); if (bno > 0) { bp->b_blkno = fsbtodb(fs, bno); (void) vnode_pager_uncache(ITOV(ip)); ffs_blkfree(ip, bprev, (long)osize); if (nsize < request) ffs_blkfree(ip, bno + numfrags(fs, nsize), (long)(request - nsize)); ip->i_blocks += btodb(nsize - osize); ip->i_flag |= IN_CHANGE | IN_UPDATE; allocbuf(bp, nsize); bp->b_flags |= B_DONE; bzero((char *)bp->b_data + osize, (u_int)nsize - osize); *bpp = bp; return (0); }#ifdef QUOTA /* * Restore user's disk quota because allocation failed. */ (void) chkdq(ip, (long)-btodb(nsize - osize), cred, FORCE);#endif brelse(bp);nospace: /* * no space available */ ffs_fserr(fs, cred->cr_uid, "file system full"); uprintf("\n%s: write failed, file system is full\n", fs->fs_fsmnt); return (ENOSPC);}/* * Reallocate a sequence of blocks into a contiguous sequence of blocks. * * The vnode and an array of buffer pointers for a range of sequential * logical blocks to be made contiguous is given. The allocator attempts * to find a range of sequential blocks starting as close as possible to * an fs_rotdelay offset from the end of the allocation for the logical * block immediately preceeding the current range. If successful, the * physical block numbers in the buffer pointers and in the inode are * changed to reflect the new allocation. If unsuccessful, the allocation * is left unchanged. The success in doing the reallocation is returned. * Note that the error return is not reflected back to the user. Rather * the previous block allocation will be used. */#include <sys/sysctl.h>int doasyncfree = 1;struct ctldebug debug14 = { "doasyncfree", &doasyncfree };intffs_reallocblks(ap) struct vop_reallocblks_args /* { struct vnode *a_vp; struct cluster_save *a_buflist; } */ *ap;{ struct fs *fs; struct inode *ip; struct vnode *vp; struct buf *sbp, *ebp; daddr_t *bap, *sbap, *ebap; struct cluster_save *buflist; daddr_t start_lbn, end_lbn, soff, eoff, newblk, blkno; struct indir start_ap[NIADDR + 1], end_ap[NIADDR + 1], *idp; int i, len, start_lvl, end_lvl, pref, ssize; vp = ap->a_vp; ip = VTOI(vp); fs = ip->i_fs; if (fs->fs_contigsumsize <= 0) return (ENOSPC); buflist = ap->a_buflist; len = buflist->bs_nchildren; start_lbn = buflist->bs_children[0]->b_lblkno; end_lbn = start_lbn + len - 1;#ifdef DIAGNOSTIC for (i = 1; i < len; i++) if (buflist->bs_children[i]->b_lblkno != start_lbn + i) panic("ffs_reallocblks: non-cluster");#endif /* * If the latest allocation is in a new cylinder group, assume that * the filesystem has decided to move and do not force it back to * the previous cylinder group. */ if (dtog(fs, dbtofsb(fs, buflist->bs_children[0]->b_blkno)) != dtog(fs, dbtofsb(fs, buflist->bs_children[len - 1]->b_blkno))) return (ENOSPC); if (ufs_getlbns(vp, start_lbn, start_ap, &start_lvl) || ufs_getlbns(vp, end_lbn, end_ap, &end_lvl)) return (ENOSPC); /* * Get the starting offset and block map for the first block. */ if (start_lvl == 0) { sbap = &ip->i_db[0]; soff = start_lbn; } else { idp = &start_ap[start_lvl - 1]; if (bread(vp, idp->in_lbn, (int)fs->fs_bsize, NOCRED, &sbp)) { brelse(sbp); return (ENOSPC); } sbap = (daddr_t *)sbp->b_data; soff = idp->in_off; } /* * Find the preferred location for the cluster. */ pref = ffs_blkpref(ip, start_lbn, soff, sbap); /* * If the block range spans two block maps, get the second map. */ if (end_lvl == 0 || (idp = &end_ap[end_lvl - 1])->in_off + 1 >= len) { ssize = len; } else {#ifdef DIAGNOSTIC if (start_ap[start_lvl-1].in_lbn == idp->in_lbn) panic("ffs_reallocblk: start == end");#endif ssize = len - (idp->in_off + 1); if (bread(vp, idp->in_lbn, (int)fs->fs_bsize, NOCRED, &ebp)) goto fail; ebap = (daddr_t *)ebp->b_data; } /* * Search the block map looking for an allocation of the desired size. */ if ((newblk = (daddr_t)ffs_hashalloc(ip, dtog(fs, pref), (long)pref, len, (u_long (*)())ffs_clusteralloc)) == 0) goto fail; /* * We have found a new contiguous block. * * First we have to replace the old block pointers with the new * block pointers in the inode and indirect blocks associated * with the file. */ blkno = newblk; for (bap = &sbap[soff], i = 0; i < len; i++, blkno += fs->fs_frag) { if (i == ssize) bap = ebap;#ifdef DIAGNOSTIC if (buflist->bs_children[i]->b_blkno != fsbtodb(fs, *bap)) panic("ffs_reallocblks: alloc mismatch");#endif *bap++ = blkno; } /* * Next we must write out the modified inode and indirect blocks. * For strict correctness, the writes should be synchronous since * the old block values may have been written to disk. In practise * they are almost never written, but if we are concerned about * strict correctness, the `doasyncfree' flag should be set to zero. * * The test on `doasyncfree' should be changed to test a flag * that shows whether the associated buffers and inodes have * been written. The flag should be set when the cluster is * started and cleared whenever the buffer or inode is flushed. * We can then check below to see if it is set, and do the * synchronous write only when it has been cleared. */ if (sbap != &ip->i_db[0]) { if (doasyncfree) bdwrite(sbp); else bwrite(sbp); } else { ip->i_flag |= IN_CHANGE | IN_UPDATE; if (!doasyncfree) VOP_UPDATE(vp, &time, &time, MNT_WAIT); } if (ssize < len) if (doasyncfree) bdwrite(ebp); else bwrite(ebp); /* * Last, free the old blocks and assign the new blocks to the buffers. */ for (blkno = newblk, i = 0; i < len; i++, blkno += fs->fs_frag) { ffs_blkfree(ip, dbtofsb(fs, buflist->bs_children[i]->b_blkno), fs->fs_bsize); buflist->bs_children[i]->b_blkno = fsbtodb(fs, blkno); } return (0);fail: if (ssize < len) brelse(ebp); if (sbap != &ip->i_db[0]) brelse(sbp); return (ENOSPC);}/* * Allocate an inode in the file system. * * If allocating a directory, use ffs_dirpref to select the inode. * If allocating in a directory, the following hierarchy is followed: * 1) allocate the preferred inode. * 2) allocate an inode in the same cylinder group. * 3) quadradically rehash into other cylinder groups, until an * available inode is located. * If no inode preference is given the following heirarchy is used * to allocate an inode: * 1) allocate an inode in cylinder group 0. * 2) quadradically rehash into other cylinder groups, until an * available inode is located. */ffs_valloc(ap) struct vop_valloc_args /* { struct vnode *a_pvp; int a_mode; struct ucred *a_cred; struct vnode **a_vpp; } */ *ap;{ register struct vnode *pvp = ap->a_pvp; register struct inode *pip; register struct fs *fs; register struct inode *ip; mode_t mode = ap->a_mode; ino_t ino, ipref; int cg, error; *ap->a_vpp = NULL; pip = VTOI(pvp); fs = pip->i_fs; if (fs->fs_cstotal.cs_nifree == 0) goto noinodes; if ((mode & IFMT) == IFDIR) ipref = ffs_dirpref(fs);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -