Since there are applications (e.g. Ceph) that use F_[GS]ET_FILE_RW_HINT, restore support for these fcntls. This patch restores functionality that was removed by commit 7b12e49669c9 ("fs: remove fs.f_write_hint"). Cc: Jeff Layton <jlayton@xxxxxxxxxx> Cc: Chuck Lever <chuck.lever@xxxxxxxxxx> Cc: Jens Axboe <axboe@xxxxxxxxx> Cc: Christoph Hellwig <hch@xxxxxx> Cc: Dave Chinner <dchinner@xxxxxxxxxx> Cc: Chaitanya Kulkarni <kch@xxxxxxxxxx> Signed-off-by: Bart Van Assche <bvanassche@xxxxxxx> --- fs/fcntl.c | 37 +++++++++++++++++++++++++++++++++++++ fs/open.c | 1 + include/linux/fs.h | 9 +++++++++ 3 files changed, 47 insertions(+) diff --git a/fs/fcntl.c b/fs/fcntl.c index fc73c5fae43c..8018c4da478c 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -322,6 +322,37 @@ static long fcntl_set_rw_hint(struct file *file, unsigned int cmd, return 0; } +static long fcntl_get_file_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 = inode->i_write_hint; + + hint = file_write_hint(file); + if (copy_to_user(argp, &hint, sizeof(*argp))) + return -EFAULT; + return 0; +} + +static long fcntl_set_file_rw_hint(struct file *file, unsigned int cmd, + unsigned long arg) +{ + 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; + + spin_lock(&file->f_lock); + file->f_write_hint = hint; + spin_unlock(&file->f_lock); + + return 0; +} + static long do_fcntl(int fd, unsigned int cmd, unsigned long arg, struct file *filp) { @@ -430,6 +461,12 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg, case F_SET_RW_HINT: err = fcntl_set_rw_hint(filp, cmd, arg); break; + case F_GET_FILE_RW_HINT: + err = fcntl_get_file_rw_hint(filp, cmd, arg); + break; + case F_SET_FILE_RW_HINT: + err = fcntl_set_file_rw_hint(filp, cmd, arg); + break; default: break; } diff --git a/fs/open.c b/fs/open.c index 02dc608d40d8..4c5c29541ac5 100644 --- a/fs/open.c +++ b/fs/open.c @@ -961,6 +961,7 @@ static int do_dentry_open(struct file *f, if (f->f_mapping->a_ops && f->f_mapping->a_ops->direct_IO) f->f_mode |= FMODE_CAN_ODIRECT; + f->f_write_hint = WRITE_LIFE_NOT_SET; f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); f->f_iocb_flags = iocb_flags(f); diff --git a/include/linux/fs.h b/include/linux/fs.h index a08014b68d6e..a6e0c4b5a72b 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -989,6 +989,7 @@ struct file { * Must not be taken from IRQ context. */ spinlock_t f_lock; + enum rw_hint f_write_hint; fmode_t f_mode; atomic_long_t f_count; struct mutex f_pos_lock; @@ -2162,6 +2163,14 @@ static inline bool HAS_UNMAPPED_ID(struct mnt_idmap *idmap, !vfsgid_valid(i_gid_into_vfsgid(idmap, inode)); } +static inline enum rw_hint file_write_hint(struct file *file) +{ + if (file->f_write_hint != WRITE_LIFE_NOT_SET) + return file->f_write_hint; + + return file_inode(file)->i_write_hint; +} + static inline void init_sync_kiocb(struct kiocb *kiocb, struct file *filp) { *kiocb = (struct kiocb) {