From 1dce27c5aa6770e9d195f2bb7db1db3d4dde5591 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 16 Feb 2012 17:49:42 +0000 Subject: Wrap accesses to the fd_sets in struct fdtable Wrap accesses to the fd_sets in struct fdtable (for recording open files and close-on-exec flags) so that we can move away from using fd_sets since we abuse the fd_set structs by not allocating the full-sized structure under normal circumstances and by non-core code looking at the internals of the fd_sets. The first abuse means that use of FD_ZERO() on these fd_sets is not permitted, since that cannot be told about their abnormal lengths. This introduces six wrapper functions for setting, clearing and testing close-on-exec flags and fd-is-open flags: void __set_close_on_exec(int fd, struct fdtable *fdt); void __clear_close_on_exec(int fd, struct fdtable *fdt); bool close_on_exec(int fd, const struct fdtable *fdt); void __set_open_fd(int fd, struct fdtable *fdt); void __clear_open_fd(int fd, struct fdtable *fdt); bool fd_is_open(int fd, const struct fdtable *fdt); Note that I've prepended '__' to the names of the set/clear functions because they require the caller to hold a lock to use them. Note also that I haven't added wrappers for looking behind the scenes at the the array. Possibly that should exist too. Signed-off-by: David Howells Link: http://lkml.kernel.org/r/20120216174942.23314.1364.stgit@warthog.procyon.org.uk Signed-off-by: H. Peter Anvin Cc: Al Viro --- fs/autofs4/dev-ioctl.c | 2 +- fs/exec.c | 4 ++-- fs/fcntl.c | 18 +++++++++--------- fs/file.c | 8 ++++---- fs/open.c | 4 ++-- fs/proc/base.c | 2 +- 6 files changed, 19 insertions(+), 19 deletions(-) (limited to 'fs') diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c index 76741d8d7786..3dfd615afb6b 100644 --- a/fs/autofs4/dev-ioctl.c +++ b/fs/autofs4/dev-ioctl.c @@ -230,7 +230,7 @@ static void autofs_dev_ioctl_fd_install(unsigned int fd, struct file *file) fdt = files_fdtable(files); BUG_ON(fdt->fd[fd] != NULL); rcu_assign_pointer(fdt->fd[fd], file); - FD_SET(fd, fdt->close_on_exec); + __set_close_on_exec(fd, fdt); spin_unlock(&files->file_lock); } diff --git a/fs/exec.c b/fs/exec.c index 92ce83a11e90..22cc38d9e79f 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -2078,8 +2078,8 @@ static int umh_pipe_setup(struct subprocess_info *info, struct cred *new) fd_install(0, rp); spin_lock(&cf->file_lock); fdt = files_fdtable(cf); - FD_SET(0, fdt->open_fds); - FD_CLR(0, fdt->close_on_exec); + __set_open_fd(0, fdt); + __clear_close_on_exec(0, fdt); spin_unlock(&cf->file_lock); /* and disallow core files too */ diff --git a/fs/fcntl.c b/fs/fcntl.c index 22764c7c8382..75e7c1f3a080 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -32,20 +32,20 @@ void set_close_on_exec(unsigned int fd, int flag) spin_lock(&files->file_lock); fdt = files_fdtable(files); if (flag) - FD_SET(fd, fdt->close_on_exec); + __set_close_on_exec(fd, fdt); else - FD_CLR(fd, fdt->close_on_exec); + __clear_close_on_exec(fd, fdt); spin_unlock(&files->file_lock); } -static int get_close_on_exec(unsigned int fd) +static bool get_close_on_exec(unsigned int fd) { struct files_struct *files = current->files; struct fdtable *fdt; - int res; + bool res; rcu_read_lock(); fdt = files_fdtable(files); - res = FD_ISSET(fd, fdt->close_on_exec); + res = close_on_exec(fd, fdt); rcu_read_unlock(); return res; } @@ -90,15 +90,15 @@ SYSCALL_DEFINE3(dup3, unsigned int, oldfd, unsigned int, newfd, int, flags) err = -EBUSY; fdt = files_fdtable(files); tofree = fdt->fd[newfd]; - if (!tofree && FD_ISSET(newfd, fdt->open_fds)) + if (!tofree && fd_is_open(newfd, fdt)) goto out_unlock; get_file(file); rcu_assign_pointer(fdt->fd[newfd], file); - FD_SET(newfd, fdt->open_fds); + __set_open_fd(newfd, fdt); if (flags & O_CLOEXEC) - FD_SET(newfd, fdt->close_on_exec); + __set_close_on_exec(newfd, fdt); else - FD_CLR(newfd, fdt->close_on_exec); + __clear_close_on_exec(newfd, fdt); spin_unlock(&files->file_lock); if (tofree) diff --git a/fs/file.c b/fs/file.c index 4c6992d8f3ba..114fea0a2cec 100644 --- a/fs/file.c +++ b/fs/file.c @@ -366,7 +366,7 @@ struct files_struct *dup_fd(struct files_struct *oldf, int *errorp) * is partway through open(). So make sure that this * fd is available to the new process. */ - FD_CLR(open_files - i, new_fdt->open_fds); + __clear_open_fd(open_files - i, new_fdt); } rcu_assign_pointer(*new_fds++, f); } @@ -460,11 +460,11 @@ repeat: if (start <= files->next_fd) files->next_fd = fd + 1; - FD_SET(fd, fdt->open_fds); + __set_open_fd(fd, fdt); if (flags & O_CLOEXEC) - FD_SET(fd, fdt->close_on_exec); + __set_close_on_exec(fd, fdt); else - FD_CLR(fd, fdt->close_on_exec); + __clear_close_on_exec(fd, fdt); error = fd; #if 1 /* Sanity check */ diff --git a/fs/open.c b/fs/open.c index 77becc041149..5720854156db 100644 --- a/fs/open.c +++ b/fs/open.c @@ -836,7 +836,7 @@ EXPORT_SYMBOL(dentry_open); static void __put_unused_fd(struct files_struct *files, unsigned int fd) { struct fdtable *fdt = files_fdtable(files); - __FD_CLR(fd, fdt->open_fds); + __clear_open_fd(fd, fdt); if (fd < files->next_fd) files->next_fd = fd; } @@ -1080,7 +1080,7 @@ SYSCALL_DEFINE1(close, unsigned int, fd) if (!filp) goto out_unlock; rcu_assign_pointer(fdt->fd[fd], NULL); - FD_CLR(fd, fdt->close_on_exec); + __clear_close_on_exec(fd, fdt); __put_unused_fd(files, fd); spin_unlock(&files->file_lock); retval = filp_close(filp, files); diff --git a/fs/proc/base.c b/fs/proc/base.c index d4548dd49b02..db6ab4b36a0b 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1754,7 +1754,7 @@ static int proc_fd_info(struct inode *inode, struct path *path, char *info) fdt = files_fdtable(files); f_flags = file->f_flags & ~O_CLOEXEC; - if (FD_ISSET(fd, fdt->close_on_exec)) + if (close_on_exec(fd, fdt)) f_flags |= O_CLOEXEC; if (path) { -- cgit v1.2.3 From 1fd36adcd98c14d2fd97f545293c488775cb2823 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 16 Feb 2012 17:49:54 +0000 Subject: Replace the fd_sets in struct fdtable with an array of unsigned longs Replace the fd_sets in struct fdtable with an array of unsigned longs and then use the standard non-atomic bit operations rather than the FD_* macros. This: (1) Removes the abuses of struct fd_set: (a) Since we don't want to allocate a full fd_set the vast majority of the time, we actually, in effect, just allocate a just-big-enough array of unsigned longs and cast it to an fd_set type - so why bother with the fd_set at all? (b) Some places outside of the core fdtable handling code (such as SELinux) want to look inside the array of unsigned longs hidden inside the fd_set struct for more efficient iteration over the entire set. (2) Eliminates the use of FD_*() macros in the kernel completely. (3) Permits the __FD_*() macros to be deleted entirely where not exposed to userspace. Signed-off-by: David Howells Link: http://lkml.kernel.org/r/20120216174954.23314.48147.stgit@warthog.procyon.org.uk Signed-off-by: H. Peter Anvin Cc: Al Viro --- fs/exec.c | 4 ++-- fs/file.c | 46 ++++++++++++++++++++++------------------------ fs/select.c | 2 +- 3 files changed, 25 insertions(+), 27 deletions(-) (limited to 'fs') diff --git a/fs/exec.c b/fs/exec.c index 22cc38d9e79f..cfd5e3047bd8 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1026,10 +1026,10 @@ static void flush_old_files(struct files_struct * files) fdt = files_fdtable(files); if (i >= fdt->max_fds) break; - set = fdt->close_on_exec->fds_bits[j]; + set = fdt->close_on_exec[j]; if (!set) continue; - fdt->close_on_exec->fds_bits[j] = 0; + fdt->close_on_exec[j] = 0; spin_unlock(&files->file_lock); for ( ; set ; i++,set >>= 1) { if (set & 1) { diff --git a/fs/file.c b/fs/file.c index 114fea0a2cec..2d479dd8484e 100644 --- a/fs/file.c +++ b/fs/file.c @@ -40,7 +40,7 @@ int sysctl_nr_open_max = 1024 * 1024; /* raised later */ */ static DEFINE_PER_CPU(struct fdtable_defer, fdtable_defer_list); -static void *alloc_fdmem(unsigned int size) +static void *alloc_fdmem(size_t size) { /* * Very large allocations can stress page reclaim, so fall back to @@ -142,7 +142,7 @@ static void copy_fdtable(struct fdtable *nfdt, struct fdtable *ofdt) static struct fdtable * alloc_fdtable(unsigned int nr) { struct fdtable *fdt; - char *data; + void *data; /* * Figure out how many fds we actually want to support in this fdtable. @@ -172,14 +172,15 @@ static struct fdtable * alloc_fdtable(unsigned int nr) data = alloc_fdmem(nr * sizeof(struct file *)); if (!data) goto out_fdt; - fdt->fd = (struct file **)data; - data = alloc_fdmem(max_t(unsigned int, + fdt->fd = data; + + data = alloc_fdmem(max_t(size_t, 2 * nr / BITS_PER_BYTE, L1_CACHE_BYTES)); if (!data) goto out_arr; - fdt->open_fds = (fd_set *)data; - data += nr / BITS_PER_BYTE; - fdt->close_on_exec = (fd_set *)data; + fdt->open_fds = data; + data += nr / BITS_PER_LONG; + fdt->close_on_exec = data; fdt->next = NULL; return fdt; @@ -275,11 +276,11 @@ static int count_open_files(struct fdtable *fdt) int i; /* Find the last open fd */ - for (i = size/(8*sizeof(long)); i > 0; ) { - if (fdt->open_fds->fds_bits[--i]) + for (i = size / BITS_PER_LONG; i > 0; ) { + if (fdt->open_fds[--i]) break; } - i = (i+1) * 8 * sizeof(long); + i = (i + 1) * BITS_PER_LONG; return i; } @@ -306,8 +307,8 @@ struct files_struct *dup_fd(struct files_struct *oldf, int *errorp) newf->next_fd = 0; new_fdt = &newf->fdtab; new_fdt->max_fds = NR_OPEN_DEFAULT; - new_fdt->close_on_exec = (fd_set *)&newf->close_on_exec_init; - new_fdt->open_fds = (fd_set *)&newf->open_fds_init; + new_fdt->close_on_exec = newf->close_on_exec_init; + new_fdt->open_fds = newf->open_fds_init; new_fdt->fd = &newf->fd_array[0]; new_fdt->next = NULL; @@ -350,10 +351,8 @@ struct files_struct *dup_fd(struct files_struct *oldf, int *errorp) old_fds = old_fdt->fd; new_fds = new_fdt->fd; - memcpy(new_fdt->open_fds->fds_bits, - old_fdt->open_fds->fds_bits, open_files/8); - memcpy(new_fdt->close_on_exec->fds_bits, - old_fdt->close_on_exec->fds_bits, open_files/8); + 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); for (i = open_files; i != 0; i--) { struct file *f = *old_fds++; @@ -379,11 +378,11 @@ struct files_struct *dup_fd(struct files_struct *oldf, int *errorp) memset(new_fds, 0, size); if (new_fdt->max_fds > open_files) { - int left = (new_fdt->max_fds-open_files)/8; - int start = open_files / (8 * sizeof(unsigned long)); + int left = (new_fdt->max_fds - open_files) / 8; + int start = open_files / BITS_PER_LONG; - memset(&new_fdt->open_fds->fds_bits[start], 0, left); - memset(&new_fdt->close_on_exec->fds_bits[start], 0, left); + memset(&new_fdt->open_fds[start], 0, left); + memset(&new_fdt->close_on_exec[start], 0, left); } rcu_assign_pointer(newf->fdt, new_fdt); @@ -419,8 +418,8 @@ struct files_struct init_files = { .fdtab = { .max_fds = NR_OPEN_DEFAULT, .fd = &init_files.fd_array[0], - .close_on_exec = (fd_set *)&init_files.close_on_exec_init, - .open_fds = (fd_set *)&init_files.open_fds_init, + .close_on_exec = init_files.close_on_exec_init, + .open_fds = init_files.open_fds_init, }, .file_lock = __SPIN_LOCK_UNLOCKED(init_task.file_lock), }; @@ -443,8 +442,7 @@ repeat: fd = files->next_fd; if (fd < fdt->max_fds) - fd = find_next_zero_bit(fdt->open_fds->fds_bits, - fdt->max_fds, fd); + fd = find_next_zero_bit(fdt->open_fds, fdt->max_fds, fd); error = expand_files(files, fd); if (error < 0) diff --git a/fs/select.c b/fs/select.c index d33418fdc858..2e7fbe8a092c 100644 --- a/fs/select.c +++ b/fs/select.c @@ -348,7 +348,7 @@ static int max_select_fd(unsigned long n, fd_set_bits *fds) set = ~(~0UL << (n & (__NFDBITS-1))); n /= __NFDBITS; fdt = files_fdtable(current->files); - open_fds = fdt->open_fds->fds_bits+n; + open_fds = fdt->open_fds + n; max = 0; if (set) { set &= BITS(fds, n); -- cgit v1.2.3 From 4ee5c0d05ce9b4e48e586a1ee168f166d191ddda Mon Sep 17 00:00:00 2001 From: "H. J. Lu" Date: Tue, 14 Feb 2012 13:30:41 -0800 Subject: compat: Create compat_sys_p{read,write}v64 For 32-bit ABIs which have real 64-bit registers, we don't want to break the position argument into two. However, we still need compat support to deal with 32-bit pointers, so we can't just use sys_p{read,write} directly. Signed-off-by: H. Peter Anvin --- fs/compat.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/compat.c b/fs/compat.c index fa9d721ecfee..83d751c78885 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -1177,10 +1177,9 @@ compat_sys_readv(unsigned long fd, const struct compat_iovec __user *vec, } asmlinkage ssize_t -compat_sys_preadv(unsigned long fd, const struct compat_iovec __user *vec, - unsigned long vlen, u32 pos_low, u32 pos_high) +compat_sys_preadv64(unsigned long fd, const struct compat_iovec __user *vec, + unsigned long vlen, loff_t pos) { - loff_t pos = ((loff_t)pos_high << 32) | pos_low; struct file *file; int fput_needed; ssize_t ret; @@ -1197,6 +1196,14 @@ compat_sys_preadv(unsigned long fd, const struct compat_iovec __user *vec, return ret; } +asmlinkage ssize_t +compat_sys_preadv(unsigned long fd, const struct compat_iovec __user *vec, + unsigned long vlen, u32 pos_low, u32 pos_high) +{ + loff_t pos = ((loff_t)pos_high << 32) | pos_low; + return compat_sys_preadv64(fd, vec, vlen, pos); +} + static size_t compat_writev(struct file *file, const struct compat_iovec __user *vec, unsigned long vlen, loff_t *pos) @@ -1236,10 +1243,9 @@ compat_sys_writev(unsigned long fd, const struct compat_iovec __user *vec, } asmlinkage ssize_t -compat_sys_pwritev(unsigned long fd, const struct compat_iovec __user *vec, - unsigned long vlen, u32 pos_low, u32 pos_high) +compat_sys_pwritev64(unsigned long fd, const struct compat_iovec __user *vec, + unsigned long vlen, loff_t pos) { - loff_t pos = ((loff_t)pos_high << 32) | pos_low; struct file *file; int fput_needed; ssize_t ret; @@ -1256,6 +1262,14 @@ compat_sys_pwritev(unsigned long fd, const struct compat_iovec __user *vec, return ret; } +asmlinkage ssize_t +compat_sys_pwritev(unsigned long fd, const struct compat_iovec __user *vec, + unsigned long vlen, u32 pos_low, u32 pos_high) +{ + loff_t pos = ((loff_t)pos_high << 32) | pos_low; + return compat_sys_pwritev64(fd, vec, vlen, pos); +} + asmlinkage long compat_sys_vmsplice(int fd, const struct compat_iovec __user *iov32, unsigned int nr_segs, unsigned int flags) -- cgit v1.2.3 From 0953f65d5db728df0fdc3d510a71fd811a3be758 Mon Sep 17 00:00:00 2001 From: "H. J. Lu" Date: Tue, 14 Feb 2012 13:34:52 -0800 Subject: elf: Allow core dump-related fields to be overridden Allow some core dump-related fields to be overridden. This allows core dumps to work correctly for x32. Signed-off-by: H. Peter Anvin Cc: Alexander Viro Cc: Roland McGrath Cc: Oleg Nesterov --- fs/binfmt_elf.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index bcb884e2d613..43ba478c3386 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -1390,6 +1390,22 @@ static void do_thread_regset_writeback(struct task_struct *task, regset->writeback(task, regset, 1); } +#ifndef PR_REG_SIZE +#define PR_REG_SIZE(S) sizeof(S) +#endif + +#ifndef PRSTATUS_SIZE +#define PRSTATUS_SIZE(S) sizeof(S) +#endif + +#ifndef PR_REG_PTR +#define PR_REG_PTR(S) (&((S)->pr_reg)) +#endif + +#ifndef SET_PR_FPVALID +#define SET_PR_FPVALID(S, V) ((S)->pr_fpvalid = (V)) +#endif + static int fill_thread_core_info(struct elf_thread_core_info *t, const struct user_regset_view *view, long signr, size_t *total) @@ -1404,11 +1420,11 @@ static int fill_thread_core_info(struct elf_thread_core_info *t, */ fill_prstatus(&t->prstatus, t->task, signr); (void) view->regsets[0].get(t->task, &view->regsets[0], - 0, sizeof(t->prstatus.pr_reg), - &t->prstatus.pr_reg, NULL); + 0, PR_REG_SIZE(t->prstatus.pr_reg), + PR_REG_PTR(&t->prstatus), NULL); fill_note(&t->notes[0], "CORE", NT_PRSTATUS, - sizeof(t->prstatus), &t->prstatus); + PRSTATUS_SIZE(t->prstatus), &t->prstatus); *total += notesize(&t->notes[0]); do_thread_regset_writeback(t->task, &view->regsets[0]); @@ -1438,7 +1454,7 @@ static int fill_thread_core_info(struct elf_thread_core_info *t, regset->core_note_type, size, data); else { - t->prstatus.pr_fpvalid = 1; + SET_PR_FPVALID(&t->prstatus, 1); fill_note(&t->notes[i], "CORE", NT_PRFPREG, size, data); } -- cgit v1.2.3 From f044db4cb4bf16893812d35b5fbeaaf3e30c9215 Mon Sep 17 00:00:00 2001 From: Bobby Powers Date: Wed, 22 Feb 2012 00:29:47 -0500 Subject: fs: Fix close_on_exec pointer in alloc_fdtable alloc_fdtable allocates space for the open_fds and close_on_exec bitfields together, as 2 * nr / BITS_PER_BYTE. close_on_exec needs to point to open_fds + nr / BITS_PER_BYTE, not open_fds + nr / BITS_PER_LONG, as introducted in 1fd36adc: Replace the fd_sets in struct fdtable with an array of unsigned longs. Signed-off-by: Bobby Powers Link: http://lkml.kernel.org/r/1329888587-3087-1-git-send-email-bobbypowers@gmail.com Acked-by: David Howells Signed-off-by: H. Peter Anvin --- fs/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/file.c b/fs/file.c index 2d479dd8484e..171f6b41b4cd 100644 --- a/fs/file.c +++ b/fs/file.c @@ -179,7 +179,7 @@ static struct fdtable * alloc_fdtable(unsigned int nr) if (!data) goto out_arr; fdt->open_fds = data; - data += nr / BITS_PER_LONG; + data += nr / BITS_PER_BYTE; fdt->close_on_exec = data; fdt->next = NULL; -- cgit v1.2.3