libc/arm: implement _tas() with LDREX/STREX, execute memory barrier on smp systems (zynq)

This commit is contained in:
cinap_lenrek 2015-07-07 19:24:10 +02:00
parent 5458506881
commit 6506147066
5 changed files with 127 additions and 64 deletions

View file

@ -1,58 +1,50 @@
#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(ulong *p, ulong ov, ulong nv);
* int cas32(u32int *p, u32int ov, u32int nv);
* int cas(uint *p, int ov, int nv);
* int casp(void **p, void *ov, void *nv);
* int casl(ulong *p, ulong ov, ulong nv);
*/
TEXT cas+0(SB),0,$0 /* r0 holds p */
TEXT casp+0(SB),0,$0 /* r0 holds p */
TEXT cas32(SB), 1, $-4
TEXT cas(SB), 1, $-4
TEXT casp(SB), 1, $-4
TEXT casl(SB), 1, $-4
MOVW ov+4(FP), R1
MOVW nv+8(FP), R2
spincas:
LDREX(0,3) /* LDREX 0(R0),R3 */
LDREX (R0), R3
CMP.S R3, R1
BNE fail
STREX(2,0,4) /* STREX 0(R0),R2,R4 */
STREX R2, (R0), R4
CMP.S $0, R4
BNE spincas
MOVW $1, R0
RET
MOVW _barrier(SB), R5
B (R5)
fail:
CLREX
MOVW $0, R0
RET
TEXT _xinc(SB), $0 /* void _xinc(long *); */
TEXT ainc(SB), $0 /* long ainc(long *); */
TEXT _xinc(SB), 1, $-4 /* void _xinc(long *); */
TEXT ainc(SB), 1, $-4 /* long ainc(long *); */
spinainc:
LDREX(0,3) /* LDREX 0(R0),R3 */
LDREX (R0), R3
ADD $1,R3
STREX(3,0,4) /* STREX 0(R0),R3,R4 */
STREX R3, (R0), R4
CMP.S $0, R4
BNE spinainc
MOVW R3, R0
RET
MOVW _barrier(SB), R5
B (R5)
TEXT _xdec(SB), $0 /* long _xdec(long *); */
TEXT adec(SB), $0 /* long adec(long *); */
TEXT _xdec(SB), 1, $-4 /* long _xdec(long *); */
TEXT adec(SB), 1, $-4 /* long adec(long *); */
spinadec:
LDREX(0,3) /* LDREX 0(R0),R3 */
LDREX (R0), R3
SUB $1,R3
STREX(3,0,4) /* STREX 0(R0),R3,R4 */
STREX R3, (R0), R4
CMP.S $0, R4
BNE spinadec
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,0) /* STREX 0(R0),R3,R0 */
RSB $1, R0
RET
MOVW _barrier(SB), R5
B (R5)

View file

@ -1,28 +0,0 @@
/*
* int swp(int r, int *p);
* uchar swpb(uchar r, uchar *p);
*
* int cas(uintptr *p, uintptr ov, uintptr nv);
*/
#define LDREX(a,r) WORD $(0xe<<28|0x01900f9f | (a)<<16 | (r)<<12)
#define STREX(a,v,r) WORD $(0xe<<28|0x01800f90 | (a)<<16 | (r)<<12 | (v)<<0)
TEXT cas+0(SB),0,$12 /* r0 holds p */
MOVW ov+4(FP), R1
MOVW nv+8(FP), R2
spin:
/* LDREX 0(R0),R3 */
LDREX(0,3)
CMP.S R3, R1
BNE fail
/* STREX 0(R0),R2,R4 */
STREX(0,2,4)
CMP.S $0, R4
BNE spin
MOVW $1, R0
RET
fail:
MOVW $0, R0
RET

89
sys/src/libc/arm/lock.c Normal file
View file

@ -0,0 +1,89 @@
#include <u.h>
#include <libc.h>
static long lockinit(long);
/*
* barrier is called from atom.s and tas.s assembly
* to execute memory barrier.
*/
long (*_barrier)(long) = lockinit;
static int
cpus(void)
{
char buf[256], *p;
int f, n;
f = open("#c/sysstat", OREAD);
if(f < 0)
return -1;
n = read(f, buf, sizeof(buf)-1);
close(f);
if(n <= 0)
return -1;
buf[n] = '\0';
n = 0;
p = buf;
while(*p != '\0'){
if(*p == '\n')
n++;
p++;
}
return n;
}
long _dmb(long);
static long
_nop(long r0)
{
return r0;
}
static long
lockinit(long r0)
{
if(cpus() > 1)
_barrier = _dmb;
else
_barrier = _nop;
return (*_barrier)(r0);
}
void
lock(Lock *lk)
{
int i;
/* once fast */
if(!_tas(&lk->val))
return;
/* a thousand times pretty fast */
for(i=0; i<1000; i++){
if(!_tas(&lk->val))
return;
sleep(0);
}
/* now nice and slow */
for(i=0; i<1000; i++){
if(!_tas(&lk->val))
return;
sleep(100);
}
/* take your time */
while(_tas(&lk->val))
sleep(1000);
}
int
canlock(Lock *lk)
{
return _tas(&lk->val) == 0;
}
void
unlock(Lock *lk)
{
lk->val = (*_barrier)(0);
}

View file

@ -23,6 +23,7 @@ CFILES=\
cycles.c\
notejmp.c\
vlrt.c\
lock.c\
HFILES=/sys/include/libc.h

View file

@ -1,5 +1,14 @@
TEXT _tas(SB), 1, $-4
MOVW R0,R1
MOVW $1,R0
SWPW R0,(R1) /* fix: deprecated in armv7 */
TEXT _tas(SB), 1, $-4
MOVW $1, R2
_tas1:
LDREX (R0), R1
STREX R2, (R0), R3
CMP.S $0, R3
BNE _tas1
MOVW R1, R0
MOVW _barrier(SB), R4
B (R4)
TEXT _dmb(SB), 1, $-4
WORD $0xf57ff05f
RET