diff options
| author | Nick Piggin <npiggin@kernel.dk> | 2010-08-17 14:37:35 -0400 |
|---|---|---|
| committer | Al Viro <viro@zeniv.linux.org.uk> | 2010-08-18 08:35:47 -0400 |
| commit | ee2ffa0dfdd2db19705f2ba1c6a4c0bfe8122dd8 (patch) | |
| tree | e48400d1a33f8d2e68589ccfd61637aa64462f08 | |
| parent | b04f784e5d19ed58892833dae845738972cea260 (diff) | |
fs: cleanup files_lock locking
fs: cleanup files_lock locking
Lock tty_files with a new spinlock, tty_files_lock; provide helpers to
manipulate the per-sb files list; unexport the files_lock spinlock.
Cc: linux-kernel@vger.kernel.org
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Acked-by: Andi Kleen <ak@linux.intel.com>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
| -rw-r--r-- | drivers/char/pty.c | 6 | ||||
| -rw-r--r-- | drivers/char/tty_io.c | 26 | ||||
| -rw-r--r-- | fs/file_table.c | 42 | ||||
| -rw-r--r-- | fs/open.c | 4 | ||||
| -rw-r--r-- | include/linux/fs.h | 7 | ||||
| -rw-r--r-- | include/linux/tty.h | 1 | ||||
| -rw-r--r-- | security/selinux/hooks.c | 4 |
7 files changed, 48 insertions, 42 deletions
diff --git a/drivers/char/pty.c b/drivers/char/pty.c index ad46eae1f9bb..2c64faa8efa4 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c | |||
| @@ -676,7 +676,11 @@ static int ptmx_open(struct inode *inode, struct file *filp) | |||
| 676 | 676 | ||
| 677 | set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ | 677 | set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ |
| 678 | filp->private_data = tty; | 678 | filp->private_data = tty; |
| 679 | file_move(filp, &tty->tty_files); | 679 | |
| 680 | file_sb_list_del(filp); /* __dentry_open has put it on the sb list */ | ||
| 681 | spin_lock(&tty_files_lock); | ||
| 682 | list_add(&filp->f_u.fu_list, &tty->tty_files); | ||
| 683 | spin_unlock(&tty_files_lock); | ||
| 680 | 684 | ||
| 681 | retval = devpts_pty_new(inode, tty->link); | 685 | retval = devpts_pty_new(inode, tty->link); |
| 682 | if (retval) | 686 | if (retval) |
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 0350c42375a2..cd5b829634ea 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c | |||
| @@ -136,6 +136,9 @@ LIST_HEAD(tty_drivers); /* linked list of tty drivers */ | |||
| 136 | DEFINE_MUTEX(tty_mutex); | 136 | DEFINE_MUTEX(tty_mutex); |
| 137 | EXPORT_SYMBOL(tty_mutex); | 137 | EXPORT_SYMBOL(tty_mutex); |
| 138 | 138 | ||
| 139 | /* Spinlock to protect the tty->tty_files list */ | ||
| 140 | DEFINE_SPINLOCK(tty_files_lock); | ||
| 141 | |||
| 139 | static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *); | 142 | static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *); |
| 140 | static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *); | 143 | static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *); |
| 141 | ssize_t redirected_tty_write(struct file *, const char __user *, | 144 | ssize_t redirected_tty_write(struct file *, const char __user *, |
| @@ -235,11 +238,11 @@ static int check_tty_count(struct tty_struct *tty, const char *routine) | |||
| 235 | struct list_head *p; | 238 | struct list_head *p; |
| 236 | int count = 0; | 239 | int count = 0; |
| 237 | 240 | ||
| 238 | file_list_lock(); | 241 | spin_lock(&tty_files_lock); |
| 239 | list_for_each(p, &tty->tty_files) { | 242 | list_for_each(p, &tty->tty_files) { |
| 240 | count++; | 243 | count++; |
| 241 | } | 244 | } |
| 242 | file_list_unlock(); | 245 | spin_unlock(&tty_files_lock); |
| 243 | if (tty->driver->type == TTY_DRIVER_TYPE_PTY && | 246 | if (tty->driver->type == TTY_DRIVER_TYPE_PTY && |
| 244 | tty->driver->subtype == PTY_TYPE_SLAVE && | 247 | tty->driver->subtype == PTY_TYPE_SLAVE && |
| 245 | tty->link && tty->link->count) | 248 | tty->link && tty->link->count) |
| @@ -519,7 +522,7 @@ void __tty_hangup(struct tty_struct *tty) | |||
| 519 | workqueue with the lock held */ | 522 | workqueue with the lock held */ |
| 520 | check_tty_count(tty, "tty_hangup"); | 523 | check_tty_count(tty, "tty_hangup"); |
| 521 | 524 | ||
| 522 | file_list_lock(); | 525 | spin_lock(&tty_files_lock); |
| 523 | /* This breaks for file handles being sent over AF_UNIX sockets ? */ | 526 | /* This breaks for file handles being sent over AF_UNIX sockets ? */ |
| 524 | list_for_each_entry(filp, &tty->tty_files, f_u.fu_list) { | 527 | list_for_each_entry(filp, &tty->tty_files, f_u.fu_list) { |
| 525 | if (filp->f_op->write == redirected_tty_write) | 528 | if (filp->f_op->write == redirected_tty_write) |
| @@ -530,7 +533,7 @@ void __tty_hangup(struct tty_struct *tty) | |||
| 530 | __tty_fasync(-1, filp, 0); /* can't block */ | 533 | __tty_fasync(-1, filp, 0); /* can't block */ |
| 531 | filp->f_op = &hung_up_tty_fops; | 534 | filp->f_op = &hung_up_tty_fops; |
| 532 | } | 535 | } |
| 533 | file_list_unlock(); | 536 | spin_unlock(&tty_files_lock); |
| 534 | 537 | ||
| 535 | tty_ldisc_hangup(tty); | 538 | tty_ldisc_hangup(tty); |
| 536 | 539 | ||
| @@ -1424,9 +1427,9 @@ static void release_one_tty(struct work_struct *work) | |||
| 1424 | tty_driver_kref_put(driver); | 1427 | tty_driver_kref_put(driver); |
| 1425 | module_put(driver->owner); | 1428 | module_put(driver->owner); |
| 1426 | 1429 | ||
| 1427 | file_list_lock(); | 1430 | spin_lock(&tty_files_lock); |
| 1428 | list_del_init(&tty->tty_files); | 1431 | list_del_init(&tty->tty_files); |
| 1429 | file_list_unlock(); | 1432 | spin_unlock(&tty_files_lock); |
| 1430 | 1433 | ||
| 1431 | put_pid(tty->pgrp); | 1434 | put_pid(tty->pgrp); |
| 1432 | put_pid(tty->session); | 1435 | put_pid(tty->session); |
| @@ -1671,7 +1674,10 @@ int tty_release(struct inode *inode, struct file *filp) | |||
| 1671 | * - do_tty_hangup no longer sees this file descriptor as | 1674 | * - do_tty_hangup no longer sees this file descriptor as |
| 1672 | * something that needs to be handled for hangups. | 1675 | * something that needs to be handled for hangups. |
| 1673 | */ | 1676 | */ |
| 1674 | file_kill(filp); | 1677 | spin_lock(&tty_files_lock); |
| 1678 | BUG_ON(list_empty(&filp->f_u.fu_list)); | ||
| 1679 | list_del_init(&filp->f_u.fu_list); | ||
| 1680 | spin_unlock(&tty_files_lock); | ||
| 1675 | filp->private_data = NULL; | 1681 | filp->private_data = NULL; |
| 1676 | 1682 | ||
| 1677 | /* | 1683 | /* |
| @@ -1840,7 +1846,11 @@ got_driver: | |||
| 1840 | } | 1846 | } |
| 1841 | 1847 | ||
| 1842 | filp->private_data = tty; | 1848 | filp->private_data = tty; |
| 1843 | file_move(filp, &tty->tty_files); | 1849 | BUG_ON(list_empty(&filp->f_u.fu_list)); |
| 1850 | file_sb_list_del(filp); /* __dentry_open has put it on the sb list */ | ||
| 1851 | spin_lock(&tty_files_lock); | ||
| 1852 | list_add(&filp->f_u.fu_list, &tty->tty_files); | ||
| 1853 | spin_unlock(&tty_files_lock); | ||
| 1844 | check_tty_count(tty, "tty_open"); | 1854 | check_tty_count(tty, "tty_open"); |
| 1845 | if (tty->driver->type == TTY_DRIVER_TYPE_PTY && | 1855 | if (tty->driver->type == TTY_DRIVER_TYPE_PTY && |
| 1846 | tty->driver->subtype == PTY_TYPE_MASTER) | 1856 | tty->driver->subtype == PTY_TYPE_MASTER) |
diff --git a/fs/file_table.c b/fs/file_table.c index edecd36fed9b..6f0e62ecfddd 100644 --- a/fs/file_table.c +++ b/fs/file_table.c | |||
| @@ -32,8 +32,7 @@ struct files_stat_struct files_stat = { | |||
| 32 | .max_files = NR_FILE | 32 | .max_files = NR_FILE |
| 33 | }; | 33 | }; |
| 34 | 34 | ||
| 35 | /* public. Not pretty! */ | 35 | static __cacheline_aligned_in_smp DEFINE_SPINLOCK(files_lock); |
| 36 | __cacheline_aligned_in_smp DEFINE_SPINLOCK(files_lock); | ||
| 37 | 36 | ||
| 38 | /* SLAB cache for file structures */ | 37 | /* SLAB cache for file structures */ |
| 39 | static struct kmem_cache *filp_cachep __read_mostly; | 38 | static struct kmem_cache *filp_cachep __read_mostly; |
| @@ -249,7 +248,7 @@ static void __fput(struct file *file) | |||
| 249 | cdev_put(inode->i_cdev); | 248 | cdev_put(inode->i_cdev); |
| 250 | fops_put(file->f_op); | 249 | fops_put(file->f_op); |
| 251 | put_pid(file->f_owner.pid); | 250 | put_pid(file->f_owner.pid); |
| 252 | file_kill(file); | 251 | file_sb_list_del(file); |
| 253 | if (file->f_mode & FMODE_WRITE) | 252 | if (file->f_mode & FMODE_WRITE) |
| 254 | drop_file_write_access(file); | 253 | drop_file_write_access(file); |
| 255 | file->f_path.dentry = NULL; | 254 | file->f_path.dentry = NULL; |
| @@ -328,31 +327,29 @@ struct file *fget_light(unsigned int fd, int *fput_needed) | |||
| 328 | return file; | 327 | return file; |
| 329 | } | 328 | } |
| 330 | 329 | ||
| 331 | |||
| 332 | void put_filp(struct file *file) | 330 | void put_filp(struct file *file) |
| 333 | { | 331 | { |
| 334 | if (atomic_long_dec_and_test(&file->f_count)) { | 332 | if (atomic_long_dec_and_test(&file->f_count)) { |
| 335 | security_file_free(file); | 333 | security_file_free(file); |
| 336 | file_kill(file); | 334 | file_sb_list_del(file); |
| 337 | file_free(file); | 335 | file_free(file); |
| 338 | } | 336 | } |
| 339 | } | 337 | } |
| 340 | 338 | ||
| 341 | void file_move(struct file *file, struct list_head *list) | 339 | void file_sb_list_add(struct file *file, struct super_block *sb) |
| 342 | { | 340 | { |
| 343 | if (!list) | 341 | spin_lock(&files_lock); |
| 344 | return; | 342 | BUG_ON(!list_empty(&file->f_u.fu_list)); |
| 345 | file_list_lock(); | 343 | list_add(&file->f_u.fu_list, &sb->s_files); |
| 346 | list_move(&file->f_u.fu_list, list); | 344 | spin_unlock(&files_lock); |
| 347 | file_list_unlock(); | ||
| 348 | } | 345 | } |
| 349 | 346 | ||
| 350 | void file_kill(struct file *file) | 347 | void file_sb_list_del(struct file *file) |
| 351 | { | 348 | { |
| 352 | if (!list_empty(&file->f_u.fu_list)) { | 349 | if (!list_empty(&file->f_u.fu_list)) { |
| 353 | file_list_lock(); | 350 | spin_lock(&files_lock); |
| 354 | list_del_init(&file->f_u.fu_list); | 351 | list_del_init(&file->f_u.fu_list); |
| 355 | file_list_unlock(); | 352 | spin_unlock(&files_lock); |
| 356 | } | 353 | } |
| 357 | } | 354 | } |
| 358 | 355 | ||
| @@ -361,7 +358,7 @@ int fs_may_remount_ro(struct super_block *sb) | |||
| 361 | struct file *file; | 358 | struct file *file; |
| 362 | 359 | ||
| 363 | /* Check that no files are currently opened for writing. */ | 360 | /* Check that no files are currently opened for writing. */ |
| 364 | file_list_lock(); | 361 | spin_lock(&files_lock); |
| 365 | list_for_each_entry(file, &sb->s_files, f_u.fu_list) { | 362 | list_for_each_entry(file, &sb->s_files, f_u.fu_list) { |
| 366 | struct inode *inode = file->f_path.dentry->d_inode; | 363 | struct inode *inode = file->f_path.dentry->d_inode; |
| 367 | 364 | ||
| @@ -373,10 +370,10 @@ int fs_may_remount_ro(struct super_block *sb) | |||
| 373 | if (S_ISREG(inode->i_mode) && (file->f_mode & FMODE_WRITE)) | 370 | if (S_ISREG(inode->i_mode) && (file->f_mode & FMODE_WRITE)) |
| 374 | goto too_bad; | 371 | goto too_bad; |
| 375 | } | 372 | } |
| 376 | file_list_unlock(); | 373 | spin_unlock(&files_lock); |
| 377 | return 1; /* Tis' cool bro. */ | 374 | return 1; /* Tis' cool bro. */ |
| 378 | too_bad: | 375 | too_bad: |
| 379 | file_list_unlock(); | 376 | spin_unlock(&files_lock); |
| 380 | return 0; | 377 | return 0; |
| 381 | } | 378 | } |
| 382 | 379 | ||
| @@ -392,7 +389,7 @@ void mark_files_ro(struct super_block *sb) | |||
| 392 | struct file *f; | 389 | struct file *f; |
| 393 | 390 | ||
| 394 | retry: | 391 | retry: |
| 395 | file_list_lock(); | 392 | spin_lock(&files_lock); |
| 396 | list_for_each_entry(f, &sb->s_files, f_u.fu_list) { | 393 | list_for_each_entry(f, &sb->s_files, f_u.fu_list) { |
| 397 | struct vfsmount *mnt; | 394 | struct vfsmount *mnt; |
| 398 | if (!S_ISREG(f->f_path.dentry->d_inode->i_mode)) | 395 | if (!S_ISREG(f->f_path.dentry->d_inode->i_mode)) |
| @@ -408,16 +405,13 @@ retry: | |||
| 408 | continue; | 405 | continue; |
| 409 | file_release_write(f); | 406 | file_release_write(f); |
| 410 | mnt = mntget(f->f_path.mnt); | 407 | mnt = mntget(f->f_path.mnt); |
| 411 | file_list_unlock(); | 408 | /* This can sleep, so we can't hold the spinlock. */ |
| 412 | /* | 409 | spin_unlock(&files_lock); |
| 413 | * This can sleep, so we can't hold | ||
| 414 | * the file_list_lock() spinlock. | ||
| 415 | */ | ||
| 416 | mnt_drop_write(mnt); | 410 | mnt_drop_write(mnt); |
| 417 | mntput(mnt); | 411 | mntput(mnt); |
| 418 | goto retry; | 412 | goto retry; |
| 419 | } | 413 | } |
| 420 | file_list_unlock(); | 414 | spin_unlock(&files_lock); |
| 421 | } | 415 | } |
| 422 | 416 | ||
| 423 | void __init files_init(unsigned long mempages) | 417 | void __init files_init(unsigned long mempages) |
| @@ -675,7 +675,7 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, | |||
| 675 | f->f_path.mnt = mnt; | 675 | f->f_path.mnt = mnt; |
| 676 | f->f_pos = 0; | 676 | f->f_pos = 0; |
| 677 | f->f_op = fops_get(inode->i_fop); | 677 | f->f_op = fops_get(inode->i_fop); |
| 678 | file_move(f, &inode->i_sb->s_files); | 678 | file_sb_list_add(f, inode->i_sb); |
| 679 | 679 | ||
| 680 | error = security_dentry_open(f, cred); | 680 | error = security_dentry_open(f, cred); |
| 681 | if (error) | 681 | if (error) |
| @@ -721,7 +721,7 @@ cleanup_all: | |||
| 721 | mnt_drop_write(mnt); | 721 | mnt_drop_write(mnt); |
| 722 | } | 722 | } |
| 723 | } | 723 | } |
| 724 | file_kill(f); | 724 | file_sb_list_del(f); |
| 725 | f->f_path.dentry = NULL; | 725 | f->f_path.dentry = NULL; |
| 726 | f->f_path.mnt = NULL; | 726 | f->f_path.mnt = NULL; |
| 727 | cleanup_file: | 727 | cleanup_file: |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 29f7c975304c..5a9a9e5a3705 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
| @@ -944,9 +944,6 @@ struct file { | |||
| 944 | unsigned long f_mnt_write_state; | 944 | unsigned long f_mnt_write_state; |
| 945 | #endif | 945 | #endif |
| 946 | }; | 946 | }; |
| 947 | extern spinlock_t files_lock; | ||
| 948 | #define file_list_lock() spin_lock(&files_lock); | ||
| 949 | #define file_list_unlock() spin_unlock(&files_lock); | ||
| 950 | 947 | ||
| 951 | #define get_file(x) atomic_long_inc(&(x)->f_count) | 948 | #define get_file(x) atomic_long_inc(&(x)->f_count) |
| 952 | #define fput_atomic(x) atomic_long_add_unless(&(x)->f_count, -1, 1) | 949 | #define fput_atomic(x) atomic_long_add_unless(&(x)->f_count, -1, 1) |
| @@ -2188,8 +2185,8 @@ static inline void insert_inode_hash(struct inode *inode) { | |||
| 2188 | __insert_inode_hash(inode, inode->i_ino); | 2185 | __insert_inode_hash(inode, inode->i_ino); |
| 2189 | } | 2186 | } |
| 2190 | 2187 | ||
| 2191 | extern void file_move(struct file *f, struct list_head *list); | 2188 | extern void file_sb_list_add(struct file *f, struct super_block *sb); |
| 2192 | extern void file_kill(struct file *f); | 2189 | extern void file_sb_list_del(struct file *f); |
| 2193 | #ifdef CONFIG_BLOCK | 2190 | #ifdef CONFIG_BLOCK |
| 2194 | extern void submit_bio(int, struct bio *); | 2191 | extern void submit_bio(int, struct bio *); |
| 2195 | extern int bdev_read_only(struct block_device *); | 2192 | extern int bdev_read_only(struct block_device *); |
diff --git a/include/linux/tty.h b/include/linux/tty.h index 1437da3ddc62..f6b371a2514e 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h | |||
| @@ -470,6 +470,7 @@ extern struct tty_struct *tty_pair_get_tty(struct tty_struct *tty); | |||
| 470 | extern struct tty_struct *tty_pair_get_pty(struct tty_struct *tty); | 470 | extern struct tty_struct *tty_pair_get_pty(struct tty_struct *tty); |
| 471 | 471 | ||
| 472 | extern struct mutex tty_mutex; | 472 | extern struct mutex tty_mutex; |
| 473 | extern spinlock_t tty_files_lock; | ||
| 473 | 474 | ||
| 474 | extern void tty_write_unlock(struct tty_struct *tty); | 475 | extern void tty_write_unlock(struct tty_struct *tty); |
| 475 | extern int tty_write_lock(struct tty_struct *tty, int ndelay); | 476 | extern int tty_write_lock(struct tty_struct *tty, int ndelay); |
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 42043f96e54f..bd7da0f0ccf3 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
| @@ -2170,7 +2170,7 @@ static inline void flush_unauthorized_files(const struct cred *cred, | |||
| 2170 | 2170 | ||
| 2171 | tty = get_current_tty(); | 2171 | tty = get_current_tty(); |
| 2172 | if (tty) { | 2172 | if (tty) { |
| 2173 | file_list_lock(); | 2173 | spin_lock(&tty_files_lock); |
| 2174 | if (!list_empty(&tty->tty_files)) { | 2174 | if (!list_empty(&tty->tty_files)) { |
| 2175 | struct inode *inode; | 2175 | struct inode *inode; |
| 2176 | 2176 | ||
| @@ -2186,7 +2186,7 @@ static inline void flush_unauthorized_files(const struct cred *cred, | |||
| 2186 | drop_tty = 1; | 2186 | drop_tty = 1; |
| 2187 | } | 2187 | } |
| 2188 | } | 2188 | } |
| 2189 | file_list_unlock(); | 2189 | spin_unlock(&tty_files_lock); |
| 2190 | tty_kref_put(tty); | 2190 | tty_kref_put(tty); |
| 2191 | } | 2191 | } |
| 2192 | /* Reset controlling tty. */ | 2192 | /* Reset controlling tty. */ |
