Me@0: /* Me@0: * Copyright 2010 OpenSourceCodeStewardshipFoundation Me@0: * Me@0: * Licensed under BSD Me@0: */ Me@0: Me@0: #include Me@0: #include Me@0: #include Me@0: Me@0: #include "VMS.h" Me@0: #include "Queue_impl/BlockingQueue.h" Me@0: Me@0: Me@0: /*Setup has two phases: Me@0: * 1) Semantic layer first calls init_VMS, which creates masterEnv, and puts Me@0: * the master work-unit into the work-queue Me@0: * 2) Semantic layer then does its own init, which creates the initial Me@0: * work-units inside the semantic layer, ready to schedule them when Me@0: * asked by the first run of the masterLoop. Me@0: * Me@0: *This part is bit weird because VMS really wants to be "always there", and Me@0: * have applications attach and detach.. for now, this VMS is part of Me@0: * the app, so the VMS system starts up as part of running the app. Me@0: * Me@0: *The semantic layer is fully isolated from the VMS internasl by Me@0: * making the semantic layer setup into a state that it's ready with its Me@0: * initial work-units, ready to schedule them to slaves when the masterLoop Me@0: * asks. Without this pattern, the semantic layer's setup would Me@0: * have to modify slaves directly to assign the initial work-units, and put Me@0: * them into the workQ itself, breaking the isolation completely. Me@0: * Me@0: * Me@0: *The semantic layer creates the initial work-unit(s), and adds its Me@0: * own environment data to masterEnv, and fills in the pointers to Me@0: * the requestHandler and slaveScheduler plug-in functions Me@0: * Me@0: *This allocates VMS data structures, populates the master VMSProc, Me@0: * and master environment, and returns the master environment to the semantic Me@0: * layer. Me@0: */ Me@0: //Global vars are all inside VMS.h Me@0: MasterEnv * Me@1: VMS__init( ) Me@1: { MasterEnv *masterEnv; Me@1: QueueStruc *workQ; Me@1: Me@0: //Make the central work-queue Me@1: _VMSWorkQ = makeQ(); Me@1: workQ = _VMSWorkQ; Me@0: Me@1: _VMSMasterEnv = malloc( sizeof(MasterEnv) ); Me@1: masterEnv = _VMSMasterEnv; Me@0: Me@0: create_master( masterEnv ); Me@0: Me@1: create_sched_slots( masterEnv ); Me@0: Me@1: masterEnv->schedSlots[0]->needsProcrAssigned = FALSE; //never checked Me@1: masterEnv->schedSlots[0]->workIsDone = FALSE; //never checked Me@1: masterEnv->schedSlots[0]->procrAssignedToSlot = masterEnv->masterVirtPr; Me@1: Me@1: //First core loop to start up gets this, which will schedule seed Pr Me@1: //TODO: debug: check address of masterVirtPr Me@1: writeQ( &(masterEnv->masterVirtPr), workQ ); Me@0: } Me@0: Me@0: Me@0: Me@1: /*Fill up the master VirtProcr data structure, which is already alloc'd Me@1: * in the masterEnv. Me@1: * The coreLoop treats master virt pr same as the slave virt processors Me@1: * Me@1: *The first time it runs, will jump to the function ptr so have to, in here, Me@1: * create the stack, which will be used by the plug-in functions, and set Me@1: * up __cdecl just like do for the other virtual processors. Me@0: */ Me@0: void Me@0: create_master( MasterEnv *masterEnv ) Me@1: { VirtProcr masterPr; Me@1: char * stackLocs, stackPtr; Me@0: Me@1: //TODO: debug this to be sure got addr of struct in masterEnv correctly Me@1: masterPr = &(masterEnv->masterVirtPr); Me@1: masterPr->initialData = masterEnv; Me@1: Me@1: masterPr->nextInstrPt = &masterLoop; Me@1: Me@1: //alloc stack locations, make stackPtr be the last addr in the locs, Me@1: // minus room for the two parameters. Put initData at stackPtr, Me@1: // animatingPr just above Me@1: stackLocs = malloc( 0x100000 ); //1 meg stack -- default Win thread's size Me@1: stackPtr = ( (char *)stackLocs + 0x100000 - 0x8 ); Me@1: masterPr->stackPtr = stackPtr; Me@1: masterPr->framePtr = stackPtr; Me@1: asm volatile("movl %0, %%esp; Me@1: movl %1, (%%esp); Me@1: movl %2, $0x4(%%esp); Me@1: movl %%esp, %%ebp; " /*framePtr in ebp never used*/ Me@1: /* outputs */ : Me@1: /* inputs */ : "g" (stackPtr), "g" (initData), "g" (animPr) Me@1: /* clobber */ : Me@1: ); Me@0: } Me@0: Me@0: void Me@1: create_sched_slots( MasterEnv *masterEnv ) Me@1: { SchedSlot *slots; Me@0: int i; Me@0: Me@1: slots = masterEnv->schedSlots; //TODO: make sure this is right Me@1: for( i = 0; i < NUM_SCHED_SLOTS; i++ ) Me@0: { Me@1: //Set state to mean "handling requests done, slot needs filling" Me@1: slots[i].workIsDone = FALSE; Me@1: slots[i].needsProcrAssigned = TRUE; Me@0: } Me@0: } Me@0: Me@0: /*Semantic layer calls this when it want the system to start running.. Me@0: * Me@0: *This creates the core loops, pins them to physical cores, gives them the Me@0: * pointer to the workQ, and starts them running. Me@0: */ Me@0: void Me@0: VMS__start() Me@0: { int retCode, coreIdx; Me@0: Me@0: //TODO: still just skeleton code -- figure out right way to do this Me@0: Me@0: //Create the PThread loops that take from work-queue, and start them Me@0: for( coreIdx=0; coreIdx < NUM_WORKERS; coreIdx++ ) Me@0: { Me@0: thdParams[coreIdx] = (ThdParams *)malloc( sizeof(ThdParams) ); Me@1: thdParams[coreIdx]->workQ = _VMSWorkQ; Me@0: thdParams[coreIdx]->id = coreIdx; Me@0: Me@0: //Now make and start thd.. the coreLoopThds entry Me@0: // has all the info needed to later stop the thread. Me@0: retCode = Me@0: pthread_create( &(coreLoopThds[coreIdx]), thdAttrs, &coreLoop, Me@0: (void *)(thdParams[coreIdx]) ); Me@0: if( retCode != 0 ) Me@0: { //error Me@0: printf("ERROR creating coreLoop %d, code: %d\n", coreIdx, retCode); Me@0: exit(-1); Me@0: } Me@0: Me@0: pinThdToCore( ); //figure out how to specify this.. Me@0: Me@0: startThd(); //look up PThread call to start the thread running, if it's Me@0: // not automatic Me@0: } Me@0: } Me@0: Me@0: /*there is a label inside this function -- save the addr of this label in Me@0: * the callingPr struc, as the pick-up point from which to start the next Me@0: * work-unit for that procr. If turns out have to save registers, then Me@0: * save them in the procr struc too. Then do assembly jump to the CoreLoop's Me@0: * "done with work-unit" label. The procr struc is in the request in the Me@0: * slave that animated the just-ended work-unit, so all the state is saved Me@0: * there, and will get passed along, inside the request handler, to the Me@0: * next work-unit for that procr. Me@0: */ Me@1: VMS__suspend_processor( VirtProcr *callingPr ) Me@1: { void *jmpPt; Me@0: Me@1: callingPr->nextInstrPt = &&ResumePt; Me@1: Me@1: //return ownership of the virt procr and sched slot to Master virt pr Me@1: callingPr->schedSlot->workIsDone = TRUE; Me@1: Me@1: jmpPt = callingPr->coreLoopStartPt; Me@1: Me@1: //put all regs in the clobber list to make sure GCC has saved all Me@1: // so safe to jump to core loop, where they *will* get clobbered Me@1: asm volatile("movl %%esp, %0; Me@1: movl %%ebp, %1; Me@1: jmp %2 " Me@1: /* outputs */ : "=m" (currPr->stackPtr), "=m" (currPr->framePtr) Me@1: /* inputs */ : "g" (jmpPt) Me@1: /* clobber */ : "memory", "%eax", "%ebx", "%ecx", "%edx", "%edi","%esi" Me@1: ); Me@1: Me@1: ResumePt: Me@0: return; Me@0: } Me@0: Me@0: Me@1: /*Create stack, then create __cdecl structure on it and put initialData and Me@1: * pointer to the new structure instance into the parameter positions on Me@1: * the stack Me@1: *Then put function pointer into nextInstrPt -- the stack is setup in std Me@1: * call structure, so jumping to function ptr is same as a GCC generated Me@1: * function call Me@1: *No need to save registers on old stack frame, because there's no old Me@1: * animator state to return to -- Me@1: * Me@1: */ Me@1: VirtProcr * Me@1: VMS__create_procr( VirtProcrFnPtr fnPtr, void *initialData ) Me@1: { VirtProcr newPr; Me@1: Me@1: newPr = malloc( sizeof(VirtProcr) ); Me@1: newPr->nextInstrPt = fnPtr; Me@1: newPr->initialData = initialData; Me@1: newPr->stackPtr = createNewStack(); Me@1: newPr->framePtr = newPr->stackPtr; Me@1: put params onto stack and setup __cdecl call structure Me@1: Me@1: return newPr; Me@1: } Me@1: Me@1: Me@0: /*The semantic virt procr is available in the request sent from the slave Me@0: * Me@0: * The request handler has to add the work-unit created to the semantic Me@0: * virtual processor the work-unit is a section of its time-line -- does this when create the Me@0: * work-unit -- means the procr data struc is available in the request sent Me@0: * from the slave, from which the new work-unit is generated.. Me@0: */ Me@1: inline void Me@1: VMS__add_request_to_slave( SlaveReqst req, VirtProcr callingPr ) Me@1: { Me@1: req->nextRequest = callingPr->requests; Me@1: callingPr->requests = req; Me@0: } Me@0: Me@0: Me@0: