aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Hansen <haveblue@us.ibm.com>2008-02-15 17:38:01 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2008-04-19 00:29:28 -0400
commitad775f5a8faa5845377f093ca11caf577404add9 (patch)
treef124ff1038672b8d2ef004d75c844f740d8fe52b
parent2e4b7fcd926006531935a4c79a5e9349fe51125b (diff)
[PATCH] r/o bind mounts: debugging for missed calls
There have been a few oopses caused by 'struct file's with NULL f_vfsmnts. There was also a set of potentially missed mnt_want_write()s from dentry_open() calls. This patch provides a very simple debugging framework to catch these kinds of bugs. It will WARN_ON() them, but should stop us from having any oopses or mnt_writer count imbalances. I'm quite convinced that this is a good thing because it found bugs in the stuff I was working on as soon as I wrote it. [hch: made it conditional on a debug option. But it's still a little bit too ugly] [hch: merged forced remount r/o fix from Dave and akpm's fix for the fix] Signed-off-by: Dave Hansen <haveblue@us.ibm.com> Acked-by: Al Viro <viro@ZenIV.linux.org.uk> Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/file_table.c11
-rw-r--r--fs/open.c12
-rw-r--r--fs/super.c3
-rw-r--r--include/linux/fs.h49
-rw-r--r--lib/Kconfig.debug10
5 files changed, 82 insertions, 3 deletions
diff --git a/fs/file_table.c b/fs/file_table.c
index 71efc7000226..7a0a9b872251 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -42,6 +42,7 @@ static inline void file_free_rcu(struct rcu_head *head)
42static inline void file_free(struct file *f) 42static inline void file_free(struct file *f)
43{ 43{
44 percpu_counter_dec(&nr_files); 44 percpu_counter_dec(&nr_files);
45 file_check_state(f);
45 call_rcu(&f->f_u.fu_rcuhead, file_free_rcu); 46 call_rcu(&f->f_u.fu_rcuhead, file_free_rcu);
46} 47}
47 48
@@ -207,6 +208,7 @@ int init_file(struct file *file, struct vfsmount *mnt, struct dentry *dentry,
207 * that we can do debugging checks at __fput() 208 * that we can do debugging checks at __fput()
208 */ 209 */
209 if ((mode & FMODE_WRITE) && !special_file(dentry->d_inode->i_mode)) { 210 if ((mode & FMODE_WRITE) && !special_file(dentry->d_inode->i_mode)) {
211 file_take_write(file);
210 error = mnt_want_write(mnt); 212 error = mnt_want_write(mnt);
211 WARN_ON(error); 213 WARN_ON(error);
212 } 214 }
@@ -237,8 +239,13 @@ void drop_file_write_access(struct file *file)
237 struct inode *inode = dentry->d_inode; 239 struct inode *inode = dentry->d_inode;
238 240
239 put_write_access(inode); 241 put_write_access(inode);
240 if (!special_file(inode->i_mode)) 242
241 mnt_drop_write(mnt); 243 if (special_file(inode->i_mode))
244 return;
245 if (file_check_writeable(file) != 0)
246 return;
247 mnt_drop_write(mnt);
248 file_release_write(file);
242} 249}
243EXPORT_SYMBOL_GPL(drop_file_write_access); 250EXPORT_SYMBOL_GPL(drop_file_write_access);
244 251
diff --git a/fs/open.c b/fs/open.c
index e58382d57e72..b70e7666bb2c 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -806,6 +806,8 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
806 error = __get_file_write_access(inode, mnt); 806 error = __get_file_write_access(inode, mnt);
807 if (error) 807 if (error)
808 goto cleanup_file; 808 goto cleanup_file;
809 if (!special_file(inode->i_mode))
810 file_take_write(f);
809 } 811 }
810 812
811 f->f_mapping = inode->i_mapping; 813 f->f_mapping = inode->i_mapping;
@@ -847,8 +849,16 @@ cleanup_all:
847 fops_put(f->f_op); 849 fops_put(f->f_op);
848 if (f->f_mode & FMODE_WRITE) { 850 if (f->f_mode & FMODE_WRITE) {
849 put_write_access(inode); 851 put_write_access(inode);
850 if (!special_file(inode->i_mode)) 852 if (!special_file(inode->i_mode)) {
853 /*
854 * We don't consider this a real
855 * mnt_want/drop_write() pair
856 * because it all happenend right
857 * here, so just reset the state.
858 */
859 file_reset_write(f);
851 mnt_drop_write(mnt); 860 mnt_drop_write(mnt);
861 }
852 } 862 }
853 file_kill(f); 863 file_kill(f);
854 f->f_path.dentry = NULL; 864 f->f_path.dentry = NULL;
diff --git a/fs/super.c b/fs/super.c
index 01d5c40e9119..1f8f05ede437 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -579,6 +579,9 @@ retry:
579 if (!(f->f_mode & FMODE_WRITE)) 579 if (!(f->f_mode & FMODE_WRITE))
580 continue; 580 continue;
581 f->f_mode &= ~FMODE_WRITE; 581 f->f_mode &= ~FMODE_WRITE;
582 if (file_check_writeable(f) != 0)
583 continue;
584 file_release_write(f);
582 mnt = mntget(f->f_path.mnt); 585 mnt = mntget(f->f_path.mnt);
583 file_list_unlock(); 586 file_list_unlock();
584 /* 587 /*
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 013b9c2b88e6..d1eeea669d2c 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -776,6 +776,9 @@ static inline int ra_has_index(struct file_ra_state *ra, pgoff_t index)
776 index < ra->start + ra->size); 776 index < ra->start + ra->size);
777} 777}
778 778
779#define FILE_MNT_WRITE_TAKEN 1
780#define FILE_MNT_WRITE_RELEASED 2
781
779struct file { 782struct file {
780 /* 783 /*
781 * fu_list becomes invalid after file_free is called and queued via 784 * fu_list becomes invalid after file_free is called and queued via
@@ -810,6 +813,9 @@ struct file {
810 spinlock_t f_ep_lock; 813 spinlock_t f_ep_lock;
811#endif /* #ifdef CONFIG_EPOLL */ 814#endif /* #ifdef CONFIG_EPOLL */
812 struct address_space *f_mapping; 815 struct address_space *f_mapping;
816#ifdef CONFIG_DEBUG_WRITECOUNT
817 unsigned long f_mnt_write_state;
818#endif
813}; 819};
814extern spinlock_t files_lock; 820extern spinlock_t files_lock;
815#define file_list_lock() spin_lock(&files_lock); 821#define file_list_lock() spin_lock(&files_lock);
@@ -818,6 +824,49 @@ extern spinlock_t files_lock;
818#define get_file(x) atomic_inc(&(x)->f_count) 824#define get_file(x) atomic_inc(&(x)->f_count)
819#define file_count(x) atomic_read(&(x)->f_count) 825#define file_count(x) atomic_read(&(x)->f_count)
820 826
827#ifdef CONFIG_DEBUG_WRITECOUNT
828static inline void file_take_write(struct file *f)
829{
830 WARN_ON(f->f_mnt_write_state != 0);
831 f->f_mnt_write_state = FILE_MNT_WRITE_TAKEN;
832}
833static inline void file_release_write(struct file *f)
834{
835 f->f_mnt_write_state |= FILE_MNT_WRITE_RELEASED;
836}
837static inline void file_reset_write(struct file *f)
838{
839 f->f_mnt_write_state = 0;
840}
841static inline void file_check_state(struct file *f)
842{
843 /*
844 * At this point, either both or neither of these bits
845 * should be set.
846 */
847 WARN_ON(f->f_mnt_write_state == FILE_MNT_WRITE_TAKEN);
848 WARN_ON(f->f_mnt_write_state == FILE_MNT_WRITE_RELEASED);
849}
850static inline int file_check_writeable(struct file *f)
851{
852 if (f->f_mnt_write_state == FILE_MNT_WRITE_TAKEN)
853 return 0;
854 printk(KERN_WARNING "writeable file with no "
855 "mnt_want_write()\n");
856 WARN_ON(1);
857 return -EINVAL;
858}
859#else /* !CONFIG_DEBUG_WRITECOUNT */
860static inline void file_take_write(struct file *filp) {}
861static inline void file_release_write(struct file *filp) {}
862static inline void file_reset_write(struct file *filp) {}
863static inline void file_check_state(struct file *filp) {}
864static inline int file_check_writeable(struct file *filp)
865{
866 return 0;
867}
868#endif /* CONFIG_DEBUG_WRITECOUNT */
869
821#define MAX_NON_LFS ((1UL<<31) - 1) 870#define MAX_NON_LFS ((1UL<<31) - 1)
822 871
823/* Page cache limit. The filesystems should put that into their s_maxbytes 872/* Page cache limit. The filesystems should put that into their s_maxbytes
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 95de3102bc87..623ef24c2381 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -427,6 +427,16 @@ config DEBUG_VM
427 427
428 If unsure, say N. 428 If unsure, say N.
429 429
430config DEBUG_WRITECOUNT
431 bool "Debug filesystem writers count"
432 depends on DEBUG_KERNEL
433 help
434 Enable this to catch wrong use of the writers count in struct
435 vfsmount. This will increase the size of each file struct by
436 32 bits.
437
438 If unsure, say N.
439
430config DEBUG_LIST 440config DEBUG_LIST
431 bool "Debug linked list manipulation" 441 bool "Debug linked list manipulation"
432 depends on DEBUG_KERNEL 442 depends on DEBUG_KERNEL