diff options
Diffstat (limited to 'fs/file_table.c')
-rw-r--r-- | fs/file_table.c | 171 |
1 files changed, 119 insertions, 52 deletions
diff --git a/fs/file_table.c b/fs/file_table.c index 32d12b78bac8..a7c231786a9a 100644 --- a/fs/file_table.c +++ b/fs/file_table.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/sysctl.h> | 22 | #include <linux/sysctl.h> |
23 | #include <linux/percpu_counter.h> | 23 | #include <linux/percpu_counter.h> |
24 | #include <linux/ima.h> | 24 | #include <linux/ima.h> |
25 | #include <linux/percpu.h> | ||
25 | 26 | ||
26 | #include <asm/atomic.h> | 27 | #include <asm/atomic.h> |
27 | 28 | ||
@@ -32,8 +33,7 @@ struct files_stat_struct files_stat = { | |||
32 | .max_files = NR_FILE | 33 | .max_files = NR_FILE |
33 | }; | 34 | }; |
34 | 35 | ||
35 | /* public. Not pretty! */ | 36 | static DEFINE_PER_CPU(spinlock_t, files_cpulock); |
36 | __cacheline_aligned_in_smp DEFINE_SPINLOCK(files_lock); | ||
37 | 37 | ||
38 | /* SLAB cache for file structures */ | 38 | /* SLAB cache for file structures */ |
39 | static struct kmem_cache *filp_cachep __read_mostly; | 39 | static struct kmem_cache *filp_cachep __read_mostly; |
@@ -258,7 +258,7 @@ void __fput(struct file *file) | |||
258 | cdev_put(inode->i_cdev); | 258 | cdev_put(inode->i_cdev); |
259 | fops_put(file->f_op); | 259 | fops_put(file->f_op); |
260 | put_pid(file->f_owner.pid); | 260 | put_pid(file->f_owner.pid); |
261 | file_kill(file); | 261 | file_sb_list_del(file); |
262 | if (file->f_mode & FMODE_WRITE) | 262 | if (file->f_mode & FMODE_WRITE) |
263 | drop_file_write_access(file); | 263 | drop_file_write_access(file); |
264 | file->f_path.dentry = NULL; | 264 | file->f_path.dentry = NULL; |
@@ -320,55 +320,112 @@ struct file *fget_light(unsigned int fd, int *fput_needed) | |||
320 | return file; | 320 | return file; |
321 | } | 321 | } |
322 | 322 | ||
323 | |||
324 | void put_filp(struct file *file) | 323 | void put_filp(struct file *file) |
325 | { | 324 | { |
326 | if (atomic_long_dec_and_test(&file->f_count)) { | 325 | if (atomic_long_dec_and_test(&file->f_count)) { |
327 | security_file_free(file); | 326 | security_file_free(file); |
328 | file_kill(file); | 327 | file_sb_list_del(file); |
329 | file_free(file); | 328 | file_free(file); |
330 | } | 329 | } |
331 | } | 330 | } |
332 | 331 | ||
333 | void file_move(struct file *file, struct list_head *list) | 332 | void file_sb_list_add(struct file *file, struct super_block *sb) |
334 | { | 333 | { |
335 | if (!list) | 334 | spinlock_t *lock; |
336 | return; | 335 | struct list_head *list; |
337 | file_list_lock(); | 336 | #ifdef CONFIG_SMP |
338 | list_move(&file->f_u.fu_list, list); | 337 | int cpu; |
339 | file_list_unlock(); | 338 | #endif |
339 | |||
340 | lock = &get_cpu_var(files_cpulock); | ||
341 | #ifdef CONFIG_SMP | ||
342 | cpu = smp_processor_id(); | ||
343 | list = per_cpu_ptr(sb->s_files, cpu); | ||
344 | file->f_sb_list_cpu = cpu; | ||
345 | #else | ||
346 | list = &sb->s_files; | ||
347 | #endif | ||
348 | put_cpu_var(files_cpulock); | ||
349 | spin_lock(lock); | ||
350 | BUG_ON(!list_empty(&file->f_u.fu_list)); | ||
351 | list_add(&file->f_u.fu_list, list); | ||
352 | spin_unlock(lock); | ||
340 | } | 353 | } |
341 | 354 | ||
342 | void file_kill(struct file *file) | 355 | void file_sb_list_del(struct file *file) |
343 | { | 356 | { |
344 | if (!list_empty(&file->f_u.fu_list)) { | 357 | if (!list_empty(&file->f_u.fu_list)) { |
345 | file_list_lock(); | 358 | spinlock_t *lock; |
359 | |||
360 | #ifdef CONFIG_SMP | ||
361 | lock = &per_cpu(files_cpulock, file->f_sb_list_cpu); | ||
362 | #else | ||
363 | lock = &__get_cpu_var(files_cpulock); | ||
364 | #endif | ||
365 | spin_lock(lock); | ||
346 | list_del_init(&file->f_u.fu_list); | 366 | list_del_init(&file->f_u.fu_list); |
347 | file_list_unlock(); | 367 | spin_unlock(lock); |
368 | } | ||
369 | } | ||
370 | |||
371 | static void file_list_lock_all(void) | ||
372 | { | ||
373 | int i; | ||
374 | int nr = 0; | ||
375 | |||
376 | for_each_possible_cpu(i) { | ||
377 | spinlock_t *lock; | ||
378 | |||
379 | lock = &per_cpu(files_cpulock, i); | ||
380 | spin_lock_nested(lock, nr); | ||
381 | nr++; | ||
382 | } | ||
383 | } | ||
384 | |||
385 | static void file_list_unlock_all(void) | ||
386 | { | ||
387 | int i; | ||
388 | |||
389 | for_each_possible_cpu(i) { | ||
390 | spinlock_t *lock; | ||
391 | |||
392 | lock = &per_cpu(files_cpulock, i); | ||
393 | spin_unlock(lock); | ||
348 | } | 394 | } |
349 | } | 395 | } |
350 | 396 | ||
351 | int fs_may_remount_ro(struct super_block *sb) | 397 | int fs_may_remount_ro(struct super_block *sb) |
352 | { | 398 | { |
353 | struct file *file; | 399 | int i; |
354 | 400 | ||
355 | /* Check that no files are currently opened for writing. */ | 401 | /* Check that no files are currently opened for writing. */ |
356 | file_list_lock(); | 402 | file_list_lock_all(); |
357 | list_for_each_entry(file, &sb->s_files, f_u.fu_list) { | 403 | for_each_possible_cpu(i) { |
358 | struct inode *inode = file->f_path.dentry->d_inode; | 404 | struct file *file; |
405 | struct list_head *list; | ||
406 | |||
407 | #ifdef CONFIG_SMP | ||
408 | list = per_cpu_ptr(sb->s_files, i); | ||
409 | #else | ||
410 | list = &sb->s_files; | ||
411 | #endif | ||
412 | list_for_each_entry(file, list, f_u.fu_list) { | ||
413 | struct inode *inode = file->f_path.dentry->d_inode; | ||
359 | 414 | ||
360 | /* File with pending delete? */ | 415 | /* File with pending delete? */ |
361 | if (inode->i_nlink == 0) | 416 | if (inode->i_nlink == 0) |
362 | goto too_bad; | 417 | goto too_bad; |
363 | 418 | ||
364 | /* Writeable file? */ | 419 | /* Writeable file? */ |
365 | if (S_ISREG(inode->i_mode) && (file->f_mode & FMODE_WRITE)) | 420 | if (S_ISREG(inode->i_mode) && |
366 | goto too_bad; | 421 | (file->f_mode & FMODE_WRITE)) |
422 | goto too_bad; | ||
423 | } | ||
367 | } | 424 | } |
368 | file_list_unlock(); | 425 | file_list_unlock_all(); |
369 | return 1; /* Tis' cool bro. */ | 426 | return 1; /* Tis' cool bro. */ |
370 | too_bad: | 427 | too_bad: |
371 | file_list_unlock(); | 428 | file_list_unlock_all(); |
372 | return 0; | 429 | return 0; |
373 | } | 430 | } |
374 | 431 | ||
@@ -381,40 +438,48 @@ too_bad: | |||
381 | */ | 438 | */ |
382 | void mark_files_ro(struct super_block *sb) | 439 | void mark_files_ro(struct super_block *sb) |
383 | { | 440 | { |
384 | struct file *f; | 441 | int i; |
385 | 442 | ||
386 | retry: | 443 | retry: |
387 | file_list_lock(); | 444 | file_list_lock_all(); |
388 | list_for_each_entry(f, &sb->s_files, f_u.fu_list) { | 445 | for_each_possible_cpu(i) { |
389 | struct vfsmount *mnt; | 446 | struct file *f; |
390 | if (!S_ISREG(f->f_path.dentry->d_inode->i_mode)) | 447 | struct list_head *list; |
391 | continue; | 448 | |
392 | if (!file_count(f)) | 449 | #ifdef CONFIG_SMP |
393 | continue; | 450 | list = per_cpu_ptr(sb->s_files, i); |
394 | if (!(f->f_mode & FMODE_WRITE)) | 451 | #else |
395 | continue; | 452 | list = &sb->s_files; |
396 | spin_lock(&f->f_lock); | 453 | #endif |
397 | f->f_mode &= ~FMODE_WRITE; | 454 | list_for_each_entry(f, list, f_u.fu_list) { |
398 | spin_unlock(&f->f_lock); | 455 | struct vfsmount *mnt; |
399 | if (file_check_writeable(f) != 0) | 456 | if (!S_ISREG(f->f_path.dentry->d_inode->i_mode)) |
400 | continue; | 457 | continue; |
401 | file_release_write(f); | 458 | if (!file_count(f)) |
402 | mnt = mntget(f->f_path.mnt); | 459 | continue; |
403 | file_list_unlock(); | 460 | if (!(f->f_mode & FMODE_WRITE)) |
404 | /* | 461 | continue; |
405 | * This can sleep, so we can't hold | 462 | spin_lock(&f->f_lock); |
406 | * the file_list_lock() spinlock. | 463 | f->f_mode &= ~FMODE_WRITE; |
407 | */ | 464 | spin_unlock(&f->f_lock); |
408 | mnt_drop_write(mnt); | 465 | if (file_check_writeable(f) != 0) |
409 | mntput(mnt); | 466 | continue; |
410 | goto retry; | 467 | file_release_write(f); |
468 | mnt = mntget(f->f_path.mnt); | ||
469 | /* This can sleep, so we can't hold the spinlock. */ | ||
470 | file_list_unlock_all(); | ||
471 | mnt_drop_write(mnt); | ||
472 | mntput(mnt); | ||
473 | goto retry; | ||
474 | } | ||
411 | } | 475 | } |
412 | file_list_unlock(); | 476 | file_list_unlock_all(); |
413 | } | 477 | } |
414 | 478 | ||
415 | void __init files_init(unsigned long mempages) | 479 | void __init files_init(unsigned long mempages) |
416 | { | 480 | { |
417 | int n; | 481 | int n; |
482 | int i; | ||
418 | 483 | ||
419 | filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0, | 484 | filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0, |
420 | SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); | 485 | SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); |
@@ -429,5 +494,7 @@ void __init files_init(unsigned long mempages) | |||
429 | if (files_stat.max_files < NR_FILE) | 494 | if (files_stat.max_files < NR_FILE) |
430 | files_stat.max_files = NR_FILE; | 495 | files_stat.max_files = NR_FILE; |
431 | files_defer_init(); | 496 | files_defer_init(); |
497 | for_each_possible_cpu(i) | ||
498 | spin_lock_init(&per_cpu(files_cpulock, i)); | ||
432 | percpu_counter_init(&nr_files, 0); | 499 | percpu_counter_init(&nr_files, 0); |
433 | } | 500 | } |