From 85d7d618c17a09cfd824c1ad4483c19e6f9637ff Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 23 Jun 2012 22:41:54 +0400 Subject: mark_files_ro(): don't bother with mntget/mntput mnt_drop_write_file() is safe under any lock Signed-off-by: Al Viro --- fs/file_table.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'fs/file_table.c') diff --git a/fs/file_table.c b/fs/file_table.c index a305d9e2d1b..9ace2781931 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -483,10 +483,8 @@ void mark_files_ro(struct super_block *sb) { struct file *f; -retry: lg_global_lock(&files_lglock); do_file_list_for_each_entry(sb, f) { - struct vfsmount *mnt; if (!S_ISREG(f->f_path.dentry->d_inode->i_mode)) continue; if (!file_count(f)) @@ -499,12 +497,7 @@ retry: if (file_check_writeable(f) != 0) continue; file_release_write(f); - mnt = mntget(f->f_path.mnt); - /* This can sleep, so we can't hold the spinlock. */ - lg_global_unlock(&files_lglock); - mnt_drop_write(mnt); - mntput(mnt); - goto retry; + mnt_drop_write_file(f); } while_file_list_for_each_entry; lg_global_unlock(&files_lglock); } -- cgit v1.2.3 From 4a9d4b024a3102fc083c925c242d98ac27b1c5f6 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 24 Jun 2012 09:56:45 +0400 Subject: switch fput to task_work_add ... and schedule_work() for interrupt/kernel_thread callers (and yes, now it *is* OK to call from interrupt). We are guaranteed that __fput() will be done before we return to userland (or exit). Note that for fput() from a kernel thread we get an async behaviour; it's almost always OK, but sometimes you might need to have __fput() completed before you do anything else. There are two mechanisms for that - a general barrier (flush_delayed_fput()) and explicit __fput_sync(). Both should be used with care (as was the case for fput() from kernel threads all along). See comments in fs/file_table.c for details. Signed-off-by: Al Viro --- fs/file_table.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 2 deletions(-) (limited to 'fs/file_table.c') diff --git a/fs/file_table.c b/fs/file_table.c index 9ace2781931..b3fc4d67a26 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include #include @@ -251,7 +253,6 @@ static void __fput(struct file *file) } fops_put(file->f_op); put_pid(file->f_owner.pid); - file_sb_list_del(file); if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) i_readcount_dec(inode); if (file->f_mode & FMODE_WRITE) @@ -263,10 +264,77 @@ static void __fput(struct file *file) mntput(mnt); } +static DEFINE_SPINLOCK(delayed_fput_lock); +static LIST_HEAD(delayed_fput_list); +static void delayed_fput(struct work_struct *unused) +{ + LIST_HEAD(head); + spin_lock_irq(&delayed_fput_lock); + list_splice_init(&delayed_fput_list, &head); + spin_unlock_irq(&delayed_fput_lock); + while (!list_empty(&head)) { + struct file *f = list_first_entry(&head, struct file, f_u.fu_list); + list_del_init(&f->f_u.fu_list); + __fput(f); + } +} + +static void ____fput(struct callback_head *work) +{ + __fput(container_of(work, struct file, f_u.fu_rcuhead)); +} + +/* + * If kernel thread really needs to have the final fput() it has done + * to complete, call this. The only user right now is the boot - we + * *do* need to make sure our writes to binaries on initramfs has + * not left us with opened struct file waiting for __fput() - execve() + * won't work without that. Please, don't add more callers without + * very good reasons; in particular, never call that with locks + * held and never call that from a thread that might need to do + * some work on any kind of umount. + */ +void flush_delayed_fput(void) +{ + delayed_fput(NULL); +} + +static DECLARE_WORK(delayed_fput_work, delayed_fput); + void fput(struct file *file) { - if (atomic_long_dec_and_test(&file->f_count)) + if (atomic_long_dec_and_test(&file->f_count)) { + struct task_struct *task = current; + file_sb_list_del(file); + if (unlikely(in_interrupt() || task->flags & PF_KTHREAD)) { + unsigned long flags; + spin_lock_irqsave(&delayed_fput_lock, flags); + list_add(&file->f_u.fu_list, &delayed_fput_list); + schedule_work(&delayed_fput_work); + spin_unlock_irqrestore(&delayed_fput_lock, flags); + return; + } + init_task_work(&file->f_u.fu_rcuhead, ____fput); + task_work_add(task, &file->f_u.fu_rcuhead, true); + } +} + +/* + * synchronous analog of fput(); for kernel threads that might be needed + * in some umount() (and thus can't use flush_delayed_fput() without + * risking deadlocks), need to wait for completion of __fput() and know + * for this specific struct file it won't involve anything that would + * need them. Use only if you really need it - at the very least, + * don't blindly convert fput() by kernel thread to that. + */ +void __fput_sync(struct file *file) +{ + if (atomic_long_dec_and_test(&file->f_count)) { + struct task_struct *task = current; + file_sb_list_del(file); + BUG_ON(!(task->flags & PF_KTHREAD)); __fput(file); + } } EXPORT_SYMBOL(fput); -- cgit v1.2.3 From 5c33b183a36500a5b0a3c53c11c431f0fec6efc8 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 20 Jul 2012 23:05:59 +0400 Subject: uninline file_free_rcu() What inline? Its only use is passing its address to call_rcu(), for fuck sake! Signed-off-by: Al Viro --- fs/file_table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/file_table.c') diff --git a/fs/file_table.c b/fs/file_table.c index b3fc4d67a26..b54bf7fd0b1 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -43,7 +43,7 @@ static struct kmem_cache *filp_cachep __read_mostly; static struct percpu_counter nr_files __cacheline_aligned_in_smp; -static inline void file_free_rcu(struct rcu_head *head) +static void file_free_rcu(struct rcu_head *head) { struct file *f = container_of(head, struct file, f_u.fu_rcuhead); -- cgit v1.2.3 From eb04c28288bb0098d0e75d81ba2a575239de71d8 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 12 Jun 2012 16:20:35 +0200 Subject: fs: Add freezing handling to mnt_want_write() / mnt_drop_write() Most of places where we want freeze protection coincides with the places where we also have remount-ro protection. So make mnt_want_write() and mnt_drop_write() (and their _file alternative) prevent freezing as well. For the few cases that are really interested only in remount-ro protection provide new function variants. BugLink: https://bugs.launchpad.net/bugs/897421 Tested-by: Kamal Mostafa Tested-by: Peter M. Petrakis Tested-by: Dann Frazier Tested-by: Massimo Morana Signed-off-by: Jan Kara Signed-off-by: Al Viro --- fs/file_table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/file_table.c') diff --git a/fs/file_table.c b/fs/file_table.c index b54bf7fd0b1..701985e4ccd 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -217,7 +217,7 @@ static void drop_file_write_access(struct file *file) return; if (file_check_writeable(file) != 0) return; - mnt_drop_write(mnt); + __mnt_drop_write(mnt); file_release_write(file); } -- cgit v1.2.3 From 4199d35cbc90c15db447d115bd96ffa5f1d60d3a Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Wed, 16 Mar 2011 22:48:43 -0400 Subject: vfs: move ima_file_free before releasing the file ima_file_free(), called on __fput(), currently flags files that have changed, so that the file is re-measured. For appraising a files's integrity, the file's hash must be re-calculated and stored in the 'security.ima' xattr to reflect any changes. This patch moves the ima_file_free() call to before releasing the file in preparation of ima-appraisal measuring the file and updating the 'security.ima' xattr. Signed-off-by: Mimi Zohar Acked-by: Serge Hallyn Acked-by: Dmitry Kasatkin --- fs/file_table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/file_table.c') diff --git a/fs/file_table.c b/fs/file_table.c index 701985e4ccd..a41f23f90b1 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -243,10 +243,10 @@ static void __fput(struct file *file) if (file->f_op && file->f_op->fasync) file->f_op->fasync(-1, file, 0); } + ima_file_free(file); if (file->f_op && file->f_op->release) file->f_op->release(inode, file); security_file_free(file); - ima_file_free(file); if (unlikely(S_ISCHR(inode->i_mode) && inode->i_cdev != NULL && !(file->f_mode & FMODE_PATH))) { cdev_put(inode->i_cdev); -- cgit v1.2.3 From 0ee8cdfe6af052deb56dccd54838a1eb32fb4ca2 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 15 Aug 2012 21:12:10 -0400 Subject: take fget() and friends to fs/file.c Signed-off-by: Al Viro --- fs/file_table.c | 106 -------------------------------------------------------- 1 file changed, 106 deletions(-) (limited to 'fs/file_table.c') diff --git a/fs/file_table.c b/fs/file_table.c index 701985e4ccd..c6780163bf3 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -339,112 +339,6 @@ void __fput_sync(struct file *file) EXPORT_SYMBOL(fput); -struct file *fget(unsigned int fd) -{ - struct file *file; - struct files_struct *files = current->files; - - rcu_read_lock(); - file = fcheck_files(files, fd); - if (file) { - /* File object ref couldn't be taken */ - if (file->f_mode & FMODE_PATH || - !atomic_long_inc_not_zero(&file->f_count)) - file = NULL; - } - rcu_read_unlock(); - - return file; -} - -EXPORT_SYMBOL(fget); - -struct file *fget_raw(unsigned int fd) -{ - struct file *file; - struct files_struct *files = current->files; - - rcu_read_lock(); - file = fcheck_files(files, fd); - if (file) { - /* File object ref couldn't be taken */ - if (!atomic_long_inc_not_zero(&file->f_count)) - file = NULL; - } - rcu_read_unlock(); - - return file; -} - -EXPORT_SYMBOL(fget_raw); - -/* - * Lightweight file lookup - no refcnt increment if fd table isn't shared. - * - * You can use this instead of fget if you satisfy all of the following - * conditions: - * 1) You must call fput_light before exiting the syscall and returning control - * to userspace (i.e. you cannot remember the returned struct file * after - * returning to userspace). - * 2) You must not call filp_close on the returned struct file * in between - * calls to fget_light and fput_light. - * 3) You must not clone the current task in between the calls to fget_light - * and fput_light. - * - * The fput_needed flag returned by fget_light should be passed to the - * corresponding fput_light. - */ -struct file *fget_light(unsigned int fd, int *fput_needed) -{ - struct file *file; - struct files_struct *files = current->files; - - *fput_needed = 0; - if (atomic_read(&files->count) == 1) { - file = fcheck_files(files, fd); - if (file && (file->f_mode & FMODE_PATH)) - file = NULL; - } else { - rcu_read_lock(); - file = fcheck_files(files, fd); - if (file) { - if (!(file->f_mode & FMODE_PATH) && - atomic_long_inc_not_zero(&file->f_count)) - *fput_needed = 1; - else - /* Didn't get the reference, someone's freed */ - file = NULL; - } - rcu_read_unlock(); - } - - return file; -} - -struct file *fget_raw_light(unsigned int fd, int *fput_needed) -{ - struct file *file; - struct files_struct *files = current->files; - - *fput_needed = 0; - if (atomic_read(&files->count) == 1) { - file = fcheck_files(files, fd); - } else { - rcu_read_lock(); - file = fcheck_files(files, fd); - if (file) { - if (atomic_long_inc_not_zero(&file->f_count)) - *fput_needed = 1; - else - /* Didn't get the reference, someone's freed */ - file = NULL; - } - rcu_read_unlock(); - } - - return file; -} - void put_filp(struct file *file) { if (atomic_long_dec_and_test(&file->f_count)) { -- cgit v1.2.3 From 4b2c551f77f5a0c496e2125b1d883f4b26aabf2c Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Tue, 9 Oct 2012 14:49:54 -0700 Subject: lglock: add DEFINE_STATIC_LGLOCK() When the lglock doesn't need to be exported we can use DEFINE_STATIC_LGLOCK(). Signed-off-by: Lai Jiangshan Cc: Alexander Viro Cc: Rusty Russell Cc: Andi Kleen Signed-off-by: Andrew Morton Signed-off-by: Al Viro --- fs/file_table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/file_table.c') diff --git a/fs/file_table.c b/fs/file_table.c index dac67923330..a72bf9ddd0d 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -36,7 +36,7 @@ struct files_stat_struct files_stat = { .max_files = NR_FILE }; -DEFINE_LGLOCK(files_lglock); +DEFINE_STATIC_LGLOCK(files_lglock); /* SLAB cache for file structures */ static struct kmem_cache *filp_cachep __read_mostly; -- cgit v1.2.3 From 72651cac884b1e285fa8e8314b10e9f1b8458802 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 5 Dec 2012 14:40:14 +0100 Subject: fs: Fix imbalance in freeze protection in mark_files_ro() File descriptors (even those for writing) do not hold freeze protection. Thus mark_files_ro() must call __mnt_drop_write() to only drop protection against remount read-only. Calling mnt_drop_write_file() as we do now results in: [ BUG: bad unlock balance detected! ] 3.7.0-rc6-00028-g88e75b6 #101 Not tainted ------------------------------------- kworker/1:2/79 is trying to release lock (sb_writers) at: [] mnt_drop_write+0x24/0x30 but there are no more locks to release! Reported-by: Zdenek Kabelac CC: stable@vger.kernel.org Signed-off-by: Jan Kara Signed-off-by: Al Viro --- fs/file_table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/file_table.c') diff --git a/fs/file_table.c b/fs/file_table.c index a72bf9ddd0d..de9e9653d61 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -458,8 +458,8 @@ void mark_files_ro(struct super_block *sb) spin_unlock(&f->f_lock); if (file_check_writeable(f) != 0) continue; + __mnt_drop_write(f->f_path.mnt); file_release_write(f); - mnt_drop_write_file(f); } while_file_list_for_each_entry; lg_global_unlock(&files_lglock); } -- cgit v1.2.3