diff options
author | Dipankar Sarma <dipankar@in.ibm.com> | 2005-09-09 16:04:13 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-09-09 16:57:55 -0400 |
commit | ab2af1f5005069321c5d130f09cce577b03f43ef (patch) | |
tree | 73a70ba486f522cd9eeeef376ede2b5a1c1b473b /fs/file_table.c | |
parent | 6e72ad2c581de121cc7e772469e2a8f6b1fd4379 (diff) |
[PATCH] files: files struct with RCU
Patch to eliminate struct files_struct.file_lock spinlock on the reader side
and use rcu refcounting rcuref_xxx api for the f_count refcounter. The
updates to the fdtable are done by allocating a new fdtable structure and
setting files->fdt to point to the new structure. The fdtable structure is
protected by RCU thereby allowing lock-free lookup. For fd arrays/sets that
are vmalloced, we use keventd to free them since RCU callbacks can't sleep. A
global list of fdtable to be freed is not scalable, so we use a per-cpu list.
If keventd is already handling the current cpu's work, we use a timer to defer
queueing of that work.
Since the last publication, this patch has been re-written to avoid using
explicit memory barriers and use rcu_assign_pointer(), rcu_dereference()
premitives instead. This required that the fd information is kept in a
separate structure (fdtable) and updated atomically.
Signed-off-by: Dipankar Sarma <dipankar@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs/file_table.c')
-rw-r--r-- | fs/file_table.c | 40 |
1 files changed, 29 insertions, 11 deletions
diff --git a/fs/file_table.c b/fs/file_table.c index 43e9e1737de2..86ec8ae985b4 100644 --- a/fs/file_table.c +++ b/fs/file_table.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/fs.h> | 14 | #include <linux/fs.h> |
15 | #include <linux/security.h> | 15 | #include <linux/security.h> |
16 | #include <linux/eventpoll.h> | 16 | #include <linux/eventpoll.h> |
17 | #include <linux/rcupdate.h> | ||
17 | #include <linux/mount.h> | 18 | #include <linux/mount.h> |
18 | #include <linux/cdev.h> | 19 | #include <linux/cdev.h> |
19 | #include <linux/fsnotify.h> | 20 | #include <linux/fsnotify.h> |
@@ -53,11 +54,17 @@ void filp_dtor(void * objp, struct kmem_cache_s *cachep, unsigned long dflags) | |||
53 | spin_unlock_irqrestore(&filp_count_lock, flags); | 54 | spin_unlock_irqrestore(&filp_count_lock, flags); |
54 | } | 55 | } |
55 | 56 | ||
56 | static inline void file_free(struct file *f) | 57 | static inline void file_free_rcu(struct rcu_head *head) |
57 | { | 58 | { |
59 | struct file *f = container_of(head, struct file, f_rcuhead); | ||
58 | kmem_cache_free(filp_cachep, f); | 60 | kmem_cache_free(filp_cachep, f); |
59 | } | 61 | } |
60 | 62 | ||
63 | static inline void file_free(struct file *f) | ||
64 | { | ||
65 | call_rcu(&f->f_rcuhead, file_free_rcu); | ||
66 | } | ||
67 | |||
61 | /* Find an unused file structure and return a pointer to it. | 68 | /* Find an unused file structure and return a pointer to it. |
62 | * Returns NULL, if there are no more free file structures or | 69 | * Returns NULL, if there are no more free file structures or |
63 | * we run out of memory. | 70 | * we run out of memory. |
@@ -110,7 +117,7 @@ EXPORT_SYMBOL(get_empty_filp); | |||
110 | 117 | ||
111 | void fastcall fput(struct file *file) | 118 | void fastcall fput(struct file *file) |
112 | { | 119 | { |
113 | if (atomic_dec_and_test(&file->f_count)) | 120 | if (rcuref_dec_and_test(&file->f_count)) |
114 | __fput(file); | 121 | __fput(file); |
115 | } | 122 | } |
116 | 123 | ||
@@ -156,11 +163,17 @@ struct file fastcall *fget(unsigned int fd) | |||
156 | struct file *file; | 163 | struct file *file; |
157 | struct files_struct *files = current->files; | 164 | struct files_struct *files = current->files; |
158 | 165 | ||
159 | spin_lock(&files->file_lock); | 166 | rcu_read_lock(); |
160 | file = fcheck_files(files, fd); | 167 | file = fcheck_files(files, fd); |
161 | if (file) | 168 | if (file) { |
162 | get_file(file); | 169 | if (!rcuref_inc_lf(&file->f_count)) { |
163 | spin_unlock(&files->file_lock); | 170 | /* File object ref couldn't be taken */ |
171 | rcu_read_unlock(); | ||
172 | return NULL; | ||
173 | } | ||
174 | } | ||
175 | rcu_read_unlock(); | ||
176 | |||
164 | return file; | 177 | return file; |
165 | } | 178 | } |
166 | 179 | ||
@@ -182,21 +195,25 @@ struct file fastcall *fget_light(unsigned int fd, int *fput_needed) | |||
182 | if (likely((atomic_read(&files->count) == 1))) { | 195 | if (likely((atomic_read(&files->count) == 1))) { |
183 | file = fcheck_files(files, fd); | 196 | file = fcheck_files(files, fd); |
184 | } else { | 197 | } else { |
185 | spin_lock(&files->file_lock); | 198 | rcu_read_lock(); |
186 | file = fcheck_files(files, fd); | 199 | file = fcheck_files(files, fd); |
187 | if (file) { | 200 | if (file) { |
188 | get_file(file); | 201 | if (rcuref_inc_lf(&file->f_count)) |
189 | *fput_needed = 1; | 202 | *fput_needed = 1; |
203 | else | ||
204 | /* Didn't get the reference, someone's freed */ | ||
205 | file = NULL; | ||
190 | } | 206 | } |
191 | spin_unlock(&files->file_lock); | 207 | rcu_read_unlock(); |
192 | } | 208 | } |
209 | |||
193 | return file; | 210 | return file; |
194 | } | 211 | } |
195 | 212 | ||
196 | 213 | ||
197 | void put_filp(struct file *file) | 214 | void put_filp(struct file *file) |
198 | { | 215 | { |
199 | if (atomic_dec_and_test(&file->f_count)) { | 216 | if (rcuref_dec_and_test(&file->f_count)) { |
200 | security_file_free(file); | 217 | security_file_free(file); |
201 | file_kill(file); | 218 | file_kill(file); |
202 | file_free(file); | 219 | file_free(file); |
@@ -257,4 +274,5 @@ void __init files_init(unsigned long mempages) | |||
257 | files_stat.max_files = n; | 274 | files_stat.max_files = n; |
258 | if (files_stat.max_files < NR_FILE) | 275 | if (files_stat.max_files < NR_FILE) |
259 | files_stat.max_files = NR_FILE; | 276 | files_stat.max_files = NR_FILE; |
277 | files_defer_init(); | ||
260 | } | 278 | } |