#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
/*
* The hardware semaphores are strange. 64 per page, replicated 16 times
* per page, 1024 pages of them. Only the low bit is meaningful.
* Reading an unset semaphore sets the semaphore and returns the old value.
* Writing a semaphore sets the value, so writing 0 resets (clears) the semaphore.
*/
enum
{
SEMPERPG = 64, /* hardware semaphores per page */
NSEMPG = 1024,
ULOCKPG = 512,
};
struct
{
Lock lock; /* lock to allocate */
uchar bmap[NSEMPG]; /* allocation map */
int ulockpg; /* count of user lock available */
}semalloc;
Page lkpgheader[NSEMPG];
#define lhash(laddr) ((int)laddr>>2)&(((NSEMPG-ULOCKPG)*(BY2PG>>2))-1)
void
lockinit(void)
{
memset(semalloc.bmap, 0, sizeof(semalloc.bmap));
/*
* Initialise the system semaphore hardware
*/
memset(SBSEM, 0, (NSEMPG-ULOCKPG)*BY2PG);
semalloc.ulockpg = ULOCKPG;
}
/* Moral equivalent of newpage for pages of hardware locks */
Page*
lkpage(ulong va)
{
uchar *p, *top;
Page *pg;
int i;
lock(&semalloc.lock);
if(--semalloc.ulockpg < 0) {
semalloc.ulockpg++;
unlock(&semalloc.lock);
return 0;
}
top = &semalloc.bmap[NSEMPG];
for(p = semalloc.bmap; *p && p < top; p++)
;
if(p == top)
panic("lkpage");
*p = 1;
i = p-semalloc.bmap;
pg = &lkpgheader[i];
pg->pa = (ulong)((i*WD2PG) + SBSEM) & ~UNCACHED;
pg->va = va;
pg->ref = 1;
unlock(&semalloc.lock);
return pg;
}
void
lkpgfree(Page *pg)
{
uchar *p;
lock(&semalloc.lock);
p = &semalloc.bmap[((pg->pa|UNCACHED)-(ulong)SBSEM)/BY2PG];
if(!*p)
panic("lkpgfree");
*p = 0;
semalloc.ulockpg++;
unlock(&semalloc.lock);
}
void
lock(Lock *lk)
{
int *hwsem;
int i, hash;
hash = lhash(lk);
hwsem = (int*)SBSEM+hash;
i = 1000000;
for(;;) {
if((*hwsem & 1) == 0) {
if(lk->val)
*hwsem = 0;
else {
lk->val = 1;
*hwsem = 0;
lk->pc = getcallerpc(lk);
return;
}
}
while(lk->val && i)
i--;
if(i <= 0)
break;
}
print("lock loop %lux pc %lux held by pc %lux\n", lk, getcallerpc(lk), lk->pc);
dumpstack();
}
int
canlock(Lock *lk)
{
int *hwsem;
int i, hash;
hash = lhash(lk);
hwsem = (int*)SBSEM+hash;
for(;;) {
if((*hwsem & 1) == 0) {
if(lk->val)
*hwsem = 0;
else {
lk->val = 1;
*hwsem = 0;
lk->pc = getcallerpc(lk);
return 1;
}
}
if(lk->val)
return 0;
}
}
void
unlock(Lock *l)
{
l->pc = 0;
l->val = 0;
}