From: Dave Hansen <dave@linux.vnet.ibm.com>
To: linux-kernel@vger.kernel.org
Cc: linux-fsdevel@vger.kernel.org, viro@zeniv.linux.org.uk,
hch@infradead.org, trond.myklebust@fys.uio.no,
Dave Hansen <dave@linux.vnet.ibm.com>
Subject: [RFC][PATCH 5/5] check mount writers at superblock remount
Date: Tue, 29 Apr 2008 11:59:50 -0700 [thread overview]
Message-ID: <20080429185950.3A37E91B@kernel> (raw)
In-Reply-To: <20080429185943.3BA6A050@kernel>
We're moving a big chunk of the burden of keeping people from
writing to r/o filesystems from the superblock into the
vfsmount. This requires that we keep track somehow of the
mounts that might allow writes to a superblock.
You could track this directly by keeping a count of such
mounts in the superblock, but I think this is much simpler
in practice.
On remount rw->ro operations:
1. Use lock_mnt_writers() to exclude any new mnt_writers
for any mount on the entire system, which also clears
each cpu_writer->mnt.
2. Lock superblock to stop anyone from setting any new
cpu_writer->mnt if the mount is of this sb.
3. unlock_mnt_writers() to let other fs's on the system
to go about their business.
4. Consult each mount of the sb to examine its
mnt->__mnt_writers. Remember, this is stable because
(1) cleared out all the per-cpu writers, and in (2)
the sb lock is held, keeping out any new writers.
5. release sb lock at end of remount operation (after
->remount_fs())
I'm not completely happy with the sb_locked thing in
do_remount_sb(). Expanding the use of lock_super() over
a larger area may make the code look simpler, but will
not be as obviously correct.
Signed-off-by: Dave Hansen <dave@linux.vnet.ibm.com>
---
linux-2.6.git-dave/fs/file_table.c | 24 -----------
linux-2.6.git-dave/fs/namespace.c | 4 -
linux-2.6.git-dave/fs/super.c | 65 ++++++++++++++++++++++++++-----
linux-2.6.git-dave/include/linux/fs.h | 2
linux-2.6.git-dave/include/linux/mount.h | 10 +++-
5 files changed, 65 insertions(+), 40 deletions(-)
diff -puN fs/file_table.c~check_mount_writers_at_superblock_remount fs/file_table.c
--- linux-2.6.git/fs/file_table.c~check_mount_writers_at_superblock_remount 2008-04-29 11:56:42.000000000 -0700
+++ linux-2.6.git-dave/fs/file_table.c 2008-04-29 11:56:42.000000000 -0700
@@ -365,30 +365,6 @@ void file_kill(struct file *file)
}
}
-int fs_may_remount_ro(struct super_block *sb)
-{
- struct file *file;
-
- /* Check that no files are currently opened for writing. */
- file_list_lock();
- list_for_each_entry(file, &sb->s_files, f_u.fu_list) {
- struct inode *inode = file->f_path.dentry->d_inode;
-
- /* File with pending delete? */
- if (inode->i_nlink == 0)
- goto too_bad;
-
- /* Writeable file? */
- if (S_ISREG(inode->i_mode) && (file->f_mode & FMODE_WRITE))
- goto too_bad;
- }
- file_list_unlock();
- return 1; /* Tis' cool bro. */
-too_bad:
- file_list_unlock();
- return 0;
-}
-
void __init files_init(unsigned long mempages)
{
int n;
diff -puN fs/namespace.c~check_mount_writers_at_superblock_remount fs/namespace.c
--- linux-2.6.git/fs/namespace.c~check_mount_writers_at_superblock_remount 2008-04-29 11:56:42.000000000 -0700
+++ linux-2.6.git-dave/fs/namespace.c 2008-04-29 11:56:42.000000000 -0700
@@ -193,7 +193,7 @@ static int __init init_mnt_writers(void)
}
fs_initcall(init_mnt_writers);
-static void unlock_mnt_writers(void)
+void unlock_mnt_writers(void)
{
int cpu;
struct mnt_writer *cpu_writer;
@@ -278,7 +278,7 @@ out:
}
EXPORT_SYMBOL_GPL(mnt_want_write);
-static void lock_mnt_writers(void)
+void lock_mnt_writers(void)
{
int cpu;
struct mnt_writer *cpu_writer;
diff -puN fs/super.c~check_mount_writers_at_superblock_remount fs/super.c
--- linux-2.6.git/fs/super.c~check_mount_writers_at_superblock_remount 2008-04-29 11:56:42.000000000 -0700
+++ linux-2.6.git-dave/fs/super.c 2008-04-29 11:56:42.000000000 -0700
@@ -36,6 +36,7 @@
#include <linux/writeback.h> /* for the emergency remount stuff */
#include <linux/idr.h>
#include <linux/kobject.h>
+#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/file.h>
#include <asm/uaccess.h>
@@ -598,6 +599,32 @@ retry:
}
/**
+ * __sb_can_remount_ro - check a superblock for active writers
+ * @sb: superblock in question
+ *
+ * Must ensure that no new writers to the superblock can come
+ * in (must hold lock_mnt_writers()) and that the s_vfsmounts
+ * list can not change (must acquire lock_super()).
+ *
+ * Returns 0 on success.
+ */
+static int __sb_can_remount_ro(struct super_block *sb)
+{
+ struct list_head *entry;
+ int ret = 0;
+
+ list_for_each(entry, &sb->s_vfsmounts) {
+ struct vfsmount *mnt;
+ mnt = list_entry(entry, struct vfsmount, mnt_sb_list);
+ if (!atomic_read(&mnt->__mnt_writers))
+ continue;
+ ret = -EBUSY;
+ break;
+ }
+ return ret;
+}
+
+/**
* do_remount_sb - asks filesystem to change mount options.
* @sb: superblock in question
* @flags: numeric part of options
@@ -608,8 +635,9 @@ retry:
*/
int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
{
- int retval;
+ int retval = 0;
int remount_rw;
+ int sb_locked = 0;
#ifdef CONFIG_BLOCK
if (!(flags & MS_RDONLY) && bdev_read_only(sb->s_bdev))
@@ -623,27 +651,44 @@ int do_remount_sb(struct super_block *sb
/* If we are remounting RDONLY and current sb is read/write,
make sure there are no rw files opened */
if ((flags & MS_RDONLY) && !(sb->s_flags & MS_RDONLY)) {
- if (force)
+ if (force) {
mark_files_ro(sb);
- else if (!fs_may_remount_ro(sb))
- return -EBUSY;
+ } else {
+ lock_mnt_writers();
+ /*
+ * this locks out any new writers
+ * on any of this sb's mounts.
+ */
+ lock_super(sb);
+ sb_locked = 1;
+ retval = __sb_can_remount_ro(sb);
+ unlock_mnt_writers();
+ if (retval)
+ goto out;
+ }
retval = DQUOT_OFF(sb, 1);
- if (retval < 0 && retval != -ENOSYS)
- return -EBUSY;
+ if (retval < 0 && retval != -ENOSYS) {
+ retval = -EBUSY;
+ goto out;
+ }
}
remount_rw = !(flags & MS_RDONLY) && (sb->s_flags & MS_RDONLY);
if (sb->s_op->remount_fs) {
- lock_super(sb);
+ if (!sb_locked)
+ lock_super(sb);
+ sb_locked = 1;
retval = sb->s_op->remount_fs(sb, &flags, data);
- unlock_super(sb);
if (retval)
- return retval;
+ goto out;
}
sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) | (flags & MS_RMT_MASK);
if (remount_rw)
DQUOT_ON_REMOUNT(sb);
- return 0;
+out:
+ if (sb_locked)
+ unlock_super(sb);
+ return retval;
}
static void do_emergency_remount(unsigned long foo)
diff -puN include/linux/fs.h~check_mount_writers_at_superblock_remount include/linux/fs.h
--- linux-2.6.git/include/linux/fs.h~check_mount_writers_at_superblock_remount 2008-04-29 11:56:42.000000000 -0700
+++ linux-2.6.git-dave/include/linux/fs.h 2008-04-29 11:56:42.000000000 -0700
@@ -1699,8 +1699,6 @@ extern const struct file_operations read
extern const struct file_operations write_fifo_fops;
extern const struct file_operations rdwr_fifo_fops;
-extern int fs_may_remount_ro(struct super_block *);
-
#ifdef CONFIG_BLOCK
/*
* return READ, READA, or WRITE
diff -puN include/linux/mount.h~check_mount_writers_at_superblock_remount include/linux/mount.h
--- linux-2.6.git/include/linux/mount.h~check_mount_writers_at_superblock_remount 2008-04-29 11:56:42.000000000 -0700
+++ linux-2.6.git-dave/include/linux/mount.h 2008-04-29 11:56:42.000000000 -0700
@@ -69,8 +69,12 @@ struct vfsmount {
int mnt_pinned;
int mnt_ghosts;
/*
- * This value is not stable unless all of the mnt_writers[] spinlocks
- * are held, and all mnt_writer[]s on this mount have 0 as their ->count
+ * This value is stable for all mounts when all of the
+ * mnt_writers[] spinlocks are held.
+ *
+ * It is stable for all mounts of an sb when a lock_super()
+ * is performed under a lock_mnt_writers() as long as the
+ * sb stays locked. See do_remount_sb(). - Dave Hansen
*/
atomic_t __mnt_writers;
};
@@ -84,6 +88,8 @@ static inline struct vfsmount *mntget(st
extern int mnt_want_write(struct vfsmount *mnt);
extern void mnt_drop_write(struct vfsmount *mnt);
+extern void lock_mnt_writers(void);
+extern void unlock_mnt_writers(void);
extern void mntput_no_expire(struct vfsmount *mnt);
extern void mnt_pin(struct vfsmount *mnt);
extern void mnt_unpin(struct vfsmount *mnt);
_
prev parent reply other threads:[~2008-04-29 18:59 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-04-29 18:59 [RFC][PATCH 0/5] do remounts without consulting sb->s_files list Dave Hansen
2008-04-29 18:59 ` [RFC][PATCH 1/5] r/o bind mounts: change spinlock to mutex Dave Hansen
2008-04-29 18:59 ` [RFC][PATCH 2/5] introduce simple_set_mnt_no_get() helper for NFS Dave Hansen
2008-04-29 21:34 ` Trond Myklebust
2008-04-30 1:46 ` Dave Hansen
2008-05-01 16:58 ` Matthew Wilcox
2008-04-29 18:59 ` [RFC][PATCH 3/5] reintroduce list of vfsmounts over superblock Dave Hansen
2008-04-29 18:59 ` [RFC][PATCH 4/5] must hold lock_super() to set initial mount writer Dave Hansen
2008-04-30 9:25 ` Miklos Szeredi
2008-05-01 16:26 ` Dave Hansen
2008-04-29 18:59 ` Dave Hansen [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20080429185950.3A37E91B@kernel \
--to=dave@linux.vnet.ibm.com \
--cc=hch@infradead.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=trond.myklebust@fys.uio.no \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).