Me@0: /* Me@0: * Copyright 2010 OpenSourceCodeStewardshipFoundation Me@0: * Me@0: * Licensed under BSD Me@0: */ Me@0: Me@0: Me@0: 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@26: int slotIdx, numFilled, filledSlotIdx, masterHasBeenQueued; Me@21: VirtProcr *schedVirtPr; Me@4: SchedSlot *currSlot, **schedSlots, **filledSlots; Me@0: MasterEnv *masterEnv; Me@26: VMSQueueStruc *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@26: //The second time MasterVP comes out of queue, the first animation of Me@26: // it hasn't written the stackPtr and framePtr yet -- but the second Me@26: // animation has already had its stackPtr and framePtr set to the old Me@26: // value by the coreLoop. Fix this by writing the correct stack and Me@26: // frame pointers here, at which point they're correct in the first Me@26: // animation of MasterVP. Me@26: //TODO: remove writing stackPtr and framePtr at the bottom, for eff Me@26: stackPtrAddr = &(masterPr->stackPtr); Me@26: framePtrAddr = &(masterPr->framePtr); Me@26: Me@26: asm volatile("movl %0, %%eax; \ Me@26: movl %%esp, (%%eax); \ Me@26: movl %1, %%eax; \ Me@26: movl %%ebp, (%%eax); " Me@26: /* outputs */ : "=g" (stackPtrAddr), "=g" (framePtrAddr) \ Me@26: /* inputs */ : \ Me@26: /* clobber */ : "memory", "%eax", "%ebx" \ Me@26: ); Me@26: Me@26: 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@26: numFilled = 0; Me@26: masterHasBeenQueued = FALSE; Me@0: Me@21: //Poll each slot's Done flag -- slot 0 reserved for master, start at 1 Me@26: for( slotIdx = 0; 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@26: schedVirtPr->schedSlot = currSlot; Me@26: currSlot->needsProcrAssigned = FALSE; Me@4: Me@26: filledSlots[ numFilled ] = currSlot; Me@4: Me@26: writeVMSQ( schedVirtPr, workQ ); Me@29: numFilled += 1; Me@29: Me@26: if( numFilled == masterEnv->numToPrecede ) Me@26: { Me@26: writeVMSQ( masterEnv->masterVirtPr, workQ ); Me@26: masterHasBeenQueued = TRUE; Me@26: } Me@26: Me@0: } Me@0: } Me@0: } Me@0: Me@26: if( !masterHasBeenQueued ) Me@26: { Me@26: writeVMSQ( masterEnv->masterVirtPr, workQ ); Me@26: } Me@26: Me@26: //Adjust the number to precede, for next round -- assume rate of Me@26: // finishing work is stable -- which is a bad assumption! But, just Me@26: // want something working for the moment, look at dynamic behavior Me@26: // later Me@26: //TODO: look at dynamic behavior -- time-average numToPrecede or something Me@26: if( numFilled < NUM_CORES - 1 ) Me@26: { Me@29: masterEnv->numToPrecede = 1; Me@26: } Me@26: else Me@26: { masterEnv->numToPrecede = numFilled - NUM_CORES + 1; Me@26: } 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@30: 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: