diff options
| author | Steve French <sfrench@us.ibm.com> | 2006-03-20 16:58:09 +0000 | 
|---|---|---|
| committer | Steve French <sfrench@us.ibm.com> | 2006-03-20 16:58:09 +0000 | 
| commit | fd4a0b92db6a57cba8d03efbe1cebf91f9124ce0 (patch) | |
| tree | 5886a08bfa1132058b06074f4666a36dc5ddd2a1 /fs/file_table.c | |
| parent | 88274815f7477dc7550439413ab87c5ce4c5a623 (diff) | |
| parent | 7705a8792b0fc82fd7d4dd923724606bbfd9fb20 (diff) | |
Merge with /pub/scm/linux/kernel/git/torvalds/linux-2.6.git
Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/file_table.c')
| -rw-r--r-- | fs/file_table.c | 87 | 
1 files changed, 55 insertions, 32 deletions
| diff --git a/fs/file_table.c b/fs/file_table.c index 768b5816754..44fabeaa941 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 <linux/config.h>  #include <linux/string.h>  #include <linux/slab.h>  #include <linux/file.h> @@ -19,52 +20,67 @@  #include <linux/capability.h>  #include <linux/cdev.h>  #include <linux/fsnotify.h> +#include <linux/sysctl.h> +#include <linux/percpu_counter.h> + +#include <asm/atomic.h>  /* 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 DEFINE_SPINLOCK(filp_count_lock); +static struct percpu_counter nr_files __cacheline_aligned_in_smp; -/* slab constructors and destructors are called from arbitrary - * context and must be fully threaded - use a local spinlock - * to protect files_stat.nr_files - */ -void filp_ctor(void *objp, struct kmem_cache *cachep, unsigned long cflags) +static inline void file_free_rcu(struct rcu_head *head)  { -	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); -	} +	struct file *f =  container_of(head, struct file, f_u.fu_rcuhead); +	kmem_cache_free(filp_cachep, f);  } -void filp_dtor(void *objp, struct kmem_cache *cachep, unsigned long dflags) +static inline void file_free(struct file *f)  { -	unsigned long flags; -	spin_lock_irqsave(&filp_count_lock, flags); -	files_stat.nr_files--; -	spin_unlock_irqrestore(&filp_count_lock, flags); +	percpu_counter_dec(&nr_files); +	call_rcu(&f->f_u.fu_rcuhead, file_free_rcu);  } -static inline void file_free_rcu(struct rcu_head *head) +/* + * Return the total number of open files in the system + */ +static int get_nr_files(void)  { -	struct file *f =  container_of(head, struct file, f_u.fu_rcuhead); -	kmem_cache_free(filp_cachep, f); +	return percpu_counter_read_positive(&nr_files);  } -static inline void file_free(struct file *f) +/* + * Return the maximum number of open files in the system + */ +int get_max_files(void)  { -	call_rcu(&f->f_u.fu_rcuhead, file_free_rcu); +	return files_stat.max_files;  } +EXPORT_SYMBOL_GPL(get_max_files); + +/* + * 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) +{ +	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 @@ -78,14 +94,20 @@ struct file *get_empty_filp(void)  	/*  	 * Privileged users can go above max_files  	 */ -	if (files_stat.nr_files >= files_stat.max_files && -				!capable(CAP_SYS_ADMIN)) -		goto over; +	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; @@ -101,10 +123,10 @@ struct file *get_empty_filp(void)  over:  	/* Ran out of filps - report that */ -	if (files_stat.nr_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.nr_files; +					get_max_files()); +		old_max = get_nr_files();  	}  	goto fail; @@ -276,4 +298,5 @@ void __init files_init(unsigned long mempages)  	if (files_stat.max_files < NR_FILE)  		files_stat.max_files = NR_FILE;  	files_defer_init(); +	percpu_counter_init(&nr_files);  }  | 
