ref: e2a6e622c715eac26cdbfba42ba4f3c2af5b8ac9
parent: fcfc849dd5fd121b02c40d744adb109a94566804
author: cinap_lenrek <[email protected]>
date: Tue Jul 7 02:30:34 EDT 2015
5e: approximate LL/SC with cas()
--- a/sys/src/cmd/5e/arm.c
+++ b/sys/src/cmd/5e/arm.c
@@ -120,10 +120,13 @@
*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 @@
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,11 +406,10 @@
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);
@@ -412,15 +416,14 @@
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 @@
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
--- a/sys/src/cmd/5e/dat.h
+++ b/sys/src/cmd/5e/dat.h
@@ -31,7 +31,10 @@
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 @@
Ref;
int flags;
RWLock rw; /* lock for SEGFLLOCK segments */
- Lock lock; /* atomic accesses */
u32int start, size;
void *data;
Ref *dref;