250 lines
4.3 KiB
C
250 lines
4.3 KiB
C
#include "u.h"
|
|
#include "../port/lib.h"
|
|
#include "mem.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "../port/error.h"
|
|
#include "edf.h"
|
|
|
|
long maxlockcycles;
|
|
long maxilockcycles;
|
|
long cumlockcycles;
|
|
long cumilockcycles;
|
|
uintptr maxlockpc;
|
|
uintptr maxilockpc;
|
|
|
|
struct
|
|
{
|
|
ulong locks;
|
|
ulong glare;
|
|
ulong inglare;
|
|
} lockstats;
|
|
|
|
static void
|
|
dumplockmem(char *tag, Lock *l)
|
|
{
|
|
uchar *cp;
|
|
int i;
|
|
|
|
iprint("%s: ", tag);
|
|
cp = (uchar*)l;
|
|
for(i = 0; i < 64; i++)
|
|
iprint("%2.2ux ", cp[i]);
|
|
iprint("\n");
|
|
}
|
|
|
|
void
|
|
lockloop(Lock *l, uintptr pc)
|
|
{
|
|
extern int panicking;
|
|
Proc *p;
|
|
|
|
if(panicking)
|
|
return;
|
|
|
|
p = l->p;
|
|
print("lock %#p loop key %#lux pc %#p held by pc %#p proc %lud\n",
|
|
l, l->key, pc, l->pc, p ? p->pid : 0);
|
|
dumpaproc(up);
|
|
if(p != nil)
|
|
dumpaproc(p);
|
|
}
|
|
|
|
int
|
|
lock(Lock *l)
|
|
{
|
|
int i;
|
|
uintptr pc;
|
|
|
|
pc = getcallerpc(&l);
|
|
|
|
lockstats.locks++;
|
|
if(up)
|
|
up->nlocks++; /* prevent being scheded */
|
|
if(tas(&l->key) == 0){
|
|
if(up)
|
|
up->lastlock = l;
|
|
l->pc = pc;
|
|
l->p = up;
|
|
l->m = MACHP(m->machno);
|
|
l->isilock = 0;
|
|
#ifdef LOCKCYCLES
|
|
l->lockcycles = -lcycles();
|
|
#endif
|
|
return 0;
|
|
}
|
|
if(up)
|
|
up->nlocks--;
|
|
|
|
lockstats.glare++;
|
|
for(;;){
|
|
lockstats.inglare++;
|
|
i = 0;
|
|
while(l->key){
|
|
if(conf.nmach < 2 && up && up->edf && (up->edf->flags & Admitted)){
|
|
/*
|
|
* Priority inversion, yield on a uniprocessor; on a
|
|
* multiprocessor, the other processor will unlock
|
|
*/
|
|
print("inversion %#p pc %#p proc %lud held by pc %#p proc %lud\n",
|
|
l, pc, up ? up->pid : 0, l->pc, l->p ? l->p->pid : 0);
|
|
up->edf->d = todget(nil); /* yield to process with lock */
|
|
}
|
|
if(i++ > 100000000){
|
|
i = 0;
|
|
lockloop(l, pc);
|
|
}
|
|
}
|
|
if(up)
|
|
up->nlocks++;
|
|
if(tas(&l->key) == 0){
|
|
if(up)
|
|
up->lastlock = l;
|
|
l->pc = pc;
|
|
l->p = up;
|
|
l->m = MACHP(m->machno);
|
|
l->isilock = 0;
|
|
#ifdef LOCKCYCLES
|
|
l->lockcycles = -lcycles();
|
|
#endif
|
|
return 1;
|
|
}
|
|
if(up)
|
|
up->nlocks--;
|
|
}
|
|
}
|
|
|
|
void
|
|
ilock(Lock *l)
|
|
{
|
|
ulong x;
|
|
uintptr pc;
|
|
|
|
pc = getcallerpc(&l);
|
|
lockstats.locks++;
|
|
|
|
x = splhi();
|
|
if(tas(&l->key) != 0){
|
|
lockstats.glare++;
|
|
/*
|
|
* Cannot also check l->pc, l->m, or l->isilock here
|
|
* because they might just not be set yet, or
|
|
* (for pc and m) the lock might have just been unlocked.
|
|
*/
|
|
for(;;){
|
|
lockstats.inglare++;
|
|
splx(x);
|
|
while(l->key)
|
|
;
|
|
x = splhi();
|
|
if(tas(&l->key) == 0)
|
|
goto acquire;
|
|
}
|
|
}
|
|
acquire:
|
|
m->ilockdepth++;
|
|
if(up)
|
|
up->lastilock = l;
|
|
l->sr = x;
|
|
l->pc = pc;
|
|
l->p = up;
|
|
l->m = MACHP(m->machno);
|
|
l->isilock = 1;
|
|
#ifdef LOCKCYCLES
|
|
l->lockcycles = -lcycles();
|
|
#endif
|
|
}
|
|
|
|
int
|
|
canlock(Lock *l)
|
|
{
|
|
if(up)
|
|
up->nlocks++;
|
|
if(tas(&l->key)){
|
|
if(up)
|
|
up->nlocks--;
|
|
return 0;
|
|
}
|
|
|
|
if(up)
|
|
up->lastlock = l;
|
|
l->pc = getcallerpc(&l);
|
|
l->p = up;
|
|
l->m = MACHP(m->machno);
|
|
l->isilock = 0;
|
|
#ifdef LOCKCYCLES
|
|
l->lockcycles = -lcycles();
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
unlock(Lock *l)
|
|
{
|
|
#ifdef LOCKCYCLES
|
|
l->lockcycles += lcycles();
|
|
cumlockcycles += l->lockcycles;
|
|
if(l->lockcycles > maxlockcycles){
|
|
maxlockcycles = l->lockcycles;
|
|
maxlockpc = l->pc;
|
|
}
|
|
#endif
|
|
if(l->key == 0)
|
|
print("unlock(%#p): not locked: pc %#p\n",
|
|
l, getcallerpc(&l));
|
|
if(l->isilock)
|
|
print("unlock(%#p) of ilock: pc %#p, held by %#p\n",
|
|
l, getcallerpc(&l), l->pc);
|
|
if(l->p != up){
|
|
print("unlock(%#p): up changed: pc %#p, acquired at pc %#p, lock p %#p, unlock up %#p\n",
|
|
l, getcallerpc(&l), l->pc, l->p, up);
|
|
dumpaproc(l->p);
|
|
dumpaproc(up);
|
|
}
|
|
l->m = nil;
|
|
coherence();
|
|
l->key = 0;
|
|
|
|
if(up && --up->nlocks == 0 && up->delaysched && islo()){
|
|
/*
|
|
* Call sched if the need arose while locks were held
|
|
* But, don't do it from interrupt routines, hence the islo() test
|
|
*/
|
|
sched();
|
|
}
|
|
}
|
|
|
|
uintptr ilockpcs[0x100] = { [0xff] = 1 };
|
|
static int n;
|
|
|
|
void
|
|
iunlock(Lock *l)
|
|
{
|
|
ulong sr;
|
|
|
|
#ifdef LOCKCYCLES
|
|
l->lockcycles += lcycles();
|
|
cumilockcycles += l->lockcycles;
|
|
if(l->lockcycles > maxilockcycles){
|
|
maxilockcycles = l->lockcycles;
|
|
maxilockpc = l->pc;
|
|
}
|
|
if(l->lockcycles > 2400)
|
|
ilockpcs[n++ & 0xff] = l->pc;
|
|
#endif
|
|
if(l->key == 0)
|
|
print("iunlock(%#p): not locked: pc %#p\n", l, getcallerpc(&l));
|
|
if(!l->isilock)
|
|
print("iunlock(%#p) of lock: pc %#p, held by %#p\n", l, getcallerpc(&l), l->pc);
|
|
if(islo())
|
|
print("iunlock(%#p) while lo: pc %#p, held by %#p\n", l, getcallerpc(&l), l->pc);
|
|
|
|
sr = l->sr;
|
|
l->m = nil;
|
|
coherence();
|
|
l->key = 0;
|
|
m->ilockdepth--;
|
|
if(up)
|
|
up->lastilock = nil;
|
|
splx(sr);
|
|
}
|