|
@@ -5,6 +5,7 @@
|
|
* Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
|
|
* Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
+#include <linux/config.h>
|
|
#include <linux/string.h>
|
|
#include <linux/string.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/file.h>
|
|
#include <linux/file.h>
|
|
@@ -19,52 +20,67 @@
|
|
#include <linux/capability.h>
|
|
#include <linux/capability.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/fsnotify.h>
|
|
#include <linux/fsnotify.h>
|
|
|
|
+#include <linux/sysctl.h>
|
|
|
|
+#include <linux/percpu_counter.h>
|
|
|
|
+
|
|
|
|
+#include <asm/atomic.h>
|
|
|
|
|
|
/* sysctl tunables... */
|
|
/* sysctl tunables... */
|
|
struct files_stat_struct files_stat = {
|
|
struct files_stat_struct files_stat = {
|
|
.max_files = NR_FILE
|
|
.max_files = NR_FILE
|
|
};
|
|
};
|
|
|
|
|
|
-EXPORT_SYMBOL(files_stat); /* Needed by unix.o */
|
|
|
|
-
|
|
|
|
/* public. Not pretty! */
|
|
/* 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.
|
|
/* Find an unused file structure and return a pointer to it.
|
|
* Returns NULL, if there are no more free file structures or
|
|
* 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
|
|
* 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);
|
|
f = kmem_cache_alloc(filp_cachep, GFP_KERNEL);
|
|
if (f == NULL)
|
|
if (f == NULL)
|
|
goto fail;
|
|
goto fail;
|
|
|
|
|
|
|
|
+ percpu_counter_inc(&nr_files);
|
|
memset(f, 0, sizeof(*f));
|
|
memset(f, 0, sizeof(*f));
|
|
if (security_file_alloc(f))
|
|
if (security_file_alloc(f))
|
|
goto fail_sec;
|
|
goto fail_sec;
|
|
@@ -101,10 +123,10 @@ struct file *get_empty_filp(void)
|
|
|
|
|
|
over:
|
|
over:
|
|
/* Ran out of filps - report that */
|
|
/* 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",
|
|
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;
|
|
goto fail;
|
|
|
|
|
|
@@ -276,4 +298,5 @@ void __init files_init(unsigned long mempages)
|
|
if (files_stat.max_files < NR_FILE)
|
|
if (files_stat.max_files < NR_FILE)
|
|
files_stat.max_files = NR_FILE;
|
|
files_stat.max_files = NR_FILE;
|
|
files_defer_init();
|
|
files_defer_init();
|
|
|
|
+ percpu_counter_init(&nr_files);
|
|
}
|
|
}
|