Commit 80ea09a0 authored by Miklos Szeredi's avatar Miklos Szeredi Committed by Miklos Szeredi

vfs: factor out inode_insert5()

Split out common helper for race free insertion of an already allocated
inode into the cache.  Use this from iget5_locked() and
insert_inode_locked4().  Make iget5_locked() use new_inode()/iput() instead
of alloc_inode()/destroy_inode() directly.

Also export to modules for use by filesystems which want to preallocate an
inode before file/directory creation.
Signed-off-by: default avatarAmir Goldstein <amir73il@gmail.com>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent b148cba4
...@@ -1003,8 +1003,8 @@ void unlock_two_nondirectories(struct inode *inode1, struct inode *inode2) ...@@ -1003,8 +1003,8 @@ void unlock_two_nondirectories(struct inode *inode1, struct inode *inode2)
EXPORT_SYMBOL(unlock_two_nondirectories); EXPORT_SYMBOL(unlock_two_nondirectories);
/** /**
* iget5_locked - obtain an inode from a mounted file system * inode_insert5 - obtain an inode from a mounted file system
* @sb: super block of file system * @inode: pre-allocated inode to use for insert to cache
* @hashval: hash value (usually inode number) to get * @hashval: hash value (usually inode number) to get
* @test: callback used for comparisons between inodes * @test: callback used for comparisons between inodes
* @set: callback used to initialize a new struct inode * @set: callback used to initialize a new struct inode
...@@ -1012,80 +1012,96 @@ EXPORT_SYMBOL(unlock_two_nondirectories); ...@@ -1012,80 +1012,96 @@ EXPORT_SYMBOL(unlock_two_nondirectories);
* *
* Search for the inode specified by @hashval and @data in the inode cache, * Search for the inode specified by @hashval and @data in the inode cache,
* and if present it is return it with an increased reference count. This is * and if present it is return it with an increased reference count. This is
* a generalized version of iget_locked() for file systems where the inode * a variant of iget5_locked() for callers that don't want to fail on memory
* number is not sufficient for unique identification of an inode. * allocation of inode.
* *
* If the inode is not in cache, allocate a new inode and return it locked, * If the inode is not in cache, insert the pre-allocated inode to cache and
* hashed, and with the I_NEW flag set. The file system gets to fill it in * return it locked, hashed, and with the I_NEW flag set. The file system gets
* before unlocking it via unlock_new_inode(). * to fill it in before unlocking it via unlock_new_inode().
* *
* Note both @test and @set are called with the inode_hash_lock held, so can't * Note both @test and @set are called with the inode_hash_lock held, so can't
* sleep. * sleep.
*/ */
struct inode *iget5_locked(struct super_block *sb, unsigned long hashval, struct inode *inode_insert5(struct inode *inode, unsigned long hashval,
int (*test)(struct inode *, void *), int (*test)(struct inode *, void *),
int (*set)(struct inode *, void *), void *data) int (*set)(struct inode *, void *), void *data)
{ {
struct hlist_head *head = inode_hashtable + hash(sb, hashval); struct hlist_head *head = inode_hashtable + hash(inode->i_sb, hashval);
struct inode *inode; struct inode *old;
again: again:
spin_lock(&inode_hash_lock); spin_lock(&inode_hash_lock);
inode = find_inode(sb, head, test, data); old = find_inode(inode->i_sb, head, test, data);
if (unlikely(old)) {
/*
* Uhhuh, somebody else created the same inode under us.
* Use the old inode instead of the preallocated one.
*/
spin_unlock(&inode_hash_lock); spin_unlock(&inode_hash_lock);
wait_on_inode(old);
if (inode) { if (unlikely(inode_unhashed(old))) {
wait_on_inode(inode); iput(old);
if (unlikely(inode_unhashed(inode))) {
iput(inode);
goto again; goto again;
} }
return inode; return old;
} }
inode = alloc_inode(sb); if (set && unlikely(set(inode, data))) {
if (inode) { inode = NULL;
struct inode *old; goto unlock;
}
spin_lock(&inode_hash_lock);
/* We released the lock, so.. */
old = find_inode(sb, head, test, data);
if (!old) {
if (set(inode, data))
goto set_failed;
/*
* Return the locked inode with I_NEW set, the
* caller is responsible for filling in the contents
*/
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
inode->i_state = I_NEW; inode->i_state |= I_NEW;
hlist_add_head(&inode->i_hash, head); hlist_add_head(&inode->i_hash, head);
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
inode_sb_list_add(inode); unlock:
spin_unlock(&inode_hash_lock); spin_unlock(&inode_hash_lock);
/* Return the locked inode with I_NEW set, the
* caller is responsible for filling in the contents
*/
return inode; return inode;
} }
EXPORT_SYMBOL(inode_insert5);
/* /**
* Uhhuh, somebody else created the same inode under * iget5_locked - obtain an inode from a mounted file system
* us. Use the old inode instead of the one we just * @sb: super block of file system
* allocated. * @hashval: hash value (usually inode number) to get
* @test: callback used for comparisons between inodes
* @set: callback used to initialize a new struct inode
* @data: opaque data pointer to pass to @test and @set
*
* Search for the inode specified by @hashval and @data in the inode cache,
* and if present it is return it with an increased reference count. This is
* a generalized version of iget_locked() for file systems where the inode
* number is not sufficient for unique identification of an inode.
*
* If the inode is not in cache, allocate a new inode and return it locked,
* hashed, and with the I_NEW flag set. The file system gets to fill it in
* before unlocking it via unlock_new_inode().
*
* Note both @test and @set are called with the inode_hash_lock held, so can't
* sleep.
*/ */
spin_unlock(&inode_hash_lock); struct inode *iget5_locked(struct super_block *sb, unsigned long hashval,
destroy_inode(inode); int (*test)(struct inode *, void *),
inode = old; int (*set)(struct inode *, void *), void *data)
wait_on_inode(inode); {
if (unlikely(inode_unhashed(inode))) { struct inode *inode = ilookup5(sb, hashval, test, data);
iput(inode);
goto again; if (!inode) {
struct inode *new = new_inode(sb);
if (new) {
inode = inode_insert5(new, hashval, test, set, data);
if (unlikely(inode != new))
iput(new);
} }
} }
return inode; return inode;
set_failed:
spin_unlock(&inode_hash_lock);
destroy_inode(inode);
return NULL;
} }
EXPORT_SYMBOL(iget5_locked); EXPORT_SYMBOL(iget5_locked);
...@@ -1426,43 +1442,13 @@ EXPORT_SYMBOL(insert_inode_locked); ...@@ -1426,43 +1442,13 @@ EXPORT_SYMBOL(insert_inode_locked);
int insert_inode_locked4(struct inode *inode, unsigned long hashval, int insert_inode_locked4(struct inode *inode, unsigned long hashval,
int (*test)(struct inode *, void *), void *data) int (*test)(struct inode *, void *), void *data)
{ {
struct super_block *sb = inode->i_sb; struct inode *old = inode_insert5(inode, hashval, test, NULL, data);
struct hlist_head *head = inode_hashtable + hash(sb, hashval);
while (1) {
struct inode *old = NULL;
spin_lock(&inode_hash_lock); if (old != inode) {
hlist_for_each_entry(old, head, i_hash) {
if (old->i_sb != sb)
continue;
if (!test(old, data))
continue;
spin_lock(&old->i_lock);
if (old->i_state & (I_FREEING|I_WILL_FREE)) {
spin_unlock(&old->i_lock);
continue;
}
break;
}
if (likely(!old)) {
spin_lock(&inode->i_lock);
inode->i_state |= I_NEW;
hlist_add_head(&inode->i_hash, head);
spin_unlock(&inode->i_lock);
spin_unlock(&inode_hash_lock);
return 0;
}
__iget(old);
spin_unlock(&old->i_lock);
spin_unlock(&inode_hash_lock);
wait_on_inode(old);
if (unlikely(!inode_unhashed(old))) {
iput(old); iput(old);
return -EBUSY; return -EBUSY;
} }
iput(old); return 0;
}
} }
EXPORT_SYMBOL(insert_inode_locked4); EXPORT_SYMBOL(insert_inode_locked4);
......
...@@ -2879,6 +2879,10 @@ extern struct inode *ilookup5(struct super_block *sb, unsigned long hashval, ...@@ -2879,6 +2879,10 @@ extern struct inode *ilookup5(struct super_block *sb, unsigned long hashval,
int (*test)(struct inode *, void *), void *data); int (*test)(struct inode *, void *), void *data);
extern struct inode *ilookup(struct super_block *sb, unsigned long ino); extern struct inode *ilookup(struct super_block *sb, unsigned long ino);
extern struct inode *inode_insert5(struct inode *inode, unsigned long hashval,
int (*test)(struct inode *, void *),
int (*set)(struct inode *, void *),
void *data);
extern struct inode * iget5_locked(struct super_block *, unsigned long, int (*test)(struct inode *, void *), int (*set)(struct inode *, void *), void *); extern struct inode * iget5_locked(struct super_block *, unsigned long, int (*test)(struct inode *, void *), int (*set)(struct inode *, void *), void *);
extern struct inode * iget_locked(struct super_block *, unsigned long); extern struct inode * iget_locked(struct super_block *, unsigned long);
extern struct inode *find_inode_nowait(struct super_block *, extern struct inode *find_inode_nowait(struct super_block *,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment