patch-2.1.49 linux/fs/inode.c

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

diff -u --recursive --new-file v2.1.48/linux/fs/inode.c linux/fs/inode.c
@@ -30,16 +30,15 @@
  * Each inode can be on two separate lists. One is
  * the hash list of the inode, used for lookups. The
  * other linked list is the "type" list:
- *  "in_use" - valid inode, hashed
- *  "dirty" - valid inode, hashed, dirty.
+ *  "in_use" - valid inode, hashed if i_nlink > 0
+ *  "dirty"  - valid inode, hashed if i_nlink > 0, dirty.
  *  "unused" - ready to be re-used. Not hashed.
  *
- * The two first versions also have a dirty list, allowing
- * for low-overhead inode sync() operations.
+ * A "dirty" list is maintained for each super block,
+ * allowing for low-overhead inode sync() operations.
  */
 
 static LIST_HEAD(inode_in_use);
-static LIST_HEAD(inode_dirty);
 static LIST_HEAD(inode_unused);
 static struct list_head inode_hashtable[HASH_SIZE];
 
@@ -59,12 +58,19 @@
 
 int max_inodes = NR_INODE;
 
+/*
+ * Put the inode on the super block's dirty list
+ */
 void __mark_inode_dirty(struct inode *inode)
 {
-	spin_lock(&inode_lock);
-	list_del(&inode->i_list);
-	list_add(&inode->i_list, &inode_dirty);
-	spin_unlock(&inode_lock);
+	struct super_block * sb = inode->i_sb;
+
+	if (sb) {
+		spin_lock(&inode_lock);
+		list_del(&inode->i_list);
+		list_add(&inode->i_list, &sb->s_dirty);
+		spin_unlock(&inode_lock);
+	}
 }
 
 static inline void unlock_inode(struct inode *inode)
@@ -169,14 +175,28 @@
 }
 
 /*
- * "sync_inodes()" goes through the dirty list 
- * and writes them out and puts them back on
- * the normal list.
+ * "sync_inodes()" goes through the super block's dirty list, 
+ * writes them out, and puts them back on the normal list.
  */
 void sync_inodes(kdev_t dev)
 {
+	struct super_block * sb = super_blocks + 0;
+	int i;
+
+	/*
+	 * Search the super_blocks array for the device(s) to sync.
+	 */
 	spin_lock(&inode_lock);
-	sync_list(&inode_dirty, &inode_in_use);
+	for (i = NR_SUPER ; i-- ; sb++) {
+		if (!sb->s_dev)
+			continue;
+		if (dev && sb->s_dev != dev)
+			continue;
+
+		sync_list(&sb->s_dirty, &inode_in_use);
+		if (dev)
+			break;
+	}
 	spin_unlock(&inode_lock);
 }
 
@@ -185,10 +205,17 @@
  */
 void write_inode_now(struct inode *inode)
 {
-	spin_lock(&inode_lock);
-	if (test_bit(I_DIRTY, &inode->i_state))
-		sync_one(&inode_dirty, &inode_in_use, &inode->i_list, inode);
-	spin_unlock(&inode_lock);
+	struct super_block * sb = inode->i_sb;
+
+	if (sb) {
+		spin_lock(&inode_lock);
+		if (test_bit(I_DIRTY, &inode->i_state))
+			sync_one(&sb->s_dirty, &inode_in_use, &inode->i_list,
+				 inode);
+		spin_unlock(&inode_lock);
+	}
+	else
+		printk("write_inode_now: no super block\n");
 }
 
 /*
@@ -232,7 +259,11 @@
 	spin_unlock(&inode_lock);
 }
 
-static int invalidate_list(struct list_head *head, kdev_t dev, struct list_head * dispose)
+/*
+ * Invalidate all inodes for a device, except for the root inode. 
+ */
+static int invalidate_list(struct list_head *head, kdev_t dev, 
+			struct inode * root, struct list_head * dispose)
 {
 	struct list_head *next;
 	int busy = 0;
@@ -248,6 +279,8 @@
 		inode = list_entry(tmp, struct inode, i_list);
 		if (inode->i_dev != dev)
 			continue;
+		if (inode == root)
+			continue;
 		if (!inode->i_count && !inode->i_state) {
 			list_del(&inode->i_hash);
 			INIT_LIST_HEAD(&inode->i_hash);
@@ -267,14 +300,16 @@
  * is because we don't want to sleep while messing
  * with the global lists..
  */
-int invalidate_inodes(kdev_t dev)
+static int invalidate_inodes_except(kdev_t dev, struct inode * root)
 {
+	struct super_block * sb = get_super(dev);
 	int busy;
 	LIST_HEAD(throw_away);
 
 	spin_lock(&inode_lock);
-	busy = invalidate_list(&inode_in_use, dev, &throw_away);
-	busy |= invalidate_list(&inode_dirty, dev, &throw_away);
+	busy = invalidate_list(&inode_in_use, dev, root, &throw_away);
+	if (sb)
+		busy |= invalidate_list(&sb->s_dirty, dev, root, &throw_away);
 	spin_unlock(&inode_lock);
 
 	dispose_list(&throw_away);
@@ -282,6 +317,11 @@
 	return busy;
 }
 
+int invalidate_inodes(kdev_t dev)
+{
+	return invalidate_inodes_except(dev, NULL);
+}
+
 /*
  * This is called with the inode lock held. It just looks at the last
  * inode on the in-use list, and if the inode is trivially freeable
@@ -534,18 +574,34 @@
 }
 
 /*
- * FIXME! These need to go through the in-use inodes to
- * check whether we can mount/umount/remount.
+ * Check whether we can mount.
  */
 int fs_may_mount(kdev_t dev)
 {
-	return 1;
+	return !invalidate_inodes(dev);
 }
 
+/*
+ * Check whether we can unmount.
+ */
 int fs_may_umount(struct super_block *sb, struct dentry * root)
 {
+	int busy;
+
 	shrink_dcache();
-	return root->d_count == 1;
+
+	if (!root->d_inode || root->d_inode->i_dev != sb->s_dev) {
+		printk("fs_may_umount: root inode not on device??\n");
+		return 0;
+	}
+
+	/*
+	 * Invalidate the inodes for this device. Device has been synced
+	 * prior to call, so there should be no dirty inodes.
+	 */
+	busy = invalidate_inodes_except(sb->s_dev, root->d_inode);
+
+	return (root->d_count == 1) && !busy;
 }
 
 /* This belongs in file_table.c, not here... */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov