diff options
| -rw-r--r-- | fs/file_table.c | 108 | ||||
| -rw-r--r-- | fs/super.c | 18 | ||||
| -rw-r--r-- | include/linux/fs.h | 7 |
3 files changed, 115 insertions, 18 deletions
diff --git a/fs/file_table.c b/fs/file_table.c index 6f0e62ecfddd..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,7 +34,8 @@ struct files_stat_struct files_stat = { | |||
| 32 | .max_files = NR_FILE | 34 | .max_files = NR_FILE |
| 33 | }; | 35 | }; |
| 34 | 36 | ||
| 35 | static __cacheline_aligned_in_smp DEFINE_SPINLOCK(files_lock); | 37 | DECLARE_LGLOCK(files_lglock); |
| 38 | DEFINE_LGLOCK(files_lglock); | ||
| 36 | 39 | ||
| 37 | /* SLAB cache for file structures */ | 40 | /* SLAB cache for file structures */ |
| 38 | static struct kmem_cache *filp_cachep __read_mostly; | 41 | static struct kmem_cache *filp_cachep __read_mostly; |
| @@ -336,30 +339,98 @@ void put_filp(struct file *file) | |||
| 336 | } | 339 | } |
| 337 | } | 340 | } |
| 338 | 341 | ||
| 342 | static inline int file_list_cpu(struct file *file) | ||
| 343 | { | ||
| 344 | #ifdef CONFIG_SMP | ||
| 345 | return file->f_sb_list_cpu; | ||
| 346 | #else | ||
| 347 | return smp_processor_id(); | ||
| 348 | #endif | ||
| 349 | } | ||
| 350 | |||
| 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 | */ | ||
| 339 | void file_sb_list_add(struct file *file, struct super_block *sb) | 374 | void file_sb_list_add(struct file *file, struct super_block *sb) |
| 340 | { | 375 | { |
| 341 | spin_lock(&files_lock); | 376 | lg_local_lock(files_lglock); |
| 342 | BUG_ON(!list_empty(&file->f_u.fu_list)); | 377 | __file_sb_list_add(file, sb); |
| 343 | list_add(&file->f_u.fu_list, &sb->s_files); | 378 | lg_local_unlock(files_lglock); |
| 344 | spin_unlock(&files_lock); | ||
| 345 | } | 379 | } |
| 346 | 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 | */ | ||
| 347 | void file_sb_list_del(struct file *file) | 388 | void file_sb_list_del(struct file *file) |
| 348 | { | 389 | { |
| 349 | if (!list_empty(&file->f_u.fu_list)) { | 390 | if (!list_empty(&file->f_u.fu_list)) { |
| 350 | spin_lock(&files_lock); | 391 | lg_local_lock_cpu(files_lglock, file_list_cpu(file)); |
| 351 | list_del_init(&file->f_u.fu_list); | 392 | list_del_init(&file->f_u.fu_list); |
| 352 | spin_unlock(&files_lock); | 393 | lg_local_unlock_cpu(files_lglock, file_list_cpu(file)); |
| 353 | } | 394 | } |
| 354 | } | 395 | } |
| 355 | 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 | |||
| 356 | int fs_may_remount_ro(struct super_block *sb) | 428 | int fs_may_remount_ro(struct super_block *sb) |
| 357 | { | 429 | { |
| 358 | struct file *file; | 430 | struct file *file; |
| 359 | |||
| 360 | /* Check that no files are currently opened for writing. */ | 431 | /* Check that no files are currently opened for writing. */ |
| 361 | spin_lock(&files_lock); | 432 | lg_global_lock(files_lglock); |
| 362 | list_for_each_entry(file, &sb->s_files, f_u.fu_list) { | 433 | do_file_list_for_each_entry(sb, file) { |
| 363 | struct inode *inode = file->f_path.dentry->d_inode; | 434 | struct inode *inode = file->f_path.dentry->d_inode; |
| 364 | 435 | ||
| 365 | /* File with pending delete? */ | 436 | /* File with pending delete? */ |
| @@ -369,11 +440,11 @@ int fs_may_remount_ro(struct super_block *sb) | |||
| 369 | /* Writeable file? */ | 440 | /* Writeable file? */ |
| 370 | if (S_ISREG(inode->i_mode) && (file->f_mode & FMODE_WRITE)) | 441 | if (S_ISREG(inode->i_mode) && (file->f_mode & FMODE_WRITE)) |
| 371 | goto too_bad; | 442 | goto too_bad; |
| 372 | } | 443 | } while_file_list_for_each_entry; |
| 373 | spin_unlock(&files_lock); | 444 | lg_global_unlock(files_lglock); |
| 374 | return 1; /* Tis' cool bro. */ | 445 | return 1; /* Tis' cool bro. */ |
| 375 | too_bad: | 446 | too_bad: |
| 376 | spin_unlock(&files_lock); | 447 | lg_global_unlock(files_lglock); |
| 377 | return 0; | 448 | return 0; |
| 378 | } | 449 | } |
| 379 | 450 | ||
| @@ -389,8 +460,8 @@ void mark_files_ro(struct super_block *sb) | |||
| 389 | struct file *f; | 460 | struct file *f; |
| 390 | 461 | ||
| 391 | retry: | 462 | retry: |
| 392 | spin_lock(&files_lock); | 463 | lg_global_lock(files_lglock); |
| 393 | list_for_each_entry(f, &sb->s_files, f_u.fu_list) { | 464 | do_file_list_for_each_entry(sb, f) { |
| 394 | struct vfsmount *mnt; | 465 | struct vfsmount *mnt; |
| 395 | if (!S_ISREG(f->f_path.dentry->d_inode->i_mode)) | 466 | if (!S_ISREG(f->f_path.dentry->d_inode->i_mode)) |
| 396 | continue; | 467 | continue; |
| @@ -406,12 +477,12 @@ retry: | |||
| 406 | file_release_write(f); | 477 | file_release_write(f); |
| 407 | mnt = mntget(f->f_path.mnt); | 478 | mnt = mntget(f->f_path.mnt); |
| 408 | /* This can sleep, so we can't hold the spinlock. */ | 479 | /* This can sleep, so we can't hold the spinlock. */ |
| 409 | spin_unlock(&files_lock); | 480 | lg_global_unlock(files_lglock); |
| 410 | mnt_drop_write(mnt); | 481 | mnt_drop_write(mnt); |
| 411 | mntput(mnt); | 482 | mntput(mnt); |
| 412 | goto retry; | 483 | goto retry; |
| 413 | } | 484 | } while_file_list_for_each_entry; |
| 414 | spin_unlock(&files_lock); | 485 | lg_global_unlock(files_lglock); |
| 415 | } | 486 | } |
| 416 | 487 | ||
| 417 | void __init files_init(unsigned long mempages) | 488 | void __init files_init(unsigned long mempages) |
| @@ -431,5 +502,6 @@ void __init files_init(unsigned long mempages) | |||
| 431 | if (files_stat.max_files < NR_FILE) | 502 | if (files_stat.max_files < NR_FILE) |
| 432 | files_stat.max_files = NR_FILE; | 503 | files_stat.max_files = NR_FILE; |
| 433 | files_defer_init(); | 504 | files_defer_init(); |
| 505 | lg_lock_init(files_lglock); | ||
| 434 | percpu_counter_init(&nr_files, 0); | 506 | percpu_counter_init(&nr_files, 0); |
| 435 | } | 507 | } |
diff --git a/fs/super.c b/fs/super.c index 9674ab2c8718..8819e3a7ff20 100644 --- a/fs/super.c +++ b/fs/super.c | |||
| @@ -54,7 +54,22 @@ static struct super_block *alloc_super(struct file_system_type *type) | |||
| 54 | s = NULL; | 54 | s = NULL; |
| 55 | goto out; | 55 | goto out; |
| 56 | } | 56 | } |
| 57 | #ifdef CONFIG_SMP | ||
| 58 | s->s_files = alloc_percpu(struct list_head); | ||
| 59 | if (!s->s_files) { | ||
| 60 | security_sb_free(s); | ||
| 61 | kfree(s); | ||
| 62 | s = NULL; | ||
| 63 | goto out; | ||
| 64 | } else { | ||
| 65 | int i; | ||
| 66 | |||
| 67 | for_each_possible_cpu(i) | ||
| 68 | INIT_LIST_HEAD(per_cpu_ptr(s->s_files, i)); | ||
| 69 | } | ||
| 70 | #else | ||
| 57 | INIT_LIST_HEAD(&s->s_files); | 71 | INIT_LIST_HEAD(&s->s_files); |
| 72 | #endif | ||
| 58 | INIT_LIST_HEAD(&s->s_instances); | 73 | INIT_LIST_HEAD(&s->s_instances); |
| 59 | INIT_HLIST_HEAD(&s->s_anon); | 74 | INIT_HLIST_HEAD(&s->s_anon); |
| 60 | INIT_LIST_HEAD(&s->s_inodes); | 75 | INIT_LIST_HEAD(&s->s_inodes); |
| @@ -108,6 +123,9 @@ out: | |||
| 108 | */ | 123 | */ |
| 109 | static inline void destroy_super(struct super_block *s) | 124 | static inline void destroy_super(struct super_block *s) |
| 110 | { | 125 | { |
| 126 | #ifdef CONFIG_SMP | ||
| 127 | free_percpu(s->s_files); | ||
| 128 | #endif | ||
| 111 | security_sb_free(s); | 129 | security_sb_free(s); |
| 112 | kfree(s->s_subtype); | 130 | kfree(s->s_subtype); |
| 113 | kfree(s->s_options); | 131 | kfree(s->s_options); |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 5e65add0f163..76041b614758 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
| @@ -920,6 +920,9 @@ struct file { | |||
| 920 | #define f_vfsmnt f_path.mnt | 920 | #define f_vfsmnt f_path.mnt |
| 921 | const struct file_operations *f_op; | 921 | const struct file_operations *f_op; |
| 922 | spinlock_t f_lock; /* f_ep_links, f_flags, no IRQ */ | 922 | spinlock_t f_lock; /* f_ep_links, f_flags, no IRQ */ |
| 923 | #ifdef CONFIG_SMP | ||
| 924 | int f_sb_list_cpu; | ||
| 925 | #endif | ||
| 923 | atomic_long_t f_count; | 926 | atomic_long_t f_count; |
| 924 | unsigned int f_flags; | 927 | unsigned int f_flags; |
| 925 | fmode_t f_mode; | 928 | fmode_t f_mode; |
| @@ -1334,7 +1337,11 @@ struct super_block { | |||
| 1334 | 1337 | ||
| 1335 | struct list_head s_inodes; /* all inodes */ | 1338 | struct list_head s_inodes; /* all inodes */ |
| 1336 | struct hlist_head s_anon; /* anonymous dentries for (nfs) exporting */ | 1339 | struct hlist_head s_anon; /* anonymous dentries for (nfs) exporting */ |
| 1340 | #ifdef CONFIG_SMP | ||
| 1341 | struct list_head __percpu *s_files; | ||
| 1342 | #else | ||
| 1337 | struct list_head s_files; | 1343 | struct list_head s_files; |
| 1344 | #endif | ||
| 1338 | /* s_dentry_lru and s_nr_dentry_unused are protected by dcache_lock */ | 1345 | /* s_dentry_lru and s_nr_dentry_unused are protected by dcache_lock */ |
| 1339 | struct list_head s_dentry_lru; /* unused dentry lru */ | 1346 | struct list_head s_dentry_lru; /* unused dentry lru */ |
| 1340 | int s_nr_dentry_unused; /* # of dentry on lru */ | 1347 | int s_nr_dentry_unused; /* # of dentry on lru */ |
