Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+22 -8
+10 -5
fs/file.c
··· 705 705 spin_unlock(&files->file_lock); 706 706 } 707 707 708 - static struct file *__fget(unsigned int fd, fmode_t mask) 708 + static struct file *__fget(unsigned int fd, fmode_t mask, unsigned int refs) 709 709 { 710 710 struct files_struct *files = current->files; 711 711 struct file *file; ··· 720 720 */ 721 721 if (file->f_mode & mask) 722 722 file = NULL; 723 - else if (!get_file_rcu(file)) 723 + else if (!get_file_rcu_many(file, refs)) 724 724 goto loop; 725 725 } 726 726 rcu_read_unlock(); ··· 728 728 return file; 729 729 } 730 730 731 + struct file *fget_many(unsigned int fd, unsigned int refs) 732 + { 733 + return __fget(fd, FMODE_PATH, refs); 734 + } 735 + 731 736 struct file *fget(unsigned int fd) 732 737 { 733 - return __fget(fd, FMODE_PATH); 738 + return __fget(fd, FMODE_PATH, 1); 734 739 } 735 740 EXPORT_SYMBOL(fget); 736 741 737 742 struct file *fget_raw(unsigned int fd) 738 743 { 739 - return __fget(fd, 0); 744 + return __fget(fd, 0, 1); 740 745 } 741 746 EXPORT_SYMBOL(fget_raw); 742 747 ··· 772 767 return 0; 773 768 return (unsigned long)file; 774 769 } else { 775 - file = __fget(fd, mask); 770 + file = __fget(fd, mask, 1); 776 771 if (!file) 777 772 return 0; 778 773 return FDPUT_FPUT | (unsigned long)file;
+7 -2
fs/file_table.c
··· 326 326 327 327 static DECLARE_DELAYED_WORK(delayed_fput_work, delayed_fput); 328 328 329 - void fput(struct file *file) 329 + void 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 332 struct task_struct *task = current; 333 333 334 334 if (likely(!in_interrupt() && !(task->flags & PF_KTHREAD))) { ··· 345 345 if (llist_add(&file->f_u.fu_llist, &delayed_fput_list)) 346 346 schedule_delayed_work(&delayed_fput_work, 1); 347 347 } 348 + } 349 + 350 + void fput(struct file *file) 351 + { 352 + fput_many(file, 1); 348 353 } 349 354 350 355 /*
+2
include/linux/file.h
··· 13 13 struct file; 14 14 15 15 extern void fput(struct file *); 16 + extern void fput_many(struct file *, unsigned int); 16 17 17 18 struct file_operations; 18 19 struct vfsmount; ··· 45 44 } 46 45 47 46 extern struct file *fget(unsigned int fd); 47 + extern struct file *fget_many(unsigned int fd, unsigned int refs); 48 48 extern struct file *fget_raw(unsigned int fd); 49 49 extern unsigned long __fdget(unsigned int fd); 50 50 extern unsigned long __fdget_raw(unsigned int fd);
+3 -1
include/linux/fs.h
··· 952 952 atomic_long_inc(&f->f_count); 953 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 958 #define fput_atomic(x) atomic_long_add_unless(&(x)->f_count, -1, 1) 957 959 #define file_count(x) atomic_long_read(&(x)->f_count) 958 960