patch-2.3.10 linux/fs/file_table.c

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

diff -u --recursive --new-file v2.3.9/linux/fs/file_table.c linux/fs/file_table.c
@@ -9,6 +9,7 @@
 #include <linux/slab.h>
 #include <linux/file.h>
 #include <linux/init.h>
+#include <linux/smp_lock.h>
 
 /* SLAB cache for filp's. */
 static kmem_cache_t *filp_cache;
@@ -18,37 +19,12 @@
 int nr_free_files = 0;	/* read only */
 int max_files = NR_FILE;/* tunable */
 
-/* Free list management, if you are here you must have f_count == 0 */
-static struct file * free_filps = NULL;
-
-static void insert_file_free(struct file *file)
-{
-	if((file->f_next = free_filps) != NULL)
-		free_filps->f_pprev = &file->f_next;
-	free_filps = file;
-	file->f_pprev = &free_filps;
-	nr_free_files++;
-}
-
-/* The list of in-use filp's must be exported (ugh...) */
-struct file *inuse_filps = NULL;
-
-static inline void put_inuse(struct file *file)
-{
-	if((file->f_next = inuse_filps) != NULL)
-		inuse_filps->f_pprev = &file->f_next;
-	inuse_filps = file;
-	file->f_pprev = &inuse_filps;
-}
-
-/* It does not matter which list it is on. */
-static inline void remove_filp(struct file *file)
-{
-	if(file->f_next)
-		file->f_next->f_pprev = file->f_pprev;
-	*file->f_pprev = file->f_next;
-}
-
+/* Here the new files go */
+static LIST_HEAD(anon_list);
+/* And here the free ones sit */
+static LIST_HEAD(free_list);
+/* public *and* exported. Not pretty! */
+spinlock_t files_lock = SPIN_LOCK_UNLOCKED;
 
 void __init file_table_init(void)
 {
@@ -67,24 +43,30 @@
 /* Find an unused file structure and return a pointer to it.
  * Returns NULL, if there are no more free file structures or
  * we run out of memory.
+ *
+ * SMP-safe.
  */
 struct file * get_empty_filp(void)
 {
 	static int old_max = 0;
 	struct file * f;
 
+	file_list_lock();
 	if (nr_free_files > NR_RESERVED_FILES) {
 	used_one:
-		f = free_filps;
-		remove_filp(f);
+		f = list_entry(free_list.next, struct file, f_list);
+		list_del(&f->f_list);
 		nr_free_files--;
 	new_one:
+		file_list_unlock();
 		memset(f, 0, sizeof(*f));
-		atomic_set(&f->f_count, 1);
+		atomic_set(&f->f_count,1);
 		f->f_version = ++event;
 		f->f_uid = current->fsuid;
 		f->f_gid = current->fsgid;
-		put_inuse(f);
+		file_list_lock();
+		list_add(&f->f_list, &anon_list);
+		file_list_unlock();
 		return f;
 	}
 	/*
@@ -96,7 +78,9 @@
 	 * Allocate a new one if we're below the limit.
 	 */
 	if (nr_files < max_files) {
+		file_list_unlock();
 		f = kmem_cache_alloc(filp_cache, SLAB_KERNEL);
+		file_list_lock();
 		if (f) {
 			nr_files++;
 			goto new_one;
@@ -108,6 +92,7 @@
 		printk("VFS: file-max limit %d reached\n", max_files);
 		old_max = max_files;
 	}
+	file_list_unlock();
 	return NULL;
 }
 
@@ -131,20 +116,77 @@
 		return 0;
 }
 
-void fput(struct file *file)
+void _fput(struct file *file)
 {
-	if (atomic_dec_and_test(&file->f_count)) {
-		locks_remove_flock(file);
-		__fput(file);
-		remove_filp(file);
-		insert_file_free(file);
-	}
+	atomic_inc(&file->f_count);
+
+	lock_kernel();
+	locks_remove_flock(file);	/* Still need the */
+	__fput(file);			/* big lock here. */
+	unlock_kernel();
+
+	atomic_set(&file->f_count, 0);
+	file_list_lock();
+	list_del(&file->f_list);
+	list_add(&file->f_list, &free_list);
+	nr_free_files++;
+	file_list_unlock();
 }
 
+/* Here. put_filp() is SMP-safe now. */
+
 void put_filp(struct file *file)
 {
-	if (atomic_dec_and_test(&file->f_count)) {
-		remove_filp(file);
-		insert_file_free(file);
+	if(atomic_dec_and_test(&file->f_count)) {
+		file_list_lock();
+		list_del(&file->f_list);
+		list_add(&file->f_list, &free_list);
+		nr_free_files++;
+		file_list_unlock();
+	}
+}
+
+void file_move(struct file *file, struct list_head *list)
+{
+	if (!list)
+		return;
+	file_list_lock();
+	list_del(&file->f_list);
+	list_add(&file->f_list, list);
+	file_list_unlock();
+}
+
+void file_moveto(struct file *new, struct file *old)
+{
+	file_list_lock();
+	list_del(&new->f_list);
+	list_add(&new->f_list, &old->f_list);
+	file_list_unlock();
+}
+
+int fs_may_remount_ro(struct super_block *sb)
+{
+	struct list_head *p;
+
+	/* Check that no files are currently opened for writing. */
+	file_list_lock();
+	for (p = sb->s_files.next; p != &sb->s_files; p = p->next) {
+		struct file *file = list_entry(p, struct file, f_list);
+		struct inode *inode = file->f_dentry->d_inode;
+		if (!inode)
+			continue;
+
+		/* File with pending delete? */
+		if (inode->i_nlink == 0)
+			goto too_bad;
+
+		/* Writable file? */
+		if (S_ISREG(inode->i_mode) && (file->f_mode & FMODE_WRITE))
+			return 0;
 	}
+	file_list_unlock();
+	return 1; /* Tis' cool bro. */
+too_bad:
+	file_list_unlock();
+	return 0;
 }

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