summaryrefslogtreecommitdiffstats
path: root/fs/file_table.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-09-28 22:25:29 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-09-28 22:25:29 +0000
commit0ae8dceaebe3659ee0c3352c08125f403e77ebca (patch)
tree5085c389f09da78182b899d19fe1068b619a69dd /fs/file_table.c
parent273767781288c35c9d679e908672b9996cda4c34 (diff)
Merge with 2.3.10.
Diffstat (limited to 'fs/file_table.c')
-rw-r--r--fs/file_table.c132
1 files changed, 87 insertions, 45 deletions
diff --git a/fs/file_table.c b/fs/file_table.c
index 80c3a08ba..cb9ef16e9 100644
--- a/fs/file_table.c
+++ b/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_files = 0; /* read only */
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 @@ void __init file_table_init(void)
/* 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 @@ struct file * get_empty_filp(void)
* 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 @@ struct file * get_empty_filp(void)
printk("VFS: file-max limit %d reached\n", max_files);
old_max = max_files;
}
+ file_list_unlock();
return NULL;
}
@@ -131,20 +116,77 @@ int init_private_file(struct file *filp, struct dentry *dentry, int mode)
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;
}