code: plan9front

Download patch

ref: e2a6e622c715eac26cdbfba42ba4f3c2af5b8ac9
parent: fcfc849dd5fd121b02c40d744adb109a94566804
author: cinap_lenrek <cinap_lenrek@felloff.net>
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;