summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/ufs/ffs/ffs_alloc.c330
-rw-r--r--sys/ufs/ffs/ffs_balloc.c492
-rw-r--r--sys/ufs/ffs/ffs_extern.h7
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);