Skip to content

Commit

Permalink
[PATCH] fdtable: Remove the free_files field
Browse files Browse the repository at this point in the history
An fdtable can either be embedded inside a files_struct or standalone (after
being expanded).  When an fdtable is being discarded after all RCU references
to it have expired, we must either free it directly, in the standalone case,
or free the files_struct it is contained within, in the embedded case.

Currently the free_files field controls this behavior, but we can get rid of
it entirely, as all the necessary information is already recorded.  We can
distinguish embedded and standalone fdtables using max_fds, and if it is
embedded we can divine the relevant files_struct using container_of().

Signed-off-by: Vadim Lobanov <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Al Viro <[email protected]>
Cc: Dipankar Sarma <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Vadim Lobanov authored and Linus Torvalds committed Dec 10, 2006
1 parent bbea9f6 commit 4fd4581
Show file tree
Hide file tree
Showing 5 changed files with 11 additions and 27 deletions.
27 changes: 8 additions & 19 deletions fs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ static void free_fdtable_work(struct work_struct *work)
}
}

static void free_fdtable_rcu(struct rcu_head *rcu)
void free_fdtable_rcu(struct rcu_head *rcu)
{
struct fdtable *fdt = container_of(rcu, struct fdtable, rcu);
int fdset_size, fdarray_size;
Expand All @@ -101,20 +101,15 @@ static void free_fdtable_rcu(struct rcu_head *rcu)
fdset_size = fdt->max_fds / 8;
fdarray_size = fdt->max_fds * sizeof(struct file *);

if (fdt->free_files) {
if (fdt->max_fds <= NR_OPEN_DEFAULT) {
/*
* The this fdtable was embedded in the files structure
* and the files structure itself was getting destroyed.
* It is now safe to free the files structure.
* This fdtable is embedded in the files structure and that
* structure itself is getting destroyed.
*/
kmem_cache_free(files_cachep, fdt->free_files);
kmem_cache_free(files_cachep,
container_of(fdt, struct files_struct, fdtab));
return;
}
if (fdt->max_fds <= NR_OPEN_DEFAULT)
/*
* The fdtable was embedded
*/
return;
if (fdset_size <= PAGE_SIZE && fdarray_size <= PAGE_SIZE) {
kfree(fdt->open_fds);
kfree(fdt->close_on_exec);
Expand All @@ -132,12 +127,6 @@ static void free_fdtable_rcu(struct rcu_head *rcu)
}
}

void free_fdtable(struct fdtable *fdt)
{
if (fdt->free_files || fdt->max_fds > NR_OPEN_DEFAULT)
call_rcu(&fdt->rcu, free_fdtable_rcu);
}

/*
* Expand the fdset in the files_struct. Called with the files spinlock
* held for write.
Expand Down Expand Up @@ -247,7 +236,6 @@ static struct fdtable *alloc_fdtable(int nr)
goto out;
fdt->fd = new_fds;
fdt->max_fds = nfds;
fdt->free_files = NULL;
return fdt;
out:
free_fdset(new_openset, nfds);
Expand Down Expand Up @@ -283,7 +271,8 @@ static int expand_fdtable(struct files_struct *files, int nr)
/* Continue as planned */
copy_fdtable(new_fdt, cur_fdt);
rcu_assign_pointer(files->fdt, new_fdt);
free_fdtable(cur_fdt);
if (cur_fdt->max_fds > NR_OPEN_DEFAULT)
call_rcu(&cur_fdt->rcu, free_fdtable_rcu);
} else {
/* Somebody else expanded, so undo our attempt */
__free_fdtable(new_fdt);
Expand Down
3 changes: 1 addition & 2 deletions include/linux/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ struct fdtable {
fd_set *close_on_exec;
fd_set *open_fds;
struct rcu_head rcu;
struct files_struct *free_files;
struct fdtable *next;
};

Expand Down Expand Up @@ -84,7 +83,7 @@ extern fd_set *alloc_fdset(int);
extern void free_fdset(fd_set *, int);

extern int expand_files(struct files_struct *, int nr);
extern void free_fdtable(struct fdtable *fdt);
extern void free_fdtable_rcu(struct rcu_head *rcu);
extern void __init files_defer_init(void);

static inline struct file * fcheck_files(struct files_struct *files, unsigned int fd)
Expand Down
1 change: 0 additions & 1 deletion include/linux/init_task.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
.close_on_exec = (fd_set *)&init_files.close_on_exec_init, \
.open_fds = (fd_set *)&init_files.open_fds_init, \
.rcu = RCU_HEAD_INIT, \
.free_files = NULL, \
.next = NULL, \
}

Expand Down
6 changes: 2 additions & 4 deletions kernel/exit.c
Original file line number Diff line number Diff line change
Expand Up @@ -466,11 +466,9 @@ void fastcall put_files_struct(struct files_struct *files)
* you can free files immediately.
*/
fdt = files_fdtable(files);
if (fdt == &files->fdtab)
fdt->free_files = files;
else
if (fdt != &files->fdtab)
kmem_cache_free(files_cachep, files);
free_fdtable(fdt);
call_rcu(&fdt->rcu, free_fdtable_rcu);
}
}

Expand Down
1 change: 0 additions & 1 deletion kernel/fork.c
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,6 @@ static struct files_struct *alloc_files(void)
fdt->open_fds = (fd_set *)&newf->open_fds_init;
fdt->fd = &newf->fd_array[0];
INIT_RCU_HEAD(&fdt->rcu);
fdt->free_files = NULL;
fdt->next = NULL;
rcu_assign_pointer(newf->fdt, fdt);
out:
Expand Down

0 comments on commit 4fd4581

Please sign in to comment.