ovl: use ovl_inode_lock in ovl_llseek()
authorAmir Goldstein <amir73il@gmail.com>
Sat, 21 Dec 2019 09:42:29 +0000 (11:42 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Wed, 22 Jan 2020 19:11:41 +0000 (20:11 +0100)
In ovl_llseek() we use the overlay inode rwsem to protect against
concurrent modifications to real file f_pos, because we copy the overlay
file f_pos to/from the real file f_pos.

This caused a lockdep warning of locking order violation when the
ovl_llseek() operation was called on a lower nested overlay layer while the
upper layer fs sb_writers is held (with patch improving copy-up efficiency
for big sparse file).

Use the internal ovl_inode_lock() instead of the overlay inode rwsem in
those cases. It is meant to be used for protecting against concurrent
changes to overlay inode internal state changes.

The locking order rules are documented to explain this case.

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/overlayfs/file.c
fs/overlayfs/inode.c

index e235a635d9ec7eb90c83ce3eab4e4eeb7631a961..859efeaaefab0a0dd50492f3047d47cd23624b63 100644 (file)
@@ -171,7 +171,7 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
         * limitations that are more strict than ->s_maxbytes for specific
         * files, so we use the real file to perform seeks.
         */
-       inode_lock(inode);
+       ovl_inode_lock(inode);
        real.file->f_pos = file->f_pos;
 
        old_cred = ovl_override_creds(inode->i_sb);
@@ -179,7 +179,7 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
        revert_creds(old_cred);
 
        file->f_pos = real.file->f_pos;
-       inode_unlock(inode);
+       ovl_inode_unlock(inode);
 
        fdput(real);
 
index bfebef7638ca486540a299bd89f0c0c1d7931360..13981b0a8235692aa4fc0a6943615c4e20346b0f 100644 (file)
@@ -527,6 +527,27 @@ static const struct address_space_operations ovl_aops = {
  * [...] &ovl_i_mutex_dir_key[depth]   (stack_depth=2)
  * [...] &ovl_i_mutex_dir_key[depth]#2 (stack_depth=1)
  * [...] &type->i_mutex_dir_key        (stack_depth=0)
+ *
+ * Locking order w.r.t ovl_want_write() is important for nested overlayfs.
+ *
+ * This chain is valid:
+ * - inode->i_rwsem                    (inode_lock[2])
+ * - upper_mnt->mnt_sb->s_writers      (ovl_want_write[0])
+ * - OVL_I(inode)->lock                        (ovl_inode_lock[2])
+ * - OVL_I(lowerinode)->lock           (ovl_inode_lock[1])
+ *
+ * And this chain is valid:
+ * - inode->i_rwsem                    (inode_lock[2])
+ * - OVL_I(inode)->lock                        (ovl_inode_lock[2])
+ * - lowerinode->i_rwsem               (inode_lock[1])
+ * - OVL_I(lowerinode)->lock           (ovl_inode_lock[1])
+ *
+ * But lowerinode->i_rwsem SHOULD NOT be acquired while ovl_want_write() is
+ * held, because it is in reverse order of the non-nested case using the same
+ * upper fs:
+ * - inode->i_rwsem                    (inode_lock[1])
+ * - upper_mnt->mnt_sb->s_writers      (ovl_want_write[0])
+ * - OVL_I(inode)->lock                        (ovl_inode_lock[1])
  */
 #define OVL_MAX_NESTING FILESYSTEM_MAX_STACK_DEPTH