From: Daniel Thompson <hidden> Date: 2014-06-12 15:42:46
A new atomic modeset/pageflip ioctl being developed in DRM requires
get_user() to work for 64bit types (in addition to just put_user()).
v1: original
v2: pass correct size to check_uaccess, and better handling of narrowing
double word read with __get_user_xb() (Russell King's suggestion)
v3: fix a couple of checkpatch issues
Signed-off-by: Rob Clark <redacted>
Signed-off-by: Daniel Thompson <redacted>
Cc: Russell King - ARM Linux <redacted>
---
Notes:
I'd like to wake this patch up again. It was rejected back in 2012 on
the grounds that other architectures (notably x86-32) didn't implement
this so adding for ARM risked portability problems in drivers. However
shortly after the discussion (in fact I believe that as a *result* of
that discussion) support for 64-bit get_user() was added for x86-32.
A quick review of different architectures uaccess.h shows that ARM is
in the minority (even after excluding 64-bit architectures) in not
implementing this feature.
The reasons to wake it up are the same as before. Recent contributions,
including to DRM[1] and binder[2] would prefer to use the 64-bit values
in their interfaces without gotchas like having to use copy_from_user().
[1] http://thread.gmane.org/gmane.comp.video.dri.devel/102135/focus=102149
[2] http://thread.gmane.org/gmane.linux.kernel/1653448/focus=1653449
arch/arm/include/asm/uaccess.h | 18 +++++++++++++++++-
arch/arm/lib/getuser.S | 17 ++++++++++++++++-
2 files changed, 33 insertions(+), 2 deletions(-)
@@ -125,11 +127,19 @@ extern int __get_user_4(void *);:"0"(__p),"r"(__l)\:__GUP_CLOBBER_##__s)+/* narrowing a double-word get into a single 32bit word register: */+#ifdef BIG_ENDIAN+#define __get_user_xb(__r2, __p, __e, __l, __s) \+__get_user_x(__r2,(uintptr_t)__p+4,__e,__l,__s)+#else+#define __get_user_xb __get_user_x+#endif+#define __get_user_check(x,p) \({\unsignedlong__limit=current_thread_info()->addr_limit-1;\registerconsttypeof(*(p))__user*__pasm("r0")=(p);\-registerunsignedlong__r2asm("r2");\+registertypeof(x)__r2asm("r2");\registerunsignedlong__lasm("r1")=__limit;\registerint__easm("r0");\switch(sizeof(*(__p))){\
@@ -142,6 +152,12 @@ extern int __get_user_4(void *);case4:\__get_user_x(__r2,__p,__e,__l,4);\break;\+case8:\+if(sizeof((x))<8)\+__get_user_xb(__r2,__p,__e,__l,4);\+else\+__get_user_x(__r2,__p,__e,__l,8);\+break;\default:__e=__get_user_bad();break;\}\x=(typeof(*(p)))__r2;\
From: Russell King - ARM Linux <hidden> Date: 2014-06-12 15:58:54
On Thu, Jun 12, 2014 at 04:42:35PM +0100, Daniel Thompson wrote:
A new atomic modeset/pageflip ioctl being developed in DRM requires
get_user() to work for 64bit types (in addition to just put_user()).
v1: original
v2: pass correct size to check_uaccess, and better handling of narrowing
double word read with __get_user_xb() (Russell King's suggestion)
v3: fix a couple of checkpatch issues
So, __r2 becomes the type of 'x'. If 'x' is a 64-bit type, and *p is
an 8-bit, 16-bit, or 32-bit type, this fails horribly by leaving the
upper word of __r2 undefined.
__r2 must follow the size of the value we are reading. I think the
last solution which was proposed was this:
arch/arm/include/asm/uaccess.h | 17 +++++++++++++----
arch/arm/lib/getuser.S | 33 ++++++++++++++++++++++++++++++++-
2 files changed, 45 insertions(+), 5 deletions(-)
From: Daniel Thompson <hidden> Date: 2014-06-17 10:17:44
On 12/06/14 16:58, Russell King - ARM Linux wrote:
On Thu, Jun 12, 2014 at 04:42:35PM +0100, Daniel Thompson wrote:
quoted
A new atomic modeset/pageflip ioctl being developed in DRM requires
get_user() to work for 64bit types (in addition to just put_user()).
v1: original
v2: pass correct size to check_uaccess, and better handling of narrowing
double word read with __get_user_xb() (Russell King's suggestion)
v3: fix a couple of checkpatch issues
So, __r2 becomes the type of 'x'. If 'x' is a 64-bit type, and *p is
an 8-bit, 16-bit, or 32-bit type, this fails horribly by leaving the
upper word of __r2 undefined.
It is true that at after the switch statement the contents of r3 are
undefined. However...
quoted hunk
register unsigned long __l asm("r1") = __limit; \
register int __e asm("r0"); \
switch (sizeof(*(__p))) { \
... at this point there is a narrowing cast followed by an implicit
widening. This results in compiler either ignoring r3 altogether or, if
spilling to the stack, generating code to set r3 to zero before doing
the store.
I think this approach also makes 8-bit and 16-bit get_user() faster in
some cases where the type of *p and x are similar 8- or 16-bit types.
This is because the compiler will never generate a redundant narrowings.
Note that the speed improvement looks extremely marginal; the size of
the .text section (for a multi_v7_defconfig kernel) only gets 96 bytes
smaller (a.k.a. 0.0015%).
Daniel.
From: Russell King - ARM Linux <hidden> Date: 2014-06-17 11:09:18
On Tue, Jun 17, 2014 at 11:17:23AM +0100, Daniel Thompson wrote:
... at this point there is a narrowing cast followed by an implicit
widening. This results in compiler either ignoring r3 altogether or, if
spilling to the stack, generating code to set r3 to zero before doing
the store.
In actual fact, there's very little difference between the two
implementations in terms of generated code.
The difference between them is what happens on the 64-bit big endian
narrowing case, where we use __get_user_4 with your version. This
adds one additional instruction.
The little endian case results in identical code except for register
usage - for example, with my test for a 32-bit being widened to 64-bit:
str lr, [sp, #-4]!
- mov r3, r0
+ mov ip, r0
mov r0, r1
#APP
@ 280 "t-getuser.c" 1
bl __get_user_4
@ 0 "" 2
- str r2, [r3, #0]
- mov r2, #0
- str r2, [r3, #4]
+ mov r3, #0
+ str r2, [ip, #0]
+ str r3, [ip, #4]
ldr pc, [sp], #4
and 64-bit narrowed to 32-bit:
str lr, [sp, #-4]!
- mov ip, r0
+ mov r3, r0
mov r0, r1
#APP
@ 275 "t-getuser.c" 1
- bl __get_user_8
+ bl __get_user_4
@ 0 "" 2
- str r2, [ip, #0]
+ str r2, [r3, #0]
ldr pc, [sp], #4
In terms of type checking, both seem to get it correct (which is something
I'm concerned about by any implementation since this is just as important
as the generated code).
--
FTTC broadband for 0.8mile line: now at 9.7Mbps down 460kbps up... slowly
improving, and getting towards what was expected from it.
From: Daniel Thompson <hidden> Date: 2014-06-17 13:29:05
On 17/06/14 12:09, Russell King - ARM Linux wrote:
On Tue, Jun 17, 2014 at 11:17:23AM +0100, Daniel Thompson wrote:
quoted
... at this point there is a narrowing cast followed by an implicit
widening. This results in compiler either ignoring r3 altogether or, if
spilling to the stack, generating code to set r3 to zero before doing
the store.
In actual fact, there's very little difference between the two
implementations in terms of generated code.
The difference between them is what happens on the 64-bit big endian
narrowing case, where we use __get_user_4 with your version. This
adds one additional instruction.
The later case avoids allocating r3 for the __get_user_x and should
reduce register pressure and, potentially, saves a few instructions
elsewhere (one of my rather large test functions does demonstrate this
effect).
I don't know if we care about that. If we do I'm certainly happy to put
a patch together than exploits this (whilst avoiding the add in the big
endian case).
Daniel.
From: Russell King - ARM Linux <hidden> Date: 2014-06-17 13:36:29
On Tue, Jun 17, 2014 at 02:28:44PM +0100, Daniel Thompson wrote:
On 17/06/14 12:09, Russell King - ARM Linux wrote:
quoted
On Tue, Jun 17, 2014 at 11:17:23AM +0100, Daniel Thompson wrote:
quoted
... at this point there is a narrowing cast followed by an implicit
widening. This results in compiler either ignoring r3 altogether or, if
spilling to the stack, generating code to set r3 to zero before doing
the store.
In actual fact, there's very little difference between the two
implementations in terms of generated code.
The difference between them is what happens on the 64-bit big endian
narrowing case, where we use __get_user_4 with your version. This
adds one additional instruction.
The later case avoids allocating r3 for the __get_user_x and should
reduce register pressure and, potentially, saves a few instructions
elsewhere (one of my rather large test functions does demonstrate this
effect).
I don't know if we care about that. If we do I'm certainly happy to put
a patch together than exploits this (whilst avoiding the add in the big
endian case).
No need - the + case is your version, the - case is my version. So your
version wins on this point. :)
--
FTTC broadband for 0.8mile line: now at 9.7Mbps down 460kbps up... slowly
improving, and getting towards what was expected from it.
From: Daniel Thompson <hidden> Date: 2014-06-17 13:54:36
On 17/06/14 14:36, Russell King - ARM Linux wrote:
On Tue, Jun 17, 2014 at 02:28:44PM +0100, Daniel Thompson wrote:
quoted
On 17/06/14 12:09, Russell King - ARM Linux wrote:
quoted
On Tue, Jun 17, 2014 at 11:17:23AM +0100, Daniel Thompson wrote:
quoted
... at this point there is a narrowing cast followed by an implicit
widening. This results in compiler either ignoring r3 altogether or, if
spilling to the stack, generating code to set r3 to zero before doing
the store.
In actual fact, there's very little difference between the two
implementations in terms of generated code.
The difference between them is what happens on the 64-bit big endian
narrowing case, where we use __get_user_4 with your version. This
adds one additional instruction.
The later case avoids allocating r3 for the __get_user_x and should
reduce register pressure and, potentially, saves a few instructions
elsewhere (one of my rather large test functions does demonstrate this
effect).
I don't know if we care about that. If we do I'm certainly happy to put
a patch together than exploits this (whilst avoiding the add in the big
endian case).
No need - the + case is your version, the - case is my version. So your
version wins on this point. :)
:) Thanks, although credit really goes to Rob Clark...
I think currently:
1. Rob's patch is better for register pressure in the narrowing case
(above).
2. Your patch is probably better for big endian due to the add in Rob's
version. I say probably because, without proof, I suspect the cost
of the add would in most cases outweigh the register pressure
benefit.
3. Your patch has better implementation of __get_user_8 (it uses ldrd).
Hence I'm suspect we need to combine elements from both patches.
From: Daniel Thompson <hidden> Date: 2014-06-20 10:02:22
Recent contributions, including to DRM and binder, introduce 64-bit
values in their interfaces. Common motivation example of this is to allow
the same ABI for 32- and 64-bit userspaces (and therefore also a shared
ABI for 32/64 hybrid userspaces). Anyhow, the developers would like to
avoid gotchas like having to use copy_from_user().
This feature is already implemented on x86-32 and the majority of other
32-bit architectures. The current list of get_user_8 hold out
architectures are: arm, avr32, blackfin, m32r, metag, microblaze,
mn10300, sh.
Credit:
My name sits rather uneasily at the top of this patch. The v1 and
v2 versions of the patch were written by Rob Clark and to produce v4
I mostly copied code from Russell King and H. Peter Anvin. However I
have mangled the patch sufficiently that *blame* is rightfully mine
even if credit should more widely shared.
Changelog:
v4: remove an inlined add on big endian systems (spotted by Russell King),
used __ARMEB__ rather than BIG_ENDIAN (to match rest of file),
cleared r3 on EFAULT during __get_user_8.
v3: fix a couple of checkpatch issues
v2: pass correct size to check_uaccess, and better handling of narrowing
double word read with __get_user_xb() (Russell King's suggestion)
v1: original
Signed-off-by: Rob Clark <redacted>
Signed-off-by: Daniel Thompson <redacted>
Cc: Russell King - ARM Linux <redacted>
---
arch/arm/include/asm/uaccess.h | 20 +++++++++++++++++++-
arch/arm/lib/getuser.S | 37 ++++++++++++++++++++++++++++++++++++-
2 files changed, 55 insertions(+), 2 deletions(-)
@@ -125,11 +129,19 @@ extern int __get_user_4(void *);:"0"(__p),"r"(__l)\:__GUP_CLOBBER_##__s)+/* narrowing a double-word get into a single 32bit word register: */+#ifdef __ARMEB__+#define __get_user_xb(__r2, __p, __e, __l, __s) \+__get_user_x(__r2,__p,__e,__l,lo8)+#else+#define __get_user_xb __get_user_x+#endif+#define __get_user_check(x,p) \({\unsignedlong__limit=current_thread_info()->addr_limit-1;\registerconsttypeof(*(p))__user*__pasm("r0")=(p);\-registerunsignedlong__r2asm("r2");\+registertypeof(x)__r2asm("r2");\registerunsignedlong__lasm("r1")=__limit;\registerint__easm("r0");\switch(sizeof(*(__p))){\
@@ -142,6 +154,12 @@ extern int __get_user_4(void *);case4:\__get_user_x(__r2,__p,__e,__l,4);\break;\+case8:\+if(sizeof((x))<8)\+__get_user_xb(__r2,__p,__e,__l,4);\+else\+__get_user_x(__r2,__p,__e,__l,8);\+break;\default:__e=__get_user_bad();break;\}\x=(typeof(*(p)))__r2;\
From: Daniel Thompson <hidden> Date: 2014-07-10 19:48:31
Recent contributions, including to DRM and binder, introduce 64-bit
values in their interfaces. A common motivation for this is to allow
the same ABI for 32- and 64-bit userspaces (and therefore also a shared
ABI for 32/64 hybrid userspaces). Anyhow, the developers would like to
avoid gotchas like having to use copy_from_user().
This feature is already implemented on x86-32 and the majority of other
32-bit architectures. The current list of get_user_8 hold out
architectures are: arm, avr32, blackfin, m32r, metag, microblaze,
mn10300, sh.
Credit:
My name sits rather uneasily at the top of this patch. The v1 and
v2 versions of the patch were written by Rob Clark and to produce v4
I mostly copied code from Russell King and H. Peter Anvin. However I
have mangled the patch sufficiently that *blame* is rightfully mine
even if credit should more widely shared.
Changelog:
v5: updated to use the ret macro (requested by Russell King)
v4: remove an inlined add on big endian systems (spotted by Russell King),
used __ARMEB__ rather than BIG_ENDIAN (to match rest of file),
cleared r3 on EFAULT during __get_user_8.
v3: fix a couple of checkpatch issues
v2: pass correct size to check_uaccess, and better handling of narrowing
double word read with __get_user_xb() (Russell King's suggestion)
v1: original
Signed-off-by: Rob Clark <redacted>
Signed-off-by: Daniel Thompson <redacted>
Cc: Russell King - ARM Linux <redacted>
---
arch/arm/include/asm/uaccess.h | 20 +++++++++++++++++++-
arch/arm/lib/getuser.S | 37 ++++++++++++++++++++++++++++++++++++-
2 files changed, 55 insertions(+), 2 deletions(-)
@@ -125,11 +129,19 @@ extern int __get_user_4(void *);:"0"(__p),"r"(__l)\:__GUP_CLOBBER_##__s)+/* narrowing a double-word get into a single 32bit word register: */+#ifdef __ARMEB__+#define __get_user_xb(__r2, __p, __e, __l, __s) \+__get_user_x(__r2,__p,__e,__l,lo8)+#else+#define __get_user_xb __get_user_x+#endif+#define __get_user_check(x,p) \({\unsignedlong__limit=current_thread_info()->addr_limit-1;\registerconsttypeof(*(p))__user*__pasm("r0")=(p);\-registerunsignedlong__r2asm("r2");\+registertypeof(x)__r2asm("r2");\registerunsignedlong__lasm("r1")=__limit;\registerint__easm("r0");\switch(sizeof(*(__p))){\
@@ -142,6 +154,12 @@ extern int __get_user_4(void *);case4:\__get_user_x(__r2,__p,__e,__l,4);\break;\+case8:\+if(sizeof((x))<8)\+__get_user_xb(__r2,__p,__e,__l,4);\+else\+__get_user_x(__r2,__p,__e,__l,8);\+break;\default:__e=__get_user_bad();break;\}\x=(typeof(*(p)))__r2;\
From: Victor Kamensky <hidden> Date: 2014-08-21 05:37:02
On 10 July 2014 12:47, Daniel Thompson [off-list ref] wrote:
quoted hunk
Recent contributions, including to DRM and binder, introduce 64-bit
values in their interfaces. A common motivation for this is to allow
the same ABI for 32- and 64-bit userspaces (and therefore also a shared
ABI for 32/64 hybrid userspaces). Anyhow, the developers would like to
avoid gotchas like having to use copy_from_user().
This feature is already implemented on x86-32 and the majority of other
32-bit architectures. The current list of get_user_8 hold out
architectures are: arm, avr32, blackfin, m32r, metag, microblaze,
mn10300, sh.
Credit:
My name sits rather uneasily at the top of this patch. The v1 and
v2 versions of the patch were written by Rob Clark and to produce v4
I mostly copied code from Russell King and H. Peter Anvin. However I
have mangled the patch sufficiently that *blame* is rightfully mine
even if credit should more widely shared.
Changelog:
v5: updated to use the ret macro (requested by Russell King)
v4: remove an inlined add on big endian systems (spotted by Russell King),
used __ARMEB__ rather than BIG_ENDIAN (to match rest of file),
cleared r3 on EFAULT during __get_user_8.
v3: fix a couple of checkpatch issues
v2: pass correct size to check_uaccess, and better handling of narrowing
double word read with __get_user_xb() (Russell King's suggestion)
v1: original
Signed-off-by: Rob Clark <redacted>
Signed-off-by: Daniel Thompson <redacted>
Cc: Russell King - ARM Linux <redacted>
---
arch/arm/include/asm/uaccess.h | 20 +++++++++++++++++++-
arch/arm/lib/getuser.S | 37 ++++++++++++++++++++++++++++++++++++-
2 files changed, 55 insertions(+), 2 deletions(-)
@@ -125,11 +129,19 @@ extern int __get_user_4(void *);:"0"(__p),"r"(__l)\:__GUP_CLOBBER_##__s)+/* narrowing a double-word get into a single 32bit word register: */+#ifdef __ARMEB__+#define __get_user_xb(__r2, __p, __e, __l, __s) \+__get_user_x(__r2,__p,__e,__l,lo8)+#else+#define __get_user_xb __get_user_x+#endif+#define __get_user_check(x,p) \({\unsignedlong__limit=current_thread_info()->addr_limit-1;\registerconsttypeof(*(p))__user*__pasm("r0")=(p);\-registerunsignedlong__r2asm("r2");\+registertypeof(x)__r2asm("r2");\