b[i].b.chain = NULL;
b[i].my_inode = NULL;
- if (fs_from_inode(ino)->orphans == ino)
- atomic_set(&b[i].pincnt, 0);
/* FIXME what else needs to be initialised? */
}
u32 hash;
u8 index;
int chainoffset;
- struct orphan_info oi;
};
-static void dir_add_orphan(struct datablock *b)
-{
- struct inode *dir = b->b.inode;
- /* i_mutex protects dirorphans */
- LAFS_BUG(!mutex_is_locked(&dir->i_mutex), &b->b);
- if (list_empty(&b->orphans)) {
- getdref(b, MKREF(dirorphan));
- list_add(&b->orphans, &LAFSI(dir)->md.file.dirorphans);
- if (list_empty(&LAFSI(dir)->orphans)) {
- struct fs *fs = fs_from_inode(dir);
- spin_lock(&fs->lock);
-// list_add(&LAFSI(dir)->orphans, &fs->pending_orphans);
- spin_unlock(&fs->lock);
- }
- }
-}
/*............................................................................
* Creating an entry in a directory.
* This is split into pre_create and commit_create
/* FIXME should I check if the orphanage is needed
* before committing this block to it?
*/
- err = lafs_orphan_prepare(fs, &doh->oi);
+ err = lafs_make_orphan(fs, doh->dirent_block);
return err;
}
* If we end up leaving that first entry, make me an orphan so
* the we can check if the chain continues in a previous block.
*/
- if (((doh->hash+1) & 0x7fff) == doh->dirent_block->b.fileaddr) {
+ if (((doh->hash+1) & 0x7fff) == doh->dirent_block->b.fileaddr)
unmap_dblock(doh->dirent_block, buf);
- lafs_orphan_commit(&doh->oi);
- } else if (lafs_dir_find(buf, bits, seed, doh->hash+1, &ignore) == 0) {
+ else if (lafs_dir_find(buf, bits, seed, doh->hash+1, &ignore) == 0) {
/* This is the end of a chain, clean up */
u8 firstpiece;
u8 piece;
NULL)->target == 0);
unmap_dblock(doh->dirent_block, buf);
-
- if (piece == firstpiece && de.target == 0)
- lafs_orphan_commit(&doh->oi);
- else
- lafs_orphan_abort(&doh->oi);
- } else {
+ } else
unmap_dblock(doh->dirent_block, buf);
- lafs_orphan_abort(&doh->oi);
- }
-
clear_bit(B_PinPending, &doh->dirent_block->b.flags);
lafs_dirty_dblock(doh->dirent_block);
putdref(doh->dirent_block, MKREF(dir_blk));
err = lafs_pin_dblock(doh->dirent_block);
if (err)
return err;
-
- err = lafs_orphan_pin(&doh->oi, doh->dirent_block, 1);
- if (err)
- return err;
-
- dir_add_orphan(doh->dirent_block);
return 0;
}
!IS_ERR(doh->dirent_block)) {
clear_bit(B_PinPending, &doh->dirent_block->b.flags);
putdref(doh->dirent_block, MKREF(dir_blk));
-
- lafs_orphan_abort(&doh->oi);
}
}
struct inode *inode = de->d_inode;
int last = (inode->i_nlink == 1);
struct dirop_handle doh;
- struct orphan_info oi;
struct update_handle uh;
struct datablock *inodb;
int err;
err = dir_delete_prepare(fs, dir, de->d_name.name, de->d_name.len,
&doh);
err = dir_log_prepare(&uh, fs, &de->d_name) ?: err;
- if (last)
- err = lafs_orphan_prepare(fs, &oi) ?: err;
inodb = lafs_inode_dblock(inode, 0, MKREF(inode_update));
if (IS_ERR(inodb))
err = PTR_ERR(inodb);
+ if (last && !err)
+ err = lafs_make_orphan(fs, inodb);
if (err)
goto abort;
retry:
err = dir_delete_pin(&doh);
err = err ?: lafs_cluster_update_pin(&uh);
err = err ?: lafs_pin_dblock(inodb);
- if (err == 0 && last)
- err = lafs_orphan_pin(&oi, inodb, 1);
if (err == -EAGAIN) {
lafs_checkpoint_unlock_wait(fs);
goto retry; /* FIXME should I not unlock ?? */
dir_log_commit(&uh, fs, dir, &de->d_name, inode->i_ino,
DIROP_UNLINK, NULL);
dir_delete_commit(&doh, fs, dir, de->d_name.name, de->d_name.len);
- if (last)
- lafs_orphan_commit(&oi);
lafs_checkpoint_unlock(fs);
lafs_dirty_inode(inode);
lafs_inode_checkpin(inode);
clear_bit(B_PinPending, &inodb->b.flags);
lafs_checkpoint_unlock(fs);
abort:
- if (last)
- lafs_orphan_abort(&oi);
if (!IS_ERR(inodb))
putdref(inodb, MKREF(inode_update));
lafs_cluster_update_abort(&uh);
struct fs *fs = fs_from_inode(dir);
struct inode *inode = de->d_inode;
struct dirop_handle doh;
- struct orphan_info oi;
struct update_handle uh;
struct datablock *inodb;
int err;
err = dir_delete_prepare(fs, dir, de->d_name.name, de->d_name.len,
&doh);
err = dir_log_prepare(&uh, fs, &de->d_name) ?: err;
- err = lafs_orphan_prepare(fs, &oi) ?: err;
inodb = lafs_inode_dblock(inode, 0, MKREF(inode_update));
if (IS_ERR(inodb))
err = PTR_ERR(inodb);
+ if (!err)
+ err = lafs_make_orphan(fs, inodb);
if (err)
goto abort;
retry:
err = dir_delete_pin(&doh);
err = err ?: lafs_cluster_update_pin(&uh);
err = err ?: lafs_pin_dblock(inodb);
- err = err ?: lafs_orphan_pin(&oi, inodb, 1);
if (err == -EAGAIN) {
lafs_checkpoint_unlock_wait(fs);
goto retry; /* FIXME should I not unlock ?? */
dir_log_commit(&uh, fs, dir, &de->d_name, inode->i_ino,
DIROP_UNLINK, NULL);
dir_delete_commit(&doh, fs, dir, de->d_name.name, de->d_name.len);
- lafs_orphan_commit(&oi);
lafs_dirty_inode(inode);
clear_bit(B_PinPending, &inodb->b.flags);
putdref(inodb, MKREF(inode_update));
lafs_checkpoint_unlock(fs);
clear_bit(B_PinPending, &inodb->b.flags);
abort:
- lafs_orphan_abort(&oi);
if (!IS_ERR(inodb))
putdref(inodb, MKREF(inode_update));
lafs_cluster_update_abort(&uh);
struct dirop_handle old_doh, new_doh;
struct update_handle old_uh, new_uh;
- struct orphan_info oi;
int last = (new_inode && new_inode->i_nlink == 1);
u32 renhandle;
int err;
if (new_inode) {
/* unlink object, update name */
- if (last)
- err = lafs_orphan_prepare(fs, &oi) ?: err;
err = dir_update_prepare(fs, new_dir,
new_dentry->d_name.name,
new_dentry->d_name.len,
newdb = lafs_inode_dblock(new_inode, 0, MKREF(inode_update));
if (IS_ERR(newdb))
err = PTR_ERR(newdb);
+ if (last && !err)
+ err = lafs_make_orphan(fs, newdb);
} else
/* create new link */
err = dir_create_prepare(fs, new_dir,
err = err ?: lafs_pin_dblock(olddb);
if (new_inode) {
err = err ?: lafs_pin_dblock(newdb);
- if (last)
- err = err ?:
- lafs_orphan_pin(&oi, newdb, 1);
err = err ?: dir_update_pin(&new_doh);
} else
err = err ?: dir_create_pin(&new_doh);
new_inode ? DIROP_REN_OLD_TARGET : DIROP_REN_NEW_TARGET,
&renhandle);
if (new_inode) {
- if (last)
- lafs_orphan_commit(&oi);
dir_update_commit(fs, old_inode->i_ino,
mode_to_dt(old_inode->i_mode),
&new_doh);
if (!IS_ERR(olddb))
putdref(olddb, MKREF(inode_update));
if (new_inode) {
- if (last)
- lafs_orphan_abort(&oi);
dir_update_abort(&new_doh);
if (!IS_ERR(newdb))
putdref(newdb, MKREF(inode_new));
if (lafs_dir_find(buf2, bits, seed, (hash-1) & MaxDirHash,
&piece) &&
lafs_dir_extract(buf2, bits, &de, piece, NULL)->target == 0)
- /* FIXME not recorded in orphan file...
- * I should move the orphan status from b to b2 */
- dir_add_orphan(b2);
+ lafs_make_orphan(fs, b2);
unmap_dblock(b2, buf2);
putdref(b2, MKREF(dir_orphan));
buf = map_dblock(b);
return err;
}
-void lafs_dir_apply_orphan(struct datablock *b, int type)
-{
- dir_add_orphan(b);
-}
/*--------------------------------------------------------------------
* Finally the read-only operations
*/
struct inode *filesys = LAFSI(dir)->filesys;
struct inode *ino;
struct datablock *b;
- struct orphan_info oi;
struct inode_map_new_info imni;
struct update_handle ui;
int err;
err = inode_map_new_prepare(fs, inum, filesys, &imni);
- err = lafs_orphan_prepare(fs, &oi) ?: err;
err = lafs_cluster_update_prepare(&ui, fs, sizeof(struct la_inode))
?: err;
+ if (err == 0)
+ err = lafs_make_orphan(fs, imni.ib);
if (err)
goto abort;
retry:
lafs_checkpoint_lock(fs);
err = inode_map_new_pin(&imni);
- err = err ?: lafs_orphan_pin(&oi, imni.ib, 1);
err = err ?: lafs_pin_dblock(imni.ib);
if (err == -EAGAIN) {
lafs_iounlock_block(&b->b);
inode_map_new_commit(&imni);
- lafs_orphan_commit(&oi);
ino = lafs_iget(dir->i_sb, b->b.fileaddr, 0);
if (IS_ERR(ino)) {
lafs_cluster_update_abort(&ui);
lafs_checkpoint_unlock(fs);
err = -ENOSPC;
abort:
- lafs_orphan_abort(&oi);
inode_map_new_abort(&imni);
lafs_cluster_update_abort(&ui);
return ERR_PTR(err);
* has been cleaned up.
* So just start the background truncate
*/
- struct orphan_info oi;
struct fs *fs = fs_from_inode(ino);
struct datablock *db = lafs_inode_dblock(ino, 0, MKREF(trunc));
wait_event(fs->trunc_wait,
!test_bit(I_Trunc, &LAFSI(ino)->iflags));
- lafs_orphan_prepare(fs, &oi);
- lafs_orphan_pin(&oi, db, 1); /* FIXME need a retry loop here !! */
- lafs_orphan_commit(&oi);
+ /* FIXME there is nothing I can do with an error here */
+ lafs_make_orphan(fs, db);
set_bit(I_Trunc, &LAFSI(ino)->iflags);
LAFSI(ino)->trunc_next = (i_size_read(ino) +
int type;
};
-struct orphan_info {
- int reserved;
- struct fs *fs;
- struct datablock *ob;
-};
-
struct update_handle {
struct fs *fs;
int reserved;
unsigned long lafs_do_checkpoint(struct fs *fs);
struct block *lafs_get_flushable(struct fs *fs, int phase);
-int lafs_orphan_prepare(struct fs *fs, struct orphan_info *oi);
-int lafs_orphan_pin(struct orphan_info *oi, struct datablock *b, int n);
-void lafs_orphan_commit(struct orphan_info *oi);
-void lafs_orphan_abort(struct orphan_info *oi);
+int lafs_make_orphan(struct fs *fs, struct datablock *db);
void lafs_orphan_release(struct fs *fs, struct datablock *b);
-void lafs_orphan_drop(struct fs *fs, struct datablock *b);
unsigned long lafs_run_orphans(struct fs *fs);
/* Segment.c */
}
#endif
-int lafs_orphan_prepare(struct fs *fs, struct orphan_info *oi)
+struct orphan_info {
+ struct fs *fs;
+ struct datablock *ob;
+};
+/* Before locking the checkpoint, we need to reserve space to
+ * store the orphan record, and make sure we hold a reference
+ * to the block.
+ */
+static int orphan_prepare(struct fs *fs, struct orphan_info *oi)
{
struct orphan_md *om = &LAFSI(fs->orphans)->md.orphan;
u32 bnum;
MKREF(orphan_reserve));
if (b) {
err = lafs_read_block(b);
- if (err) {
+ if (err)
putdref(b, MKREF(orphan_reserve));
- oi->reserved = 0;
- } else {
+ else
om->reserved++;
- oi->reserved = 1;
- }
- } else {
- oi->reserved = 0;
+ } else
err = -ENOMEM;
- }
mutex_unlock(&fs->orphans->i_mutex);
return err;
}
-int lafs_orphan_pin(struct orphan_info *oi, struct datablock *b, int n)
+static int orphan_pin(struct orphan_info *oi, struct datablock *b)
{
struct fs *fs = oi->fs;
struct orphan_md *om = &LAFSI(fs->orphans)->md.orphan;
u32 bnum;
struct datablock *ob;
- if (test_bit(B_Orphan, &b->b.flags))
- /* FIXME I need to make sure it stays an orphan... */
- return 0;
mutex_lock_nested(&fs->orphans->i_mutex, I_MUTEX_QUOTA);
slot = om->nextfree;
bnum = slot >> (fs->prime_sb->s_blocksize_bits-4);
lafs_phase_wait(&ob->b);
oi->ob = ob;
- atomic_inc(&ob->pincnt);
err = lafs_pin_dblock(ob);
if (err) {
- atomic_dec(&ob->pincnt);
mutex_unlock(&fs->orphans->i_mutex);
putdref(ob, MKREF(orphan));
oi->ob = NULL;
return err;
}
+ /* Committed to being an orphan now */
b->orphan_slot = slot;
+ spin_lock(&fs->lock);
set_bit(B_Orphan, &b->b.flags);
+ list_add_tail(&b->orphans, &fs->pending_orphans);
+ spin_unlock(&fs->lock);
dprintk("%p->orphan_slot=%d (%lu,%lu,%lu) %s\n", b, b->orphan_slot,
LAFSI(b->b.inode)->filesys->i_ino,
b->b.inode->i_ino, b->b.fileaddr, strblk(&b->b));
- oi->reserved = 0;
om->nextfree++;
om->reserved--;
putdref(ob, MKREF(orphan_reserve));
or = map_dblock(ob);
ent = slot - (bnum << (fs->prime_sb->s_blocksize_bits-4));
- or[ent].type = cpu_to_le32(n);
+ or[ent].type = cpu_to_le32(1);
or[ent].filesys = cpu_to_le32(LAFSI(b->b.inode)->filesys->i_ino);
or[ent].inum = cpu_to_le32(b->b.inode->i_ino);
or[ent].addr = cpu_to_le32(b->b.fileaddr);
unmap_dblock(ob, or);
+ lafs_dirty_dblock(oi->ob);
+ clear_bit(B_PinPending, &ob->b.flags);
mutex_unlock(&fs->orphans->i_mutex);
return 0;
}
-void lafs_orphan_commit(struct orphan_info *oi)
+/* We failed to allocate space to write the update to the orphan
+ * block so we need to revert the reservation done in orphan_pin.
+ * Note that we don't 'unmark' the block from being an orphan.
+ * We let regular orphan processing find that out and release it.
+ * This avoids races.
+ */
+static void orphan_abort(struct orphan_info *oi)
{
- /* All the interesting work was done in _pin.
- * Just mark the block dirty and clean up
- */
- /* If block was already an orphan, we didn't do any work in 'pin',
- * So there is not much to do here
+ struct fs *fs = oi->fs;
+ u32 bnum;
+ struct datablock *b;
+ struct orphan_md *om = &LAFSI(fs->orphans)->md.orphan;
+ mutex_lock_nested(&fs->orphans->i_mutex, I_MUTEX_QUOTA);
+ om->reserved--;
+ bnum = (om->nextfree + om->reserved) >>
+ (fs->prime_sb->s_blocksize_bits-4);
+ b = lafs_get_block(fs->orphans, bnum, NULL, GFP_KERNEL,
+ MKREF(orphanx));
+ /* Note that we now own two references to this block, one
+ * we just got and one we are trying to get rid of
*/
- if (oi->ob) {
- LAFS_BUG(!test_bit(B_PinPending, &oi->ob->b.flags),
- &oi->ob->b);
- lafs_dirty_dblock(oi->ob);
- if (atomic_dec_and_test(&oi->ob->pincnt))
- /* FIXME this is racy */
- clear_bit(B_PinPending, &oi->ob->b.flags);
- /* We don't 'put' the reference. It is now held through
- * the B_Orphan flag and the orphan_slot field.
- */
- oi->ob = NULL;
- }
- /* Otherwise we might still hold a reservation which
- * we can drop by calling orphan_abort
+ putdref(b, MKREF(orphanx));
+
+ /* If this was the last block in the file,
+ * we need to punch a hole
*/
- lafs_orphan_abort(oi);
+ if (((om->nextfree + om->reserved + 1) >>
+ (fs->prime_sb->s_blocksize_bits-4))
+ != bnum)
+ lafs_erase_dblock(b);
+
+ putdref(b, MKREF(orphan_reserve));
+ mutex_unlock(&fs->orphans->i_mutex);
}
-/* Note that we don't 'unmark' the block from being an orphan.
- * We let regular orphan processing find that out and release it.
- * This avoids races.
+/* When we are about to make a change which might make a block
+ * into an 'orphan', we call lafs_make_orphan to record the fact.
+ * This sets B_Orphan, put the block on the fs->pending_orphans list
+ * and stores the fs/inode/block number in the orphan file.
+ * The handle_orphans routine for the relevant file type must ensure
+ * that orphan handling doesn't happen until the block is genuinely
+ * an orphan.
+ * For directories, i_mutex is used for this.
+ * For inodes, B_IOLock is used.
*/
-void lafs_orphan_abort(struct orphan_info *oi)
+int lafs_make_orphan(struct fs *fs, struct datablock *db)
{
- struct fs *fs = oi->fs;
- if (oi->reserved) {
- u32 bnum;
- struct datablock *b;
- struct orphan_md *om = &LAFSI(fs->orphans)->md.orphan;
- mutex_lock_nested(&fs->orphans->i_mutex, I_MUTEX_QUOTA);
- om->reserved--;
- bnum = (om->nextfree + om->reserved) >>
- (fs->prime_sb->s_blocksize_bits-4);
- b = lafs_get_block(fs->orphans, bnum, NULL, GFP_KERNEL,
- MKREF(orphanx));
- /* Note that we now own two references to this block, one
- * we just got and one we are trying to get rid of
- */
- putdref(b, MKREF(orphanx));
+ struct orphan_info oi;
+ int err;
- /* If this was the last block in the file,
- * we need to punch a hole
- */
- if (((om->nextfree + om->reserved + 1) >>
- (fs->prime_sb->s_blocksize_bits-4))
- != bnum)
- lafs_erase_dblock(b);
+ if (test_bit(B_Orphan, &db->b.flags))
+ return 0;
- putdref(b, MKREF(orphan_reserve));
- mutex_unlock(&fs->orphans->i_mutex);
- }
- /* FIXME maybe discard this altogether */
- if (oi->ob) {
- if (atomic_dec_and_test(&oi->ob->pincnt))
- /* FIXME this is racy */
- clear_bit(B_PinPending, &oi->ob->b.flags);
- //putdref(oi->ob, MKREF(orphan));
- oi->ob = NULL;
+ err = orphan_prepare(fs, &oi);
+ if (err)
+ return err;
+ retry:
+ lafs_checkpoint_lock(fs);
+ err = orphan_pin(&oi, db);
+ if (err == -EAGAIN) {
+ lafs_checkpoint_unlock_wait(fs);
+ goto retry;
}
+ if (err)
+ orphan_abort(&oi);
+ /* there is no 'commit' - 'pin' did all the work */
+ lafs_checkpoint_unlock(fs);
+ return 0;
}
/*
return;
mutex_lock_nested(&fs->orphans->i_mutex, I_MUTEX_QUOTA);
- if (!test_bit(B_Orphan, &b->b.flags)) {
- mutex_unlock(&fs->orphans->i_mutex);
- return;
- }
ob1 = lafs_get_block(fs->orphans, b->orphan_slot >> shift, NULL,
GFP_KERNEL, MKREF(orphan_release));
}
om->nextfree--;
om->reserved++;
+ spin_lock(&fs->lock);
clear_bit(B_Orphan, &b->b.flags);
+ list_del_init(&b->orphans);
+ spin_unlock(&fs->lock);
/* Now drop the reservation we just synthesised */
om->reserved--;
*/
union {
struct inode *my_inode; /* only valid for block holding an inode */
- atomic_t pincnt; /* only valid for blocks in the orphan file */
};
};
struct indexblock {