diff options
author | Michael I. Bushnell <mib@gnu.org> | 1994-05-26 18:10:27 +0000 |
---|---|---|
committer | Michael I. Bushnell <mib@gnu.org> | 1994-05-26 18:10:27 +0000 |
commit | c7c492e42a63e6b8e6752234e53cb0a9b57a885c (patch) | |
tree | aaa9965110005b7edd26fbc631b3a2f9d1533a15 /ufs/alloc.c | |
parent | ec12040856becd0c795be579c6672141254eb5bd (diff) |
Initial revision
Diffstat (limited to 'ufs/alloc.c')
-rw-r--r-- | ufs/alloc.c | 1053 |
1 files changed, 1053 insertions, 0 deletions
diff --git a/ufs/alloc.c b/ufs/alloc.c new file mode 100644 index 00000000..161edcb8 --- /dev/null +++ b/ufs/alloc.c @@ -0,0 +1,1053 @@ +/* Disk allocation routines + Copyright (C) 1993, 1994 Free Software Foundation + +This file is part of the GNU Hurd. + +The GNU Hurd is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +The GNU Hurd is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the GNU Hurd; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Modified from UCB by Michael I. Bushnell. */ + +/* + * Copyright (c) 1982, 1986, 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution is only permitted until one year after the first shipment + * of 4.4BSD by the Regents. Otherwise, redistribution and use in source and + * binary forms are permitted provided that: (1) source distributions retain + * this entire copyright notice and comment, and (2) distributions including + * binaries display the following acknowledgement: This product includes + * software developed by the University of California, Berkeley and its + * contributors'' in the documentation or other materials provided with the + * distribution and in all advertising materials mentioning features or use + * of this software. 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 AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#)ufs_alloc.c 7.20 (Berkeley) 6/28/90 + */ + +#include "ufs.h" +#include "fs.h" +#include "dinode.h" + +#include <stdio.h> + +static u_long alloccg (int, daddr_t, int); +static u_long ialloccg (int, daddr_t, int); +static u_long hashalloc (int, long, int, u_long(*)(int, daddr_t, int)); +static daddr_t fragextend (int, long, int, int); +static daddr_t alloccgblk (struct cg *, daddr_t); +static daddr_t mapsearch (struct cg *, daddr_t, int); +static ino_t dirpref (); + +/* These are in tables.c. */ +extern int inside[], around[]; +extern unsigned char *fragtbl[]; + +static spin_lock_t alloclock = SPIN_LOCK_INITIALIZER; + +/* + * 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. + */ +error_t +alloc(struct node *np, + daddr_t lbn, + daddr_t bpref, + int size, + daddr_t *bnp, + struct protid *cred) +{ + int cg; + daddr_t bno; + + *bnp = 0; + assert ("Alloc of bad sized block" && (unsigned) size <= sblock->fs_bsize + && !fragoff(size)); + + spin_lock (&alloclock); + + if (size == sblock->fs_bsize && sblock->fs_cstotal.cs_nbfree == 0) + goto nospace; + if (cred && !diskfs_isuid (0, cred) && freespace(sblock->fs_minfree) <= 0) + goto nospace; + + if (bpref >= sblock->fs_size) + bpref = 0; + if (bpref == 0) + cg = itog(np->dn->number); + else + cg = dtog(bpref); + bno = (daddr_t)hashalloc(cg, (long)bpref, size, alloccg); + + spin_unlock (&alloclock); + + if (bno > 0) + { + np->dn_stat.st_blocks += btodb(size); + np->dn_set_mtime = 1; + np->dn_set_ctime = 1; + *bnp = bno; + return 0; + } + + nospace: + spin_unlock (&alloclock); + printf("file system full\n"); + 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. + */ +error_t +realloccg(struct node *np, + daddr_t lbprev, + volatile daddr_t bpref, + int osize, + int nsize, + daddr_t *pbn, + struct protid *cred) +{ + volatile int cg, request; + daddr_t bprev, bno; + error_t error; + + *pbn = 0; + assert ("bad old size" && (unsigned) osize <= sblock->fs_bsize + && !fragoff (osize)); + assert ("bad new size" && (unsigned) nsize <= sblock->fs_bsize + && !fragoff (nsize)); + + spin_lock (&alloclock); + + if (cred && !diskfs_isuid (0, cred) && freespace(sblock->fs_minfree) <= 0) + { + spin_unlock (&alloclock); + goto nospace; + } + + if (error = diskfs_catch_exception ()) + return error; + bprev = dinodes[np->dn->number].di_db[lbprev]; + diskfs_end_catch_exception (); + assert ("old block not allocated" && bprev); + + /* + * Check for extension in the existing location. + */ + cg = dtog(bprev); + if (bno = fragextend(cg, (long)bprev, osize, nsize)) + { + spin_unlock (&alloclock); + assert ("fragextend behaved incorrectly" && bprev == bno); + np->dn_stat.st_blocks += btodb(nsize - osize); + np->dn_set_mtime = 1; + np->dn_set_ctime = 1; + *pbn = bno; + return (0); + } + /* + * Allocate a new disk location. + */ + if (bpref >= sblock->fs_size) + bpref = 0; + switch ((int)sblock->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 (sblock->fs_minfree < 5 || + sblock->fs_cstotal.cs_nffree > + sblock->fs_dsize * sblock->fs_minfree / (2 * 100)) + break; + printf("optimization changed from SPACE to TIME\n"); + sblock->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 + * `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 = sblock->fs_bsize; + if (sblock->fs_cstotal.cs_nffree < + sblock->fs_dsize * (sblock->fs_minfree - 2) / 100) + break; + printf("%s: optimization changed from TIME to SPACE\n", + sblock->fs_fsmnt); + sblock->fs_optim = FS_OPTSPACE; + break; + default: + assert ("filesystem opitimazation bad value" && 0); + } + + bno = (daddr_t)hashalloc(cg, (long)bpref, request, + (u_long (*)())alloccg); + + spin_unlock (&alloclock); + + if (bno > 0) + { + blkfree(bprev, (off_t)osize); + if (nsize < request) + blkfree(bno + numfrags(nsize), (off_t)(request - nsize)); + np->dn_stat.st_blocks += btodb (nsize - osize); + np->dn_set_mtime = 1; + np->dn_set_ctime = 1; + *pbn = bno; + return (0); + } + nospace: + /* + * no space available + */ + printf("file system full\n"); + return (ENOSPC); +} + + +/* Implement the diskfs_alloc_node callback from the diskfs library. + See <hurd/diskfs.h> for the interface description. */ +error_t +diskfs_alloc_node(struct node *dir, + mode_t mode, + struct node **npp) +{ + int ino; + struct node *np; + int cg; + error_t error; + int ipref; + + if (S_ISDIR (mode)) + ipref = dirpref (); + else + ipref = dir->dn->number; + + *npp = 0; + + if (sblock->fs_cstotal.cs_nifree == 0) + goto noinodes; + if (ipref >= sblock->fs_ncg * sblock->fs_ipg) + ipref = 0; + cg = itog(ipref); + spin_lock (&alloclock); + ino = (int)hashalloc(cg, (long)ipref, mode, ialloccg); + spin_unlock (&alloclock); + if (ino == 0) + goto noinodes; + if (error = iget(ino, &np)) + return error; + *npp = np; + assert ("duplicate allocation" && !np->dn_stat.st_mode); + if (np->dn_stat.st_blocks) + { + printf("free inode %d had %d blocks\n", ino, np->dn_stat.st_blocks); + np->dn_stat.st_blocks = 0; + np->dn_set_ctime = 1; + } + /* + * Set up a new generation number for this inode. + */ + spin_lock (&gennumberlock); + if (++nextgennumber < (u_long)diskfs_mtime->seconds) + nextgennumber = diskfs_mtime->seconds; + np->dn_stat.st_gen = nextgennumber; + spin_unlock (&gennumberlock); + return (0); + noinodes: + printf("out of inodes\n"); + return (ENOSPC); +} + +/* + * Find a cylinder to place a directory. + * + * The policy implemented by this algorithm is to select from + * among those cylinder groups with above the average number of + * free inodes, the one with the smallest number of directories. + */ +static ino_t +dirpref() +{ + int cg, minndir, mincg, avgifree; + + spin_lock (&alloclock); + avgifree = sblock->fs_cstotal.cs_nifree / sblock->fs_ncg; + minndir = sblock->fs_ipg; + mincg = 0; + for (cg = 0; cg < sblock->fs_ncg; cg++) + if (csum[cg].cs_ndir < minndir && csum[cg].cs_nifree >= avgifree) + { + mincg = cg; + minndir = csum[cg].cs_ndir; + } + spin_unlock (&alloclock); + return ((int)(sblock->fs_ipg * mincg)); +} + +/* + * Select the desired position for the next block in a file. The file is + * logically divided into sections. The first section is composed of the + * direct blocks. Each additional section contains fs_maxbpg blocks. + * + * If no blocks have been allocated in the first section, the policy is to + * request a block in the same cylinder group as the inode that describes + * the file. If no blocks have been allocated in any other section, the + * policy is to place the section in a cylinder group with a greater than + * average number of free blocks. An appropriate cylinder group is found + * by using a rotor that sweeps the cylinder groups. When a new group of + * blocks is needed, the sweep begins in the cylinder group following the + * cylinder group from which the previous allocation was made. The sweep + * continues until a cylinder group with greater than the average number + * of free blocks is found. If the allocation is for the first block in an + * indirect block, the information on the previous allocation is unavailable; + * here a best guess is made based upon the logical block number being + * allocated. + * + * If a section is already partially allocated, the policy is to + * contiguously allocate fs_maxcontig blocks. The end of one of these + * contiguous blocks and the beginning of the next is physically separated + * so that the disk head will be in transit between them for at least + * fs_rotdelay milliseconds. This is to allow time for the processor to + * schedule another I/O transfer. + */ +daddr_t +blkpref(struct node *np, + daddr_t lbn, + int indx, + daddr_t *bap) +{ + int cg; + int avgbfree, startcg; + daddr_t nextblk; + + spin_lock (&alloclock); + if (indx % sblock->fs_maxbpg == 0 || bap[indx - 1] == 0) + { + if (lbn < NDADDR) + { + spin_unlock (&alloclock); + cg = itog(np->dn->number); + return (sblock->fs_fpg * cg + sblock->fs_frag); + } + /* + * Find a cylinder with greater than average number of + * unused data blocks. + */ + if (indx == 0 || bap[indx - 1] == 0) + startcg = itog(np->dn->number) + lbn / sblock->fs_maxbpg; + else + startcg = dtog(bap[indx - 1]) + 1; + startcg %= sblock->fs_ncg; + avgbfree = sblock->fs_cstotal.cs_nbfree / sblock->fs_ncg; + for (cg = startcg; cg < sblock->fs_ncg; cg++) + if (csum[cg].cs_nbfree >= avgbfree) + { + spin_unlock (&alloclock); + sblock->fs_cgrotor = cg; + return (sblock->fs_fpg * cg + sblock->fs_frag); + } + for (cg = 0; cg <= startcg; cg++) + if (csum[cg].cs_nbfree >= avgbfree) + { + spin_unlock (&alloclock); + sblock->fs_cgrotor = cg; + return (sblock->fs_fpg * cg + sblock->fs_frag); + } + spin_unlock (&alloclock); + return 0; + } + + spin_unlock (&alloclock); + + /* + * One or more previous blocks have been laid out. If less + * than fs_maxcontig previous blocks are contiguous, the + * next block is requested contiguously, otherwise it is + * requested rotationally delayed by fs_rotdelay milliseconds. + */ + nextblk = bap[indx - 1] + sblock->fs_frag; + if (indx > sblock->fs_maxcontig && + bap[indx - sblock->fs_maxcontig] + blkstofrags(sblock->fs_maxcontig) + != nextblk) + return (nextblk); + if (sblock->fs_rotdelay != 0) + /* + * Here we convert ms of delay to frags as: + * (frags) = (ms) * (rev/sec) * (sect/rev) / + * ((sect/frag) * (ms/sec)) + * then round up to the next block. + */ + nextblk += roundup(sblock->fs_rotdelay * sblock->fs_rps + * sblock->fs_nsect / (NSPF * 1000), sblock->fs_frag); + return (nextblk); +} + +/* + * Implement the cylinder overflow algorithm. + * + * The policy implemented by this algorithm is: + * 1) allocate the block in its requested cylinder group. + * 2) quadradically rehash on the cylinder group number. + * 3) brute force search for a free block. + */ +/*VARARGS5*/ +static u_long +hashalloc(int cg, + long pref, + int size, /* size for data blocks, mode for inodes */ + u_long (*allocator)(int, daddr_t, int)) +{ + long result; + int i, icg = cg; + + /* + * 1: preferred cylinder group + */ + result = (*allocator)(cg, pref, size); + if (result) + return (result); + /* + * 2: quadratic rehash + */ + for (i = 1; i < sblock->fs_ncg; i *= 2) + { + cg += i; + if (cg >= sblock->fs_ncg) + cg -= sblock->fs_ncg; + result = (*allocator)(cg, 0, size); + if (result) + return (result); + } + /* + * 3: brute force search + * Note that we start at i == 2, since 0 was checked initially, + * and 1 is always checked in the quadratic rehash. + */ + cg = (icg + 2) % sblock->fs_ncg; + for (i = 2; i < sblock->fs_ncg; i++) + { + result = (*allocator)(cg, 0, size); + if (result) + return (result); + cg++; + if (cg == sblock->fs_ncg) + cg = 0; + } + return 0; +} + +/* + * Determine whether a fragment can be extended. + * + * Check to see if the necessary fragments are available, and + * if they are, allocate them. + */ +static daddr_t +fragextend(int cg, + long bprev, + int osize, + int nsize) +{ + struct cg *cgp; + long bno; + int frags, bbase; + int i; + + if (csum[cg].cs_nffree < numfrags(nsize - osize)) + return 0; + frags = numfrags(nsize); + bbase = fragnum(bprev); + if (bbase > fragnum((bprev + frags - 1))) + /* cannot extend across a block boundary */ + return 0; + + cgp = (struct cg *) (cgs + sblock->fs_bsize * cg); + + if (diskfs_catch_exception ()) + return 0; /* bogus, but that's what BSD does... */ + + if (!cg_chkmagic(cgp)) + { + printf ("Cylinder group %d bad magic number: %ld/%ld\n", + cg, cgp->cg_magic, ((struct ocg *)(cgp))->cg_magic); + diskfs_end_catch_exception (); + return 0; + } + cgp->cg_time = diskfs_mtime->seconds; + bno = dtogd(bprev); + for (i = numfrags(osize); i < frags; i++) + if (isclr(cg_blksfree(cgp), bno + i)) + { + diskfs_end_catch_exception (); + return 0; + } + + /* + * the current fragment can be extended + * deduct the count on fragment being extended into + * increase the count on the remaining fragment (if any) + * allocate the extended piece + */ + for (i = frags; i < sblock->fs_frag - bbase; i++) + if (isclr(cg_blksfree(cgp), bno + i)) + break; + cgp->cg_frsum[i - numfrags(osize)]--; + if (i != frags) + cgp->cg_frsum[i - frags]++; + for (i = numfrags(osize); i < frags; i++) + { + clrbit(cg_blksfree(cgp), bno + i); + cgp->cg_cs.cs_nffree--; + sblock->fs_cstotal.cs_nffree--; + csum[cg].cs_nffree--; + } + diskfs_end_catch_exception (); + return (bprev); +} + +/* + * Determine whether a block can be allocated. + * + * Check to see if a block of the apprpriate size is available, + * and if it is, allocate it. + */ +static u_long +alloccg(int cg, + volatile daddr_t bpref, + int size) +{ + struct cg *cgp; + int i; + int bno, frags, allocsiz; + + if (csum[cg].cs_nbfree == 0 && size == sblock->fs_bsize) + return 0; + cgp = (struct cg *) (cgs + sblock->fs_bsize * cg); + + if (diskfs_catch_exception ()) + return 0; + + if (!cg_chkmagic(cgp) || + (cgp->cg_cs.cs_nbfree == 0 && size == sblock->fs_bsize)) + { + printf ("Cylinder group %d bad magic number: %ld/%ld\n", + cg, cgp->cg_magic, ((struct ocg *)(cgp))->cg_magic); + diskfs_end_catch_exception (); + return 0; + } + cgp->cg_time = diskfs_mtime->seconds; + if (size == sblock->fs_bsize) + { + bno = alloccgblk(cgp, bpref); + diskfs_end_catch_exception (); + return (u_long) (bno); + } + /* + * check to see if any fragments are already available + * allocsiz is the size which will be allocated, hacking + * it down to a smaller size if necessary + */ + frags = numfrags(size); + for (allocsiz = frags; allocsiz < sblock->fs_frag; allocsiz++) + if (cgp->cg_frsum[allocsiz] != 0) + break; + if (allocsiz == sblock->fs_frag) + { + /* + * no fragments were available, so a block will be + * allocated, and hacked up + */ + if (cgp->cg_cs.cs_nbfree == 0) + { + diskfs_end_catch_exception (); + return 0; + } + + bno = alloccgblk(cgp, bpref); + bpref = dtogd(bno); + for (i = frags; i < sblock->fs_frag; i++) + setbit(cg_blksfree(cgp), bpref + i); + i = sblock->fs_frag - frags; + cgp->cg_cs.cs_nffree += i; + sblock->fs_cstotal.cs_nffree += i; + csum[cg].cs_nffree += i; + cgp->cg_frsum[i]++; + return (u_long)(bno); + } + bno = mapsearch(cgp, bpref, allocsiz); + if (bno < 0) + { + diskfs_end_catch_exception (); + return 0; + } + + for (i = 0; i < frags; i++) + clrbit(cg_blksfree(cgp), bno + i); + cgp->cg_cs.cs_nffree -= frags; + sblock->fs_cstotal.cs_nffree -= frags; + csum[cg].cs_nffree -= frags; + cgp->cg_frsum[allocsiz]--; + if (frags != allocsiz) + cgp->cg_frsum[allocsiz - frags]++; + diskfs_end_catch_exception (); + return (u_long) (cg * sblock->fs_fpg + bno); +} + +/* + * Allocate a block in a cylinder group. + * + * This algorithm implements the following policy: + * 1) allocate the requested block. + * 2) allocate a rotationally optimal block in the same cylinder. + * 3) allocate the next available block on the block rotor for the + * specified cylinder group. + * Note that this routine only allocates fs_bsize blocks; these + * blocks may be fragmented by the routine that allocates them. + */ +static daddr_t +alloccgblk(struct cg *cgp, + volatile daddr_t bpref) +{ + daddr_t bno; + int cylno, pos, delta; + short *cylbp; + int i; + daddr_t ret; + + if (diskfs_catch_exception ()) + return 0; + + if (bpref == 0) + { + bpref = cgp->cg_rotor; + goto norot; + } + bpref = blknum(bpref); + bpref = dtogd(bpref); + /* + * if the requested block is available, use it + */ + if (isblock(cg_blksfree(cgp), fragstoblks(bpref))) + { + bno = bpref; + goto gotit; + } + /* + * check for a block available on the same cylinder + */ + cylno = cbtocylno(bpref); + if (cg_blktot(cgp)[cylno] == 0) + goto norot; + if (sblock->fs_cpc == 0) + { + /* + * block layout info is not available, so just have + * to take any block in this cylinder. + */ + bpref = howmany(sblock->fs_spc * cylno, NSPF); + goto norot; + } + /* + * check the summary information to see if a block is + * available in the requested cylinder starting at the + * requested rotational position and proceeding around. + */ + cylbp = cg_blks(cgp, cylno); + pos = cbtorpos(bpref); + for (i = pos; i < sblock->fs_nrpos; i++) + if (cylbp[i] > 0) + break; + if (i == sblock->fs_nrpos) + for (i = 0; i < pos; i++) + if (cylbp[i] > 0) + break; + if (cylbp[i] > 0) + { + /* + * found a rotational position, now find the actual + * block. A panic if none is actually there. + */ + pos = cylno % sblock->fs_cpc; + bno = (cylno - pos) * sblock->fs_spc / NSPB; + assert ("postbl table bad" &&fs_postbl(pos)[i] != -1); + for (i = fs_postbl(pos)[i];; ) + { + if (isblock(cg_blksfree(cgp), bno + i)) + { + bno = blkstofrags(bno + i); + goto gotit; + } + delta = fs_rotbl[i]; + if (delta <= 0 || + delta + i > fragstoblks(sblock->fs_fpg)) + break; + i += delta; + } + assert ("Inconsistent rotbl table" && 0); + } + norot: + /* + * no blocks in the requested cylinder, so take next + * available one in this cylinder group. + */ + bno = mapsearch(cgp, bpref, (int)sblock->fs_frag); + if (bno < 0) + { + diskfs_end_catch_exception (); + return 0; + } + cgp->cg_rotor = bno; + gotit: + clrblock(cg_blksfree(cgp), (long)fragstoblks(bno)); + cgp->cg_cs.cs_nbfree--; + sblock->fs_cstotal.cs_nbfree--; + csum[cgp->cg_cgx].cs_nbfree--; + cylno = cbtocylno(bno); + cg_blks(cgp, cylno)[cbtorpos(bno)]--; + cg_blktot(cgp)[cylno]--; + ret = cgp->cg_cgx * sblock->fs_fpg + bno; + diskfs_end_catch_exception (); + return ret; +} + +/* + * Determine whether an inode can be allocated. + * + * Check to see if an inode is available, and if it is, + * allocate it using the following policy: + * 1) allocate the requested inode. + * 2) allocate the next available inode after the requested + * inode in the specified cylinder group. + */ +static u_long +ialloccg(int cg, + volatile daddr_t ipref, + int modein) +{ + struct cg *cgp; + int start, len, loc, map, i; + mode_t mode = (mode_t) modein; + + if (csum[cg].cs_nifree == 0) + return 0; + + cgp = (struct cg *)(cgs + sblock->fs_bsize * cg); + + if (diskfs_catch_exception ()) + return 0; + + if (!cg_chkmagic(cgp) || cgp->cg_cs.cs_nifree == 0) + { + printf ("Cylinder group %d bad magic number: %ld/%ld\n", + cg, cgp->cg_magic, ((struct ocg *)(cgp))->cg_magic); + diskfs_end_catch_exception (); + return 0; + } + cgp->cg_time = diskfs_mtime->seconds; + if (ipref) + { + ipref %= sblock->fs_ipg; + if (isclr(cg_inosused(cgp), ipref)) + goto gotit; + } + start = cgp->cg_irotor / NBBY; + len = howmany(sblock->fs_ipg - cgp->cg_irotor, NBBY); + loc = skpc(0xff, len, (u_char *) &cg_inosused(cgp)[start]); + if (loc == 0) + { + len = start + 1; + start = 0; + loc = skpc(0xff, len, (u_char *) &cg_inosused(cgp)[0]); + assert ("inconsistent cg_inosused table" && loc); + } + i = start + len - loc; + map = cg_inosused(cgp)[i]; + ipref = i * NBBY; + for (i = 1; i < (1 << NBBY); i <<= 1, ipref++) + { + if ((map & i) == 0) + { + cgp->cg_irotor = ipref; + goto gotit; + } + } + assert ("inconsistent cg_inosused table" && 0); + gotit: + setbit(cg_inosused(cgp), ipref); + cgp->cg_cs.cs_nifree--; + sblock->fs_cstotal.cs_nifree--; + csum[cg].cs_nifree--; + if ((mode & IFMT) == IFDIR) + { + cgp->cg_cs.cs_ndir++; + sblock->fs_cstotal.cs_ndir++; + csum[cg].cs_ndir++; + } + diskfs_end_catch_exception (); + return (u_long)(cg * sblock->fs_ipg + ipref); +} + +/* + * Free a block or fragment. + * + * The specified block or fragment is placed back in the + * free map. If a fragment is deallocated, a possible + * block reassembly is checked. + */ +void +blkfree(volatile daddr_t bno, + int size) +{ + struct cg *cgp; + int cg, blk, frags, bbase; + int i; + + assert ("free of bad sized block" &&(unsigned) size <= sblock->fs_bsize + && !fragoff (size)); + cg = dtog(bno); + if ((unsigned)bno >= sblock->fs_size) + { + printf("bad block %ld\n", bno); + return; + } + + cgp = (struct cg *)(cgs + sblock->fs_bsize * cg); + + spin_lock (&alloclock); + + if (diskfs_catch_exception ()) + { + spin_unlock (&alloclock); + return; + } + + if (!cg_chkmagic(cgp)) + { + spin_unlock (&alloclock); + printf ("Cylinder group %d bad magic number: %ld/%ld\n", + cg, cgp->cg_magic, ((struct ocg *)(cgp))->cg_magic); + diskfs_end_catch_exception (); + return; + } + cgp->cg_time = diskfs_mtime->seconds; + bno = dtogd(bno); + if (size == sblock->fs_bsize) + { + assert ("inconsistent cg_blskfree table" + && !isblock (cg_blksfree (cgp), fragstoblks (bno))); + setblock(cg_blksfree(cgp), fragstoblks(bno)); + cgp->cg_cs.cs_nbfree++; + sblock->fs_cstotal.cs_nbfree++; + csum[cg].cs_nbfree++; + i = cbtocylno(bno); + cg_blks(cgp, i)[cbtorpos(bno)]++; + cg_blktot(cgp)[i]++; + } + else + { + bbase = bno - fragnum(bno); + /* + * decrement the counts associated with the old frags + */ + blk = blkmap(cg_blksfree(cgp), bbase); + fragacct(blk, cgp->cg_frsum, -1); + /* + * deallocate the fragment + */ + frags = numfrags(size); + for (i = 0; i < frags; i++) + { + assert ("inconsistent cg_blksfree table" + && !isset (cg_blksfree (cgp), bno + i)); + setbit(cg_blksfree(cgp), bno + i); + } + cgp->cg_cs.cs_nffree += i; + sblock->fs_cstotal.cs_nffree += i; + csum[cg].cs_nffree += i; + /* + * add back in counts associated with the new frags + */ + blk = blkmap(cg_blksfree(cgp), bbase); + fragacct(blk, cgp->cg_frsum, 1); + /* + * if a complete block has been reassembled, account for it + */ + if (isblock(cg_blksfree(cgp), (daddr_t)fragstoblks(bbase))) + { + cgp->cg_cs.cs_nffree -= sblock->fs_frag; + sblock->fs_cstotal.cs_nffree -= sblock->fs_frag; + csum[cg].cs_nffree -= sblock->fs_frag; + cgp->cg_cs.cs_nbfree++; + sblock->fs_cstotal.cs_nbfree++; + csum[cg].cs_nbfree++; + i = cbtocylno(bbase); + cg_blks(cgp, i)[cbtorpos(bbase)]++; + cg_blktot(cgp)[i]++; + } + } + spin_unlock (&alloclock); + diskfs_end_catch_exception (); +} + +/* + * Free an inode. + * + * The specified inode is placed back in the free map. + */ +void +diskfs_free_node(struct node *np, mode_t mode) +{ + struct cg *cgp; + int cg; + volatile int ino = np->dn->number; + + assert ("invalid inode number" && ino < sblock->fs_ipg * sblock->fs_ncg); + + cg = itog(ino); + cgp = (struct cg *)(cgs + sblock->fs_bsize * cg); + + spin_lock (&alloclock); + if (diskfs_catch_exception ()) + { + spin_unlock (&alloclock); + return; + } + + if (!cg_chkmagic(cgp)) + { + spin_unlock (&alloclock); + printf ("Cylinder group %d bad magic number: %ld/%ld\n", + cg, cgp->cg_magic, ((struct ocg *)(cgp))->cg_magic); + diskfs_end_catch_exception (); + return; + } + cgp->cg_time = diskfs_mtime->seconds; + ino %= sblock->fs_ipg; + assert ("inconsistent cg_inosused table" && !isclr (cg_inosused (cgp), ino)); + clrbit(cg_inosused(cgp), ino); + if (ino < cgp->cg_irotor) + cgp->cg_irotor = ino; + cgp->cg_cs.cs_nifree++; + sblock->fs_cstotal.cs_nifree++; + csum[cg].cs_nifree++; + if ((mode & IFMT) == IFDIR) + { + cgp->cg_cs.cs_ndir--; + sblock->fs_cstotal.cs_ndir--; + csum[cg].cs_ndir--; + } + spin_unlock (&alloclock); + diskfs_end_catch_exception (); +} + + +/* + * Find a block of the specified size in the specified cylinder group. + * + * It is a panic if a request is made to find a block if none are + * available. + */ +/* This routine expects to be called from inside a diskfs_catch_exception */ +static daddr_t +mapsearch(struct cg *cgp, + daddr_t bpref, + int allocsiz) +{ + daddr_t bno; + int start, len, loc, i; + int blk, field, subfield, pos; + + /* + * find the fragment by searching through the free block + * map for an appropriate bit pattern + */ + if (bpref) + start = dtogd(bpref) / NBBY; + else + start = cgp->cg_frotor / NBBY; + len = howmany(sblock->fs_fpg, NBBY) - start; + loc = scanc((unsigned)len, (u_char *)&cg_blksfree(cgp)[start], + (u_char *)fragtbl[sblock->fs_frag], + (u_char)(1 << (allocsiz - 1 + (sblock->fs_frag % NBBY)))); + if (loc == 0) + { + len = start + 1; + start = 0; + loc = scanc((unsigned)len, (u_char *)&cg_blksfree(cgp)[0], + (u_char *)fragtbl[sblock->fs_frag], + (u_char)(1 << (allocsiz - 1 + (sblock->fs_frag % NBBY)))); + assert ("incosistent cg_blksfree table" && loc); + } + bno = (start + len - loc) * NBBY; + cgp->cg_frotor = bno; + /* + * found the byte in the map + * sift through the bits to find the selected frag + */ + for (i = bno + NBBY; bno < i; bno += sblock->fs_frag) + { + blk = blkmap(cg_blksfree(cgp), bno); + blk <<= 1; + field = around[allocsiz]; + subfield = inside[allocsiz]; + for (pos = 0; pos <= sblock->fs_frag - allocsiz; pos++) + { + if ((blk & field) == subfield) + return (bno + pos); + field <<= 1; + subfield <<= 1; + } + } + assert ("inconsistent cg_blksfree table" && 0); +} + + |