X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Ffile_table.c;fp=fs%2Ffile_table.c;h=889491defc43fa65af9f33182e466c1c02a46b52;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=eb260df6c93788639aace22422dd4f82d5789b05;hpb=cee37fe97739d85991964371c1f3a745c00dd236;p=linux-2.6.git diff --git a/fs/file_table.c b/fs/file_table.c index eb260df6c..889491def 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -5,6 +5,7 @@ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) */ +#include #include #include #include @@ -14,50 +15,74 @@ #include #include #include +#include #include +#include #include +#include +#include +#include #include #include +#include + /* sysctl tunables... */ struct files_stat_struct files_stat = { .max_files = NR_FILE }; -EXPORT_SYMBOL(files_stat); /* Needed by unix.o */ - /* public. Not pretty! */ - __cacheline_aligned_in_smp DEFINE_SPINLOCK(files_lock); +__cacheline_aligned_in_smp DEFINE_SPINLOCK(files_lock); + +static struct percpu_counter nr_files __cacheline_aligned_in_smp; + +static inline void file_free_rcu(struct rcu_head *head) +{ + struct file *f = container_of(head, struct file, f_u.fu_rcuhead); + kmem_cache_free(filp_cachep, f); +} -static DEFINE_SPINLOCK(filp_count_lock); +static inline void file_free(struct file *f) +{ + percpu_counter_dec(&nr_files); + call_rcu(&f->f_u.fu_rcuhead, file_free_rcu); +} -/* slab constructors and destructors are called from arbitrary - * context and must be fully threaded - use a local spinlock - * to protect files_stat.nr_files +/* + * Return the total number of open files in the system */ -void filp_ctor(void * objp, struct kmem_cache_s *cachep, unsigned long cflags) +static int get_nr_files(void) { - if ((cflags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == - SLAB_CTOR_CONSTRUCTOR) { - unsigned long flags; - spin_lock_irqsave(&filp_count_lock, flags); - files_stat.nr_files++; - spin_unlock_irqrestore(&filp_count_lock, flags); - } + return percpu_counter_read_positive(&nr_files); } -void filp_dtor(void * objp, struct kmem_cache_s *cachep, unsigned long dflags) +/* + * Return the maximum number of open files in the system + */ +int get_max_files(void) { - unsigned long flags; - spin_lock_irqsave(&filp_count_lock, flags); - files_stat.nr_files--; - spin_unlock_irqrestore(&filp_count_lock, flags); + return files_stat.max_files; } +EXPORT_SYMBOL_GPL(get_max_files); -static inline void file_free(struct file *f) +/* + * Handle nr_files sysctl + */ +#if defined(CONFIG_SYSCTL) && defined(CONFIG_PROC_FS) +int proc_nr_files(ctl_table *table, int write, struct file *filp, + void __user *buffer, size_t *lenp, loff_t *ppos) { - kmem_cache_free(filp_cachep, f); + files_stat.nr_files = get_nr_files(); + return proc_dointvec(table, write, filp, buffer, lenp, ppos); } +#else +int proc_nr_files(ctl_table *table, int write, struct file *filp, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + return -ENOSYS; +} +#endif /* Find an unused file structure and return a pointer to it. * Returns NULL, if there are no more free file structures or @@ -65,44 +90,54 @@ static inline void file_free(struct file *f) */ struct file *get_empty_filp(void) { -static int old_max; + struct task_struct *tsk; + static int old_max; struct file * f; /* * Privileged users can go above max_files */ - if (files_stat.nr_files < files_stat.max_files || - capable(CAP_SYS_ADMIN)) { - f = kmem_cache_alloc(filp_cachep, GFP_KERNEL); - if (f) { - memset(f, 0, sizeof(*f)); - if (security_file_alloc(f)) { - file_free(f); - goto fail; - } - eventpoll_init_file(f); - atomic_set(&f->f_count, 1); - f->f_uid = current->fsuid; - f->f_gid = current->fsgid; - rwlock_init(&f->f_owner.lock); - /* f->f_version: 0 */ - INIT_LIST_HEAD(&f->f_list); - f->f_xid = vx_current_xid(); - vx_files_inc(f); - f->f_maxcount = INT_MAX; - return f; - } + if (get_nr_files() >= files_stat.max_files && !capable(CAP_SYS_ADMIN)) { + /* + * percpu_counters are inaccurate. Do an expensive check before + * we go and fail. + */ + if (percpu_counter_sum(&nr_files) >= files_stat.max_files) + goto over; } + f = kmem_cache_alloc(filp_cachep, GFP_KERNEL); + if (f == NULL) + goto fail; + + percpu_counter_inc(&nr_files); + memset(f, 0, sizeof(*f)); + if (security_file_alloc(f)) + goto fail_sec; + + tsk = current; + INIT_LIST_HEAD(&f->f_u.fu_list); + atomic_set(&f->f_count, 1); + rwlock_init(&f->f_owner.lock); + f->f_uid = tsk->fsuid; + f->f_gid = tsk->fsgid; + eventpoll_init_file(f); + /* f->f_version: 0 */ + f->f_xid = vx_current_xid(); + vx_files_inc(f); + return f; + +over: /* Ran out of filps - report that */ - if (files_stat.max_files >= old_max) { + if (get_nr_files() > old_max) { printk(KERN_INFO "VFS: file-max limit %d reached\n", - files_stat.max_files); - old_max = files_stat.max_files; - } else { - /* Big problems... */ - printk(KERN_WARNING "VFS: filp allocation failed\n"); + get_max_files()); + old_max = get_nr_files(); } + goto fail; + +fail_sec: + file_free(f); fail: return NULL; } @@ -127,6 +162,8 @@ void fastcall __fput(struct file *file) struct inode *inode = dentry->d_inode; might_sleep(); + + fsnotify_close(file); /* * The function eventpoll_release() should be the first called * in the file cleanup chain. @@ -157,11 +194,17 @@ struct file fastcall *fget(unsigned int fd) struct file *file; struct files_struct *files = current->files; - spin_lock(&files->file_lock); + rcu_read_lock(); file = fcheck_files(files, fd); - if (file) - get_file(file); - spin_unlock(&files->file_lock); + if (file) { + if (!atomic_inc_not_zero(&file->f_count)) { + /* File object ref couldn't be taken */ + rcu_read_unlock(); + return NULL; + } + } + rcu_read_unlock(); + return file; } @@ -183,14 +226,18 @@ struct file fastcall *fget_light(unsigned int fd, int *fput_needed) if (likely((atomic_read(&files->count) == 1))) { file = fcheck_files(files, fd); } else { - spin_lock(&files->file_lock); + rcu_read_lock(); file = fcheck_files(files, fd); if (file) { - get_file(file); - *fput_needed = 1; + if (atomic_inc_not_zero(&file->f_count)) + *fput_needed = 1; + else + /* Didn't get the reference, someone's freed */ + file = NULL; } - spin_unlock(&files->file_lock); + rcu_read_unlock(); } + return file; } @@ -211,15 +258,15 @@ void file_move(struct file *file, struct list_head *list) if (!list) return; file_list_lock(); - list_move(&file->f_list, list); + list_move(&file->f_u.fu_list, list); file_list_unlock(); } void file_kill(struct file *file) { - if (!list_empty(&file->f_list)) { + if (!list_empty(&file->f_u.fu_list)) { file_list_lock(); - list_del_init(&file->f_list); + list_del_init(&file->f_u.fu_list); file_list_unlock(); } } @@ -231,7 +278,7 @@ int fs_may_remount_ro(struct super_block *sb) /* Check that no files are currently opened for writing. */ file_list_lock(); list_for_each(p, &sb->s_files) { - struct file *file = list_entry(p, struct file, f_list); + struct file *file = list_entry(p, struct file, f_u.fu_list); struct inode *inode = file->f_dentry->d_inode; /* File with pending delete? */ @@ -260,4 +307,6 @@ void __init files_init(unsigned long mempages) files_stat.max_files = n; if (files_stat.max_files < NR_FILE) files_stat.max_files = NR_FILE; + files_defer_init(); + percpu_counter_init(&nr_files); }