b = (struct datablock *)p->private;
b += index & ((1<<bits)-1);
+ /* spinlock is just to sync with lafs_refile */
+ spin_lock(&ino->i_data.private_lock);
getdref_locked(b, REF);
+ spin_unlock(&ino->i_data.private_lock);
if (unlock) {
unlock_page(p);
* wait for any pending IO to complete (so page can be freed)
*/
for (i = 0; i < (1<<bits); i++) {
+ spin_lock(&ino->i_data.private_lock);
+ (void)getdref_locked(&b[i], MKREF(inval));
+ spin_unlock(&ino->i_data.private_lock);
+
if (b_start >= offset &&
test_and_clear_bit(B_Async, &b[i].b.flags))
putdref(&b[i], MKREF(Async));
LAFS_BUG(offset == 0 &&
test_bit(B_IOLock, &b[i].b.flags),
&b[i].b);
+ putdref(&b[i], MKREF(inval));
}
}
if (offset == 0) {
{
int err = 0;
struct fs *fs = fs_from_inode(b->inode);
+ int in_emergency;
if (!test_bit(B_PhysValid, &b->flags))
b->physaddr = 0;
b);
else
err = lafs_setparent(dblk(b));
+ if (err)
+ return err;
/* If there is already a physaddr, or the data is
* stored in the inode, then we aren't really allocating
*/
alloc_type = ReleaseSpace;
+ /* Important to test EmergencyClean before we
+ * called in to lafs_space_alloc to avoid races:
+ * space becomes available and EmergencyClean are
+ * set at the same time (strange, but true).
+ */
+ in_emergency = test_bit(EmergencyClean, &fs->fsstate);
/* Allocate space in the filesystem */
- err = err ?: lafs_prealloc(b, alloc_type);
+ err = lafs_prealloc(b, alloc_type);
+ if (err) {
+ if (alloc_type == NewSpace) {
+ if (in_emergency)
+ return -ENOSPC;
+ return -EAGAIN;
+ }
+ if (alloc_type == ReleaseSpace)
+ return -EAGAIN;
+ LAFS_BUG(1, b);
+ }
/* Allocate space in the file (and quota set) */
- if (err == 0 && b->physaddr == 0 &&
+ if (b->physaddr == 0 &&
!test_bit(B_Index, &b->flags) &&
!test_and_set_bit(B_Prealloc, &b->flags)) {
err = lafs_summary_allocate(fs, b->inode, 1);
if (err)
clear_bit(B_Prealloc, &b->flags);
}
+ if (err) {
+ LAFS_BUG(alloc_type == AccountSpace, b);
+ return err;
+ }
/* Having reserved the block, we need to get a segref,
* which will involve reserving those blocks too.
- * However we never get a segref for Root.
+ * However we never get a segref for Root or any
+ * InoIdx block.
*/
+ if (test_bit(B_InoIdx, &b->flags))
+ b = &LAFSI(b->inode)->dblock->b;
+
while (err == 0
&& !test_bit(B_Root, &b->flags)
&& !test_bit(B_SegRef, &b->flags))
err = lafs_seg_ref_block(b, 0);
- if (err == 0)
- return 0;
- if (alloc_type == NewSpace) {
- if (test_bit(EmergencyClean, &fs->fsstate))
- return -ENOSPC;
- return -EAGAIN;
- }
- if (alloc_type == ReleaseSpace)
- return -EAGAIN;
- LAFS_BUG(1, b);
+ return err;
}
int
*/
int err;
struct fs *fs = fs_from_inode(b->b.inode);
- struct block *blk;
- struct inode *ino = NULL;
-
- /* We don't pin a datablock of an inode if there is an
- * InoIdx block. We pin the InoIdx block instead.
- * They might both be pinned at the same time, but
- * only when the index block has swapped phase and the
- * data block is waiting to be written.
- */
- if ((ino = rcu_my_inode(b)) != NULL &&
- LAFSI(ino)->iblock) {
- struct indexblock *ib = lafs_make_iblock(ino, ADOPT, SYNC,
- MKREF(pindb));
- if (IS_ERR(ib))
- blk = getref(&b->b, MKREF(pindb));
- else
- blk = &ib->b;
- } else
- blk = getref(&b->b, MKREF(pindb));
- rcu_iput(ino);
LAFS_BUG(!test_bit(B_PinPending, &b->b.flags), &b->b);
if (LAFSI(b->b.inode)->type != TypeSegmentMap) {
* been written and we want to flip it before it
* can be dirtied.
*/
- if (blk == &b->b &&
- test_bit(B_Pinned, &b->b.flags) &&
+ if (test_bit(B_Pinned, &b->b.flags) &&
!!test_bit(B_Phase1, &b->b.flags) != fs->phase) {
clear_bit(B_PinPending, &b->b.flags);
lafs_refile(&b->b, 0);
lafs_iounlock_block(&b->b);
}
- err = lafs_reserve_block(blk, alloc_type);
+ err = lafs_reserve_block(&b->b, alloc_type);
- if (err) {
- putref(blk, MKREF(pindb));
+ if (err)
return err;
- }
- lafs_pin_block(blk);
- putref(blk, MKREF(pindb));
+ lafs_pin_block(&b->b);
return 0;
}
* block lives in.
* Need private_lock to be allowed to dereference ->iblock
* though if b was dirty we shouldn't.... FIXME.
+ * We need to hold the ref to idb for the getiref_locked_needsync to
+ * be safe.
*/
struct indexblock *ib;
- spin_lock(&b->b.inode->i_data.private_lock);
+ struct datablock *idb = lafs_inode_dblock(b->b.inode, SYNC, MKREF(erase));
+ if (IS_ERR(idb)) {
+ /* not much we can do here */
+ BUG();
+ goto skip;
+ }
+ spin_lock(&lafs_hash_lock);
ib = LAFSI(b->b.inode)->iblock;
if (ib)
- getiref_locked(ib, MKREF(erasedblock));
- spin_unlock(&b->b.inode->i_data.private_lock);
+ getiref_locked_needsync(ib, MKREF(erasedblock));
+ spin_unlock(&lafs_hash_lock);
+ sync_ref(&ib->b);
+ putdref(idb, MKREF(erase));
if (ib) {
lafs_iolock_written(&ib->b);
if (ib->depth == 0) {
LAFS_BUG(LAFSI(b->b.inode)->depth !=
ib->depth, &b->b);
+ ib->depth = 1;
+ LAFSI(b->b.inode)->depth = 1;
lafs_clear_index(ib);
clear_bit(B_PhysValid, &b->b.flags);
+ clear_bit(B_SegRef, &b->b.flags); /* Just in case */
}
lafs_iounlock_block(&ib->b);
putiref(ib, MKREF(erasedblock));
}
+ skip:;
}
if (LAFSI(b->b.inode)->type == TypeInodeFile) {
* leaf list, so we must remove it.
* However it is IOLocked so it might not be on the leaf list.
*/
- int onlru = 0;
LAFS_BUG(test_bit(B_Writeback, &b->b.flags), &b->b);
if (!list_empty(&b->b.lru)) {
- onlru = 1;
list_del_init(&b->b.lru);
}
if (!test_bit(B_Root, &b->b.flags))
spin_unlock(&fs->lock);
if (!test_bit(B_Root, &b->b.flags))
lafs_refile(&b->b.parent->b, 0);
- if (onlru)
- putiref(b, MKREF(leaf));
} else
spin_unlock(&fs->lock);
}
void
-lafs_dirty_iblock(struct indexblock *b)
+lafs_dirty_iblock(struct indexblock *b, int want_realloc)
{
/* Note, only need to set the phase if locked.
* Then no-one may change it while in phase transition.
*/
LAFS_BUG(!test_bit(B_Pinned, &b->b.flags), &b->b);
+ LAFS_BUG(!test_bit(B_Valid, &b->b.flags) && b->depth > 0, &b->b);
+
+ if (want_realloc) {
+ /* Try to make for Realloc instead. If we cannot get the
+ * credits, fall back on Dirty
+ */
+ struct fs *fs = fs_from_inode(b->b.inode);
+ if (!test_bit(B_Realloc, &b->b.flags)) {
+ /* I cannot use B_Credit to fill B_Realloc as that
+ * might still be needed for B_Dirty.
+ * So if we cannot allocated a new credit,
+ * just set the block as 'dirty' now.
+ */
+ if (lafs_space_alloc(fs, 1, CleanSpace) == 1) {
+ if (test_and_set_bit(B_Realloc, &b->b.flags))
+ lafs_space_return(fs, 1);
+ } else
+ goto dirty;
+ }
+ if (!test_bit(B_UnincCredit, &b->b.flags)) {
+ /* Ditto for UnincCredit */
+ if (lafs_space_alloc(fs, 1, CleanSpace) == 1) {
+ if (test_and_set_bit(B_UnincCredit, &b->b.flags))
+ lafs_space_return(fs, 1);
+ } else
+ goto dirty;
+ }
+ return;
+ }
+dirty:
if (!test_and_set_bit(B_Dirty, &b->b.flags)) {
if (!test_and_clear_bit(B_Credit, &b->b.flags)) {
printk(KERN_ERR "Why have I no credits?\n");