diff options
-rw-r--r-- | fs/dcache.c | 2 | ||||
-rw-r--r-- | fs/file_table.c | 87 | ||||
-rw-r--r-- | include/linux/file.h | 2 | ||||
-rw-r--r-- | include/linux/fs.h | 1 | ||||
-rw-r--r-- | kernel/sysctl.c | 5 | ||||
-rw-r--r-- | net/unix/af_unix.c | 2 |
6 files changed, 62 insertions, 37 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index a173bba32666..11dc83092d4a 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -1736,7 +1736,7 @@ void __init vfs_caches_init(unsigned long mempages) | |||
1736 | SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL); | 1736 | SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL); |
1737 | 1737 | ||
1738 | filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0, | 1738 | filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0, |
1739 | SLAB_HWCACHE_ALIGN|SLAB_PANIC, filp_ctor, filp_dtor); | 1739 | SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL); |
1740 | 1740 | ||
1741 | dcache_init(mempages); | 1741 | dcache_init(mempages); |
1742 | inode_init(mempages); | 1742 | inode_init(mempages); |
diff --git a/fs/file_table.c b/fs/file_table.c index 768b58167543..44fabeaa9415 100644 --- a/fs/file_table.c +++ b/fs/file_table.c | |||
@@ -5,6 +5,7 @@ | |||
5 | * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) | 5 | * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) |
6 | */ | 6 | */ |
7 | 7 | ||
8 | #include <linux/config.h> | ||
8 | #include <linux/string.h> | 9 | #include <linux/string.h> |
9 | #include <linux/slab.h> | 10 | #include <linux/slab.h> |
10 | #include <linux/file.h> | 11 | #include <linux/file.h> |
@@ -19,52 +20,67 @@ | |||
19 | #include <linux/capability.h> | 20 | #include <linux/capability.h> |
20 | #include <linux/cdev.h> | 21 | #include <linux/cdev.h> |
21 | #include <linux/fsnotify.h> | 22 | #include <linux/fsnotify.h> |
23 | #include <linux/sysctl.h> | ||
24 | #include <linux/percpu_counter.h> | ||
25 | |||
26 | #include <asm/atomic.h> | ||
22 | 27 | ||
23 | /* sysctl tunables... */ | 28 | /* sysctl tunables... */ |
24 | struct files_stat_struct files_stat = { | 29 | struct files_stat_struct files_stat = { |
25 | .max_files = NR_FILE | 30 | .max_files = NR_FILE |
26 | }; | 31 | }; |
27 | 32 | ||
28 | EXPORT_SYMBOL(files_stat); /* Needed by unix.o */ | ||
29 | |||
30 | /* public. Not pretty! */ | 33 | /* public. Not pretty! */ |
31 | __cacheline_aligned_in_smp DEFINE_SPINLOCK(files_lock); | 34 | __cacheline_aligned_in_smp DEFINE_SPINLOCK(files_lock); |
32 | 35 | ||
33 | static DEFINE_SPINLOCK(filp_count_lock); | 36 | static struct percpu_counter nr_files __cacheline_aligned_in_smp; |
34 | 37 | ||
35 | /* slab constructors and destructors are called from arbitrary | 38 | static inline void file_free_rcu(struct rcu_head *head) |
36 | * context and must be fully threaded - use a local spinlock | ||
37 | * to protect files_stat.nr_files | ||
38 | */ | ||
39 | void filp_ctor(void *objp, struct kmem_cache *cachep, unsigned long cflags) | ||
40 | { | 39 | { |
41 | if ((cflags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == | 40 | struct file *f = container_of(head, struct file, f_u.fu_rcuhead); |
42 | SLAB_CTOR_CONSTRUCTOR) { | 41 | kmem_cache_free(filp_cachep, f); |
43 | unsigned long flags; | ||
44 | spin_lock_irqsave(&filp_count_lock, flags); | ||
45 | files_stat.nr_files++; | ||
46 | spin_unlock_irqrestore(&filp_count_lock, flags); | ||
47 | } | ||
48 | } | 42 | } |
49 | 43 | ||
50 | void filp_dtor(void *objp, struct kmem_cache *cachep, unsigned long dflags) | 44 | static inline void file_free(struct file *f) |
51 | { | 45 | { |
52 | unsigned long flags; | 46 | percpu_counter_dec(&nr_files); |
53 | spin_lock_irqsave(&filp_count_lock, flags); | 47 | call_rcu(&f->f_u.fu_rcuhead, file_free_rcu); |
54 | files_stat.nr_files--; | ||
55 | spin_unlock_irqrestore(&filp_count_lock, flags); | ||
56 | } | 48 | } |
57 | 49 | ||
58 | static inline void file_free_rcu(struct rcu_head *head) | 50 | /* |
51 | * Return the total number of open files in the system | ||
52 | */ | ||
53 | static int get_nr_files(void) | ||
59 | { | 54 | { |
60 | struct file *f = container_of(head, struct file, f_u.fu_rcuhead); | 55 | return percpu_counter_read_positive(&nr_files); |
61 | kmem_cache_free(filp_cachep, f); | ||
62 | } | 56 | } |
63 | 57 | ||
64 | static inline void file_free(struct file *f) | 58 | /* |
59 | * Return the maximum number of open files in the system | ||
60 | */ | ||
61 | int get_max_files(void) | ||
65 | { | 62 | { |
66 | call_rcu(&f->f_u.fu_rcuhead, file_free_rcu); | 63 | return files_stat.max_files; |
67 | } | 64 | } |
65 | EXPORT_SYMBOL_GPL(get_max_files); | ||
66 | |||
67 | /* | ||
68 | * Handle nr_files sysctl | ||
69 | */ | ||
70 | #if defined(CONFIG_SYSCTL) && defined(CONFIG_PROC_FS) | ||
71 | int proc_nr_files(ctl_table *table, int write, struct file *filp, | ||
72 | void __user *buffer, size_t *lenp, loff_t *ppos) | ||
73 | { | ||
74 | files_stat.nr_files = get_nr_files(); | ||
75 | return proc_dointvec(table, write, filp, buffer, lenp, ppos); | ||
76 | } | ||
77 | #else | ||
78 | int proc_nr_files(ctl_table *table, int write, struct file *filp, | ||
79 | void __user *buffer, size_t *lenp, loff_t *ppos) | ||
80 | { | ||
81 | return -ENOSYS; | ||
82 | } | ||
83 | #endif | ||
68 | 84 | ||
69 | /* Find an unused file structure and return a pointer to it. | 85 | /* Find an unused file structure and return a pointer to it. |
70 | * Returns NULL, if there are no more free file structures or | 86 | * Returns NULL, if there are no more free file structures or |
@@ -78,14 +94,20 @@ struct file *get_empty_filp(void) | |||
78 | /* | 94 | /* |
79 | * Privileged users can go above max_files | 95 | * Privileged users can go above max_files |
80 | */ | 96 | */ |
81 | if (files_stat.nr_files >= files_stat.max_files && | 97 | if (get_nr_files() >= files_stat.max_files && !capable(CAP_SYS_ADMIN)) { |
82 | !capable(CAP_SYS_ADMIN)) | 98 | /* |
83 | goto over; | 99 | * percpu_counters are inaccurate. Do an expensive check before |
100 | * we go and fail. | ||
101 | */ | ||
102 | if (percpu_counter_sum(&nr_files) >= files_stat.max_files) | ||
103 | goto over; | ||
104 | } | ||
84 | 105 | ||
85 | f = kmem_cache_alloc(filp_cachep, GFP_KERNEL); | 106 | f = kmem_cache_alloc(filp_cachep, GFP_KERNEL); |
86 | if (f == NULL) | 107 | if (f == NULL) |
87 | goto fail; | 108 | goto fail; |
88 | 109 | ||
110 | percpu_counter_inc(&nr_files); | ||
89 | memset(f, 0, sizeof(*f)); | 111 | memset(f, 0, sizeof(*f)); |
90 | if (security_file_alloc(f)) | 112 | if (security_file_alloc(f)) |
91 | goto fail_sec; | 113 | goto fail_sec; |
@@ -101,10 +123,10 @@ struct file *get_empty_filp(void) | |||
101 | 123 | ||
102 | over: | 124 | over: |
103 | /* Ran out of filps - report that */ | 125 | /* Ran out of filps - report that */ |
104 | if (files_stat.nr_files > old_max) { | 126 | if (get_nr_files() > old_max) { |
105 | printk(KERN_INFO "VFS: file-max limit %d reached\n", | 127 | printk(KERN_INFO "VFS: file-max limit %d reached\n", |
106 | files_stat.max_files); | 128 | get_max_files()); |
107 | old_max = files_stat.nr_files; | 129 | old_max = get_nr_files(); |
108 | } | 130 | } |
109 | goto fail; | 131 | goto fail; |
110 | 132 | ||
@@ -276,4 +298,5 @@ void __init files_init(unsigned long mempages) | |||
276 | if (files_stat.max_files < NR_FILE) | 298 | if (files_stat.max_files < NR_FILE) |
277 | files_stat.max_files = NR_FILE; | 299 | files_stat.max_files = NR_FILE; |
278 | files_defer_init(); | 300 | files_defer_init(); |
301 | percpu_counter_init(&nr_files); | ||
279 | } | 302 | } |
diff --git a/include/linux/file.h b/include/linux/file.h index 418b6101b59a..9901b850f2e4 100644 --- a/include/linux/file.h +++ b/include/linux/file.h | |||
@@ -60,8 +60,6 @@ extern void put_filp(struct file *); | |||
60 | extern int get_unused_fd(void); | 60 | extern int get_unused_fd(void); |
61 | extern void FASTCALL(put_unused_fd(unsigned int fd)); | 61 | extern void FASTCALL(put_unused_fd(unsigned int fd)); |
62 | struct kmem_cache; | 62 | struct kmem_cache; |
63 | extern void filp_ctor(void * objp, struct kmem_cache *cachep, unsigned long cflags); | ||
64 | extern void filp_dtor(void * objp, struct kmem_cache *cachep, unsigned long dflags); | ||
65 | 63 | ||
66 | extern struct file ** alloc_fd_array(int); | 64 | extern struct file ** alloc_fd_array(int); |
67 | extern void free_fd_array(struct file **, int); | 65 | extern void free_fd_array(struct file **, int); |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 0cc34b1c42c9..51c0c93bdf93 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -35,6 +35,7 @@ struct files_stat_struct { | |||
35 | int max_files; /* tunable */ | 35 | int max_files; /* tunable */ |
36 | }; | 36 | }; |
37 | extern struct files_stat_struct files_stat; | 37 | extern struct files_stat_struct files_stat; |
38 | extern int get_max_files(void); | ||
38 | 39 | ||
39 | struct inodes_stat_t { | 40 | struct inodes_stat_t { |
40 | int nr_inodes; | 41 | int nr_inodes; |
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index de2d9109194e..32b48e8ee36e 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
@@ -50,6 +50,9 @@ | |||
50 | #include <asm/uaccess.h> | 50 | #include <asm/uaccess.h> |
51 | #include <asm/processor.h> | 51 | #include <asm/processor.h> |
52 | 52 | ||
53 | extern int proc_nr_files(ctl_table *table, int write, struct file *filp, | ||
54 | void __user *buffer, size_t *lenp, loff_t *ppos); | ||
55 | |||
53 | #if defined(CONFIG_SYSCTL) | 56 | #if defined(CONFIG_SYSCTL) |
54 | 57 | ||
55 | /* External variables not in a header file. */ | 58 | /* External variables not in a header file. */ |
@@ -943,7 +946,7 @@ static ctl_table fs_table[] = { | |||
943 | .data = &files_stat, | 946 | .data = &files_stat, |
944 | .maxlen = 3*sizeof(int), | 947 | .maxlen = 3*sizeof(int), |
945 | .mode = 0444, | 948 | .mode = 0444, |
946 | .proc_handler = &proc_dointvec, | 949 | .proc_handler = &proc_nr_files, |
947 | }, | 950 | }, |
948 | { | 951 | { |
949 | .ctl_name = FS_MAXFILE, | 952 | .ctl_name = FS_MAXFILE, |
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 1b5989b1b670..c323cc6a28b0 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c | |||
@@ -547,7 +547,7 @@ static struct sock * unix_create1(struct socket *sock) | |||
547 | struct sock *sk = NULL; | 547 | struct sock *sk = NULL; |
548 | struct unix_sock *u; | 548 | struct unix_sock *u; |
549 | 549 | ||
550 | if (atomic_read(&unix_nr_socks) >= 2*files_stat.max_files) | 550 | if (atomic_read(&unix_nr_socks) >= 2*get_max_files()) |
551 | goto out; | 551 | goto out; |
552 | 552 | ||
553 | sk = sk_alloc(PF_UNIX, GFP_KERNEL, &unix_proto, 1); | 553 | sk = sk_alloc(PF_UNIX, GFP_KERNEL, &unix_proto, 1); |