Commit 6b7eb57f authored by Linus Torvalds's avatar Linus Torvalds

Split up "iput()" and make it more readable.

    
Add "drop_inode" VFS interface to make FS operations cleaner
and race-free.  Remove old force_delete interface, and update
filesystems that used it to use the new infrastructure.
parent a9907091
......@@ -88,6 +88,7 @@ prototypes:
void (*read_inode) (struct inode *);
void (*write_inode) (struct inode *, int);
void (*put_inode) (struct inode *);
void (*drop_inode) (struct inode *);
void (*delete_inode) (struct inode *);
void (*put_super) (struct super_block *);
void (*write_super) (struct super_block *);
......@@ -102,6 +103,7 @@ locking rules:
read_inode: yes (see below)
write_inode: no
put_inode: no
drop_inode: no !!!inode_lock!!!
delete_inode: no
clear_inode: no
put_super: yes yes maybe (see below)
......
......@@ -178,6 +178,7 @@ struct super_operations {
void (*read_inode) (struct inode *);
void (*write_inode) (struct inode *, int);
void (*put_inode) (struct inode *);
void (*drop_inode) (struct inode *);
void (*delete_inode) (struct inode *);
int (*notify_change) (struct dentry *, struct iattr *);
void (*put_super) (struct super_block *);
......@@ -204,6 +205,19 @@ or bottom half).
put_inode: called when the VFS inode is removed from the inode
cache. This method is optional
drop_inode: called when the last access to the inode is dropped,
with the inode_lock spinlock held.
This method should be either NULL (normal unix filesystem
semantics) or "generic_delete_inode" (for filesystems that do not
want to cache inodes - causing "delete_inode" to always be
called regardless of the value of i_nlink)
The "generic_delete_inode()" behaviour is equivalent to the
old practice of using "force_delete" in the put_inode() case,
but does not have the races that the "force_delete()" approach
had.
delete_inode: called when the VFS wants to delete an inode
notify_change: called when VFS inode attributes are changed. If this
......
......@@ -290,7 +290,7 @@ static struct inode_operations pcihpfs_dir_inode_operations = {
static struct super_operations pcihpfs_ops = {
statfs: simple_statfs,
put_inode: force_delete,
drop_inode: generic_delete_inode,
};
static int pcihpfs_fill_super(struct super_block *sb, void *data, int silent)
......
......@@ -298,7 +298,7 @@ static struct inode_operations usbfs_dir_inode_operations = {
static struct super_operations usbfs_ops = {
statfs: simple_statfs,
put_inode: force_delete,
drop_inode: generic_delete_inode,
};
static int usbfs_fill_super(struct super_block *sb, void *data, int silent)
......
......@@ -621,7 +621,7 @@ static struct file_operations bm_status_operations = {
static struct super_operations s_ops = {
statfs: simple_statfs,
put_inode: force_delete,
drop_inode: generic_delete_inode,
clear_inode: bm_clear_inode,
};
......
......@@ -2576,7 +2576,7 @@ static void devfs_clear_inode (struct inode *inode)
static struct super_operations devfs_sops =
{
put_inode: force_delete,
drop_inode: generic_delete_inode,
clear_inode: devfs_clear_inode,
statfs: simple_statfs,
};
......
......@@ -442,7 +442,7 @@ static struct dentry_operations driverfs_dentry_file_ops = {
static struct super_operations driverfs_ops = {
statfs: simple_statfs,
put_inode: force_delete,
drop_inode: generic_delete_inode,
};
static int driverfs_fill_super(struct super_block *sb, void *data, int silent)
......
......@@ -782,6 +782,97 @@ void remove_inode_hash(struct inode *inode)
spin_unlock(&inode_lock);
}
void generic_delete_inode(struct inode *inode)
{
struct super_operations *op = inode->i_sb->s_op;
list_del(&inode->i_hash);
INIT_LIST_HEAD(&inode->i_hash);
list_del(&inode->i_list);
INIT_LIST_HEAD(&inode->i_list);
inode->i_state|=I_FREEING;
inodes_stat.nr_inodes--;
spin_unlock(&inode_lock);
if (inode->i_data.nrpages)
truncate_inode_pages(&inode->i_data, 0);
if (op && op->delete_inode) {
void (*delete)(struct inode *) = op->delete_inode;
if (!is_bad_inode(inode))
DQUOT_INIT(inode);
/* s_op->delete_inode internally recalls clear_inode() */
delete(inode);
} else
clear_inode(inode);
if (inode->i_state != I_CLEAR)
BUG();
destroy_inode(inode);
}
EXPORT_SYMBOL(generic_delete_inode);
static void generic_forget_inode(struct inode *inode)
{
struct super_block *sb = inode->i_sb;
if (!list_empty(&inode->i_hash)) {
if (!(inode->i_state & (I_DIRTY|I_LOCK))) {
list_del(&inode->i_list);
list_add(&inode->i_list, &inode_unused);
}
inodes_stat.nr_unused++;
spin_unlock(&inode_lock);
if (!sb || (sb->s_flags & MS_ACTIVE))
return;
write_inode_now(inode, 1);
spin_lock(&inode_lock);
inodes_stat.nr_unused--;
list_del_init(&inode->i_hash);
}
list_del_init(&inode->i_list);
inode->i_state|=I_FREEING;
inodes_stat.nr_inodes--;
spin_unlock(&inode_lock);
if (inode->i_data.nrpages)
truncate_inode_pages(&inode->i_data, 0);
clear_inode(inode);
destroy_inode(inode);
}
/*
* Normal UNIX filesystem behaviour: delete the
* inode when the usage count drops to zero, and
* i_nlink is zero.
*/
static void generic_drop_inode(struct inode *inode)
{
if (!inode->i_nlink)
generic_delete_inode(inode);
else
generic_forget_inode(inode);
}
/*
* Called when we're dropping the last reference
* to an inode.
*
* Call the FS "drop()" function, defaulting to
* the legacy UNIX filesystem behaviour..
*
* NOTE! NOTE! NOTE! We're called with the inode lock
* held, and the drop function is supposed to release
* the lock!
*/
static inline void iput_final(struct inode *inode)
{
struct super_operations *op = inode->i_sb->s_op;
void (*drop)(struct inode *) = generic_drop_inode;
if (op && op->drop_inode)
drop = op->drop_inode;
drop(inode);
}
/**
* iput - put an inode
* @inode: inode to put
......@@ -793,79 +884,19 @@ void remove_inode_hash(struct inode *inode)
void iput(struct inode *inode)
{
if (inode) {
struct super_block *sb = inode->i_sb;
struct super_operations *op = NULL;
struct super_operations *op = inode->i_sb->s_op;
if (inode->i_state == I_CLEAR)
BUG();
if (sb && sb->s_op)
op = sb->s_op;
if (op && op->put_inode)
op->put_inode(inode);
if (!atomic_dec_and_lock(&inode->i_count, &inode_lock))
return;
if (!inode->i_nlink) {
list_del(&inode->i_hash);
INIT_LIST_HEAD(&inode->i_hash);
list_del(&inode->i_list);
INIT_LIST_HEAD(&inode->i_list);
inode->i_state|=I_FREEING;
inodes_stat.nr_inodes--;
spin_unlock(&inode_lock);
if (inode->i_data.nrpages)
truncate_inode_pages(&inode->i_data, 0);
if (op && op->delete_inode) {
void (*delete)(struct inode *) = op->delete_inode;
if (!is_bad_inode(inode))
DQUOT_INIT(inode);
/* s_op->delete_inode internally recalls clear_inode() */
delete(inode);
} else
clear_inode(inode);
if (inode->i_state != I_CLEAR)
BUG();
} else {
if (!list_empty(&inode->i_hash)) {
if (!(inode->i_state & (I_DIRTY|I_LOCK))) {
list_del(&inode->i_list);
list_add(&inode->i_list, &inode_unused);
}
inodes_stat.nr_unused++;
spin_unlock(&inode_lock);
if (!sb || (sb->s_flags & MS_ACTIVE))
return;
write_inode_now(inode, 1);
spin_lock(&inode_lock);
inodes_stat.nr_unused--;
list_del_init(&inode->i_hash);
}
list_del_init(&inode->i_list);
inode->i_state|=I_FREEING;
inodes_stat.nr_inodes--;
spin_unlock(&inode_lock);
if (inode->i_data.nrpages)
truncate_inode_pages(&inode->i_data, 0);
clear_inode(inode);
}
destroy_inode(inode);
if (atomic_dec_and_lock(&inode->i_count, &inode_lock))
iput_final(inode);
}
}
void force_delete(struct inode *inode)
{
/*
* Kill off unused inodes ... iput() will unhash and
* delete the inode if we set i_nlink to zero.
*/
if (atomic_read(&inode->i_count) == 1)
inode->i_nlink = 0;
}
/**
* bmap - find a block number in a file
* @inode: inode of file
......
......@@ -84,7 +84,7 @@ static struct super_operations ncp_sops =
{
alloc_inode: ncp_alloc_inode,
destroy_inode: ncp_destroy_inode,
put_inode: force_delete,
drop_inode: generic_delete_inode,
delete_inode: ncp_delete_inode,
put_super: ncp_put_super,
statfs: ncp_statfs,
......
......@@ -121,7 +121,7 @@ static struct super_operations proc_sops = {
alloc_inode: proc_alloc_inode,
destroy_inode: proc_destroy_inode,
read_inode: proc_read_inode,
put_inode: force_delete,
drop_inode: generic_delete_inode,
delete_inode: proc_delete_inode,
statfs: simple_statfs,
};
......
......@@ -277,7 +277,7 @@ static struct inode_operations ramfs_dir_inode_operations = {
static struct super_operations ramfs_ops = {
statfs: simple_statfs,
put_inode: force_delete,
drop_inode: generic_delete_inode,
};
static int ramfs_fill_super(struct super_block * sb, void * data, int silent)
......
......@@ -94,7 +94,7 @@ static struct super_operations smb_sops =
{
alloc_inode: smb_alloc_inode,
destroy_inode: smb_destroy_inode,
put_inode: force_delete,
drop_inode: generic_delete_inode,
delete_inode: smb_delete_inode,
put_super: smb_put_super,
statfs: smb_statfs,
......
......@@ -800,6 +800,7 @@ struct super_operations {
void (*dirty_inode) (struct inode *);
void (*write_inode) (struct inode *, int);
void (*put_inode) (struct inode *);
void (*drop_inode) (struct inode *);
void (*delete_inode) (struct inode *);
void (*put_super) (struct super_block *);
void (*write_super) (struct super_block *);
......@@ -1183,10 +1184,10 @@ extern loff_t default_llseek(struct file *file, loff_t offset, int origin);
extern void inode_init_once(struct inode *);
extern void iput(struct inode *);
extern void force_delete(struct inode *);
extern struct inode * igrab(struct inode *);
extern ino_t iunique(struct super_block *, ino_t);
extern int inode_needs_sync(struct inode *inode);
extern void generic_delete_inode(struct inode *inode);
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);
......
......@@ -140,7 +140,6 @@ EXPORT_SYMBOL(igrab);
EXPORT_SYMBOL(iunique);
EXPORT_SYMBOL(iput);
EXPORT_SYMBOL(inode_init_once);
EXPORT_SYMBOL(force_delete);
EXPORT_SYMBOL(follow_up);
EXPORT_SYMBOL(follow_down);
EXPORT_SYMBOL(lookup_mnt);
......
......@@ -1483,7 +1483,7 @@ static struct super_operations shmem_ops = {
remount_fs: shmem_remount_fs,
#endif
delete_inode: shmem_delete_inode,
put_inode: force_delete,
drop_inode: generic_delete_inode,
put_super: shmem_put_super,
};
......
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