@@ -20,6 +20,7 @@
#include <linux/fiemap.h>
#include <linux/security.h>
#include <asm/uaccess.h>
+#include <linux/writeback.h>
#include "gfs2.h"
#include "incore.h"
@@ -37,61 +38,92 @@
#include "super.h"
#include "glops.h"
-struct gfs2_skip_data {
+struct gfs2_match {
u64 no_addr;
- int skipped;
- int non_block;
+ struct gfs2_freeing_inode *freeing;
};
-static int iget_test(struct inode *inode, void *opaque)
+/* gfs2_match_inode - Inode matching function
+ * @inode: inode pointer
+ * @l: hash value (unused, since it may not be able to hold our no_addr)
+ * @opaque: points to a gfs2_freeing_inode structure
+ */
+static int gfs2_match_inode(struct inode *inode, unsigned long l, void *opaque)
{
- struct gfs2_inode *ip = GFS2_I(inode);
- struct gfs2_skip_data *data = opaque;
+ struct gfs2_match *match = opaque;
+ struct gfs2_freeing_inode *freeing = match->freeing;
- if (ip->i_no_addr == data->no_addr) {
- if (data->non_block &&
- inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE)) {
- data->skipped = 1;
- return 0;
- }
- return 1;
+ if (GFS2_I(inode)->i_no_addr != match->no_addr)
+ return 0;
+
+ spin_lock(&inode->i_lock);
+ if (inode->i_state & (I_FREEING|I_WILL_FREE)) {
+ freeing->wq = prepare_wait_on_freeing_inode(inode,
+ &freeing->bit_wait);
+ spin_unlock(&inode->i_lock);
+ return -1;
}
- return 0;
+ __iget(inode);
+ spin_unlock(&inode->i_lock);
+ return 1;
}
-static int iget_set(struct inode *inode, void *opaque)
+static int gfs2_test_inode(struct inode *inode, void *opaque)
{
- struct gfs2_inode *ip = GFS2_I(inode);
- struct gfs2_skip_data *data = opaque;
+ struct gfs2_match *match = opaque;
- if (data->skipped)
- return -ENOENT;
- inode->i_ino = (unsigned long)(data->no_addr);
- ip->i_no_addr = data->no_addr;
- return 0;
+ return GFS2_I(inode)->i_no_addr == match->no_addr;
}
struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr)
{
- unsigned long hash = (unsigned long)no_addr;
- struct gfs2_skip_data data;
+ struct gfs2_match match = {
+ .no_addr = no_addr,
+ };
- data.no_addr = no_addr;
- data.skipped = 0;
- data.non_block = 0;
- return ilookup5(sb, hash, iget_test, &data);
+ return ilookup5(sb, no_addr, gfs2_test_inode, &match);
}
static struct inode *gfs2_iget(struct super_block *sb, u64 no_addr,
int non_block)
{
- struct gfs2_skip_data data;
- unsigned long hash = (unsigned long)no_addr;
+ struct gfs2_freeing_inode freeing;
+ struct gfs2_match match = {
+ .no_addr = no_addr,
+ .freeing = &freeing,
+ };
+ struct inode *inode;
- data.no_addr = no_addr;
- data.skipped = 0;
- data.non_block = non_block;
- return iget5_locked(sb, hash, iget_test, iget_set, &data);
+ while (1) {
+ freeing.wq = NULL;
+ inode = find_inode_nowait(sb, no_addr,
+ gfs2_match_inode, &match);
+ if (inode) {
+ wait_on_inode(inode);
+ return inode;
+ }
+ if (freeing.wq) {
+ if (non_block) {
+ finish_wait(freeing.wq, &freeing.bit_wait.wait);
+ return ERR_PTR(-EAGAIN);
+ }
+ schedule();
+ finish_wait(freeing.wq, &freeing.bit_wait.wait);
+ continue;
+ }
+
+ inode = new_inode(sb);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+ inode->i_ino = no_addr;
+ GFS2_I(inode)->i_no_addr = no_addr;
+ if (insert_inode_locked4(inode, no_addr,
+ gfs2_test_inode, &match) < 0) {
+ iput(inode);
+ continue;
+ }
+ return inode;
+ }
}
/**
@@ -146,8 +178,8 @@ struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned int type,
int error;
inode = gfs2_iget(sb, no_addr, non_block);
- if (!inode)
- return ERR_PTR(-ENOMEM);
+ if (IS_ERR(inode))
+ return inode;
ip = GFS2_I(inode);
if (inode->i_state & I_NEW) {
@@ -93,6 +93,11 @@ err:
return -EIO;
}
+struct gfs2_freeing_inode {
+ wait_queue_head_t *wq;
+ struct wait_bit_queue bit_wait;
+};
+
extern struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned type,
u64 no_addr, u64 no_formal_ino,
int non_block);
@@ -388,6 +388,7 @@ void __iget(struct inode *inode)
{
atomic_inc(&inode->i_count);
}
+EXPORT_SYMBOL(__iget);
/*
* get additional reference to inode; caller must already hold one.