fs: add fget_many() and fput_many()
authorJens Axboe <axboe@kernel.dk>
Wed, 21 Nov 2018 17:32:39 +0000 (10:32 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 8 Dec 2021 07:46:55 +0000 (08:46 +0100)
commit 091141a42e15fe47ada737f3996b317072afcefb upstream.

Some uses cases repeatedly get and put references to the same file, but
the only exposed interface is doing these one at the time. As each of
these entail an atomic inc or dec on a shared structure, that cost can
add up.

Add fget_many(), which works just like fget(), except it takes an
argument for how many references to get on the file. Ditto fput_many(),
which can drop an arbitrary number of references to a file.

Reviewed-by: Hannes Reinecke <hare@suse.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/file.c
fs/file_table.c
include/linux/file.h
include/linux/fs.h

index 97c6f0df39daeb7f3a3059ec113913a9324dada6..c0e7bc2c6ca0d1791ef4b67c99c96e4cb0062e5f 100644 (file)
--- a/fs/file.c
+++ b/fs/file.c
@@ -679,7 +679,7 @@ void do_close_on_exec(struct files_struct *files)
        spin_unlock(&files->file_lock);
 }
 
-static struct file *__fget(unsigned int fd, fmode_t mask)
+static struct file *__fget(unsigned int fd, fmode_t mask, unsigned int refs)
 {
        struct files_struct *files = current->files;
        struct file *file;
@@ -694,7 +694,7 @@ static struct file *__fget(unsigned int fd, fmode_t mask)
                 */
                if (file->f_mode & mask)
                        file = NULL;
-               else if (!get_file_rcu(file))
+               else if (!get_file_rcu_many(file, refs))
                        goto loop;
        }
        rcu_read_unlock();
@@ -702,15 +702,20 @@ static struct file *__fget(unsigned int fd, fmode_t mask)
        return file;
 }
 
+struct file *fget_many(unsigned int fd, unsigned int refs)
+{
+       return __fget(fd, FMODE_PATH, refs);
+}
+
 struct file *fget(unsigned int fd)
 {
-       return __fget(fd, FMODE_PATH);
+       return __fget(fd, FMODE_PATH, 1);
 }
 EXPORT_SYMBOL(fget);
 
 struct file *fget_raw(unsigned int fd)
 {
-       return __fget(fd, 0);
+       return __fget(fd, 0, 1);
 }
 EXPORT_SYMBOL(fget_raw);
 
@@ -741,7 +746,7 @@ static unsigned long __fget_light(unsigned int fd, fmode_t mask)
                        return 0;
                return (unsigned long)file;
        } else {
-               file = __fget(fd, mask);
+               file = __fget(fd, mask, 1);
                if (!file)
                        return 0;
                return FDPUT_FPUT | (unsigned long)file;
index 61517f57f8ef744e9915c33659cfba96c0a1f767..4ceb4982ffea1374a6f1adc473f940b54ad7b1c8 100644 (file)
@@ -261,9 +261,9 @@ void flush_delayed_fput(void)
 
 static DECLARE_DELAYED_WORK(delayed_fput_work, delayed_fput);
 
-void fput(struct file *file)
+void fput_many(struct file *file, unsigned int refs)
 {
-       if (atomic_long_dec_and_test(&file->f_count)) {
+       if (atomic_long_sub_and_test(refs, &file->f_count)) {
                struct task_struct *task = current;
 
                if (likely(!in_interrupt() && !(task->flags & PF_KTHREAD))) {
@@ -282,6 +282,11 @@ void fput(struct file *file)
        }
 }
 
+void fput(struct file *file)
+{
+       fput_many(file, 1);
+}
+
 /*
  * synchronous analog of fput(); for kernel threads that might be needed
  * in some umount() (and thus can't use flush_delayed_fput() without
index 279720db984af394c90737ed31b8ec09ef73c2ca..d26462b127a3bc62eeeaa0ed5b20ca2609e60d55 100644 (file)
@@ -13,6 +13,7 @@
 struct file;
 
 extern void fput(struct file *);
+extern void fput_many(struct file *, unsigned int);
 
 struct file_operations;
 struct vfsmount;
@@ -41,6 +42,7 @@ static inline void fdput(struct fd fd)
 }
 
 extern struct file *fget(unsigned int fd);
+extern struct file *fget_many(unsigned int fd, unsigned int refs);
 extern struct file *fget_raw(unsigned int fd);
 extern unsigned long __fdget(unsigned int fd);
 extern unsigned long __fdget_raw(unsigned int fd);
index 309c151decd8c52a77363c9dab2378b5e010e1fd..1084f1335c9f339b913ea695948574908c01c764 100644 (file)
@@ -908,7 +908,9 @@ static inline struct file *get_file(struct file *f)
        atomic_long_inc(&f->f_count);
        return f;
 }
-#define get_file_rcu(x) atomic_long_inc_not_zero(&(x)->f_count)
+#define get_file_rcu_many(x, cnt)      \
+       atomic_long_add_unless(&(x)->f_count, (cnt), 0)
+#define get_file_rcu(x) get_file_rcu_many((x), 1)
 #define fput_atomic(x) atomic_long_add_unless(&(x)->f_count, -1, 1)
 #define file_count(x)  atomic_long_read(&(x)->f_count)