diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2015-07-07 06:30:34 +0200 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2015-07-07 06:30:34 +0200 |
commit | e2a6e622c715eac26cdbfba42ba4f3c2af5b8ac9 (patch) | |
tree | 07ad8ca29ba4e489cc3aa7506b625d62f22cc906 /sys/src/cmd | |
parent | fcfc849dd5fd121b02c40d744adb109a94566804 (diff) |
5e: approximate LL/SC with cas()
Diffstat (limited to 'sys/src/cmd')
-rw-r--r-- | sys/src/cmd/5e/arm.c | 59 | ||||
-rw-r--r-- | sys/src/cmd/5e/dat.h | 6 |
2 files changed, 33 insertions, 32 deletions
diff --git a/sys/src/cmd/5e/arm.c b/sys/src/cmd/5e/arm.c index f824d8a01..5d130d746 100644 --- a/sys/src/cmd/5e/arm.c +++ b/sys/src/cmd/5e/arm.c @@ -120,10 +120,13 @@ single(u32int instr) *Rn = addr; } +/* atomic compare and swap from libc */ +extern int cas(u32int *p, u32int old, u32int new); + static void swap(u32int instr) { - u32int *Rm, *Rn, *Rd, *targ, addr, tmp; + u32int *Rm, *Rn, *Rd, *targ, addr, old, new; Segment *seg; Rm = P->R + (instr & 15); @@ -134,19 +137,21 @@ swap(u32int instr) addr = *Rn; if((instr & fB) == 0) addr = evenaddr(addr, 3); - clrex(); - targ = (u32int *) vaddr(addr, 4, &seg); - lock(&seg->lock); + targ = (u32int *) vaddr(addr & ~3, 4, &seg); + do { + old = *targ; + new = *Rm; + if(instr & fB){ + new &= 0xFF; + new <<= 8*(addr&3); + new |= old & ~(0xFF << 8*(addr&3)); + } + } while(!cas(targ, old, new)); if(instr & fB) { - tmp = *(u8int*) targ; - *(u8int*) targ = *Rm; - *Rd = tmp; - } else { - tmp = *targ; - *targ = *Rm; - *Rd = tmp; + old >>= 8*(addr&3); + old &= 0xFF; } - unlock(&seg->lock); + *Rd = old; segunlock(seg); } @@ -401,26 +406,24 @@ singleex(u32int instr) invalid(instr); addr = evenaddr(*Rn, 3); if(instr & fS) { - clrex(); targ = vaddr(addr, 4, &seg); - lock(&seg->lock); - P->excl = seg; *Rd = *targ; + P->lladdr = addr; + P->llval = *Rd; segunlock(seg); } else { Rm = P->R + (instr & 15); if(Rm == P->R + 15) invalid(instr); targ = vaddr(addr, 4, &seg); - if(canlock(&seg->lock)) { - unlock(&seg->lock); - *Rd = 1; - } else if(P->excl != seg) { - *Rd = 1; - } else { - *targ = *Rm; - *Rd = 0; - } + + /* + * this is not quite correct as we will succeed even + * if the value was modified and then restored to its + * original value but good enougth approximation for + * libc's _tas(), _cas() and _xinc()/_xdec(). + */ + *Rd = addr != P->lladdr || !cas(targ, P->llval, *Rm); segunlock(seg); clrex(); } @@ -429,12 +432,8 @@ singleex(u32int instr) void clrex(void) { - Segment *seg; - - seg = P->excl; - P->excl = nil; - if(seg != nil) - unlock(&seg->lock); + P->lladdr = 0; + P->llval = 0; } static void diff --git a/sys/src/cmd/5e/dat.h b/sys/src/cmd/5e/dat.h index 138656596..326abbc01 100644 --- a/sys/src/cmd/5e/dat.h +++ b/sys/src/cmd/5e/dat.h @@ -31,7 +31,10 @@ struct Process { Ref *path; /* Ref + string data */ Segment *S[SEGNUM]; /* memory */ - Segment *excl; /* recently acquired exclusive access */ + + u32int lladdr; /* LL/SC emulation */ + u32int llval; + u32int R[16]; /* general purpose registers / PC (R15) */ u32int CPSR; /* status register */ @@ -66,7 +69,6 @@ struct Segment { Ref; int flags; RWLock rw; /* lock for SEGFLLOCK segments */ - Lock lock; /* atomic accesses */ u32int start, size; void *data; Ref *dref; |