#include <linux/random.h>
#include <linux/delay.h>
+
+/* Supporting an async 'iget' - as required by the cleaner -
+ * is slightly non-trivial.
+ * iget*_locked will normally wait for any inode with one
+ * of the flags I_FREEING I_CLEAR I_WILL_FREE I_NEW
+ * to either be unhashed or has the flag cleared.
+ * We cannot afford that wait in the cleaner as we could deadlock.
+ * So we use iget5_locked and provide a test function that fails
+ * if it finds the inode with any of those flags set.
+ * If it does see the inode like that it clear the inum
+ * that is passed in (by reference) so that it knows to continue
+ * failing (for consistency) and so that the 'set' function
+ * we provide can know to fail the 'set'.
+ * The result of this is that if iget finds an inode it would
+ * have to wait on, the inum is cleared and NULL is returned.
+ * An unfortunate side effect is that an inode will be allocated
+ * and then destroyed to no avail.
+ * This is avoided by calling ilookup5 first. This also allows
+ * us to only allocate/load the data block if there really seems
+ * to be a need.
+ */
+#define NO_INO (~(ino_t)0)
+static int async_itest(struct inode *inode, void *data)
+{
+ ino_t *inump = data;
+ ino_t inum = *inump;
+
+ if (inum == NO_INO)
+ /* found and is freeing */
+ return 0;
+ if (inode->i_ino != inum)
+ return 0;
+ if (inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE|I_NEW)) {
+ *inump = NO_INO;
+ return 0;
+ }
+ return 1;
+}
+
+static int async_iset(struct inode *inode, void *data)
+{
+ ino_t *inump = data;
+ if (!*inump)
+ return -EBUSY;
+ inode->i_ino = *inump;
+ return 0;
+}
+
struct inode *
lafs_iget(struct super_block *sb, ino_t inum, int async)
{
/* find, and load if needed, this inum */
- struct inode *ino = iget_locked(sb, inum);
- struct datablock *b;
+ struct inode *ino = NULL;
+ struct datablock *b = NULL;
struct inode *inodefile;
- int err;
-
- if (!(ino->i_state & I_NEW)) {
- if (ino->i_mode)
- return ino;
- iput(ino);
- return ERR_PTR(-ENOENT);
- }
+ int err = 0;
- /* Need to load block 'inum' from an inode file...
- */
+ BUG_ON(inum == NO_INO);
if (sb->s_root)
inodefile = LAFSI(sb->s_root->d_inode)->filesys;
inodefile = fs->ss[0].root;
}
- b = lafs_get_block(inodefile, inum, NULL, GFP_KERNEL, MKREF(iget));
- if (async)
- err = lafs_read_block_async(b);
- else
- err = lafs_read_block(b);
+ if (async) {
+ /* We cannot afford to block on 'freeing_inode'
+ * So use iget5_locked and refuse to match such
+ * inodes.
+ * If the inode is 'freeing', inum gets set to NO_INO.
+ * ilookup5 is used first to avoid an unnecessary
+ * alloc/free if the inode is locked in some way.
+ */
+ while (!ino) {
+ ino_t inum2 = inum;
+ err = 0;
+ ino = ilookup5(sb, inum, async_itest, &inum2);
+ if (ino)
+ break;
+
+ if (inum2 == NO_INO)
+ err = -EAGAIN;
+
+ /* For async we will always want the dblock loaded,
+ * and we need to load it first as we cannot afford
+ * to fail -EAGAIN once we have an I_NEW inode.
+ */
+ if (!b)
+ b = lafs_get_block(inodefile, inum, NULL,
+ GFP_NOFS, MKREF(iget));
+ if (!b)
+ return ERR_PTR(-ENOMEM);
+
+ if (!err)
+ err = lafs_read_block_async(b);
+
+ if (!err) {
+ /* Have the block, so safe to iget */
+ inum2 = inum;
+ ino = iget5_locked(sb, inum,
+ async_itest, async_iset,
+ &inum2);
+ if (!ino) {
+ if (inum2 == NO_INO)
+ err = -EAGAIN;
+ else
+ err = -ENOMEM;
+ }
+ }
+ if (err) {
+ if (test_and_set_bit(B_Async, &b->b.flags)) {
+ putdref(b, MKREF(iget));
+ return ERR_PTR(err);
+ }
+ getdref(b, MKREF(async));
+ }
+ }
+ } else
+ ino = iget_locked(sb, inum);
+
+ if (!ino) {
+ putdref(b, MKREF(iget));
+ return ERR_PTR(-ENOMEM);
+ }
+
+ if (!(ino->i_state & I_NEW)) {
+ putdref(b, MKREF(iget));
+ if (ino->i_mode)
+ return ino;
+ iput(ino);
+ return ERR_PTR(-ENOENT);
+ }
+
+ /* Need to load block 'inum' from an inode file...
+ */
+ if (!b) {
+ b = lafs_get_block(inodefile, inum, NULL, GFP_KERNEL, MKREF(iget));
+ if (!b)
+ err = -ENOMEM;
+ else
+ err = lafs_read_block(b);
+ }
if (err)
goto err;
printk("lafs_import_inode failed %d\n", err);
goto err;
}
- putdref(b, MKREF(iget));
unlock_new_inode(ino);
+out:
+ if (b && test_and_clear_bit(B_Async, &b->b.flags)) {
+ struct fs *fs = sb->s_fs_info;
+ putdref(b, MKREF(async));
+ lafs_wake_cleaner(fs);
+ }
+ putdref(b, MKREF(iget));
return ino;
err:
ino->i_nlink = 0;
unlock_new_inode(ino);
- putdref(b, MKREF(iget));
iput(ino);
- return ERR_PTR(err);
+ ino = ERR_PTR(err);
+ goto out;
}
struct inode *
return 0;
}
+static void lafs_drop_inode(struct inode *inode)
+{
+ struct fs *fs = fs_from_inode(inode);
+ struct datablock *db = NULL;
+
+ /* This lock that we now hold on the inode could prevent
+ * the cleaner from getting the inode. So after
+ * the complete the drop we might need to wake the cleaner.
+ */
+
+ spin_lock(&inode->i_data.private_lock);
+ if (LAFSI(inode)->dblock)
+ db = getdref_locked(LAFSI(inode)->dblock, MKREF(drop));
+ spin_unlock(&inode->i_data.private_lock);
+
+ generic_drop_inode(inode);
+ if (db && test_bit(B_Async, &db->b.flags))
+ lafs_wake_cleaner(fs);
+ if (db)
+ putdref(db, MKREF(drop));
+}
+
static struct super_operations lafs_sops = {
.alloc_inode = lafs_alloc_inode,
.destroy_inode = lafs_destroy_inode, /* Inverse of 'alloc_inode' */
.dirty_inode = lafs_dirty_inode,
.write_inode = lafs_write_inode,
/* put_inode ?? */
+ .drop_inode = lafs_drop_inode,
/* drop_inode ?? */ /* default will call delete or forget
* where 'forget' flushes and clears
*/