via sources miller@hamnavoe.com Update ARM atomic operations (tas, cas, casp, xinc/xdec, ainc/adec) to work correctly on multiprocessors (armv7 and later). This entails changing SWPW to LDREX/STREX, and adding DMB barriers. Also add emulation of DMB to the kw (armv5) kernel, so the new library routines can still be used with older ARM systems. Reference: /n/atom/patch/armv6-atomic Date: Sun Jun 19 23:08:50 CES 2016 Signed-off-by: quanstro@quanstro.net --- /sys/src/libc/arm/atom.s Sun Jun 19 23:08:20 2016 +++ /sys/src/libc/arm/atom.s Sun Jun 19 23:08:21 2016 @@ -1,3 +1,4 @@ +#define DMB MCR 15, 0, R0, C7, C10, 5 #define CLREX WORD $0xf57ff01f #define LDREX(a,r) WORD $(0xe<<28|0x01900f9f | (a)<<16 | (r)<<12) /* `The order of operands is from left to right in dataflow order' - asm man */ @@ -19,11 +20,14 @@ STREX(2,0,4) /* STREX 0(R0),R2,R4 */ CMP.S $0, R4 BNE spincas + MOVW $0, R0 + DMB MOVW $1, R0 RET fail: CLREX MOVW $0, R0 + DMB RET TEXT ainc(SB), $0 /* int ainc(int *); */ @@ -33,6 +37,8 @@ STREX(3,0,4) /* STREX 0(R0),R3,R4 */ CMP.S $0, R4 BNE spinainc + MOVW $0, R0 + DMB MOVW R3, R0 RET @@ -43,6 +49,8 @@ STREX(3,0,4) /* STREX 0(R0),R3,R4 */ CMP.S $0, R4 BNE spinadec + MOVW $0, R0 + DMB MOVW R3, R0 RET @@ -52,6 +60,8 @@ TEXT storecond(SB), $0 /* int storecond(long *, long); */ MOVW ov+4(FP), R3 - STREX(3,0,0) /* STREX 0(R0),R3,R0 */ - RSB $1, R0 + STREX(3,0,4) /* STREX 0(R0),R3,R4 */ + MOVW $0, R0 + DMB + RSB $1, R4, R0 RET --- /sys/src/libc/arm/tas.s Sun Jun 19 23:08:23 2016 +++ /sys/src/libc/arm/tas.s Sun Jun 19 23:08:24 2016 @@ -1,5 +1,29 @@ -TEXT _tas(SB), 1, $-4 - MOVW R0,R1 - MOVW $1,R0 - SWPW R0,(R1) /* fix: deprecated in armv7 */ +#define DMB MCR 15, 0, R0, C7, C10, 5 +#define STREX(f,tp,r) WORD $(0xe<<28|0x01800f90 | (tp)<<16 | (r)<<12 | (f)<<0) +#define LDREX(fp,t) WORD $(0xe<<28|0x01900f9f | (fp)<<16 | (t)<<12) +#define CLREX WORD $0xf57ff01f + +TEXT _tas(SB), $-4 /* _tas(ulong *) */ + /* returns old (R0) after modifying (R0) */ + MOVW R0,R5 + MOVW $0, R0 + DMB + + MOVW $1,R2 /* new value of (R0) */ +tas1: + LDREX(5,1) /* LDREX 0(R5),R1 */ + CMP.S $0, R1 /* old value non-zero (lock taken)? */ + BNE lockbusy /* we lose */ + STREX(2,5,4) /* STREX R2,(R5),R4 */ + CMP.S $0, R4 + BNE tas1 /* strex failed? try again */ + MOVW $0, R0 + DMB + MOVW R1, R0 + RET +lockbusy: + CLREX + MOVW $0, R0 + DMB + MOVW R1, R0 RET --- /sys/src/ape/lib/ap/arm/atom.s Thu Jan 1 00:00:00 1970 +++ /sys/src/ape/lib/ap/arm/atom.s Sun Jun 19 23:08:25 2016 @@ -0,0 +1,67 @@ +#define DMB MCR 15, 0, R0, C7, C10, 5 +#define CLREX WORD $0xf57ff01f +#define LDREX(a,r) WORD $(0xe<<28|0x01900f9f | (a)<<16 | (r)<<12) +/* `The order of operands is from left to right in dataflow order' - asm man */ +#define STREX(v,a,r) WORD $(0xe<<28|0x01800f90 | (a)<<16 | (r)<<12 | (v)<<0) + +/* + * int cas(int *p, int ov, int nv); + */ + +TEXT cas+0(SB),0,$0 /* r0 holds p */ +TEXT cas32+0(SB),0,$0 /* r0 holds p */ +TEXT casp+0(SB),0,$0 /* r0 holds p */ + MOVW ov+4(FP), R1 + MOVW nv+8(FP), R2 +spincas: + LDREX(0,3) /* LDREX 0(R0),R3 */ + CMP.S R3, R1 + BNE fail + STREX(2,0,4) /* STREX 0(R0),R2,R4 */ + CMP.S $0, R4 + BNE spincas + MOVW $0, R0 + DMB + MOVW $1, R0 + RET +fail: + CLREX + MOVW $0, R0 + DMB + RET + +TEXT ainc(SB), $0 /* int ainc(int *); */ +spinainc: + LDREX(0,3) /* LDREX 0(R0),R3 */ + ADD $1,R3 + STREX(3,0,4) /* STREX 0(R0),R3,R4 */ + CMP.S $0, R4 + BNE spinainc + MOVW $0, R0 + DMB + MOVW R3, R0 + RET + +TEXT adec(SB), $0 /* int adec(int *); */ +spinadec: + LDREX(0,3) /* LDREX 0(R0),R3 */ + SUB $1,R3 + STREX(3,0,4) /* STREX 0(R0),R3,R4 */ + CMP.S $0, R4 + BNE spinadec + MOVW $0, R0 + DMB + MOVW R3, R0 + RET + +TEXT loadlinked(SB), $0 /* long loadlinked(long *); */ + LDREX(0,0) /* LDREX 0(R0),R0 */ + RET + +TEXT storecond(SB), $0 /* int storecond(long *, long); */ + MOVW ov+4(FP), R3 + STREX(3,0,4) /* STREX 0(R0),R3,R4 */ + MOVW $0, R0 + DMB + RSB $1, R4, R0 + RET --- /sys/src/ape/lib/ap/arm/tas.s Sun Jun 19 23:08:27 2016 +++ /sys/src/ape/lib/ap/arm/tas.s Sun Jun 19 23:08:28 2016 @@ -1,5 +1,29 @@ -TEXT tas(SB), $-4 - MOVW R0,R1 - MOVW $1,R0 - SWPW R0,(R1) +#define DMB MCR 15, 0, R0, C7, C10, 5 +#define STREX(f,tp,r) WORD $(0xe<<28|0x01800f90 | (tp)<<16 | (r)<<12 | (f)<<0) +#define LDREX(fp,t) WORD $(0xe<<28|0x01900f9f | (fp)<<16 | (t)<<12) +#define CLREX WORD $0xf57ff01f + +TEXT _tas(SB), $-4 /* _tas(ulong *) */ + /* returns old (R0) after modifying (R0) */ + MOVW R0,R5 + MOVW $0, R0 + DMB + + MOVW $1,R2 /* new value of (R0) */ +tas1: + LDREX(5,1) /* LDREX 0(R5),R1 */ + CMP.S $0, R1 /* old value non-zero (lock taken)? */ + BNE lockbusy /* we lose */ + STREX(2,5,4) /* STREX R2,(R5),R4 */ + CMP.S $0, R4 + BNE tas1 /* strex failed? try again */ + MOVW $0, R0 + DMB + MOVW R1, R0 + RET +lockbusy: + CLREX + MOVW $0, R0 + DMB + MOVW R1, R0 RET --- /sys/src/9/kw/fpiarm.c Sun Jun 19 23:08:30 2016 +++ /sys/src/9/kw/fpiarm.c Sun Jun 19 23:08:31 2016 @@ -468,6 +468,14 @@ spllo(); } +void +dmb(ulong pc, ulong op, Ureg *ur) +{ + USED(pc); + USED(op); + USED(ur); +} + int ldrexvalid; void @@ -533,6 +541,7 @@ { 0x01800f90, 0x0ff00ff0, strex }, { 0xf57ff01f, 0xffffffff, clrex }, { 0x0ed00100, 0x0ef08100, casemu }, + { 0xee070fba, 0xffffffff, dmb }, { 0x00000000, 0x00000000, nil } };