fs: Add freezing handling to mnt_want_write() / mnt_drop_write()
authorJan Kara <jack@suse.cz>
Tue, 12 Jun 2012 14:20:35 +0000 (16:20 +0200)
committerAl Viro <viro@zeniv.linux.org.uk>
Tue, 31 Jul 2012 05:40:38 +0000 (09:40 +0400)
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 <kamal@canonical.com>
Tested-by: Peter M. Petrakis <peter.petrakis@canonical.com>
Tested-by: Dann Frazier <dann.frazier@canonical.com>
Tested-by: Massimo Morana <massimo.morana@canonical.com>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/file_table.c
fs/inode.c
fs/internal.h
fs/namespace.c
fs/open.c

index b54bf7fd0b158bed8e32806da07a4cf0a491f69f..701985e4ccda4fc5afc05977f511bd6ca11ba3af 100644 (file)
@@ -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);
 }
 
index 775cbabd4fa5f3fe3840277a4f0aac4456f02396..006c85ca06ebe05ad8eb7438040bddd3af64968d 100644 (file)
@@ -1660,11 +1660,11 @@ int file_update_time(struct file *file)
                return 0;
 
        /* Finally allowed to write? Takes lock. */
-       if (mnt_want_write_file(file))
+       if (__mnt_want_write_file(file))
                return 0;
 
        ret = update_time(inode, &now, sync_it);
-       mnt_drop_write_file(file);
+       __mnt_drop_write_file(file);
 
        return ret;
 }
index a6fd56c68b1164928d380fdc787a988107c15a55..371bcc4b1697df808e506a6f88735209dec17dd8 100644 (file)
@@ -61,6 +61,10 @@ extern void __init mnt_init(void);
 
 extern struct lglock vfsmount_lock;
 
+extern int __mnt_want_write(struct vfsmount *);
+extern int __mnt_want_write_file(struct file *);
+extern void __mnt_drop_write(struct vfsmount *);
+extern void __mnt_drop_write_file(struct file *);
 
 /*
  * fs_struct.c
index c53d3381b0d0043d91e2f7c47e4cafbdaaaf13d6..4d31f73e2561d4d0e19d85becc0223dda27b4710 100644 (file)
@@ -283,24 +283,22 @@ static int mnt_is_readonly(struct vfsmount *mnt)
 }
 
 /*
- * Most r/o checks on a fs are for operations that take
- * discrete amounts of time, like a write() or unlink().
- * We must keep track of when those operations start
- * (for permission checks) and when they end, so that
- * we can determine when writes are able to occur to
- * a filesystem.
+ * Most r/o & frozen checks on a fs are for operations that take discrete
+ * amounts of time, like a write() or unlink().  We must keep track of when
+ * those operations start (for permission checks) and when they end, so that we
+ * can determine when writes are able to occur to a filesystem.
  */
 /**
- * mnt_want_write - get write access to a mount
+ * __mnt_want_write - get write access to a mount without freeze protection
  * @m: the mount on which to take a write
  *
- * This tells the low-level filesystem that a write is
- * about to be performed to it, and makes sure that
- * writes are allowed before returning success.  When
- * the write operation is finished, mnt_drop_write()
- * must be called.  This is effectively a refcount.
+ * This tells the low-level filesystem that a write is about to be performed to
+ * it, and makes sure that writes are allowed (mnt it read-write) before
+ * returning success. This operation does not protect against filesystem being
+ * frozen. When the write operation is finished, __mnt_drop_write() must be
+ * called. This is effectively a refcount.
  */
-int mnt_want_write(struct vfsmount *m)
+int __mnt_want_write(struct vfsmount *m)
 {
        struct mount *mnt = real_mount(m);
        int ret = 0;
@@ -326,6 +324,27 @@ int mnt_want_write(struct vfsmount *m)
                ret = -EROFS;
        }
        preempt_enable();
+
+       return ret;
+}
+
+/**
+ * mnt_want_write - get write access to a mount
+ * @m: the mount on which to take a write
+ *
+ * This tells the low-level filesystem that a write is about to be performed to
+ * it, and makes sure that writes are allowed (mount is read-write, filesystem
+ * is not frozen) before returning success.  When the write operation is
+ * finished, mnt_drop_write() must be called.  This is effectively a refcount.
+ */
+int mnt_want_write(struct vfsmount *m)
+{
+       int ret;
+
+       sb_start_write(m->mnt_sb);
+       ret = __mnt_want_write(m);
+       if (ret)
+               sb_end_write(m->mnt_sb);
        return ret;
 }
 EXPORT_SYMBOL_GPL(mnt_want_write);
@@ -355,38 +374,76 @@ int mnt_clone_write(struct vfsmount *mnt)
 EXPORT_SYMBOL_GPL(mnt_clone_write);
 
 /**
- * mnt_want_write_file - get write access to a file's mount
+ * __mnt_want_write_file - get write access to a file's mount
  * @file: the file who's mount on which to take a write
  *
- * This is like mnt_want_write, but it takes a file and can
+ * This is like __mnt_want_write, but it takes a file and can
  * do some optimisations if the file is open for write already
  */
-int mnt_want_write_file(struct file *file)
+int __mnt_want_write_file(struct file *file)
 {
        struct inode *inode = file->f_dentry->d_inode;
+
        if (!(file->f_mode & FMODE_WRITE) || special_file(inode->i_mode))
-               return mnt_want_write(file->f_path.mnt);
+               return __mnt_want_write(file->f_path.mnt);
        else
                return mnt_clone_write(file->f_path.mnt);
 }
+
+/**
+ * mnt_want_write_file - get write access to a file's mount
+ * @file: the file who's mount on which to take a write
+ *
+ * This is like mnt_want_write, but it takes a file and can
+ * do some optimisations if the file is open for write already
+ */
+int mnt_want_write_file(struct file *file)
+{
+       int ret;
+
+       sb_start_write(file->f_path.mnt->mnt_sb);
+       ret = __mnt_want_write_file(file);
+       if (ret)
+               sb_end_write(file->f_path.mnt->mnt_sb);
+       return ret;
+}
 EXPORT_SYMBOL_GPL(mnt_want_write_file);
 
 /**
- * mnt_drop_write - give up write access to a mount
+ * __mnt_drop_write - give up write access to a mount
  * @mnt: the mount on which to give up write access
  *
  * Tells the low-level filesystem that we are done
  * performing writes to it.  Must be matched with
- * mnt_want_write() call above.
+ * __mnt_want_write() call above.
  */
-void mnt_drop_write(struct vfsmount *mnt)
+void __mnt_drop_write(struct vfsmount *mnt)
 {
        preempt_disable();
        mnt_dec_writers(real_mount(mnt));
        preempt_enable();
 }
+
+/**
+ * mnt_drop_write - give up write access to a mount
+ * @mnt: the mount on which to give up write access
+ *
+ * Tells the low-level filesystem that we are done performing writes to it and
+ * also allows filesystem to be frozen again.  Must be matched with
+ * mnt_want_write() call above.
+ */
+void mnt_drop_write(struct vfsmount *mnt)
+{
+       __mnt_drop_write(mnt);
+       sb_end_write(mnt->mnt_sb);
+}
 EXPORT_SYMBOL_GPL(mnt_drop_write);
 
+void __mnt_drop_write_file(struct file *file)
+{
+       __mnt_drop_write(file->f_path.mnt);
+}
+
 void mnt_drop_write_file(struct file *file)
 {
        mnt_drop_write(file->f_path.mnt);
index 8d2c8970029c0bc345b58702318e6555102e2a10..9ddc1856550379e223dcc962ec43ac432807a0d7 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -620,7 +620,7 @@ static inline int __get_file_write_access(struct inode *inode,
                /*
                 * Balanced in __fput()
                 */
-               error = mnt_want_write(mnt);
+               error = __mnt_want_write(mnt);
                if (error)
                        put_write_access(inode);
        }