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: ...@@ -88,6 +88,7 @@ prototypes:
void (*read_inode) (struct inode *); void (*read_inode) (struct inode *);
void (*write_inode) (struct inode *, int); void (*write_inode) (struct inode *, int);
void (*put_inode) (struct inode *); void (*put_inode) (struct inode *);
void (*drop_inode) (struct inode *);
void (*delete_inode) (struct inode *); void (*delete_inode) (struct inode *);
void (*put_super) (struct super_block *); void (*put_super) (struct super_block *);
void (*write_super) (struct super_block *); void (*write_super) (struct super_block *);
...@@ -102,6 +103,7 @@ locking rules: ...@@ -102,6 +103,7 @@ locking rules:
read_inode: yes (see below) read_inode: yes (see below)
write_inode: no write_inode: no
put_inode: no put_inode: no
drop_inode: no !!!inode_lock!!!
delete_inode: no delete_inode: no
clear_inode: no clear_inode: no
put_super: yes yes maybe (see below) put_super: yes yes maybe (see below)
......
...@@ -178,6 +178,7 @@ struct super_operations { ...@@ -178,6 +178,7 @@ struct super_operations {
void (*read_inode) (struct inode *); void (*read_inode) (struct inode *);
void (*write_inode) (struct inode *, int); void (*write_inode) (struct inode *, int);
void (*put_inode) (struct inode *); void (*put_inode) (struct inode *);
void (*drop_inode) (struct inode *);
void (*delete_inode) (struct inode *); void (*delete_inode) (struct inode *);
int (*notify_change) (struct dentry *, struct iattr *); int (*notify_change) (struct dentry *, struct iattr *);
void (*put_super) (struct super_block *); void (*put_super) (struct super_block *);
...@@ -204,6 +205,19 @@ or bottom half). ...@@ -204,6 +205,19 @@ or bottom half).
put_inode: called when the VFS inode is removed from the inode put_inode: called when the VFS inode is removed from the inode
cache. This method is optional 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 delete_inode: called when the VFS wants to delete an inode
notify_change: called when VFS inode attributes are changed. If this notify_change: called when VFS inode attributes are changed. If this
......
...@@ -290,7 +290,7 @@ static struct inode_operations pcihpfs_dir_inode_operations = { ...@@ -290,7 +290,7 @@ static struct inode_operations pcihpfs_dir_inode_operations = {
static struct super_operations pcihpfs_ops = { static struct super_operations pcihpfs_ops = {
statfs: simple_statfs, 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) 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 = { ...@@ -298,7 +298,7 @@ static struct inode_operations usbfs_dir_inode_operations = {
static struct super_operations usbfs_ops = { static struct super_operations usbfs_ops = {
statfs: simple_statfs, 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) static int usbfs_fill_super(struct super_block *sb, void *data, int silent)
......
...@@ -621,7 +621,7 @@ static struct file_operations bm_status_operations = { ...@@ -621,7 +621,7 @@ static struct file_operations bm_status_operations = {
static struct super_operations s_ops = { static struct super_operations s_ops = {
statfs: simple_statfs, statfs: simple_statfs,
put_inode: force_delete, drop_inode: generic_delete_inode,
clear_inode: bm_clear_inode, clear_inode: bm_clear_inode,
}; };
......
...@@ -2576,7 +2576,7 @@ static void devfs_clear_inode (struct inode *inode) ...@@ -2576,7 +2576,7 @@ static void devfs_clear_inode (struct inode *inode)
static struct super_operations devfs_sops = static struct super_operations devfs_sops =
{ {
put_inode: force_delete, drop_inode: generic_delete_inode,
clear_inode: devfs_clear_inode, clear_inode: devfs_clear_inode,
statfs: simple_statfs, statfs: simple_statfs,
}; };
......
...@@ -442,7 +442,7 @@ static struct dentry_operations driverfs_dentry_file_ops = { ...@@ -442,7 +442,7 @@ static struct dentry_operations driverfs_dentry_file_ops = {
static struct super_operations driverfs_ops = { static struct super_operations driverfs_ops = {
statfs: simple_statfs, 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) static int driverfs_fill_super(struct super_block *sb, void *data, int silent)
......
...@@ -782,6 +782,97 @@ void remove_inode_hash(struct inode *inode) ...@@ -782,6 +782,97 @@ void remove_inode_hash(struct inode *inode)
spin_unlock(&inode_lock); 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 * iput - put an inode
* @inode: inode to put * @inode: inode to put
...@@ -793,79 +884,19 @@ void remove_inode_hash(struct inode *inode) ...@@ -793,79 +884,19 @@ void remove_inode_hash(struct inode *inode)
void iput(struct inode *inode) void iput(struct inode *inode)
{ {
if (inode) { if (inode) {
struct super_block *sb = inode->i_sb; struct super_operations *op = inode->i_sb->s_op;
struct super_operations *op = NULL;
if (inode->i_state == I_CLEAR) if (inode->i_state == I_CLEAR)
BUG(); BUG();
if (sb && sb->s_op)
op = sb->s_op;
if (op && op->put_inode) if (op && op->put_inode)
op->put_inode(inode); op->put_inode(inode);
if (!atomic_dec_and_lock(&inode->i_count, &inode_lock)) if (atomic_dec_and_lock(&inode->i_count, &inode_lock))
return; iput_final(inode);
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);
} }
} }
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 * bmap - find a block number in a file
* @inode: inode of file * @inode: inode of file
......
...@@ -84,7 +84,7 @@ static struct super_operations ncp_sops = ...@@ -84,7 +84,7 @@ static struct super_operations ncp_sops =
{ {
alloc_inode: ncp_alloc_inode, alloc_inode: ncp_alloc_inode,
destroy_inode: ncp_destroy_inode, destroy_inode: ncp_destroy_inode,
put_inode: force_delete, drop_inode: generic_delete_inode,
delete_inode: ncp_delete_inode, delete_inode: ncp_delete_inode,
put_super: ncp_put_super, put_super: ncp_put_super,
statfs: ncp_statfs, statfs: ncp_statfs,
......
...@@ -121,7 +121,7 @@ static struct super_operations proc_sops = { ...@@ -121,7 +121,7 @@ static struct super_operations proc_sops = {
alloc_inode: proc_alloc_inode, alloc_inode: proc_alloc_inode,
destroy_inode: proc_destroy_inode, destroy_inode: proc_destroy_inode,
read_inode: proc_read_inode, read_inode: proc_read_inode,
put_inode: force_delete, drop_inode: generic_delete_inode,
delete_inode: proc_delete_inode, delete_inode: proc_delete_inode,
statfs: simple_statfs, statfs: simple_statfs,
}; };
......
...@@ -277,7 +277,7 @@ static struct inode_operations ramfs_dir_inode_operations = { ...@@ -277,7 +277,7 @@ static struct inode_operations ramfs_dir_inode_operations = {
static struct super_operations ramfs_ops = { static struct super_operations ramfs_ops = {
statfs: simple_statfs, 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) static int ramfs_fill_super(struct super_block * sb, void * data, int silent)
......
...@@ -94,7 +94,7 @@ static struct super_operations smb_sops = ...@@ -94,7 +94,7 @@ static struct super_operations smb_sops =
{ {
alloc_inode: smb_alloc_inode, alloc_inode: smb_alloc_inode,
destroy_inode: smb_destroy_inode, destroy_inode: smb_destroy_inode,
put_inode: force_delete, drop_inode: generic_delete_inode,
delete_inode: smb_delete_inode, delete_inode: smb_delete_inode,
put_super: smb_put_super, put_super: smb_put_super,
statfs: smb_statfs, statfs: smb_statfs,
......
...@@ -800,6 +800,7 @@ struct super_operations { ...@@ -800,6 +800,7 @@ struct super_operations {
void (*dirty_inode) (struct inode *); void (*dirty_inode) (struct inode *);
void (*write_inode) (struct inode *, int); void (*write_inode) (struct inode *, int);
void (*put_inode) (struct inode *); void (*put_inode) (struct inode *);
void (*drop_inode) (struct inode *);
void (*delete_inode) (struct inode *); void (*delete_inode) (struct inode *);
void (*put_super) (struct super_block *); void (*put_super) (struct super_block *);
void (*write_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); ...@@ -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 inode_init_once(struct inode *);
extern void iput(struct inode *); extern void iput(struct inode *);
extern void force_delete(struct inode *);
extern struct inode * igrab(struct inode *); extern struct inode * igrab(struct inode *);
extern ino_t iunique(struct super_block *, ino_t); extern ino_t iunique(struct super_block *, ino_t);
extern int inode_needs_sync(struct inode *inode); 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 * 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);
......
...@@ -140,7 +140,6 @@ EXPORT_SYMBOL(igrab); ...@@ -140,7 +140,6 @@ EXPORT_SYMBOL(igrab);
EXPORT_SYMBOL(iunique); EXPORT_SYMBOL(iunique);
EXPORT_SYMBOL(iput); EXPORT_SYMBOL(iput);
EXPORT_SYMBOL(inode_init_once); EXPORT_SYMBOL(inode_init_once);
EXPORT_SYMBOL(force_delete);
EXPORT_SYMBOL(follow_up); EXPORT_SYMBOL(follow_up);
EXPORT_SYMBOL(follow_down); EXPORT_SYMBOL(follow_down);
EXPORT_SYMBOL(lookup_mnt); EXPORT_SYMBOL(lookup_mnt);
......
...@@ -1483,7 +1483,7 @@ static struct super_operations shmem_ops = { ...@@ -1483,7 +1483,7 @@ static struct super_operations shmem_ops = {
remount_fs: shmem_remount_fs, remount_fs: shmem_remount_fs,
#endif #endif
delete_inode: shmem_delete_inode, delete_inode: shmem_delete_inode,
put_inode: force_delete, drop_inode: generic_delete_inode,
put_super: shmem_put_super, 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