diff options
author | Ingo Molnar <mingo@elte.hu> | 2010-08-31 03:45:21 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-08-31 03:45:46 -0400 |
commit | daab7fc734a53fdeaf844b7c03053118ad1769da (patch) | |
tree | 575deb3cdcc6dda562acaed6f7c29bc81ae01cf2 /fs/file_table.c | |
parent | 774ea0bcb27f57b6fd521b3b6c43237782fed4b9 (diff) | |
parent | 2bfc96a127bc1cc94d26bfaa40159966064f9c8c (diff) |
Merge commit 'v2.6.36-rc3' into x86/memblock
Conflicts:
arch/x86/kernel/trampoline.c
mm/memblock.c
Merge reason: Resolve the conflicts, update to latest upstream.
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'fs/file_table.c')
-rw-r--r-- | fs/file_table.c | 143 |
1 files changed, 109 insertions, 34 deletions
diff --git a/fs/file_table.c b/fs/file_table.c index 5c7d10ead4ad..a04bdd81c11c 100644 --- a/fs/file_table.c +++ b/fs/file_table.c | |||
@@ -20,7 +20,9 @@ | |||
20 | #include <linux/cdev.h> | 20 | #include <linux/cdev.h> |
21 | #include <linux/fsnotify.h> | 21 | #include <linux/fsnotify.h> |
22 | #include <linux/sysctl.h> | 22 | #include <linux/sysctl.h> |
23 | #include <linux/lglock.h> | ||
23 | #include <linux/percpu_counter.h> | 24 | #include <linux/percpu_counter.h> |
25 | #include <linux/percpu.h> | ||
24 | #include <linux/ima.h> | 26 | #include <linux/ima.h> |
25 | 27 | ||
26 | #include <asm/atomic.h> | 28 | #include <asm/atomic.h> |
@@ -32,8 +34,8 @@ struct files_stat_struct files_stat = { | |||
32 | .max_files = NR_FILE | 34 | .max_files = NR_FILE |
33 | }; | 35 | }; |
34 | 36 | ||
35 | /* public. Not pretty! */ | 37 | DECLARE_LGLOCK(files_lglock); |
36 | __cacheline_aligned_in_smp DEFINE_SPINLOCK(files_lock); | 38 | DEFINE_LGLOCK(files_lglock); |
37 | 39 | ||
38 | /* SLAB cache for file structures */ | 40 | /* SLAB cache for file structures */ |
39 | static struct kmem_cache *filp_cachep __read_mostly; | 41 | static struct kmem_cache *filp_cachep __read_mostly; |
@@ -249,7 +251,7 @@ static void __fput(struct file *file) | |||
249 | cdev_put(inode->i_cdev); | 251 | cdev_put(inode->i_cdev); |
250 | fops_put(file->f_op); | 252 | fops_put(file->f_op); |
251 | put_pid(file->f_owner.pid); | 253 | put_pid(file->f_owner.pid); |
252 | file_kill(file); | 254 | file_sb_list_del(file); |
253 | if (file->f_mode & FMODE_WRITE) | 255 | if (file->f_mode & FMODE_WRITE) |
254 | drop_file_write_access(file); | 256 | drop_file_write_access(file); |
255 | file->f_path.dentry = NULL; | 257 | file->f_path.dentry = NULL; |
@@ -289,11 +291,20 @@ struct file *fget(unsigned int fd) | |||
289 | EXPORT_SYMBOL(fget); | 291 | EXPORT_SYMBOL(fget); |
290 | 292 | ||
291 | /* | 293 | /* |
292 | * Lightweight file lookup - no refcnt increment if fd table isn't shared. | 294 | * Lightweight file lookup - no refcnt increment if fd table isn't shared. |
293 | * You can use this only if it is guranteed that the current task already | 295 | * |
294 | * holds a refcnt to that file. That check has to be done at fget() only | 296 | * You can use this instead of fget if you satisfy all of the following |
295 | * and a flag is returned to be passed to the corresponding fput_light(). | 297 | * conditions: |
296 | * There must not be a cloning between an fget_light/fput_light pair. | 298 | * 1) You must call fput_light before exiting the syscall and returning control |
299 | * to userspace (i.e. you cannot remember the returned struct file * after | ||
300 | * returning to userspace). | ||
301 | * 2) You must not call filp_close on the returned struct file * in between | ||
302 | * calls to fget_light and fput_light. | ||
303 | * 3) You must not clone the current task in between the calls to fget_light | ||
304 | * and fput_light. | ||
305 | * | ||
306 | * The fput_needed flag returned by fget_light should be passed to the | ||
307 | * corresponding fput_light. | ||
297 | */ | 308 | */ |
298 | struct file *fget_light(unsigned int fd, int *fput_needed) | 309 | struct file *fget_light(unsigned int fd, int *fput_needed) |
299 | { | 310 | { |
@@ -319,41 +330,107 @@ struct file *fget_light(unsigned int fd, int *fput_needed) | |||
319 | return file; | 330 | return file; |
320 | } | 331 | } |
321 | 332 | ||
322 | |||
323 | void put_filp(struct file *file) | 333 | void put_filp(struct file *file) |
324 | { | 334 | { |
325 | if (atomic_long_dec_and_test(&file->f_count)) { | 335 | if (atomic_long_dec_and_test(&file->f_count)) { |
326 | security_file_free(file); | 336 | security_file_free(file); |
327 | file_kill(file); | 337 | file_sb_list_del(file); |
328 | file_free(file); | 338 | file_free(file); |
329 | } | 339 | } |
330 | } | 340 | } |
331 | 341 | ||
332 | void file_move(struct file *file, struct list_head *list) | 342 | static inline int file_list_cpu(struct file *file) |
333 | { | 343 | { |
334 | if (!list) | 344 | #ifdef CONFIG_SMP |
335 | return; | 345 | return file->f_sb_list_cpu; |
336 | file_list_lock(); | 346 | #else |
337 | list_move(&file->f_u.fu_list, list); | 347 | return smp_processor_id(); |
338 | file_list_unlock(); | 348 | #endif |
339 | } | 349 | } |
340 | 350 | ||
341 | void file_kill(struct file *file) | 351 | /* helper for file_sb_list_add to reduce ifdefs */ |
352 | static inline void __file_sb_list_add(struct file *file, struct super_block *sb) | ||
353 | { | ||
354 | struct list_head *list; | ||
355 | #ifdef CONFIG_SMP | ||
356 | int cpu; | ||
357 | cpu = smp_processor_id(); | ||
358 | file->f_sb_list_cpu = cpu; | ||
359 | list = per_cpu_ptr(sb->s_files, cpu); | ||
360 | #else | ||
361 | list = &sb->s_files; | ||
362 | #endif | ||
363 | list_add(&file->f_u.fu_list, list); | ||
364 | } | ||
365 | |||
366 | /** | ||
367 | * file_sb_list_add - add a file to the sb's file list | ||
368 | * @file: file to add | ||
369 | * @sb: sb to add it to | ||
370 | * | ||
371 | * Use this function to associate a file with the superblock of the inode it | ||
372 | * refers to. | ||
373 | */ | ||
374 | void file_sb_list_add(struct file *file, struct super_block *sb) | ||
375 | { | ||
376 | lg_local_lock(files_lglock); | ||
377 | __file_sb_list_add(file, sb); | ||
378 | lg_local_unlock(files_lglock); | ||
379 | } | ||
380 | |||
381 | /** | ||
382 | * file_sb_list_del - remove a file from the sb's file list | ||
383 | * @file: file to remove | ||
384 | * @sb: sb to remove it from | ||
385 | * | ||
386 | * Use this function to remove a file from its superblock. | ||
387 | */ | ||
388 | void file_sb_list_del(struct file *file) | ||
342 | { | 389 | { |
343 | if (!list_empty(&file->f_u.fu_list)) { | 390 | if (!list_empty(&file->f_u.fu_list)) { |
344 | file_list_lock(); | 391 | lg_local_lock_cpu(files_lglock, file_list_cpu(file)); |
345 | list_del_init(&file->f_u.fu_list); | 392 | list_del_init(&file->f_u.fu_list); |
346 | file_list_unlock(); | 393 | lg_local_unlock_cpu(files_lglock, file_list_cpu(file)); |
347 | } | 394 | } |
348 | } | 395 | } |
349 | 396 | ||
397 | #ifdef CONFIG_SMP | ||
398 | |||
399 | /* | ||
400 | * These macros iterate all files on all CPUs for a given superblock. | ||
401 | * files_lglock must be held globally. | ||
402 | */ | ||
403 | #define do_file_list_for_each_entry(__sb, __file) \ | ||
404 | { \ | ||
405 | int i; \ | ||
406 | for_each_possible_cpu(i) { \ | ||
407 | struct list_head *list; \ | ||
408 | list = per_cpu_ptr((__sb)->s_files, i); \ | ||
409 | list_for_each_entry((__file), list, f_u.fu_list) | ||
410 | |||
411 | #define while_file_list_for_each_entry \ | ||
412 | } \ | ||
413 | } | ||
414 | |||
415 | #else | ||
416 | |||
417 | #define do_file_list_for_each_entry(__sb, __file) \ | ||
418 | { \ | ||
419 | struct list_head *list; \ | ||
420 | list = &(sb)->s_files; \ | ||
421 | list_for_each_entry((__file), list, f_u.fu_list) | ||
422 | |||
423 | #define while_file_list_for_each_entry \ | ||
424 | } | ||
425 | |||
426 | #endif | ||
427 | |||
350 | int fs_may_remount_ro(struct super_block *sb) | 428 | int fs_may_remount_ro(struct super_block *sb) |
351 | { | 429 | { |
352 | struct file *file; | 430 | struct file *file; |
353 | |||
354 | /* Check that no files are currently opened for writing. */ | 431 | /* Check that no files are currently opened for writing. */ |
355 | file_list_lock(); | 432 | lg_global_lock(files_lglock); |
356 | list_for_each_entry(file, &sb->s_files, f_u.fu_list) { | 433 | do_file_list_for_each_entry(sb, file) { |
357 | struct inode *inode = file->f_path.dentry->d_inode; | 434 | struct inode *inode = file->f_path.dentry->d_inode; |
358 | 435 | ||
359 | /* File with pending delete? */ | 436 | /* File with pending delete? */ |
@@ -363,11 +440,11 @@ int fs_may_remount_ro(struct super_block *sb) | |||
363 | /* Writeable file? */ | 440 | /* Writeable file? */ |
364 | if (S_ISREG(inode->i_mode) && (file->f_mode & FMODE_WRITE)) | 441 | if (S_ISREG(inode->i_mode) && (file->f_mode & FMODE_WRITE)) |
365 | goto too_bad; | 442 | goto too_bad; |
366 | } | 443 | } while_file_list_for_each_entry; |
367 | file_list_unlock(); | 444 | lg_global_unlock(files_lglock); |
368 | return 1; /* Tis' cool bro. */ | 445 | return 1; /* Tis' cool bro. */ |
369 | too_bad: | 446 | too_bad: |
370 | file_list_unlock(); | 447 | lg_global_unlock(files_lglock); |
371 | return 0; | 448 | return 0; |
372 | } | 449 | } |
373 | 450 | ||
@@ -383,8 +460,8 @@ void mark_files_ro(struct super_block *sb) | |||
383 | struct file *f; | 460 | struct file *f; |
384 | 461 | ||
385 | retry: | 462 | retry: |
386 | file_list_lock(); | 463 | lg_global_lock(files_lglock); |
387 | list_for_each_entry(f, &sb->s_files, f_u.fu_list) { | 464 | do_file_list_for_each_entry(sb, f) { |
388 | struct vfsmount *mnt; | 465 | struct vfsmount *mnt; |
389 | if (!S_ISREG(f->f_path.dentry->d_inode->i_mode)) | 466 | if (!S_ISREG(f->f_path.dentry->d_inode->i_mode)) |
390 | continue; | 467 | continue; |
@@ -399,16 +476,13 @@ retry: | |||
399 | continue; | 476 | continue; |
400 | file_release_write(f); | 477 | file_release_write(f); |
401 | mnt = mntget(f->f_path.mnt); | 478 | mnt = mntget(f->f_path.mnt); |
402 | file_list_unlock(); | 479 | /* This can sleep, so we can't hold the spinlock. */ |
403 | /* | 480 | lg_global_unlock(files_lglock); |
404 | * This can sleep, so we can't hold | ||
405 | * the file_list_lock() spinlock. | ||
406 | */ | ||
407 | mnt_drop_write(mnt); | 481 | mnt_drop_write(mnt); |
408 | mntput(mnt); | 482 | mntput(mnt); |
409 | goto retry; | 483 | goto retry; |
410 | } | 484 | } while_file_list_for_each_entry; |
411 | file_list_unlock(); | 485 | lg_global_unlock(files_lglock); |
412 | } | 486 | } |
413 | 487 | ||
414 | void __init files_init(unsigned long mempages) | 488 | void __init files_init(unsigned long mempages) |
@@ -428,5 +502,6 @@ void __init files_init(unsigned long mempages) | |||
428 | if (files_stat.max_files < NR_FILE) | 502 | if (files_stat.max_files < NR_FILE) |
429 | files_stat.max_files = NR_FILE; | 503 | files_stat.max_files = NR_FILE; |
430 | files_defer_init(); | 504 | files_defer_init(); |
505 | lg_lock_init(files_lglock); | ||
431 | percpu_counter_init(&nr_files, 0); | 506 | percpu_counter_init(&nr_files, 0); |
432 | } | 507 | } |