This appendix contains complete, working source code for the HLS proportional share scheduler. Its purpose is to provide a concrete example of code using the scheduling primitives presented in Chapters 4 and 9 , and in Appendix B.
/*++ Copyright (c) 2000, 2001 Dept. of Computer Science, University of Virginia Module Name: hls_sched_ps.c Abstract: Uniprocessor proportional share scheduler, based on SFQ and BVT. Author: John Regehr 14-Feb-2000 Environment: Revision History: --*//* * static per-instance data must live in this struct */structPS_INSTANCE_DATA {structHLS_VPROC TVP;intPSInitState; SCHAR DefaultPri;structHLS_SCHED_INSTANCE *Self;intNumThreads; LIST_ENTRY AllThreads; ULONG TimeIncrement; };/* * per-bottom-VPROC data lives in this struct */structPS_UD { WNT_TIME AVT, StartTime; ULONG Warp, Share;structHLS_VPROC *udvp; LIST_ENTRY AllThreadsEntry; };/* * utility functions to hide null pointers from the rest of the scheduler */// top virtual processor to proportional share instance datastaticstructPS_INSTANCE_DATA *TVPtoPSID (structHLS_VPROC *tvp) {structPS_INSTANCE_DATA *id; id = (structPS_INSTANCE_DATA *)tvp->BottomSched->SchedData;returnid; }// scheduler instance pointer to PS instance datastaticstructPS_INSTANCE_DATA *SItoPSID (structHLS_SCHED_INSTANCE *si) {structPS_INSTANCE_DATA *id; id = (structPS_INSTANCE_DATA *)si->SchedData;returnid; }// bottom VP to PS instance datastaticstructPS_INSTANCE_DATA *BVPtoPSID (structHLS_VPROC *bvp) {structPS_INSTANCE_DATA *id; id = (structPS_INSTANCE_DATA *)bvp->TopSched->SchedData;returnid; }// bottom VP to per-VP datastaticstructPS_UD *BVPtoPSUD (structHLS_VPROC *bvp) {structPS_UD *ud; ud = (structPS_UD *)bvp->UpperData;returnud; }// generic message pointer to PS-specific datastaticstructHLS_RR_MSG *MsgToRRMsg (MsgHandle Context) {structHLS_RR_MSG *p = (structHLS_RR_MSG *) Context;returnp; }/* * main body of scheduler: HLS callbacks and support functions */staticvoidRegisterPS (structPS_INSTANCE_DATA *Inst) { Inst->TVP.TopSched->CB->B_RegisterVP (Inst->TVP.TopSched, Inst->Self, &Inst->TVP);if(strncmp (Inst->TVP.TopSched->Type, "priority", 8) == 0) {structHLS_RR_MSG NewMsg; HLS_STATUS status; NewMsg.Type = RR_MSG_SETPRI; NewMsg.Pri = Inst->DefaultPri; status = Inst->TVP.TopSched->CB->B_Msg (NULL, &Inst->TVP, (MsgHandle)&NewMsg); } }staticvoidMakeItSo (structPS_INSTANCE_DATA *Inst, WNT_TIME Now);staticHLS_STATUS PS_B_CallbackRegisterVP (structHLS_SCHED_INSTANCE *Parent,structHLS_SCHED_INSTANCE *Child,structHLS_VPROC *bvp) {structPS_INSTANCE_DATA *Inst = BVPtoPSID (bvp); bvp->UpperData = (TDHandle) hls_malloc (sizeof(structPS_UD)); {structPS_UD *ud = (structPS_UD *) bvp->UpperData; ud->udvp = bvp; ud->Warp = 0; ud->AVT = 0; ud->Share = 0; InsertTailList(&Inst->AllThreads, &ud->AllThreadsEntry); } bvp->State = VP_Waiting; bvp->Proc = NO_PROC;if(Inst->NumThreads == 0) { RegisterPS (Inst); HLSSetTimer (-(10*HLS_MsToNT), Inst->Self); } Inst->NumThreads++; { WNT_TIME Now = HLSGetCurrentTime (); MakeItSo (Inst, Now); }returnHLS_SUCCESS; }staticvoidPS_B_CallbackUnregisterVP (structHLS_VPROC *bvp) {structPS_INSTANCE_DATA *Inst = BVPtoPSID (bvp);structPS_UD *ud = BVPtoPSUD (bvp); Inst->NumThreads--;if(Inst->NumThreads == 0) { Inst->TVP.TopSched->CB->B_UnregisterVP (&Inst->TVP); } RemoveEntryList (&ud->AllThreadsEntry); hls_free ((void*)bvp->UpperData); }staticvoidGrantIt (structPS_INSTANCE_DATA *Inst) {structHLS_VPROC *bvp;structPS_UD *ud;/* * this will have already been set to the runnable bottom VP with * the earliest deadline */bvp = Inst->TVP.BottomData; ud = BVPtoPSUD (bvp); bvp->Proc = Inst->TVP.Proc; bvp->State = VP_Running; bvp->BottomSched->CB->T_VP_Grant (bvp); ud->StartTime = HLSGetCurrentTime (); }/* * update actual virtual time of a child VP */staticvoidUpdateAVT (structPS_UD *ud, WNT_TIME Now) { WNT_TIME RunTime; RunTime = Now - ud->StartTime;if(RunTime < 0) { RunTime = 0; } ud->AVT += 1 + (RunTime / ud->Share); }staticvoidPS_T_CallbackRevoke (structHLS_VPROC *tvp) {structPS_INSTANCE_DATA *Inst = TVPtoPSID (tvp); WNT_TIME Now = HLSGetCurrentTime ();structHLS_VPROC *Current = tvp->BottomData; UpdateAVT (BVPtoPSUD (Current), Now); Current->State = VP_Ready; Current->BottomSched->CB->T_VP_Revoke (Current); Current->Proc = NO_PROC; MakeItSo (Inst, Now); }/* * return effective virtual time for a child VP */staticWNT_TIME EVT (structPS_UD *t) {return(t->Warp > t->AVT) ? 0 : (t->AVT - t->Warp); }/* * return pointer to runnable virtual processor with smallest effective virtual time */staticstructHLS_VPROC *GetVPSmallestEVT (structPS_INSTANCE_DATA *Inst) {structPS_UD *min = NULL; PRLIST_ENTRY ListHead = &Inst->AllThreads; PRLIST_ENTRY NextEntry = ListHead->Flink;while(NextEntry != ListHead) {structPS_UD *ud = CONTAINING_RECORD (NextEntry,structPS_UD, AllThreadsEntry); NextEntry = NextEntry->Flink;if(ud->udvp->State != VP_Waiting && (!min || EVT (ud) < EVT (min))) { min = ud; } }return(min) ? min->udvp : NULL; }staticvoidPS_T_CallbackGrant (structHLS_VPROC *tvp) {structPS_INSTANCE_DATA *Inst = TVPtoPSID (tvp); tvp->BottomData = GetVPSmallestEVT (Inst); GrantIt (Inst); }staticvoidMaybePreempt (structHLS_VPROC *Current, WNT_TIME Now) {if(Current->State == VP_Running) { UpdateAVT (BVPtoPSUD (Current), Now); Current->State = VP_Ready; Current->BottomSched->CB->T_VP_Revoke (Current); Current->Proc = NO_PROC; } }/* * generic reschedule */staticvoidMakeItSo (structPS_INSTANCE_DATA *Inst, WNT_TIME Now) {structHLS_VPROC *bvp = Inst->TVP.BottomData;structHLS_VPROC *Next;structHLS_VPROC *Current;if(bvp) {if(bvp->State == VP_Running) {structPS_UD *ud = BVPtoPSUD (bvp); UpdateAVT (ud, Now); ud->StartTime = Now; } }if(Inst->TVP.State != VP_Ready) { Next = GetVPSmallestEVT (Inst); }else{ Next = NULL; } Current = Inst->TVP.BottomData;if(Current) {if(Next) {if(Current != Next) { MaybePreempt (Current, Now); Inst->TVP.BottomData = Next; GrantIt (Inst); } }else{ MaybePreempt (Current, Now);if(Inst->TVP.State != VP_Ready) { Inst->TVP.TopSched->CB->B_VP_Release (&Inst->TVP); } Inst->TVP.BottomData = NULL; } }else{if(Next) { Inst->TVP.TopSched->CB->B_VP_Request (&Inst->TVP); }else{ Inst->TVP.BottomData = NULL;if(Inst->TVP.State == VP_Ready && !GetVPSmallestEVT(Inst)) { Inst->TVP.TopSched->CB->B_VP_Release (&Inst->TVP); } } } }/* * compute system virtual time - only used when a VP unblocks */staticWNT_TIME SVT (structPS_INSTANCE_DATA *Inst) {structPS_UD *min = NULL; PRLIST_ENTRY ListHead = &Inst->AllThreads; PRLIST_ENTRY NextEntry = ListHead->Flink;while(NextEntry != ListHead) {structPS_UD *ud = CONTAINING_RECORD (NextEntry,structPS_UD, AllThreadsEntry); NextEntry = NextEntry->Flink;if(ud->udvp->State != VP_Waiting && (!min || ud->AVT < min->AVT)) { min = ud; } }return(min) ? min->AVT : 0; }staticvoidPS_B_CallbackRequest (structHLS_VPROC *bvp) {structPS_UD *ud = BVPtoPSUD (bvp);structPS_INSTANCE_DATA *Inst = BVPtoPSID (bvp); WNT_TIME Now = HLSGetCurrentTime (); WNT_TIME tSVT; tSVT = SVT (Inst); ud->AVT = max (ud->AVT, tSVT); bvp->State = VP_Ready; MakeItSo (Inst, Now); }staticvoidPS_B_CallbackRelease (structHLS_VPROC *bvp) {structPS_INSTANCE_DATA *Inst = BVPtoPSID (bvp); WNT_TIME Now = HLSGetCurrentTime ();if(bvp->State == VP_Running) { UpdateAVT (BVPtoPSUD(bvp), Now); } bvp->State = VP_Waiting; bvp->Proc = NO_PROC; MakeItSo (Inst, Now); }staticvoidPS_I_TimerCallback (structHLS_SCHED_INSTANCE *Self) {structPS_INSTANCE_DATA *Inst = SItoPSID (Self); WNT_TIME Now = HLSGetCurrentTime ();if(Inst->NumThreads == 0) {return; } MakeItSo (Inst, Now); HLSSetTimer (-(10*HLS_MsToNT), Inst->Self); }staticHLS_STATUS PS_B_CallbackMsg (structHLS_SCHED_INSTANCE *InstArg,structHLS_VPROC *bvp, MsgHandle Msg) {structPS_INSTANCE_DATA *Inst;structHLS_RR_MSG *m; HLS_STATUS status;if(bvp) { Inst = BVPtoPSID (bvp); }else{ Inst = SItoPSID (InstArg); } m = MsgToRRMsg (Msg);switch(m->Type) {caseRR_MSG_SETDEFPRI: Inst->DefaultPri = m->Pri; status = HLS_SUCCESS;break;caseRR_MSG_SETPRI: {structPS_UD *ud = BVPtoPSUD (bvp); ud->Share = m->Pri * 10;if(ud->Share == 0) { ud->Share = 10; } status = HLS_SUCCESS; }break;caseRR_MSG_SETRR: status = HLS_SUCCESS;break;caseRR_MSG_SETSHARE: {structPS_UD *ud = BVPtoPSUD (bvp); ud->Warp = m->Warp; ud->Share = m->Share; status = HLS_SUCCESS; }break;caseRR_MSG_NULL: status = HLS_SUCCESS;break;default: status = HLS_INVALID_PARAMETER; }returnstatus; }staticvoidPS_I_Init (structHLS_SCHED_INSTANCE *Self,structHLS_SCHED_INSTANCE *Parent) { Self->SchedData = (SDHandle) hls_malloc (sizeof(structPS_INSTANCE_DATA)); {structPS_INSTANCE_DATA *Inst = (structPS_INSTANCE_DATA *)Self->SchedData; InitializeListHead (&Inst->AllThreads); Inst->PSInitState = 1; Inst->NumThreads = 0; Inst->TimeIncrement = KeQueryTimeIncrement (); Inst->TVP.State = VP_Waiting; Inst->TVP.BottomData = NULL; Inst->TVP.BottomSched = Self; Inst->TVP.TopSched = Parent; strcpy (Self->Type, "priority"); Inst->DefaultPri = -1; Inst->Self = Self; } }staticvoidPS_I_Deinit (structHLS_SCHED_INSTANCE *Self) { hls_free ((void*)Self->SchedData); }/* * this structure makes callbacks available to the rest of the system */structHLS_CALLBACKS PS_CB = { "PS", PS_B_CallbackRegisterVP, PS_B_CallbackUnregisterVP, PS_B_CallbackRequest, PS_B_CallbackRelease, PS_B_CallbackMsg, PS_T_CallbackGrant, PS_T_CallbackRevoke, PS_I_Init, PS_I_Deinit, PS_I_TimerCallback, };