Me@0: /* Me@0: * Copyright 2010 OpenSourceCodeStewardshipFoundation Me@0: * Me@0: * Licensed under BSD Me@0: */ Me@0: Me@0: Me@0: Me@9: #include Me@0: #include Me@0: #include Me@9: #include Me@0: Me@0: #include "VMS.h" Me@0: Me@0: Me@0: Me@0: /*This code is animated by the virtual Master processor. Me@0: * Me@11: *Polls each sched slot exactly once, hands any requests made by a newly Me@11: * done slave to the "request handler" plug-in function Me@0: * Me@11: *Any slots that need a virt procr assigned are given to the "schedule" Me@11: * plug-in function, which tries to assign a virt procr (slave) to it. Me@0: * Me@11: *When all slots needing a processor have been given to the schedule plug-in, Me@11: * a fraction of the procrs successfully scheduled are put into the Me@11: * work queue, then a continuation of this function is put in, then the rest Me@11: * of the virt procrs that were successfully scheduled. Me@0: * Me@11: *The first thing the continuation does is busy-wait until the previous Me@11: * animation completes. This is because an (unlikely) continuation may Me@11: * sneak through queue before previous continuation is done putting second Me@11: * part of scheduled slaves in, which is the only race condition. Me@0: * Me@0: */ Me@0: Me@4: /*May 29, 2010 -- birth a Master during init so that first core loop to Me@11: * start running gets it and does all the stuff for a newly born -- Me@11: * from then on, will be doing continuation, but do suspension self Me@4: * directly at end of master loop Me@4: *So VMS__init just births the master virtual processor same way it births Me@4: * all the others -- then does any extra setup needed and puts it into the Me@4: * work queue. Me@4: *However means have to make masterEnv a global static volatile the same way Me@4: * did with workQ in core loop. -- for performance, put the Me@11: * jump to the core loop directly in here, and have it directly jump back. Me@4: */ Me@4: void masterLoop( void *initData, VirtProcr *masterPr ) Me@21: { Me@21: int slotIdx, numFilled, numInFirstChunk, filledSlotIdx; Me@21: VirtProcr *schedVirtPr; Me@4: SchedSlot *currSlot, **schedSlots, **filledSlots; Me@0: MasterEnv *masterEnv; Me@11: CASQueueStruc *workQ; Me@21: void *jmpPt, *stackPtrAddr, *framePtrAddr, *stillRunningAddr; Me@21: void *coreLoopFramePtr, *coreLoopStackPtr, *semanticEnv; Me@4: Me@0: SlaveScheduler slaveScheduler; Me@0: RequestHandler requestHandler; Me@0: Me@4: //this will run as the first virt processor in workQ, and will be a Me@4: // new born -- so will do all the GCC-generated allocating space on Me@4: // the stack owned by master virt procr -- and will run this last bit Me@4: // of setup code.. Me@4: masterPr->nextInstrPt = &&masterLoopStartPt; Me@0: Me@4: Me@4: masterLoopStartPt: Me@0: Me@4: //if another reference to same Master VirtProcr still going, busy-wait Me@4: //Could put this lower, but don't want to think about shared stack.. Me@21: while( _VMSMasterEnv->stillRunning ) /*busy wait*/ ; Me@4: //TODO: want to do busy-wait as assembly, to be sure stack not touched? Me@4: Me@4: //this is the only master running now, set flag again Me@21: _VMSMasterEnv->stillRunning = TRUE; Me@21: masterEnv = _VMSMasterEnv; Me@4: Me@4: //TODO: gdb -- check that a volatile _VMSMasterEnv and _VMSWorkQ means Me@4: // all these will be re-filled every time jump here.. Me@4: workQ = _VMSWorkQ; Me@0: requestHandler = masterEnv->requestHandler; Me@0: slaveScheduler = masterEnv->slaveScheduler; Me@4: schedSlots = masterEnv->schedSlots; Me@4: filledSlots = masterEnv->filledSlots; Me@11: masterPr = masterEnv->masterVirtPr; //post-jmp clobbered, re-load Me@21: semanticEnv = masterEnv->semanticEnv; Me@0: Me@0: //prepare for scheduling Me@4: masterEnv->numFilled = 0; Me@0: Me@21: //Poll each slot's Done flag -- slot 0 reserved for master, start at 1 Me@4: for( slotIdx = 1; slotIdx < NUM_SCHED_SLOTS; slotIdx++) Me@0: { Me@4: currSlot = schedSlots[ slotIdx ]; Me@0: Me@4: if( currSlot->workIsDone ) Me@0: { Me@4: currSlot->workIsDone = FALSE; Me@4: currSlot->needsProcrAssigned = TRUE; Me@0: Me@0: //process requests from slave to master Me@21: (*requestHandler)( currSlot->procrAssignedToSlot, semanticEnv ); Me@0: } Me@4: if( currSlot->needsProcrAssigned ) Me@4: { //give slot a new virt procr Me@21: schedVirtPr = Me@21: (*slaveScheduler)( semanticEnv ); Me@0: Me@21: if( schedVirtPr != NULL ) Me@21: { currSlot->procrAssignedToSlot = schedVirtPr; Me@21: schedVirtPr->schedSlot = currSlot; Me@4: Me@21: filledSlots[ masterEnv->numFilled ] = currSlot; Me@4: masterEnv->numFilled += 1; Me@4: Me@4: currSlot->needsProcrAssigned = FALSE; Me@0: } Me@0: } Me@0: } Me@0: Me@21: //put some scheduled slaves in, then Master continuation, then rest Me@21: //Adjust position of master such that it maintains close to a fixed Me@21: // ratio --> make NUM_CORES - 1 slots or fewer come after the master Me@21: numFilled = masterEnv->numFilled; Me@21: Me@21: int numPrecede = numFilled; Me@21: int numFollow = NUM_CORES - 1; Me@21: Me@21: if( numFilled < numFollow ) Me@21: { numFollow = numFilled; Me@21: numPrecede = 0; Me@21: } Me@21: else Me@21: { numPrecede -= numFollow; Me@21: } Me@21: Me@21: for( filledSlotIdx = 0; filledSlotIdx < numPrecede; filledSlotIdx++) Me@0: { Me@11: writeCASQ( filledSlots[ filledSlotIdx ]->procrAssignedToSlot, workQ ); Me@0: } Me@0: Me@0: //enqueue continuation of this loop Me@0: // note that After this enqueue, continuation might sneak through Me@21: writeCASQ( masterEnv->masterVirtPr, workQ ); Me@21: Me@21: for( filledSlotIdx = numPrecede; Me@21: filledSlotIdx < numFilled; Me@4: filledSlotIdx++) Me@0: { Me@11: writeCASQ( filledSlots[ filledSlotIdx ]->procrAssignedToSlot, workQ ); Me@0: } Me@0: Me@4: masterEnv->numFilled = 0; Me@4: Me@4: Me@4: //Save stack ptr and frame -- don't need to, take out later, but safe Me@4: // Also, wait to set stillRunning to FALSE until just before jump, to Me@21: // be safe -- although the two simulatneously animated MasterLoops Me@21: // are on different cores, so have different stacks, so no worries Me@21: // there. Me@21: //Restore CoreLoop's stack frame (and stack pointer, to be safe) Me@21: //TODO: cafefully verify don't need to force saving anything to stack Me@21: // before jumping back to core loop. Me@21: stackPtrAddr = &(masterPr->stackPtr); Me@21: framePtrAddr = &(masterPr->framePtr); Me@21: stillRunningAddr = &(_VMSMasterEnv->stillRunning); //when race condition Me@21: //arises, stillRunning is shared between the two cores both animating Me@21: // MasterLoop -- but those two cores have different esp & ebp, so safe Me@21: // to change stack and frame pointer here, without one messing up other Me@21: // one Me@21: Me@21: jmpPt = masterPr->coreLoopStartPt; Me@21: coreLoopFramePtr = masterPr->coreLoopFramePtr;//need this only Me@21: coreLoopStackPtr = masterPr->coreLoopStackPtr;//shouldn't need -- safety Me@21: Me@21: asm volatile("movl %0, %%eax; \ Me@21: movl %%esp, (%%eax); \ Me@21: movl %1, %%eax; \ Me@21: movl %%ebp, (%%eax); \ Me@21: movl %2, %%ebx; \ Me@21: movl %3, %%eax; \ Me@21: movl %4, %%esp; \ Me@21: movl %5, %%ebp; \ Me@21: movl $0x0, (%%ebx); \ Me@21: jmp %%eax " \ Me@21: /* outputs */ : "=g" (stackPtrAddr), "=g" (framePtrAddr), \ Me@21: "=g"(stillRunningAddr) \ Me@21: /* inputs */ : "g" (jmpPt), "g"(coreLoopStackPtr), "g"(coreLoopFramePtr)\ Me@21: /* clobber */ : "memory", "%eax", "%ebx", "%ecx", "%edx", "%edi", "%esi" \ Me@21: );//can probably make clobber list empty -- but safe for now Me@0: } Me@0: Me@0: