ext4: avoid hangs in ext4_da_should_update_i_disksize()
[linux-2.6.git] / fs / sysv / inode.c
index 425c976..0630eb9 100644 (file)
  *  the superblock.
  */
 
-#include <linux/smp_lock.h>
 #include <linux/highuid.h>
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/buffer_head.h>
 #include <linux/vfs.h>
+#include <linux/writeback.h>
 #include <linux/namei.h>
 #include <asm/byteorder.h>
 #include "sysv.h"
 
-/* This is only called on sync() and umount(), when s_dirt=1. */
-static void sysv_write_super(struct super_block *sb)
+static int sysv_sync_fs(struct super_block *sb, int wait)
 {
        struct sysv_sb_info *sbi = SYSV_SB(sb);
        unsigned long time = get_seconds(), old_time;
 
        lock_super(sb);
-       lock_kernel();
-       if (sb->s_flags & MS_RDONLY)
-               goto clean;
 
        /*
         * If we are going to write out the super block,
         * then attach current time stamp.
         * But if the filesystem was marked clean, keep it clean.
         */
+       sb->s_dirt = 0;
        old_time = fs32_to_cpu(sbi, *sbi->s_sb_time);
        if (sbi->s_type == FSTYPE_SYSV4) {
                if (*sbi->s_sb_state == cpu_to_fs32(sbi, 0x7c269d38 - old_time))
@@ -54,10 +51,18 @@ static void sysv_write_super(struct super_block *sb)
                *sbi->s_sb_time = cpu_to_fs32(sbi, time);
                mark_buffer_dirty(sbi->s_bh2);
        }
-clean:
-       sb->s_dirt = 0;
-       unlock_kernel();
+
        unlock_super(sb);
+
+       return 0;
+}
+
+static void sysv_write_super(struct super_block *sb)
+{
+       if (!(sb->s_flags & MS_RDONLY))
+               sysv_sync_fs(sb, 1);
+       else
+               sb->s_dirt = 0;
 }
 
 static int sysv_remount(struct super_block *sb, int *flags, char *data)
@@ -66,8 +71,8 @@ static int sysv_remount(struct super_block *sb, int *flags, char *data)
        lock_super(sb);
        if (sbi->s_forced_ro)
                *flags |= MS_RDONLY;
-       if (!(*flags & MS_RDONLY))
-               sb->s_dirt = 1;
+       if (*flags & MS_RDONLY)
+               sysv_write_super(sb);
        unlock_super(sb);
        return 0;
 }
@@ -76,8 +81,6 @@ static void sysv_put_super(struct super_block *sb)
 {
        struct sysv_sb_info *sbi = SYSV_SB(sb);
 
-       lock_kernel();
-
        if (sb->s_dirt)
                sysv_write_super(sb);
 
@@ -93,8 +96,6 @@ static void sysv_put_super(struct super_block *sb)
                brelse(sbi->s_bh2);
 
        kfree(sbi);
-
-       unlock_kernel();
 }
 
 static int sysv_statfs(struct dentry *dentry, struct kstatfs *buf)
@@ -247,7 +248,7 @@ bad_inode:
        return ERR_PTR(-EIO);
 }
 
-int sysv_write_inode(struct inode *inode, int wait)
+static int __sysv_write_inode(struct inode *inode, int wait)
 {
        struct super_block * sb = inode->i_sb;
        struct sysv_sb_info * sbi = SYSV_SB(sb);
@@ -269,7 +270,6 @@ int sysv_write_inode(struct inode *inode, int wait)
                return -EIO;
        }
 
-       lock_kernel();
        raw_inode->i_mode = cpu_to_fs16(sbi, inode->i_mode);
        raw_inode->i_uid = cpu_to_fs16(sbi, fs_high2lowuid(inode->i_uid));
        raw_inode->i_gid = cpu_to_fs16(sbi, fs_high2lowgid(inode->i_gid));
@@ -285,7 +285,6 @@ int sysv_write_inode(struct inode *inode, int wait)
        for (block = 0; block < 10+1+1+1; block++)
                write3byte(sbi, (u8 *)&si->i_data[block],
                        &raw_inode->i_data[3*block]);
-       unlock_kernel();
        mark_buffer_dirty(bh);
        if (wait) {
                 sync_dirty_buffer(bh);
@@ -299,19 +298,27 @@ int sysv_write_inode(struct inode *inode, int wait)
        return 0;
 }
 
+int sysv_write_inode(struct inode *inode, struct writeback_control *wbc)
+{
+       return __sysv_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
+}
+
 int sysv_sync_inode(struct inode *inode)
 {
-       return sysv_write_inode(inode, 1);
+       return __sysv_write_inode(inode, 1);
 }
 
-static void sysv_delete_inode(struct inode *inode)
+static void sysv_evict_inode(struct inode *inode)
 {
        truncate_inode_pages(&inode->i_data, 0);
-       inode->i_size = 0;
-       sysv_truncate(inode);
-       lock_kernel();
-       sysv_free_inode(inode);
-       unlock_kernel();
+       if (!inode->i_nlink) {
+               inode->i_size = 0;
+               sysv_truncate(inode);
+       }
+       invalidate_inode_buffers(inode);
+       end_writeback(inode);
+       if (!inode->i_nlink)
+               sysv_free_inode(inode);
 }
 
 static struct kmem_cache *sysv_inode_cachep;
@@ -326,11 +333,18 @@ static struct inode *sysv_alloc_inode(struct super_block *sb)
        return &si->vfs_inode;
 }
 
-static void sysv_destroy_inode(struct inode *inode)
+static void sysv_i_callback(struct rcu_head *head)
 {
+       struct inode *inode = container_of(head, struct inode, i_rcu);
+       INIT_LIST_HEAD(&inode->i_dentry);
        kmem_cache_free(sysv_inode_cachep, SYSV_I(inode));
 }
 
+static void sysv_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, sysv_i_callback);
+}
+
 static void init_once(void *p)
 {
        struct sysv_inode_info *si = (struct sysv_inode_info *)p;
@@ -342,9 +356,10 @@ const struct super_operations sysv_sops = {
        .alloc_inode    = sysv_alloc_inode,
        .destroy_inode  = sysv_destroy_inode,
        .write_inode    = sysv_write_inode,
-       .delete_inode   = sysv_delete_inode,
+       .evict_inode    = sysv_evict_inode,
        .put_super      = sysv_put_super,
        .write_super    = sysv_write_super,
+       .sync_fs        = sysv_sync_fs,
        .remount_fs     = sysv_remount,
        .statfs         = sysv_statfs,
 };