patch-2.4.10 linux/fs/inode.c

Next file: linux/fs/isofs/rock.c
Previous file: linux/fs/hpfs/inode.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.9/linux/fs/inode.c linux/fs/inode.c
@@ -15,6 +15,8 @@
 #include <linux/cache.h>
 #include <linux/swap.h>
 #include <linux/swapctl.h>
+#include <linux/prefetch.h>
+#include <linux/locks.h>
 
 /*
  * New inode.c implementation.
@@ -77,7 +79,7 @@
 	 ((struct inode *) kmem_cache_alloc(inode_cachep, SLAB_KERNEL))
 static void destroy_inode(struct inode *inode) 
 {
-	if (!list_empty(&inode->i_dirty_buffers))
+	if (inode_has_buffers(inode))
 		BUG();
 	kmem_cache_free(inode_cachep, (inode));
 }
@@ -103,6 +105,8 @@
 		INIT_LIST_HEAD(&inode->i_data.locked_pages);
 		INIT_LIST_HEAD(&inode->i_dentry);
 		INIT_LIST_HEAD(&inode->i_dirty_buffers);
+		INIT_LIST_HEAD(&inode->i_dirty_data_buffers);
+		INIT_LIST_HEAD(&inode->i_devices);
 		sema_init(&inode->i_sem, 1);
 		sema_init(&inode->i_zombie, 1);
 		spin_lock_init(&inode->i_data.i_shared_lock);
@@ -125,14 +129,18 @@
 /**
  *	__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)
+		return;
+
 	/* 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)
@@ -271,27 +279,18 @@
 	}
 }
 
-static inline int try_to_sync_unused_list(struct list_head *head)
+static inline int try_to_sync_unused_list(struct list_head *head, int nr_inodes)
 {
 	struct list_head *tmp = head;
 	struct inode *inode;
 
-	while ((tmp = tmp->prev) != head) {
+	while (nr_inodes && (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
-			 */
+			__sync_one(inode, 0);
+			nr_inodes--;
 
-			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.
@@ -299,7 +298,8 @@
 			tmp = head;
 		}
 	}
-	return 1;
+
+	return nr_inodes;
 }
 
 void sync_inodes_sb(struct super_block *sb)
@@ -395,26 +395,25 @@
 	}
 }
 
-/*
- * Called with the spinlock already held..
- */
-static void try_to_sync_unused_inodes(void)
+static void try_to_sync_unused_inodes(void * arg)
 {
 	struct super_block * sb;
+	int nr_inodes = inodes_stat.nr_unused;
 
+	spin_lock(&inode_lock);
 	spin_lock(&sb_lock);
 	sb = sb_entry(super_blocks.next);
-	for (; sb != sb_entry(&super_blocks); sb = sb_entry(sb->s_list.next)) {
-		if (!sb->s_dev)
-			continue;
+	for (; nr_inodes && sb != sb_entry(&super_blocks); sb = sb_entry(sb->s_list.next)) {
 		spin_unlock(&sb_lock);
-		if (!try_to_sync_unused_list(&sb->s_dirty))
-			return;
+		nr_inodes = try_to_sync_unused_list(&sb->s_dirty, nr_inodes);
 		spin_lock(&sb_lock);
 	}
 	spin_unlock(&sb_lock);
+	spin_unlock(&inode_lock);
 }
 
+static struct tq_struct unused_inodes_flush_task;
+
 /**
  *	write_inode_now	-	write an inode to disk
  *	@inode: inode to write to disk
@@ -433,9 +432,11 @@
 		while (inode->i_state & I_DIRTY)
 			sync_one(inode, sync);
 		spin_unlock(&inode_lock);
+		if (sync)
+			wait_on_inode(inode);
 	}
 	else
-		printk("write_inode_now: no super block\n");
+		printk(KERN_ERR "write_inode_now: no super block\n");
 }
 
 /**
@@ -447,9 +448,9 @@
  * O_SYNC flag set, to flush dirty writes to disk.  
  */
 
-int generic_osync_inode(struct inode *inode, int datasync)
+int generic_osync_inode(struct inode *inode, int what)
 {
-	int err;
+	int err = 0, err2 = 0, need_write_inode_now = 0;
 	
 	/* 
 	 * WARNING
@@ -472,23 +473,24 @@
 	 * every O_SYNC write, not just the synchronous I/Os.  --sct
 	 */
 
-#ifdef WRITERS_QUEUE_IO
-	err = osync_inode_buffers(inode);
-#else
-	err = fsync_inode_buffers(inode);
-#endif
+	if (what & OSYNC_METADATA)
+		err = fsync_inode_buffers(inode);
+	if (what & OSYNC_DATA)
+		err2 = fsync_inode_data_buffers(inode);
+	if (!err)
+		err = err2;
 
 	spin_lock(&inode_lock);
-	if (!(inode->i_state & I_DIRTY))
-		goto out;
-	if (datasync && !(inode->i_state & I_DIRTY_DATASYNC))
-		goto out;
+	if ((inode->i_state & I_DIRTY) &&
+	    ((what & OSYNC_INODE) || (inode->i_state & I_DIRTY_DATASYNC)))
+		need_write_inode_now = 1;
 	spin_unlock(&inode_lock);
-	write_inode_now(inode, 1);
-	return err;
 
- out:
-	spin_unlock(&inode_lock);
+	if (need_write_inode_now)
+		write_inode_now(inode, 1);
+	else
+		wait_on_inode(inode);
+
 	return err;
 }
 
@@ -503,8 +505,7 @@
  
 void clear_inode(struct inode *inode)
 {
-	if (!list_empty(&inode->i_dirty_buffers))
-		invalidate_inode_buffers(inode);
+	invalidate_inode_buffers(inode);
        
 	if (inode->i_data.nrpages)
 		BUG();
@@ -513,15 +514,12 @@
 	if (inode->i_state & I_CLEAR)
 		BUG();
 	wait_on_inode(inode);
-	if (IS_QUOTAINIT(inode))
-		DQUOT_DROP(inode);
+	DQUOT_DROP(inode);
 	if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->clear_inode)
 		inode->i_sb->s_op->clear_inode(inode);
-	if (inode->i_bdev) {
-		bdput(inode->i_bdev);
-		inode->i_bdev = NULL;
-	}
-	if (inode->i_cdev) {
+	if (inode->i_bdev)
+		bd_forget(inode);
+	else if (inode->i_cdev) {
 		cdput(inode->i_cdev);
 		inode->i_cdev = NULL;
 	}
@@ -631,6 +629,13 @@
 	res = 0;
 	sb = get_super(dev);
 	if (sb) {
+		/*
+		 * no need to lock the super, get_super holds the
+		 * read semaphore so the filesystem cannot go away
+		 * under us (->put_super runs with the write lock
+		 * hold).
+		 */
+		shrink_dcache_sb(sb);
 		res = invalidate_inodes(sb);
 		drop_super(sb);
 	}
@@ -659,12 +664,11 @@
 {
 	LIST_HEAD(list);
 	struct list_head *entry, *freeable = &list;
-	int count, synced = 0;
+	int count;
 	struct inode * inode;
 
 	spin_lock(&inode_lock);
 
-free_unused:
 	count = 0;
 	entry = inode_unused.prev;
 	while (entry != &inode_unused)
@@ -694,18 +698,13 @@
 	dispose_list(freeable);
 
 	/* 
-	 * If we freed enough clean inodes, avoid writing 
-	 * dirty ones. Also giveup if we already tried to
-	 * sync dirty inodes.
+	 * If we didn't freed enough clean inodes schedule
+	 * a sync of the dirty inodes, we cannot do it
+	 * from here or we're either synchronously dogslow
+	 * or we deadlock with oom.
 	 */
-	if (!goal || synced)
-		return;
-	
-	synced = 1;
-
-	spin_lock(&inode_lock);
-	try_to_sync_unused_inodes();
-	goto free_unused;
+	if (goal)
+		schedule_task(&unused_inodes_flush_task);
 }
 
 int shrink_icache_memory(int priority, int gfp_mask)
@@ -722,7 +721,7 @@
 	if (!(gfp_mask & __GFP_FS))
 		return 0;
 
-	count = inodes_stat.nr_unused >> priority;
+	count = inodes_stat.nr_unused / priority;
 
 	prune_icache(count);
 	kmem_cache_shrink(inode_cachep);
@@ -777,6 +776,7 @@
 	inode->i_nlink = 1;
 	atomic_set(&inode->i_writecount, 0);
 	inode->i_size = 0;
+	inode->i_blocks = 0;
 	inode->i_generation = 0;
 	memset(&inode->i_dquot, 0, sizeof(inode->i_dquot));
 	inode->i_pipe = NULL;
@@ -806,6 +806,8 @@
 	static unsigned long last_ino;
 	struct inode * inode;
 
+	spin_lock_prefetch(&inode_lock);
+	
 	inode = alloc_inode();
 	if (inode)
 	{
@@ -1027,6 +1029,9 @@
 	if (inode) {
 		struct super_operations *op = NULL;
 
+		if (inode->i_state == I_CLEAR)
+			BUG();
+
 		if (inode->i_sb && inode->i_sb->s_op)
 			op = inode->i_sb->s_op;
 		if (op && op->put_inode)
@@ -1049,6 +1054,8 @@
 
 			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
@@ -1161,6 +1168,8 @@
 					 NULL);
 	if (!inode_cachep)
 		panic("cannot create inode slab cache");
+
+	unused_inodes_flush_task.routine = try_to_sync_unused_inodes;
 }
 
 /**

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)