[PATCH] files: files struct with RCU

Patch to eliminate struct files_struct.file_lock spinlock on the reader side
and use rcu refcounting rcuref_xxx api for the f_count refcounter.  The
updates to the fdtable are done by allocating a new fdtable structure and
setting files->fdt to point to the new structure.  The fdtable structure is
protected by RCU thereby allowing lock-free lookup.  For fd arrays/sets that
are vmalloced, we use keventd to free them since RCU callbacks can't sleep.  A
global list of fdtable to be freed is not scalable, so we use a per-cpu list.
If keventd is already handling the current cpu's work, we use a timer to defer
queueing of that work.

Since the last publication, this patch has been re-written to avoid using
explicit memory barriers and use rcu_assign_pointer(), rcu_dereference()
premitives instead.  This required that the fd information is kept in a
separate structure (fdtable) and updated atomically.

Signed-off-by: Dipankar Sarma <dipankar@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
Dipankar Sarma
2005-09-09 13:04:13 -07:00
committed by Linus Torvalds
parent 6e72ad2c58
commit ab2af1f500
10 changed files with 354 additions and 175 deletions

View File

@@ -14,6 +14,7 @@
#include <linux/fs.h>
#include <linux/security.h>
#include <linux/eventpoll.h>
#include <linux/rcupdate.h>
#include <linux/mount.h>
#include <linux/cdev.h>
#include <linux/fsnotify.h>
@@ -53,9 +54,15 @@ void filp_dtor(void * objp, struct kmem_cache_s *cachep, unsigned long dflags)
spin_unlock_irqrestore(&filp_count_lock, flags);
}
static inline void file_free_rcu(struct rcu_head *head)
{
struct file *f = container_of(head, struct file, f_rcuhead);
kmem_cache_free(filp_cachep, f);
}
static inline void file_free(struct file *f)
{
kmem_cache_free(filp_cachep, f);
call_rcu(&f->f_rcuhead, file_free_rcu);
}
/* Find an unused file structure and return a pointer to it.
@@ -110,7 +117,7 @@ EXPORT_SYMBOL(get_empty_filp);
void fastcall fput(struct file *file)
{
if (atomic_dec_and_test(&file->f_count))
if (rcuref_dec_and_test(&file->f_count))
__fput(file);
}
@@ -156,11 +163,17 @@ struct file fastcall *fget(unsigned int fd)
struct file *file;
struct files_struct *files = current->files;
spin_lock(&files->file_lock);
rcu_read_lock();
file = fcheck_files(files, fd);
if (file)
get_file(file);
spin_unlock(&files->file_lock);
if (file) {
if (!rcuref_inc_lf(&file->f_count)) {
/* File object ref couldn't be taken */
rcu_read_unlock();
return NULL;
}
}
rcu_read_unlock();
return file;
}
@@ -182,21 +195,25 @@ struct file fastcall *fget_light(unsigned int fd, int *fput_needed)
if (likely((atomic_read(&files->count) == 1))) {
file = fcheck_files(files, fd);
} else {
spin_lock(&files->file_lock);
rcu_read_lock();
file = fcheck_files(files, fd);
if (file) {
get_file(file);
*fput_needed = 1;
if (rcuref_inc_lf(&file->f_count))
*fput_needed = 1;
else
/* Didn't get the reference, someone's freed */
file = NULL;
}
spin_unlock(&files->file_lock);
rcu_read_unlock();
}
return file;
}
void put_filp(struct file *file)
{
if (atomic_dec_and_test(&file->f_count)) {
if (rcuref_dec_and_test(&file->f_count)) {
security_file_free(file);
file_kill(file);
file_free(file);
@@ -257,4 +274,5 @@ void __init files_init(unsigned long mempages)
files_stat.max_files = n;
if (files_stat.max_files < NR_FILE)
files_stat.max_files = NR_FILE;
files_defer_init();
}