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

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

Advertisement

Kernel v2.4.3-ac14 /fs/inode.c

Filename:/fs/inode.c
Lines Added:191
Lines Deleted:70
Also changed in: (Previous) 2.4.3-ac12  2.4.3-ac11  2.4.3-ac10  2.4.3-ac8  2.4.3-ac9  2.4.3-ac7 
(Following) 2.4.3-ac13  2.4.4-pre7  2.4.4-pre8  2.4.4  2.4.4-ac1  2.4.4-ac2 

Location
[  2.4.3-ac14
  [  fs
     o  inode.c

Patch

diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/fs/inode.c linux.ac/fs/inode.c
--- linux.vanilla/fs/inode.c   Tue Apr  3 17:32:25 2001
+++ linux.ac/fs/inode.c   Tue Apr 24 17:48:52 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.
@@ -123,36 +125,35 @@
 /**
  *   __mark_inode_dirty -   internal function
  *   @inode: inode to mark
- *
- *   Mark an inode as dirty. Callers should use mark_inode_dirty.
+ *   @flags: what kind of dirty (i.e. I_DIRTY_SYNC)
+ *   Mark an inode as dirty. Callers should use mark_inode_dirty or
+ *     mark_inode_dirty_sync.
  */
  
 void __mark_inode_dirty(struct inode *inode, int flags)
 {
    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)
@@ -190,13 +191,48 @@
       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;
+   list_del(&inode->i_list);
+   list_add(&inode->i_list, inode->i_state & I_DIRTY
+               ? &inode->i_sb->s_dirty
+               : atomic_read(&inode->i_count)
+                  ? &inode_in_use
+                  : &inode_unused);
+   wake_up(&inode->i_wait);
+}
+
 static inline void sync_one(struct inode *inode, int sync)
 {
    if (inode->i_state & I_LOCK) {
@@ -206,38 +242,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 +322,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 +354,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;
    }
 }
 
@@ -303,7 +407,7 @@
       spin_unlock(&inode_lock);
    }
    else
-      printk("write_inode_now: no super block\n");
+      printk(KERN_ERR "write_inode_now: no super block\n");
 }
 
 /**
@@ -476,6 +580,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 +608,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 +621,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 +640,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)
@@ -607,6 +725,8 @@
    inode->i_nlink = 1;
    atomic_set(&inode->i_writecount, 0);
    inode->i_size = 0;
+   inode->i_blocks = 0;
+   inode->i_bytes = 0;
    inode->i_generation = 0;
    memset(&inode->i_dquot, 0, sizeof(inode->i_dquot));
    inode->i_pipe = NULL;
@@ -886,10 +1006,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 +1151,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.