From: Andreas Gruenbacher <agruenba@xxxxxxxxxx> Change gfs2_iget to use non-blocking lookups internally. This will be used to prevent glock deadlocks. (Requires exporting __iget from fs/inode.c.) Signed-off-by: Andreas Gruenbacher <agruenba@xxxxxxxxxx> --- fs/gfs2/inode.c | 104 ++++++++++++++++++++++++++++++++++++-------------------- fs/gfs2/inode.h | 5 +++ fs/inode.c | 1 + 3 files changed, 74 insertions(+), 36 deletions(-) diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 5dff5da..d1dde39 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -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) { diff --git a/fs/gfs2/inode.h b/fs/gfs2/inode.h index 22c27a8..4863513 100644 --- a/fs/gfs2/inode.h +++ b/fs/gfs2/inode.h @@ -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); diff --git a/fs/inode.c b/fs/inode.c index e00fec4..c18ace5 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -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. -- 2.5.5 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html