From e7e2f2f02536af50fb7406ff4ebf3a5522473f04 Mon Sep 17 00:00:00 2001 From: Dave Hansen Date: Fri, 15 Feb 2008 14:38:01 -0800 Subject: [PATCH] --- yaml --- r: 90965 b: refs/heads/master c: ad775f5a8faa5845377f093ca11caf577404add9 h: refs/heads/master i: 90963: c2f30c0d96a1f9282ec68a76f3e9cff417a1d68f v: v3 --- [refs] | 2 +- trunk/fs/file_table.c | 11 +++++++-- trunk/fs/open.c | 12 +++++++++- trunk/fs/super.c | 3 +++ trunk/include/linux/fs.h | 49 ++++++++++++++++++++++++++++++++++++++++ trunk/lib/Kconfig.debug | 10 ++++++++ 6 files changed, 83 insertions(+), 4 deletions(-) diff --git a/[refs] b/[refs] index fbbbe1e745a3..4bc1911bfb96 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 2e4b7fcd926006531935a4c79a5e9349fe51125b +refs/heads/master: ad775f5a8faa5845377f093ca11caf577404add9 diff --git a/trunk/fs/file_table.c b/trunk/fs/file_table.c index 71efc7000226..7a0a9b872251 100644 --- a/trunk/fs/file_table.c +++ b/trunk/fs/file_table.c @@ -42,6 +42,7 @@ static inline void file_free_rcu(struct rcu_head *head) static inline void file_free(struct file *f) { percpu_counter_dec(&nr_files); + file_check_state(f); call_rcu(&f->f_u.fu_rcuhead, file_free_rcu); } @@ -207,6 +208,7 @@ int init_file(struct file *file, struct vfsmount *mnt, struct dentry *dentry, * that we can do debugging checks at __fput() */ if ((mode & FMODE_WRITE) && !special_file(dentry->d_inode->i_mode)) { + file_take_write(file); error = mnt_want_write(mnt); WARN_ON(error); } @@ -237,8 +239,13 @@ void drop_file_write_access(struct file *file) struct inode *inode = dentry->d_inode; put_write_access(inode); - if (!special_file(inode->i_mode)) - mnt_drop_write(mnt); + + if (special_file(inode->i_mode)) + return; + if (file_check_writeable(file) != 0) + return; + mnt_drop_write(mnt); + file_release_write(file); } EXPORT_SYMBOL_GPL(drop_file_write_access); diff --git a/trunk/fs/open.c b/trunk/fs/open.c index e58382d57e72..b70e7666bb2c 100644 --- a/trunk/fs/open.c +++ b/trunk/fs/open.c @@ -806,6 +806,8 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, error = __get_file_write_access(inode, mnt); if (error) goto cleanup_file; + if (!special_file(inode->i_mode)) + file_take_write(f); } f->f_mapping = inode->i_mapping; @@ -847,8 +849,16 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, fops_put(f->f_op); if (f->f_mode & FMODE_WRITE) { put_write_access(inode); - if (!special_file(inode->i_mode)) + if (!special_file(inode->i_mode)) { + /* + * We don't consider this a real + * mnt_want/drop_write() pair + * because it all happenend right + * here, so just reset the state. + */ + file_reset_write(f); mnt_drop_write(mnt); + } } file_kill(f); f->f_path.dentry = NULL; diff --git a/trunk/fs/super.c b/trunk/fs/super.c index 01d5c40e9119..1f8f05ede437 100644 --- a/trunk/fs/super.c +++ b/trunk/fs/super.c @@ -579,6 +579,9 @@ static void mark_files_ro(struct super_block *sb) if (!(f->f_mode & FMODE_WRITE)) continue; f->f_mode &= ~FMODE_WRITE; + if (file_check_writeable(f) != 0) + continue; + file_release_write(f); mnt = mntget(f->f_path.mnt); file_list_unlock(); /* diff --git a/trunk/include/linux/fs.h b/trunk/include/linux/fs.h index 013b9c2b88e6..d1eeea669d2c 100644 --- a/trunk/include/linux/fs.h +++ b/trunk/include/linux/fs.h @@ -776,6 +776,9 @@ static inline int ra_has_index(struct file_ra_state *ra, pgoff_t index) index < ra->start + ra->size); } +#define FILE_MNT_WRITE_TAKEN 1 +#define FILE_MNT_WRITE_RELEASED 2 + struct file { /* * fu_list becomes invalid after file_free is called and queued via @@ -810,6 +813,9 @@ struct file { spinlock_t f_ep_lock; #endif /* #ifdef CONFIG_EPOLL */ struct address_space *f_mapping; +#ifdef CONFIG_DEBUG_WRITECOUNT + unsigned long f_mnt_write_state; +#endif }; extern spinlock_t files_lock; #define file_list_lock() spin_lock(&files_lock); @@ -818,6 +824,49 @@ extern spinlock_t files_lock; #define get_file(x) atomic_inc(&(x)->f_count) #define file_count(x) atomic_read(&(x)->f_count) +#ifdef CONFIG_DEBUG_WRITECOUNT +static inline void file_take_write(struct file *f) +{ + WARN_ON(f->f_mnt_write_state != 0); + f->f_mnt_write_state = FILE_MNT_WRITE_TAKEN; +} +static inline void file_release_write(struct file *f) +{ + f->f_mnt_write_state |= FILE_MNT_WRITE_RELEASED; +} +static inline void file_reset_write(struct file *f) +{ + f->f_mnt_write_state = 0; +} +static inline void file_check_state(struct file *f) +{ + /* + * At this point, either both or neither of these bits + * should be set. + */ + WARN_ON(f->f_mnt_write_state == FILE_MNT_WRITE_TAKEN); + WARN_ON(f->f_mnt_write_state == FILE_MNT_WRITE_RELEASED); +} +static inline int file_check_writeable(struct file *f) +{ + if (f->f_mnt_write_state == FILE_MNT_WRITE_TAKEN) + return 0; + printk(KERN_WARNING "writeable file with no " + "mnt_want_write()\n"); + WARN_ON(1); + return -EINVAL; +} +#else /* !CONFIG_DEBUG_WRITECOUNT */ +static inline void file_take_write(struct file *filp) {} +static inline void file_release_write(struct file *filp) {} +static inline void file_reset_write(struct file *filp) {} +static inline void file_check_state(struct file *filp) {} +static inline int file_check_writeable(struct file *filp) +{ + return 0; +} +#endif /* CONFIG_DEBUG_WRITECOUNT */ + #define MAX_NON_LFS ((1UL<<31) - 1) /* Page cache limit. The filesystems should put that into their s_maxbytes diff --git a/trunk/lib/Kconfig.debug b/trunk/lib/Kconfig.debug index 95de3102bc87..623ef24c2381 100644 --- a/trunk/lib/Kconfig.debug +++ b/trunk/lib/Kconfig.debug @@ -427,6 +427,16 @@ config DEBUG_VM If unsure, say N. +config DEBUG_WRITECOUNT + bool "Debug filesystem writers count" + depends on DEBUG_KERNEL + help + Enable this to catch wrong use of the writers count in struct + vfsmount. This will increase the size of each file struct by + 32 bits. + + If unsure, say N. + config DEBUG_LIST bool "Debug linked list manipulation" depends on DEBUG_KERNEL