2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* linux/fs/file.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 1998-1999, Stephen Tweedie and Bill Hawes
|
|
|
|
*
|
|
|
|
* Manage the dynamic fd arrays in the process files_struct.
|
|
|
|
*/
|
|
|
|
|
2011-11-17 11:57:37 +07:00
|
|
|
#include <linux/export.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/mm.h>
|
2011-04-28 05:26:41 +07:00
|
|
|
#include <linux/mmzone.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/time.h>
|
2009-10-07 20:09:06 +07:00
|
|
|
#include <linux/sched.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/vmalloc.h>
|
|
|
|
#include <linux/file.h>
|
2008-04-24 18:44:08 +07:00
|
|
|
#include <linux/fdtable.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/bitops.h>
|
2005-09-10 03:04:13 +07:00
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/spinlock.h>
|
|
|
|
#include <linux/rcupdate.h>
|
|
|
|
#include <linux/workqueue.h>
|
|
|
|
|
|
|
|
struct fdtable_defer {
|
|
|
|
spinlock_t lock;
|
|
|
|
struct work_struct wq;
|
|
|
|
struct fdtable *next;
|
|
|
|
};
|
|
|
|
|
2008-02-06 16:37:16 +07:00
|
|
|
int sysctl_nr_open __read_mostly = 1024*1024;
|
2008-05-10 21:08:32 +07:00
|
|
|
int sysctl_nr_open_min = BITS_PER_LONG;
|
|
|
|
int sysctl_nr_open_max = 1024 * 1024; /* raised later */
|
2008-02-06 16:37:16 +07:00
|
|
|
|
2005-09-10 03:04:13 +07:00
|
|
|
/*
|
|
|
|
* We use this list to defer free fdtables that have vmalloced
|
|
|
|
* sets/arrays. By keeping a per-cpu list, we avoid having to embed
|
|
|
|
* the work_struct in fdtable itself which avoids a 64 byte (i386) increase in
|
|
|
|
* this per-task structure.
|
|
|
|
*/
|
|
|
|
static DEFINE_PER_CPU(struct fdtable_defer, fdtable_defer_list);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2012-02-17 00:49:54 +07:00
|
|
|
static void *alloc_fdmem(size_t size)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2011-04-28 05:26:41 +07:00
|
|
|
/*
|
|
|
|
* Very large allocations can stress page reclaim, so fall back to
|
|
|
|
* vmalloc() if the allocation size will be considered "large" by the VM.
|
|
|
|
*/
|
|
|
|
if (size <= (PAGE_SIZE << PAGE_ALLOC_COSTLY_ORDER)) {
|
|
|
|
void *data = kmalloc(size, GFP_KERNEL|__GFP_NOWARN);
|
|
|
|
if (data != NULL)
|
|
|
|
return data;
|
|
|
|
}
|
2010-08-11 08:01:35 +07:00
|
|
|
return vmalloc(size);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2010-08-11 08:01:35 +07:00
|
|
|
static void free_fdmem(void *ptr)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2010-08-11 08:01:35 +07:00
|
|
|
is_vmalloc_addr(ptr) ? vfree(ptr) : kfree(ptr);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2010-08-11 08:01:35 +07:00
|
|
|
static void __free_fdtable(struct fdtable *fdt)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2010-08-11 08:01:35 +07:00
|
|
|
free_fdmem(fdt->fd);
|
|
|
|
free_fdmem(fdt->open_fds);
|
|
|
|
kfree(fdt);
|
2005-09-10 03:04:13 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-11-22 21:55:48 +07:00
|
|
|
static void free_fdtable_work(struct work_struct *work)
|
2005-09-10 03:04:13 +07:00
|
|
|
{
|
2006-11-22 21:55:48 +07:00
|
|
|
struct fdtable_defer *f =
|
|
|
|
container_of(work, struct fdtable_defer, wq);
|
2005-09-10 03:04:13 +07:00
|
|
|
struct fdtable *fdt;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-09-10 03:04:13 +07:00
|
|
|
spin_lock_bh(&f->lock);
|
|
|
|
fdt = f->next;
|
|
|
|
f->next = NULL;
|
|
|
|
spin_unlock_bh(&f->lock);
|
|
|
|
while(fdt) {
|
|
|
|
struct fdtable *next = fdt->next;
|
2010-08-11 08:01:35 +07:00
|
|
|
|
|
|
|
__free_fdtable(fdt);
|
2005-09-10 03:04:13 +07:00
|
|
|
fdt = next;
|
|
|
|
}
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2012-08-16 06:56:12 +07:00
|
|
|
static void free_fdtable_rcu(struct rcu_head *rcu)
|
2005-09-10 03:04:13 +07:00
|
|
|
{
|
|
|
|
struct fdtable *fdt = container_of(rcu, struct fdtable, rcu);
|
|
|
|
struct fdtable_defer *fddef;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-09-10 03:04:13 +07:00
|
|
|
BUG_ON(!fdt);
|
|
|
|
|
2006-12-10 17:21:17 +07:00
|
|
|
if (fdt->max_fds <= NR_OPEN_DEFAULT) {
|
2005-09-10 03:04:13 +07:00
|
|
|
/*
|
2006-12-10 17:21:17 +07:00
|
|
|
* This fdtable is embedded in the files structure and that
|
|
|
|
* structure itself is getting destroyed.
|
2005-09-10 03:04:13 +07:00
|
|
|
*/
|
2006-12-10 17:21:17 +07:00
|
|
|
kmem_cache_free(files_cachep,
|
|
|
|
container_of(fdt, struct files_struct, fdtab));
|
2005-09-10 03:04:13 +07:00
|
|
|
return;
|
|
|
|
}
|
2010-08-11 08:01:35 +07:00
|
|
|
if (!is_vmalloc_addr(fdt->fd) && !is_vmalloc_addr(fdt->open_fds)) {
|
2005-09-10 03:04:13 +07:00
|
|
|
kfree(fdt->fd);
|
[PATCH] fdtable: Implement new pagesize-based fdtable allocator
This patch provides an improved fdtable allocation scheme, useful for
expanding fdtable file descriptor entries. The main focus is on the fdarray,
as its memory usage grows 128 times faster than that of an fdset.
The allocation algorithm sizes the fdarray in such a way that its memory usage
increases in easy page-sized chunks. The overall algorithm expands the allowed
size in powers of two, in order to amortize the cost of invoking vmalloc() for
larger allocation sizes. Namely, the following sizes for the fdarray are
considered, and the smallest that accommodates the requested fd count is
chosen:
pagesize / 4
pagesize / 2
pagesize <- memory allocator switch point
pagesize * 2
pagesize * 4
...etc...
Unlike the current implementation, this allocation scheme does not require a
loop to compute the optimal fdarray size, and can be done in efficient
straightline code.
Furthermore, since the fdarray overflows the pagesize boundary long before any
of the fdsets do, it makes sense to optimize run-time by allocating both
fdsets in a single swoop. Even together, they will still be, by far, smaller
than the fdarray. The fdtable->open_fds is now used as the anchor for the
fdset memory allocation.
Signed-off-by: Vadim Lobanov <vlobanov@speakeasy.net>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Dipankar Sarma <dipankar@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-10 17:21:22 +07:00
|
|
|
kfree(fdt->open_fds);
|
2005-09-10 03:04:13 +07:00
|
|
|
kfree(fdt);
|
2005-04-17 05:20:36 +07:00
|
|
|
} else {
|
2005-09-10 03:04:13 +07:00
|
|
|
fddef = &get_cpu_var(fdtable_defer_list);
|
|
|
|
spin_lock(&fddef->lock);
|
|
|
|
fdt->next = fddef->next;
|
|
|
|
fddef->next = fdt;
|
2006-12-07 11:36:01 +07:00
|
|
|
/* vmallocs are handled from the workqueue context */
|
|
|
|
schedule_work(&fddef->wq);
|
2005-09-10 03:04:13 +07:00
|
|
|
spin_unlock(&fddef->lock);
|
|
|
|
put_cpu_var(fdtable_defer_list);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2005-09-10 03:04:13 +07:00
|
|
|
}
|
|
|
|
|
2012-08-16 06:56:12 +07:00
|
|
|
static inline void free_fdtable(struct fdtable *fdt)
|
|
|
|
{
|
|
|
|
call_rcu(&fdt->rcu, free_fdtable_rcu);
|
|
|
|
}
|
|
|
|
|
2005-09-10 03:04:13 +07:00
|
|
|
/*
|
|
|
|
* Expand the fdset in the files_struct. Called with the files spinlock
|
|
|
|
* held for write.
|
|
|
|
*/
|
[PATCH] fdtable: Implement new pagesize-based fdtable allocator
This patch provides an improved fdtable allocation scheme, useful for
expanding fdtable file descriptor entries. The main focus is on the fdarray,
as its memory usage grows 128 times faster than that of an fdset.
The allocation algorithm sizes the fdarray in such a way that its memory usage
increases in easy page-sized chunks. The overall algorithm expands the allowed
size in powers of two, in order to amortize the cost of invoking vmalloc() for
larger allocation sizes. Namely, the following sizes for the fdarray are
considered, and the smallest that accommodates the requested fd count is
chosen:
pagesize / 4
pagesize / 2
pagesize <- memory allocator switch point
pagesize * 2
pagesize * 4
...etc...
Unlike the current implementation, this allocation scheme does not require a
loop to compute the optimal fdarray size, and can be done in efficient
straightline code.
Furthermore, since the fdarray overflows the pagesize boundary long before any
of the fdsets do, it makes sense to optimize run-time by allocating both
fdsets in a single swoop. Even together, they will still be, by far, smaller
than the fdarray. The fdtable->open_fds is now used as the anchor for the
fdset memory allocation.
Signed-off-by: Vadim Lobanov <vlobanov@speakeasy.net>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Dipankar Sarma <dipankar@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-10 17:21:22 +07:00
|
|
|
static void copy_fdtable(struct fdtable *nfdt, struct fdtable *ofdt)
|
2005-09-10 03:04:13 +07:00
|
|
|
{
|
[PATCH] fdtable: Implement new pagesize-based fdtable allocator
This patch provides an improved fdtable allocation scheme, useful for
expanding fdtable file descriptor entries. The main focus is on the fdarray,
as its memory usage grows 128 times faster than that of an fdset.
The allocation algorithm sizes the fdarray in such a way that its memory usage
increases in easy page-sized chunks. The overall algorithm expands the allowed
size in powers of two, in order to amortize the cost of invoking vmalloc() for
larger allocation sizes. Namely, the following sizes for the fdarray are
considered, and the smallest that accommodates the requested fd count is
chosen:
pagesize / 4
pagesize / 2
pagesize <- memory allocator switch point
pagesize * 2
pagesize * 4
...etc...
Unlike the current implementation, this allocation scheme does not require a
loop to compute the optimal fdarray size, and can be done in efficient
straightline code.
Furthermore, since the fdarray overflows the pagesize boundary long before any
of the fdsets do, it makes sense to optimize run-time by allocating both
fdsets in a single swoop. Even together, they will still be, by far, smaller
than the fdarray. The fdtable->open_fds is now used as the anchor for the
fdset memory allocation.
Signed-off-by: Vadim Lobanov <vlobanov@speakeasy.net>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Dipankar Sarma <dipankar@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-10 17:21:22 +07:00
|
|
|
unsigned int cpy, set;
|
2005-09-10 03:04:13 +07:00
|
|
|
|
[PATCH] fdtable: Implement new pagesize-based fdtable allocator
This patch provides an improved fdtable allocation scheme, useful for
expanding fdtable file descriptor entries. The main focus is on the fdarray,
as its memory usage grows 128 times faster than that of an fdset.
The allocation algorithm sizes the fdarray in such a way that its memory usage
increases in easy page-sized chunks. The overall algorithm expands the allowed
size in powers of two, in order to amortize the cost of invoking vmalloc() for
larger allocation sizes. Namely, the following sizes for the fdarray are
considered, and the smallest that accommodates the requested fd count is
chosen:
pagesize / 4
pagesize / 2
pagesize <- memory allocator switch point
pagesize * 2
pagesize * 4
...etc...
Unlike the current implementation, this allocation scheme does not require a
loop to compute the optimal fdarray size, and can be done in efficient
straightline code.
Furthermore, since the fdarray overflows the pagesize boundary long before any
of the fdsets do, it makes sense to optimize run-time by allocating both
fdsets in a single swoop. Even together, they will still be, by far, smaller
than the fdarray. The fdtable->open_fds is now used as the anchor for the
fdset memory allocation.
Signed-off-by: Vadim Lobanov <vlobanov@speakeasy.net>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Dipankar Sarma <dipankar@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-10 17:21:22 +07:00
|
|
|
BUG_ON(nfdt->max_fds < ofdt->max_fds);
|
|
|
|
|
|
|
|
cpy = ofdt->max_fds * sizeof(struct file *);
|
|
|
|
set = (nfdt->max_fds - ofdt->max_fds) * sizeof(struct file *);
|
|
|
|
memcpy(nfdt->fd, ofdt->fd, cpy);
|
|
|
|
memset((char *)(nfdt->fd) + cpy, 0, set);
|
|
|
|
|
|
|
|
cpy = ofdt->max_fds / BITS_PER_BYTE;
|
|
|
|
set = (nfdt->max_fds - ofdt->max_fds) / BITS_PER_BYTE;
|
|
|
|
memcpy(nfdt->open_fds, ofdt->open_fds, cpy);
|
|
|
|
memset((char *)(nfdt->open_fds) + cpy, 0, set);
|
|
|
|
memcpy(nfdt->close_on_exec, ofdt->close_on_exec, cpy);
|
|
|
|
memset((char *)(nfdt->close_on_exec) + cpy, 0, set);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
[PATCH] fdtable: Implement new pagesize-based fdtable allocator
This patch provides an improved fdtable allocation scheme, useful for
expanding fdtable file descriptor entries. The main focus is on the fdarray,
as its memory usage grows 128 times faster than that of an fdset.
The allocation algorithm sizes the fdarray in such a way that its memory usage
increases in easy page-sized chunks. The overall algorithm expands the allowed
size in powers of two, in order to amortize the cost of invoking vmalloc() for
larger allocation sizes. Namely, the following sizes for the fdarray are
considered, and the smallest that accommodates the requested fd count is
chosen:
pagesize / 4
pagesize / 2
pagesize <- memory allocator switch point
pagesize * 2
pagesize * 4
...etc...
Unlike the current implementation, this allocation scheme does not require a
loop to compute the optimal fdarray size, and can be done in efficient
straightline code.
Furthermore, since the fdarray overflows the pagesize boundary long before any
of the fdsets do, it makes sense to optimize run-time by allocating both
fdsets in a single swoop. Even together, they will still be, by far, smaller
than the fdarray. The fdtable->open_fds is now used as the anchor for the
fdset memory allocation.
Signed-off-by: Vadim Lobanov <vlobanov@speakeasy.net>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Dipankar Sarma <dipankar@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-10 17:21:22 +07:00
|
|
|
static struct fdtable * alloc_fdtable(unsigned int nr)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
[PATCH] fdtable: Implement new pagesize-based fdtable allocator
This patch provides an improved fdtable allocation scheme, useful for
expanding fdtable file descriptor entries. The main focus is on the fdarray,
as its memory usage grows 128 times faster than that of an fdset.
The allocation algorithm sizes the fdarray in such a way that its memory usage
increases in easy page-sized chunks. The overall algorithm expands the allowed
size in powers of two, in order to amortize the cost of invoking vmalloc() for
larger allocation sizes. Namely, the following sizes for the fdarray are
considered, and the smallest that accommodates the requested fd count is
chosen:
pagesize / 4
pagesize / 2
pagesize <- memory allocator switch point
pagesize * 2
pagesize * 4
...etc...
Unlike the current implementation, this allocation scheme does not require a
loop to compute the optimal fdarray size, and can be done in efficient
straightline code.
Furthermore, since the fdarray overflows the pagesize boundary long before any
of the fdsets do, it makes sense to optimize run-time by allocating both
fdsets in a single swoop. Even together, they will still be, by far, smaller
than the fdarray. The fdtable->open_fds is now used as the anchor for the
fdset memory allocation.
Signed-off-by: Vadim Lobanov <vlobanov@speakeasy.net>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Dipankar Sarma <dipankar@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-10 17:21:22 +07:00
|
|
|
struct fdtable *fdt;
|
2012-02-17 00:49:54 +07:00
|
|
|
void *data;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-09-10 03:04:13 +07:00
|
|
|
/*
|
[PATCH] fdtable: Implement new pagesize-based fdtable allocator
This patch provides an improved fdtable allocation scheme, useful for
expanding fdtable file descriptor entries. The main focus is on the fdarray,
as its memory usage grows 128 times faster than that of an fdset.
The allocation algorithm sizes the fdarray in such a way that its memory usage
increases in easy page-sized chunks. The overall algorithm expands the allowed
size in powers of two, in order to amortize the cost of invoking vmalloc() for
larger allocation sizes. Namely, the following sizes for the fdarray are
considered, and the smallest that accommodates the requested fd count is
chosen:
pagesize / 4
pagesize / 2
pagesize <- memory allocator switch point
pagesize * 2
pagesize * 4
...etc...
Unlike the current implementation, this allocation scheme does not require a
loop to compute the optimal fdarray size, and can be done in efficient
straightline code.
Furthermore, since the fdarray overflows the pagesize boundary long before any
of the fdsets do, it makes sense to optimize run-time by allocating both
fdsets in a single swoop. Even together, they will still be, by far, smaller
than the fdarray. The fdtable->open_fds is now used as the anchor for the
fdset memory allocation.
Signed-off-by: Vadim Lobanov <vlobanov@speakeasy.net>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Dipankar Sarma <dipankar@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-10 17:21:22 +07:00
|
|
|
* Figure out how many fds we actually want to support in this fdtable.
|
|
|
|
* Allocation steps are keyed to the size of the fdarray, since it
|
|
|
|
* grows far faster than any of the other dynamic data. We try to fit
|
|
|
|
* the fdarray into comfortable page-tuned chunks: starting at 1024B
|
|
|
|
* and growing in powers of two from there on.
|
2005-09-10 03:04:13 +07:00
|
|
|
*/
|
[PATCH] fdtable: Implement new pagesize-based fdtable allocator
This patch provides an improved fdtable allocation scheme, useful for
expanding fdtable file descriptor entries. The main focus is on the fdarray,
as its memory usage grows 128 times faster than that of an fdset.
The allocation algorithm sizes the fdarray in such a way that its memory usage
increases in easy page-sized chunks. The overall algorithm expands the allowed
size in powers of two, in order to amortize the cost of invoking vmalloc() for
larger allocation sizes. Namely, the following sizes for the fdarray are
considered, and the smallest that accommodates the requested fd count is
chosen:
pagesize / 4
pagesize / 2
pagesize <- memory allocator switch point
pagesize * 2
pagesize * 4
...etc...
Unlike the current implementation, this allocation scheme does not require a
loop to compute the optimal fdarray size, and can be done in efficient
straightline code.
Furthermore, since the fdarray overflows the pagesize boundary long before any
of the fdsets do, it makes sense to optimize run-time by allocating both
fdsets in a single swoop. Even together, they will still be, by far, smaller
than the fdarray. The fdtable->open_fds is now used as the anchor for the
fdset memory allocation.
Signed-off-by: Vadim Lobanov <vlobanov@speakeasy.net>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Dipankar Sarma <dipankar@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-10 17:21:22 +07:00
|
|
|
nr /= (1024 / sizeof(struct file *));
|
|
|
|
nr = roundup_pow_of_two(nr + 1);
|
|
|
|
nr *= (1024 / sizeof(struct file *));
|
2008-04-28 07:04:15 +07:00
|
|
|
/*
|
|
|
|
* Note that this can drive nr *below* what we had passed if sysctl_nr_open
|
|
|
|
* had been set lower between the check in expand_files() and here. Deal
|
|
|
|
* with that in caller, it's cheaper that way.
|
|
|
|
*
|
|
|
|
* We make sure that nr remains a multiple of BITS_PER_LONG - otherwise
|
|
|
|
* bitmaps handling below becomes unpleasant, to put it mildly...
|
|
|
|
*/
|
|
|
|
if (unlikely(nr > sysctl_nr_open))
|
|
|
|
nr = ((sysctl_nr_open - 1) | (BITS_PER_LONG - 1)) + 1;
|
2006-12-10 17:21:12 +07:00
|
|
|
|
[PATCH] fdtable: Implement new pagesize-based fdtable allocator
This patch provides an improved fdtable allocation scheme, useful for
expanding fdtable file descriptor entries. The main focus is on the fdarray,
as its memory usage grows 128 times faster than that of an fdset.
The allocation algorithm sizes the fdarray in such a way that its memory usage
increases in easy page-sized chunks. The overall algorithm expands the allowed
size in powers of two, in order to amortize the cost of invoking vmalloc() for
larger allocation sizes. Namely, the following sizes for the fdarray are
considered, and the smallest that accommodates the requested fd count is
chosen:
pagesize / 4
pagesize / 2
pagesize <- memory allocator switch point
pagesize * 2
pagesize * 4
...etc...
Unlike the current implementation, this allocation scheme does not require a
loop to compute the optimal fdarray size, and can be done in efficient
straightline code.
Furthermore, since the fdarray overflows the pagesize boundary long before any
of the fdsets do, it makes sense to optimize run-time by allocating both
fdsets in a single swoop. Even together, they will still be, by far, smaller
than the fdarray. The fdtable->open_fds is now used as the anchor for the
fdset memory allocation.
Signed-off-by: Vadim Lobanov <vlobanov@speakeasy.net>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Dipankar Sarma <dipankar@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-10 17:21:22 +07:00
|
|
|
fdt = kmalloc(sizeof(struct fdtable), GFP_KERNEL);
|
|
|
|
if (!fdt)
|
2006-12-10 17:21:12 +07:00
|
|
|
goto out;
|
[PATCH] fdtable: Implement new pagesize-based fdtable allocator
This patch provides an improved fdtable allocation scheme, useful for
expanding fdtable file descriptor entries. The main focus is on the fdarray,
as its memory usage grows 128 times faster than that of an fdset.
The allocation algorithm sizes the fdarray in such a way that its memory usage
increases in easy page-sized chunks. The overall algorithm expands the allowed
size in powers of two, in order to amortize the cost of invoking vmalloc() for
larger allocation sizes. Namely, the following sizes for the fdarray are
considered, and the smallest that accommodates the requested fd count is
chosen:
pagesize / 4
pagesize / 2
pagesize <- memory allocator switch point
pagesize * 2
pagesize * 4
...etc...
Unlike the current implementation, this allocation scheme does not require a
loop to compute the optimal fdarray size, and can be done in efficient
straightline code.
Furthermore, since the fdarray overflows the pagesize boundary long before any
of the fdsets do, it makes sense to optimize run-time by allocating both
fdsets in a single swoop. Even together, they will still be, by far, smaller
than the fdarray. The fdtable->open_fds is now used as the anchor for the
fdset memory allocation.
Signed-off-by: Vadim Lobanov <vlobanov@speakeasy.net>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Dipankar Sarma <dipankar@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-10 17:21:22 +07:00
|
|
|
fdt->max_fds = nr;
|
|
|
|
data = alloc_fdmem(nr * sizeof(struct file *));
|
|
|
|
if (!data)
|
|
|
|
goto out_fdt;
|
2012-02-17 00:49:54 +07:00
|
|
|
fdt->fd = data;
|
|
|
|
|
|
|
|
data = alloc_fdmem(max_t(size_t,
|
[PATCH] fdtable: Implement new pagesize-based fdtable allocator
This patch provides an improved fdtable allocation scheme, useful for
expanding fdtable file descriptor entries. The main focus is on the fdarray,
as its memory usage grows 128 times faster than that of an fdset.
The allocation algorithm sizes the fdarray in such a way that its memory usage
increases in easy page-sized chunks. The overall algorithm expands the allowed
size in powers of two, in order to amortize the cost of invoking vmalloc() for
larger allocation sizes. Namely, the following sizes for the fdarray are
considered, and the smallest that accommodates the requested fd count is
chosen:
pagesize / 4
pagesize / 2
pagesize <- memory allocator switch point
pagesize * 2
pagesize * 4
...etc...
Unlike the current implementation, this allocation scheme does not require a
loop to compute the optimal fdarray size, and can be done in efficient
straightline code.
Furthermore, since the fdarray overflows the pagesize boundary long before any
of the fdsets do, it makes sense to optimize run-time by allocating both
fdsets in a single swoop. Even together, they will still be, by far, smaller
than the fdarray. The fdtable->open_fds is now used as the anchor for the
fdset memory allocation.
Signed-off-by: Vadim Lobanov <vlobanov@speakeasy.net>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Dipankar Sarma <dipankar@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-10 17:21:22 +07:00
|
|
|
2 * nr / BITS_PER_BYTE, L1_CACHE_BYTES));
|
|
|
|
if (!data)
|
|
|
|
goto out_arr;
|
2012-02-17 00:49:54 +07:00
|
|
|
fdt->open_fds = data;
|
[PATCH] fdtable: Implement new pagesize-based fdtable allocator
This patch provides an improved fdtable allocation scheme, useful for
expanding fdtable file descriptor entries. The main focus is on the fdarray,
as its memory usage grows 128 times faster than that of an fdset.
The allocation algorithm sizes the fdarray in such a way that its memory usage
increases in easy page-sized chunks. The overall algorithm expands the allowed
size in powers of two, in order to amortize the cost of invoking vmalloc() for
larger allocation sizes. Namely, the following sizes for the fdarray are
considered, and the smallest that accommodates the requested fd count is
chosen:
pagesize / 4
pagesize / 2
pagesize <- memory allocator switch point
pagesize * 2
pagesize * 4
...etc...
Unlike the current implementation, this allocation scheme does not require a
loop to compute the optimal fdarray size, and can be done in efficient
straightline code.
Furthermore, since the fdarray overflows the pagesize boundary long before any
of the fdsets do, it makes sense to optimize run-time by allocating both
fdsets in a single swoop. Even together, they will still be, by far, smaller
than the fdarray. The fdtable->open_fds is now used as the anchor for the
fdset memory allocation.
Signed-off-by: Vadim Lobanov <vlobanov@speakeasy.net>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Dipankar Sarma <dipankar@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-10 17:21:22 +07:00
|
|
|
data += nr / BITS_PER_BYTE;
|
2012-02-17 00:49:54 +07:00
|
|
|
fdt->close_on_exec = data;
|
[PATCH] fdtable: Implement new pagesize-based fdtable allocator
This patch provides an improved fdtable allocation scheme, useful for
expanding fdtable file descriptor entries. The main focus is on the fdarray,
as its memory usage grows 128 times faster than that of an fdset.
The allocation algorithm sizes the fdarray in such a way that its memory usage
increases in easy page-sized chunks. The overall algorithm expands the allowed
size in powers of two, in order to amortize the cost of invoking vmalloc() for
larger allocation sizes. Namely, the following sizes for the fdarray are
considered, and the smallest that accommodates the requested fd count is
chosen:
pagesize / 4
pagesize / 2
pagesize <- memory allocator switch point
pagesize * 2
pagesize * 4
...etc...
Unlike the current implementation, this allocation scheme does not require a
loop to compute the optimal fdarray size, and can be done in efficient
straightline code.
Furthermore, since the fdarray overflows the pagesize boundary long before any
of the fdsets do, it makes sense to optimize run-time by allocating both
fdsets in a single swoop. Even together, they will still be, by far, smaller
than the fdarray. The fdtable->open_fds is now used as the anchor for the
fdset memory allocation.
Signed-off-by: Vadim Lobanov <vlobanov@speakeasy.net>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Dipankar Sarma <dipankar@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-10 17:21:22 +07:00
|
|
|
fdt->next = NULL;
|
|
|
|
|
2005-09-10 03:04:13 +07:00
|
|
|
return fdt;
|
[PATCH] fdtable: Implement new pagesize-based fdtable allocator
This patch provides an improved fdtable allocation scheme, useful for
expanding fdtable file descriptor entries. The main focus is on the fdarray,
as its memory usage grows 128 times faster than that of an fdset.
The allocation algorithm sizes the fdarray in such a way that its memory usage
increases in easy page-sized chunks. The overall algorithm expands the allowed
size in powers of two, in order to amortize the cost of invoking vmalloc() for
larger allocation sizes. Namely, the following sizes for the fdarray are
considered, and the smallest that accommodates the requested fd count is
chosen:
pagesize / 4
pagesize / 2
pagesize <- memory allocator switch point
pagesize * 2
pagesize * 4
...etc...
Unlike the current implementation, this allocation scheme does not require a
loop to compute the optimal fdarray size, and can be done in efficient
straightline code.
Furthermore, since the fdarray overflows the pagesize boundary long before any
of the fdsets do, it makes sense to optimize run-time by allocating both
fdsets in a single swoop. Even together, they will still be, by far, smaller
than the fdarray. The fdtable->open_fds is now used as the anchor for the
fdset memory allocation.
Signed-off-by: Vadim Lobanov <vlobanov@speakeasy.net>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Dipankar Sarma <dipankar@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-10 17:21:22 +07:00
|
|
|
|
|
|
|
out_arr:
|
2010-08-11 08:01:35 +07:00
|
|
|
free_fdmem(fdt->fd);
|
[PATCH] fdtable: Implement new pagesize-based fdtable allocator
This patch provides an improved fdtable allocation scheme, useful for
expanding fdtable file descriptor entries. The main focus is on the fdarray,
as its memory usage grows 128 times faster than that of an fdset.
The allocation algorithm sizes the fdarray in such a way that its memory usage
increases in easy page-sized chunks. The overall algorithm expands the allowed
size in powers of two, in order to amortize the cost of invoking vmalloc() for
larger allocation sizes. Namely, the following sizes for the fdarray are
considered, and the smallest that accommodates the requested fd count is
chosen:
pagesize / 4
pagesize / 2
pagesize <- memory allocator switch point
pagesize * 2
pagesize * 4
...etc...
Unlike the current implementation, this allocation scheme does not require a
loop to compute the optimal fdarray size, and can be done in efficient
straightline code.
Furthermore, since the fdarray overflows the pagesize boundary long before any
of the fdsets do, it makes sense to optimize run-time by allocating both
fdsets in a single swoop. Even together, they will still be, by far, smaller
than the fdarray. The fdtable->open_fds is now used as the anchor for the
fdset memory allocation.
Signed-off-by: Vadim Lobanov <vlobanov@speakeasy.net>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Dipankar Sarma <dipankar@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-10 17:21:22 +07:00
|
|
|
out_fdt:
|
2005-09-10 03:04:13 +07:00
|
|
|
kfree(fdt);
|
[PATCH] fdtable: Implement new pagesize-based fdtable allocator
This patch provides an improved fdtable allocation scheme, useful for
expanding fdtable file descriptor entries. The main focus is on the fdarray,
as its memory usage grows 128 times faster than that of an fdset.
The allocation algorithm sizes the fdarray in such a way that its memory usage
increases in easy page-sized chunks. The overall algorithm expands the allowed
size in powers of two, in order to amortize the cost of invoking vmalloc() for
larger allocation sizes. Namely, the following sizes for the fdarray are
considered, and the smallest that accommodates the requested fd count is
chosen:
pagesize / 4
pagesize / 2
pagesize <- memory allocator switch point
pagesize * 2
pagesize * 4
...etc...
Unlike the current implementation, this allocation scheme does not require a
loop to compute the optimal fdarray size, and can be done in efficient
straightline code.
Furthermore, since the fdarray overflows the pagesize boundary long before any
of the fdsets do, it makes sense to optimize run-time by allocating both
fdsets in a single swoop. Even together, they will still be, by far, smaller
than the fdarray. The fdtable->open_fds is now used as the anchor for the
fdset memory allocation.
Signed-off-by: Vadim Lobanov <vlobanov@speakeasy.net>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Dipankar Sarma <dipankar@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-10 17:21:22 +07:00
|
|
|
out:
|
2005-09-10 03:04:13 +07:00
|
|
|
return NULL;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-09-10 03:04:13 +07:00
|
|
|
/*
|
2006-09-29 16:01:43 +07:00
|
|
|
* Expand the file descriptor table.
|
|
|
|
* This function will allocate a new fdtable and both fd array and fdset, of
|
|
|
|
* the given size.
|
|
|
|
* Return <0 error code on error; 1 on successful completion.
|
|
|
|
* The files->file_lock should be held on entry, and will be held on exit.
|
2005-09-10 03:04:13 +07:00
|
|
|
*/
|
|
|
|
static int expand_fdtable(struct files_struct *files, int nr)
|
|
|
|
__releases(files->file_lock)
|
|
|
|
__acquires(files->file_lock)
|
|
|
|
{
|
2006-09-29 16:01:43 +07:00
|
|
|
struct fdtable *new_fdt, *cur_fdt;
|
2005-09-10 03:04:13 +07:00
|
|
|
|
|
|
|
spin_unlock(&files->file_lock);
|
2006-09-29 16:01:43 +07:00
|
|
|
new_fdt = alloc_fdtable(nr);
|
2005-09-10 03:04:13 +07:00
|
|
|
spin_lock(&files->file_lock);
|
2006-09-29 16:01:43 +07:00
|
|
|
if (!new_fdt)
|
|
|
|
return -ENOMEM;
|
2008-04-28 07:04:15 +07:00
|
|
|
/*
|
|
|
|
* extremely unlikely race - sysctl_nr_open decreased between the check in
|
|
|
|
* caller and alloc_fdtable(). Cheaper to catch it here...
|
|
|
|
*/
|
|
|
|
if (unlikely(new_fdt->max_fds <= nr)) {
|
2010-08-11 08:01:35 +07:00
|
|
|
__free_fdtable(new_fdt);
|
2008-04-28 07:04:15 +07:00
|
|
|
return -EMFILE;
|
|
|
|
}
|
2005-09-10 03:04:13 +07:00
|
|
|
/*
|
2006-09-29 16:01:43 +07:00
|
|
|
* Check again since another task may have expanded the fd table while
|
|
|
|
* we dropped the lock
|
2005-09-10 03:04:13 +07:00
|
|
|
*/
|
2006-09-29 16:01:43 +07:00
|
|
|
cur_fdt = files_fdtable(files);
|
2006-12-10 17:21:12 +07:00
|
|
|
if (nr >= cur_fdt->max_fds) {
|
2006-09-29 16:01:43 +07:00
|
|
|
/* Continue as planned */
|
|
|
|
copy_fdtable(new_fdt, cur_fdt);
|
|
|
|
rcu_assign_pointer(files->fdt, new_fdt);
|
2006-12-10 17:21:17 +07:00
|
|
|
if (cur_fdt->max_fds > NR_OPEN_DEFAULT)
|
2006-12-22 16:10:43 +07:00
|
|
|
free_fdtable(cur_fdt);
|
2005-09-10 03:04:13 +07:00
|
|
|
} else {
|
2006-09-29 16:01:43 +07:00
|
|
|
/* Somebody else expanded, so undo our attempt */
|
2010-08-11 08:01:35 +07:00
|
|
|
__free_fdtable(new_fdt);
|
2005-09-10 03:04:13 +07:00
|
|
|
}
|
2006-09-29 16:01:43 +07:00
|
|
|
return 1;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Expand files.
|
2006-09-29 16:01:43 +07:00
|
|
|
* This function will expand the file structures, if the requested size exceeds
|
|
|
|
* the current capacity and there is room for expansion.
|
|
|
|
* Return <0 error code on error; 0 when nothing done; 1 when files were
|
|
|
|
* expanded and execution may have blocked.
|
|
|
|
* The files->file_lock should be held on entry, and will be held on exit.
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
|
|
|
int expand_files(struct files_struct *files, int nr)
|
|
|
|
{
|
2005-09-10 03:04:10 +07:00
|
|
|
struct fdtable *fdt;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-09-10 03:04:10 +07:00
|
|
|
fdt = files_fdtable(files);
|
2008-07-27 03:01:20 +07:00
|
|
|
|
2006-09-29 16:01:43 +07:00
|
|
|
/* Do we need to expand? */
|
2006-12-10 17:21:12 +07:00
|
|
|
if (nr < fdt->max_fds)
|
2006-09-29 16:01:43 +07:00
|
|
|
return 0;
|
2008-07-27 03:01:20 +07:00
|
|
|
|
2006-09-29 16:01:43 +07:00
|
|
|
/* Can we expand? */
|
2008-02-06 16:37:16 +07:00
|
|
|
if (nr >= sysctl_nr_open)
|
2006-09-29 16:01:43 +07:00
|
|
|
return -EMFILE;
|
|
|
|
|
|
|
|
/* All good, so we try */
|
|
|
|
return expand_fdtable(files, nr);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2005-09-10 03:04:13 +07:00
|
|
|
|
2008-05-09 06:42:56 +07:00
|
|
|
static int count_open_files(struct fdtable *fdt)
|
|
|
|
{
|
|
|
|
int size = fdt->max_fds;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Find the last open fd */
|
2012-02-17 00:49:54 +07:00
|
|
|
for (i = size / BITS_PER_LONG; i > 0; ) {
|
|
|
|
if (fdt->open_fds[--i])
|
2008-05-09 06:42:56 +07:00
|
|
|
break;
|
|
|
|
}
|
2012-02-17 00:49:54 +07:00
|
|
|
i = (i + 1) * BITS_PER_LONG;
|
2008-05-09 06:42:56 +07:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate a new files structure and copy contents from the
|
|
|
|
* passed in files structure.
|
|
|
|
* errorp will be valid only when the returned files_struct is NULL.
|
|
|
|
*/
|
|
|
|
struct files_struct *dup_fd(struct files_struct *oldf, int *errorp)
|
|
|
|
{
|
|
|
|
struct files_struct *newf;
|
|
|
|
struct file **old_fds, **new_fds;
|
|
|
|
int open_files, size, i;
|
|
|
|
struct fdtable *old_fdt, *new_fdt;
|
|
|
|
|
|
|
|
*errorp = -ENOMEM;
|
2008-05-09 08:11:17 +07:00
|
|
|
newf = kmem_cache_alloc(files_cachep, GFP_KERNEL);
|
2008-05-09 06:42:56 +07:00
|
|
|
if (!newf)
|
|
|
|
goto out;
|
|
|
|
|
2008-05-09 08:11:17 +07:00
|
|
|
atomic_set(&newf->count, 1);
|
|
|
|
|
|
|
|
spin_lock_init(&newf->file_lock);
|
|
|
|
newf->next_fd = 0;
|
|
|
|
new_fdt = &newf->fdtab;
|
|
|
|
new_fdt->max_fds = NR_OPEN_DEFAULT;
|
2012-02-17 00:49:54 +07:00
|
|
|
new_fdt->close_on_exec = newf->close_on_exec_init;
|
|
|
|
new_fdt->open_fds = newf->open_fds_init;
|
2008-05-09 08:11:17 +07:00
|
|
|
new_fdt->fd = &newf->fd_array[0];
|
|
|
|
new_fdt->next = NULL;
|
|
|
|
|
2008-05-09 06:42:56 +07:00
|
|
|
spin_lock(&oldf->file_lock);
|
|
|
|
old_fdt = files_fdtable(oldf);
|
|
|
|
open_files = count_open_files(old_fdt);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check whether we need to allocate a larger fd array and fd set.
|
|
|
|
*/
|
2008-05-09 08:19:42 +07:00
|
|
|
while (unlikely(open_files > new_fdt->max_fds)) {
|
2008-05-09 06:42:56 +07:00
|
|
|
spin_unlock(&oldf->file_lock);
|
2008-05-09 08:02:45 +07:00
|
|
|
|
2010-08-11 08:01:35 +07:00
|
|
|
if (new_fdt != &newf->fdtab)
|
|
|
|
__free_fdtable(new_fdt);
|
2008-05-09 08:19:42 +07:00
|
|
|
|
2008-05-09 08:02:45 +07:00
|
|
|
new_fdt = alloc_fdtable(open_files - 1);
|
|
|
|
if (!new_fdt) {
|
|
|
|
*errorp = -ENOMEM;
|
|
|
|
goto out_release;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* beyond sysctl_nr_open; nothing to do */
|
|
|
|
if (unlikely(new_fdt->max_fds < open_files)) {
|
2010-08-11 08:01:35 +07:00
|
|
|
__free_fdtable(new_fdt);
|
2008-05-09 08:02:45 +07:00
|
|
|
*errorp = -EMFILE;
|
2008-05-09 06:42:56 +07:00
|
|
|
goto out_release;
|
2008-05-09 08:02:45 +07:00
|
|
|
}
|
|
|
|
|
2008-05-09 06:42:56 +07:00
|
|
|
/*
|
|
|
|
* Reacquire the oldf lock and a pointer to its fd table
|
|
|
|
* who knows it may have a new bigger fd table. We need
|
|
|
|
* the latest pointer.
|
|
|
|
*/
|
|
|
|
spin_lock(&oldf->file_lock);
|
|
|
|
old_fdt = files_fdtable(oldf);
|
2008-05-09 08:19:42 +07:00
|
|
|
open_files = count_open_files(old_fdt);
|
2008-05-09 06:42:56 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
old_fds = old_fdt->fd;
|
|
|
|
new_fds = new_fdt->fd;
|
|
|
|
|
2012-02-17 00:49:54 +07:00
|
|
|
memcpy(new_fdt->open_fds, old_fdt->open_fds, open_files / 8);
|
|
|
|
memcpy(new_fdt->close_on_exec, old_fdt->close_on_exec, open_files / 8);
|
2008-05-09 06:42:56 +07:00
|
|
|
|
|
|
|
for (i = open_files; i != 0; i--) {
|
|
|
|
struct file *f = *old_fds++;
|
|
|
|
if (f) {
|
|
|
|
get_file(f);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* The fd may be claimed in the fd bitmap but not yet
|
|
|
|
* instantiated in the files array if a sibling thread
|
|
|
|
* is partway through open(). So make sure that this
|
|
|
|
* fd is available to the new process.
|
|
|
|
*/
|
2012-02-17 00:49:42 +07:00
|
|
|
__clear_open_fd(open_files - i, new_fdt);
|
2008-05-09 06:42:56 +07:00
|
|
|
}
|
|
|
|
rcu_assign_pointer(*new_fds++, f);
|
|
|
|
}
|
|
|
|
spin_unlock(&oldf->file_lock);
|
|
|
|
|
|
|
|
/* compute the remainder to be cleared */
|
|
|
|
size = (new_fdt->max_fds - open_files) * sizeof(struct file *);
|
|
|
|
|
|
|
|
/* This is long word aligned thus could use a optimized version */
|
|
|
|
memset(new_fds, 0, size);
|
|
|
|
|
|
|
|
if (new_fdt->max_fds > open_files) {
|
2012-02-17 00:49:54 +07:00
|
|
|
int left = (new_fdt->max_fds - open_files) / 8;
|
|
|
|
int start = open_files / BITS_PER_LONG;
|
2008-05-09 06:42:56 +07:00
|
|
|
|
2012-02-17 00:49:54 +07:00
|
|
|
memset(&new_fdt->open_fds[start], 0, left);
|
|
|
|
memset(&new_fdt->close_on_exec[start], 0, left);
|
2008-05-09 06:42:56 +07:00
|
|
|
}
|
|
|
|
|
2008-05-09 08:11:17 +07:00
|
|
|
rcu_assign_pointer(newf->fdt, new_fdt);
|
|
|
|
|
2008-05-09 06:42:56 +07:00
|
|
|
return newf;
|
|
|
|
|
|
|
|
out_release:
|
|
|
|
kmem_cache_free(files_cachep, newf);
|
|
|
|
out:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-08-16 06:56:12 +07:00
|
|
|
static void close_files(struct files_struct * files)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
struct fdtable *fdt;
|
|
|
|
|
|
|
|
j = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It is safe to dereference the fd table without RCU or
|
|
|
|
* ->file_lock because this is the last reference to the
|
|
|
|
* files structure. But use RCU to shut RCU-lockdep up.
|
|
|
|
*/
|
|
|
|
rcu_read_lock();
|
|
|
|
fdt = files_fdtable(files);
|
|
|
|
rcu_read_unlock();
|
|
|
|
for (;;) {
|
|
|
|
unsigned long set;
|
|
|
|
i = j * BITS_PER_LONG;
|
|
|
|
if (i >= fdt->max_fds)
|
|
|
|
break;
|
|
|
|
set = fdt->open_fds[j++];
|
|
|
|
while (set) {
|
|
|
|
if (set & 1) {
|
|
|
|
struct file * file = xchg(&fdt->fd[i], NULL);
|
|
|
|
if (file) {
|
|
|
|
filp_close(file, files);
|
|
|
|
cond_resched();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
set >>= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct files_struct *get_files_struct(struct task_struct *task)
|
|
|
|
{
|
|
|
|
struct files_struct *files;
|
|
|
|
|
|
|
|
task_lock(task);
|
|
|
|
files = task->files;
|
|
|
|
if (files)
|
|
|
|
atomic_inc(&files->count);
|
|
|
|
task_unlock(task);
|
|
|
|
|
|
|
|
return files;
|
|
|
|
}
|
|
|
|
|
|
|
|
void put_files_struct(struct files_struct *files)
|
|
|
|
{
|
|
|
|
struct fdtable *fdt;
|
|
|
|
|
|
|
|
if (atomic_dec_and_test(&files->count)) {
|
|
|
|
close_files(files);
|
|
|
|
/*
|
|
|
|
* Free the fd and fdset arrays if we expanded them.
|
|
|
|
* If the fdtable was embedded, pass files for freeing
|
|
|
|
* at the end of the RCU grace period. Otherwise,
|
|
|
|
* you can free files immediately.
|
|
|
|
*/
|
|
|
|
rcu_read_lock();
|
|
|
|
fdt = files_fdtable(files);
|
|
|
|
if (fdt != &files->fdtab)
|
|
|
|
kmem_cache_free(files_cachep, files);
|
|
|
|
free_fdtable(fdt);
|
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void reset_files_struct(struct files_struct *files)
|
|
|
|
{
|
|
|
|
struct task_struct *tsk = current;
|
|
|
|
struct files_struct *old;
|
|
|
|
|
|
|
|
old = tsk->files;
|
|
|
|
task_lock(tsk);
|
|
|
|
tsk->files = files;
|
|
|
|
task_unlock(tsk);
|
|
|
|
put_files_struct(old);
|
|
|
|
}
|
|
|
|
|
|
|
|
void exit_files(struct task_struct *tsk)
|
|
|
|
{
|
|
|
|
struct files_struct * files = tsk->files;
|
|
|
|
|
|
|
|
if (files) {
|
|
|
|
task_lock(tsk);
|
|
|
|
tsk->files = NULL;
|
|
|
|
task_unlock(tsk);
|
|
|
|
put_files_struct(files);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-09-10 03:04:13 +07:00
|
|
|
static void __devinit fdtable_defer_list_init(int cpu)
|
|
|
|
{
|
|
|
|
struct fdtable_defer *fddef = &per_cpu(fdtable_defer_list, cpu);
|
|
|
|
spin_lock_init(&fddef->lock);
|
2006-11-22 21:55:48 +07:00
|
|
|
INIT_WORK(&fddef->wq, free_fdtable_work);
|
2005-09-10 03:04:13 +07:00
|
|
|
fddef->next = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void __init files_defer_init(void)
|
|
|
|
{
|
|
|
|
int i;
|
2006-03-28 16:56:37 +07:00
|
|
|
for_each_possible_cpu(i)
|
2005-09-10 03:04:13 +07:00
|
|
|
fdtable_defer_list_init(i);
|
2008-05-10 21:08:32 +07:00
|
|
|
sysctl_nr_open_max = min((size_t)INT_MAX, ~(size_t)0/sizeof(void *)) &
|
|
|
|
-BITS_PER_LONG;
|
2005-09-10 03:04:13 +07:00
|
|
|
}
|
2008-05-09 05:19:16 +07:00
|
|
|
|
|
|
|
struct files_struct init_files = {
|
|
|
|
.count = ATOMIC_INIT(1),
|
|
|
|
.fdt = &init_files.fdtab,
|
|
|
|
.fdtab = {
|
|
|
|
.max_fds = NR_OPEN_DEFAULT,
|
|
|
|
.fd = &init_files.fd_array[0],
|
2012-02-17 00:49:54 +07:00
|
|
|
.close_on_exec = init_files.close_on_exec_init,
|
|
|
|
.open_fds = init_files.open_fds_init,
|
2008-05-09 05:19:16 +07:00
|
|
|
},
|
|
|
|
.file_lock = __SPIN_LOCK_UNLOCKED(init_task.file_lock),
|
|
|
|
};
|
2008-07-30 15:13:04 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* allocate a file descriptor, mark it busy.
|
|
|
|
*/
|
2012-08-13 04:27:30 +07:00
|
|
|
int __alloc_fd(struct files_struct *files,
|
|
|
|
unsigned start, unsigned end, unsigned flags)
|
2008-07-30 15:13:04 +07:00
|
|
|
{
|
|
|
|
unsigned int fd;
|
|
|
|
int error;
|
|
|
|
struct fdtable *fdt;
|
|
|
|
|
|
|
|
spin_lock(&files->file_lock);
|
|
|
|
repeat:
|
|
|
|
fdt = files_fdtable(files);
|
|
|
|
fd = start;
|
|
|
|
if (fd < files->next_fd)
|
|
|
|
fd = files->next_fd;
|
|
|
|
|
|
|
|
if (fd < fdt->max_fds)
|
2012-02-17 00:49:54 +07:00
|
|
|
fd = find_next_zero_bit(fdt->open_fds, fdt->max_fds, fd);
|
2008-07-30 15:13:04 +07:00
|
|
|
|
2012-08-13 03:17:59 +07:00
|
|
|
/*
|
|
|
|
* N.B. For clone tasks sharing a files structure, this test
|
|
|
|
* will limit the total number of files that can be opened.
|
|
|
|
*/
|
|
|
|
error = -EMFILE;
|
|
|
|
if (fd >= end)
|
|
|
|
goto out;
|
|
|
|
|
2008-07-30 15:13:04 +07:00
|
|
|
error = expand_files(files, fd);
|
|
|
|
if (error < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we needed to expand the fs array we
|
|
|
|
* might have blocked - try again.
|
|
|
|
*/
|
|
|
|
if (error)
|
|
|
|
goto repeat;
|
|
|
|
|
|
|
|
if (start <= files->next_fd)
|
|
|
|
files->next_fd = fd + 1;
|
|
|
|
|
2012-02-17 00:49:42 +07:00
|
|
|
__set_open_fd(fd, fdt);
|
2008-07-30 15:13:04 +07:00
|
|
|
if (flags & O_CLOEXEC)
|
2012-02-17 00:49:42 +07:00
|
|
|
__set_close_on_exec(fd, fdt);
|
2008-07-30 15:13:04 +07:00
|
|
|
else
|
2012-02-17 00:49:42 +07:00
|
|
|
__clear_close_on_exec(fd, fdt);
|
2008-07-30 15:13:04 +07:00
|
|
|
error = fd;
|
|
|
|
#if 1
|
|
|
|
/* Sanity check */
|
2010-02-23 08:04:52 +07:00
|
|
|
if (rcu_dereference_raw(fdt->fd[fd]) != NULL) {
|
2008-07-30 15:13:04 +07:00
|
|
|
printk(KERN_WARNING "alloc_fd: slot %d not NULL!\n", fd);
|
|
|
|
rcu_assign_pointer(fdt->fd[fd], NULL);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
out:
|
|
|
|
spin_unlock(&files->file_lock);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2012-08-13 04:27:30 +07:00
|
|
|
int alloc_fd(unsigned start, unsigned flags)
|
|
|
|
{
|
|
|
|
return __alloc_fd(current->files, start, rlimit(RLIMIT_NOFILE), flags);
|
|
|
|
}
|
|
|
|
|
2012-08-13 04:18:05 +07:00
|
|
|
int get_unused_fd_flags(unsigned flags)
|
2008-07-30 15:13:04 +07:00
|
|
|
{
|
2012-08-13 04:27:30 +07:00
|
|
|
return __alloc_fd(current->files, 0, rlimit(RLIMIT_NOFILE), flags);
|
2008-07-30 15:13:04 +07:00
|
|
|
}
|
2012-08-13 04:18:05 +07:00
|
|
|
EXPORT_SYMBOL(get_unused_fd_flags);
|