Skip to content

Commit

Permalink
fs: add fget_many() and fput_many()
Browse files Browse the repository at this point in the history
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 <[email protected]>
Reviewed-by: Christoph Hellwig <[email protected]>
Signed-off-by: Jens Axboe <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
axboe authored and gregkh committed Dec 8, 2021
1 parent 4f9a4d5 commit 895538f
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 8 deletions.
15 changes: 10 additions & 5 deletions fs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -677,7 +677,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;
Expand All @@ -692,23 +692,28 @@ 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();

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);

Expand Down Expand Up @@ -739,7 +744,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;
Expand Down
9 changes: 7 additions & 2 deletions fs/file_table.c
Original file line number Diff line number Diff line change
Expand Up @@ -326,9 +326,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))) {
Expand All @@ -347,6 +347,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
Expand Down
2 changes: 2 additions & 0 deletions include/linux/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
struct file;

extern void fput(struct file *);
extern void fput_many(struct file *, unsigned int);

struct file_operations;
struct vfsmount;
Expand Down Expand Up @@ -44,6 +45,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);
Expand Down
4 changes: 3 additions & 1 deletion include/linux/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -942,7 +942,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)

Expand Down

0 comments on commit 895538f

Please sign in to comment.