diff options
-rw-r--r-- | sys/ufs/ffs/ffs_alloc.c | 330 | ||||
-rw-r--r-- | sys/ufs/ffs/ffs_balloc.c | 492 | ||||
-rw-r--r-- | sys/ufs/ffs/ffs_extern.h | 7 |
3 files changed, 802 insertions, 27 deletions
diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c index 158621ced72..26bfc562339 100644 --- a/sys/ufs/ffs/ffs_alloc.c +++ b/sys/ufs/ffs/ffs_alloc.c @@ -1,7 +1,16 @@ -/* $OpenBSD: ffs_alloc.c,v 1.60 2006/03/05 10:35:51 pedro Exp $ */ +/* $OpenBSD: ffs_alloc.c,v 1.61 2006/03/11 21:00:48 pedro Exp $ */ /* $NetBSD: ffs_alloc.c,v 1.11 1996/05/11 18:27:09 mycroft Exp $ */ /* + * Copyright (c) 2002 Networks Associates Technology, Inc. + * All rights reserved. + * + * This software was developed for the FreeBSD Project by Marshall + * Kirk McKusick and Network Associates Laboratories, the Security + * Research Division of Network Associates, Inc. under DARPA/SPAWAR + * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS + * research program. + * * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * @@ -70,6 +79,11 @@ static u_long ffs_hashalloc(struct inode *, int, long, int, static daddr_t ffs_nodealloccg(struct inode *, int, daddr_t, int); static daddr_t ffs_mapsearch(struct fs *, struct cg *, daddr_t, int); +int ffs1_reallocblks(void *); +#ifdef FFS2 +int ffs2_reallocblks(void *); +#endif + #ifdef DIAGNOSTIC static int ffs_checkblk(struct inode *, daddr_t, long); #endif @@ -338,7 +352,7 @@ int doreallocblks = 1; int prtrealloc = 0; int -ffs_reallocblks(void *v) +ffs1_reallocblks(void *v) { struct vop_reallocblks_args /* { struct vnode *a_vp; @@ -371,17 +385,17 @@ ffs_reallocblks(void *v) for (i = 0; i < len; i++) if (!ffs_checkblk(ip, dbtofsb(fs, buflist->bs_children[i]->b_blkno), fs->fs_bsize)) - panic("ffs_reallocblks: unallocated block 1"); + panic("ffs1_reallocblks: unallocated block 1"); for (i = 1; i < len; i++) if (buflist->bs_children[i]->b_lblkno != start_lbn + i) - panic("ffs_reallocblks: non-logical cluster"); + panic("ffs1_reallocblks: non-logical cluster"); blkno = buflist->bs_children[0]->b_blkno; ssize = fsbtodb(fs, fs->fs_frag); for (i = 1; i < len - 1; i++) if (buflist->bs_children[i]->b_blkno != blkno + (i * ssize)) - panic("ffs_reallocblks: non-physical cluster %d", i); + panic("ffs1_reallocblks: non-physical cluster %d", i); #endif /* * If the latest allocation is in a new cylinder group, assume that @@ -412,7 +426,7 @@ ffs_reallocblks(void *v) /* * Find the preferred location for the cluster. */ - pref = ffs_blkpref(ip, start_lbn, soff, sbap); + pref = ffs1_blkpref(ip, start_lbn, soff, sbap); /* * If the block range spans two block maps, get the second map. */ @@ -422,7 +436,7 @@ ffs_reallocblks(void *v) #ifdef DIAGNOSTIC if (start_lvl > 1 && start_ap[start_lvl-1].in_lbn == idp->in_lbn) - panic("ffs_reallocblk: start == end"); + panic("ffs1_reallocblk: start == end"); #endif ssize = len - (idp->in_off + 1); if (bread(vp, idp->in_lbn, (int)fs->fs_bsize, NOCRED, &ebp)) @@ -456,9 +470,9 @@ ffs_reallocblks(void *v) #ifdef DIAGNOSTIC if (!ffs_checkblk(ip, dbtofsb(fs, buflist->bs_children[i]->b_blkno), fs->fs_bsize)) - panic("ffs_reallocblks: unallocated block 2"); + panic("ffs1_reallocblks: unallocated block 2"); if (dbtofsb(fs, buflist->bs_children[i]->b_blkno) != *bap) - panic("ffs_reallocblks: alloc mismatch"); + panic("ffs1_reallocblks: alloc mismatch"); #endif #ifdef DEBUG if (prtrealloc) @@ -524,7 +538,7 @@ ffs_reallocblks(void *v) #ifdef DIAGNOSTIC if (!ffs_checkblk(ip, dbtofsb(fs, buflist->bs_children[i]->b_blkno), fs->fs_bsize)) - panic("ffs_reallocblks: unallocated block 3"); + panic("ffs1_reallocblks: unallocated block 3"); if (prtrealloc) printf(" %d,", blkno); #endif @@ -545,6 +559,247 @@ fail: return (ENOSPC); } +#ifdef FFS2 +int +ffs2_reallocblks(void *v) +{ + struct vop_reallocblks_args /* { + struct vnode *a_vp; + struct cluster_save *a_buflist; + } */ *ap = v; + struct fs *fs; + struct inode *ip; + struct vnode *vp; + struct buf *sbp, *ebp; + ufs2_daddr_t *bap, *sbap, *ebap = 0; + struct cluster_save *buflist; + struct ufsmount *ump; + ufs_lbn_t start_lbn, end_lbn; + ufs2_daddr_t soff, newblk, blkno, pref; + struct indir start_ap[NIADDR + 1], end_ap[NIADDR + 1], *idp; + int i, len, start_lvl, end_lvl, ssize; + + vp = ap->a_vp; + ip = VTOI(vp); + fs = ip->i_fs; + ump = ip->i_ump; + + 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 = 0; i < len; i++) + if (!ffs_checkblk(ip, + dbtofsb(fs, buflist->bs_children[i]->b_blkno), fs->fs_bsize)) + panic("ffs2_reallocblks: unallocated block 1"); + + for (i = 1; i < len; i++) + if (buflist->bs_children[i]->b_lblkno != start_lbn + i) + panic("ffs2_reallocblks: non-logical cluster"); + + blkno = buflist->bs_children[0]->b_blkno; + ssize = fsbtodb(fs, fs->fs_frag); + + for (i = 1; i < len - 1; i++) + if (buflist->bs_children[i]->b_blkno != blkno + (i * ssize)) + panic("ffs2_reallocblks: non-physical cluster %d", i); +#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_din2->di_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 = (ufs2_daddr_t *)sbp->b_data; + soff = idp->in_off; + } + + /* + * 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("ffs2_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 = (ufs2_daddr_t *)ebp->b_data; + } + + /* + * Find the preferred location for the cluster. + */ + pref = ffs2_blkpref(ip, start_lbn, soff, sbap); + + /* + * Search the block map looking for an allocation of the desired size. + */ + if ((newblk = ffs_hashalloc(ip, dtog(fs, pref), pref, + len, 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. + */ +#ifdef DEBUG + if (prtrealloc) + printf("realloc: ino %d, lbns %jd-%jd\n\told:", ip->i_number, + (intmax_t)start_lbn, (intmax_t)end_lbn); +#endif + + blkno = newblk; + + for (bap = &sbap[soff], i = 0; i < len; i++, blkno += fs->fs_frag) { + if (i == ssize) { + bap = ebap; + soff = -i; + } +#ifdef DIAGNOSTIC + if (!ffs_checkblk(ip, + dbtofsb(fs, buflist->bs_children[i]->b_blkno), fs->fs_bsize)) + panic("ffs2_reallocblks: unallocated block 2"); + if (dbtofsb(fs, buflist->bs_children[i]->b_blkno) != *bap) + panic("ffs2_reallocblks: alloc mismatch"); +#endif +#ifdef DEBUG + if (prtrealloc) + printf(" %jd,", (intmax_t)*bap); +#endif + if (DOINGSOFTDEP(vp)) { + if (sbap == &ip->i_din2->di_db[0] && i < ssize) + softdep_setup_allocdirect(ip, start_lbn + i, + blkno, *bap, fs->fs_bsize, fs->fs_bsize, + buflist->bs_children[i]); + else + softdep_setup_allocindir_page(ip, start_lbn + i, + i < ssize ? sbp : ebp, soff + i, blkno, + *bap, buflist->bs_children[i]); + } + *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_din2->di_db[0]) { + if (doasyncfree) + bdwrite(sbp); + else + bwrite(sbp); + } else { + ip->i_flag |= IN_CHANGE | IN_UPDATE; + if (!doasyncfree) + ffs_update(ip, NULL, NULL, 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. + */ +#ifdef DEBUG + if (prtrealloc) + printf("\n\tnew:"); +#endif + for (blkno = newblk, i = 0; i < len; i++, blkno += fs->fs_frag) { + if (!DOINGSOFTDEP(vp)) + ffs_blkfree(ip, dbtofsb(fs, + buflist->bs_children[i]->b_blkno), fs->fs_bsize); + buflist->bs_children[i]->b_blkno = fsbtodb(fs, blkno); +#ifdef DIAGNOSTIC + if (!ffs_checkblk(ip, + dbtofsb(fs, buflist->bs_children[i]->b_blkno), fs->fs_bsize)) + panic("ffs2_reallocblks: unallocated block 3"); +#endif +#ifdef DEBUG + if (prtrealloc) + printf(" %jd,", (intmax_t)blkno); +#endif + } +#ifdef DEBUG + if (prtrealloc) { + prtrealloc--; + printf("\n"); + } +#endif + + return (0); + +fail: + if (ssize < len) + brelse(ebp); + + if (sbap != &ip->i_din2->di_db[0]) + brelse(sbp); + + return (ENOSPC); +} +#endif /* FFS2 */ + +int +ffs_reallocblks(void *v) +{ + if (!doreallocblks) + return (ENOSPC); + +#ifdef FFS2 + struct vop_reallocblks_args *ap = v; + + if (VTOI(ap->a_vp)->i_ump->um_fstype == UM_UFS2) + return (ffs2_reallocblks(v)); +#endif + + return (ffs1_reallocblks(v)); +} + /* * Allocate an inode in the file system. * @@ -784,13 +1039,13 @@ end: * fs_rotdelay milliseconds. This is to allow time for the processor to * schedule another I/O transfer. */ -daddr_t -ffs_blkpref(struct inode *ip, daddr_t lbn, int indx, daddr_t *bap) +ufs1_daddr_t +ffs1_blkpref(struct inode *ip, daddr_t lbn, int indx, ufs1_daddr_t *bap) { struct fs *fs; int cg; int avgbfree, startcg; - daddr_t nextblk; + ufs1_daddr_t nextblk; fs = ip->i_fs; if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) { @@ -844,6 +1099,55 @@ ffs_blkpref(struct inode *ip, daddr_t lbn, int indx, daddr_t *bap) } /* + * Same as above, for UFS2. + */ +#ifdef FFS2 +ufs2_daddr_t +ffs2_blkpref(struct inode *ip, daddr_t lbn, int indx, ufs2_daddr_t *bap) +{ + struct fs *fs; + int cg, avgbfree, startcg; + + fs = ip->i_fs; + + if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) { + if (lbn < NDADDR + NINDIR(fs)) { + cg = ino_to_cg(fs, ip->i_number); + return (fs->fs_fpg * cg + fs->fs_frag); + } + + /* + * Find a cylinder with greater than average number of + * unused data blocks. + */ + if (indx == 0 || bap[indx - 1] == 0) + startcg = ino_to_cg(fs, ip->i_number) + + lbn / fs->fs_maxbpg; + else + startcg = dtog(fs, bap[indx - 1] + 1); + + startcg %= fs->fs_ncg; + avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg; + + for (cg = startcg; cg < fs->fs_ncg; cg++) + if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) + return (fs->fs_fpg * cg + fs->fs_frag); + + for (cg = 0; cg < startcg; cg++) + if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) + return (fs->fs_fpg * cg + fs->fs_frag); + + return (0); + } + + /* + * We always just try to lay things out contiguously. + */ + return (bap[indx - 1] + fs->fs_frag); +} +#endif /* FFS2 */ + +/* * Implement the cylinder overflow algorithm. * * The policy implemented by this algorithm is: diff --git a/sys/ufs/ffs/ffs_balloc.c b/sys/ufs/ffs/ffs_balloc.c index d0f58eb633a..ad63bc2533a 100644 --- a/sys/ufs/ffs/ffs_balloc.c +++ b/sys/ufs/ffs/ffs_balloc.c @@ -1,7 +1,16 @@ -/* $OpenBSD: ffs_balloc.c,v 1.25 2005/12/28 20:48:17 pedro Exp $ */ +/* $OpenBSD: ffs_balloc.c,v 1.26 2006/03/11 21:00:48 pedro Exp $ */ /* $NetBSD: ffs_balloc.c,v 1.3 1996/02/09 22:22:21 christos Exp $ */ /* + * Copyright (c) 2002 Networks Associates Technology, Inc. + * All rights reserved. + * + * This software was developed for the FreeBSD Project by Marshall + * Kirk McKusick and Network Associates Laboratories, the Security + * Research Division of Network Associates, Inc. under DARPA/SPAWAR + * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS + * research program. + * * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * @@ -50,13 +59,18 @@ #include <ufs/ffs/fs.h> #include <ufs/ffs/ffs_extern.h> +int ffs1_balloc(struct inode *, off_t, int, struct ucred *, int, struct buf **); +#ifdef FFS2 +int ffs2_balloc(struct inode *, off_t, int, struct ucred *, int, struct buf **); +#endif + /* * Balloc defines the structure of file system storage * by allocating the physical blocks on a device given * the inode and the logical block number in a file. */ int -ffs_balloc(struct inode *ip, off_t startoffset, int size, struct ucred *cred, +ffs1_balloc(struct inode *ip, off_t startoffset, int size, struct ucred *cred, int flags, struct buf **bpp) { daddr_t lbn; @@ -65,9 +79,9 @@ ffs_balloc(struct inode *ip, off_t startoffset, int size, struct ucred *cred, struct buf *bp, *nbp; struct vnode *vp; struct indir indirs[NIADDR + 2]; - daddr_t newb, *bap, pref; + ufs1_daddr_t newb, *bap, pref; int deallocated, osize, nsize, num, i, error; - daddr_t *allocib, *blkp, *allocblk, allociblk[NIADDR+1]; + ufs1_daddr_t *allocib, *blkp, *allocblk, allociblk[NIADDR+1]; int unwindidx = -1; vp = ITOV(ip); @@ -75,7 +89,7 @@ ffs_balloc(struct inode *ip, off_t startoffset, int size, struct ucred *cred, lbn = lblkno(fs, startoffset); size = blkoff(fs, startoffset) + size; if (size > fs->fs_bsize) - panic("ffs_balloc: blk too big"); + panic("ffs1_balloc: blk too big"); if (bpp != NULL) *bpp = NULL; if (lbn < 0) @@ -91,7 +105,7 @@ ffs_balloc(struct inode *ip, off_t startoffset, int size, struct ucred *cred, osize = blksize(fs, ip, nb); if (osize < fs->fs_bsize && osize > 0) { error = ffs_realloccg(ip, nb, - ffs_blkpref(ip, nb, (int)nb, &ip->i_ffs1_db[0]), + ffs1_blkpref(ip, nb, (int)nb, &ip->i_ffs1_db[0]), osize, (int)fs->fs_bsize, cred, bpp, &newb); if (error) return (error); @@ -162,7 +176,7 @@ ffs_balloc(struct inode *ip, off_t startoffset, int size, struct ucred *cred, * want, grow it. */ error = ffs_realloccg(ip, lbn, - ffs_blkpref(ip, lbn, (int)lbn, + ffs1_blkpref(ip, lbn, (int)lbn, &ip->i_ffs1_db[0]), osize, nsize, cred, bpp, &newb); if (error) @@ -183,7 +197,7 @@ ffs_balloc(struct inode *ip, off_t startoffset, int size, struct ucred *cred, else nsize = fs->fs_bsize; error = ffs_alloc(ip, lbn, - ffs_blkpref(ip, lbn, (int)lbn, &ip->i_ffs1_db[0]), + ffs1_blkpref(ip, lbn, (int)lbn, &ip->i_ffs1_db[0]), nsize, cred, &newb); if (error) return (error); @@ -210,7 +224,7 @@ ffs_balloc(struct inode *ip, off_t startoffset, int size, struct ucred *cred, return(error); #ifdef DIAGNOSTIC if (num < 1) - panic ("ffs_balloc: ufs_bmaparray returned indirect block"); + panic ("ffs1_balloc: ufs_bmaparray returned indirect block"); #endif /* * Fetch the first indirect block allocating if necessary. @@ -221,7 +235,7 @@ ffs_balloc(struct inode *ip, off_t startoffset, int size, struct ucred *cred, allocib = NULL; allocblk = allociblk; if (nb == 0) { - pref = ffs_blkpref(ip, lbn, 0, (daddr_t *)0); + pref = ffs1_blkpref(ip, lbn, 0, (daddr_t *)0); error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, cred, &newb); if (error) @@ -270,7 +284,7 @@ ffs_balloc(struct inode *ip, off_t startoffset, int size, struct ucred *cred, continue; } if (pref == 0) - pref = ffs_blkpref(ip, lbn, 0, (daddr_t *)0); + pref = ffs1_blkpref(ip, lbn, 0, (daddr_t *)0); error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, cred, &newb); if (error) { @@ -314,7 +328,7 @@ ffs_balloc(struct inode *ip, off_t startoffset, int size, struct ucred *cred, * Get the data block, allocating if necessary. */ if (nb == 0) { - pref = ffs_blkpref(ip, lbn, indirs[i].in_off, &bap[0]); + pref = ffs1_blkpref(ip, lbn, indirs[i].in_off, &bap[0]); error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, cred, &newb); if (error) { @@ -399,3 +413,457 @@ fail: return (error); } + +#ifdef FFS2 +int +ffs2_balloc(struct inode *ip, off_t off, int size, struct ucred *cred, + int flags, struct buf **bpp) +{ + daddr_t lbn, lastlbn, nb, newb, *blkp; + daddr_t pref, *allocblk, allociblk[NIADDR + 1]; + ufs2_daddr_t *bap, *allocib; + int deallocated, osize, nsize, num, i, error, unwindidx, r; + struct buf *bp, *nbp; + struct indir indirs[NIADDR + 2]; + struct fs *fs; + struct vnode *vp; + + vp = ITOV(ip); + fs = ip->i_fs; + unwindidx = 1; + + lbn = lblkno(fs, off); + size = blkoff(fs, off) + size; + + if (size > fs->fs_bsize) + panic("ffs2_balloc: block too big"); + + if (bpp != NULL) + *bpp = NULL; + + if (lbn < 0) + return (EFBIG); + + /* + * If the next write will extend the file into a new block, and the + * file is currently composed of a fragment, this fragment has to be + * extended to be a full block. + */ + lastlbn = lblkno(fs, ip->i_ffs2_size); + if (lastlbn < NDADDR && lastlbn < lbn) { + nb = lastlbn; + osize = blksize(fs, ip, nb); + if (osize < fs->fs_bsize && osize > 0) { + error = ffs_realloccg(ip, nb, ffs2_blkpref(ip, + lastlbn, nb, &ip->i_ffs2_db[0]), osize, + (int) fs->fs_bsize, cred, bpp, &newb); + if (error) + return (error); + + if (DOINGSOFTDEP(vp)) + softdep_setup_allocdirect(ip, nb, newb, + ip->i_ffs2_db[nb], fs->fs_bsize, osize, + bpp ? *bpp : NULL); + + ip->i_ffs2_size = lblktosize(fs, nb + 1); + uvm_vnp_setsize(vp, ip->i_ffs2_size); + ip->i_ffs2_db[nb] = newb; + ip->i_flag |= IN_CHANGE | IN_UPDATE; + + if (bpp) { + if (flags & B_SYNC) + bwrite(*bpp); + else + bawrite(*bpp); + } + } + } + + /* + * The first NDADDR blocks are direct. + */ + if (lbn < NDADDR) { + + nb = ip->i_ffs2_db[lbn]; + + if (nb != 0 && ip->i_ffs2_size >= lblktosize(fs, lbn + 1)) { + /* + * The direct block is already allocated and the file + * extends past this block, thus this must be a whole + * block. Just read it, if requested. + */ + if (bpp != NULL) { + error = bread(vp, lbn, fs->fs_bsize, NOCRED, + bpp); + if (error) { + brelse(*bpp); + return (error); + } + } + + return (0); + } + + if (nb != 0) { + /* + * Consider the need to allocate a fragment. + */ + osize = fragroundup(fs, blkoff(fs, ip->i_ffs2_size)); + nsize = fragroundup(fs, size); + + if (nsize <= osize) { + /* + * The existing block is already at least as + * big as we want. Just read it, if requested. + */ + if (bpp != NULL) { + error = bread(vp, lbn, osize, NOCRED, + bpp); + if (error) { + brelse(*bpp); + return (error); + } + } + + return (0); + } else { + /* + * The existing block is smaller than we want, + * grow it. + */ + error = ffs_realloccg(ip, lbn, + ffs2_blkpref(ip, lbn, (int) lbn, + &ip->i_ffs2_db[0]), osize, nsize, cred, + bpp, &newb); + if (error) + return (error); + + if (DOINGSOFTDEP(vp)) + softdep_setup_allocdirect(ip, lbn, + newb, nb, nsize, osize, + bpp ? *bpp : NULL); + } + } else { + /* + * The block was not previously allocated, allocate a + * new block or fragment. + */ + if (ip->i_ffs2_size < lblktosize(fs, lbn + 1)) + nsize = fragroundup(fs, size); + else + nsize = fs->fs_bsize; + + error = ffs_alloc(ip, lbn, ffs2_blkpref(ip, lbn, + (int) lbn, &ip->i_ffs2_db[0]), nsize, cred, &newb); + if (error) + return (error); + + if (bpp != NULL) { + bp = getblk(vp, lbn, nsize, 0, 0); + bp->b_blkno = fsbtodb(fs, newb); + if (flags & B_CLRBUF) + clrbuf(bp); + *bpp = bp; + } + + if (DOINGSOFTDEP(vp)) + softdep_setup_allocdirect(ip, lbn, newb, 0, + nsize, 0, bpp ? *bpp : NULL); + + ip->i_ffs2_db[lbn] = newb; + ip->i_flag |= IN_CHANGE | IN_UPDATE; + + return (0); + } + + } + + /* + * Determine the number of levels of indirection. + */ + pref = 0; + error = ufs_getlbns(vp, lbn, indirs, &num); + if (error) + return (error); + + /* + * Fetch the first indirect block allocating it necessary. + */ + --num; + nb = ip->i_ffs2_ib[indirs[0].in_off]; + allocib = NULL; + allocblk = allociblk; + + if (nb == 0) { + pref = ffs2_blkpref(ip, lbn, 0, NULL); + error = ffs_alloc(ip, lbn, pref, (int) fs->fs_bsize, cred, + &newb); + if (error) + goto fail; + + nb = newb; + *allocblk++ = nb; + bp = getblk(vp, indirs[1].in_lbn, fs->fs_bsize, 0, 0); + bp->b_blkno = fsbtodb(fs, nb); + clrbuf(bp); + + if (DOINGSOFTDEP(vp)) { + softdep_setup_allocdirect(ip, NDADDR + indirs[0].in_off, + newb, 0, fs->fs_bsize, 0, bp); + bdwrite(bp); + } else { + /* + * Write synchronously so that indirect blocks never + * point at garbage. + */ + error = bwrite(bp); + if (error) + goto fail; + } + + unwindidx = 0; + allocib = &ip->i_ffs2_ib[indirs[0].in_off]; + *allocib = nb; + ip->i_flag |= IN_CHANGE | IN_UPDATE; + } + + /* + * Fetch through the indirect blocks, allocating as necessary. + */ + for (i = 1;;) { + error = bread(vp, indirs[i].in_lbn, (int) fs->fs_bsize, + NOCRED, &bp); + if (error) { + brelse(bp); + goto fail; + } + + bap = (int64_t *) bp->b_data; + nb = bap[indirs[i].in_off]; + + if (i == num) + break; + + i++; + + if (nb != 0) { + brelse(bp); + continue; + } + + if (pref == 0) + pref = ffs2_blkpref(ip, lbn, 0, NULL); + + error = ffs_alloc(ip, lbn, pref, (int) fs->fs_bsize, cred, + &newb); + if (error) { + brelse(bp); + goto fail; + } + + nb = newb; + *allocblk++ = nb; + nbp = getblk(vp, indirs[i].in_lbn, fs->fs_bsize, 0, 0); + nbp->b_blkno = fsbtodb(fs, nb); + clrbuf(nbp); + + if (DOINGSOFTDEP(vp)) { + softdep_setup_allocindir_meta(nbp, ip, bp, + indirs[i - 1].in_off, nb); + bdwrite(nbp); + } else { + /* + * Write synchronously so that indirect blocks never + * point at garbage. + */ + error = bwrite(nbp); + if (error) { + brelse(bp); + goto fail; + } + } + + if (unwindidx < 0) + unwindidx = i - 1; + + bap[indirs[i - 1].in_off] = nb; + + /* + * If required, write synchronously, otherwise use delayed + * write. + */ + if (flags & B_SYNC) + bwrite(bp); + else + bdwrite(bp); + } + + /* + * Get the data block, allocating if necessary. + */ + if (nb == 0) { + pref = ffs2_blkpref(ip, lbn, indirs[num].in_off, &bap[0]); + + error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, cred, + &newb); + if (error) { + brelse(bp); + goto fail; + } + + nb = newb; + *allocblk++ = nb; + + if (bpp != NULL) { + nbp = getblk(vp, lbn, fs->fs_bsize, 0, 0); + nbp->b_blkno = fsbtodb(fs, nb); + if (flags & B_CLRBUF) + clrbuf(nbp); + *bpp = nbp; + } + + if (DOINGSOFTDEP(vp)) + softdep_setup_allocindir_page(ip, lbn, bp, + indirs[num].in_off, nb, 0, bpp ? *bpp : NULL); + + bap[indirs[num].in_off] = nb; + + if (allocib == NULL && unwindidx < 0) + unwindidx = i - 1; + + /* + * If required, write synchronously, otherwise use delayed + * write. + */ + if (flags & B_SYNC) + bwrite(bp); + else + bdwrite(bp); + + return (0); + } + + brelse(bp); + + if (bpp != NULL) { + if (flags & B_CLRBUF) { + error = bread(vp, lbn, (int)fs->fs_bsize, NOCRED, &nbp); + if (error) { + brelse(nbp); + goto fail; + } + } else { + nbp = getblk(vp, lbn, fs->fs_bsize, 0, 0); + nbp->b_blkno = fsbtodb(fs, nb); + clrbuf(nbp); + } + + *bpp = nbp; + } + + return (0); + +fail: + + /* + * If we have failed part way through block allocation, we have to + * deallocate any indirect blocks that we have allocated. + */ + if (unwindidx >= 0) { + /* + * First write out any buffers we've created to resolve their + * softdeps. This must be done in reverse order of creation so + * that we resolve the dependencies in one pass. + * Write the cylinder group buffers for these buffers too. + */ + for (i = num; i >= unwindidx; i--) { + if (i == 0) + break; + + bp = getblk(vp, indirs[i].in_lbn, (int) fs->fs_bsize, + 0, 0); + if (bp->b_flags & B_DELWRI) { + nb = fsbtodb(fs, cgtod(fs, dtog(fs, + dbtofsb(fs, bp->b_blkno)))); + bwrite(bp); + bp = getblk(ip->i_devvp, nb, + (int) fs->fs_cgsize, 0, 0); + if (bp->b_flags & B_DELWRI) + bwrite(bp); + else { + bp->b_flags |= B_INVAL; + brelse(bp); + } + } else { + bp->b_flags |= B_INVAL; + brelse(bp); + } + } + + if (DOINGSOFTDEP(vp) && unwindidx == 0) { + ip->i_flag |= IN_CHANGE | IN_UPDATE; + ffs_update(ip, NULL, NULL, MNT_WAIT); + } + + /* + * Now that any dependencies that we created have been + * resolved, we can undo the partial allocation. + */ + if (unwindidx == 0) { + *allocib = 0; + ip->i_flag |= IN_CHANGE | IN_UPDATE; + if (DOINGSOFTDEP(vp)) + ffs_update(ip, NULL, NULL, MNT_WAIT); + } else { + r = bread(vp, indirs[unwindidx].in_lbn, + (int) fs->fs_bsize, NOCRED, &bp); + if (r) + panic("ffs2_balloc: unwind failed"); + + bap = (int64_t *) bp->b_data; + bap[indirs[unwindidx].in_off] = 0; + bwrite(bp); + } + + for (i = unwindidx + 1; i <= num; i++) { + bp = getblk(vp, indirs[i].in_lbn, (int)fs->fs_bsize, 0, + 0); + bp->b_flags |= B_INVAL; + brelse(bp); + } + } + + for (deallocated = 0, blkp = allociblk; blkp < allocblk; blkp++) { + ffs_blkfree(ip, *blkp, fs->fs_bsize); + deallocated += fs->fs_bsize; + } + + if (deallocated) { + /* + * Restore user's disk quota because allocation failed. + */ + (void) ufs_quota_free_blocks(ip, btodb(deallocated), cred); + + ip->i_ffs2_blocks -= btodb(deallocated); + ip->i_flag |= IN_CHANGE | IN_UPDATE; + } + + return (error); +} +#endif /* FFS2 */ + +/* + * Balloc defines the structure of file system storage by allocating the + * physical blocks given the inode and the logical block number in a file. + */ +int +ffs_balloc(struct inode *ip, off_t off, int size, struct ucred *cred, + int flags, struct buf **bpp) +{ +#ifdef FFS2 + if (ip->i_fs->fs_magic == FS_UFS2_MAGIC) + return (ffs2_balloc(ip, off, size, cred, flags, bpp)); + else +#endif + return (ffs1_balloc(ip, off, size, cred, flags, bpp)); +} diff --git a/sys/ufs/ffs/ffs_extern.h b/sys/ufs/ffs/ffs_extern.h index 2645dfff7e7..933de638874 100644 --- a/sys/ufs/ffs/ffs_extern.h +++ b/sys/ufs/ffs/ffs_extern.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ffs_extern.h,v 1.26 2005/12/17 13:56:01 pedro Exp $ */ +/* $OpenBSD: ffs_extern.h,v 1.27 2006/03/11 21:00:48 pedro Exp $ */ /* $NetBSD: ffs_extern.h,v 1.4 1996/02/09 22:22:22 christos Exp $ */ /* @@ -106,7 +106,10 @@ int ffs_inode_alloc(struct inode *, mode_t, struct ucred *, struct vnode **); int ffs_inode_free(struct inode *, ino_t, mode_t); int ffs_freefile(struct inode *, ino_t, mode_t); -daddr_t ffs_blkpref(struct inode *, daddr_t, int, daddr_t *); +ufs1_daddr_t ffs1_blkpref(struct inode *, daddr_t, int, ufs1_daddr_t *); +#ifdef FFS2 +ufs2_daddr_t ffs2_blkpref(struct inode *, daddr_t, int, ufs2_daddr_t *); +#endif void ffs_blkfree(struct inode *, daddr_t, long); void ffs_clusteracct(struct fs *, struct cg *, daddr_t, int); |