mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'vfs-6.13.file' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull vfs file updates from Christian Brauner:
"This contains changes the changes for files for this cycle:
- Introduce a new reference counting mechanism for files.
As atomic_inc_not_zero() is implemented with a try_cmpxchg() loop
it has O(N^2) behaviour under contention with N concurrent
operations and it is in a hot path in __fget_files_rcu().
The rcuref infrastructures remedies this problem by using an
unconditional increment relying on safe- and dead zones to make
this work and requiring rcu protection for the data structure in
question. This not just scales better it also introduces overflow
protection.
However, in contrast to generic rcuref, files require a memory
barrier and thus cannot rely on *_relaxed() atomic operations and
also require to be built on atomic_long_t as having massive amounts
of reference isn't unheard of even if it is just an attack.
This adds a file specific variant instead of making this a generic
library.
This has been tested by various people and it gives consistent
improvement up to 3-5% on workloads with loads of threads.
- Add a fastpath for find_next_zero_bit(). Skip 2-levels searching
via find_next_zero_bit() when there is a free slot in the word that
contains the next fd. This improves pts/blogbench-1.1.0 read by 8%
and write by 4% on Intel ICX 160.
- Conditionally clear full_fds_bits since it's very likely that a bit
in full_fds_bits has been cleared during __clear_open_fds(). This
improves pts/blogbench-1.1.0 read up to 13%, and write up to 5% on
Intel ICX 160.
- Get rid of all lookup_*_fdget_rcu() variants. They were used to
lookup files without taking a reference count. That became invalid
once files were switched to SLAB_TYPESAFE_BY_RCU and now we're
always taking a reference count. Switch to an already existing
helper and remove the legacy variants.
- Remove pointless includes of <linux/fdtable.h>.
- Avoid cmpxchg() in close_files() as nobody else has a reference to
the files_struct at that point.
- Move close_range() into fs/file.c and fold __close_range() into it.
- Cleanup calling conventions of alloc_fdtable() and expand_files().
- Merge __{set,clear}_close_on_exec() into one.
- Make __set_open_fd() set cloexec as well instead of doing it in two
separate steps"
* tag 'vfs-6.13.file' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
selftests: add file SLAB_TYPESAFE_BY_RCU recycling stressor
fs: port files to file_ref
fs: add file_ref
expand_files(): simplify calling conventions
make __set_open_fd() set cloexec state as well
fs: protect backing files with rcu
file.c: merge __{set,clear}_close_on_exec()
alloc_fdtable(): change calling conventions.
fs/file.c: add fast path in find_next_fd()
fs/file.c: conditionally clear full_fds
fs/file.c: remove sanity_check and add likely/unlikely in alloc_fd()
move close_range(2) into fs/file.c, fold __close_range() into it
close_files(): don't bother with xchg()
remove pointless includes of <linux/fdtable.h>
get rid of ...lookup...fdget_rcu() family
This commit is contained in:
@@ -73,9 +73,7 @@ static struct spu_context *coredump_next_context(int *fd)
|
||||
return NULL;
|
||||
*fd = n - 1;
|
||||
|
||||
rcu_read_lock();
|
||||
file = lookup_fdget_rcu(*fd);
|
||||
rcu_read_unlock();
|
||||
file = fget_raw(*fd);
|
||||
if (file) {
|
||||
ctx = SPUFS_I(file_inode(file))->i_ctx;
|
||||
get_spu_context(ctx);
|
||||
|
||||
@@ -40,7 +40,7 @@ struct file *shmem_create_from_object(struct drm_i915_gem_object *obj)
|
||||
|
||||
if (i915_gem_object_is_shmem(obj)) {
|
||||
file = obj->base.filp;
|
||||
atomic_long_inc(&file->f_count);
|
||||
get_file(file);
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
@@ -471,7 +471,7 @@ void ttm_object_device_release(struct ttm_object_device **p_tdev)
|
||||
*/
|
||||
static bool __must_check get_dma_buf_unless_doomed(struct dma_buf *dmabuf)
|
||||
{
|
||||
return atomic_long_inc_not_zero(&dmabuf->file->f_count) != 0L;
|
||||
return file_ref_get(&dmabuf->file->f_ref);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -1003,7 +1003,7 @@ static struct file *epi_fget(const struct epitem *epi)
|
||||
struct file *file;
|
||||
|
||||
file = epi->ffd.file;
|
||||
if (!atomic_long_inc_not_zero(&file->f_count))
|
||||
if (!file_ref_get(&file->f_ref))
|
||||
file = NULL;
|
||||
return file;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/filelock.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fdtable.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/dnotify.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
+37
-13
@@ -9,7 +9,6 @@
|
||||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fdtable.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
@@ -40,13 +39,17 @@ static struct files_stat_struct files_stat = {
|
||||
|
||||
/* SLAB cache for file structures */
|
||||
static struct kmem_cache *filp_cachep __ro_after_init;
|
||||
static struct kmem_cache *bfilp_cachep __ro_after_init;
|
||||
|
||||
static struct percpu_counter nr_files __cacheline_aligned_in_smp;
|
||||
|
||||
/* Container for backing file with optional user path */
|
||||
struct backing_file {
|
||||
struct file file;
|
||||
struct path user_path;
|
||||
union {
|
||||
struct path user_path;
|
||||
freeptr_t bf_freeptr;
|
||||
};
|
||||
};
|
||||
|
||||
static inline struct backing_file *backing_file(struct file *f)
|
||||
@@ -68,7 +71,7 @@ static inline void file_free(struct file *f)
|
||||
put_cred(f->f_cred);
|
||||
if (unlikely(f->f_mode & FMODE_BACKING)) {
|
||||
path_put(backing_file_user_path(f));
|
||||
kfree(backing_file(f));
|
||||
kmem_cache_free(bfilp_cachep, backing_file(f));
|
||||
} else {
|
||||
kmem_cache_free(filp_cachep, f);
|
||||
}
|
||||
@@ -165,16 +168,32 @@ static int init_file(struct file *f, int flags, const struct cred *cred)
|
||||
* the respective member when opening the file.
|
||||
*/
|
||||
mutex_init(&f->f_pos_lock);
|
||||
f->f_flags = flags;
|
||||
f->f_mode = OPEN_FMODE(flags);
|
||||
/* f->f_version: 0 */
|
||||
memset(&f->f_path, 0, sizeof(f->f_path));
|
||||
memset(&f->f_ra, 0, sizeof(f->f_ra));
|
||||
|
||||
f->f_flags = flags;
|
||||
f->f_mode = OPEN_FMODE(flags);
|
||||
|
||||
f->f_op = NULL;
|
||||
f->f_mapping = NULL;
|
||||
f->private_data = NULL;
|
||||
f->f_inode = NULL;
|
||||
f->f_owner = NULL;
|
||||
#ifdef CONFIG_EPOLL
|
||||
f->f_ep = NULL;
|
||||
#endif
|
||||
|
||||
f->f_iocb_flags = 0;
|
||||
f->f_pos = 0;
|
||||
f->f_wb_err = 0;
|
||||
f->f_sb_err = 0;
|
||||
|
||||
/*
|
||||
* We're SLAB_TYPESAFE_BY_RCU so initialize f_count last. While
|
||||
* fget-rcu pattern users need to be able to handle spurious
|
||||
* refcount bumps we should reinitialize the reused file first.
|
||||
*/
|
||||
atomic_long_set(&f->f_count, 1);
|
||||
file_ref_init(&f->f_ref, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -206,7 +225,7 @@ struct file *alloc_empty_file(int flags, const struct cred *cred)
|
||||
goto over;
|
||||
}
|
||||
|
||||
f = kmem_cache_zalloc(filp_cachep, GFP_KERNEL);
|
||||
f = kmem_cache_alloc(filp_cachep, GFP_KERNEL);
|
||||
if (unlikely(!f))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
@@ -240,7 +259,7 @@ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred)
|
||||
struct file *f;
|
||||
int error;
|
||||
|
||||
f = kmem_cache_zalloc(filp_cachep, GFP_KERNEL);
|
||||
f = kmem_cache_alloc(filp_cachep, GFP_KERNEL);
|
||||
if (unlikely(!f))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
@@ -267,13 +286,13 @@ struct file *alloc_empty_backing_file(int flags, const struct cred *cred)
|
||||
struct backing_file *ff;
|
||||
int error;
|
||||
|
||||
ff = kzalloc(sizeof(struct backing_file), GFP_KERNEL);
|
||||
ff = kmem_cache_alloc(bfilp_cachep, GFP_KERNEL);
|
||||
if (unlikely(!ff))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
error = init_file(&ff->file, flags, cred);
|
||||
if (unlikely(error)) {
|
||||
kfree(ff);
|
||||
kmem_cache_free(bfilp_cachep, ff);
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
@@ -479,7 +498,7 @@ static DECLARE_DELAYED_WORK(delayed_fput_work, delayed_fput);
|
||||
|
||||
void fput(struct file *file)
|
||||
{
|
||||
if (atomic_long_dec_and_test(&file->f_count)) {
|
||||
if (file_ref_put(&file->f_ref)) {
|
||||
struct task_struct *task = current;
|
||||
|
||||
if (unlikely(!(file->f_mode & (FMODE_BACKING | FMODE_OPENED)))) {
|
||||
@@ -512,7 +531,7 @@ void fput(struct file *file)
|
||||
*/
|
||||
void __fput_sync(struct file *file)
|
||||
{
|
||||
if (atomic_long_dec_and_test(&file->f_count))
|
||||
if (file_ref_put(&file->f_ref))
|
||||
__fput(file);
|
||||
}
|
||||
|
||||
@@ -529,6 +548,11 @@ void __init files_init(void)
|
||||
filp_cachep = kmem_cache_create("filp", sizeof(struct file), &args,
|
||||
SLAB_HWCACHE_ALIGN | SLAB_PANIC |
|
||||
SLAB_ACCOUNT | SLAB_TYPESAFE_BY_RCU);
|
||||
|
||||
args.freeptr_offset = offsetof(struct backing_file, bf_freeptr);
|
||||
bfilp_cachep = kmem_cache_create("bfilp", sizeof(struct backing_file),
|
||||
&args, SLAB_HWCACHE_ALIGN | SLAB_PANIC |
|
||||
SLAB_ACCOUNT | SLAB_TYPESAFE_BY_RCU);
|
||||
percpu_counter_init(&nr_files, 0, GFP_KERNEL);
|
||||
}
|
||||
|
||||
|
||||
+2
-10
@@ -34,7 +34,6 @@
|
||||
#include <linux/lockref.h>
|
||||
#include <linux/rhashtable.h>
|
||||
#include <linux/pid_namespace.h>
|
||||
#include <linux/fdtable.h>
|
||||
#include <linux/file.h>
|
||||
|
||||
#include "gfs2.h"
|
||||
@@ -2768,25 +2767,18 @@ static struct file *gfs2_glockfd_next_file(struct gfs2_glockfd_iter *i)
|
||||
i->file = NULL;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
for(;; i->fd++) {
|
||||
struct inode *inode;
|
||||
|
||||
i->file = task_lookup_next_fdget_rcu(i->task, &i->fd);
|
||||
i->file = fget_task_next(i->task, &i->fd);
|
||||
if (!i->file) {
|
||||
i->fd = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
inode = file_inode(i->file);
|
||||
if (inode->i_sb == i->sb)
|
||||
if (file_inode(i->file)->i_sb == i->sb)
|
||||
break;
|
||||
|
||||
rcu_read_unlock();
|
||||
fput(i->file);
|
||||
rcu_read_lock();
|
||||
}
|
||||
rcu_read_unlock();
|
||||
return i->file;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <linux/security.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fdtable.h>
|
||||
#include <linux/fsnotify_backend.h>
|
||||
|
||||
static int dir_notify_enable __read_mostly = 1;
|
||||
@@ -347,9 +346,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned int arg)
|
||||
new_fsn_mark = NULL;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
f = lookup_fdget_rcu(fd);
|
||||
rcu_read_unlock();
|
||||
f = fget_raw(fd);
|
||||
|
||||
/* if (f != filp) means that we lost a race and another task/thread
|
||||
* actually closed the fd we are still playing with before we grabbed
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/fanotify.h>
|
||||
#include <linux/fdtable.h>
|
||||
#include <linux/fsnotify_backend.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/jiffies.h>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/fanotify.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/fdtable.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/anon_inodes.h>
|
||||
|
||||
@@ -1576,23 +1576,6 @@ SYSCALL_DEFINE1(close, unsigned int, fd)
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* sys_close_range() - Close all file descriptors in a given range.
|
||||
*
|
||||
* @fd: starting file descriptor to close
|
||||
* @max_fd: last file descriptor to close
|
||||
* @flags: reserved for future extensions
|
||||
*
|
||||
* This closes a range of file descriptors. All file descriptors
|
||||
* from @fd up to and including @max_fd are closed.
|
||||
* Currently, errors to close a given file descriptor are ignored.
|
||||
*/
|
||||
SYSCALL_DEFINE3(close_range, unsigned int, fd, unsigned int, max_fd,
|
||||
unsigned int, flags)
|
||||
{
|
||||
return __close_range(fd, max_fd, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine simulates a hangup on the tty, to arrange that users
|
||||
* are given clean terminals at login time.
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/fdtable.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/exportfs.h>
|
||||
#include "overlayfs.h"
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fdtable.h>
|
||||
#include <linux/generic-radix-tree.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
+3
-9
@@ -116,9 +116,7 @@ static bool tid_fd_mode(struct task_struct *task, unsigned fd, fmode_t *mode)
|
||||
{
|
||||
struct file *file;
|
||||
|
||||
rcu_read_lock();
|
||||
file = task_lookup_fdget_rcu(task, fd);
|
||||
rcu_read_unlock();
|
||||
file = fget_task(task, fd);
|
||||
if (file) {
|
||||
*mode = file->f_mode;
|
||||
fput(file);
|
||||
@@ -258,19 +256,17 @@ static int proc_readfd_common(struct file *file, struct dir_context *ctx,
|
||||
if (!dir_emit_dots(file, ctx))
|
||||
goto out;
|
||||
|
||||
rcu_read_lock();
|
||||
for (fd = ctx->pos - 2;; fd++) {
|
||||
struct file *f;
|
||||
struct fd_data data;
|
||||
char name[10 + 1];
|
||||
unsigned int len;
|
||||
|
||||
f = task_lookup_next_fdget_rcu(p, &fd);
|
||||
f = fget_task_next(p, &fd);
|
||||
ctx->pos = fd + 2LL;
|
||||
if (!f)
|
||||
break;
|
||||
data.mode = f->f_mode;
|
||||
rcu_read_unlock();
|
||||
fput(f);
|
||||
data.fd = fd;
|
||||
|
||||
@@ -278,11 +274,9 @@ static int proc_readfd_common(struct file *file, struct dir_context *ctx,
|
||||
if (!proc_fill_cache(file, ctx,
|
||||
name, len, instantiate, p,
|
||||
&data))
|
||||
goto out;
|
||||
break;
|
||||
cond_resched();
|
||||
rcu_read_lock();
|
||||
}
|
||||
rcu_read_unlock();
|
||||
out:
|
||||
put_task_struct(p);
|
||||
return 0;
|
||||
|
||||
@@ -92,10 +92,6 @@ static inline struct file *files_lookup_fd_locked(struct files_struct *files, un
|
||||
return files_lookup_fd_raw(files, fd);
|
||||
}
|
||||
|
||||
struct file *lookup_fdget_rcu(unsigned int fd);
|
||||
struct file *task_lookup_fdget_rcu(struct task_struct *task, unsigned int fd);
|
||||
struct file *task_lookup_next_fdget_rcu(struct task_struct *task, unsigned int *fd);
|
||||
|
||||
static inline bool close_on_exec(unsigned int fd, const struct files_struct *files)
|
||||
{
|
||||
return test_bit(fd, files_fdtable(files)->close_on_exec);
|
||||
@@ -115,7 +111,6 @@ int iterate_fd(struct files_struct *, unsigned,
|
||||
const void *);
|
||||
|
||||
extern int close_fd(unsigned int fd);
|
||||
extern int __close_range(unsigned int fd, unsigned int max_fd, unsigned int flags);
|
||||
extern struct file *file_close_fd(unsigned int fd);
|
||||
|
||||
extern struct kmem_cache *files_cachep;
|
||||
|
||||
@@ -72,6 +72,7 @@ static inline void fdput(struct fd fd)
|
||||
extern struct file *fget(unsigned int fd);
|
||||
extern struct file *fget_raw(unsigned int fd);
|
||||
extern struct file *fget_task(struct task_struct *task, unsigned int fd);
|
||||
extern struct file *fget_task_next(struct task_struct *task, unsigned int *fd);
|
||||
extern void __f_unlock_pos(struct file *);
|
||||
|
||||
struct fd fdget(unsigned int fd);
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
#ifndef _LINUX_FILE_REF_H
|
||||
#define _LINUX_FILE_REF_H
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/preempt.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* file_ref is a reference count implementation specifically for use by
|
||||
* files. It takes inspiration from rcuref but differs in key aspects
|
||||
* such as support for SLAB_TYPESAFE_BY_RCU type caches.
|
||||
*
|
||||
* FILE_REF_ONEREF FILE_REF_MAXREF
|
||||
* 0x0000000000000000UL 0x7FFFFFFFFFFFFFFFUL
|
||||
* <-------------------valid ------------------->
|
||||
*
|
||||
* FILE_REF_SATURATED
|
||||
* 0x8000000000000000UL 0xA000000000000000UL 0xBFFFFFFFFFFFFFFFUL
|
||||
* <-----------------------saturation zone---------------------->
|
||||
*
|
||||
* FILE_REF_RELEASED FILE_REF_DEAD
|
||||
* 0xC000000000000000UL 0xE000000000000000UL
|
||||
* <-------------------dead zone------------------->
|
||||
*
|
||||
* FILE_REF_NOREF
|
||||
* 0xFFFFFFFFFFFFFFFFUL
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
#define FILE_REF_ONEREF 0x0000000000000000UL
|
||||
#define FILE_REF_MAXREF 0x7FFFFFFFFFFFFFFFUL
|
||||
#define FILE_REF_SATURATED 0xA000000000000000UL
|
||||
#define FILE_REF_RELEASED 0xC000000000000000UL
|
||||
#define FILE_REF_DEAD 0xE000000000000000UL
|
||||
#define FILE_REF_NOREF 0xFFFFFFFFFFFFFFFFUL
|
||||
#else
|
||||
#define FILE_REF_ONEREF 0x00000000U
|
||||
#define FILE_REF_MAXREF 0x7FFFFFFFU
|
||||
#define FILE_REF_SATURATED 0xA0000000U
|
||||
#define FILE_REF_RELEASED 0xC0000000U
|
||||
#define FILE_REF_DEAD 0xE0000000U
|
||||
#define FILE_REF_NOREF 0xFFFFFFFFU
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
#ifdef CONFIG_64BIT
|
||||
atomic64_t refcnt;
|
||||
#else
|
||||
atomic_t refcnt;
|
||||
#endif
|
||||
} file_ref_t;
|
||||
|
||||
/**
|
||||
* file_ref_init - Initialize a file reference count
|
||||
* @ref: Pointer to the reference count
|
||||
* @cnt: The initial reference count typically '1'
|
||||
*/
|
||||
static inline void file_ref_init(file_ref_t *ref, unsigned long cnt)
|
||||
{
|
||||
atomic_long_set(&ref->refcnt, cnt - 1);
|
||||
}
|
||||
|
||||
bool __file_ref_put(file_ref_t *ref, unsigned long cnt);
|
||||
|
||||
/**
|
||||
* file_ref_get - Acquire one reference on a file
|
||||
* @ref: Pointer to the reference count
|
||||
*
|
||||
* Similar to atomic_inc_not_zero() but saturates at FILE_REF_MAXREF.
|
||||
*
|
||||
* Provides full memory ordering.
|
||||
*
|
||||
* Return: False if the attempt to acquire a reference failed. This happens
|
||||
* when the last reference has been put already. True if a reference
|
||||
* was successfully acquired
|
||||
*/
|
||||
static __always_inline __must_check bool file_ref_get(file_ref_t *ref)
|
||||
{
|
||||
/*
|
||||
* Unconditionally increase the reference count with full
|
||||
* ordering. The saturation and dead zones provide enough
|
||||
* tolerance for this.
|
||||
*
|
||||
* If this indicates negative the file in question the fail can
|
||||
* be freed and immediately reused due to SLAB_TYPSAFE_BY_RCU.
|
||||
* Hence, unconditionally altering the file reference count to
|
||||
* e.g., reset the file reference count back to the middle of
|
||||
* the deadzone risk end up marking someone else's file as dead
|
||||
* behind their back.
|
||||
*
|
||||
* It would be possible to do a careful:
|
||||
*
|
||||
* cnt = atomic_long_inc_return();
|
||||
* if (likely(cnt >= 0))
|
||||
* return true;
|
||||
*
|
||||
* and then something like:
|
||||
*
|
||||
* if (cnt >= FILE_REF_RELEASE)
|
||||
* atomic_long_try_cmpxchg(&ref->refcnt, &cnt, FILE_REF_DEAD),
|
||||
*
|
||||
* to set the value back to the middle of the deadzone. But it's
|
||||
* practically impossible to go from FILE_REF_DEAD to
|
||||
* FILE_REF_ONEREF. It would need 2305843009213693952/2^61
|
||||
* file_ref_get()s to resurrect such a dead file.
|
||||
*/
|
||||
return !atomic_long_add_negative(1, &ref->refcnt);
|
||||
}
|
||||
|
||||
/**
|
||||
* file_ref_inc - Acquire one reference on a file
|
||||
* @ref: Pointer to the reference count
|
||||
*
|
||||
* Acquire an additional reference on a file. Warns if the caller didn't
|
||||
* already hold a reference.
|
||||
*/
|
||||
static __always_inline void file_ref_inc(file_ref_t *ref)
|
||||
{
|
||||
long prior = atomic_long_fetch_inc_relaxed(&ref->refcnt);
|
||||
WARN_ONCE(prior < 0, "file_ref_inc() on a released file reference");
|
||||
}
|
||||
|
||||
/**
|
||||
* file_ref_put -- Release a file reference
|
||||
* @ref: Pointer to the reference count
|
||||
*
|
||||
* Provides release memory ordering, such that prior loads and stores
|
||||
* are done before, and provides an acquire ordering on success such
|
||||
* that free() must come after.
|
||||
*
|
||||
* Return: True if this was the last reference with no future references
|
||||
* possible. This signals the caller that it can safely release
|
||||
* the object which is protected by the reference counter.
|
||||
* False if there are still active references or the put() raced
|
||||
* with a concurrent get()/put() pair. Caller is not allowed to
|
||||
* release the protected object.
|
||||
*/
|
||||
static __always_inline __must_check bool file_ref_put(file_ref_t *ref)
|
||||
{
|
||||
long cnt;
|
||||
|
||||
/*
|
||||
* While files are SLAB_TYPESAFE_BY_RCU and thus file_ref_put()
|
||||
* calls don't risk UAFs when a file is recyclyed, it is still
|
||||
* vulnerable to UAFs caused by freeing the whole slab page once
|
||||
* it becomes unused. Prevent file_ref_put() from being
|
||||
* preempted protects against this.
|
||||
*/
|
||||
guard(preempt)();
|
||||
/*
|
||||
* Unconditionally decrease the reference count. The saturation
|
||||
* and dead zones provide enough tolerance for this. If this
|
||||
* fails then we need to handle the last reference drop and
|
||||
* cases inside the saturation and dead zones.
|
||||
*/
|
||||
cnt = atomic_long_dec_return(&ref->refcnt);
|
||||
if (cnt >= 0)
|
||||
return false;
|
||||
return __file_ref_put(ref, cnt);
|
||||
}
|
||||
|
||||
/**
|
||||
* file_ref_read - Read the number of file references
|
||||
* @ref: Pointer to the reference count
|
||||
*
|
||||
* Return: The number of held references (0 ... N)
|
||||
*/
|
||||
static inline unsigned long file_ref_read(file_ref_t *ref)
|
||||
{
|
||||
unsigned long c = atomic_long_read(&ref->refcnt);
|
||||
|
||||
/* Return 0 if within the DEAD zone. */
|
||||
return c >= FILE_REF_RELEASED ? 0 : c + 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
+5
-5
@@ -45,6 +45,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/maple_tree.h>
|
||||
#include <linux/rw_hint.h>
|
||||
#include <linux/file_ref.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <uapi/linux/fs.h>
|
||||
@@ -1006,7 +1007,7 @@ static inline int ra_has_index(struct file_ra_state *ra, pgoff_t index)
|
||||
|
||||
/**
|
||||
* struct file - Represents a file
|
||||
* @f_count: reference count
|
||||
* @f_ref: reference count
|
||||
* @f_lock: Protects f_ep, f_flags. Must not be taken from IRQ context.
|
||||
* @f_mode: FMODE_* flags often used in hotpaths
|
||||
* @f_op: file operations
|
||||
@@ -1031,7 +1032,7 @@ static inline int ra_has_index(struct file_ra_state *ra, pgoff_t index)
|
||||
* @f_freeptr: Pointer used by SLAB_TYPESAFE_BY_RCU file cache (don't touch.)
|
||||
*/
|
||||
struct file {
|
||||
atomic_long_t f_count;
|
||||
file_ref_t f_ref;
|
||||
spinlock_t f_lock;
|
||||
fmode_t f_mode;
|
||||
const struct file_operations *f_op;
|
||||
@@ -1079,15 +1080,14 @@ struct file_handle {
|
||||
|
||||
static inline struct file *get_file(struct file *f)
|
||||
{
|
||||
long prior = atomic_long_fetch_inc_relaxed(&f->f_count);
|
||||
WARN_ONCE(!prior, "struct file::f_count incremented from zero; use-after-free condition present!\n");
|
||||
file_ref_inc(&f->f_ref);
|
||||
return f;
|
||||
}
|
||||
|
||||
struct file *get_file_rcu(struct file __rcu **f);
|
||||
struct file *get_file_active(struct file **f);
|
||||
|
||||
#define file_count(x) atomic_long_read(&(x)->f_count)
|
||||
#define file_count(f) file_ref_read(&(f)->f_ref)
|
||||
|
||||
#define MAX_NON_LFS ((1UL<<31) - 1)
|
||||
|
||||
|
||||
@@ -51,7 +51,6 @@
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fdtable.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/percpu.h>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user