5e: approximate LL/SC with cas()

This commit is contained in:
cinap_lenrek 2015-07-07 06:30:34 +02:00
parent fcfc849dd5
commit e2a6e622c7
2 changed files with 33 additions and 32 deletions

View file

@ -120,10 +120,13 @@ single(u32int instr)
*Rn = addr; *Rn = addr;
} }
/* atomic compare and swap from libc */
extern int cas(u32int *p, u32int old, u32int new);
static void static void
swap(u32int instr) swap(u32int instr)
{ {
u32int *Rm, *Rn, *Rd, *targ, addr, tmp; u32int *Rm, *Rn, *Rd, *targ, addr, old, new;
Segment *seg; Segment *seg;
Rm = P->R + (instr & 15); Rm = P->R + (instr & 15);
@ -134,19 +137,21 @@ swap(u32int instr)
addr = *Rn; addr = *Rn;
if((instr & fB) == 0) if((instr & fB) == 0)
addr = evenaddr(addr, 3); addr = evenaddr(addr, 3);
clrex(); targ = (u32int *) vaddr(addr & ~3, 4, &seg);
targ = (u32int *) vaddr(addr, 4, &seg); do {
lock(&seg->lock); old = *targ;
if(instr & fB) { new = *Rm;
tmp = *(u8int*) targ; if(instr & fB){
*(u8int*) targ = *Rm; new &= 0xFF;
*Rd = tmp; new <<= 8*(addr&3);
} else { new |= old & ~(0xFF << 8*(addr&3));
tmp = *targ;
*targ = *Rm;
*Rd = tmp;
} }
unlock(&seg->lock); } while(!cas(targ, old, new));
if(instr & fB) {
old >>= 8*(addr&3);
old &= 0xFF;
}
*Rd = old;
segunlock(seg); segunlock(seg);
} }
@ -401,26 +406,24 @@ singleex(u32int instr)
invalid(instr); invalid(instr);
addr = evenaddr(*Rn, 3); addr = evenaddr(*Rn, 3);
if(instr & fS) { if(instr & fS) {
clrex();
targ = vaddr(addr, 4, &seg); targ = vaddr(addr, 4, &seg);
lock(&seg->lock);
P->excl = seg;
*Rd = *targ; *Rd = *targ;
P->lladdr = addr;
P->llval = *Rd;
segunlock(seg); segunlock(seg);
} else { } else {
Rm = P->R + (instr & 15); Rm = P->R + (instr & 15);
if(Rm == P->R + 15) if(Rm == P->R + 15)
invalid(instr); invalid(instr);
targ = vaddr(addr, 4, &seg); targ = vaddr(addr, 4, &seg);
if(canlock(&seg->lock)) {
unlock(&seg->lock); /*
*Rd = 1; * this is not quite correct as we will succeed even
} else if(P->excl != seg) { * if the value was modified and then restored to its
*Rd = 1; * original value but good enougth approximation for
} else { * libc's _tas(), _cas() and _xinc()/_xdec().
*targ = *Rm; */
*Rd = 0; *Rd = addr != P->lladdr || !cas(targ, P->llval, *Rm);
}
segunlock(seg); segunlock(seg);
clrex(); clrex();
} }
@ -429,12 +432,8 @@ singleex(u32int instr)
void void
clrex(void) clrex(void)
{ {
Segment *seg; P->lladdr = 0;
P->llval = 0;
seg = P->excl;
P->excl = nil;
if(seg != nil)
unlock(&seg->lock);
} }
static void static void

View file

@ -31,7 +31,10 @@ struct Process {
Ref *path; /* Ref + string data */ Ref *path; /* Ref + string data */
Segment *S[SEGNUM]; /* memory */ 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 R[16]; /* general purpose registers / PC (R15) */
u32int CPSR; /* status register */ u32int CPSR; /* status register */
@ -66,7 +69,6 @@ struct Segment {
Ref; Ref;
int flags; int flags;
RWLock rw; /* lock for SEGFLLOCK segments */ RWLock rw; /* lock for SEGFLLOCK segments */
Lock lock; /* atomic accesses */
u32int start, size; u32int start, size;
void *data; void *data;
Ref *dref; Ref *dref;