1
0
Files
irix-657m-src/irix/kern/fs/efs/efs_dalloc.c
T
2022-09-29 17:59:04 +03:00

877 lines
20 KiB
C

/**************************************************************************
* *
* Copyright (C) 1988, Silicon Graphics, Inc. *
* *
* These coded instructions, statements, and computer programs contain *
* unpublished proprietary information of Silicon Graphics, Inc., and *
* are protected by Federal copyright law. They may not be disclosed *
* to third parties or copied or duplicated in any form, in whole or *
* in part, without the prior written consent of Silicon Graphics, Inc. *
* *
**************************************************************************/
/*
* inode/data allocation for the efs.
*/
#ident "$Revision: 3.60 $"
#include <sys/types.h>
#include <sys/buf.h>
#include <sys/debug.h>
#include <sys/errno.h>
#include <sys/immu.h> /* for NDPP */
#include <sys/param.h>
#include <sys/quota.h>
#include <sys/systm.h>
#include <sys/kthread.h>
#include <sys/vnode.h>
#include <sys/cmn_err.h>
#ifdef _SHAREII
#include <sys/shareIIstubs.h>
#endif /* _SHAREII */
#include "efs_bitmap.h"
#include "efs_fs.h"
#include "efs_inode.h"
#include "efs_sb.h"
extern int efs_findfree(struct efs *, int, int, struct bmapval *);
static void efs_btoss(struct inode *, extent *, int);
/*
* efs_alloc:
* - allocate len worth of space on fs for inode ip
* - if we can't find len's worth, then return in ex
* the amount we did find.
* - fill in ex with the new information
* - return 0 on success, errno on failure.
* - musthave means that we cannot compromise desiredlen
*/
int
efs_alloc(
struct inode *ip,
extent *ex,
register int desiredlen,
int musthave,
struct bmapval *wex)
{
register struct cg *cg;
struct cg *lastcg;
struct efs *fs;
register int i;
register int firstbit;
register int bitlen;
struct bmapval save;
int firstbn, lastbn;
int wrapped;
int ncgs;
int quota_len;
int error;
int spaceleft;
#ifdef _SHAREII
int share_len;
#endif /* _SHAREII */
ASSERT(desiredlen > 0);
#ifdef _SHAREII
/*
* Check with share and update user's lnode. We may not
* actually get the blocks, but we'll fix that up later.
* Normally share does the check and allocation separately,
* but this leads to a race-condition, and makes us always
* call it twice, rather than just on failure.
*
* If the allocation fails, SHlimitDisk
* returns how much space is available for allocation
* which is then used for another try.
*
* Note that the amount of disk the user is allowed to
* have is the minimum of what share allows and what quotas
* allow, if they are both active. Note that desiredlen is
* reduced to the Share limit if necessary, which is then
* passed to the quota system for validation (and possible
* further limiting).
*/
if (SHR_ACTIVE)
{
unsigned long remain;
while(error = SHR_LIMITDISK(itovfs(ip),
ip->i_uid,
(u_long)desiredlen,
DEV_BSIZE,
LI_ALLOC | LI_ENFORCE | LI_UPDATE,
&remain,
NULL))
{
if (!musthave && remain != 0)
{
/*
* Make this a multiple of page size if possible.
*/
desiredlen = remain;
if (desiredlen > NDPP)
desiredlen -= desiredlen % NDPP;
ASSERT(desiredlen <= remain);
} else
goto out_of_quota;
}
share_len = desiredlen;
spaceleft = remain;
}
#endif /* _SHAREII */
while (error = qt_chkdq(ip, (long)desiredlen, 0, &spaceleft)) {
/*
* A smaller allocation may succeed.
*/
ASSERT(spaceleft < desiredlen);
if (!musthave && spaceleft) {
/*
* Make this a multiple of page size if possible.
*/
while (desiredlen > spaceleft)
desiredlen -= NDPP;
if (desiredlen <= 0)
desiredlen = spaceleft;
} else
goto out_of_quota;
}
quota_len = desiredlen;
fs = mtoefs(ip->i_mount);
mutex_lock(fs->fs_lock, PINOD);
if (fs->fs_tfree <= 0)
goto no_more_room;
lastcg = &fs->fs_cgs[fs->fs_ncg];
/*
* If we've got to have it, look at all cgs.
* Otherwise, scan half the file
* system or 600,000 blocks, whichever is less.
*/
if (musthave) {
ncgs = fs->fs_ncg;
} else {
ncgs = (fs->fs_ncg + 1) / 2;
i = 600000 / fs->fs_cgfsize;
ncgs = MIN(i, ncgs);
}
/*
* If we're having trouble finding things, then
* go right to the rotor rather than starting near
* our inode.
*/
if (fs->fs_diskfull) {
firstbn = 0;
lastbn = 0;
wrapped = 0;
save.bn = 0;
save.length = 0;
save.offset = desiredlen;
goto rotor;
}
/*
* For the first cg, ignore blocks preceding the last
* extent's blocks.
*/
if (ip->i_numextents > 0) {
extent *lex = &ip->i_extents[ip->i_numextents-1];
firstbit = lex->ex_bn;
cg = &fs->fs_cgs[EFS_BBTOCG(fs, firstbit)];
/*
* Have to do this *after* cg calculation so it
* doesn't overflow into nonexistent cg when
* extent maps the last blocks of the fs.
*/
firstbit += lex->ex_length;
if (firstbit < cg->cg_firstdfree)
firstbit = cg->cg_firstdfree;
if (firstbit >= cg->cg_firstbn + fs->fs_cgfsize) {
if (++cg >= lastcg)
cg = &fs->fs_cgs[0];
firstbit = cg->cg_firstdfree;
}
if (cg >= lastcg) {
printf("botch: fs %x cg %x lastcg %x fb %x lex %x",
fs, cg, lastcg, firstbit, lex);
}
ASSERT(cg < lastcg);
} else {
cg = &fs->fs_cgs[EFS_ITOCG(fs, ip->i_number)];
firstbit = cg->cg_firstdfree;
ASSERT(cg < lastcg);
}
firstbn = firstbit;
save.bn = 0;
save.length = 0;
save.offset = desiredlen;
/*
* In the degenerate case of a fs with one cylinder group,
* or the musthave case, we want the start cylinder group
* to be searched from cg_firstdfree also and not based on the
* lex->ex_bn since none of the free blocks may be between
* lex->ex_bn and (cg_firstbn+fs_cgfsize).
*/
i = ncgs + 1;
while (--i >= 0) {
if (cg->cg_dfree) {
bitlen = cg->cg_firstbn + fs->fs_cgfsize - firstbit;
if (bitlen > fs->fs_cgfsize - fs->fs_cgisize) {
printf("botch: fs %x cg %x firstbit %x",
fs, cg, firstbit);
}
ASSERT(bitlen <= fs->fs_cgfsize - fs->fs_cgisize);
if (efs_findfree(fs, firstbit, bitlen, &save)) {
firstbit = save.bn;
ASSERT(save.length >= desiredlen);
goto found;
}
}
/*
* When cg is full, firstdfree it set to firstbn of NEXT cg.
* So set lastbn to firstdbn+cgfsize so there won't ever
* be an accidental match in ``wrapped'' calculation.
*/
lastbn = cg->cg_firstdbn + fs->fs_cgfsize;
if (++cg >= lastcg)
cg = &fs->fs_cgs[0];
firstbit = cg->cg_firstdfree;
};
ASSERT(!musthave || ncgs == fs->fs_ncg);
/*
* If we've looked at all the cgs and we found at least something,
* then go with what we've got without looking further.
*/
if (ncgs == fs->fs_ncg)
goto searched_all;
/*
* Don't bother to look at cgs that were examined
* during the first loop.
*/
if (lastbn > firstbn)
wrapped = 0;
else {
wrapped = 1;
i = lastbn;
lastbn = firstbn;
firstbn = i;
}
rotor:
cg = fs->fs_cgrotor;
firstbit = cg->cg_firstdfree;
i = fs->fs_ncg;
while (--i >= 0) {
/*
* Don't bother to look at cgs that were examined
* during the first loop.
*/
if (wrapped) {
if (firstbit < firstbn || firstbit >= lastbn)
goto nextcg;
} else {
if (firstbit < lastbn && firstbit >= firstbn)
goto nextcg;
}
if (cg->cg_dfree) {
bitlen = cg->cg_firstbn + fs->fs_cgfsize - firstbit;
ASSERT(bitlen <= fs->fs_cgfsize - fs->fs_cgisize);
if (efs_findfree(fs, firstbit, bitlen, &save)) {
firstbit = save.bn;
ASSERT(save.length >= desiredlen);
/*
* Set the rotor to where we found
* what we need.
*/
fs->fs_cgrotor = cg;
goto found;
}
/*
* Stop if we've searched ncgs cylinder groups
* and have found anything.
*/
if (--ncgs <= 0 && save.length)
break;
}
nextcg:
if (++cg >= lastcg)
cg = &fs->fs_cgs[0];
firstbit = cg->cg_firstdfree;
};
searched_all:
/*
* If we're here we know we did not find as much as
* we were looking for. Set fs_diskfull so the fs
* will take whatever it can get for a while. This should
* speed up allocations in a highly fragmented fs.
*/
if (fs->fs_diskfull == 0)
fs->fs_diskfull = lbolt;
if (musthave || save.length == 0)
goto no_more_room;
/*
* Found a piece to use, but it isn't the right length.
* Truncate desiredlen so the new block will map up to a page
* boundary, if possible. We have to assume that the caller
* is requesting enough blocks to map to a page boundary.
*/
ASSERT(save.length < desiredlen);
while (desiredlen > save.length)
desiredlen -= NDPP;
if (desiredlen <= 0)
desiredlen = save.length;
firstbit = save.bn;
cg = &fs->fs_cgs[EFS_BBTOCG(fs, firstbit)];
fs->fs_cgrotor = cg;
found:
ASSERT((firstbit - fs->fs_firstcg) % fs->fs_cgfsize >= fs->fs_cgisize);
ASSERT(desiredlen == efs_tstfree(fs, firstbit, desiredlen));
efs_bitalloc(fs, firstbit, desiredlen);
if (firstbit == cg->cg_firstdfree)
cg->cg_firstdfree += desiredlen;
cg->cg_dfree -= desiredlen;
ASSERT(cg->cg_dfree >= 0);
fs->fs_tfree -= desiredlen;
fs->fs_fmod = 1;
ASSERT(fs->fs_dirty == EFS_ACTIVEDIRT || \
fs->fs_ncg != 1 || \
fs->fs_tfree == cg->cg_dfree);
mutex_unlock(fs->fs_lock);
if ((doexlist_trash & 1) && (firstbit == 0 || desiredlen == 0)) {
prdev("efs_alloc: zero length or block I=%d",
ip->i_dev, ip->i_number);
cmn_err(CE_PANIC, "efs_alloc: exlist trash");
}
ex->ex_bn = firstbit;
ex->ex_length = desiredlen;
/*
* Return info if caller is interested.
*/
if (wex) {
wex->bn = firstbit;
wex->length = desiredlen;
}
#ifdef _SHAREII
/*
* We've already committed the change to the lnode at the start,
* but if we ended up allocating less than that we give it back
* to the lnode for further allocation.
*/
if (SHR_ACTIVE && (share_len - desiredlen) != 0)
{
SHR_LIMITDISK(itovfs(ip),
ip->i_uid,
(u_long)(share_len - desiredlen),
DEV_BSIZE,
LI_UPDATE | LI_FREE,
NULL,
NULL);
}
#endif /* _SHAREII */
if ((quota_len - desiredlen) != 0)
(void) qt_chkdq(ip, (long)-(quota_len - desiredlen), 0, NULL);
return (0);
/*
* Out of space. Thats the way the partition crumbles.
*/
no_more_room:
(void) qt_chkdq(ip, (long)(-desiredlen), 0, NULL);
#ifdef _SHAREII
/*
* We relinquish all the disk previously claimed so the user
* can fill up another partition.
*/
SHR_LIMITDISK(itovfs(ip), ip->i_uid, (u_long)share_len,
DEV_BSIZE, LI_FREE|LI_UPDATE, NULL, NULL);
#endif /* _SHAREII */
error = ENOSPC;
mutex_unlock(fs->fs_lock);
out_of_quota:
return error;
}
/*
* efs_move:
* - reallocate ``ex->ex_length + add'' worth of space on fs
* for inode ip
* - if we can't find room in the same cylinder group as ex's,
* return 0, otherwise return 1
* - copy data from old backing store to new and purge old buffer
*/
int
efs_move(
register struct inode *ip,
register extent *ex,
register int add)
{
register struct cg *cg;
struct efs *fs;
register int firstbit;
register int bitlen;
register buf_t *obp, *nbp;
register int want;
struct bmapval save;
ASSERT(itov(ip)->v_type != VREG);
if (ex->ex_length + add > EFS_MAXEXTENTLEN ||
#ifdef _SHAREII
/*
* Check with share that this is OK, and claim the space
* from the user's lnode. If we don't end up using it all
* later we'll give it back.
*/
SHR_LIMITDISK(itovfs(ip),
ip->i_uid,
(u_long)add,
DEV_BSIZE,
LI_ALLOC|LI_ENFORCE|LI_UPDATE,
NULL,
NULL)
||
#endif /* _SHAREII */
qt_chkdq(ip, (long)add, 0, (int *)NULL)
)
return 0;
fs = mtoefs(ip->i_mount);
cg = &fs->fs_cgs[EFS_BBTOCG(fs, ex->ex_bn)];
/*
* No reason to grab filesystem lock when just looking,
* since the superblock layout isn't going to change.
* If we miss cg_dfree increasing, tough.
*/
if (cg->cg_dfree < (want = ex->ex_length + add))
goto quota_out;
save.offset = want;
mutex_lock(fs->fs_lock, PINOD);
firstbit = cg->cg_firstdfree;
bitlen = cg->cg_firstbn + fs->fs_cgfsize - firstbit;
ASSERT(bitlen <= fs->fs_cgfsize - fs->fs_cgisize);
if (!(efs_findfree(fs, firstbit, bitlen, &save))) {
mutex_unlock(fs->fs_lock);
goto quota_out;
}
firstbit = save.bn;
ASSERT(save.length >= want);
#ifdef EFSDEBUG
if (efs_badextent(fs, ex, ip)) {
mutex_unlock(fs->fs_lock);
goto quota_out;
}
#endif
efs_bitalloc(fs, firstbit, want);
if (firstbit == cg->cg_firstdfree)
cg->cg_firstdfree += want;
cg->cg_dfree -= want;
ASSERT(cg->cg_dfree >= 0);
fs->fs_tfree -= want;
fs->fs_fmod = 1;
ASSERT(fs->fs_dirty == EFS_ACTIVEDIRT || \
fs->fs_ncg != 1 || \
fs->fs_tfree == cg->cg_dfree);
mutex_unlock(fs->fs_lock);
obp = bread(ip->i_dev, ex->ex_bn, ex->ex_length);
nbp = getblk(ip->i_dev, firstbit, want);
bcopy(obp->b_un.b_addr, nbp->b_un.b_addr, BBTOB(ex->ex_length));
obp->b_flags |= B_STALE;
obp->b_flags &= ~B_DELWRI;
brelse(obp);
bwrite(nbp);
ip->i_flags |= IUPD | ICHG | ISYN;
efs_iupdat(ip);
/*
* Free ex's old blocks.
*/
mutex_lock(fs->fs_lock, PINOD);
if (cg->cg_firstdfree > ex->ex_bn)
cg->cg_firstdfree = ex->ex_bn;
fs->fs_tfree += ex->ex_length; /* update total free */
cg->cg_dfree += ex->ex_length;
fs->fs_fmod = 1;
efs_bitfree(fs, (int) ex->ex_bn, (int) ex->ex_length);
ASSERT(fs->fs_dirty == EFS_ACTIVEDIRT || \
fs->fs_ncg != 1 || \
fs->fs_tfree == cg->cg_dfree);
mutex_unlock(fs->fs_lock);
if ((doexlist_trash & 1) && (firstbit == 0 || want == 0)) {
prdev("efs_alloc: zero length or block I=%d",
ip->i_dev, ip->i_number);
cmn_err(CE_PANIC, "efs_move: exlist trash");
}
ex->ex_bn = firstbit;
ex->ex_length = want;
return 1;
quota_out:
#ifdef _SHAREII
/*
* We didn't use the disk claimed from the user's lnode before, so
* we'll give it back.
*/
SHR_LIMITDISK(itovfs(ip), ip->i_uid, (u_long)add, DEV_BSIZE, LI_UPDATE|LI_FREE, NULL, NULL);
#endif /* _SHAREII */
(void) qt_chkdq(ip, (long)-add, 0, NULL);
return 0;
}
#ifdef notdef
/*
* Exchange ex's blocks for some in ``better position''.
*/
efs_realloc(
register struct efs *fs,
register int desiredlen,
register int stopbn)
{
register struct cg *cg;
register int length;
register int firstbit;
register int bitlen;
cg = &fs->fs_cgs[EFS_BBTOCG(fs, stopbn)];
mutex_lock(fs->fs_lock, PINOD);
ASSERT(stopbn >= fs->fs_firstcg && stopbn >= cg->cg_firstdbn);
if (cg->cg_dfree <= desiredlen)
goto out;
firstbit = cg->cg_firstdfree;
if ((bitlen = stopbn - firstbit) <= 0)
goto out;
ASSERT(bitlen <= fs->fs_cgfsize - fs->fs_cgisize);
while (bitlen) {
length = efs_tstfree(fs, firstbit, bitlen);
if (length < 0) /* Bitmap I/O error */
break;
if (length >= desiredlen) {
ASSERT((firstbit - fs->fs_firstcg) % fs->fs_cgfsize >= \
fs->fs_cgisize);
efs_bitalloc(fs, firstbit, desiredlen);
if (firstbit == cg->cg_firstdfree)
cg->cg_firstdfree += desiredlen;
cg->cg_dfree -= desiredlen;
ASSERT(cg->cg_dfree >= 0);
fs->fs_tfree -= desiredlen;
fs->fs_fmod = 1;
ASSERT(fs->fs_dirty == EFS_ACTIVEDIRT || \
fs->fs_ncg != 1 || \
fs->fs_tfree == cg->cg_dfree);
mutex_unlock(fs->fs_lock);
return(firstbit);
}
firstbit += length;
bitlen -= length;
if (bitlen == 0)
break;
length = efs_tstalloc(fs, firstbit, bitlen);
if (length < 0) /* Bitmap I/O error */
break;
firstbit += length;
bitlen -= length;
}
/*
* No chunk available.
*/
out:
mutex_unlock(fs->fs_lock);
return(0);
}
#endif
/*
* efs_grow:
* - attempt to extend the given extent attached to the given inode
*/
int
efs_grow(
register struct inode *ip,
register extent *ex,
int growlen,
struct bmapval *wex)
{
register int firstbit;
register struct cg *cg;
register struct efs *fs;
register efs_daddr_t bn;
register int exlen;
register int len = growlen;
long quota_len;
fs = mtoefs(ip->i_mount);
#ifdef EFSDEBUG
mutex_lock(fs->fs_lock, PINOD);
if (efs_badextent(fs, ex, ip)) {
mutex_unlock(fs->fs_lock);
return (0);
}
mutex_unlock(fs->fs_lock);
#endif
/*
* Ensure that we don't extend across a cylinder group
* nor that we attempt to make an extent too large.
*/
bn = ex->ex_bn;
exlen = ex->ex_length;
cg = &fs->fs_cgs[EFS_BBTOCG(fs, bn)];
firstbit = bn + exlen;
if (firstbit + len >= cg->cg_firstbn + fs->fs_cgfsize)
len = cg->cg_firstbn + fs->fs_cgfsize - firstbit;
if (exlen + len > EFS_MAXEXTENTLEN)
len = EFS_MAXEXTENTLEN - exlen;
if (len == 0)
return (0);
quota_len = len; /* can we grow by len bb's */
#ifdef _SHAREII
/*
* Check with ShareII that this growth won't exceed the user's
* disk limit. This also allocates the disk from the user's
* lnode. If it's not used because the allocation ultimately
* fails, we'll give it back later.
*/
if (SHR_LIMITDISK(itovfs(ip),
ip->i_uid,
(u_long)quota_len,
DEV_BSIZE,
LI_ALLOC|LI_UPDATE|LI_ENFORCE,
NULL,
NULL))
return 0;
#endif /* _SHAREII */
if (qt_chkdq(ip, (long)(quota_len), 0, NULL))
return(0);
/*
* Scan bitmap starting at bb just following last extent, counting
* the number of free bb's directly connected to the last extent.
* Stop when either len is exhausted or an allocated bb is found.
*/
mutex_lock(fs->fs_lock, PINOD);
len = efs_tstfree(fs, firstbit, len);
/* Bitmap I/O error */
if (len <= 0) {
len = 0;
goto out;
}
/*
* If wex is passed, caller is growing a regular file and is
* attemping to grow extent to logical block boundary. If we
* can't grow to a logical block boundary, grow to a page boundary.
*/
if (wex)
while (growlen > len)
growlen -= NDPP;
if (growlen > 0)
len = growlen;
else {
len = 0;
goto out;
}
ASSERT(len > 0);
efs_bitalloc(fs, firstbit, len);
fs->fs_tfree -= len;
fs->fs_fmod = 1;
ex->ex_length += len;
cg->cg_dfree -= len;
if (firstbit == cg->cg_firstdfree)
cg->cg_firstdfree += len;
ASSERT(cg->cg_dfree >= 0);
ASSERT(fs->fs_dirty == EFS_ACTIVEDIRT || \
fs->fs_ncg != 1 || \
fs->fs_tfree == cg->cg_dfree);
/*
* Pass back block numbers if the caller is interested.
*/
if (wex) {
wex->bn = bn + exlen;
wex->length = len;
}
out:
mutex_unlock(fs->fs_lock);
/*
* ## of bb's we thought we were going to use, but are not due to
* boundary and availability conditions.
*/
quota_len -= len;
if (quota_len)
{
#ifdef _SHAREII
/*
* We ended up not using the allocation above, so we'll
* give it back.
*/
SHR_LIMITDISK(itovfs(ip),
ip->i_uid,
(u_long)quota_len,
DEV_BSIZE,
LI_FREE|LI_UPDATE,
NULL,
NULL);
#endif /* _SHAREII */
(void)qt_chkdq(ip, (long)-quota_len, 0, NULL);
}
return(len);
}
/*
* efs_free:
* - free the given extent
*/
void
efs_free(
register struct inode *ip,
register extent *ex,
register int tosslen)
{
register struct efs *fs;
register struct cg *cg;
register efs_daddr_t bn;
register int len;
fs = itoefs(ip);
#ifdef EFSDEBUG
/* Check and see if the filesystem is corrupted. If it is
* or if the extent is somehow messed up, throw it away.
*/
mutex_lock(fs->fs_lock, PINOD);
if (efs_badextent(fs, ex, ip)) {
mutex_unlock(fs->fs_lock);
return;
}
mutex_unlock(fs->fs_lock);
#endif
/* Must toss buffer blocks before we free up the disk blocks.
*/
if (tosslen)
efs_btoss(ip, ex, tosslen);
len = ex->ex_length;
bn = ex->ex_bn;
mutex_lock(fs->fs_lock, PINOD);
cg = &fs->fs_cgs[EFS_BBTOCG(fs, bn)];
cg->cg_dfree += len;
if (cg->cg_firstdfree > bn)
cg->cg_firstdfree = bn;
fs->fs_tfree += len; /* update total free */
fs->fs_fmod = 1;
/*
* If space is tight, indicate that some has been freed up.
*/
if (fs->fs_diskfull) {
fs->fs_freedblock = 1;
}
#ifdef _SHAREII
/*
* Update lnode with free blocks
*/
SHR_LIMITDISK(itovfs(ip),
ip->i_uid,
(u_long)len,
DEV_BSIZE,
LI_FREE|LI_UPDATE,
NULL,
NULL);
#endif /* _SHAREII */
(void) qt_chkdq(ip, (int)-len, 0, NULL);
efs_bitfree(fs, (int) bn, (int) len);
mutex_unlock(fs->fs_lock);
}
/*
* Remove individual buffers from buffer cache.
*
* Any blocks for which efs_btoss is called map the entire
* extent into one buffer. Tosslen is the size of the
* extent before being trimmed/removed. If it is not the same
* as the ``tossed'' extent size, the extent is being trimmed,
* not totally removed, so push out dirty data before
* invalidating the buffer.
*/
static void
efs_btoss(register struct inode *ip, register extent *ex, int oldlen)
{
register buf_t *bp;
register dev_t dev = ip->i_dev;
register long bn;
register int len;
ASSERT(ex->ex_length);
bn = ex->ex_bn;
len = ex->ex_length;
bp = incore(dev, bn, oldlen, 1);
if (bp) {
bp->b_flags |= B_STALE;
bp->b_flags &= ~B_ASYNC;
if (oldlen != len) {
if (bp->b_flags & B_DELWRI) {
bwrite(bp);
return;
}
}
bp->b_flags &= ~B_DELWRI;
brelse(bp);
}
}