Skip to content
  • Linus Torvalds's avatar
    Make non-compat preadv/pwritev use native register size · 601cc11d
    Linus Torvalds authored
    
    
    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: default avatarGerd 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: default avatarLinus Torvalds <torvalds@linux-foundation.org>
    601cc11d