diff options
Diffstat (limited to 'fs/super.c')
-rw-r--r-- | fs/super.c | 25 |
1 files changed, 23 insertions, 2 deletions
diff --git a/fs/super.c b/fs/super.c index 09008dbd264e..4798350b2bc9 100644 --- a/fs/super.c +++ b/fs/super.c | |||
@@ -37,7 +37,9 @@ | |||
37 | #include <linux/idr.h> | 37 | #include <linux/idr.h> |
38 | #include <linux/kobject.h> | 38 | #include <linux/kobject.h> |
39 | #include <linux/mutex.h> | 39 | #include <linux/mutex.h> |
40 | #include <linux/file.h> | ||
40 | #include <asm/uaccess.h> | 41 | #include <asm/uaccess.h> |
42 | #include "internal.h" | ||
41 | 43 | ||
42 | 44 | ||
43 | LIST_HEAD(super_blocks); | 45 | LIST_HEAD(super_blocks); |
@@ -567,10 +569,29 @@ static void mark_files_ro(struct super_block *sb) | |||
567 | { | 569 | { |
568 | struct file *f; | 570 | struct file *f; |
569 | 571 | ||
572 | retry: | ||
570 | file_list_lock(); | 573 | file_list_lock(); |
571 | list_for_each_entry(f, &sb->s_files, f_u.fu_list) { | 574 | list_for_each_entry(f, &sb->s_files, f_u.fu_list) { |
572 | if (S_ISREG(f->f_path.dentry->d_inode->i_mode) && file_count(f)) | 575 | struct vfsmount *mnt; |
573 | f->f_mode &= ~FMODE_WRITE; | 576 | if (!S_ISREG(f->f_path.dentry->d_inode->i_mode)) |
577 | continue; | ||
578 | if (!file_count(f)) | ||
579 | continue; | ||
580 | if (!(f->f_mode & FMODE_WRITE)) | ||
581 | continue; | ||
582 | f->f_mode &= ~FMODE_WRITE; | ||
583 | if (file_check_writeable(f) != 0) | ||
584 | continue; | ||
585 | file_release_write(f); | ||
586 | mnt = mntget(f->f_path.mnt); | ||
587 | file_list_unlock(); | ||
588 | /* | ||
589 | * This can sleep, so we can't hold | ||
590 | * the file_list_lock() spinlock. | ||
591 | */ | ||
592 | mnt_drop_write(mnt); | ||
593 | mntput(mnt); | ||
594 | goto retry; | ||
574 | } | 595 | } |
575 | file_list_unlock(); | 596 | file_list_unlock(); |
576 | } | 597 | } |