aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2009-04-03 11:03:22 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-04-04 17:20:34 -0400
commit601cc11d054ae4b5e9b5babec3d8e4667a2cb9b5 (patch)
tree725ec3422b4fe50267915c1d5b80c49712ffff75
parent6bb597507f9839b13498781e481f5458aea33620 (diff)
Make non-compat preadv/pwritev use native register size
Instead of always splitting the file offset into 32-bit 'high' and 'low' parts, just split them into the largest natural word-size - which in C terms is 'unsigned long'. This allows 64-bit architectures to avoid the unnecessary 32-bit shifting and masking for native format (while the compat interfaces will obviously always have to do it). This also changes the order of 'high' and 'low' to be "low first". Why? Because when we have it like this, the 64-bit system calls now don't use the "pos_high" argument at all, and it makes more sense for the native system call to simply match the user-mode prototype. This results in a much more natural calling convention, and allows the compiler to generate much more straightforward code. On x86-64, we now generate testq %rcx, %rcx # pos_l js .L122 #, movq %rcx, -48(%rbp) # pos_l, pos from the C source loff_t pos = pos_from_hilo(pos_h, pos_l); ... if (pos < 0) return -EINVAL; and the 'pos_h' register isn't even touched. It used to generate code like mov %r8d, %r8d # pos_low, pos_low salq $32, %rcx #, tmp71 movq %r8, %rax # pos_low, pos.386 orq %rcx, %rax # tmp71, pos.386 js .L122 #, movq %rax, -48(%rbp) # pos.386, pos which isn't _that_ horrible, but it does show how the natural word size is just a more sensible interface (same arguments will hold in the user level glibc wrapper function, of course, so the kernel side is just half of the equation!) Note: in all cases the user code wrapper can again be the same. You can just do #define HALF_BITS (sizeof(unsigned long)*4) __syscall(PWRITEV, fd, iov, count, offset, (offset >> HALF_BITS) >> HALF_BITS); or something like that. That way the user mode wrapper will also be nicely passing in a zero (it won't actually have to do the shifts, the compiler will understand what is going on) for the last argument. And that is a good idea, even if nobody will necessarily ever care: if we ever do move to a 128-bit lloff_t, this particular system call might be left alone. Of course, that will be the least of our worries if we really ever need to care, so this may not be worth really caring about. [ Fixed for lost 'loff_t' cast noticed by Andrew Morton ] Acked-by: Gerd Hoffmann <kraxel@redhat.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: linux-api@vger.kernel.org Cc: linux-arch@vger.kernel.org Cc: Ingo Molnar <mingo@elte.hu> Cc: Ralf Baechle <ralf@linux-mips.org>> Cc: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/compat.c4
-rw-r--r--fs/read_write.c14
-rw-r--r--include/linux/compat.h4
-rw-r--r--include/linux/syscalls.h4
4 files changed, 16 insertions, 10 deletions
diff --git a/fs/compat.c b/fs/compat.c
index 1c859dae758f..3f84d5f15889 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -1236,7 +1236,7 @@ compat_sys_readv(unsigned long fd, const struct compat_iovec __user *vec,
1236 1236
1237asmlinkage ssize_t 1237asmlinkage ssize_t
1238compat_sys_preadv(unsigned long fd, const struct compat_iovec __user *vec, 1238compat_sys_preadv(unsigned long fd, const struct compat_iovec __user *vec,
1239 unsigned long vlen, u32 pos_high, u32 pos_low) 1239 unsigned long vlen, u32 pos_low, u32 pos_high)
1240{ 1240{
1241 loff_t pos = ((loff_t)pos_high << 32) | pos_low; 1241 loff_t pos = ((loff_t)pos_high << 32) | pos_low;
1242 struct file *file; 1242 struct file *file;
@@ -1293,7 +1293,7 @@ compat_sys_writev(unsigned long fd, const struct compat_iovec __user *vec,
1293 1293
1294asmlinkage ssize_t 1294asmlinkage ssize_t
1295compat_sys_pwritev(unsigned long fd, const struct compat_iovec __user *vec, 1295compat_sys_pwritev(unsigned long fd, const struct compat_iovec __user *vec,
1296 unsigned long vlen, u32 pos_high, u32 pos_low) 1296 unsigned long vlen, u32 pos_low, u32 pos_high)
1297{ 1297{
1298 loff_t pos = ((loff_t)pos_high << 32) | pos_low; 1298 loff_t pos = ((loff_t)pos_high << 32) | pos_low;
1299 struct file *file; 1299 struct file *file;
diff --git a/fs/read_write.c b/fs/read_write.c
index 6d5d8ff238aa..9d1e76bb9ee1 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -731,10 +731,16 @@ SYSCALL_DEFINE3(writev, unsigned long, fd, const struct iovec __user *, vec,
731 return ret; 731 return ret;
732} 732}
733 733
734static inline loff_t pos_from_hilo(unsigned long high, unsigned long low)
735{
736#define HALF_LONG_BITS (BITS_PER_LONG / 2)
737 return (((loff_t)high << HALF_LONG_BITS) << HALF_LONG_BITS) | low;
738}
739
734SYSCALL_DEFINE5(preadv, unsigned long, fd, const struct iovec __user *, vec, 740SYSCALL_DEFINE5(preadv, unsigned long, fd, const struct iovec __user *, vec,
735 unsigned long, vlen, u32, pos_high, u32, pos_low) 741 unsigned long, vlen, unsigned long, pos_l, unsigned long, pos_h)
736{ 742{
737 loff_t pos = ((loff_t)pos_high << 32) | pos_low; 743 loff_t pos = pos_from_hilo(pos_h, pos_l);
738 struct file *file; 744 struct file *file;
739 ssize_t ret = -EBADF; 745 ssize_t ret = -EBADF;
740 int fput_needed; 746 int fput_needed;
@@ -757,9 +763,9 @@ SYSCALL_DEFINE5(preadv, unsigned long, fd, const struct iovec __user *, vec,
757} 763}
758 764
759SYSCALL_DEFINE5(pwritev, unsigned long, fd, const struct iovec __user *, vec, 765SYSCALL_DEFINE5(pwritev, unsigned long, fd, const struct iovec __user *, vec,
760 unsigned long, vlen, u32, pos_high, u32, pos_low) 766 unsigned long, vlen, unsigned long, pos_l, unsigned long, pos_h)
761{ 767{
762 loff_t pos = ((loff_t)pos_high << 32) | pos_low; 768 loff_t pos = pos_from_hilo(pos_h, pos_l);
763 struct file *file; 769 struct file *file;
764 ssize_t ret = -EBADF; 770 ssize_t ret = -EBADF;
765 int fput_needed; 771 int fput_needed;
diff --git a/include/linux/compat.h b/include/linux/compat.h
index 9723edd6455c..f2ded21f9a3c 100644
--- a/include/linux/compat.h
+++ b/include/linux/compat.h
@@ -193,10 +193,10 @@ asmlinkage ssize_t compat_sys_writev(unsigned long fd,
193 const struct compat_iovec __user *vec, unsigned long vlen); 193 const struct compat_iovec __user *vec, unsigned long vlen);
194asmlinkage ssize_t compat_sys_preadv(unsigned long fd, 194asmlinkage ssize_t compat_sys_preadv(unsigned long fd,
195 const struct compat_iovec __user *vec, 195 const struct compat_iovec __user *vec,
196 unsigned long vlen, u32 pos_high, u32 pos_low); 196 unsigned long vlen, u32 pos_low, u32 pos_high);
197asmlinkage ssize_t compat_sys_pwritev(unsigned long fd, 197asmlinkage ssize_t compat_sys_pwritev(unsigned long fd,
198 const struct compat_iovec __user *vec, 198 const struct compat_iovec __user *vec,
199 unsigned long vlen, u32 pos_high, u32 pos_low); 199 unsigned long vlen, u32 pos_low, u32 pos_high);
200 200
201int compat_do_execve(char * filename, compat_uptr_t __user *argv, 201int compat_do_execve(char * filename, compat_uptr_t __user *argv,
202 compat_uptr_t __user *envp, struct pt_regs * regs); 202 compat_uptr_t __user *envp, struct pt_regs * regs);
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index b299a82a05e7..18771cac2f85 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -462,9 +462,9 @@ asmlinkage long sys_pread64(unsigned int fd, char __user *buf,
462asmlinkage long sys_pwrite64(unsigned int fd, const char __user *buf, 462asmlinkage long sys_pwrite64(unsigned int fd, const char __user *buf,
463 size_t count, loff_t pos); 463 size_t count, loff_t pos);
464asmlinkage long sys_preadv(unsigned long fd, const struct iovec __user *vec, 464asmlinkage long sys_preadv(unsigned long fd, const struct iovec __user *vec,
465 unsigned long vlen, u32 pos_high, u32 pos_low); 465 unsigned long vlen, unsigned long pos_l, unsigned long pos_h);
466asmlinkage long sys_pwritev(unsigned long fd, const struct iovec __user *vec, 466asmlinkage long sys_pwritev(unsigned long fd, const struct iovec __user *vec,
467 unsigned long vlen, u32 pos_high, u32 pos_low); 467 unsigned long vlen, unsigned long pos_l, unsigned long pos_h);
468asmlinkage long sys_getcwd(char __user *buf, unsigned long size); 468asmlinkage long sys_getcwd(char __user *buf, unsigned long size);
469asmlinkage long sys_mkdir(const char __user *pathname, int mode); 469asmlinkage long sys_mkdir(const char __user *pathname, int mode);
470asmlinkage long sys_chdir(const char __user *filename); 470asmlinkage long sys_chdir(const char __user *filename);