summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJens Axboe <axboe@kernel.dk>2018-11-21 12:32:39 -0500
committerJens Axboe <axboe@kernel.dk>2019-02-28 10:24:23 -0500
commit091141a42e15fe47ada737f3996b317072afcefb (patch)
treed7a89e516c2e1064841dfa938cf043b5c4572675
parentdef596e9557c91d9846fc4d84d26f2c564644416 (diff)
fs: add fget_many() and fput_many()
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>
-rw-r--r--fs/file.c15
-rw-r--r--fs/file_table.c9
-rw-r--r--include/linux/file.h2
-rw-r--r--include/linux/fs.h4
4 files changed, 22 insertions, 8 deletions
diff --git a/fs/file.c b/fs/file.c
index 3209ee271c41..97df385d6ab0 100644
--- a/fs/file.c
+++ b/fs/file.c
@@ -705,7 +705,7 @@ void do_close_on_exec(struct files_struct *files)
705 spin_unlock(&files->file_lock); 705 spin_unlock(&files->file_lock);
706} 706}
707 707
708static struct file *__fget(unsigned int fd, fmode_t mask) 708static struct file *__fget(unsigned int fd, fmode_t mask, unsigned int refs)
709{ 709{
710 struct files_struct *files = current->files; 710 struct files_struct *files = current->files;
711 struct file *file; 711 struct file *file;
@@ -720,7 +720,7 @@ loop:
720 */ 720 */
721 if (file->f_mode & mask) 721 if (file->f_mode & mask)
722 file = NULL; 722 file = NULL;
723 else if (!get_file_rcu(file)) 723 else if (!get_file_rcu_many(file, refs))
724 goto loop; 724 goto loop;
725 } 725 }
726 rcu_read_unlock(); 726 rcu_read_unlock();
@@ -728,15 +728,20 @@ loop:
728 return file; 728 return file;
729} 729}
730 730
731struct file *fget_many(unsigned int fd, unsigned int refs)
732{
733 return __fget(fd, FMODE_PATH, refs);
734}
735
731struct file *fget(unsigned int fd) 736struct file *fget(unsigned int fd)
732{ 737{
733 return __fget(fd, FMODE_PATH); 738 return __fget(fd, FMODE_PATH, 1);
734} 739}
735EXPORT_SYMBOL(fget); 740EXPORT_SYMBOL(fget);
736 741
737struct file *fget_raw(unsigned int fd) 742struct file *fget_raw(unsigned int fd)
738{ 743{
739 return __fget(fd, 0); 744 return __fget(fd, 0, 1);
740} 745}
741EXPORT_SYMBOL(fget_raw); 746EXPORT_SYMBOL(fget_raw);
742 747
@@ -767,7 +772,7 @@ static unsigned long __fget_light(unsigned int fd, fmode_t mask)
767 return 0; 772 return 0;
768 return (unsigned long)file; 773 return (unsigned long)file;
769 } else { 774 } else {
770 file = __fget(fd, mask); 775 file = __fget(fd, mask, 1);
771 if (!file) 776 if (!file)
772 return 0; 777 return 0;
773 return FDPUT_FPUT | (unsigned long)file; 778 return FDPUT_FPUT | (unsigned long)file;
diff --git a/fs/file_table.c b/fs/file_table.c
index 5679e7fcb6b0..155d7514a094 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -326,9 +326,9 @@ void flush_delayed_fput(void)
326 326
327static DECLARE_DELAYED_WORK(delayed_fput_work, delayed_fput); 327static DECLARE_DELAYED_WORK(delayed_fput_work, delayed_fput);
328 328
329void fput(struct file *file) 329void fput_many(struct file *file, unsigned int refs)
330{ 330{
331 if (atomic_long_dec_and_test(&file->f_count)) { 331 if (atomic_long_sub_and_test(refs, &file->f_count)) {
332 struct task_struct *task = current; 332 struct task_struct *task = current;
333 333
334 if (likely(!in_interrupt() && !(task->flags & PF_KTHREAD))) { 334 if (likely(!in_interrupt() && !(task->flags & PF_KTHREAD))) {
@@ -347,6 +347,11 @@ void fput(struct file *file)
347 } 347 }
348} 348}
349 349
350void fput(struct file *file)
351{
352 fput_many(file, 1);
353}
354
350/* 355/*
351 * synchronous analog of fput(); for kernel threads that might be needed 356 * synchronous analog of fput(); for kernel threads that might be needed
352 * in some umount() (and thus can't use flush_delayed_fput() without 357 * in some umount() (and thus can't use flush_delayed_fput() without
diff --git a/include/linux/file.h b/include/linux/file.h
index 6b2fb032416c..3fcddff56bc4 100644
--- a/include/linux/file.h
+++ b/include/linux/file.h
@@ -13,6 +13,7 @@
13struct file; 13struct file;
14 14
15extern void fput(struct file *); 15extern void fput(struct file *);
16extern void fput_many(struct file *, unsigned int);
16 17
17struct file_operations; 18struct file_operations;
18struct vfsmount; 19struct vfsmount;
@@ -44,6 +45,7 @@ static inline void fdput(struct fd fd)
44} 45}
45 46
46extern struct file *fget(unsigned int fd); 47extern struct file *fget(unsigned int fd);
48extern struct file *fget_many(unsigned int fd, unsigned int refs);
47extern struct file *fget_raw(unsigned int fd); 49extern struct file *fget_raw(unsigned int fd);
48extern unsigned long __fdget(unsigned int fd); 50extern unsigned long __fdget(unsigned int fd);
49extern unsigned long __fdget_raw(unsigned int fd); 51extern unsigned long __fdget_raw(unsigned int fd);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 61aa210f0c2b..80e1b199a4b1 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -952,7 +952,9 @@ static inline struct file *get_file(struct file *f)
952 atomic_long_inc(&f->f_count); 952 atomic_long_inc(&f->f_count);
953 return f; 953 return f;
954} 954}
955#define get_file_rcu(x) atomic_long_inc_not_zero(&(x)->f_count) 955#define get_file_rcu_many(x, cnt) \
956 atomic_long_add_unless(&(x)->f_count, (cnt), 0)
957#define get_file_rcu(x) get_file_rcu_many((x), 1)
956#define fput_atomic(x) atomic_long_add_unless(&(x)->f_count, -1, 1) 958#define fput_atomic(x) atomic_long_add_unless(&(x)->f_count, -1, 1)
957#define file_count(x) atomic_long_read(&(x)->f_count) 959#define file_count(x) atomic_long_read(&(x)->f_count)
958 960