Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#define _LOCK_EXTENSION
#include <stdlib.h>
#include <string.h>
#include "../plan9/sys9.h"
#include <lock.h>

enum
{
        Pagesize        = 4096,
        Semperpg        = Pagesize/(16*sizeof(unsigned int)),
        Lockaddr        = 0x60000000,

        POWER           = 0x320,
        MAGNUM          = 0x330,
        MAGNUMII        = 0x340,
        R4K             = 0x500,
};

static  int arch;
extern  int C_3ktas(int*);
extern  int C_4ktas(int*);
extern  int C_fcr0(void);

static void
lockinit(void)
{
        int n;

        if(arch != 0)
                return; /* allow multiple calls */
        arch = C_fcr0();
        switch(arch) {
        case POWER:
                n = _SEGATTACH(0,  "lock", (void*)Lockaddr, Pagesize);
                if(n < 0) {
                        arch = MAGNUM;
                        break;
                }
                memset((void*)Lockaddr, 0, Pagesize);
                break;
        case MAGNUM:
        case MAGNUMII:
        case R4K:
                break;
        default:
                arch = R4K;
                break;
        }
        
}

void
lock(Lock *lk)
{
        int *hwsem;
        int hash;

retry:
        switch(arch) {
        case 0:
                lockinit();
                goto retry;
        case MAGNUM:
        case MAGNUMII:
                while(C_3ktas(&lk->val))
                        _SLEEP(0);
                return;
        case R4K:
                for(;;){
                        while(lk->val)
                                ;
                        if(C_4ktas(&lk->val) == 0)
                                return;
                }
                break;
        case POWER:
                /* Use low order lock bits to generate hash */
                hash = ((int)lk/sizeof(int)) & (Semperpg-1);
                hwsem = (int*)Lockaddr+hash;

                for(;;) {
                        if((*hwsem & 1) == 0) {
                                if(lk->val)
                                        *hwsem = 0;
                                else {
                                        lk->val = 1;
                                        *hwsem = 0;
                                        return;
                                }
                        }
                        while(lk->val)
                                ;
                }
        }       
}

int
canlock(Lock *lk)
{
        int *hwsem;
        int hash;

retry:
        switch(arch) {
        case 0:
                lockinit();
                goto retry;
        case MAGNUM:
        case MAGNUMII:
                if(C_3ktas(&lk->val))
                        return 0;
                return 1;
        case R4K:
                if(C_4ktas(&lk->val))
                        return 0;
                return 1;
        case POWER:
                /* Use low order lock bits to generate hash */
                hash = ((int)lk/sizeof(int)) & (Semperpg-1);
                hwsem = (int*)Lockaddr+hash;

                if((*hwsem & 1) == 0) {
                        if(lk->val)
                                *hwsem = 0;
                        else {
                                lk->val = 1;
                                *hwsem = 0;
                                return 1;
                        }
                }
                return 0;
        default:
                return 0;
        }       
}

void
unlock(Lock *lk)
{
        lk->val = 0;
}

int
tas(int *p)
{
        int *hwsem;
        int hash;

retry:
        switch(arch) {
        case 0:
                lockinit();
                goto retry;
        case MAGNUM:
        case MAGNUMII:
                return C_3ktas(p);
        case R4K:
                return C_4ktas(p);
        case POWER:
                /* Use low order lock bits to generate hash */
                hash = ((int)p/sizeof(int)) & (Semperpg-1);
                hwsem = (int*)Lockaddr+hash;

                if((*hwsem & 1) == 0) {
                        if(*p)
                                *hwsem = 0;
                        else {
                                *p = 1;
                                *hwsem = 0;
                                return 0;
                        }
                }
                return 1;
        default:
                return 0;
        }       
}