patch-2.1.48 linux/fs/inode.c

Next file: linux/fs/namei.c
Previous file: linux/fs/hpfs/hpfs_fs.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.47/linux/fs/inode.c linux/fs/inode.c
@@ -206,14 +206,36 @@
 	inode->i_state = 0;
 }
 
-#define CAN_UNUSE(inode) \
-	(((inode)->i_count == 0) && \
-	 ((inode)->i_nrpages == 0) && \
-	 (!(inode)->i_state))
+/*
+ * Dispose-list gets a local list, so it doesn't need to
+ * worry about list corruption.
+ */
+static void dispose_list(struct list_head * head)
+{
+	struct list_head *next;
+
+	next = head->next;
+	for (;;) {
+		struct list_head * tmp = next;
+		struct inode * inode;
 
-static void invalidate_list(struct list_head *head, kdev_t dev)
+		next = next->next;
+		if (tmp == head)
+			break;
+		inode = list_entry(tmp, struct inode, i_list);
+		truncate_inode_pages(inode, 0);
+	}
+
+	/* Add them all to the unused list in one fell swoop */
+	spin_lock(&inode_lock);
+	list_splice(head, &inode_unused);
+	spin_unlock(&inode_lock);
+}
+
+static int invalidate_list(struct list_head *head, kdev_t dev, struct list_head * dispose)
 {
 	struct list_head *next;
+	int busy = 0;
 
 	next = head->next;
 	for (;;) {
@@ -225,22 +247,39 @@
 			break;
 		inode = list_entry(tmp, struct inode, i_list);
 		if (inode->i_dev != dev)
-			continue;		
-		if (!CAN_UNUSE(inode))
 			continue;
-		list_del(&inode->i_hash);
-		INIT_LIST_HEAD(&inode->i_hash);
-		list_del(&inode->i_list);
-		list_add(&inode->i_list, &inode_unused);
+		if (!inode->i_count && !inode->i_state) {
+			list_del(&inode->i_hash);
+			INIT_LIST_HEAD(&inode->i_hash);
+			list_del(&inode->i_list);
+			list_add(&inode->i_list, dispose);
+			continue;
+		}
+		busy = 1;
 	}
+	return busy;
 }
 
-void invalidate_inodes(kdev_t dev)
+/*
+ * This is a two-stage process. First we collect all
+ * offending inodes onto the throw-away list, and in
+ * the second stage we actually dispose of them. This
+ * is because we don't want to sleep while messing
+ * with the global lists..
+ */
+int invalidate_inodes(kdev_t dev)
 {
+	int busy;
+	LIST_HEAD(throw_away);
+
 	spin_lock(&inode_lock);
-	invalidate_list(&inode_in_use, dev);
-	invalidate_list(&inode_dirty, dev);
+	busy = invalidate_list(&inode_in_use, dev, &throw_away);
+	busy |= invalidate_list(&inode_dirty, dev, &throw_away);
 	spin_unlock(&inode_lock);
+
+	dispose_list(&throw_away);
+
+	return busy;
 }
 
 /*
@@ -251,6 +290,11 @@
  * Otherwise we just move the inode to be the first inode and expect to
  * get back to the problem later..
  */
+#define CAN_UNUSE(inode) \
+	(((inode)->i_count == 0) && \
+	 ((inode)->i_nrpages == 0) && \
+	 (!(inode)->i_state))
+
 static void try_to_free_inodes(void)
 {
 	struct list_head * tmp;
@@ -504,7 +548,22 @@
 	return root->d_count == 1;
 }
 
+/* This belongs in file_table.c, not here... */
 int fs_may_remount_ro(struct super_block *sb)
 {
-	return 1;
+	struct file *file;
+	kdev_t dev = sb->s_dev;
+
+	/* Check that no files are currently opened for writing. */
+	for (file = inuse_filps; file; file = file->f_next) {
+		struct inode *inode;
+		if (!file->f_dentry)
+			continue;
+		inode = file->f_dentry->d_inode;
+		if (!inode || inode->i_dev != dev)
+			continue;
+		if (S_ISREG(inode->i_mode) && file->f_mode & FMODE_WRITE)
+			return 0;
+	}
+	return 1; /* Tis' cool bro. */
 }

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