Skip to content

Commit 93faf42

Browse files
mjguzikbrauner
authored andcommitted
vfs: shave work on failed file open
Failed opens (mostly ENOENT) legitimately happen a lot, for example here are stats from stracing kernel build for few seconds (strace -fc make): % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ------------------ 0.76 0.076233 5 15040 3688 openat (this is tons of header files tried in different paths) In the common case of there being nothing to close (only the file object to free) there is a lot of overhead which can be avoided. This is most notably delegation of freeing to task_work, which comes with an enormous cost (see 021a160 ("fs: use __fput_sync in close(2)" for an example). Benchmarked with will-it-scale with a custom testcase based on tests/open1.c, stuffed into tests/openneg.c: [snip] while (1) { int fd = open("/tmp/nonexistent", O_RDONLY); assert(fd == -1); (*iterations)++; } [/snip] Sapphire Rapids, openneg_processes -t 1 (ops/s): before: 1950013 after: 2914973 (+49%) file refcount is checked as a safety belt against buggy consumers with an atomic cmpxchg. Technically it is not necessary, but it happens to not be measurable due to several other atomics which immediately follow. Optmizing them away to make this atomic into a problem is left as an exercise for the reader. v2: - unexport fput_badopen and move to fs/internal.h - handle the refcount with cmpxchg, adjust commentary accordingly - tweak the commit message Signed-off-by: Mateusz Guzik <mjguzik@gmail.com> Link: https://lore.kernel.org/r/20230926162228.68666-1-mjguzik@gmail.com Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent 6036c5f commit 93faf42

3 files changed

Lines changed: 17 additions & 1 deletion

File tree

fs/file_table.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,18 @@ static inline void file_free(struct file *f)
8282
call_rcu(&f->f_rcuhead, file_free_rcu);
8383
}
8484

85+
void release_empty_file(struct file *f)
86+
{
87+
WARN_ON_ONCE(f->f_mode & (FMODE_BACKING | FMODE_OPENED));
88+
/* Uhm, we better find out who grabs references to an unopened file. */
89+
WARN_ON_ONCE(atomic_long_cmpxchg(&f->f_count, 1, 0) != 1);
90+
security_file_free(f);
91+
put_cred(f->f_cred);
92+
if (likely(!(f->f_mode & FMODE_NOACCOUNT)))
93+
percpu_counter_dec(&nr_files);
94+
kmem_cache_free(filp_cachep, f);
95+
}
96+
8597
/*
8698
* Return the total number of open files in the system
8799
*/

fs/internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ extern void chroot_fs_refs(const struct path *, const struct path *);
9494
struct file *alloc_empty_file(int flags, const struct cred *cred);
9595
struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred);
9696
struct file *alloc_empty_backing_file(int flags, const struct cred *cred);
97+
void release_empty_file(struct file *f);
9798

9899
static inline void put_file_access(struct file *file)
99100
{

fs/namei.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3783,7 +3783,10 @@ static struct file *path_openat(struct nameidata *nd,
37833783
WARN_ON(1);
37843784
error = -EINVAL;
37853785
}
3786-
fput(file);
3786+
if (unlikely(file->f_mode & FMODE_OPENED))
3787+
fput(file);
3788+
else
3789+
release_empty_file(file);
37873790
if (error == -EOPENSTALE) {
37883791
if (flags & LOOKUP_RCU)
37893792
error = -ECHILD;

0 commit comments

Comments
 (0)