Linux Headquarters
[ Register ]
[ About us ] [ Home Page ]

Advertisement
[ Kernel ] [ Documentation ] [ Links ] [ Books ]

Kernel v2.4.4 /fs/inode.c

Filename:/fs/inode.c
Lines Added:191
Lines Deleted:68
Also changed in: (Previous) 2.4.4-pre8  2.4.4-pre7  2.4.4-pre6  2.4.4-pre5  2.4.3-ac13  2.4.3-ac14 
(Following) 2.4.4-ac1  2.4.4-ac2  2.4.4-ac3  2.4.4-ac4  2.4.4-ac5  2.4.4-ac6 

Location
[  2.4.4
  [  fs
     o  inode.c

Patch

diff -u --recursive --new-file v2.4.3/linux/fs/inode.c linux/fs/inode.c
--- v2.4.3/linux/fs/inode.c   Thu Mar 22 11:04:13 2001
+++ linux/fs/inode.c   Fri Apr 27 14:11:30 2001
@@ -13,6 +13,8 @@
 #include <linux/quotaops.h>
 #include <linux/slab.h>
 #include <linux/cache.h>
+#include <linux/swap.h>
+#include <linux/swapctl.h>
 
 /*
  * New inode.c implementation.
@@ -131,28 +133,26 @@
 {
    struct super_block * sb = inode->i_sb;
 
-   if (sb) {
-      /* Don't do this for I_DIRTY_PAGES - that doesn't actually dirty the inode itself */
-      if (flags & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) {
-         if (sb->s_op && sb->s_op->dirty_inode)
-            sb->s_op->dirty_inode(inode);
-      }
+   /* Don't do this for I_DIRTY_PAGES - that doesn't actually dirty the inode itself */
+   if (flags & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) {
+      if (sb->s_op && sb->s_op->dirty_inode)
+         sb->s_op->dirty_inode(inode);
+   }
 
-      /* avoid the locking if we can */
-      if ((inode->i_state & flags) == flags)
-         return;
+   /* avoid the locking if we can */
+   if ((inode->i_state & flags) == flags)
+      return;
 
-      spin_lock(&inode_lock);
-      if ((inode->i_state & flags) != flags) {
-         inode->i_state |= flags;
-         /* Only add valid (ie hashed) inodes to the dirty list */
-         if (!list_empty(&inode->i_hash)) {
-            list_del(&inode->i_list);
-            list_add(&inode->i_list, &sb->s_dirty);
-         }
+   spin_lock(&inode_lock);
+   if ((inode->i_state & flags) != flags) {
+      inode->i_state |= flags;
+      /* Only add valid (ie hashed) inodes to the dirty list */
+      if (!(inode->i_state & I_LOCK) && !list_empty(&inode->i_hash)) {
+         list_del(&inode->i_list);
+         list_add(&inode->i_list, &sb->s_dirty);
       }
-      spin_unlock(&inode_lock);
    }
+   spin_unlock(&inode_lock);
 }
 
 static void __wait_on_inode(struct inode * inode)
@@ -179,7 +179,7 @@
 
 static inline void write_inode(struct inode *inode, int sync)
 {
-   if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->write_inode)
+   if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->write_inode && !is_bad_inode(inode))
       inode->i_sb->s_op->write_inode(inode, sync);
 }
 
@@ -190,13 +190,53 @@
       return;
    }
    atomic_inc(&inode->i_count);
-   if (!(inode->i_state & I_DIRTY)) {
+   if (!(inode->i_state & (I_DIRTY|I_LOCK))) {
       list_del(&inode->i_list);
       list_add(&inode->i_list, &inode_in_use);
    }
    inodes_stat.nr_unused--;
 }
 
+static inline void __sync_one(struct inode *inode, int sync)
+{
+   unsigned dirty;
+
+   list_del(&inode->i_list);
+   list_add(&inode->i_list, &inode->i_sb->s_locked_inodes);
+
+   if (inode->i_state & I_LOCK)
+      BUG();
+
+   /* Set I_LOCK, reset I_DIRTY */
+   dirty = inode->i_state & I_DIRTY;
+   inode->i_state |= I_LOCK;
+   inode->i_state &= ~I_DIRTY;
+   spin_unlock(&inode_lock);
+
+   filemap_fdatasync(inode->i_mapping);
+
+   /* Don't write the inode if only I_DIRTY_PAGES was set */
+   if (dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC))
+      write_inode(inode, sync);
+
+   filemap_fdatawait(inode->i_mapping);
+
+   spin_lock(&inode_lock);
+   inode->i_state &= ~I_LOCK;
+   if (!(inode->i_state & I_FREEING)) {
+      struct list_head *to;
+      if (inode->i_state & I_DIRTY)
+         to = &inode->i_sb->s_dirty;
+      else if (atomic_read(&inode->i_count))
+         to = &inode_in_use;
+      else
+         to = &inode_unused;
+      list_del(&inode->i_list);
+      list_add(&inode->i_list, to);
+   }
+   wake_up(&inode->i_wait);
+}
+
 static inline void sync_one(struct inode *inode, int sync)
 {
    if (inode->i_state & I_LOCK) {
@@ -206,38 +246,77 @@
       iput(inode);
       spin_lock(&inode_lock);
    } else {
-      unsigned dirty;
-
-      list_del(&inode->i_list);
-      list_add(&inode->i_list, atomic_read(&inode->i_count)
-                     ? &inode_in_use
-                     : &inode_unused);
-      /* Set I_LOCK, reset I_DIRTY */
-      dirty = inode->i_state & I_DIRTY;
-      inode->i_state |= I_LOCK;
-      inode->i_state &= ~I_DIRTY;
-      spin_unlock(&inode_lock);
-
-      filemap_fdatasync(inode->i_mapping);
+      __sync_one(inode, sync);
+   }
+}
 
-      /* Don't write the inode if only I_DIRTY_PAGES was set */
-      if (dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC))
-         write_inode(inode, sync);
+static inline void sync_list(struct list_head *head)
+{
+   struct list_head * tmp;
 
-      filemap_fdatawait(inode->i_mapping);
+   while ((tmp = head->prev) != head) 
+      __sync_one(list_entry(tmp, struct inode, i_list), 0);
+}
 
+static inline int wait_on_dirty(struct list_head *head)
+{
+   struct list_head * tmp;
+   list_for_each(tmp, head) {
+      struct inode *inode = list_entry(tmp, struct inode, i_list);
+      if (!inode->i_state & I_DIRTY)
+         continue;
+      __iget(inode);
+      spin_unlock(&inode_lock);
+      __wait_on_inode(inode);
+      iput(inode);
       spin_lock(&inode_lock);
-      inode->i_state &= ~I_LOCK;
-      wake_up(&inode->i_wait);
+      return 1;
    }
+   return 0;
 }
 
-static inline void sync_list(struct list_head *head)
+static inline void wait_on_locked(struct list_head *head)
 {
    struct list_head * tmp;
+   while ((tmp = head->prev) != head) {
+      struct inode *inode = list_entry(tmp, struct inode, i_list);
+      __iget(inode);
+      spin_unlock(&inode_lock);
+      __wait_on_inode(inode);
+      iput(inode);
+      spin_lock(&inode_lock);
+   }
+}
 
-   while ((tmp = head->prev) != head)
-      sync_one(list_entry(tmp, struct inode, i_list), 0);
+static inline int try_to_sync_unused_list(struct list_head *head)
+{
+   struct list_head *tmp = head;
+   struct inode *inode;
+
+   while ((tmp = tmp->prev) != head) {
+      inode = list_entry(tmp, struct inode, i_list);
+
+      if (!atomic_read(&inode->i_count)) {
+         /* 
+          * We're under PF_MEMALLOC here, and syncing the 
+          * inode may have to allocate memory. To avoid
+          * running into a OOM deadlock, we write one 
+          * inode synchronously and stop syncing in case 
+          * we're under freepages.low
+          */
+
+         int sync = nr_free_pages() < freepages.low;
+         __sync_one(inode, sync);
+         if (sync) 
+            return 0;
+         /* 
+          * __sync_one moved the inode to another list,
+          * so we have to start looking from the list head.
+          */
+         tmp = head;
+      }
+   }
+   return 1;
 }
 
 /**
@@ -247,7 +326,31 @@
  *   sync_inodes goes through the super block's dirty list, 
  *   writes them out, and puts them back on the normal list.
  */
+
+/*
+ * caller holds exclusive lock on sb->s_umount
+ */
  
+void sync_inodes_sb(struct super_block *sb)
+{
+   spin_lock(&inode_lock);
+   sync_list(&sb->s_dirty);
+   wait_on_locked(&sb->s_locked_inodes);
+   spin_unlock(&inode_lock);
+}
+
+void sync_unlocked_inodes(void)
+{
+   struct super_block * sb = sb_entry(super_blocks.next);
+   for (; sb != sb_entry(&super_blocks); sb = sb_entry(sb->s_list.next)) {
+      if (!list_empty(&sb->s_dirty)) {
+         spin_lock(&inode_lock);
+         sync_list(&sb->s_dirty);
+         spin_unlock(&inode_lock);
+      }
+   }
+}
+
 void sync_inodes(kdev_t dev)
 {
    struct super_block * sb = sb_entry(super_blocks.next);
@@ -255,31 +358,36 @@
    /*
     * Search the super_blocks array for the device(s) to sync.
     */
-   spin_lock(&inode_lock);
    for (; sb != sb_entry(&super_blocks); sb = sb_entry(sb->s_list.next)) {
       if (!sb->s_dev)
          continue;
       if (dev && sb->s_dev != dev)
          continue;
-
-      sync_list(&sb->s_dirty);
-
+      down_read(&sb->s_umount);
+      if (sb->s_dev && (sb->s_dev == dev || !dev)) {
+         spin_lock(&inode_lock);
+         do {
+            sync_list(&sb->s_dirty);
+         } while (wait_on_dirty(&sb->s_locked_inodes));
+         spin_unlock(&inode_lock);
+      }
+      up_read(&sb->s_umount);
       if (dev)
          break;
    }
-   spin_unlock(&inode_lock);
 }
 
 /*
  * Called with the spinlock already held..
  */
-static void sync_all_inodes(void)
+static void try_to_sync_unused_inodes(void)
 {
    struct super_block * sb = sb_entry(super_blocks.next);
    for (; sb != sb_entry(&super_blocks); sb = sb_entry(sb->s_list.next)) {
       if (!sb->s_dev)
          continue;
-      sync_list(&sb->s_dirty);
+      if (!try_to_sync_unused_list(&sb->s_dirty))
+         break;
    }
 }
 
@@ -476,6 +584,7 @@
    busy = invalidate_list(&inode_in_use, sb, &throw_away);
    busy |= invalidate_list(&inode_unused, sb, &throw_away);
    busy |= invalidate_list(&sb->s_dirty, sb, &throw_away);
+   busy |= invalidate_list(&sb->s_locked_inodes, sb, &throw_away);
    spin_unlock(&inode_lock);
 
    dispose_list(&throw_away);
@@ -503,13 +612,12 @@
 {
    LIST_HEAD(list);
    struct list_head *entry, *freeable = &list;
-   int count = 0;
+   int count = 0, synced = 0;
    struct inode * inode;
 
    spin_lock(&inode_lock);
-   /* go simple and safe syncing everything before starting */
-   sync_all_inodes();
 
+free_unused:
    entry = inode_unused.prev;
    while (entry != &inode_unused)
    {
@@ -517,7 +625,7 @@
 
       entry = entry->prev;
       inode = INODE(tmp);
-      if (inode->i_state & (I_FREEING|I_CLEAR))
+      if (inode->i_state & (I_FREEING|I_CLEAR|I_LOCK))
          BUG();
       if (!CAN_UNUSE(inode))
          continue;
@@ -536,6 +644,20 @@
    spin_unlock(&inode_lock);
 
    dispose_list(freeable);
+
+   /* 
+    * If we freed enough clean inodes, avoid writing 
+    * dirty ones. Also giveup if we already tried to
+    * sync dirty inodes.
+    */
+   if (!goal || synced)
+      return;
+   
+   synced = 1;
+
+   spin_lock(&inode_lock);
+   try_to_sync_unused_inodes();
+   goto free_unused;
 }
 
 void shrink_icache_memory(int priority, int gfp_mask)
@@ -886,10 +1008,9 @@
             BUG();
       } else {
          if (!list_empty(&inode->i_hash)) {
-            if (!(inode->i_state & I_DIRTY)) {
+            if (!(inode->i_state & (I_DIRTY|I_LOCK))) {
                list_del(&inode->i_list);
-               list_add(&inode->i_list,
-                   &inode_unused);
+               list_add(&inode->i_list, &inode_unused);
             }
             inodes_stat.nr_unused++;
             spin_unlock(&inode_lock);
@@ -1032,23 +1153,25 @@
    /* We have to be protected against other CPUs */
    spin_lock(&inode_lock);
  
-   for (act_head = inode_in_use.next; act_head != &inode_in_use; act_head = act_head->next) {
+   list_for_each(act_head, &inode_in_use) {
       inode = list_entry(act_head, struct inode, i_list);
-      if (inode->i_sb != sb || !IS_QUOTAINIT(inode))
-         continue;
-      remove_inode_dquot_ref(inode, type, &tofree_head);
+      if (inode->i_sb == sb && IS_QUOTAINIT(inode))
+         remove_inode_dquot_ref(inode, type, &tofree_head);
    }
-   for (act_head = inode_unused.next; act_head != &inode_unused; act_head = act_head->next) {
+   list_for_each(act_head, &inode_unused) {
       inode = list_entry(act_head, struct inode, i_list);
-      if (inode->i_sb != sb || !IS_QUOTAINIT(inode))
-         continue;
-      remove_inode_dquot_ref(inode, type, &tofree_head);
+      if (inode->i_sb == sb && IS_QUOTAINIT(inode))
+         remove_inode_dquot_ref(inode, type, &tofree_head);
    }
-   for (act_head = sb->s_dirty.next; act_head != &sb->s_dirty; act_head = act_head->next) {
+   list_for_each(act_head, &sb->s_dirty) {
       inode = list_entry(act_head, struct inode, i_list);
-      if (!IS_QUOTAINIT(inode))
-         continue;
-        remove_inode_dquot_ref(inode, type, &tofree_head);
+      if (IS_QUOTAINIT(inode))
+         remove_inode_dquot_ref(inode, type, &tofree_head);
+   }
+   list_for_each(act_head, &sb->s_locked_inodes) {
+      inode = list_entry(act_head, struct inode, i_list);
+      if (IS_QUOTAINIT(inode))
+         remove_inode_dquot_ref(inode, type, &tofree_head);
    }
    spin_unlock(&inode_lock);
 


Comments: webmaster (at) linuxhq.com.
Advertising: banners (at) linuxhq.com.
Compilation ©1998-2008 Linux Headquarters, Inc.