Skip to content

Commit

Permalink
[PATCH] files: break up files struct
Browse files Browse the repository at this point in the history
In order for the RCU to work, the file table array, sets and their sizes must
be updated atomically.  Instead of ensuring this through too many memory
barriers, we put the arrays and their sizes in a separate structure.  This
patch takes the first step of putting the file table elements in a separate
structure fdtable that is embedded withing files_struct.  It also changes all
the users to refer to the file table using files_fdtable() macro.  Subsequent
applciation of RCU becomes easier after this.

Signed-off-by: Dipankar Sarma <[email protected]>
Signed-Off-By: David Howells <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
dipankarsarma authored and Linus Torvalds committed Sep 9, 2005
1 parent c0dfb29 commit badf166
Show file tree
Hide file tree
Showing 17 changed files with 211 additions and 124 deletions.
4 changes: 3 additions & 1 deletion arch/alpha/kernel/osf_sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,7 @@ osf_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp,
size_t size;
long timeout;
int ret = -EINVAL;
struct fdtable *fdt;

timeout = MAX_SCHEDULE_TIMEOUT;
if (tvp) {
Expand All @@ -995,7 +996,8 @@ osf_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp,
}
}

if (n < 0 || n > current->files->max_fdset)
fdt = files_fdtable(current->files);
if (n < 0 || n > fdt->max_fdset)
goto out_nofds;

/*
Expand Down
7 changes: 5 additions & 2 deletions arch/ia64/kernel/perfmon.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include <linux/pagemap.h>
#include <linux/mount.h>
#include <linux/bitops.h>
#include <linux/rcupdate.h>

#include <asm/errno.h>
#include <asm/intrinsics.h>
Expand Down Expand Up @@ -2217,15 +2218,17 @@ static void
pfm_free_fd(int fd, struct file *file)
{
struct files_struct *files = current->files;
struct fdtable *fdt = files_fdtable(files);

/*
* there ie no fd_uninstall(), so we do it here
*/
spin_lock(&files->file_lock);
files->fd[fd] = NULL;
rcu_assign_pointer(fdt->fd[fd], NULL);
spin_unlock(&files->file_lock);

if (file) put_filp(file);
if (file)
put_filp(file);
put_unused_fd(fd);
}

Expand Down
8 changes: 5 additions & 3 deletions arch/sparc64/solaris/ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -293,11 +293,13 @@ static struct module_info {
static inline int solaris_sockmod(unsigned int fd, unsigned int cmd, u32 arg)
{
struct inode *ino;
struct fdtable *fdt;
/* I wonder which of these tests are superfluous... --patrik */
spin_lock(&current->files->file_lock);
if (! current->files->fd[fd] ||
! current->files->fd[fd]->f_dentry ||
! (ino = current->files->fd[fd]->f_dentry->d_inode) ||
fdt = files_fdtable(current->files);
if (! fdt->fd[fd] ||
! fdt->fd[fd]->f_dentry ||
! (ino = fdt->fd[fd]->f_dentry->d_inode) ||
! S_ISSOCK(ino->i_mode)) {
spin_unlock(&current->files->file_lock);
return TBADF;
Expand Down
4 changes: 3 additions & 1 deletion drivers/char/tty_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -2454,6 +2454,7 @@ static void __do_SAK(void *arg)
int i;
struct file *filp;
struct tty_ldisc *disc;
struct fdtable *fdt;

if (!tty)
return;
Expand All @@ -2480,7 +2481,8 @@ static void __do_SAK(void *arg)
task_lock(p);
if (p->files) {
spin_lock(&p->files->file_lock);
for (i=0; i < p->files->max_fds; i++) {
fdt = files_fdtable(p->files);
for (i=0; i < fdt->max_fds; i++) {
filp = fcheck_files(p->files, i);
if (!filp)
continue;
Expand Down
8 changes: 5 additions & 3 deletions fs/exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -798,19 +798,21 @@ static inline int de_thread(struct task_struct *tsk)
static inline void flush_old_files(struct files_struct * files)
{
long j = -1;
struct fdtable *fdt;

spin_lock(&files->file_lock);
for (;;) {
unsigned long set, i;

j++;
i = j * __NFDBITS;
if (i >= files->max_fds || i >= files->max_fdset)
fdt = files_fdtable(files);
if (i >= fdt->max_fds || i >= fdt->max_fdset)
break;
set = files->close_on_exec->fds_bits[j];
set = fdt->close_on_exec->fds_bits[j];
if (!set)
continue;
files->close_on_exec->fds_bits[j] = 0;
fdt->close_on_exec->fds_bits[j] = 0;
spin_unlock(&files->file_lock);
for ( ; set ; i++,set >>= 1) {
if (set & 1) {
Expand Down
47 changes: 29 additions & 18 deletions fs/fcntl.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,24 @@
void fastcall set_close_on_exec(unsigned int fd, int flag)
{
struct files_struct *files = current->files;
struct fdtable *fdt;
spin_lock(&files->file_lock);
fdt = files_fdtable(files);
if (flag)
FD_SET(fd, files->close_on_exec);
FD_SET(fd, fdt->close_on_exec);
else
FD_CLR(fd, files->close_on_exec);
FD_CLR(fd, fdt->close_on_exec);
spin_unlock(&files->file_lock);
}

static inline int get_close_on_exec(unsigned int fd)
{
struct files_struct *files = current->files;
struct fdtable *fdt;
int res;
spin_lock(&files->file_lock);
res = FD_ISSET(fd, files->close_on_exec);
fdt = files_fdtable(files);
res = FD_ISSET(fd, fdt->close_on_exec);
spin_unlock(&files->file_lock);
return res;
}
Expand All @@ -54,24 +58,26 @@ static int locate_fd(struct files_struct *files,
unsigned int newfd;
unsigned int start;
int error;
struct fdtable *fdt;

error = -EINVAL;
if (orig_start >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
goto out;

fdt = files_fdtable(files);
repeat:
/*
* Someone might have closed fd's in the range
* orig_start..files->next_fd
* orig_start..fdt->next_fd
*/
start = orig_start;
if (start < files->next_fd)
start = files->next_fd;
if (start < fdt->next_fd)
start = fdt->next_fd;

newfd = start;
if (start < files->max_fdset) {
newfd = find_next_zero_bit(files->open_fds->fds_bits,
files->max_fdset, start);
if (start < fdt->max_fdset) {
newfd = find_next_zero_bit(fdt->open_fds->fds_bits,
fdt->max_fdset, start);
}

error = -EMFILE;
Expand All @@ -89,8 +95,8 @@ static int locate_fd(struct files_struct *files,
if (error)
goto repeat;

if (start <= files->next_fd)
files->next_fd = newfd + 1;
if (start <= fdt->next_fd)
fdt->next_fd = newfd + 1;

error = newfd;

Expand All @@ -101,13 +107,16 @@ static int locate_fd(struct files_struct *files,
static int dupfd(struct file *file, unsigned int start)
{
struct files_struct * files = current->files;
struct fdtable *fdt;
int fd;

spin_lock(&files->file_lock);
fd = locate_fd(files, file, start);
if (fd >= 0) {
FD_SET(fd, files->open_fds);
FD_CLR(fd, files->close_on_exec);
/* locate_fd() may have expanded fdtable, load the ptr */
fdt = files_fdtable(files);
FD_SET(fd, fdt->open_fds);
FD_CLR(fd, fdt->close_on_exec);
spin_unlock(&files->file_lock);
fd_install(fd, file);
} else {
Expand All @@ -123,6 +132,7 @@ asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd)
int err = -EBADF;
struct file * file, *tofree;
struct files_struct * files = current->files;
struct fdtable *fdt;

spin_lock(&files->file_lock);
if (!(file = fcheck(oldfd)))
Expand All @@ -148,13 +158,14 @@ asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd)

/* Yes. It's a race. In user space. Nothing sane to do */
err = -EBUSY;
tofree = files->fd[newfd];
if (!tofree && FD_ISSET(newfd, files->open_fds))
fdt = files_fdtable(files);
tofree = fdt->fd[newfd];
if (!tofree && FD_ISSET(newfd, fdt->open_fds))
goto out_fput;

files->fd[newfd] = file;
FD_SET(newfd, files->open_fds);
FD_CLR(newfd, files->close_on_exec);
fdt->fd[newfd] = file;
FD_SET(newfd, fdt->open_fds);
FD_CLR(newfd, fdt->close_on_exec);
spin_unlock(&files->file_lock);

if (tofree)
Expand Down
42 changes: 25 additions & 17 deletions fs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,15 @@ static int expand_fd_array(struct files_struct *files, int nr)
{
struct file **new_fds;
int error, nfds;
struct fdtable *fdt;


error = -EMFILE;
if (files->max_fds >= NR_OPEN || nr >= NR_OPEN)
fdt = files_fdtable(files);
if (fdt->max_fds >= NR_OPEN || nr >= NR_OPEN)
goto out;

nfds = files->max_fds;
nfds = fdt->max_fds;
spin_unlock(&files->file_lock);

/*
Expand Down Expand Up @@ -95,13 +97,14 @@ static int expand_fd_array(struct files_struct *files, int nr)
goto out;

/* Copy the existing array and install the new pointer */
fdt = files_fdtable(files);

if (nfds > files->max_fds) {
if (nfds > fdt->max_fds) {
struct file **old_fds;
int i;

old_fds = xchg(&files->fd, new_fds);
i = xchg(&files->max_fds, nfds);
old_fds = xchg(&fdt->fd, new_fds);
i = xchg(&fdt->max_fds, nfds);

/* Don't copy/clear the array if we are creating a new
fd array for fork() */
Expand Down Expand Up @@ -164,12 +167,14 @@ static int expand_fdset(struct files_struct *files, int nr)
{
fd_set *new_openset = NULL, *new_execset = NULL;
int error, nfds = 0;
struct fdtable *fdt;

error = -EMFILE;
if (files->max_fdset >= NR_OPEN || nr >= NR_OPEN)
fdt = files_fdtable(files);
if (fdt->max_fdset >= NR_OPEN || nr >= NR_OPEN)
goto out;

nfds = files->max_fdset;
nfds = fdt->max_fdset;
spin_unlock(&files->file_lock);

/* Expand to the max in easy steps */
Expand All @@ -193,24 +198,25 @@ static int expand_fdset(struct files_struct *files, int nr)
error = 0;

/* Copy the existing tables and install the new pointers */
if (nfds > files->max_fdset) {
int i = files->max_fdset / (sizeof(unsigned long) * 8);
int count = (nfds - files->max_fdset) / 8;
fdt = files_fdtable(files);
if (nfds > fdt->max_fdset) {
int i = fdt->max_fdset / (sizeof(unsigned long) * 8);
int count = (nfds - fdt->max_fdset) / 8;

/*
* Don't copy the entire array if the current fdset is
* not yet initialised.
*/
if (i) {
memcpy (new_openset, files->open_fds, files->max_fdset/8);
memcpy (new_execset, files->close_on_exec, files->max_fdset/8);
memcpy (new_openset, fdt->open_fds, fdt->max_fdset/8);
memcpy (new_execset, fdt->close_on_exec, fdt->max_fdset/8);
memset (&new_openset->fds_bits[i], 0, count);
memset (&new_execset->fds_bits[i], 0, count);
}

nfds = xchg(&files->max_fdset, nfds);
new_openset = xchg(&files->open_fds, new_openset);
new_execset = xchg(&files->close_on_exec, new_execset);
nfds = xchg(&fdt->max_fdset, nfds);
new_openset = xchg(&fdt->open_fds, new_openset);
new_execset = xchg(&fdt->close_on_exec, new_execset);
spin_unlock(&files->file_lock);
free_fdset (new_openset, nfds);
free_fdset (new_execset, nfds);
Expand All @@ -237,13 +243,15 @@ static int expand_fdset(struct files_struct *files, int nr)
int expand_files(struct files_struct *files, int nr)
{
int err, expand = 0;
struct fdtable *fdt;

if (nr >= files->max_fdset) {
fdt = files_fdtable(files);
if (nr >= fdt->max_fdset) {
expand = 1;
if ((err = expand_fdset(files, nr)))
goto out;
}
if (nr >= files->max_fds) {
if (nr >= fdt->max_fds) {
expand = 1;
if ((err = expand_fd_array(files, nr)))
goto out;
Expand Down
8 changes: 5 additions & 3 deletions fs/locks.c
Original file line number Diff line number Diff line change
Expand Up @@ -2198,21 +2198,23 @@ void steal_locks(fl_owner_t from)
{
struct files_struct *files = current->files;
int i, j;
struct fdtable *fdt;

if (from == files)
return;

lock_kernel();
j = 0;
fdt = files_fdtable(files);
for (;;) {
unsigned long set;
i = j * __NFDBITS;
if (i >= files->max_fdset || i >= files->max_fds)
if (i >= fdt->max_fdset || i >= fdt->max_fds)
break;
set = files->open_fds->fds_bits[j++];
set = fdt->open_fds->fds_bits[j++];
while (set) {
if (set & 1) {
struct file *file = files->fd[i];
struct file *file = fdt->fd[i];
if (file)
__steal_locks(file, from);
}
Expand Down
Loading

0 comments on commit badf166

Please sign in to comment.