Merge tag 'vfs-6.9.iomap' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[linux-2.6-block.git] / fs / fcntl.c
index c3e342eb74afa61ee2f427423e28f33c1105c76e..54cc85d3338ed5afb12021e02a4fe6877aa675af 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/memfd.h>
 #include <linux/compat.h>
 #include <linux/mount.h>
+#include <linux/rw_hint.h>
 
 #include <linux/poll.h>
 #include <asm/siginfo.h>
@@ -268,8 +269,15 @@ static int f_getowner_uids(struct file *filp, unsigned long arg)
 }
 #endif
 
-static bool rw_hint_valid(enum rw_hint hint)
+static bool rw_hint_valid(u64 hint)
 {
+       BUILD_BUG_ON(WRITE_LIFE_NOT_SET != RWH_WRITE_LIFE_NOT_SET);
+       BUILD_BUG_ON(WRITE_LIFE_NONE != RWH_WRITE_LIFE_NONE);
+       BUILD_BUG_ON(WRITE_LIFE_SHORT != RWH_WRITE_LIFE_SHORT);
+       BUILD_BUG_ON(WRITE_LIFE_MEDIUM != RWH_WRITE_LIFE_MEDIUM);
+       BUILD_BUG_ON(WRITE_LIFE_LONG != RWH_WRITE_LIFE_LONG);
+       BUILD_BUG_ON(WRITE_LIFE_EXTREME != RWH_WRITE_LIFE_EXTREME);
+
        switch (hint) {
        case RWH_WRITE_LIFE_NOT_SET:
        case RWH_WRITE_LIFE_NONE:
@@ -283,34 +291,40 @@ static bool rw_hint_valid(enum rw_hint hint)
        }
 }
 
-static long fcntl_rw_hint(struct file *file, unsigned int cmd,
-                         unsigned long arg)
+static long fcntl_get_rw_hint(struct file *file, unsigned int cmd,
+                             unsigned long arg)
 {
        struct inode *inode = file_inode(file);
        u64 __user *argp = (u64 __user *)arg;
-       enum rw_hint hint;
-       u64 h;
+       u64 hint = READ_ONCE(inode->i_write_hint);
 
-       switch (cmd) {
-       case F_GET_RW_HINT:
-               h = inode->i_write_hint;
-               if (copy_to_user(argp, &h, sizeof(*argp)))
-                       return -EFAULT;
-               return 0;
-       case F_SET_RW_HINT:
-               if (copy_from_user(&h, argp, sizeof(h)))
-                       return -EFAULT;
-               hint = (enum rw_hint) h;
-               if (!rw_hint_valid(hint))
-                       return -EINVAL;
+       if (copy_to_user(argp, &hint, sizeof(*argp)))
+               return -EFAULT;
+       return 0;
+}
 
-               inode_lock(inode);
-               inode->i_write_hint = hint;
-               inode_unlock(inode);
-               return 0;
-       default:
+static long fcntl_set_rw_hint(struct file *file, unsigned int cmd,
+                             unsigned long arg)
+{
+       struct inode *inode = file_inode(file);
+       u64 __user *argp = (u64 __user *)arg;
+       u64 hint;
+
+       if (copy_from_user(&hint, argp, sizeof(hint)))
+               return -EFAULT;
+       if (!rw_hint_valid(hint))
                return -EINVAL;
-       }
+
+       WRITE_ONCE(inode->i_write_hint, hint);
+
+       /*
+        * file->f_mapping->host may differ from inode. As an example,
+        * blkdev_open() modifies file->f_mapping.
+        */
+       if (file->f_mapping->host != inode)
+               WRITE_ONCE(file->f_mapping->host->i_write_hint, hint);
+
+       return 0;
 }
 
 static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
@@ -416,8 +430,10 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
                err = memfd_fcntl(filp, cmd, argi);
                break;
        case F_GET_RW_HINT:
+               err = fcntl_get_rw_hint(filp, cmd, arg);
+               break;
        case F_SET_RW_HINT:
-               err = fcntl_rw_hint(filp, cmd, arg);
+               err = fcntl_set_rw_hint(filp, cmd, arg);
                break;
        default:
                break;