2c93b33b5f
git-svn-id: svn://opensvn.adaptivecomputing.com/maui/trunk@143 3f5042e3-fb1d-0410-be18-d6ca2573e517
8952 lines
189 KiB
C
8952 lines
189 KiB
C
/* HEADER */
|
|
|
|
#include "moab.h"
|
|
#include "msched-proto.h"
|
|
|
|
/*
|
|
#define __MSEEKLONGRANGE 1
|
|
*/
|
|
|
|
extern msched_t MSched;
|
|
extern mnode_t *MNode[];
|
|
extern mstat_t MStat;
|
|
extern mpar_t MPar[];
|
|
extern mattrlist_t MAList;
|
|
extern mres_t *MRes[];
|
|
extern sres_t SRes[];
|
|
extern sres_t OSRes[];
|
|
|
|
extern mre_t MRE[];
|
|
|
|
extern mrm_t MRM[];
|
|
extern mam_t MAM[];
|
|
extern mckpt_t MCP;
|
|
|
|
extern mqos_t MQOS[];
|
|
extern mclass_t MClass[];
|
|
|
|
extern const char *MResType[];
|
|
extern const char *MJRType[];
|
|
extern const char *MCBType[];
|
|
extern const char *MNodeState[];
|
|
extern const char *MResAttr[];
|
|
extern const char *MResFlags[];
|
|
extern const char *MWeekDay[];
|
|
extern const char *MNResAttr[];
|
|
extern const char *MAttrO[];
|
|
extern const char *MXO[];
|
|
extern const char *MXOC[];
|
|
|
|
extern mlog_t mlog;
|
|
|
|
extern mrange_t MRange[MAX_MRANGE];
|
|
extern mx_t X;
|
|
|
|
|
|
|
|
|
|
int MResCreate(
|
|
|
|
int Type, /* I */
|
|
macl_t *ACL, /* I */
|
|
char *CAName,
|
|
unsigned long Flags,
|
|
mnalloc_t *NodeList, /* I */
|
|
long StartTime,
|
|
long EndTime,
|
|
int NodeCount, /* I */
|
|
int ProcCount, /* I */
|
|
char *RBaseName,
|
|
mres_t **ResP, /* O */
|
|
char *RegEx, /* I */
|
|
mcres_t *DRes) /* I */
|
|
|
|
{
|
|
int rindex;
|
|
|
|
mres_t *R;
|
|
|
|
char Message[MAX_MLINE];
|
|
|
|
enum MHoldReasonEnum Reason;
|
|
|
|
long WallTime;
|
|
|
|
const char *FName = "MResCreate";
|
|
|
|
DBG(3,fSTRUCT) DPrint("%s(%s,ACL,%s,%ld,NodeList,%ld,%ld,%d,%d,%s,ResP,'%s',DRes)\n",
|
|
FName,
|
|
MResType[Type],
|
|
(CAName != NULL) ?
|
|
CAName :
|
|
"NULL",
|
|
Flags,
|
|
StartTime,
|
|
EndTime,
|
|
NodeCount,
|
|
ProcCount,
|
|
RBaseName,
|
|
(RegEx != NULL) ? RegEx : "NULL");
|
|
|
|
if (ResP != NULL)
|
|
*ResP = NULL;
|
|
|
|
/* perform sanity check */
|
|
|
|
if (StartTime >= EndTime)
|
|
{
|
|
/* invalid timeframe */
|
|
|
|
DBG(0,fSTRUCT) DPrint("ERROR: invalid timeframe for reservation ID '%s' (starttime %ld >= endtime %ld)\n",
|
|
((ACL != NULL) && (ACL[0].Name[0] != '\0')) ?
|
|
ACL[0].Name :
|
|
"SYSTEM",
|
|
StartTime,
|
|
EndTime);
|
|
|
|
return(FAILURE);
|
|
}
|
|
|
|
if (MSched.Time > MCONST_EFFINF)
|
|
{
|
|
StartTime = MAX(StartTime,MSched.Time - MCONST_EFFINF);
|
|
}
|
|
|
|
/* find first available reservation slot */
|
|
|
|
for (rindex = 0;rindex < MAX_MRES;rindex++)
|
|
{
|
|
R = MRes[rindex];
|
|
|
|
if ((R == NULL) || (R->Name[0] == '\0') || (R->Name[0] == '\1'))
|
|
break;
|
|
} /* END for (rindex) */
|
|
|
|
if (rindex == MAX_MRES)
|
|
{
|
|
DBG(1,fSTRUCT) DPrint("ERROR: reservation overflow on reservation type %s for ID '%s'\n",
|
|
MResType[Type],
|
|
((ACL != NULL) && (ACL[0].Name[0] != '\0')) ?
|
|
ACL[0].Name :
|
|
"SYSTEM");
|
|
|
|
return(FAILURE);
|
|
}
|
|
|
|
MResInitialize(&MRes[rindex],NULL);
|
|
|
|
R = MRes[rindex];
|
|
|
|
R->Index = rindex;
|
|
|
|
MResSetAttr(R,mraACL,(void *)ACL,0,0);
|
|
|
|
MACLSet(R->CL,maRes,RBaseName,mcmpSEQ,nmNeutralAffinity,0,0);
|
|
MACLSet(R->ACL,maRes,RBaseName,mcmpSEQ,nmNeutralAffinity,0,0);
|
|
|
|
if ((NodeCount > 0) || (Flags & (1 << mrfStandingRes)))
|
|
{
|
|
/* is reservation is populated or is standing reservation */
|
|
|
|
MResGetRID(R,RBaseName,R->Name);
|
|
}
|
|
else
|
|
{
|
|
/* use base/restore checkpointed name */
|
|
|
|
strcpy(R->Name,RBaseName);
|
|
}
|
|
|
|
MTRAPRES(R,FName);
|
|
|
|
/* check allocation */
|
|
|
|
if ((CAName != NULL) &&
|
|
(CAName[0] != '\0') &&
|
|
strcmp(CAName,NONE))
|
|
{
|
|
MAcctAdd(CAName,&R->A);
|
|
|
|
/* prevent checkpointed rsv's from double creating AM reservations */
|
|
|
|
if (MSched.Iteration >= 0)
|
|
{
|
|
if (StartTime > MSched.Time)
|
|
WallTime = MIN(MAM[0].FlushInterval,EndTime - StartTime);
|
|
else
|
|
WallTime = MIN(MAM[0].FlushTime,EndTime) - MSched.Time;
|
|
|
|
if (MAMAllocRReserve(
|
|
&MAM[0],
|
|
R->Name,
|
|
StartTime,
|
|
CAName,
|
|
ProcCount,
|
|
NodeCount,
|
|
WallTime,
|
|
0,
|
|
MDEF_NODETYPE,
|
|
&Reason) == FAILURE)
|
|
{
|
|
DBG(1,fSCHED) DPrint("ALERT: cannot reserve allocation for %d procs for reservation %s\n",
|
|
ProcCount,
|
|
R->Name);
|
|
|
|
MResDestroy(&R);
|
|
|
|
return(FAILURE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
R->AllocResPending = TRUE;
|
|
}
|
|
}
|
|
|
|
MResSetAttr(R,mraType,(void **)&Type,0,0);
|
|
|
|
R->StartTime = StartTime;
|
|
R->EndTime = MIN(EndTime,MAX_MTIME - 1);
|
|
|
|
MREInsert(MRE,R->StartTime,R->EndTime,rindex,DRes,(MAX_MRES << 2));
|
|
|
|
MResSetAttr(R,mraFlags,(void **)&Flags,0,0);
|
|
MResSetAttr(R,mraHostExp,(void **)RegEx,mdfString,mSet);
|
|
|
|
if ((NodeList != NULL) && (NodeList[0].N != NULL))
|
|
{
|
|
R->PtIndex = NodeList[0].N->PtIndex;
|
|
}
|
|
else
|
|
{
|
|
R->PtIndex = -1;
|
|
}
|
|
|
|
if (DRes != NULL)
|
|
{
|
|
memcpy(&R->DRes,DRes,sizeof(R->DRes));
|
|
}
|
|
else
|
|
{
|
|
R->DRes.Procs = -1;
|
|
R->DRes.Mem = -1;
|
|
R->DRes.Disk = -1;
|
|
R->DRes.Swap = -1;
|
|
}
|
|
|
|
if (NodeList != NULL)
|
|
{
|
|
if (MResAllocate(R,NodeList) == FAILURE)
|
|
{
|
|
MResDestroy(&R);
|
|
|
|
return(FAILURE);
|
|
}
|
|
} /* END if (NodeList != NULL) */
|
|
|
|
if ((Type != mrtJob) && !(R->Flags & (1 << mrfStandingRes)))
|
|
{
|
|
sprintf(Message,"RESERVATIONCREATED: %s %s %s %ld %ld %ld %d\n",
|
|
R->Name,
|
|
MResType[Type],
|
|
RBaseName,
|
|
MSched.Time,
|
|
StartTime,
|
|
EndTime,
|
|
R->NodeCount);
|
|
|
|
MSysRegEvent(Message,0,0,1);
|
|
}
|
|
|
|
if (ResP != NULL)
|
|
*ResP = R;
|
|
|
|
if (R->Flags & (1 << mrfMeta))
|
|
MResAdjustGResUsage(R,1);
|
|
|
|
R->CTime = MSched.Time;
|
|
R->MTime = MSched.Time;
|
|
|
|
return(SUCCESS);
|
|
} /* END MResCreate() */
|
|
|
|
|
|
|
|
|
|
int MResAllocate(
|
|
|
|
mres_t *R, /* I (modified) */
|
|
mnalloc_t *NodeList) /* I */
|
|
|
|
{
|
|
int nindex;
|
|
|
|
int TC;
|
|
|
|
mnode_t *N;
|
|
|
|
const char *FName = "MResAllocate";
|
|
|
|
DBG(7,fSTRUCT) DPrint("%s(%s,NodeList)\n",
|
|
FName,
|
|
(R != NULL) ? R->Name : "NULL");
|
|
|
|
if ((R == NULL) || (NodeList == NULL))
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
R->NodeCount = 0;
|
|
R->TaskCount = 0;
|
|
R->AllocPC = 0;
|
|
|
|
/* link nodes to reservation */
|
|
|
|
for (nindex = 0;nindex < MAX_MNODE;nindex++)
|
|
{
|
|
if ((NodeList == NULL) || (NodeList[nindex].N == NULL))
|
|
break;
|
|
|
|
N = NodeList[nindex].N;
|
|
|
|
MTRAPNODE(N,FName);
|
|
|
|
TC = NodeList[nindex].TC;
|
|
|
|
R->NodeCount ++;
|
|
R->TaskCount ++;
|
|
R->AllocPC += (R->DRes.Procs == -1) ?
|
|
N->CRes.Procs :
|
|
(TC * R->DRes.Procs);
|
|
|
|
if (MResAddNode(R,N,TC,0) == FAILURE)
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
if (N->PtIndex != R->PtIndex)
|
|
R->PtIndex = 0;
|
|
} /* END for (nindex) */
|
|
|
|
if (nindex == MAX_MNODE)
|
|
{
|
|
DBG(1,fSTRUCT) DPrint("ERROR: node overflow in %s\n",
|
|
FName);
|
|
}
|
|
|
|
return(SUCCESS);
|
|
} /* END MResAllocate() */
|
|
|
|
|
|
|
|
|
|
int MResAdjust(
|
|
|
|
mres_t *RS, /* I (optional) */
|
|
long StartTime, /* I (optional) */
|
|
int TaskCount) /* I (optional) */
|
|
|
|
{
|
|
mres_t *R;
|
|
|
|
int rindex;
|
|
long BestStartTime;
|
|
|
|
long Delta;
|
|
|
|
char NodeMap[MAX_MNODE];
|
|
|
|
mrange_t GRange[MAX_MRANGE];
|
|
|
|
mnodelist_t MNodeList;
|
|
|
|
mjob_t tmpJ;
|
|
mreq_t tmpRQ;
|
|
|
|
macl_t tmpCL[MAX_MACL];
|
|
macl_t tmpACL[MAX_MACL];
|
|
|
|
mnalloc_t tmpNodeList[MAX_MNODE + 1];
|
|
|
|
mjob_t *J;
|
|
|
|
const char *FName = "MResAdjust";
|
|
|
|
DBG(2,fSCHED) DPrint("%s(%s,%ld,%d)\n",
|
|
FName,
|
|
(RS != NULL) ? RS->Name : "NULL",
|
|
StartTime,
|
|
TaskCount);
|
|
|
|
J = &tmpJ;
|
|
|
|
MJobMkTemp(J,&tmpRQ,tmpACL,tmpCL,tmpNodeList,NULL);
|
|
|
|
for (rindex = 0;rindex < MAX_MRES;rindex++)
|
|
{
|
|
R = MRes[rindex];
|
|
|
|
if ((R == NULL) || (R->Name[0] == '\0'))
|
|
break;
|
|
|
|
if (R->Name[0] == '\1')
|
|
continue;
|
|
|
|
if (RS == NULL)
|
|
{
|
|
if (!(R->Flags & (1 << mrfMeta)) ||
|
|
!(R->Flags & (1 << mrfTimeFlex)))
|
|
{
|
|
/* reservation is not adjustable */
|
|
|
|
continue;
|
|
}
|
|
|
|
if (R->StartTime <= MSched.Time)
|
|
{
|
|
/* reservation is already active */
|
|
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (R != RS)
|
|
{
|
|
/* reservation not specified */
|
|
|
|
continue;
|
|
}
|
|
} /* END else (RS == NULL) */
|
|
|
|
DBG(1,fCORE) DPrint("INFO: evaluating reservation %s\n",
|
|
R->Name);
|
|
|
|
/* maintain existing reservation */
|
|
|
|
MResToJob(R,J);
|
|
|
|
MJobAddAccess(J,R->Name);
|
|
|
|
if (StartTime > 0)
|
|
BestStartTime = StartTime;
|
|
else
|
|
BestStartTime = R->StartTime;
|
|
|
|
if (TaskCount > 0)
|
|
MReqSetAttr(J,J->Req[0],mrqaTCReqMin,(void **)&TaskCount,mdfInt,mSet);
|
|
|
|
if (MJobGetRange(
|
|
J,
|
|
J->Req[0],
|
|
&MPar[0],
|
|
MSched.Time,
|
|
GRange,
|
|
NULL,
|
|
NULL,
|
|
NodeMap,
|
|
(1 << rtcStartEarliest),
|
|
NULL) == FAILURE)
|
|
{
|
|
/* cannot locate available resources */
|
|
|
|
continue;
|
|
}
|
|
|
|
if (StartTime > 0)
|
|
{
|
|
if (GRange[0].StartTime != StartTime)
|
|
{
|
|
DBG(2,fSCHED) DPrint("INFO: cannot obtain desired starttime (%ld != %ld)\n",
|
|
GRange[0].StartTime,
|
|
StartTime);
|
|
|
|
return(FAILURE);
|
|
}
|
|
}
|
|
else if (GRange[0].StartTime >= BestStartTime)
|
|
{
|
|
/* new reservation is not earlier */
|
|
|
|
continue;
|
|
}
|
|
|
|
BestStartTime = GRange[0].StartTime;
|
|
|
|
/* select resources */
|
|
|
|
if (MJobGetRange(
|
|
J,
|
|
J->Req[0],
|
|
&MPar[0],
|
|
BestStartTime,
|
|
GRange,
|
|
MNodeList,
|
|
NULL,
|
|
NodeMap,
|
|
0,
|
|
NULL) == FAILURE)
|
|
{
|
|
/* cannot locate new resources */
|
|
|
|
continue;
|
|
}
|
|
|
|
/* NOTE: must adjust global MRE table */
|
|
|
|
MResDeallocateResources(R);
|
|
|
|
MRERelease(MRE,R->Index,(MAX_MRES << 2));
|
|
|
|
Delta = R->StartTime - BestStartTime;
|
|
|
|
R->StartTime -= Delta;
|
|
R->EndTime -= Delta;
|
|
|
|
MREInsert(
|
|
MRE,
|
|
R->StartTime,
|
|
R->EndTime,
|
|
R->Index,
|
|
&R->DRes,
|
|
(MAX_MRES << 2));
|
|
|
|
if (MResAllocate(R,(mnalloc_t *)MNodeList[0]) == FAILURE)
|
|
{
|
|
DBG(1,fCORE) DPrint("ALERT: cannot adjust reservation %s (reservation empty)\n",
|
|
R->Name);
|
|
|
|
continue;
|
|
}
|
|
} /* END for (rindex) */
|
|
|
|
return(SUCCESS);
|
|
} /* END MResAdjust() */
|
|
|
|
|
|
|
|
|
|
int MResSetAttr(
|
|
|
|
mres_t *R, /* I (modified) */
|
|
enum MResAttrEnum AIndex, /* I */
|
|
void *AVal, /* I */
|
|
int Format, /* I */
|
|
int Mode) /* I */
|
|
|
|
{
|
|
int nindex;
|
|
int sindex;
|
|
|
|
mnode_t *N;
|
|
|
|
long tmpStartTime;
|
|
long tmpDuration = -1;
|
|
|
|
mre_t *RE;
|
|
|
|
if (R == NULL)
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
/* NOTE: Mode: 0: == -1: -= +1: += */
|
|
|
|
switch(AIndex)
|
|
{
|
|
case mraACL:
|
|
|
|
if (AVal == NULL)
|
|
{
|
|
R->ACL[0].Type = maNONE;
|
|
}
|
|
else if (Format != mdfString)
|
|
{
|
|
memcpy(R->ACL,AVal,sizeof(R->ACL));
|
|
}
|
|
else if (strcmp((char *)AVal,NONE) != 0)
|
|
{
|
|
MACLLoadConfigLine(R->ACL,(char *)AVal);
|
|
} /* END if (strcmp((char *)AVal,NONE) != 0) */
|
|
|
|
break;
|
|
|
|
case mraAAccount:
|
|
|
|
if (Format == mdfString)
|
|
{
|
|
if (strcmp((char *)AVal,NONE))
|
|
{
|
|
if (MAcctAdd((char *)AVal,&R->A) == FAILURE)
|
|
{
|
|
DBG(1,fCKPT) DPrint("ALERT: cannot add account %s for reservation %s\n",
|
|
(char *)AVal,
|
|
R->Name);
|
|
|
|
return(FAILURE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
R->A = (mgcred_t *)AVal;
|
|
}
|
|
|
|
break;
|
|
|
|
case mraAGroup:
|
|
|
|
if (Format == mdfString)
|
|
{
|
|
if (strcmp((char *)AVal,NONE))
|
|
{
|
|
if (MGroupAdd((char *)AVal,&R->G) == FAILURE)
|
|
{
|
|
DBG(1,fCKPT) DPrint("ALERT: cannot add group %s for reservation %s\n",
|
|
(char *)AVal,
|
|
R->Name);
|
|
|
|
return(FAILURE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
R->G = (mgcred_t *)AVal;
|
|
}
|
|
|
|
break;
|
|
|
|
case mraAUser:
|
|
|
|
if (Format == mdfString)
|
|
{
|
|
if (strcmp((char *)AVal,NONE))
|
|
{
|
|
if (MUserAdd((char *)AVal,&R->U) == FAILURE)
|
|
{
|
|
DBG(1,fCKPT) DPrint("ALERT: cannot add user %s for reservation %s\n",
|
|
(char *)AVal,
|
|
R->Name);
|
|
|
|
return(FAILURE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
R->U = (mgcred_t *)AVal;
|
|
}
|
|
|
|
break;
|
|
|
|
case mraFlags:
|
|
|
|
if (Format == mdfString)
|
|
R->Flags = strtol((char *)AVal,NULL,0);
|
|
else
|
|
R->Flags = *(long *)AVal;
|
|
|
|
break;
|
|
|
|
case mraHostExp:
|
|
|
|
if ((AVal == NULL) || !strcmp((char *)AVal,NONE))
|
|
{
|
|
MUFree(&R->RegEx);
|
|
|
|
break;
|
|
}
|
|
|
|
if ((R->RegEx == NULL) &&
|
|
((R->RegEx = (char *)calloc(1,strlen((char *)AVal) + 1)) == NULL))
|
|
{
|
|
break;
|
|
}
|
|
|
|
MUStrCpy(R->RegEx,(char *)AVal,strlen((char *)AVal) + 1);
|
|
|
|
break;
|
|
|
|
case mraMaxTasks:
|
|
|
|
if (Format == mdfString)
|
|
R->MaxTasks = (int)strtol((char *)AVal,NULL,0);
|
|
else
|
|
R->MaxTasks = *(int *)AVal;
|
|
|
|
break;
|
|
|
|
case mraName:
|
|
|
|
MUStrCpy(R->Name,(char *)AVal,sizeof(R->Name));
|
|
|
|
break;
|
|
|
|
case mraNodeCount:
|
|
|
|
if (Format == mdfString)
|
|
R->NodeCount = (int)strtol((char *)AVal,NULL,0);
|
|
else
|
|
R->NodeCount = *(int *)AVal;
|
|
|
|
break;
|
|
|
|
case mraNodeList:
|
|
|
|
{
|
|
int NIndex;
|
|
int nindex;
|
|
|
|
int NCount;
|
|
|
|
mnode_t *N;
|
|
mnode_t *NPtr;
|
|
|
|
mnalloc_t *NL;
|
|
|
|
if (AVal == NULL)
|
|
return(FAILURE);
|
|
|
|
/* assume ordered list */
|
|
|
|
NL = (mnalloc_t *)AVal;
|
|
|
|
NIndex = 0;
|
|
|
|
if (NL[0].N == NULL)
|
|
{
|
|
NPtr = NULL;
|
|
NCount = 0;
|
|
}
|
|
else
|
|
{
|
|
NPtr = NL[0].N;
|
|
NCount = NL[0].TC;
|
|
}
|
|
|
|
/* locate nodes */
|
|
|
|
for (nindex = 0;nindex < MAX_MNODE;nindex++)
|
|
{
|
|
N = MNode[nindex];
|
|
|
|
if ((N == NULL) || (N->Name[0] == '\0'))
|
|
break;
|
|
|
|
if (N->Name[0] == '\1')
|
|
continue;
|
|
|
|
switch(Mode)
|
|
{
|
|
case -1:
|
|
|
|
if (NPtr == N)
|
|
MResAddNode(R,N,NCount,Mode);
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
if (NPtr == N)
|
|
MResAddNode(R,N,NCount,Mode);
|
|
else
|
|
MResAddNode(R,N,0,Mode);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
if (NPtr == N)
|
|
MResAddNode(R,N,NCount,Mode);
|
|
|
|
break;
|
|
} /* END switch(Mode) */
|
|
|
|
if (N == NPtr)
|
|
{
|
|
NIndex++;
|
|
|
|
if (NL[NIndex].N == NULL)
|
|
break;
|
|
|
|
NPtr = NL[NIndex].N;
|
|
NCount = NL[NIndex].TC;
|
|
}
|
|
} /* END for (nindex) */
|
|
} /* END BLOCK */
|
|
|
|
break;
|
|
|
|
case mraResources:
|
|
|
|
if (Format == mdfString)
|
|
{
|
|
memset(&R->DRes,0,sizeof(R->DRes));
|
|
|
|
R->DRes.Procs = -1;
|
|
|
|
MUCResFromString(&R->DRes,(char *)AVal);
|
|
}
|
|
else
|
|
{
|
|
memcpy(&R->DRes,(void *)AVal,sizeof(R->DRes));
|
|
}
|
|
|
|
break;
|
|
|
|
case mraEndTime:
|
|
case mraDuration:
|
|
case mraStartTime:
|
|
|
|
{
|
|
int StartForward = FALSE;
|
|
int EndForward = FALSE;
|
|
|
|
long tmpStartTime = 0;
|
|
long tmpDuration = 0;
|
|
|
|
/* adjust rsv timeframe */
|
|
|
|
if (AIndex == mraEndTime)
|
|
{
|
|
if (Format == mdfString)
|
|
R->EndTime = strtol((char *)AVal,NULL,0);
|
|
else
|
|
R->EndTime = *(long *)AVal;
|
|
|
|
tmpStartTime = R->StartTime;
|
|
}
|
|
else if (AIndex == mraStartTime)
|
|
{
|
|
if (Format == mdfString)
|
|
tmpStartTime = strtol((char *)AVal,NULL,0);
|
|
else
|
|
tmpStartTime = *(long *)AVal;
|
|
|
|
if ((R->StartTime > 0) && (R->EndTime > 0))
|
|
tmpDuration = R->EndTime - R->StartTime;
|
|
}
|
|
else /* set duration */
|
|
{
|
|
tmpStartTime = R->StartTime;
|
|
|
|
if (Format == mdfString)
|
|
tmpDuration = strtol((char *)AVal,NULL,0);
|
|
else
|
|
tmpDuration = *(long *)AVal;
|
|
}
|
|
|
|
if (R->StartTime > 0)
|
|
{
|
|
if (tmpStartTime < R->StartTime)
|
|
{
|
|
StartForward = TRUE;
|
|
EndForward = TRUE;
|
|
}
|
|
|
|
if (tmpDuration < R->EndTime - R->StartTime)
|
|
{
|
|
EndForward = TRUE;
|
|
}
|
|
}
|
|
|
|
R->StartTime = tmpStartTime;
|
|
|
|
if (tmpDuration > 0)
|
|
R->EndTime = tmpStartTime + tmpDuration;
|
|
|
|
/* change all RE objects */
|
|
|
|
for (nindex = 0;nindex < MAX_MNODE;nindex++)
|
|
{
|
|
N = MNode[nindex];
|
|
|
|
if ((N == NULL) || (N->Name[0] == '\0'))
|
|
break;
|
|
|
|
if (N->Name[0] == '\1')
|
|
continue;
|
|
|
|
for (sindex = 0;sindex < MSched.ResDepth << 1;sindex++)
|
|
{
|
|
RE = &N->RE[sindex];
|
|
|
|
if (RE == (mre_t *)1)
|
|
continue;
|
|
|
|
if (RE == NULL)
|
|
break;
|
|
|
|
if (N->R[RE->Index] != R)
|
|
continue;
|
|
|
|
/* adjust times of RE */
|
|
|
|
/* NYI */
|
|
|
|
/* if node contains multiple container reservations, node RE table must be rebuilt */
|
|
|
|
MNodeBuildRE(N,R,FALSE);
|
|
}
|
|
} /* END for (nindex) */
|
|
} /* END BLOCK */
|
|
|
|
break;
|
|
|
|
case mraStatCAPS:
|
|
|
|
if (Format == mdfString)
|
|
R->CAPS = strtod((char *)AVal,NULL);
|
|
else
|
|
R->CAPS = *(double *)AVal;
|
|
|
|
break;
|
|
|
|
case mraStatCIPS:
|
|
|
|
if (Format == mdfString)
|
|
R->CIPS = strtod((char *)AVal,NULL);
|
|
else
|
|
R->CIPS = *(double *)AVal;
|
|
|
|
break;
|
|
|
|
case mraStatTAPS:
|
|
|
|
if (Format == mdfString)
|
|
R->TAPS = strtod((char *)AVal,NULL);
|
|
else
|
|
R->TAPS = *(double *)AVal;
|
|
|
|
break;
|
|
|
|
case mraStatTIPS:
|
|
|
|
if (Format == mdfString)
|
|
R->TIPS = strtod((char *)AVal,NULL);
|
|
else
|
|
R->TIPS = *(double *)AVal;
|
|
|
|
break;
|
|
|
|
case mraType:
|
|
|
|
if (Format == mdfString)
|
|
R->Type = (int)strtol((char *)AVal,NULL,0);
|
|
else
|
|
R->Type = *(int *)AVal;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* not handled */
|
|
|
|
return(FAILURE);
|
|
|
|
/*NOTREACHED*/
|
|
|
|
break;
|
|
} /* END switch(AIndex) */
|
|
|
|
return(SUCCESS);
|
|
} /* END MResSetAttr() */
|
|
|
|
|
|
|
|
|
|
int MResAToString(
|
|
|
|
mres_t *R, /* I */
|
|
int AIndex, /* I */
|
|
char *Buf, /* O */
|
|
int SBufSize, /* I (optional) */
|
|
int Mode) /* I */
|
|
|
|
{
|
|
int BufSize;
|
|
|
|
if (Buf == NULL)
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
BufSize = (SBufSize > 0) ? SBufSize : MMAX_LINE;
|
|
|
|
Buf[0] = '\0';
|
|
|
|
if (R == NULL)
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
switch(AIndex)
|
|
{
|
|
case mraAAccount: /* accountable account */
|
|
|
|
if (R->A != NULL)
|
|
strcpy(Buf,R->A->Name);
|
|
|
|
break;
|
|
|
|
case mraACL:
|
|
|
|
MACLListShow(R->ACL,-1,(1 << mfmAVP)|(1 << mfmVerbose),Buf);
|
|
|
|
break;
|
|
|
|
case mraAGroup: /* accountable group */
|
|
|
|
if (R->G != NULL)
|
|
strcpy(Buf,R->G->Name);
|
|
|
|
break;
|
|
|
|
case mraAUser: /* accountable user */
|
|
|
|
if (R->U != NULL)
|
|
strcpy(Buf,R->U->Name);
|
|
|
|
break;
|
|
|
|
case mraCreds:
|
|
|
|
{
|
|
/* FORMAT: <CRED>=<VAL>[,<CRED>=<VAL>]... */
|
|
|
|
int aindex;
|
|
|
|
if (R->CL == NULL)
|
|
break;
|
|
|
|
for (aindex = 0;R->CL[aindex].Type != maNONE;aindex++)
|
|
{
|
|
if (aindex != 0)
|
|
strcat(Buf,",");
|
|
|
|
switch(R->CL[aindex].Cmp)
|
|
{
|
|
case mcmpSEQ:
|
|
case mcmpSSUB:
|
|
case mcmpSNE:
|
|
|
|
sprintf(Buf,"%s%s=%s",
|
|
Buf,
|
|
MAttrO[(int)R->CL[aindex].Type],
|
|
R->CL[aindex].Name);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
sprintf(Buf,"%s%s=%ld",
|
|
Buf,
|
|
MAttrO[(int)R->CL[aindex].Type],
|
|
R->CL[aindex].Value);
|
|
|
|
break;
|
|
} /* END switch(R->CL[aindex].Cmp) */
|
|
} /* END for (aindex) */
|
|
} /* END BLOCK */
|
|
|
|
break;
|
|
|
|
case mraDuration:
|
|
|
|
sprintf(Buf,"%ld",
|
|
R->EndTime - R->StartTime);
|
|
|
|
break;
|
|
|
|
case mraEndTime:
|
|
|
|
sprintf(Buf,"%ld",
|
|
R->EndTime);
|
|
|
|
break;
|
|
|
|
case mraFlags:
|
|
|
|
sprintf(Buf,"%ld",
|
|
R->Flags);
|
|
|
|
break;
|
|
|
|
case mraHostExp:
|
|
|
|
if (R->RegEx != NULL)
|
|
{
|
|
MUStrCpy(Buf,R->RegEx,BufSize);
|
|
}
|
|
|
|
break;
|
|
|
|
case mraJState:
|
|
|
|
{
|
|
if ((R->Type != mrtJob) || (R->J == NULL))
|
|
break;
|
|
|
|
strcpy(Buf,MJobState[((mjob_t *)R->J)->State]);
|
|
} /* END BLOCK */
|
|
|
|
break;
|
|
|
|
case mraMaxTasks:
|
|
|
|
sprintf(Buf,"%d",
|
|
R->MaxTasks);
|
|
|
|
break;
|
|
|
|
case mraName:
|
|
|
|
if (R->Name[0] != '\0')
|
|
strcpy(Buf,R->Name);
|
|
|
|
break;
|
|
|
|
case mraNodeCount:
|
|
|
|
sprintf(Buf,"%d",
|
|
R->NodeCount);
|
|
|
|
break;
|
|
|
|
case mraNodeList:
|
|
|
|
/* NYI */
|
|
|
|
break;
|
|
|
|
case mraOwner:
|
|
|
|
if (R->O != NULL)
|
|
{
|
|
sprintf(Buf,"%s:%s",
|
|
MXOC[R->OType],
|
|
MOGetName(R->O,R->OType,NULL));
|
|
}
|
|
|
|
break;
|
|
|
|
case mraResources:
|
|
|
|
MUCResToString(&R->DRes,0,2,Buf);
|
|
|
|
break;
|
|
|
|
case mraStartTime:
|
|
|
|
sprintf(Buf,"%ld",
|
|
R->StartTime);
|
|
|
|
break;
|
|
|
|
case mraStatCAPS:
|
|
|
|
sprintf(Buf,"%.2lf",
|
|
R->CAPS);
|
|
|
|
break;
|
|
|
|
case mraStatCIPS:
|
|
|
|
sprintf(Buf,"%.2lf",
|
|
R->CIPS);
|
|
|
|
break;
|
|
|
|
case mraStatTAPS:
|
|
|
|
sprintf(Buf,"%.2lf",
|
|
R->TAPS);
|
|
|
|
break;
|
|
|
|
case mraStatTIPS:
|
|
|
|
sprintf(Buf,"%.2lf",
|
|
R->TIPS);
|
|
|
|
break;
|
|
|
|
case mraTaskCount:
|
|
|
|
/* NYI */
|
|
|
|
break;
|
|
|
|
case mraType:
|
|
|
|
sprintf(Buf,"%d",
|
|
R->Type);
|
|
|
|
break;
|
|
|
|
case mraXAttr:
|
|
|
|
if (X.XResCPCreate != (char *(*)())0)
|
|
strcpy(Buf,(*X.XResCPCreate)(X.xd,R));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return(FAILURE);
|
|
|
|
/*NOTREACHED*/
|
|
|
|
break;
|
|
} /* END switch(AIndex) */
|
|
|
|
return(SUCCESS);
|
|
} /* END MResAToString() */
|
|
|
|
|
|
|
|
|
|
int MResToXML(
|
|
|
|
mres_t *R, /* I */
|
|
mxml_t *E, /* O */
|
|
int *SAList) /* I (optional) */
|
|
|
|
{
|
|
const int DAList[] = {
|
|
mraName,
|
|
mraType,
|
|
mraStartTime,
|
|
mraEndTime,
|
|
mraNodeCount,
|
|
mraACL,
|
|
mraFlags,
|
|
mraAAccount,
|
|
mraAGroup,
|
|
mraAUser,
|
|
mraStatCAPS,
|
|
mraStatCIPS,
|
|
mraStatTAPS,
|
|
mraStatTIPS,
|
|
mraResources,
|
|
mraHostExp,
|
|
mraMaxTasks,
|
|
mraXAttr,
|
|
-1 };
|
|
|
|
int aindex;
|
|
|
|
int *AList;
|
|
|
|
char tmpString[MAX_MBUFFER];
|
|
|
|
if ((R == NULL) || (E == NULL))
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
if (SAList != NULL)
|
|
AList = SAList;
|
|
else
|
|
AList = (int *)DAList;
|
|
|
|
for (aindex = 0;AList[aindex] != -1;aindex++)
|
|
{
|
|
if ((MResAToString(R,AList[aindex],tmpString,sizeof(tmpString),0) == FAILURE) ||
|
|
(tmpString[0] == '\0'))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
MXMLSetAttr(E,(char *)MResAttr[AList[aindex]],tmpString,mdfString);
|
|
} /* END for (aindex) */
|
|
|
|
return(SUCCESS);
|
|
} /* END MResToXML() */
|
|
|
|
|
|
|
|
|
|
int MResLoadCP(
|
|
|
|
mres_t *RS, /* I (optional) */
|
|
char *Buf) /* I */
|
|
|
|
{
|
|
char tmpName[MAX_MNAME];
|
|
char ResID[MAX_MNAME];
|
|
|
|
long CkTime;
|
|
|
|
char *ptr;
|
|
|
|
mxml_t *E = NULL;
|
|
|
|
mres_t *R;
|
|
|
|
mres_t *ResP;
|
|
|
|
mres_t tmpR;
|
|
|
|
const char *FName = "MResLoadCP";
|
|
|
|
DBG(4,fCKPT) DPrint("%s(RS,%s)\n",
|
|
FName,
|
|
(Buf != NULL) ? Buf : "NULL");
|
|
|
|
if (Buf == NULL)
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
/* FORMAT: <RESHEADER> <RESID> <CKTIME> <RESSTRING> */
|
|
|
|
/* determine res ID */
|
|
|
|
sscanf(Buf,"%s %s %ld",
|
|
tmpName,
|
|
ResID,
|
|
&CkTime);
|
|
|
|
if (((long)MSched.Time - CkTime) > MCP.CPExpirationTime)
|
|
{
|
|
return(SUCCESS);
|
|
}
|
|
|
|
if ((ptr = strchr(Buf,'<')) == NULL)
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
if (MXMLFromString(&E,ptr,NULL,NULL) == FAILURE)
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
if (RS == NULL)
|
|
{
|
|
if (MResFind(ResID,&R) != SUCCESS)
|
|
{
|
|
R = &tmpR;
|
|
|
|
MResInitialize(&R,NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
R = RS;
|
|
}
|
|
|
|
MResFromXML(R,E);
|
|
|
|
MXMLDestroyE(&E);
|
|
|
|
if (R == &tmpR)
|
|
{
|
|
if (MResCreate(
|
|
R->Type,
|
|
R->ACL,
|
|
(R->A != NULL) ? R->A->Name : "",
|
|
R->Flags,
|
|
/* R->NodeList, */ NULL,
|
|
R->StartTime,
|
|
R->EndTime,
|
|
0,
|
|
0,
|
|
R->Name,
|
|
&ResP,
|
|
R->RegEx,
|
|
&R->DRes) == FAILURE)
|
|
{
|
|
/* cannot create reservation */
|
|
|
|
return(FAILURE);
|
|
}
|
|
|
|
if (R->XCP != NULL)
|
|
{
|
|
if (X.XResCPLoad != (int (*)())0)
|
|
{
|
|
(*X.XResCPLoad)(X.xd,R->XCP,ResP);
|
|
}
|
|
|
|
MUFree(&R->XCP);
|
|
}
|
|
|
|
ResP->CAPS = R->CAPS;
|
|
ResP->CIPS = R->CIPS;
|
|
|
|
ResP->TAPS = R->TAPS;
|
|
ResP->TIPS = R->TIPS;
|
|
|
|
if (R->A != NULL)
|
|
MResSetAttr(ResP,mraAAccount,(void *)R->A,0,0);
|
|
|
|
if (R->G != NULL)
|
|
MResSetAttr(ResP,mraAGroup,(void *)R->G,0,0);
|
|
|
|
if (R->U != NULL)
|
|
MResSetAttr(ResP,mraAUser,(void *)R->U,0,0);
|
|
|
|
if (R->MaxTasks > 0)
|
|
MResSetAttr(ResP,mraMaxTasks,(void *)&R->MaxTasks,mdfInt,0);
|
|
} /* END if (RS == &tmpR) */
|
|
|
|
return(SUCCESS);
|
|
} /* END MResLoadCP() */
|
|
|
|
|
|
|
|
|
|
int MResFromXML(
|
|
|
|
mres_t *R, /* I (modified) */
|
|
mxml_t *E) /* I */
|
|
|
|
{
|
|
int aindex;
|
|
int raindex;
|
|
|
|
if ((R == NULL) || (E == NULL))
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
/* NOTE: do not initialize. may be overlaying data */
|
|
|
|
for (aindex = 0;aindex < E->ACount;aindex++)
|
|
{
|
|
raindex = MUGetIndex(E->AName[aindex],MResAttr,FALSE,0);
|
|
|
|
if (raindex == 0)
|
|
continue;
|
|
|
|
MResSetAttr(R,raindex,(void **)E->AVal[aindex],mdfString,mSet);
|
|
} /* END for (aindex) */
|
|
|
|
return(SUCCESS);
|
|
} /* END MResFromXML() */
|
|
|
|
|
|
|
|
|
|
int MResTrap(
|
|
|
|
mres_t *R)
|
|
|
|
{
|
|
/* NO-OP */
|
|
|
|
return(SUCCESS);
|
|
} /* END MResTrap() */
|
|
|
|
|
|
|
|
|
|
int MResFind(
|
|
|
|
char *ResID,
|
|
mres_t **RV)
|
|
|
|
{
|
|
int rindex;
|
|
mres_t *R;
|
|
|
|
const char *FName = "MResFind";
|
|
|
|
DBG(8,fCORE) DPrint("%s(%s,Res)\n",
|
|
FName,
|
|
ResID);
|
|
|
|
/* locate reservation */
|
|
|
|
R = NULL;
|
|
|
|
for (rindex = 0;rindex < MAX_MRES;rindex++)
|
|
{
|
|
R = MRes[rindex];
|
|
|
|
if ((R == NULL) || (R->Name[0] == '\0'))
|
|
{
|
|
DBG(8,fUI) DPrint("INFO: cannot locate reservation '%s'\n",
|
|
ResID);
|
|
|
|
return(FAILURE);
|
|
}
|
|
|
|
if (R->Name[0] == '\1')
|
|
continue;
|
|
|
|
if (!strcmp(ResID,R->Name))
|
|
break;
|
|
} /* END for (rindex) */
|
|
|
|
if (rindex == MAX_MRES)
|
|
{
|
|
DBG(8,fUI) DPrint("INFO: cannot locate reservation '%s' (end of list)\n",
|
|
ResID);
|
|
|
|
return(FAILURE);
|
|
}
|
|
|
|
*RV = R;
|
|
|
|
if (R == NULL)
|
|
return(FAILURE);
|
|
|
|
return(SUCCESS);
|
|
} /* END MResFind() */
|
|
|
|
|
|
|
|
|
|
int MResBuildACL(
|
|
|
|
mres_t *R) /* I */
|
|
|
|
{
|
|
int aindex;
|
|
|
|
if (R == NULL)
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
aindex = 0;
|
|
|
|
if (R->Name != NULL)
|
|
{
|
|
R->CL[aindex].Type = maRes;
|
|
strcpy(R->CL[aindex].Name,R->Name);
|
|
R->CL[aindex].Cmp = mcmpSEQ;
|
|
R->CL[aindex].Affinity = nmNeutralAffinity;
|
|
|
|
aindex++;
|
|
}
|
|
|
|
if (R->EndTime > 0)
|
|
{
|
|
R->CL[aindex].Type = maDuration;
|
|
R->CL[aindex].Value = R->EndTime - R->StartTime;
|
|
R->CL[aindex].Cmp = mcmpEQ;
|
|
R->CL[aindex].Affinity = nmNeutralAffinity;
|
|
|
|
aindex++;
|
|
}
|
|
|
|
if (R->Flags & (1 << mrfByName))
|
|
{
|
|
R->CL[aindex].Type = maRes;
|
|
R->CL[aindex].Name[0] = '\0';
|
|
R->CL[aindex].Cmp = mcmpSEQ;
|
|
R->CL[aindex].Affinity = nmNeutralAffinity;
|
|
R->CL[aindex].Flags |= (1 << maclRequired);
|
|
|
|
aindex++;
|
|
}
|
|
|
|
/* accountable credentials also provide reservation creds */
|
|
|
|
if (R->U != NULL)
|
|
{
|
|
R->CL[aindex].Type = maUser;
|
|
strcpy(R->CL[aindex].Name,R->U->Name);
|
|
R->CL[aindex].Value = 0;
|
|
R->CL[aindex].Cmp = mcmpSEQ;
|
|
R->CL[aindex].Affinity = nmNeutralAffinity;
|
|
|
|
aindex++;
|
|
}
|
|
|
|
if (R->G != NULL)
|
|
{
|
|
R->CL[aindex].Type = maGroup;
|
|
strcpy(R->CL[aindex].Name,R->G->Name);
|
|
R->CL[aindex].Value = 0;
|
|
R->CL[aindex].Cmp = mcmpSEQ;
|
|
R->CL[aindex].Affinity = nmNeutralAffinity;
|
|
|
|
aindex++;
|
|
}
|
|
|
|
if (R->A != NULL)
|
|
{
|
|
R->CL[aindex].Type = maAcct;
|
|
strcpy(R->CL[aindex].Name,R->A->Name);
|
|
R->CL[aindex].Value = 0;
|
|
R->CL[aindex].Cmp = mcmpSEQ;
|
|
R->CL[aindex].Affinity = nmNeutralAffinity;
|
|
|
|
aindex++;
|
|
}
|
|
|
|
R->CL[aindex].Type = maNONE;
|
|
|
|
aindex = 0;
|
|
|
|
/* reservation ACL includes job, user, group, account, QoS, or class names */
|
|
|
|
return(SUCCESS);
|
|
} /* END MResBuildACL() */
|
|
|
|
|
|
|
|
|
|
|
|
int MResGetNRange(
|
|
|
|
mres_t *RPtr, /* I: reservation requesting access */
|
|
mnode_t *N, /* I: node possessing resources */
|
|
long Duration, /* I: minimum duration */
|
|
mrange_t *RRange, /* I: required timeframe */
|
|
mrange_t *ARange, /* O: availability range */
|
|
mcres_t *DRes,
|
|
char *BRName) /* O: blocking reservation name */
|
|
|
|
{
|
|
int eindex;
|
|
int rindex;
|
|
int index;
|
|
int index2;
|
|
|
|
long RangeTime;
|
|
|
|
mcres_t CurRes;
|
|
mcres_t DedRes;
|
|
mcres_t DQRes;
|
|
|
|
int TC;
|
|
|
|
mres_t *R;
|
|
int RC;
|
|
|
|
mcres_t *BR;
|
|
|
|
long Overlap;
|
|
|
|
int PreRes;
|
|
int Same;
|
|
|
|
int ActiveRange;
|
|
|
|
char WCString[MAX_MNAME];
|
|
|
|
int ResBuffer;
|
|
|
|
int ResourcesAdded;
|
|
int ResourcesRemoved;
|
|
|
|
int InclusiveReservationCount;
|
|
|
|
int tmpTC;
|
|
|
|
const char *FName = "MResGetNRange";
|
|
|
|
DBG(7,fSCHED) DPrint("%s(%s,%s,%ld,RRange,ARange,BRName)\n",
|
|
FName,
|
|
(RPtr != NULL) ? RPtr->Name : "NULL",
|
|
(N != NULL) ? N->Name : "NULL",
|
|
Duration);
|
|
|
|
if ((RPtr == NULL) || (N == NULL))
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
MTRAPRES(RPtr,FName);
|
|
MTRAPNODE(N,FName);
|
|
|
|
memset(&ARange[0],0,sizeof(mrange_t));
|
|
|
|
#ifdef __MRESBUFFER
|
|
ResBuffer = MSched.RMPollInterval;
|
|
#else /* __MRESBUFFER */
|
|
ResBuffer = 0;
|
|
#endif /* __MRESBUFFER */
|
|
|
|
/* initialize available range */
|
|
|
|
ARange[0].EndTime = 0;
|
|
ARange[0].TaskCount = 0;
|
|
ARange[0].NodeCount = 0;
|
|
|
|
R = NULL;
|
|
|
|
/* locate first relevant event */
|
|
|
|
for (eindex = 0;eindex < (MSched.ResDepth << 1);eindex++)
|
|
{
|
|
if (N->RE[eindex].Type == mreNONE)
|
|
break;
|
|
|
|
R = N->R[N->RE[eindex].Index];
|
|
|
|
if ((R->EndTime > RRange[0].StartTime) ||
|
|
(R->EndTime >= MAX_MTIME - 1))
|
|
break;
|
|
|
|
DBG(6,fSCHED) DPrint("INFO: ignoring early RE[%d] event for '%s' (E: %ld <= S: %ld) : P: %ld\n",
|
|
eindex,
|
|
R->Name,
|
|
R->EndTime,
|
|
RRange[0].StartTime,
|
|
MSched.Time);
|
|
} /* END for (eindex) */
|
|
|
|
if (N->RE[eindex].Type == mreNONE)
|
|
{
|
|
/* if no relevant reservations present */
|
|
|
|
ARange[0].TaskCount =
|
|
MNodeGetTC(N,&N->ARes,&N->CRes,&N->DRes,&RPtr->DRes,
|
|
MAX(MSched.Time,RRange[0].StartTime));
|
|
|
|
ARange[0].NodeCount = 1;
|
|
|
|
ARange[0].StartTime = RRange[0].StartTime + ResBuffer;
|
|
ARange[0].EndTime = MAX_MTIME;
|
|
|
|
ARange[0].Affinity = nmNeutralAffinity;
|
|
|
|
/* terminate range list */
|
|
|
|
ARange[1].EndTime = 0;
|
|
|
|
if (ARange[0].TaskCount < RRange[0].TaskCount)
|
|
{
|
|
DBG(6,fSCHED) DPrint("INFO: node %s contains inadequate resources (State: %s)\n",
|
|
N->Name,
|
|
MNodeState[N->State]);
|
|
|
|
tmpTC = MNodeGetTC(N,&N->ARes,&N->CRes,&N->DRes,&RPtr->DRes,MAX_MTIME);
|
|
|
|
/* fail unless resbuffer in use */
|
|
|
|
if ((tmpTC < RRange[0].TaskCount) && (ResBuffer > 0))
|
|
{
|
|
/* experiencing resource race condition */
|
|
}
|
|
else
|
|
{
|
|
ARange[0].Affinity = nmUnavailable;
|
|
|
|
return(FAILURE);
|
|
}
|
|
}
|
|
|
|
DBG(8,fSCHED)
|
|
{
|
|
strcpy(WCString,MULToTString(ARange[0].StartTime - MSched.Time));
|
|
|
|
DPrint("INFO: closing ARange[%d] (%s -> %s : %d) (0)\n",
|
|
0,
|
|
WCString,
|
|
MULToTString(ARange[0].EndTime - MSched.Time),
|
|
ARange[0].TaskCount);
|
|
}
|
|
|
|
strcpy(WCString,MULToTString(R->EndTime - R->StartTime));
|
|
|
|
DBG(5,fSCHED) DPrint("INFO: node %s supports %d task%s of res %s for %s of %s (no RE)\n",
|
|
N->Name,
|
|
ARange[0].TaskCount,
|
|
(ARange[0].TaskCount == 1) ? "" : "s",
|
|
R->Name,
|
|
MULToTString(ARange[0].EndTime - ARange[0].StartTime),
|
|
WCString);
|
|
|
|
if (DRes != NULL)
|
|
{
|
|
memset(DRes,0,sizeof(mcres_t));
|
|
|
|
MCResAdd(DRes,&N->CRes,&N->CRes,1,FALSE);
|
|
}
|
|
|
|
if (RPtr->Flags & (1 << mrfAdvRes))
|
|
{
|
|
ARange[0].Affinity = nmUnavailable;
|
|
|
|
DBG(5,fSCHED) DPrint("INFO: no reservation found for AdvRes res %s\n",
|
|
RPtr->Name);
|
|
|
|
return(FAILURE);
|
|
}
|
|
else
|
|
{
|
|
if (ARange[0].TaskCount >= RRange[0].TaskCount)
|
|
{
|
|
return(SUCCESS);
|
|
}
|
|
else
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
}
|
|
} /* END if (N->RE[eindex].Type == mreNONE) */
|
|
|
|
/* initialize reservation state */
|
|
|
|
InclusiveReservationCount = 0;
|
|
|
|
ActiveRange = FALSE;
|
|
|
|
PreRes = TRUE;
|
|
rindex = 0;
|
|
|
|
/* initialize available resources */
|
|
|
|
memcpy(&CurRes,&N->CRes,sizeof(CurRes));
|
|
memset(&DedRes,0,sizeof(DedRes));
|
|
|
|
memset(&DQRes,0,sizeof(DQRes));
|
|
|
|
DQRes.Procs = -1;
|
|
|
|
DBG(8,fSCHED)
|
|
{
|
|
DPrint("INFO: initial resources: %s\n",
|
|
MUCResToString(&CurRes,0,0,NULL));
|
|
|
|
DPrint("INFO: requested resources: %s (x%d)\n",
|
|
MUCResToString(&RPtr->DRes,0,0,NULL),
|
|
RRange[0].TaskCount);
|
|
}
|
|
|
|
/* consume required resources */
|
|
|
|
MCResRemove(&CurRes,&N->CRes,&RPtr->DRes,RRange[0].TaskCount,FALSE);
|
|
|
|
if (MUCResIsNeg(&CurRes) == SUCCESS)
|
|
{
|
|
/* if inadequate resources are configured */
|
|
|
|
DBG(6,fSCHED) DPrint("INFO: inadequate resources configured\n");
|
|
|
|
ARange[0].Affinity = nmUnavailable;
|
|
|
|
return(FAILURE);
|
|
}
|
|
|
|
if (RPtr->Flags & (1 << mrfDedicatedNode))
|
|
{
|
|
/* all resources dedicated */
|
|
|
|
memset(&CurRes,0,sizeof(CurRes));
|
|
}
|
|
|
|
if (DRes != NULL)
|
|
{
|
|
/* set up default structures */
|
|
|
|
memset(DRes,0,sizeof(mcres_t));
|
|
|
|
MCResAdd(DRes,&CurRes,&N->CRes,1,FALSE);
|
|
}
|
|
|
|
ResourcesAdded = TRUE;
|
|
ResourcesRemoved = TRUE;
|
|
|
|
/* step through node events */
|
|
|
|
for (;eindex < (MSched.ResDepth << 1);eindex++)
|
|
{
|
|
if (N->RE[eindex].Type == mreNONE)
|
|
break;
|
|
|
|
R = N->R[N->RE[eindex].Index];
|
|
|
|
if (R->EndTime <= RRange[0].StartTime)
|
|
continue;
|
|
|
|
if ((N->RE[eindex].Time > RRange[0].StartTime) &&
|
|
(PreRes == TRUE))
|
|
{
|
|
/* adjust/check requirements at job start */
|
|
|
|
DBG(7,fSCHED) DPrint("INFO: performing starttime check (%d)\n",
|
|
eindex);
|
|
|
|
PreRes = FALSE;
|
|
|
|
if ((MUCResIsNeg(&CurRes) == SUCCESS) ||
|
|
((RPtr->Flags & (1 << mrfAdvRes)) &&
|
|
(InclusiveReservationCount <= 0)))
|
|
{
|
|
DBG(6,fSCHED) DPrint("INFO: resources unavailable at time %s\n",
|
|
MULToTString(RRange[0].StartTime - MSched.Time));
|
|
|
|
/* no active range can exist if resource unavailable at starttime */
|
|
|
|
ActiveRange = FALSE;
|
|
|
|
/* 'starttime' range is not active */
|
|
|
|
if (N->RE[eindex].Time >= RRange[0].EndTime)
|
|
{
|
|
/* range not active at time '0' and next event is too late to be considered */
|
|
|
|
DBG(8,fSCHED) DPrint("INFO: reservation extends beyond acceptable time range\n");
|
|
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TC = MNodeGetTC(N,&CurRes,&N->CRes,&DedRes,&RPtr->DRes,MSched.Time);
|
|
|
|
ARange[rindex].TaskCount = RRange[0].TaskCount + TC;
|
|
ARange[rindex].NodeCount = 1;
|
|
ARange[rindex].StartTime = RRange[0].StartTime + ResBuffer;
|
|
|
|
ActiveRange = TRUE;
|
|
|
|
if (DRes != NULL)
|
|
MUCResGetMin(DRes,DRes,&CurRes);
|
|
}
|
|
} /* END if ((N->RE[eindex].Time > RRange[0].StartTime) && ... */
|
|
|
|
if ((N->RE[eindex].Time > RRange[0].EndTime) &&
|
|
(ActiveRange == FALSE))
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* adjust resources */
|
|
|
|
if (N->R[N->RE[eindex].Index]->Type != mrtUser)
|
|
{
|
|
/* NOTE: BR represents 'per task' resources dedicated */
|
|
|
|
BR = &N->R[N->RE[eindex].Index]->DRes;
|
|
RC = N->RC[N->RE[eindex].Index];
|
|
|
|
DBG(8,fSCHED) DPrint("INFO: non-user reservation[%d] '%s'x%d resources %s\n",
|
|
N->RE[eindex].Index,
|
|
N->R[N->RE[eindex].Index]->Name,
|
|
RC,
|
|
MUCResToString(BR,0,0,NULL));
|
|
}
|
|
else
|
|
{
|
|
/* NOTE: BR represents total resources dedicated */
|
|
|
|
BR = &N->RE[eindex].DRes;
|
|
RC = 1;
|
|
|
|
DBG(8,fSCHED) DPrint("INFO: user reservation[%d] '%s'x%d resources %s\n",
|
|
N->RE[eindex].Index,
|
|
N->R[N->RE[eindex].Index]->Name,
|
|
RC,
|
|
MUCResToString(BR,0,0,NULL));
|
|
}
|
|
|
|
if (ActiveRange == TRUE)
|
|
{
|
|
/* be generous (ie, Duration - (R->StartTime - ARange[rindex].StartTime) */
|
|
|
|
Overlap =
|
|
MIN(R->EndTime,ARange[rindex].StartTime + Duration) -
|
|
MAX(R->StartTime,ARange[rindex].StartTime);
|
|
|
|
if (Overlap < 0)
|
|
Overlap = MIN(R->EndTime - R->StartTime,Duration);
|
|
}
|
|
else
|
|
{
|
|
Overlap = MIN(
|
|
R->EndTime - MAX(R->StartTime,RRange[0].StartTime),
|
|
Duration);
|
|
}
|
|
|
|
Overlap = MIN(Duration,Overlap);
|
|
|
|
DBG(7,fSCHED) DPrint("INFO: checking reservation %s %s at time %s (O: %ld)\n",
|
|
N->R[N->RE[eindex].Index]->Name,
|
|
(N->RE[eindex].Type == mreStart) ? "start" : "end",
|
|
MULToTString(N->RE[eindex].Time - MSched.Time),
|
|
Overlap);
|
|
|
|
if (N->RE[eindex].Type == mreStart)
|
|
{
|
|
/* reservation start */
|
|
|
|
if (MResCheckRAccess(R,RPtr,Overlap,&Same,&ARange[0].Affinity) == TRUE)
|
|
{
|
|
DBG(8,fSCHED) DPrint("INFO: reservations are inclusive\n");
|
|
|
|
InclusiveReservationCount++;
|
|
|
|
if (RPtr->Flags & (1 << mrfAdvRes))
|
|
ResourcesAdded = TRUE;
|
|
}
|
|
else
|
|
{
|
|
DBG(8,fSCHED) DPrint("INFO: reservations are exclusive\n");
|
|
|
|
ResourcesRemoved = TRUE;
|
|
|
|
MCResRemove(&CurRes,&N->CRes,BR,RC,FALSE);
|
|
|
|
DBG(7,fSCHED)
|
|
{
|
|
DPrint("INFO: removed resources: %s (x%d)\n",
|
|
MUCResToString(BR,0,0,NULL),
|
|
RC);
|
|
|
|
DPrint("INFO: resulting resources: %s\n",
|
|
MUCResToString(&CurRes,0,0,NULL));
|
|
}
|
|
|
|
if (BRName != NULL)
|
|
strcpy(BRName,N->R[N->RE[eindex].Index]->Name);
|
|
}
|
|
} /* END if (N->RE[eindex].Type == mreStart) */
|
|
else if (N->RE[eindex].Type == mreEnd)
|
|
{
|
|
/* reservation end */
|
|
|
|
if (MResCheckRAccess(R,RPtr,Overlap,&Same,&ARange[0].Affinity) == TRUE)
|
|
{
|
|
DBG(8,fSCHED) DPrint("INFO: reservations are inclusive\n");
|
|
|
|
InclusiveReservationCount--;
|
|
|
|
ResourcesRemoved = TRUE;
|
|
}
|
|
else
|
|
{
|
|
DBG(8,fSCHED) DPrint("INFO: reservations are exclusive\n");
|
|
|
|
ResourcesAdded = TRUE;
|
|
|
|
MCResAdd(&CurRes,&N->CRes,BR,RC,FALSE);
|
|
|
|
DBG(7,fSCHED)
|
|
{
|
|
DPrint("INFO: added resources: %s (x%d)\n",
|
|
MUCResToString(BR,0,0,NULL),
|
|
RC);
|
|
|
|
DPrint("INFO: resulting resources: %s\n",
|
|
MUCResToString(&CurRes,0,0,NULL));
|
|
}
|
|
}
|
|
} /* END else (N->RE[eindex].Type == mreStart) */
|
|
|
|
/* verify resources once per timestamp */
|
|
|
|
if ((N->RE[eindex + 1].Type == mreNONE) ||
|
|
(N->RE[eindex].Time != N->RE[eindex + 1].Time))
|
|
{
|
|
DBG(8,fSCHED) DPrint("INFO: verifying resource access at %s (%d)\n",
|
|
MULToTString(N->RE[eindex].Time - MSched.Time),
|
|
eindex);
|
|
|
|
if ((R->StartTime == RRange[0].StartTime) && (PreRes == TRUE))
|
|
{
|
|
/* adjust/check requirements at job start */
|
|
|
|
DBG(8,fSCHED) DPrint("INFO: performing starttime check (%d)\n",
|
|
eindex);
|
|
|
|
PreRes = FALSE;
|
|
|
|
if (MUCResIsNeg(&CurRes) == SUCCESS)
|
|
{
|
|
DBG(7,fSCHED) DPrint("INFO: resources unavailable at time %s\n",
|
|
MULToTString(RRange[0].StartTime - MSched.Time));
|
|
|
|
if (N->RE[eindex].Time <= RRange[0].StartTime)
|
|
ActiveRange = FALSE;
|
|
|
|
if (N->RE[eindex].Time >= RRange[0].EndTime)
|
|
{
|
|
/* end of required range reached */
|
|
|
|
break;
|
|
}
|
|
}
|
|
else if (!(RPtr->Flags & (1 << mrfAdvRes)) ||
|
|
(InclusiveReservationCount > 0))
|
|
{
|
|
/* range is active unless advance reservation is required and not found */
|
|
|
|
ActiveRange = TRUE;
|
|
|
|
TC = MNodeGetTC(N,&CurRes,&N->CRes,&DedRes,&RPtr->DRes,MSched.Time);
|
|
|
|
ARange[rindex].TaskCount = RRange[0].TaskCount + TC;
|
|
ARange[rindex].NodeCount = 1;
|
|
ARange[rindex].StartTime = RRange[0].StartTime + ResBuffer;
|
|
|
|
if (DRes != NULL)
|
|
MUCResGetMin(DRes,DRes,&CurRes);
|
|
}
|
|
} /* END if ((R->StartTime >= RRange[0].StartTime) && ... */
|
|
|
|
if ((ResourcesRemoved == TRUE) && (ActiveRange == TRUE))
|
|
{
|
|
ResourcesRemoved = FALSE;
|
|
ResourcesAdded = FALSE;
|
|
|
|
if ((MUCResIsNeg(&CurRes) == SUCCESS) ||
|
|
((RPtr->Flags & (1 << mrfAdvRes)) &&
|
|
(InclusiveReservationCount == 0)))
|
|
{
|
|
/* terminate active range */
|
|
|
|
DBG(6,fSCHED) DPrint("INFO: resources unavailable at time %s during reservation %s %s\n",
|
|
MULToTString(N->RE[eindex].Time - MSched.Time),
|
|
R->Name,
|
|
(N->RE[eindex].Type == mreStart) ? "start" : "end");
|
|
|
|
if (((N->RE[eindex].Time - ARange[rindex].StartTime) >= Duration) ||
|
|
((rindex > 0) && (ARange[rindex].StartTime == ARange[rindex - 1].EndTime)))
|
|
{
|
|
/* if reservation is long enough */
|
|
|
|
ARange[rindex].EndTime = N->RE[eindex].Time;
|
|
|
|
DBG(8,fSCHED)
|
|
{
|
|
strcpy(WCString,MULToTString(ARange[rindex].StartTime - MSched.Time));
|
|
|
|
DPrint("INFO: closing ARange[%d] (%s -> %s : %d) (1)\n",
|
|
rindex,
|
|
WCString,
|
|
MULToTString(ARange[rindex].EndTime - MSched.Time),
|
|
ARange[rindex].TaskCount);
|
|
}
|
|
|
|
rindex++;
|
|
|
|
if (rindex >= MAX_MRANGE)
|
|
{
|
|
DBG(6,fSCHED) DPrint("ALERT: range overflow in %s(%s,%s)\n",
|
|
FName,
|
|
RPtr->Name,
|
|
N->Name);
|
|
|
|
return(FAILURE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBG(8,fSCHED) DPrint("INFO: range too short (ignoring)\n");
|
|
}
|
|
|
|
ActiveRange = FALSE;
|
|
|
|
if (N->RE[eindex].Time >= RRange[0].EndTime)
|
|
{
|
|
break;
|
|
}
|
|
} /* END if (MUCResIsNeg(&CurRes) || ... */
|
|
else
|
|
{
|
|
/* check new taskcount */
|
|
|
|
TC = MNodeGetTC(N,&CurRes,&N->CRes,&DedRes,&RPtr->DRes,MSched.Time);
|
|
|
|
if (N->RE[eindex].Time >= RRange[0].StartTime)
|
|
{
|
|
/* if in 'active' time */
|
|
|
|
if (((TC + RRange[0].TaskCount) != ARange[rindex].TaskCount) &&
|
|
((RPtr->TaskCount == 0) ||
|
|
(TC + RRange[0].TaskCount < RPtr->TaskCount) ||
|
|
(TC + RRange[rindex].TaskCount < ARange[rindex].TaskCount) ||
|
|
(ARange[rindex].TaskCount < RPtr->TaskCount)))
|
|
{
|
|
if (N->RE[eindex].Time > ARange[rindex].StartTime)
|
|
{
|
|
/* create new range */
|
|
|
|
ARange[rindex].EndTime = N->RE[eindex].Time;
|
|
|
|
DBG(8,fSCHED)
|
|
{
|
|
strcpy(WCString,MULToTString(ARange[rindex].StartTime - MSched.Time));
|
|
|
|
DPrint("INFO: closing ARange[%d] (%s -> %s : %d) (2)\n",
|
|
rindex,
|
|
WCString,
|
|
MULToTString(ARange[rindex].EndTime - MSched.Time),
|
|
ARange[rindex].TaskCount);
|
|
}
|
|
|
|
rindex++;
|
|
|
|
if (rindex >= MAX_MRANGE)
|
|
{
|
|
DBG(1,fSCHED) DPrint("ALERT: range overflow in %s(%s,%s)\n",
|
|
FName,
|
|
RPtr->Name,
|
|
N->Name);
|
|
|
|
return(FAILURE);
|
|
}
|
|
} /* END if (N->RE[eindex].Time > ARange[rindex].StartTime) */
|
|
|
|
ARange[rindex].StartTime = N->RE[eindex].Time + ResBuffer;
|
|
ARange[rindex].TaskCount = RRange[0].TaskCount + TC;
|
|
ARange[rindex].NodeCount = 1;
|
|
|
|
if (DRes != NULL)
|
|
MUCResGetMin(DRes,DRes,&CurRes);
|
|
} /* END if (((TC + ...))) */
|
|
}
|
|
else if ((RRange[0].TaskCount + TC) < ARange[rindex].TaskCount)
|
|
{
|
|
/* in 'pre-active' time */
|
|
|
|
DBG(6,fSCHED) DPrint("INFO: adjusting 'preactive' ARange[%d] taskcount from %d to %d\n",
|
|
rindex,
|
|
ARange[rindex].TaskCount,
|
|
RRange[0].TaskCount + TC);
|
|
|
|
ARange[rindex].TaskCount = RRange[0].TaskCount + TC;
|
|
}
|
|
else
|
|
{
|
|
DBG(6,fSCHED) DPrint("INFO: ARange[%d] taskcount not affected by reservation change\n",
|
|
rindex);
|
|
}
|
|
} /* END else (MUCResIsNeg(&CurRes) || ... */
|
|
}
|
|
else if ((ResourcesAdded == TRUE) && (ActiveRange == TRUE))
|
|
{
|
|
/* adjust taskcount, create new range */
|
|
|
|
TC = MNodeGetTC(N,&CurRes,&N->CRes,&DedRes,&RPtr->DRes,MSched.Time);
|
|
|
|
if (((TC + RRange[0].TaskCount) != ARange[rindex].TaskCount) &&
|
|
((RPtr->TaskCount == 0) ||
|
|
(TC + RRange[0].TaskCount < RPtr->TaskCount) ||
|
|
(ARange[rindex].TaskCount < RPtr->TaskCount)))
|
|
{
|
|
ARange[rindex].EndTime = N->RE[eindex].Time;
|
|
|
|
DBG(8,fSCHED)
|
|
{
|
|
strcpy(WCString,MULToTString(ARange[rindex].StartTime - MSched.Time));
|
|
|
|
DPrint("INFO: closing ARange[%d] (%s -> %s : %d) (3)\n",
|
|
rindex,
|
|
WCString,
|
|
MULToTString(ARange[rindex].EndTime - MSched.Time),
|
|
ARange[rindex].TaskCount);
|
|
}
|
|
|
|
rindex++;
|
|
|
|
if (rindex >= MAX_MRANGE)
|
|
{
|
|
DBG(6,fSCHED) DPrint("ALERT: range overflow in %s(%s,%s)\n",
|
|
FName,
|
|
RPtr->Name,
|
|
N->Name);
|
|
|
|
return(FAILURE);
|
|
}
|
|
|
|
ARange[rindex].StartTime = N->RE[eindex].Time + ResBuffer;
|
|
|
|
ARange[rindex].TaskCount = RRange[0].TaskCount + TC;
|
|
|
|
ARange[rindex].NodeCount = 1;
|
|
}
|
|
}
|
|
else if ((ResourcesAdded == TRUE) && (ActiveRange == FALSE))
|
|
{
|
|
ResourcesAdded = FALSE;
|
|
ResourcesRemoved = FALSE;
|
|
|
|
if ((MUCResIsNeg(&CurRes) == FAILURE) &&
|
|
(!(RPtr->Flags & (1 << mrfAdvRes)) ||
|
|
(InclusiveReservationCount > 0)))
|
|
{
|
|
/* initiate active range */
|
|
|
|
DBG(6,fSCHED) DPrint("INFO: resources available at time %s during %s %s\n",
|
|
MULToTString(N->RE[eindex].Time - MSched.Time),
|
|
R->Name,
|
|
(N->RE[eindex].Type == mreStart) ? "start" : "end");
|
|
|
|
ARange[rindex].StartTime = MAX(RRange[0].StartTime,N->RE[eindex].Time) + ResBuffer;
|
|
|
|
/* adjust taskcount */
|
|
|
|
TC = MNodeGetTC(N,&CurRes,&N->CRes,&DedRes,&RPtr->DRes,MSched.Time);
|
|
|
|
ARange[rindex].TaskCount = RRange[0].TaskCount + TC;
|
|
|
|
ARange[rindex].NodeCount = 1;
|
|
|
|
ActiveRange = TRUE;
|
|
|
|
if (DRes != NULL)
|
|
{
|
|
MUCResGetMin(DRes,DRes,&CurRes);
|
|
}
|
|
} /* END if (MUCResIsNeg()) */
|
|
} /* END else if (N->RE[eindex].Type == mreEnd) */
|
|
} /* END if (N->RE[eindex].Time != N->RE[eindex + 1].Time) */
|
|
} /* END for (eindex) */
|
|
|
|
if (ActiveRange == TRUE)
|
|
{
|
|
ARange[rindex].EndTime = MAX_MTIME;
|
|
|
|
DBG(8,fSCHED)
|
|
{
|
|
strcpy(WCString,MULToTString(ARange[rindex].StartTime - MSched.Time));
|
|
|
|
DPrint("INFO: closing ARange[%d] (%s -> %s : %d) (4)\n",
|
|
rindex,
|
|
WCString,
|
|
MULToTString(ARange[rindex].EndTime - MSched.Time),
|
|
ARange[rindex].TaskCount);
|
|
}
|
|
|
|
rindex++;
|
|
|
|
if (rindex >= MAX_MRANGE)
|
|
{
|
|
DBG(6,fSCHED) DPrint("ALERT: range overflow in %s(%s,%s)\n",
|
|
FName,
|
|
RPtr->Name,
|
|
N->Name);
|
|
|
|
return(FAILURE);
|
|
}
|
|
} /* END if (ActiveRange == TRUE) */
|
|
|
|
/* terminate range list */
|
|
|
|
ARange[rindex].EndTime = 0;
|
|
|
|
rindex = 0;
|
|
|
|
/* NOTE: RRange,ARange are availability ranges, not start ranges */
|
|
|
|
for (index = 0;ARange[index].EndTime != 0;index++)
|
|
{
|
|
/* enforce time constraints */
|
|
|
|
if ((MPar[0].NodeDownStateDelayTime > 0) &&
|
|
((N->State == mnsDrained) ||
|
|
(N->State == mnsDown) ||
|
|
((N->State == mnsBusy) && (N->EState == mnsIdle))))
|
|
{
|
|
ARange[index].StartTime = MAX(
|
|
ARange[index].StartTime,
|
|
MIN(ARange[index].EndTime,MSched.Time + MPar[0].NodeDownStateDelayTime));
|
|
}
|
|
|
|
if (ARange[index].EndTime < RRange[0].StartTime)
|
|
{
|
|
DBG(6,fSCHED) DPrint("INFO: ARange[%d] too early for job %s (E: %ld < S: %ld P: %ld): removing range\n",
|
|
index,
|
|
RPtr->Name,
|
|
ARange[index].EndTime,
|
|
RRange[0].StartTime,
|
|
MSched.Time);
|
|
|
|
ARange[index].TaskCount = 0;
|
|
}
|
|
else if (ARange[index].StartTime < RRange[0].StartTime)
|
|
{
|
|
DBG(6,fSCHED) DPrint("INFO: ARange[%d] too early for job %s (S: %ld < S: %ld): truncating range\n",
|
|
index,
|
|
RPtr->Name,
|
|
ARange[index].StartTime,
|
|
RRange[0].StartTime);
|
|
|
|
ARange[index].StartTime = RRange[0].StartTime;
|
|
}
|
|
|
|
if (ARange[index].TaskCount < RRange[0].TaskCount)
|
|
{
|
|
DBG(6,fSCHED) DPrint("INFO: ARange[%d] too small for job %s (%d < %d): removing range\n",
|
|
index,
|
|
RPtr->Name,
|
|
ARange[index].TaskCount,
|
|
RRange[0].TaskCount);
|
|
}
|
|
else
|
|
{
|
|
/* adequate range found */
|
|
|
|
if (index != rindex)
|
|
{
|
|
memcpy(&ARange[rindex],&ARange[index],sizeof(ARange[index]));
|
|
}
|
|
|
|
rindex++;
|
|
}
|
|
} /* END for (index) */
|
|
|
|
ARange[rindex].EndTime = 0;
|
|
|
|
rindex = 0;
|
|
|
|
/* second pass: remove ranges too short */
|
|
|
|
for (index = 0;ARange[index].EndTime != 0;index++)
|
|
{
|
|
/* collapse short ranges */
|
|
|
|
RangeTime = ARange[index].EndTime - ARange[index].StartTime;
|
|
|
|
if (RangeTime < Duration)
|
|
{
|
|
/* append prior ranges if immediate */
|
|
|
|
for (index2 = index - 1;index2 >= 0;index2--)
|
|
{
|
|
if (ARange[index2].EndTime != ARange[index2 + 1].StartTime)
|
|
break;
|
|
|
|
RangeTime += (ARange[index2].EndTime - ARange[index2].StartTime);
|
|
|
|
if (RangeTime >= Duration)
|
|
break;
|
|
}
|
|
|
|
if (RangeTime < Duration)
|
|
{
|
|
/* append later ranges if immediate */
|
|
|
|
for (index2 = index + 1;ARange[index2].EndTime != 0;index2++)
|
|
{
|
|
if (ARange[index2 - 1].EndTime != ARange[index2].StartTime)
|
|
break;
|
|
|
|
RangeTime += (ARange[index2].EndTime - ARange[index2].StartTime);
|
|
|
|
if (RangeTime >= Duration)
|
|
break;
|
|
}
|
|
}
|
|
} /* END if (RangeTime < Duration) */
|
|
|
|
if (ARange[index].StartTime > RRange[0].EndTime)
|
|
{
|
|
DBG(6,fSCHED) DPrint("INFO: ARange[%d] (%ld -> %ld)x%d too late for job %s by %ld\n",
|
|
index,
|
|
ARange[index].StartTime,
|
|
ARange[index].EndTime,
|
|
ARange[index].TaskCount,
|
|
RPtr->Name,
|
|
ARange[index].StartTime - RRange[0].EndTime);
|
|
|
|
/* can range be merged with previous range? */
|
|
|
|
/* are long ranges or wide ranges sought? */
|
|
|
|
if ((rindex > 0) &&
|
|
(ARange[rindex - 1].EndTime == ARange[index].StartTime)
|
|
#ifndef __MSEEKLONGRANGE
|
|
&& (ARange[rindex].TaskCount >= ARange[rindex - 1].TaskCount)
|
|
#endif /* __MSEEKLONGRANGE */
|
|
)
|
|
{
|
|
ARange[rindex - 1].EndTime = ARange[index].EndTime;
|
|
ARange[rindex - 1].TaskCount =
|
|
MIN(ARange[rindex - 1].TaskCount,ARange[index].TaskCount);
|
|
}
|
|
|
|
ARange[index].TaskCount = 0;
|
|
}
|
|
else if ((RRange[0].EndTime > RRange[0].StartTime) &&
|
|
(ARange[index].EndTime > RRange[0].EndTime))
|
|
{
|
|
/* HACK: allow backfill windows to be located */
|
|
|
|
DBG(6,fSCHED) DPrint("INFO: ARange[%d] too late for job %s (E: %ld > E: %ld P: %ld): truncating range\n",
|
|
index,
|
|
RPtr->Name,
|
|
ARange[index].EndTime,
|
|
RRange[0].EndTime,
|
|
MSched.Time);
|
|
|
|
ARange[index].EndTime = RRange[0].EndTime;
|
|
}
|
|
|
|
if (RangeTime < MAX(1,Duration))
|
|
{
|
|
DBG(6,fSCHED) DPrint("INFO: ARange[%d] too short for job %s (MR: %ld < W: %ld): removing range\n",
|
|
index,
|
|
RPtr->Name,
|
|
RangeTime,
|
|
Duration);
|
|
|
|
ARange[index].TaskCount = 0;
|
|
}
|
|
|
|
if (ARange[index].TaskCount == 0)
|
|
{
|
|
/* remove range */
|
|
}
|
|
else
|
|
{
|
|
if (index != rindex)
|
|
{
|
|
memcpy(&ARange[rindex],&ARange[index],sizeof(ARange[index]));
|
|
}
|
|
|
|
rindex++;
|
|
}
|
|
} /* END for (index = 0;ARange[index].EndTime != 0;index++) */
|
|
|
|
ARange[rindex].EndTime = 0;
|
|
|
|
if ((ARange[0].EndTime == 0) || (ARange[0].TaskCount == 0))
|
|
{
|
|
DBG(6,fSCHED) DPrint("INFO: node %s unavailable for job %s at %s\n",
|
|
N->Name,
|
|
RPtr->Name,
|
|
MULToTString(RRange[0].StartTime - MSched.Time));
|
|
|
|
ARange[0].Affinity = nmUnavailable;
|
|
|
|
return(FAILURE);
|
|
}
|
|
|
|
DBG(6,fSCHED)
|
|
{
|
|
for (rindex = 0;ARange[rindex].EndTime != 0;rindex++)
|
|
{
|
|
strcpy(WCString,MULToTString(ARange[rindex].StartTime - MSched.Time));
|
|
|
|
DPrint("INFO: node %s supports %d task%c of job %s for %s at %s\n",
|
|
N->Name,
|
|
ARange[rindex].TaskCount,
|
|
(ARange[rindex].TaskCount == 1) ? ' ' : 's',
|
|
RPtr->Name,
|
|
MULToTString(ARange[rindex].EndTime - ARange[rindex].StartTime),
|
|
WCString);
|
|
}
|
|
}
|
|
|
|
/* add required resources to min resource available */
|
|
|
|
if (DRes != NULL)
|
|
{
|
|
MCResAdd(DRes,&N->CRes,&R->DRes,RRange[0].TaskCount,FALSE);
|
|
}
|
|
|
|
return(SUCCESS);
|
|
} /* END MResGetNRange() */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int MResToJob(
|
|
|
|
mres_t *R, /* I */
|
|
mjob_t *J) /* O */
|
|
|
|
{
|
|
if ((R == NULL) || (J == NULL))
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
strcpy(J->Name,R->Name);
|
|
|
|
/* copy creds */
|
|
|
|
if (J->Cred.CL != NULL)
|
|
memcpy(J->Cred.CL,R->CL,sizeof(macl_t) * MAX_MACL);
|
|
|
|
/* clear res access list */
|
|
|
|
J->RAList[0][0] = '\0';
|
|
|
|
J->Request.TC = R->TaskCount;
|
|
J->WCLimit = R->EndTime - R->StartTime;
|
|
J->SpecWCLimit[0] = J->WCLimit;
|
|
|
|
/* NOTE: support single req reservations */
|
|
|
|
if (J->Req[0] != NULL)
|
|
{
|
|
mreq_t *RQ;
|
|
|
|
RQ = J->Req[0];
|
|
|
|
memcpy(&RQ->DRes,&R->DRes,sizeof(mcres_t));
|
|
} /* END if (J->Req[0] != NULL) */
|
|
|
|
return(SUCCESS);
|
|
} /* END MResToJob() */
|
|
|
|
|
|
|
|
|
|
int MResAdjustTime(
|
|
|
|
long Delta)
|
|
|
|
{
|
|
int rindex;
|
|
int nindex;
|
|
int reindex;
|
|
|
|
mnode_t *N;
|
|
mres_t *R;
|
|
|
|
const char *FName = "MResAdjustTime";
|
|
|
|
DBG(4,fSTRUCT) DPrint("%s(%ld)\n",
|
|
FName,
|
|
Delta);
|
|
|
|
for (rindex = 0;rindex < MAX_MRES;rindex++)
|
|
{
|
|
R = MRes[rindex];
|
|
|
|
if ((R == NULL) || (R->Name[0] == '\0'))
|
|
break;
|
|
|
|
if (R->Name[0] == '\1')
|
|
continue;
|
|
|
|
R->StartTime = MIN(R->StartTime + Delta,MAX_MTIME);
|
|
R->EndTime = MIN(R->EndTime + Delta,MAX_MTIME);
|
|
} /* END for (rindex) */
|
|
|
|
for (nindex = 0;nindex < MAX_MNODE;nindex++)
|
|
{
|
|
N = MNode[nindex];
|
|
|
|
if ((N == NULL) || (N->Name[0] == '\0'))
|
|
break;
|
|
|
|
if (N->Name[0] == '\1')
|
|
continue;
|
|
|
|
for (reindex = 0;reindex < (MSched.ResDepth << 1);reindex++)
|
|
{
|
|
if (N->RE[reindex].Type == mreNONE)
|
|
break;
|
|
|
|
N->RE[reindex].Time = MIN(N->RE[reindex].Time + Delta,MAX_MTIME);
|
|
} /* END for (reindex) */
|
|
} /* END for (nindex) */
|
|
|
|
return(SUCCESS);
|
|
} /* END MResAdjustTime() */
|
|
|
|
|
|
|
|
|
|
|
|
int MResAddNode(
|
|
|
|
mres_t *R, /* I (modified) */
|
|
mnode_t *N, /* I */
|
|
int TaskCount, /* I */
|
|
int Mode) /* I */
|
|
|
|
{
|
|
int sindex;
|
|
int FreeSlot;
|
|
|
|
mcres_t DRes;
|
|
|
|
const char *FName = "MResAddNode";
|
|
|
|
DBG(5,fSTRUCT) DPrint("%s(%s,%s,%d,%d)\n",
|
|
FName,
|
|
(R != NULL) ? R->Name : "NULL",
|
|
(N != NULL) ? N->Name : "NULL",
|
|
TaskCount,
|
|
Mode);
|
|
|
|
if ((R == NULL) || (N == NULL))
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
R->MTime = MSched.Time;
|
|
N->ResMTime = MSched.Time;
|
|
|
|
/* find matching/available reservation slot */
|
|
|
|
FreeSlot = -1;
|
|
|
|
for (sindex = 0;sindex < MSched.ResDepth;sindex++)
|
|
{
|
|
if (N->R[sindex] == NULL)
|
|
{
|
|
/* end of list located */
|
|
|
|
break;
|
|
}
|
|
|
|
if ((FreeSlot == -1) && (N->R[sindex] == (mres_t *)1))
|
|
{
|
|
/* free slot located */
|
|
|
|
FreeSlot = sindex;
|
|
}
|
|
|
|
if (N->R[sindex] == R)
|
|
{
|
|
/* specified reservation located */
|
|
|
|
break;
|
|
}
|
|
} /* END for (sindex) */
|
|
|
|
if (FreeSlot != -1)
|
|
{
|
|
sindex = FreeSlot;
|
|
}
|
|
else if (sindex == MSched.ResDepth)
|
|
{
|
|
DBG(2,fSTRUCT) DPrint("WARNING: reservation overflow on node %s (%d >= %d) - increase %s\n",
|
|
N->Name,
|
|
sindex,
|
|
MSched.ResDepth,
|
|
MParam[pResDepth]);
|
|
|
|
return(FAILURE);
|
|
}
|
|
|
|
DBG(6,fSCHED)
|
|
{
|
|
MRECheck(N,"MResAddNode-Start",TRUE);
|
|
}
|
|
|
|
memcpy(&DRes,&R->DRes,sizeof(DRes));
|
|
|
|
if ((R->Type == mrtUser) && (TaskCount > 1))
|
|
{
|
|
/* NOTE: system reservations have DRes set to 'total' blocked resources */
|
|
|
|
MCResAdd(&DRes,&N->CRes,&R->DRes,(TaskCount - 1),TRUE);
|
|
|
|
TaskCount = 1;
|
|
}
|
|
|
|
switch(Mode)
|
|
{
|
|
case -1: /* remove node */
|
|
|
|
if (N->R[sindex] == R)
|
|
{
|
|
if ((TaskCount == -1) || (TaskCount >= N->RC[sindex]))
|
|
{
|
|
N->R[sindex] = (mres_t *)1;
|
|
|
|
/* remove RE */
|
|
|
|
MRERelease(N->RE,sindex,MSched.ResDepth << 1);
|
|
}
|
|
else
|
|
{
|
|
N->RC[sindex] -= TaskCount;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* reservation does not exist on node */
|
|
|
|
/* no action required */
|
|
}
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
if (N->R[sindex] == R)
|
|
{
|
|
/* set reservation taskcount */
|
|
|
|
N->RC[sindex] = TaskCount;
|
|
}
|
|
else
|
|
{
|
|
/* create new reservation entry */
|
|
|
|
N->R[sindex] = R;
|
|
|
|
N->RC[sindex] = TaskCount;
|
|
|
|
MREInsert(N->RE,R->StartTime,R->EndTime,sindex,&DRes,MSched.ResDepth << 1);
|
|
}
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
if (N->R[sindex] == R)
|
|
{
|
|
/* set reservation taskcount */
|
|
|
|
N->RC[sindex] += TaskCount;
|
|
}
|
|
else
|
|
{
|
|
/* create new reservation entry */
|
|
|
|
N->R[sindex] = R;
|
|
|
|
N->RC[sindex] = TaskCount;
|
|
|
|
MREInsert(N->RE,R->StartTime,R->EndTime,sindex,&DRes,MSched.ResDepth << 1);
|
|
}
|
|
|
|
break;
|
|
} /* END switch(Mode) */
|
|
|
|
DBG(6,fSCHED)
|
|
{
|
|
MRECheck(N,"MResAddNode-End",TRUE);
|
|
}
|
|
|
|
return(SUCCESS);
|
|
} /* END MResAddNode() */
|
|
|
|
|
|
|
|
|
|
int MNodeBuildRE(
|
|
|
|
mnode_t *N, /* I: required */
|
|
mres_t *RPtr, /* I: optional */
|
|
int Rebuild) /* I: boolean */
|
|
|
|
{
|
|
mres_t *JR;
|
|
mres_t *R;
|
|
|
|
mjob_t *J;
|
|
|
|
int JRC;
|
|
int Overlap;
|
|
|
|
int crindex;
|
|
|
|
int jrindex1;
|
|
int jrindex2;
|
|
|
|
int oreindex;
|
|
int nreindex;
|
|
int creindex;
|
|
int jreindex;
|
|
int treindex;
|
|
|
|
int ResFound; /* (boolean) */
|
|
|
|
long InitialJStartTime;
|
|
long CRStartTime;
|
|
long CREndTime;
|
|
long CROverlap;
|
|
long JRETime;
|
|
|
|
short CRC[MAX_MRES_DEPTH];
|
|
short CRI[MAX_MRES_DEPTH];
|
|
mre_t mCRE[MAX_MRES_DEPTH];
|
|
short CRESI[MAX_MRES_DEPTH];
|
|
short CREEI[MAX_MRES_DEPTH];
|
|
|
|
mre_t *CRE; /* container reservation events */
|
|
mre_t *ORE; /* old reservation events */
|
|
mre_t *NRE; /* new reservation events */
|
|
mre_t *JRE; /* job reservation events */
|
|
|
|
mcres_t BR;
|
|
mcres_t IBR; /* blocked resources */
|
|
mcres_t ZRes; /* empty CRes structure */
|
|
|
|
int OPC;
|
|
int NPC;
|
|
|
|
const char *FName = "MNodeBuildRE";
|
|
|
|
DBG(4,fCORE) DPrint("%s(%s,%s)\n",
|
|
FName,
|
|
(N != NULL) ? N->Name : "NULL",
|
|
(RPtr != NULL) ? RPtr->Name : "NULL");
|
|
|
|
if (N == NULL)
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
/* must calculate BRes for each 'container' reservation at */
|
|
/* each 'job' event boundary. collapse container reservations */
|
|
/* where possible using memcmp. Insert all container reservation */
|
|
/* events at BRes calculation completion */
|
|
|
|
/* NOTE: new RE table consists of *
|
|
* job reservation events *
|
|
* container reservations events *
|
|
* in case of multiple containers *
|
|
* jobs extract resources from all inclusive reservations *
|
|
* in case of multiple competing containers *
|
|
* available resources assigned to reservations in priority order */
|
|
|
|
/* if Rebuild is set, rebuild full RE table *
|
|
* otherwise, build only events occuring in R timeframe */
|
|
|
|
memset(&ZRes,0,sizeof(ZRes));
|
|
|
|
CRE = (mre_t *)calloc(((MAX_MRES_DEPTH << 1) + 1), sizeof(mre_t));
|
|
ORE = (mre_t *)calloc(((MAX_MRES_DEPTH << 1) + 1), sizeof(mre_t));
|
|
NRE = (mre_t *)calloc(((MAX_MRES_DEPTH << 1) + 1), sizeof(mre_t));
|
|
JRE = (mre_t *)calloc(((MAX_MRES_DEPTH << 1) + 1), sizeof(mre_t));
|
|
|
|
if (CRE == NULL || ORE == NULL || NRE == NULL || JRE == NULL )
|
|
{
|
|
DBG(4,fCORE) DPrint("ALERT: cannot allocate memory in %s\n",
|
|
FName);
|
|
|
|
return(FAILURE);
|
|
}
|
|
|
|
CRI[0] = -1;
|
|
|
|
ResFound = FALSE;
|
|
|
|
jreindex = 0;
|
|
|
|
JRETime = 0;
|
|
|
|
for (nreindex = 0;N->RE[nreindex].Type != mreNONE;nreindex++)
|
|
{
|
|
if (nreindex >= (MSched.ResDepth << 1))
|
|
break;
|
|
|
|
if (N->R[N->RE[nreindex].Index]->Type == mrtJob)
|
|
{
|
|
JRETime = N->RE[nreindex].Time;
|
|
|
|
/* job reservation located */
|
|
|
|
memcpy(&JRE[jreindex],&N->RE[nreindex],sizeof(JRE[0]));
|
|
|
|
if ((ResFound == FALSE) && (RPtr == N->R[N->RE[nreindex].Index]))
|
|
{
|
|
MTRAPRES(RPtr,FName);
|
|
|
|
ResFound = TRUE;
|
|
}
|
|
|
|
jreindex++;
|
|
}
|
|
else
|
|
{
|
|
/* record each unique container reservation */
|
|
|
|
for (crindex = 0;crindex < MSched.ResDepth;crindex++)
|
|
{
|
|
if (N->RE[nreindex].Index == CRI[crindex])
|
|
{
|
|
/* verify job res event matches CR split start time */
|
|
/* if not, both events should block same (MAX) resources */
|
|
|
|
if (JRETime != N->RE[nreindex].Time)
|
|
{
|
|
/* look for matching job re entries */
|
|
|
|
for (treindex = nreindex + 1;N->RE[treindex].Type != mreNONE;treindex++)
|
|
{
|
|
if (N->RE[treindex].Time != N->RE[nreindex].Time)
|
|
break;
|
|
|
|
if (N->R[N->RE[nreindex].Index]->Type == mrtJob)
|
|
{
|
|
JRETime = N->RE[nreindex].Time;
|
|
|
|
break;
|
|
}
|
|
} /* END for (treindex) */
|
|
} /* END if (JRETime != N->RE[nreindex].Time) */
|
|
|
|
if (JRETime != N->RE[nreindex].Time)
|
|
{
|
|
/* no matching job res event found */
|
|
|
|
/* must update both start and end events */
|
|
|
|
if (N->RE[nreindex].Type == mreStart)
|
|
{
|
|
OPC = (mCRE[crindex].DRes.Procs != -1) ? mCRE[crindex].DRes.Procs : N->CRes.Procs;
|
|
NPC = (N->RE[nreindex].DRes.Procs != -1) ? N->RE[nreindex].DRes.Procs : N->CRes.Procs;
|
|
|
|
if (OPC > NPC)
|
|
{
|
|
/* if previous event tuple is larger, locate outstanding end event */
|
|
|
|
for (treindex = nreindex + 1;N->RE[treindex].Type != mreNONE;treindex++)
|
|
{
|
|
if ((N->RE[treindex].Index != N->RE[nreindex].Index) ||
|
|
(N->RE[treindex].Type != mreEnd))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* end event found */
|
|
|
|
break;
|
|
} /* END for (treindex) */
|
|
|
|
memcpy(&N->RE[nreindex].DRes,&mCRE[crindex].DRes,sizeof(N->RE[0].DRes));
|
|
memcpy(&N->RE[treindex].DRes,&mCRE[crindex].DRes,sizeof(N->RE[0].DRes));
|
|
|
|
if ((N->RE[CRESI[crindex]].DRes.Procs == -1) || (N->RE[CREEI[crindex]].DRes.Procs == -1))
|
|
{
|
|
fprintf(stderr,"OUCH: negative blocked resources detected\n");
|
|
}
|
|
}
|
|
else if (OPC < NPC)
|
|
{
|
|
memcpy(&mCRE[crindex].DRes,&N->RE[nreindex].DRes,sizeof(mCRE[crindex].DRes));
|
|
|
|
memcpy(&N->RE[CRESI[crindex]].DRes,&mCRE[crindex].DRes,sizeof(N->RE[0].DRes));
|
|
memcpy(&N->RE[CREEI[crindex]].DRes,&mCRE[crindex].DRes,sizeof(N->RE[0].DRes));
|
|
|
|
if ((N->RE[CRESI[crindex]].DRes.Procs == -1) || (N->RE[CREEI[crindex]].DRes.Procs == -1))
|
|
{
|
|
fprintf(stderr,"OUCH: negative blocked resources detected\n");
|
|
}
|
|
}
|
|
|
|
CRESI[crindex] = nreindex;
|
|
}
|
|
else
|
|
{
|
|
CREEI[crindex] = nreindex;
|
|
}
|
|
} /* END if (JRETime != N->RE[nreindex].Time) */
|
|
|
|
break;
|
|
} /* END if (N->RE[nreindex].Index == CRI[crindex]) */
|
|
|
|
if (CRI[crindex] == -1)
|
|
{
|
|
CRI[crindex] = N->RE[nreindex].Index;
|
|
CRC[crindex] = N->RC[N->RE[nreindex].Index];
|
|
CRESI[crindex] = nreindex;
|
|
|
|
memcpy(&mCRE[crindex],&N->RE[nreindex],sizeof(mCRE[crindex]));
|
|
|
|
CRI[crindex + 1] = -1;
|
|
|
|
break;
|
|
}
|
|
} /* END for (crindex) */
|
|
} /* END else (R[N->RE[nreindex]...) */
|
|
} /* END for (nreindex) */
|
|
|
|
JRE[jreindex].Type = mreNONE;
|
|
JRE[jreindex].Index = 0;
|
|
|
|
InitialJStartTime = (jreindex > 0) ?
|
|
N->R[JRE[0].Index]->StartTime :
|
|
MAX_MTIME;
|
|
|
|
if (((RPtr != NULL) && (ResFound == FALSE)) ||
|
|
(jreindex == 0))
|
|
{
|
|
/* requested reservation not found on node */
|
|
|
|
/* issue, must update reservavation utilization in MResDestroy()
|
|
situations where job RE's have just been removed. ie,
|
|
job RE impact on inclusive reservations must be handled
|
|
*/
|
|
|
|
if (Rebuild == FALSE)
|
|
{
|
|
return(SUCCESS);
|
|
}
|
|
}
|
|
|
|
MTRAPNODE(N,FName);
|
|
|
|
DBG(6,fSCHED)
|
|
{
|
|
MRECheck(N,"MNodeBuildRE-Start",TRUE);
|
|
}
|
|
|
|
memcpy(ORE,JRE,sizeof(ORE[0]) * (jreindex + 1));
|
|
|
|
DBG(6,fSTRUCT) DPrint("INFO: updating cr reservations for res '%s'\n",
|
|
(RPtr != NULL) ? RPtr->Name : "NULL");
|
|
|
|
/* CR: Container Reservation */
|
|
|
|
for (crindex = 0;CRI[crindex] != -1;crindex++)
|
|
{
|
|
creindex = 0;
|
|
|
|
memset(CRE,0,sizeof(mre_t) * (MAX_MRES_DEPTH << 1));
|
|
|
|
R = N->R[CRI[crindex]];
|
|
|
|
CRStartTime = MAX(MSched.Time,R->StartTime);
|
|
|
|
CREndTime = R->EndTime;
|
|
|
|
/* blocked resources =~ dedicated resource per task * task count */
|
|
|
|
memset(&IBR,0,sizeof(BR));
|
|
|
|
/* IBR is total blocked resources */
|
|
|
|
MCResAdd(&IBR,&N->CRes,&R->DRes,CRC[crindex],FALSE);
|
|
|
|
if (CRStartTime < InitialJStartTime)
|
|
{
|
|
CRE[creindex].Index = CRI[crindex];
|
|
CRE[creindex].Time = CRStartTime;
|
|
memcpy(&CRE[creindex].DRes,&IBR,sizeof(IBR));
|
|
CRE[creindex].Type = mreStart;
|
|
|
|
creindex++;
|
|
|
|
CRE[creindex].Index = CRI[crindex];
|
|
CRE[creindex].Time = MIN(CREndTime,InitialJStartTime);
|
|
memcpy(&CRE[creindex].DRes,&IBR,sizeof(IBR));
|
|
CRE[creindex].Type = mreEnd;
|
|
|
|
CRStartTime = InitialJStartTime;
|
|
|
|
creindex++;
|
|
} /* END if (CRStartTime < InitialJStartTime) */
|
|
|
|
for (jrindex1 = 0;JRE[jrindex1].Type != mreNONE;jrindex1++)
|
|
{
|
|
/* locate smallest event interval */
|
|
|
|
for (jrindex2 = 0;JRE[jrindex2].Type != mreNONE;jrindex2++)
|
|
{
|
|
if (JRE[jrindex2].Time > CRStartTime)
|
|
{
|
|
CREndTime = MIN(CREndTime,JRE[jrindex2].Time);
|
|
|
|
break;
|
|
}
|
|
} /* END for (jrindex2) */
|
|
|
|
Overlap =
|
|
MIN(CREndTime,R->EndTime) -
|
|
MAX(CRStartTime,R->StartTime);
|
|
|
|
if (Overlap <= 0)
|
|
{
|
|
if (CRStartTime >= R->EndTime)
|
|
break;
|
|
else
|
|
continue;
|
|
}
|
|
|
|
memcpy(&BR,&IBR,sizeof(BR));
|
|
|
|
for (jrindex2 = 0;JRE[jrindex2].Type != mreNONE;jrindex2++)
|
|
{
|
|
if (JRE[jrindex2].Type != mreStart)
|
|
continue;
|
|
|
|
JR = N->R[JRE[jrindex2].Index];
|
|
|
|
if (JRE[jrindex2].Time >= CREndTime)
|
|
break;
|
|
|
|
Overlap =
|
|
MIN(CREndTime,JR->EndTime) -
|
|
MAX(CRStartTime,JR->StartTime);
|
|
|
|
if (Overlap <= 0)
|
|
{
|
|
if (JR->StartTime > CREndTime)
|
|
break;
|
|
else
|
|
continue;
|
|
}
|
|
|
|
JRC = N->RC[JRE[jrindex2].Index];
|
|
|
|
J = (mjob_t *)JR->J;
|
|
|
|
/* if job reservation overlaps container reservation component */
|
|
|
|
switch(R->Type)
|
|
{
|
|
case mrtUser:
|
|
|
|
CROverlap =
|
|
MIN(R->EndTime,JR->EndTime) -
|
|
MAX(R->StartTime,JR->StartTime);
|
|
|
|
if (MResCheckJAccess(R,J,CROverlap,NULL,NULL) == TRUE)
|
|
{
|
|
if (JRE[jrindex2].Type == mreStart)
|
|
{
|
|
/* remove dedicated job resources from container blocked resources */
|
|
|
|
MCResRemove(&BR,&N->CRes,&JR->DRes,JRC,FALSE);
|
|
}
|
|
else if (JRE[jrindex2].Type == mreEnd)
|
|
{
|
|
/* remove dedicated job resources from container blocked resources */
|
|
|
|
MCResAdd(&BR,&N->CRes,&JR->DRes,JRC,FALSE);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* NO-OP */
|
|
|
|
break;
|
|
} /* END switch (CR->Type) */
|
|
} /* END for (jrindex2) */
|
|
|
|
if (CRStartTime == R->EndTime)
|
|
break;
|
|
|
|
MUCResGetMax(&BR,&BR,&ZRes);
|
|
|
|
if ((creindex > 0) &&
|
|
(memcmp(&BR,&CRE[creindex - 1].DRes,sizeof(BR)) == 0))
|
|
{
|
|
/* merge ranges */
|
|
|
|
CRE[creindex - 1].Time = CREndTime;
|
|
}
|
|
else
|
|
{
|
|
/* create new range */
|
|
|
|
CRE[creindex].Index = CRI[crindex];
|
|
CRE[creindex].Time = CRStartTime;
|
|
memcpy(&CRE[creindex].DRes,&BR,sizeof(CRE[0].DRes));
|
|
CRE[creindex].Type = mreStart;
|
|
|
|
creindex++;
|
|
|
|
CRE[creindex].Index = CRI[crindex];
|
|
CRE[creindex].Time = CREndTime;
|
|
memcpy(&CRE[creindex].DRes,&BR,sizeof(CRE[0].DRes));
|
|
CRE[creindex].Type = mreEnd;
|
|
|
|
creindex++;
|
|
}
|
|
|
|
if (CREndTime >= R->EndTime)
|
|
break;
|
|
|
|
/* advance start/end time for new CRes */
|
|
|
|
CRStartTime = CREndTime;
|
|
CREndTime = R->EndTime;
|
|
} /* END for (jrindex1) */
|
|
|
|
if (creindex == 0)
|
|
{
|
|
/* no job overlap, use original CR events */
|
|
|
|
CRE[creindex].Index = CRI[crindex];
|
|
CRE[creindex].Time = CRStartTime;
|
|
memcpy(&CRE[creindex].DRes,&IBR,sizeof(CRE[0].DRes));
|
|
CRE[creindex].Type = mreStart;
|
|
|
|
creindex++;
|
|
|
|
CRE[creindex].Index = CRI[crindex];
|
|
CRE[creindex].Time = CREndTime;
|
|
memcpy(&CRE[creindex].DRes,&IBR,sizeof(CRE[0].DRes));
|
|
CRE[creindex].Type = mreEnd;
|
|
|
|
creindex++;
|
|
}
|
|
|
|
/* merge CR events with JR events */
|
|
|
|
CRE[creindex].Type = mreNONE;
|
|
|
|
creindex = 0;
|
|
oreindex = 0;
|
|
nreindex = 0;
|
|
|
|
while ((CRE[creindex].Type != mreNONE) ||
|
|
(ORE[oreindex].Type != mreNONE))
|
|
{
|
|
if ((creindex >= (MSched.ResDepth << 1)) ||
|
|
(oreindex >= (MSched.ResDepth << 1)) ||
|
|
(nreindex >= (MSched.ResDepth << 1)))
|
|
{
|
|
DBG(1,fSTRUCT) DPrint("ALERT: node reservation event overflow (N: %d C: %d O: %d) - increase %s\n",
|
|
nreindex,
|
|
creindex,
|
|
oreindex,
|
|
MParam[pResDepth]);
|
|
|
|
break;
|
|
}
|
|
|
|
if ((ORE[oreindex].Type == mreNONE) ||
|
|
((CRE[creindex].Type != mreNONE) &&
|
|
(CRE[creindex].Time < ORE[oreindex].Time)))
|
|
{
|
|
memcpy(&NRE[nreindex],&CRE[creindex],sizeof(NRE[0]));
|
|
|
|
nreindex++;
|
|
|
|
creindex++;
|
|
}
|
|
else
|
|
{
|
|
memcpy(&NRE[nreindex],&ORE[oreindex],sizeof(NRE[0]));
|
|
|
|
nreindex++;
|
|
|
|
oreindex++;
|
|
}
|
|
} /* END while ((CRE[creindex].Type != mreNONE) || (ORE[oreindex].Type != mreNONE)) */
|
|
|
|
memcpy(ORE,NRE,(sizeof(ORE[0]) * nreindex));
|
|
|
|
ORE[nreindex].Type = mreNONE;
|
|
} /* END for (crindex) */
|
|
|
|
memcpy(N->RE,ORE,sizeof(N->RE[0]) * (nreindex + 1));
|
|
|
|
/* perform sanity check on RE table */
|
|
|
|
CRStartTime = 0;
|
|
|
|
for (nreindex = 0;N->RE[nreindex].Type != mreNONE;nreindex++)
|
|
{
|
|
if (nreindex >= (MSched.ResDepth << 1))
|
|
break;
|
|
|
|
DBG(6,fSTRUCT) DPrint("INFO: N[%s]->RE[%02d] (%s %s)\n",
|
|
N->Name,
|
|
nreindex,
|
|
N->R[N->RE[nreindex].Index]->Name,
|
|
MULToTString(N->RE[nreindex].Time - MSched.Time));
|
|
|
|
if (CRStartTime > N->RE[nreindex].Time)
|
|
{
|
|
DBG(2,fSTRUCT) DPrint("ALERT: node %s RE table is corrupt. RE[%d] '%s' at %s is out of time order\n",
|
|
N->Name,
|
|
nreindex,
|
|
N->R[N->RE[nreindex].Index]->Name,
|
|
MULToTString(N->RE[nreindex].Time - MSched.Time));
|
|
}
|
|
|
|
CRStartTime = N->RE[nreindex].Time;
|
|
} /* END for (reindex) */
|
|
|
|
DBG(6,fSCHED)
|
|
{
|
|
MRECheck(N,"MNodeBuildRE-End",TRUE);
|
|
}
|
|
|
|
free(ORE);
|
|
free(CRE);
|
|
free(NRE);
|
|
free(JRE);
|
|
|
|
return(SUCCESS);
|
|
} /* END MNodeBuildRE() */
|
|
|
|
|
|
|
|
|
|
int MResCheckRAccess(
|
|
|
|
mres_t *R, /* I: existing reservation */
|
|
mres_t *ReqR, /* I: requesting reservation */
|
|
long Overlap, /* I: duration of overlap */
|
|
int *Same, /* O: boolean: reservations are identical */
|
|
char *Affinity) /* O: affinity of access */
|
|
|
|
{
|
|
int InclusiveReservation;
|
|
|
|
const char *FName = "MResCheckRAccess";
|
|
|
|
/* R: existing job or user reservation */
|
|
|
|
/* attributes of a META reservation */
|
|
|
|
/* container reservation */
|
|
/* owned by user */
|
|
/* cannot be moved in time */
|
|
/* queries should be allowed to request META res <X> or free resources */
|
|
|
|
DBG(8,fSTRUCT) DPrint("%s(%s,%s,%ld,Same,Affinity)\n",
|
|
FName,
|
|
(R != NULL) ? R->Name : "NULL",
|
|
(ReqR != NULL) ? ReqR->Name : "NULL",
|
|
Overlap);
|
|
|
|
if ((R == NULL) || (ReqR == NULL))
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
if ((R->ExpireTime > 0) && (R->ExpireTime != MAX_MTIME))
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
if (Same != NULL)
|
|
*Same = FALSE;
|
|
|
|
if ((R->Flags & (1 << mrfDedicatedResource)) ||
|
|
(ReqR->Flags & (1 << mrfDedicatedResource)))
|
|
{
|
|
/* exclusive reservations */
|
|
|
|
DBG(8,fSTRUCT) DPrint("INFO: exclusive (two job reservations)\n");
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
MACLCheckAccess(R->ACL,ReqR->CL,Affinity,&InclusiveReservation);
|
|
|
|
return(InclusiveReservation);
|
|
} /* END MResCheckRAccess() */
|
|
|
|
|
|
|
|
int MResGetFeasibleTasks(
|
|
|
|
mres_t *R,
|
|
mrange_t *ARange)
|
|
|
|
{
|
|
if (R == NULL)
|
|
return(FAILURE);
|
|
|
|
return(SUCCESS);
|
|
} /* MResGetFeasibleTasks() */
|
|
|
|
|
|
|
|
|
|
int MResUpdateStats()
|
|
|
|
{
|
|
mres_t *R;
|
|
mnode_t *N;
|
|
|
|
int nindex;
|
|
int reindex;
|
|
int rindex;
|
|
|
|
int RC;
|
|
int PC;
|
|
|
|
int BlockedProcs;
|
|
|
|
mcres_t *BR;
|
|
|
|
double IPS;
|
|
|
|
double interval;
|
|
double fsusage;
|
|
|
|
const char *FName = "MResUpdateStats";
|
|
|
|
DBG(3,fSTRUCT) DPrint("%s()\n",
|
|
FName);
|
|
|
|
interval = (double)MSched.Interval / 100.0;
|
|
|
|
for (rindex = 0;rindex < MAX_MRES;rindex++)
|
|
{
|
|
R = MRes[rindex];
|
|
|
|
if ((R == NULL) || (R->Name[0] == '\0'))
|
|
break;
|
|
|
|
if (R->Name[0] == '\1')
|
|
continue;
|
|
|
|
if (MSched.Time < R->StartTime)
|
|
continue;
|
|
|
|
if (MSched.Time > R->EndTime)
|
|
continue;
|
|
|
|
if (R->IsActive == FALSE)
|
|
{
|
|
/* reservation is now active */
|
|
|
|
R->IsActive = TRUE;
|
|
} /* END if (R->IsActive == FALSE) */
|
|
|
|
if (X.XResUpdate != (int (*)())0)
|
|
{
|
|
(*X.XResUpdate)(X.xd,R);
|
|
}
|
|
} /* END for (rindex) */
|
|
|
|
for (nindex = 0;nindex < MAX_MNODE;nindex++)
|
|
{
|
|
N = MNode[nindex];
|
|
|
|
if ((N == NULL) || (N->Name[0] == '\0'))
|
|
break;
|
|
|
|
if (N->Name[0] == '\1')
|
|
continue;
|
|
|
|
MTRAPNODE(N,FName);
|
|
|
|
for (reindex = 0;reindex < (MSched.ResDepth << 1);reindex++)
|
|
{
|
|
if (N->RE[reindex].Type == mreNONE)
|
|
break;
|
|
|
|
if (N->RE[reindex].Time > MSched.Time)
|
|
break;
|
|
|
|
if (N->RE[reindex].Type != mreStart)
|
|
continue;
|
|
|
|
R = N->R[N->RE[reindex].Index];
|
|
|
|
if (R->Type == mrtJob)
|
|
continue;
|
|
|
|
if (MSched.Time > R->EndTime)
|
|
continue;
|
|
|
|
RC = N->RC[N->RE[reindex].Index];
|
|
|
|
PC = RC * ((R->DRes.Procs != -1) ? R->DRes.Procs : N->CRes.Procs);
|
|
|
|
BR = &N->RE[reindex].DRes; /* total resources dedicated */
|
|
|
|
BlockedProcs = (BR->Procs == -1) ? N->CRes.Procs : BR->Procs;
|
|
|
|
DBG(9,fSTRUCT) DPrint("INFO: checking utilization for MNode[%03d] '%s', slot %d\n",
|
|
nindex,
|
|
N->Name,
|
|
N->RE[reindex].Index);
|
|
|
|
DBG(5,fSTRUCT) DPrint("INFO: updating usage stats for reservation %s on node '%s'\n",
|
|
R->Name,
|
|
N->Name);
|
|
|
|
IPS = (interval * BlockedProcs);
|
|
|
|
R->CAPS += (interval * (PC - BlockedProcs));
|
|
R->CIPS += IPS;
|
|
|
|
switch(MPar[0].FSC.FSPolicy)
|
|
{
|
|
case fspDPES:
|
|
|
|
{
|
|
double PEC;
|
|
|
|
MResGetPE(BR,&MPar[0],&PEC);
|
|
|
|
fsusage = interval * PEC;
|
|
}
|
|
|
|
break;
|
|
|
|
case fspUPS:
|
|
|
|
fsusage = 0.0;
|
|
|
|
break;
|
|
|
|
case fspDPS:
|
|
default:
|
|
|
|
fsusage = IPS;
|
|
|
|
break;
|
|
} /* END switch(FS[0].FSPolicy) */
|
|
|
|
if (R->A != NULL)
|
|
R->A->F.FSUsage[0] += fsusage;
|
|
|
|
if (R->G != NULL)
|
|
R->G->F.FSUsage[0] += fsusage;
|
|
|
|
if (R->U != NULL)
|
|
R->U->F.FSUsage[0] += fsusage;
|
|
} /* END for reindex */
|
|
} /* END for nindex */
|
|
|
|
return(SUCCESS);
|
|
} /* END MResUpdateStats() */
|
|
|
|
|
|
|
|
|
|
int MResChargeAllocation(
|
|
|
|
mres_t *RS,
|
|
int Mode)
|
|
|
|
{
|
|
int rindex;
|
|
enum MHoldReasonEnum Reason;
|
|
|
|
mres_t *R;
|
|
|
|
long WallTime;
|
|
|
|
const char *FName = "MResChargeAllocation";
|
|
|
|
DBG(3,fSTAT) DPrint("%s(%s,%d)\n",
|
|
FName,
|
|
(RS != NULL) ? RS->Name : "NULL",
|
|
Mode);
|
|
|
|
for (rindex = 0;rindex < MAX_MRES;rindex++)
|
|
{
|
|
R = MRes[rindex];
|
|
|
|
if ((R == NULL) || (R->Name[0] == '\0'))
|
|
break;
|
|
|
|
if (R->Name[0] == '\1')
|
|
continue;
|
|
|
|
if ((RS != NULL) && (R != RS))
|
|
continue;
|
|
|
|
if (R->Type == mrtJob)
|
|
continue;
|
|
|
|
if ((R->A == NULL) || !strcmp(R->A->Name,NONE))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (R->AllocResPending == TRUE)
|
|
{
|
|
if (R->StartTime > MSched.Time)
|
|
WallTime = MIN(MAM[0].FlushInterval,R->EndTime - R->StartTime);
|
|
else
|
|
WallTime = MIN(MAM[0].FlushTime,R->EndTime) - MSched.Time;
|
|
|
|
if (MAMAllocRReserve(
|
|
&MAM[0],
|
|
R->Name,
|
|
MSched.Time,
|
|
R->A->Name,
|
|
R->AllocPC,
|
|
R->NodeCount,
|
|
WallTime,
|
|
0,
|
|
MDEF_NODETYPE,
|
|
&Reason) == FAILURE)
|
|
{
|
|
DBG(1,fSCHED) DPrint("ALERT: cannot reserve allocation for %d procs for reservation %s\n",
|
|
R->AllocPC,
|
|
R->Name);
|
|
|
|
if (Reason == mhrNoFunds)
|
|
MResDestroy(&R);
|
|
|
|
continue;
|
|
}
|
|
|
|
R->AllocResPending = FALSE;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (R->StartTime > MSched.Time)
|
|
{
|
|
/* reservation in future */
|
|
|
|
continue;
|
|
}
|
|
|
|
if (R->CIPS <= 0.0)
|
|
{
|
|
if (Mode == 2)
|
|
{
|
|
if (MAMAllocResCancel(
|
|
(R->A != NULL) ? R->A->Name : NONE,
|
|
R->Name,
|
|
"reservation released",
|
|
NULL,
|
|
&Reason) == FAILURE)
|
|
{
|
|
DBG(1,fSTAT) DPrint("ALERT: cannot cancel allocation reservation for reservation %s\n",
|
|
R->Name);
|
|
}
|
|
}
|
|
else if (MAMAllocRReserve(
|
|
&MAM[0],
|
|
R->Name,
|
|
MSched.Time,
|
|
(R->A != NULL) ? R->A->Name : NONE,
|
|
R->AllocPC,
|
|
R->NodeCount,
|
|
MAX(1,MIN(R->EndTime,MAM[0].FlushTime) - MAX(MSched.Time,R->StartTime)),
|
|
0,
|
|
MDEF_NODETYPE,
|
|
&Reason) == FAILURE)
|
|
{
|
|
DBG(3,fUI) DPrint("WARNING: cannot reserve allocation for %s on %d proc%s\n",
|
|
R->Name,
|
|
R->AllocPC,
|
|
(R->AllocPC == 1) ? "" : "s");
|
|
|
|
if (Reason == mhrNoFunds)
|
|
{
|
|
MResDestroy(&R);
|
|
}
|
|
}
|
|
|
|
continue;
|
|
} /* END if (R->CIPS <= 0.0) */
|
|
|
|
if (MAMAllocRDebit(&MAM[0],R,&Reason,NULL) == SUCCESS)
|
|
{
|
|
R->TIPS += R->CIPS;
|
|
R->TAPS += R->CAPS;
|
|
|
|
R->CIPS = 0.0;
|
|
R->CAPS = 0.0;
|
|
|
|
/* create new allocation reservation */
|
|
|
|
if (Mode == 0)
|
|
{
|
|
if (MAMAllocRReserve(
|
|
&MAM[0],
|
|
R->Name,
|
|
MSched.Time,
|
|
(R->A != NULL) ? R->A->Name : NONE,
|
|
R->AllocPC,
|
|
R->NodeCount,
|
|
MAX(1,MIN(R->EndTime,MAM[0].FlushTime) - MAX(MSched.Time,R->StartTime)),
|
|
0,
|
|
MDEF_NODETYPE,
|
|
&Reason) == FAILURE)
|
|
{
|
|
DBG(3,fUI) DPrint("WARNING: cannot reserve allocation for %s on %d proc%s\n",
|
|
R->Name,
|
|
R->AllocPC,
|
|
(R->AllocPC == 1) ? "" : "s");
|
|
|
|
if (Reason == mhrNoFunds)
|
|
{
|
|
MResDestroy(&R);
|
|
}
|
|
}
|
|
} /* END if (Mode == 0) */
|
|
}
|
|
else
|
|
{
|
|
DBG(1,fSTAT) DPrint("ALERT: cannot charge %6.2f PS to account %s for reservation %s\n",
|
|
R->CIPS,
|
|
(R->A != NULL) ? R->A->Name : NONE,
|
|
R->Name);
|
|
}
|
|
} /* END for (rindex) */
|
|
|
|
return(SUCCESS);
|
|
} /* END MResChargeAllocation() */
|
|
|
|
|
|
|
|
|
|
int MRLAND(
|
|
|
|
mrange_t *RDst,
|
|
mrange_t *R1,
|
|
mrange_t *R2)
|
|
|
|
{
|
|
int index1;
|
|
int index2;
|
|
int cindex;
|
|
|
|
mrange_t C[MAX_MRANGE];
|
|
|
|
long ETime = 0;
|
|
long STime = 0;
|
|
|
|
long ETime2 = 0;
|
|
long STime2 = 0;
|
|
|
|
/* step through range events */
|
|
/* if R2 not active, C not active */
|
|
|
|
index1 = 0;
|
|
index2 = 0;
|
|
cindex = 0;
|
|
|
|
if (R1[index1].EndTime != 0)
|
|
{
|
|
/* ignore early ranges */
|
|
|
|
while ((R2[index2].EndTime < R1[index1].StartTime) &&
|
|
(R2[index2 + 1].EndTime != 0))
|
|
{
|
|
index2++;
|
|
}
|
|
|
|
if (R2[index2].EndTime >= R1[index1].StartTime)
|
|
{
|
|
STime2 = R2[index2].StartTime;
|
|
|
|
/* merge conjoined mask ranges */
|
|
|
|
while ((R2[index2].EndTime == R2[index2 + 1].StartTime) &&
|
|
(R2[index2 + 1].EndTime != 0))
|
|
{
|
|
index2++;
|
|
}
|
|
|
|
ETime2 = R2[index2].EndTime;
|
|
}
|
|
} /* END if (R1[index1].EndTime != 0) */
|
|
|
|
for (index1 = 0;R1[index1].EndTime != 0;index1++)
|
|
{
|
|
if (R2[index2].EndTime == 0)
|
|
break;
|
|
|
|
/* process R1 range */
|
|
|
|
while (STime2 <= R1[index1].EndTime)
|
|
{
|
|
STime = STime2;
|
|
ETime = ETime2;
|
|
|
|
/* determine next range */
|
|
|
|
/* if current mask range endtime is exceeded by R1 range endtime */
|
|
|
|
if (ETime <= R1[index1].EndTime)
|
|
{
|
|
index2++;
|
|
|
|
if (R2[index2].EndTime != 0)
|
|
{
|
|
/* ignore early ranges */
|
|
|
|
while ((R2[index2].EndTime < R1[index1].StartTime) &&
|
|
(R2[index2 + 1].EndTime != 0))
|
|
{
|
|
index2++;
|
|
}
|
|
|
|
if (R2[index2].EndTime < R1[index1].StartTime)
|
|
{
|
|
/* end of R2 list detected. no overlapping ranges */
|
|
|
|
STime2 = MAX_MTIME + 1;
|
|
}
|
|
else
|
|
{
|
|
STime2 = R2[index2].StartTime;
|
|
|
|
/* merge conjoined mask ranges */
|
|
|
|
while ((R2[index2].EndTime == R2[index2 + 1].StartTime) &&
|
|
(R2[index2 + 1].EndTime != 0))
|
|
{
|
|
index2++;
|
|
}
|
|
|
|
ETime2 = R2[index2].EndTime;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
STime2 = MAX_MTIME + 1;
|
|
}
|
|
}
|
|
|
|
/* ignore early ranges */
|
|
|
|
if (ETime < R1[index1].StartTime)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* overlapping range located */
|
|
|
|
C[cindex].StartTime = MAX(STime,R1[index1].StartTime);
|
|
C[cindex].EndTime = MIN(ETime,R1[index1].EndTime);
|
|
C[cindex].TaskCount = R1[index1].TaskCount;
|
|
C[cindex].NodeCount = R1[index1].NodeCount;
|
|
|
|
cindex++;
|
|
|
|
if (cindex >= (MAX_MRANGE - 1))
|
|
break;
|
|
|
|
if (R1[index1].EndTime < ETime)
|
|
{
|
|
/* advance R1 range */
|
|
|
|
break;
|
|
}
|
|
} /* END while (STime2 <= R1[index1].EndTime) */
|
|
} /* END for (index1) */
|
|
|
|
/* terminate range */
|
|
|
|
C[cindex].EndTime = 0;
|
|
|
|
if (RDst != NULL)
|
|
memcpy(RDst,C,MAX_MRANGE);
|
|
|
|
return(SUCCESS);
|
|
} /* END MRLAND() */
|
|
|
|
|
|
|
|
|
|
int MNodeUpdateResExpression(
|
|
|
|
mnode_t *N) /* I */
|
|
|
|
{
|
|
int rindex;
|
|
int nrindex;
|
|
int srindex;
|
|
|
|
short NodeList[2];
|
|
|
|
int NodeCount;
|
|
int TC;
|
|
|
|
char Buffer[MAX_MLINE];
|
|
|
|
mres_t *R;
|
|
|
|
const char *FName = "MNodeUpdateResExpression";
|
|
|
|
DBG(3,fSTRUCT) DPrint("%s(%s)\n",
|
|
FName,
|
|
(N != NULL) ? N->Name : "NULL");
|
|
|
|
/* NOTE: empty standing reservations should be supported */
|
|
|
|
if (N == NULL)
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
for (rindex = 0;rindex < MAX_MRES;rindex++)
|
|
{
|
|
R = MRes[rindex];
|
|
|
|
if ((R == NULL) || (R->Name[0] == '\0'))
|
|
break;
|
|
|
|
if (R->Name[0] == '\1')
|
|
continue;
|
|
|
|
if (R->StartTime == 0)
|
|
continue;
|
|
|
|
if ((R->Type == mrtJob) || (R->RegEx == NULL))
|
|
continue;
|
|
|
|
DBG(5,fSTRUCT) DPrint("INFO: checking R[%03d]: '%s' end: %s\n",
|
|
rindex,
|
|
R->Name,
|
|
MULToTString(R->EndTime - MSched.Time));
|
|
|
|
if ((R->MaxTasks > 0) && (R->TaskCount >= R->MaxTasks))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* verify node is not already connected to reservation */
|
|
|
|
for (nrindex = 0;N->R[nrindex] != NULL;nrindex++)
|
|
{
|
|
if (N->R[nrindex] == R)
|
|
break;
|
|
} /* END for (nrindex) */
|
|
|
|
if (N->R[nrindex] == R)
|
|
{
|
|
/* reservation already exists on node */
|
|
|
|
continue;
|
|
}
|
|
|
|
strcpy(Buffer,N->Name);
|
|
|
|
if (MUREToList(
|
|
R->RegEx,
|
|
mxoNode,
|
|
-1,
|
|
NodeList,
|
|
&NodeCount,
|
|
Buffer) == FAILURE)
|
|
{
|
|
DBG(2,fSTRUCT) DPrint("ERROR: cannot expand regex '%s' to check node '%s'\n",
|
|
R->RegEx,
|
|
N->Name);
|
|
|
|
continue;
|
|
}
|
|
|
|
/* node belongs to reservation */
|
|
|
|
if (NodeCount == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
TC = MNodeGetTC(
|
|
N,
|
|
&N->CRes,
|
|
&N->CRes,
|
|
&N->DRes,
|
|
&R->DRes,
|
|
MAX_MTIME);
|
|
|
|
TC = MAX(TC,1);
|
|
|
|
if (R->MaxTasks > 0)
|
|
TC = MIN(TC,R->MaxTasks - R->TaskCount);
|
|
|
|
/* find first available reservation slot on node */
|
|
|
|
if (MResAddNode(R,N,TC,0) == FAILURE)
|
|
{
|
|
DBG(1,fSTRUCT) DPrint("ALERT: cannot add node %s to reservation %s\n",
|
|
N->Name,
|
|
R->Name);
|
|
|
|
continue;
|
|
}
|
|
|
|
R->NodeCount ++;
|
|
|
|
R->TaskCount += TC;
|
|
|
|
R->AllocPC += (R->DRes.Procs == -1) ?
|
|
N->CRes.Procs :
|
|
(TC * R->DRes.Procs);
|
|
|
|
if (R->PtIndex == -1)
|
|
R->PtIndex = N->PtIndex;
|
|
else if (R->PtIndex != N->PtIndex)
|
|
R->PtIndex = 0;
|
|
} /* END for (rindex) */
|
|
|
|
/* NOTE: examine all empty standing reservations */
|
|
|
|
for (srindex = 0;srindex < MAX_MSRES;srindex++)
|
|
{
|
|
sres_t *SR;
|
|
int nindex;
|
|
mnalloc_t *HL;
|
|
|
|
SR = &SRes[srindex];
|
|
|
|
if (SR->Name[0] == '\0')
|
|
break;
|
|
|
|
if (SR->R[0] != NULL)
|
|
continue;
|
|
|
|
if (SR->HostList == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
HL = (mnalloc_t *)SR->HostList;
|
|
|
|
/* rebuild SR if N in SR hostlist */
|
|
|
|
for (nindex = 0;nindex < MAX_MNODE;nindex++)
|
|
{
|
|
if (HL[nindex].N == N)
|
|
{
|
|
/* node included in SR hostlist */
|
|
|
|
MSRUpdate(SR);
|
|
|
|
break;
|
|
}
|
|
|
|
if (HL[nindex].N == NULL)
|
|
break;
|
|
} /* END for (nindex) */
|
|
} /* END for (srindex) */
|
|
|
|
return(SUCCESS);
|
|
} /* END MNodeUpdateResExpression() */
|
|
|
|
|
|
|
|
|
|
int MResCheckJAccess(
|
|
|
|
mres_t *R, /* I (existing reservation) */
|
|
mjob_t *J, /* I (requesting consumer) */
|
|
long Overlap, /* I */
|
|
int *Same, /* O (optional) */
|
|
char *Affinity) /* O (optional) */
|
|
|
|
{
|
|
int IsInclusive;
|
|
|
|
char tmpAffinity;
|
|
|
|
mjob_t *RJ;
|
|
|
|
char *tail;
|
|
|
|
mreq_t *RQ;
|
|
|
|
/* R: existing job or user reservation */
|
|
/* J: job wrapper for new reservation */
|
|
|
|
/* attributes of a grid reservation */
|
|
|
|
/* container reservation */
|
|
/* owned by user */
|
|
/* cannot be moved in time */
|
|
/* queries should be allowed to request META res <X> or free resources */
|
|
|
|
const char *FName = "MResCheckJAccess";
|
|
|
|
DBG(8,fSTRUCT) DPrint("%s(%s,%s,%ld,Same,Affinity)\n",
|
|
FName,
|
|
(R != NULL) ? R->Name : "NULL",
|
|
(J != NULL) ? J->Name : "NULL",
|
|
Overlap);
|
|
|
|
if ((R == NULL) || (J == NULL))
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
IsInclusive = FALSE;
|
|
tmpAffinity = nmPositiveAffinity;
|
|
|
|
if (Same != NULL)
|
|
*Same = FALSE;
|
|
|
|
if (J->RAList != NULL)
|
|
{
|
|
int aindex;
|
|
|
|
for (aindex = 0;J->RAList[aindex][0] != '\0';aindex++)
|
|
{
|
|
if (!strcmp(J->RAList[aindex],R->Name) ||
|
|
!strcmp(J->RAList[aindex],ALL) ||
|
|
(!strcmp(J->RAList[aindex],"[ALLJOB]") && (R->J != NULL)))
|
|
{
|
|
/* reservation access is granted */
|
|
|
|
if (Affinity != NULL)
|
|
*Affinity = nmPositiveAffinity;
|
|
|
|
return(TRUE);
|
|
}
|
|
} /* END for (aindex) */
|
|
}
|
|
|
|
if (R->Type == mrtJob)
|
|
{
|
|
RJ = (mjob_t *)R->J;
|
|
|
|
if (!(J->Flags & (1 << mjfResMap)))
|
|
{
|
|
if (!strcmp(R->Name,J->Name))
|
|
{
|
|
/* job located its own reservation */
|
|
|
|
if (Affinity != NULL)
|
|
*Affinity = nmPositiveAffinity;
|
|
|
|
return(TRUE);
|
|
}
|
|
else
|
|
{
|
|
/* two job reservations (ALWAYS EXCLUSIVE) */
|
|
|
|
DBG(8,fSTRUCT) DPrint("INFO: exclusive (two job reservations)\n");
|
|
|
|
return(FALSE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RJ = NULL;
|
|
}
|
|
|
|
RQ = J->Req[0]; /* FIXME: only handles one req */
|
|
|
|
if ((R->Flags & (1 << mrfByName)) || (J->Flags & (1 << mjfByName)))
|
|
{
|
|
if (R->Type != mrtJob)
|
|
{
|
|
if (strcmp(J->ResName,R->Name) != 0)
|
|
{
|
|
/* reservation is not specifically requested */
|
|
|
|
DBG(8,fSTRUCT) DPrint("INFO: exclusive (not by name)\n");
|
|
|
|
return(FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((RJ == NULL) || strcmp(J->Name,RJ->Name))
|
|
{
|
|
/* reservation is not specificailly requested */
|
|
|
|
DBG(8,fSTRUCT) DPrint("INFO: exclusive (not by name)\n");
|
|
|
|
return(FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((R->Type == mrtUser) && (J->Flags & (1 << mjfResMap)))
|
|
{
|
|
/* two user reservations */
|
|
|
|
if (R->Flags & (1 << mrfStandingRes))
|
|
{
|
|
/* check if both standing reservations are same */
|
|
|
|
if ((tail = strrchr(R->Name,'.')) != NULL)
|
|
{
|
|
if (!strncmp(R->Name,J->Name,(tail - R->Name)))
|
|
{
|
|
/* same standing reservation */
|
|
|
|
if (Same != NULL)
|
|
*Same = TRUE;
|
|
|
|
if (Affinity != NULL)
|
|
*Affinity = nmPositiveAffinity;
|
|
|
|
return(TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((R->Flags & (1 << mrfDedicatedResource)) ||
|
|
!(J->Flags & (1 << mjfSharedResource)))
|
|
{
|
|
DBG(8,fSTRUCT) DPrint("INFO: exclusive (not shared resource)\n");
|
|
|
|
return(FALSE);
|
|
}
|
|
else
|
|
{
|
|
/* at least one reservation will share resources */
|
|
|
|
if (Affinity != NULL)
|
|
*Affinity = nmPositiveAffinity;
|
|
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
/* check 'ResName' specification */
|
|
|
|
if ((J->ResName[0] != '\0') && strcmp(R->Name,J->ResName))
|
|
{
|
|
DBG(8,fSTRUCT) DPrint("INFO: exclusive (not 'ResName' reservation)\n");
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
if (R->Type == mrtJob)
|
|
{
|
|
if ((RJ->ResName[0] != '\0') && strcmp(J->Name,RJ->ResName))
|
|
{
|
|
DBG(8,fSTRUCT) DPrint("INFO: exclusive (not 'ResName' reservation)\n");
|
|
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
if (J->Cred.CL == NULL)
|
|
{
|
|
DBG(0,fSTRUCT) DPrint("WARNING: job '%s' has NULL cred list\n",
|
|
J->Name);
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
/* one reservation/one job */
|
|
|
|
/* job seeks access to reservation */
|
|
|
|
if ((R->ExpireTime <= 0) || (R->ExpireTime == MAX_MTIME))
|
|
{
|
|
long OTime;
|
|
int aindex;
|
|
|
|
/* NOTE: temporarily adjust 'overlap' time */
|
|
|
|
OTime = -1;
|
|
|
|
for (aindex = 0;aindex < MAX_MACL;aindex++)
|
|
{
|
|
if (J->Cred.CL[aindex].Type == maDuration)
|
|
{
|
|
OTime = J->Cred.CL[aindex].Value;
|
|
|
|
J->Cred.CL[aindex].Value = Overlap;
|
|
|
|
break;
|
|
}
|
|
} /* END for (aindex) */
|
|
|
|
if (aindex < MAX_MACL)
|
|
{
|
|
if (J->Cred.CL[aindex].Type == maNONE)
|
|
{
|
|
/* create new time CL */
|
|
|
|
J->Cred.CL[aindex].Type = maDuration;
|
|
J->Cred.CL[aindex].Value = Overlap;
|
|
J->Cred.CL[aindex].Cmp = mcmpEQ;
|
|
J->Cred.CL[aindex].Affinity = nmPositiveAffinity;
|
|
|
|
OTime = -1;
|
|
}
|
|
}
|
|
|
|
/* NOTE: res wrappers gain access if R(job) is inclusive on J(res) */
|
|
|
|
if (J->SpecFlags & (1 << mjfResMap))
|
|
MACLCheckAccess(J->Cred.CL,R->CL,Affinity,&IsInclusive);
|
|
else
|
|
MACLCheckAccess(R->ACL,J->Cred.CL,Affinity,&IsInclusive);
|
|
|
|
if (aindex < MAX_MACL)
|
|
{
|
|
/* restore time CL */
|
|
|
|
if (OTime == -1)
|
|
J->Cred.CL[aindex].Type = maNONE;
|
|
else
|
|
J->Cred.CL[aindex].Value = OTime;
|
|
}
|
|
} /* END if ((R->ExpireTime <= 0) || (R->ExpireTime == MAX_MTIME)) */
|
|
|
|
return(IsInclusive);
|
|
} /* END MResCheckJAccess() */
|
|
|
|
|
|
|
|
|
|
int MResCheckStatus(
|
|
|
|
mres_t *SR) /* I */
|
|
|
|
{
|
|
int rindex;
|
|
int nindex;
|
|
int index;
|
|
int reindex;
|
|
int reindex2;
|
|
int RIndex;
|
|
|
|
int Offset;
|
|
|
|
mnode_t *N;
|
|
|
|
mres_t *R;
|
|
|
|
mjob_t *J;
|
|
|
|
const char *FName = "MResCheckStatus";
|
|
|
|
DBG(4,fSTRUCT) DPrint("%s(%s)\n",
|
|
FName,
|
|
(SR != NULL) ? SR->Name : "NULL");
|
|
|
|
for (rindex = 0;rindex < MAX_MRES;rindex++)
|
|
{
|
|
R = MRes[rindex];
|
|
|
|
if ((R == NULL) || (R->Name[0] == '\0'))
|
|
break;
|
|
|
|
if (R->Name[0] == '\1')
|
|
continue;
|
|
|
|
if ((SR != NULL) && (R != SR))
|
|
continue;
|
|
|
|
if (R->StartTime == 0)
|
|
continue;
|
|
|
|
DBG(5,fSTRUCT) DPrint("INFO: checking R[%03d]: '%s' end: %s\n",
|
|
rindex,
|
|
R->Name,
|
|
MULToTString(R->EndTime - MSched.Time));
|
|
|
|
if ((MSched.Time > R->EndTime) ||
|
|
((R->ExpireTime > 0) && (MSched.Time > R->ExpireTime)))
|
|
{
|
|
J = (mjob_t *)R->J;
|
|
|
|
if ((R->Type == mrtJob) && (J != NULL) &&
|
|
((J->State == mjsStarting) || (J->State == mjsRunning)))
|
|
{
|
|
/* extend reservation */
|
|
|
|
DBG(1,fSTRUCT) DPrint("INFO: extending reservation for overrun job %s by %ld seconds\n",
|
|
J->Name,
|
|
MSched.Time + MSched.RMPollInterval - R->EndTime);
|
|
|
|
R->EndTime = MSched.Time + MSched.RMPollInterval;
|
|
}
|
|
else
|
|
{
|
|
DBG(1,fSTRUCT) DPrint("INFO: clearing expired%s reservation[%03d] '%s' on iteration %d (start: %ld end: %ld)\n",
|
|
((R->ExpireTime > 0) && (R->ExpireTime < MAX_MTIME)) ? " courtesy" : "",
|
|
rindex,
|
|
R->Name,
|
|
MSched.Iteration,
|
|
R->StartTime - MSched.Time,
|
|
R->EndTime - MSched.Time);
|
|
|
|
MResDestroy(&R);
|
|
}
|
|
} /* END if ((MSched.Time) || ... ) */
|
|
} /* END for (rindex) */
|
|
|
|
/* evaluate node RE structures */
|
|
|
|
for (nindex = 0;nindex < MAX_MNODE;nindex++)
|
|
{
|
|
N = MNode[nindex];
|
|
|
|
if ((N == NULL) || (N->Name[0] == '\0'))
|
|
break;
|
|
|
|
if (N->Name[0] == '\1')
|
|
continue;
|
|
|
|
for (reindex = 0;reindex < (MSched.ResDepth << 1);reindex++)
|
|
{
|
|
if (N->RE[reindex].Type == mreNONE)
|
|
break;
|
|
|
|
if (N->RE[reindex].Type != mreEnd)
|
|
continue;
|
|
|
|
if (N->RE[reindex].Time <= MSched.Time)
|
|
{
|
|
/* RE end reached. Locate associated RE start */
|
|
|
|
RIndex = N->RE[reindex].Index;
|
|
|
|
DBG(6,fSCHED)
|
|
{
|
|
MRECheck(N,"MResCheckStatus-Start",TRUE);
|
|
}
|
|
|
|
for (reindex2 = reindex;reindex2 >= 0;reindex2--)
|
|
{
|
|
if (N->RE[reindex2].Index != RIndex)
|
|
continue;
|
|
|
|
if (N->RE[reindex2].Type == mreStart)
|
|
break;
|
|
} /* END for (reindex2) */
|
|
|
|
if ((N->RE[reindex2].Type != mreStart) ||
|
|
(N->RE[reindex2].Index != RIndex))
|
|
{
|
|
DBG(1,fSTRUCT) DPrint("ERROR: RE table on node '%s' is corrupt. (cannot locate start of res '%s'\n",
|
|
N->Name,
|
|
N->R[RIndex]->Name);
|
|
|
|
continue;
|
|
}
|
|
|
|
/* remove RE entries from reindex2 to reindex */
|
|
|
|
Offset = 0;
|
|
|
|
for (index = reindex2;index < (MSched.ResDepth << 1);index++)
|
|
{
|
|
if (N->RE[index].Type == mreNONE)
|
|
break;
|
|
|
|
if (N->RE[index].Index == RIndex)
|
|
{
|
|
if (Offset < 2)
|
|
{
|
|
Offset++;
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (Offset > 0)
|
|
memcpy(&N->RE[index - Offset],&N->RE[index],sizeof(mre_t));
|
|
} /* END for (index) */
|
|
|
|
N->RE[index - Offset].Type = mreNONE;
|
|
|
|
/* remove reservation if all RE pointers expired */
|
|
|
|
for (index = 0;index < (MSched.ResDepth << 1);index++)
|
|
{
|
|
if (N->RE[index].Type == mreNONE)
|
|
break;
|
|
|
|
if (N->RE[index].Index == RIndex)
|
|
break;
|
|
}
|
|
|
|
if (N->RE[index].Type == mreNONE)
|
|
{
|
|
/* no event references to reservation */
|
|
|
|
/* NOTE: assumes flat reservation model */
|
|
|
|
MResDestroy(&N->R[RIndex]);
|
|
}
|
|
|
|
DBG(6,fSCHED)
|
|
{
|
|
MRECheck(N,"MResCheckStatus-End",TRUE);
|
|
}
|
|
} /* END if (N->RE[reindex].Time < MSched.Time) */
|
|
} /* END for (reindex) */
|
|
} /* END for (nindex) */
|
|
|
|
return(SUCCESS);
|
|
} /* END MResCheckStatus() */
|
|
|
|
|
|
|
|
|
|
int MRECheck(
|
|
|
|
mnode_t *SN, /* I */
|
|
char *Location, /* O */
|
|
int Force) /* I (boolean) */
|
|
|
|
{
|
|
int reindex;
|
|
int rindex;
|
|
|
|
int nindex;
|
|
int cindex;
|
|
|
|
int IsCorrupt;
|
|
|
|
int StartEventFound;
|
|
|
|
mnode_t *N;
|
|
|
|
mcres_t ZRes;
|
|
|
|
int Count[MAX_MRES_DEPTH];
|
|
int TCount[MAX_MRES_DEPTH];
|
|
|
|
int RC;
|
|
int RIndex;
|
|
|
|
mres_t *R;
|
|
|
|
const char *FName = "MRECheck";
|
|
|
|
DBG(4,(fSCHED|fSTRUCT)) DPrint("%s(%s,%s,%s)\n",
|
|
FName,
|
|
(SN != NULL) ? SN->Name : "",
|
|
Location,
|
|
(Force == TRUE) ? "FORCE" : "NOFORCE");
|
|
|
|
memset(&ZRes,0,sizeof(ZRes));
|
|
|
|
for (nindex = 0;nindex < MAX_MNODE;nindex++)
|
|
{
|
|
N = MNode[nindex];
|
|
|
|
if ((N == NULL) || (N->Name[0] == '\0'))
|
|
break;
|
|
|
|
if (N->Name[0] == '\1')
|
|
continue;
|
|
|
|
if ((SN != NULL) && (SN != N))
|
|
continue;
|
|
|
|
memset(Count,0,sizeof(Count));
|
|
memset(TCount,0,sizeof(TCount));
|
|
|
|
IsCorrupt = FALSE;
|
|
|
|
/* check reservation table */
|
|
|
|
for (rindex = 0;rindex < MSched.ResDepth;rindex++)
|
|
{
|
|
if (N->R[rindex] == NULL)
|
|
break;
|
|
|
|
if (N->R[rindex] == (mres_t *)1)
|
|
continue;
|
|
|
|
StartEventFound = FALSE;
|
|
|
|
for (reindex = 0;reindex < (MSched.ResDepth << 1);reindex++)
|
|
{
|
|
if (N->RE[reindex].Type == mreNONE)
|
|
break;
|
|
|
|
if ((N->RE[reindex].Index == rindex) &&
|
|
(N->RE[reindex].Type == mreStart))
|
|
{
|
|
StartEventFound = TRUE;
|
|
|
|
break;
|
|
}
|
|
} /* END for (reindex) */
|
|
|
|
if (StartEventFound == FALSE)
|
|
{
|
|
IsCorrupt = TRUE;
|
|
}
|
|
} /* END for (rindex) */
|
|
|
|
/* check reservation event table */
|
|
|
|
for (reindex = 0;reindex < (MSched.ResDepth << 1);reindex++)
|
|
{
|
|
if (N->RE[reindex].Type == mreNONE)
|
|
break;
|
|
|
|
RIndex = N->RE[reindex].Index;
|
|
R = N->R[RIndex];
|
|
|
|
if (N->RE[reindex].Type == mreStart)
|
|
Count[RIndex]++;
|
|
else if (N->RE[reindex].Type == mreEnd)
|
|
Count[RIndex]--;
|
|
|
|
TCount[RIndex]++;
|
|
|
|
RC = N->RC[RIndex];
|
|
|
|
if ((R == NULL) || (R == (mres_t *)1))
|
|
{
|
|
IsCorrupt = TRUE;
|
|
}
|
|
else if ((R->Type == mrtUser) &&
|
|
(R->StartTime <= MSched.Time) &&
|
|
(N->RE[reindex].DRes.Procs * RC < R->DRes.Procs * RC - N->DRes.Procs))
|
|
{
|
|
IsCorrupt = TRUE;
|
|
}
|
|
/*
|
|
else if ((R->Type == mrtUser) &&
|
|
(R->StartTime <= MSched.Time) &&
|
|
(N->RE[reindex].DRes.Procs == 0) &&
|
|
(N->DRes.Procs == 0))
|
|
{
|
|
IsCorrupt = TRUE;
|
|
}
|
|
*/
|
|
|
|
if ((Count[RIndex] < 0) || (Count[RIndex] > 1))
|
|
{
|
|
IsCorrupt = TRUE;
|
|
}
|
|
} /* END for (reindex) */
|
|
|
|
if (IsCorrupt == FALSE)
|
|
{
|
|
for (cindex = 0;cindex < MSched.ResDepth;cindex++)
|
|
{
|
|
if (N->R[cindex] == NULL)
|
|
break;
|
|
|
|
if (N->R[cindex] == (mres_t *)1)
|
|
continue;
|
|
|
|
if ((Count[cindex] != 0) || (TCount[cindex] < 2))
|
|
{
|
|
IsCorrupt = TRUE;
|
|
|
|
break;
|
|
}
|
|
} /* END for (cindex) */
|
|
}
|
|
|
|
if ((IsCorrupt == TRUE) || (Force == TRUE))
|
|
{
|
|
if (IsCorrupt == TRUE)
|
|
{
|
|
DBG(0,(fSCHED|fSTRUCT)) DPrint("ALERT: corruption found on iteration %d in location %s on node %s\n",
|
|
MSched.Iteration,
|
|
Location,
|
|
N->Name);
|
|
}
|
|
|
|
/* check reservation table */
|
|
|
|
for (rindex = 0;rindex < MSched.ResDepth;rindex++)
|
|
{
|
|
if (N->R[rindex] == NULL)
|
|
break;
|
|
|
|
if (N->R[rindex] == (mres_t *)1)
|
|
continue;
|
|
|
|
StartEventFound = FALSE;
|
|
|
|
for (reindex = 0;reindex < (MSched.ResDepth << 1);reindex++)
|
|
{
|
|
if (N->RE[reindex].Type == mreNONE)
|
|
break;
|
|
|
|
if ((N->RE[reindex].Index == rindex) &&
|
|
(N->RE[reindex].Type == mreStart))
|
|
{
|
|
StartEventFound = TRUE;
|
|
|
|
break;
|
|
}
|
|
} /* END for (reindex) */
|
|
|
|
if (StartEventFound == FALSE)
|
|
{
|
|
DBG(1,fSTRUCT) DPrint("ALERT: R[%03d] '%s' has no start event\n",
|
|
rindex,
|
|
N->R[rindex]->Name);
|
|
}
|
|
} /* END for (rindex) */
|
|
|
|
memset(Count,0,sizeof(Count));
|
|
|
|
for (reindex = 0;reindex < (MSched.ResDepth << 1);reindex++)
|
|
{
|
|
if (N->RE[reindex].Type == mreNONE)
|
|
break;
|
|
|
|
RIndex = N->RE[reindex].Index;
|
|
R = N->R[RIndex];
|
|
RC = N->RC[RIndex];
|
|
|
|
if ((R == NULL) || (R == (mres_t *)1))
|
|
{
|
|
DBG(1,(fSTRUCT|fSCHED)) DPrint("ALERT: bad pointer in RE[%3d] %c %2d R: %p\n",
|
|
reindex,
|
|
(N->RE[reindex].Type == mreStart) ? 'S' : 'E',
|
|
RIndex,
|
|
R);
|
|
}
|
|
else
|
|
{
|
|
DBG(7,(fSTRUCT|fSCHED)) DPrint("INFO: N[%s]->RE[%03d] %c %s(%d) %s R: '%s'x%d\n",
|
|
N->Name,
|
|
reindex,
|
|
(N->RE[reindex].Type == mreStart) ? 'S' : 'E',
|
|
R->Name,
|
|
RIndex,
|
|
MULToTString(N->RE[reindex].Time - MSched.Time),
|
|
MUCResToString(&N->RE[reindex].DRes,0,0,NULL),
|
|
RC);
|
|
|
|
if ((R->Type == mrtUser) &&
|
|
(R->StartTime <= MSched.Time) &&
|
|
(N->RE[reindex].DRes.Procs * RC < R->DRes.Procs * RC - N->DRes.Procs))
|
|
{
|
|
DBG(1,(fSTRUCT|fSCHED)) DPrint("ALERT: corrupt resource structure in RE[%d] (RED*RC < RD*RC - ND) RC: %d RED: %d RD: %d ND: %d\n",
|
|
reindex,
|
|
RC,
|
|
N->RE[reindex].DRes.Procs,
|
|
R->DRes.Procs,
|
|
N->DRes.Procs);
|
|
}
|
|
}
|
|
|
|
if (N->RE[reindex].Type == mreStart)
|
|
Count[RIndex]++;
|
|
else if (N->RE[reindex].Type == mreEnd)
|
|
Count[RIndex]--;
|
|
|
|
if ((Count[RIndex] < 0) ||
|
|
(Count[RIndex] > 1))
|
|
{
|
|
DBG(1,(fSTRUCT|fSCHED)) DPrint("ALERT: RE[%03d] %c %2d Count: %d\n",
|
|
reindex,
|
|
(N->RE[reindex].Type == mreStart) ? 'S' : 'E',
|
|
RIndex,
|
|
Count[RIndex]);
|
|
|
|
Count[RIndex] =
|
|
(Count[RIndex] < 0) ? 0 : 1;
|
|
}
|
|
} /* END for (reindex) */
|
|
|
|
for (cindex = 0;cindex < MSched.ResDepth;cindex++)
|
|
{
|
|
if (N->R[cindex] == NULL)
|
|
break;
|
|
|
|
if (N->R[cindex] == (mres_t *)1)
|
|
continue;
|
|
|
|
if (Count[cindex] != 0)
|
|
{
|
|
DBG(1,fSTRUCT) DPrint("ALERT: R[%03d] %s started but not ended\n",
|
|
cindex,
|
|
N->R[cindex]->Name);
|
|
}
|
|
|
|
if (TCount[cindex] < 2)
|
|
{
|
|
DBG(1,(fSTRUCT|fSCHED)) DPrint("ALERT: R[%03d] %s has no associated events\n",
|
|
cindex,
|
|
N->R[cindex]->Name);
|
|
}
|
|
} /* END for (cindex) */
|
|
} /* END if (IsCorrupt == TRUE) */
|
|
} /* END for (nindex) */
|
|
|
|
return(SUCCESS);
|
|
} /* END MRECheck() */
|
|
|
|
|
|
|
|
|
|
|
|
int MResPreempt(
|
|
|
|
mres_t *R)
|
|
|
|
{
|
|
unsigned long MinPriority;
|
|
|
|
int rindex;
|
|
|
|
mres_t *PR;
|
|
mres_t *MinPR;
|
|
|
|
const char *FName = "MResPreempt";
|
|
|
|
DBG(3,fSTRUCT) DPrint("%s(%s)\n",
|
|
FName,
|
|
R->Name);
|
|
|
|
MinPriority = 0xffffffff;
|
|
|
|
MinPR = NULL;
|
|
|
|
for (rindex = 0;rindex < MAX_MRES;rindex++)
|
|
{
|
|
PR = MRes[rindex];
|
|
|
|
if ((PR == NULL) || (PR->Name[0] == '\0'))
|
|
break;
|
|
|
|
if (PR->Name[0] == '\1')
|
|
continue;
|
|
|
|
if (!(PR->Flags & (1 << mrfPreemptible)))
|
|
continue;
|
|
|
|
if (PR->Priority >= MIN(R->Priority,MinPriority))
|
|
continue;
|
|
|
|
MinPR = PR;
|
|
|
|
MinPriority = PR->Priority;
|
|
} /* END for (rindex) */
|
|
|
|
if (MinPriority >= R->Priority)
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
MResDestroy(&MinPR);
|
|
|
|
return(SUCCESS);
|
|
} /* END MResPreempt() */
|
|
|
|
|
|
|
|
|
|
|
|
/* NOTE: do not free reservation structure. just mark it empty */
|
|
|
|
int MResDestroy(
|
|
|
|
mres_t **RP) /* I (modified) */
|
|
|
|
{
|
|
int sindex;
|
|
int index;
|
|
|
|
mjob_t *J;
|
|
|
|
char Message[MAX_MLINE];
|
|
char WCString[MAX_MNAME];
|
|
|
|
mres_t *R;
|
|
|
|
const char *FName = "MResDestroy";
|
|
|
|
DBG(3,fSTRUCT) DPrint("%s(%s)\n",
|
|
FName,
|
|
((RP != NULL) && (*RP != NULL)) ? (*RP)->Name : "NULL");
|
|
|
|
/* invalid reservation specified */
|
|
|
|
if ((RP == NULL) || (*RP == NULL) || (*RP == (mres_t *)1))
|
|
{
|
|
DBG(8,fSTRUCT) DPrint("INFO: no reservation to release\n");
|
|
|
|
return(SUCCESS);
|
|
}
|
|
|
|
R = *RP;
|
|
|
|
if ((R->Name[0] == '\0') || (R->Name[0] == '\1'))
|
|
{
|
|
DBG(8,fSTRUCT) DPrint("INFO: reservation already released\n");
|
|
|
|
return(SUCCESS);
|
|
}
|
|
|
|
DBG(8,fSTRUCT)
|
|
{
|
|
strcpy(WCString,MULToTString(R->StartTime - MSched.Time));
|
|
|
|
DPrint("INFO: releasing reservation %s (%s -> %s : %d)\n",
|
|
R->Name,
|
|
WCString,
|
|
MULToTString(R->EndTime - MSched.Time),
|
|
R->TaskCount);
|
|
}
|
|
|
|
MTRAPRES(R,FName);
|
|
|
|
if (R->Flags & (1 << mrfStandingRes))
|
|
{
|
|
/* clear SR pointer */
|
|
|
|
for (sindex = 0;sindex < MAX_MSRES;sindex++)
|
|
{
|
|
for (index = 0;index < MAX_SRES_DEPTH;index++)
|
|
{
|
|
if (SRes[sindex].R[index] == R)
|
|
{
|
|
SRes[sindex].R[index] = NULL;
|
|
|
|
break;
|
|
}
|
|
} /* END for (index) */
|
|
} /* END for (sindex) */
|
|
} /* END if (R->Flags) */
|
|
|
|
/* clear node reservation pointers */
|
|
|
|
if (X.XResDestroy != (int (*)())0)
|
|
(*X.XResDestroy)(X.xd,R);
|
|
|
|
MResChargeAllocation(R,2);
|
|
|
|
MResDeallocateResources(R);
|
|
|
|
J = NULL;
|
|
|
|
if (R->Type == mrtJob)
|
|
{
|
|
/* clear job reservation pointer */
|
|
|
|
if (MJobFind(R->Name,&J,0) == SUCCESS)
|
|
{
|
|
J->R = NULL;
|
|
|
|
DBG(5,fSTRUCT) DPrint("INFO: job '%s' reservation released (tasks requested: %d)\n",
|
|
J->Name,
|
|
J->Request.TC);
|
|
}
|
|
else
|
|
{
|
|
DBG(1,fSTRUCT) DPrint("WARNING: cannot locate job associated with reservation '%s' (nodes released)\n",
|
|
R->Name);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* send notification for non-job reservations only */
|
|
|
|
sprintf(Message,"RESERVATIONDESTROYED: %s %s %ld %ld %ld %d\n",
|
|
R->Name,
|
|
MResType[R->Type],
|
|
MSched.Time,
|
|
R->StartTime,
|
|
R->EndTime,
|
|
R->NodeCount);
|
|
|
|
MSysRegEvent(Message,0,0,1);
|
|
} /* END if (R->Type == mrtJob) */
|
|
|
|
/* clear reservation */
|
|
|
|
MRERelease(MRE,R->Index,(MAX_MRES << 2));
|
|
|
|
R->StartTime = 0;
|
|
|
|
MUFree(&R->SystemID);
|
|
MUFree(&R->RegEx);
|
|
|
|
if ((J != NULL) && (MSched.InitialLoad != TRUE))
|
|
MResAdjustDRes(J->Name,1);
|
|
|
|
if (R->Flags & (1 << mrfMeta))
|
|
MResAdjustGResUsage(R,-1);
|
|
|
|
if (R->J != NULL)
|
|
((mjob_t *)(R->J))->RType = mjrNone;
|
|
|
|
*RP = NULL;
|
|
|
|
DBG(7,fSTRUCT) DPrint("INFO: reservation '%s' released\n",
|
|
R->Name);
|
|
|
|
R->Name[0] = '\1';
|
|
|
|
return(SUCCESS);
|
|
} /* END MResDestroy() */
|
|
|
|
|
|
|
|
|
|
int MResShowHostList(
|
|
|
|
mres_t *R) /* I */
|
|
|
|
{
|
|
int nindex;
|
|
int sindex;
|
|
|
|
int NC;
|
|
int TC;
|
|
|
|
const char *FName = "MResShowHostList";
|
|
|
|
mnode_t *N;
|
|
|
|
DBG(9,fSTRUCT) DPrint("%s(%s)\n",
|
|
FName,
|
|
(R != NULL) ? R->Name : "NULL");
|
|
|
|
if (R == NULL)
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
NC = 0;
|
|
TC = 0;
|
|
|
|
DBG(0,fSTRUCT) DPrint("INFO: node list for res '%s'\n",
|
|
R->Name);
|
|
|
|
for (nindex = 0;nindex < MAX_MNODE;nindex++)
|
|
{
|
|
N = MNode[nindex];
|
|
|
|
if ((N == NULL) || (N->Name[0] == '\0'))
|
|
break;
|
|
|
|
if (N->Name[0] == '\1')
|
|
continue;
|
|
|
|
for (sindex = 0;sindex < MSched.ResDepth;sindex++)
|
|
{
|
|
DBG(6,fSTRUCT) DPrint("INFO: checking status of node '%s', slot %d\n",
|
|
N->Name,
|
|
sindex);
|
|
|
|
if (N->R[sindex] == NULL)
|
|
break;
|
|
|
|
if (N->R[sindex] == R)
|
|
{
|
|
fprintf(mlog.logfp,"[%s]",
|
|
N->Name);
|
|
|
|
NC ++;
|
|
TC += N->RC[sindex];
|
|
|
|
break;
|
|
}
|
|
} /* END for (sindex) */
|
|
} /* END for (nindex) */
|
|
|
|
fprintf(mlog.logfp," (%d nodes/%d tasks located)\n",
|
|
NC,
|
|
TC);
|
|
|
|
return(SUCCESS);
|
|
} /* END MResShowHostList() */
|
|
|
|
|
|
|
|
|
|
int MResDeallocateResources(
|
|
|
|
mres_t *R) /* I (modified) */
|
|
|
|
{
|
|
int nindex;
|
|
int sindex;
|
|
int sindex2;
|
|
|
|
int Offset;
|
|
|
|
mnode_t *N;
|
|
|
|
int NC;
|
|
int TC;
|
|
|
|
const char *FName = "MResDeallocateResources";
|
|
|
|
if (R == NULL)
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
NC = 0;
|
|
TC = 0;
|
|
|
|
for (nindex = 0;nindex < MAX_MNODE;nindex++)
|
|
{
|
|
N = MNode[nindex];
|
|
|
|
if ((N == NULL) || (N->Name[0] == '\0'))
|
|
break;
|
|
|
|
if (N->Name[0] == '\1')
|
|
continue;
|
|
|
|
for (sindex = 0;sindex < MSched.ResDepth;sindex++)
|
|
{
|
|
DBG(9,fSTRUCT) DPrint("INFO: checking release of node '%s', slot %d\n",
|
|
N->Name,
|
|
sindex);
|
|
|
|
if (N->R[sindex] == NULL)
|
|
break;
|
|
|
|
if (N->R[sindex] == R)
|
|
{
|
|
MTRAPNODE(N,FName);
|
|
|
|
NC ++;
|
|
TC += N->RC[sindex];
|
|
|
|
/* remove RE objects */
|
|
|
|
DBG(6,fSCHED)
|
|
{
|
|
MRECheck(N,"MResDeallocateResources-Start",TRUE);
|
|
}
|
|
|
|
N->R[sindex] = (mres_t *)1;
|
|
|
|
Offset = 0;
|
|
|
|
for (sindex2 = 0;sindex2 < (MSched.ResDepth << 1);sindex2++)
|
|
{
|
|
if (N->RE[sindex2].Type == mreNONE)
|
|
break;
|
|
|
|
if (N->RE[sindex2].Index == sindex)
|
|
{
|
|
Offset++;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (Offset > 0)
|
|
{
|
|
memcpy(
|
|
&N->RE[sindex2 - Offset],
|
|
&N->RE[sindex2],
|
|
sizeof(mre_t));
|
|
}
|
|
} /* END for (sindex2) */
|
|
|
|
N->RE[sindex2 - Offset].Type = mreNONE;
|
|
|
|
N->ResMTime = MSched.Time;
|
|
|
|
DBG(6,fSCHED)
|
|
{
|
|
MRECheck(N,"MResDeallocateResources-End",TRUE);
|
|
}
|
|
|
|
DBG(5,fSTRUCT) DPrint("INFO: node '%s' released from reservation\n",
|
|
N->Name);
|
|
} /* END if (N->R[sindex] == R) */
|
|
} /* END for (sindex) */
|
|
} /* END for (nindex) */
|
|
|
|
R->NodeCount = 0;
|
|
R->TaskCount = 0;
|
|
R->AllocPC = 0;
|
|
|
|
DBG(5,fSTRUCT) DPrint("INFO: %d nodes/%d tasks released from reservation\n",
|
|
NC,
|
|
TC);
|
|
|
|
return(SUCCESS);
|
|
} /* END MResDeallocateResources() */
|
|
|
|
|
|
|
|
|
|
int MResCheckJobMatch(
|
|
|
|
mjob_t *J, /* I */
|
|
mres_t *R) /* I */
|
|
|
|
{
|
|
int nindex;
|
|
int nlindex;
|
|
int sindex;
|
|
int rqindex;
|
|
|
|
mnode_t *N;
|
|
mreq_t *RQ;
|
|
|
|
long tmpEndTime;
|
|
|
|
double NPFactor;
|
|
|
|
int NLTaskCount;
|
|
|
|
const char *FName = "MResCheckJobMatch";
|
|
|
|
DBG(6,fSTRUCT) DPrint("%s(%s,%s)\n",
|
|
FName,
|
|
(J != NULL) ? J->Name : "NULL",
|
|
(R != NULL) ? R->Name : "NULL");
|
|
|
|
if ((J == NULL) || (R == NULL))
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
/* return success if reservation adequately 'covers' up-to-date job requirements */
|
|
|
|
/* NOTE: DISABLE UNTIL FULLY TESTED */
|
|
|
|
return(FAILURE);
|
|
|
|
/*NOTREACHED*/
|
|
|
|
/* verify reservation timeframe */
|
|
|
|
if (J->DispatchTime != R->StartTime)
|
|
{
|
|
/* job start time has changed */
|
|
|
|
DBG(3,fSTRUCT) DPrint("ALERT: start time has changed for job %s (%ld != %ld)\n",
|
|
J->Name,
|
|
J->DispatchTime,
|
|
R->StartTime);
|
|
|
|
return(FAILURE);
|
|
}
|
|
|
|
NPFactor = 9999999.0;
|
|
|
|
for (rqindex = 0;J->Req[rqindex] != NULL;rqindex++)
|
|
{
|
|
RQ = J->Req[rqindex];
|
|
|
|
if (RQ->NodeList != NULL)
|
|
{
|
|
double tmpD;
|
|
|
|
MUNLGetMinAVal(
|
|
RQ->NodeList,
|
|
mnaSpeed,
|
|
NULL,
|
|
(void **)&tmpD);
|
|
|
|
NPFactor = MIN(NPFactor,tmpD);
|
|
}
|
|
} /* END for (rqindex) */
|
|
|
|
/* verify endtime */
|
|
|
|
tmpEndTime = MAX(
|
|
J->DispatchTime + (long)((double)J->SpecWCLimit[0] / NPFactor),
|
|
MSched.Time + 1);
|
|
|
|
if (tmpEndTime != R->EndTime)
|
|
{
|
|
/* job completion time has changed */
|
|
|
|
DBG(3,fSTRUCT) DPrint("ALERT: completion time has changed for job %s (%ld != %ld)\n",
|
|
J->Name,
|
|
J->DispatchTime + (long)((double)J->SpecWCLimit[0] / NPFactor),
|
|
R->EndTime);
|
|
|
|
return(FAILURE);
|
|
}
|
|
|
|
/* verify reservation tasks match job tasks */
|
|
|
|
/* verify all job tasks have matching reservation task */
|
|
/* verify all node reservations have matching job task */
|
|
|
|
NLTaskCount = 0;
|
|
|
|
for (nlindex = 0;J->Req[0]->NodeList[nlindex].N != NULL;nlindex++)
|
|
{
|
|
NLTaskCount += J->Req[0]->NodeList[nlindex].TC;
|
|
}
|
|
|
|
for (nindex = 0;nindex < MAX_MNODE;nindex++)
|
|
{
|
|
N = MNode[nindex];
|
|
|
|
if ((N == NULL) || (N->Name[0] == '\0'))
|
|
break;
|
|
|
|
if (N->Name[0] == '\1')
|
|
continue;
|
|
|
|
for (sindex = 0;sindex < MSched.ResDepth;sindex++)
|
|
{
|
|
if (N->R[sindex] == NULL)
|
|
break;
|
|
|
|
if (N->R[sindex] != R)
|
|
continue;
|
|
|
|
/* matching reservation found, verify job requests node */
|
|
|
|
for (nlindex = 0;J->Req[0]->NodeList[nlindex].N != NULL;nlindex++)
|
|
{
|
|
if (nindex == J->Req[0]->NodeList[nlindex].N->Index)
|
|
break;
|
|
}
|
|
|
|
if (J->Req[0]->NodeList[nlindex].N == NULL)
|
|
{
|
|
/* reserved node not in current job nodelist */
|
|
|
|
DBG(3,fSTRUCT) DPrint("ALERT: reserved node '%s' no longer in job %s nodelist\n",
|
|
N->Name,
|
|
J->Name);
|
|
|
|
return(FAILURE);
|
|
}
|
|
|
|
NLTaskCount -= N->RC[sindex];
|
|
} /* END for (sindex) */
|
|
} /* END for (nindex) */
|
|
|
|
if (NLTaskCount != 0)
|
|
{
|
|
/* node list task count mismatch */
|
|
|
|
DBG(3,fSTRUCT) DPrint("ALERT: node list taskcount mismatch for job %s (%d %s tasks)\n",
|
|
J->Name,
|
|
(NLTaskCount > 0) ? NLTaskCount : -NLTaskCount,
|
|
(NLTaskCount > 0) ? "missing" : "additional");
|
|
|
|
return(FAILURE);
|
|
}
|
|
|
|
return(SUCCESS);
|
|
} /* END MResCheckJobMatch() */
|
|
|
|
|
|
|
|
|
|
int MRangeGetIntersection(
|
|
|
|
mjob_t *J, /* I */
|
|
mrange_t *Range1, /* I */
|
|
mrange_t *Range2) /* O */
|
|
|
|
{
|
|
/* NYI */
|
|
|
|
return(SUCCESS);
|
|
} /* END MRangeGetIntersection() */
|
|
|
|
|
|
|
|
|
|
int MRLMerge(
|
|
|
|
mrange_t *R1, /* I */
|
|
mrange_t *R2, /* I */
|
|
int MinTaskCount, /* I */
|
|
long *StartTime) /* O: earliest time <MinTaskCount> tasks are available */
|
|
|
|
{
|
|
int index1;
|
|
int index2;
|
|
int cindex;
|
|
|
|
mrange_t C[MAX_MRANGE + 2];
|
|
|
|
enum { rNone = 0, rStart, rEnd, rInstant };
|
|
|
|
long R1Time;
|
|
long R2Time;
|
|
|
|
int R1State;
|
|
int R2State;
|
|
|
|
long ETime;
|
|
|
|
int TaskCount;
|
|
int OldTaskCount;
|
|
|
|
int NodeCount;
|
|
int OldNodeCount;
|
|
|
|
int IsCorrupt;
|
|
|
|
const char *FName = "MRLMerge";
|
|
|
|
DBG(8,fSTRUCT) DPrint("%s(R1,R2,%d,StartTime)\n",
|
|
FName,
|
|
MinTaskCount);
|
|
|
|
/* initialize variables */
|
|
|
|
index1 = 0;
|
|
index2 = 0;
|
|
cindex = 0;
|
|
|
|
R1State = rStart; /* 'rStart' : next time to be processed is start */
|
|
R2State = rStart;
|
|
|
|
TaskCount = 0;
|
|
NodeCount = 0;
|
|
|
|
IsCorrupt = FALSE;
|
|
|
|
/* TaskCount/NodeCount indicate resources in current range */
|
|
|
|
if (StartTime != NULL)
|
|
*StartTime = MAX_MTIME;
|
|
|
|
while ((R1[index1].EndTime != 0) ||
|
|
(R2[index2].EndTime != 0))
|
|
{
|
|
if (cindex >= MAX_MRANGE)
|
|
{
|
|
DBG(2,fSTRUCT) DPrint("ALERT: range overflow in %s()\n",
|
|
FName);
|
|
|
|
IsCorrupt = TRUE;
|
|
|
|
break;
|
|
}
|
|
|
|
if (R1[index1].EndTime == 0)
|
|
{
|
|
R1State = rNone;
|
|
R1Time = MAX_MTIME + 1;
|
|
}
|
|
else
|
|
{
|
|
R1Time = (R1State != rEnd) ? R1[index1].StartTime : R1[index1].EndTime;
|
|
}
|
|
|
|
if (R2[index2].EndTime == 0)
|
|
{
|
|
R2State = rNone;
|
|
R2Time = MAX_MTIME + 1;
|
|
}
|
|
else
|
|
{
|
|
R2Time = (R2State != rEnd) ? R2[index2].StartTime : R2[index2].EndTime;
|
|
}
|
|
|
|
ETime = MIN(R1Time,R2Time);
|
|
|
|
if ((R1[index1].EndTime == ETime) && (R1[index1].StartTime == ETime))
|
|
R1State = rInstant;
|
|
|
|
if ((R2[index2].EndTime == ETime) && (R2[index2].StartTime == ETime))
|
|
R2State = rInstant;
|
|
|
|
/* handle instant states */
|
|
|
|
if (((R1State == rInstant) && (R1Time == ETime)) ||
|
|
((R2State == rInstant) && (R2Time == ETime)))
|
|
{
|
|
if ((R1State == rInstant) && (R1Time == ETime))
|
|
{
|
|
/* R1 instant, R2 ??? */
|
|
|
|
if ((R2State == rEnd) && (R2Time == ETime))
|
|
{
|
|
/* R1 instant, R2 bordering end */
|
|
|
|
/* implied open range */
|
|
|
|
C[cindex].EndTime = ETime;
|
|
cindex++;
|
|
|
|
OldTaskCount = TaskCount;
|
|
OldNodeCount = NodeCount;
|
|
|
|
/* advance R2 */
|
|
|
|
TaskCount -= R2[index2].TaskCount;
|
|
NodeCount -= R2[index2].NodeCount;
|
|
|
|
index2++;
|
|
|
|
R2State = rStart;
|
|
|
|
/* create instant range */
|
|
|
|
C[cindex].StartTime = ETime;
|
|
|
|
if (R2[index2].EndTime == 0)
|
|
R2Time = MAX_MTIME + 1;
|
|
else
|
|
R2Time = R2[index2].StartTime;
|
|
|
|
/* R1 instant, R2 ??? (new) */
|
|
/* if instant R2 exists, it must be this range */
|
|
|
|
if (R2Time == ETime)
|
|
{
|
|
C[cindex].TaskCount = R1[index1].TaskCount + R2[index2].TaskCount;
|
|
C[cindex].NodeCount = R1[index1].NodeCount + R2[index2].NodeCount;
|
|
|
|
if (R2Time == R2[index2].EndTime)
|
|
{
|
|
/* R1 instant, R2 instant */
|
|
|
|
TaskCount = 0;
|
|
NodeCount = 0;
|
|
|
|
index2++;
|
|
|
|
R2State = rStart;
|
|
}
|
|
else
|
|
{
|
|
/* R1 instant, R2 start */
|
|
|
|
/* DO NOTHING */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* R1 instant only */
|
|
|
|
C[cindex].TaskCount = R1[index1].TaskCount + OldTaskCount;
|
|
C[cindex].NodeCount = R1[index1].NodeCount + OldNodeCount;
|
|
}
|
|
|
|
C[cindex].EndTime = ETime;
|
|
} /* END if ((R2State == rEnd) && (R2Time == ETime)) */
|
|
else
|
|
{
|
|
/* implied closed range */
|
|
|
|
/* start instant range */
|
|
|
|
C[cindex].StartTime = ETime;
|
|
C[cindex].EndTime = ETime;
|
|
|
|
if (R2State == rInstant)
|
|
{
|
|
/* R1 instant, R2 instant */
|
|
|
|
TaskCount = R1[index1].TaskCount + R2[index2].TaskCount;
|
|
NodeCount = R1[index1].NodeCount + R2[index2].NodeCount;
|
|
|
|
C[cindex].TaskCount = TaskCount;
|
|
C[cindex].NodeCount = NodeCount;
|
|
|
|
TaskCount = 0;
|
|
NodeCount = 0;
|
|
|
|
index2++;
|
|
|
|
R2State = rStart;
|
|
}
|
|
else if ((R2State == rStart) && (R2Time == ETime))
|
|
{
|
|
/* R1 instant, R2 start */
|
|
|
|
C[cindex].TaskCount = R1[index1].TaskCount + R2[index2].TaskCount;
|
|
C[cindex].NodeCount = R1[index1].NodeCount + R2[index2].NodeCount;
|
|
}
|
|
else
|
|
{
|
|
/* R1 instant only */
|
|
|
|
C[cindex].TaskCount = TaskCount + R1[index1].TaskCount;
|
|
C[cindex].NodeCount = NodeCount + R1[index1].NodeCount;
|
|
}
|
|
}
|
|
|
|
index1++;
|
|
|
|
R1State = rStart;
|
|
}
|
|
else
|
|
{
|
|
/* R1 ???, R2 instant */
|
|
/* R1 not instant */
|
|
|
|
if ((R1State == rEnd) && (R1Time == ETime))
|
|
{
|
|
/* R2 instant, R1 bordering end */
|
|
|
|
/* implied open range */
|
|
|
|
C[cindex].EndTime = ETime;
|
|
cindex++;
|
|
|
|
OldTaskCount = TaskCount;
|
|
OldNodeCount = NodeCount;
|
|
|
|
/* advance R1 */
|
|
|
|
TaskCount -= R1[index1].TaskCount;
|
|
NodeCount -= R1[index1].NodeCount;
|
|
|
|
index1++;
|
|
|
|
R1State = rStart;
|
|
|
|
/* create instant range */
|
|
|
|
C[cindex].StartTime = ETime;
|
|
|
|
if (R1[index1].EndTime == 0)
|
|
R1Time = MAX_MTIME + 1;
|
|
else
|
|
R1Time = R1[index1].StartTime;
|
|
|
|
/* R2 instant, R1 ??? (new) */
|
|
/* if instant R1 exists, it must be this range */
|
|
|
|
if (R1Time == ETime)
|
|
{
|
|
C[cindex].TaskCount = R1[index1].TaskCount + R2[index2].TaskCount;
|
|
C[cindex].NodeCount = R1[index1].NodeCount + R2[index2].NodeCount;
|
|
|
|
if (R1Time == R1[index1].EndTime)
|
|
{
|
|
/* R1 instant, R2 instant */
|
|
|
|
index1++;
|
|
|
|
R1State = rStart;
|
|
}
|
|
else
|
|
{
|
|
/* R2 instant, R1 start */
|
|
|
|
/* DO NOTHING */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* R2 instant only */
|
|
|
|
C[cindex].TaskCount = R2[index2].TaskCount + OldTaskCount;
|
|
C[cindex].NodeCount = R2[index2].NodeCount + OldNodeCount;
|
|
}
|
|
|
|
C[cindex].EndTime = ETime;
|
|
} /* END if ((R1State == rEnd) && (R1Time == ETime)) */
|
|
else
|
|
{
|
|
/* R1 not instant, R2 instant, R1 not end */
|
|
|
|
if (TaskCount > 0)
|
|
{
|
|
C[cindex].EndTime = ETime;
|
|
|
|
cindex++;
|
|
}
|
|
|
|
/* start instant range */
|
|
|
|
C[cindex].StartTime = ETime;
|
|
|
|
if ((R1State == rStart) && (R1Time == ETime))
|
|
{
|
|
/* R2 instant, R1 start */
|
|
|
|
C[cindex].TaskCount = R2[index2].TaskCount + R1[index1].TaskCount;
|
|
C[cindex].NodeCount = R2[index2].NodeCount + R1[index1].NodeCount;
|
|
}
|
|
else
|
|
{
|
|
/* R2 instant only */
|
|
|
|
C[cindex].TaskCount = TaskCount + R2[index2].TaskCount;
|
|
C[cindex].NodeCount = NodeCount + R2[index2].NodeCount;
|
|
}
|
|
|
|
C[cindex].EndTime = ETime;
|
|
} /* END else ((R1State == rEnd) && (R1Time == ETime)) */
|
|
|
|
index2++;
|
|
|
|
R2State = rStart;
|
|
} /* END else ((R1State == rInstant) && (R1Time == ETime)) */
|
|
|
|
cindex++;
|
|
|
|
if (TaskCount > 0)
|
|
{
|
|
C[cindex].StartTime = ETime;
|
|
C[cindex].TaskCount = TaskCount;
|
|
C[cindex].NodeCount = NodeCount;
|
|
}
|
|
|
|
continue;
|
|
} /* END if (((R1State == rInstant) && (R1Time == ETime)) || ... */
|
|
|
|
if (((R1State == rEnd) && (R1Time == ETime)) ||
|
|
((R2State == rEnd) && (R2Time == ETime)))
|
|
{
|
|
/* implied open range */
|
|
|
|
C[cindex].EndTime = ETime;
|
|
cindex++;
|
|
|
|
if ((R1State == rEnd) && (R1Time == ETime))
|
|
{
|
|
TaskCount -= R1[index1].TaskCount;
|
|
NodeCount -= R1[index1].NodeCount;
|
|
|
|
index1++;
|
|
|
|
R1State = rStart;
|
|
}
|
|
|
|
if ((R2State == rEnd) && (R2Time == ETime))
|
|
{
|
|
TaskCount -= R2[index2].TaskCount;
|
|
NodeCount -= R2[index2].NodeCount;
|
|
|
|
index2++;
|
|
|
|
R2State = rStart;
|
|
}
|
|
|
|
if (TaskCount > 0)
|
|
{
|
|
C[cindex].StartTime = ETime;
|
|
C[cindex].TaskCount = TaskCount;
|
|
C[cindex].NodeCount = NodeCount;
|
|
}
|
|
|
|
continue;
|
|
} /* END if (((R1State == rEnd) && (R1Time == ETime)) || ... */
|
|
|
|
if (((R1State == rStart) && (R1Time == ETime)) ||
|
|
((R2State == rStart) && (R2Time == ETime)))
|
|
{
|
|
if ((TaskCount > 0) && (ETime > C[cindex].StartTime))
|
|
{
|
|
C[cindex].EndTime = ETime;
|
|
cindex++;
|
|
}
|
|
|
|
if ((R1State == rStart) && (R1Time == ETime))
|
|
{
|
|
TaskCount += R1[index1].TaskCount;
|
|
NodeCount += R1[index1].NodeCount;
|
|
|
|
R1State = rEnd;
|
|
}
|
|
|
|
if ((R2State == rStart) && (R2Time == ETime))
|
|
{
|
|
TaskCount += R2[index2].TaskCount;
|
|
NodeCount += R2[index2].NodeCount;
|
|
|
|
R2State = rEnd;
|
|
}
|
|
|
|
if (TaskCount > 0)
|
|
{
|
|
C[cindex].StartTime = ETime;
|
|
C[cindex].TaskCount = TaskCount;
|
|
C[cindex].NodeCount = NodeCount;
|
|
}
|
|
|
|
continue;
|
|
} /* END if (((R1State == rStart) && (R1Time == ETime)) || ... */
|
|
} /* END while ((R1[index1].EndTime != 0) || (R2[index2].EndTime != 0)) */
|
|
|
|
/* terminate range */
|
|
|
|
cindex = MIN(cindex,MAX_MRANGE - 1);
|
|
|
|
C[cindex].EndTime = 0;
|
|
|
|
DBG(3,fALL)
|
|
{
|
|
DBG(6,fSCHED) DPrint("INFO: range count: %d\n",
|
|
cindex);
|
|
|
|
for (index1 = 0;C[index1].EndTime != 0;index1++)
|
|
{
|
|
DBG(5,fSCHED) DPrint("INFO: C[%02d] S: %ld E: %ld T: %3d N: %d\n",
|
|
index1,
|
|
C[index1].StartTime,
|
|
C[index1].EndTime,
|
|
C[index1].TaskCount,
|
|
C[index1].NodeCount);
|
|
|
|
if (C[index1].TaskCount <= 0)
|
|
{
|
|
IsCorrupt = TRUE;
|
|
|
|
DPrint("ALERT: corrupt taskcount detected (%d)\n",
|
|
C[index1].TaskCount);
|
|
}
|
|
|
|
if ((C[index1].NodeCount <= 0) || (C[index1].NodeCount > C[index1].TaskCount))
|
|
{
|
|
IsCorrupt = TRUE;
|
|
|
|
DPrint("ALERT: corrupt nodecount detected (%d)\n",
|
|
C[index1].NodeCount);
|
|
}
|
|
|
|
if (C[index1].EndTime < C[index1].StartTime)
|
|
{
|
|
IsCorrupt = TRUE;
|
|
|
|
DPrint("ALERT: corrupt time detected: E(%ld) < S(%ld)\n",
|
|
C[index1].EndTime,
|
|
C[index1].StartTime);
|
|
}
|
|
|
|
if ((C[index1 + 1].EndTime != 0) &&
|
|
(C[index1].EndTime > C[index1 + 1].StartTime))
|
|
{
|
|
IsCorrupt = TRUE;
|
|
|
|
DPrint("ALERT: corrupt time detected: S2(%ld) < E1(%ld)\n",
|
|
C[index1 + 1].StartTime,
|
|
C[index1].EndTime);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (IsCorrupt == TRUE)
|
|
{
|
|
DBG(3,fALL)
|
|
{
|
|
for (index1 = 0;R1[index1].EndTime != 0;index1++)
|
|
{
|
|
DPrint("INFO: R1[%d] S: %ld E: %ld T: %d N: %d\n",
|
|
index1,
|
|
R1[index1].StartTime,
|
|
R1[index1].EndTime,
|
|
R1[index1].TaskCount,
|
|
R1[index1].NodeCount);
|
|
}
|
|
|
|
for (index1 = 0;R2[index1].EndTime != 0;index1++)
|
|
{
|
|
DPrint("INFO: R2[%d] S: %ld E: %ld T: %d N: %d\n",
|
|
index1,
|
|
R2[index1].StartTime,
|
|
R2[index1].EndTime,
|
|
R2[index1].TaskCount,
|
|
R2[index1].NodeCount);
|
|
} /* END for (index1) */
|
|
}
|
|
} /* END if (IsCorrupt == TRUE) */
|
|
|
|
memcpy(R1,C,sizeof(mrange_t) * (cindex + 1));
|
|
|
|
return(SUCCESS);
|
|
} /* END MRLMerge() */
|
|
|
|
|
|
|
|
|
|
int MRangeApplyGlobalDistributionConstraints(
|
|
|
|
mrange_t *GRange, /* I/O */
|
|
mjob_t *J, /* I */
|
|
int *AvlTaskCount) /* O */
|
|
|
|
{
|
|
int rindex;
|
|
|
|
if (GRange == NULL)
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
for (rindex = 0;rindex < MAX_MRANGE;rindex++)
|
|
{
|
|
/* NYI */
|
|
} /* END for (rindex) */
|
|
|
|
return(SUCCESS);
|
|
} /* END MRangeApplyGlobalDistributionConstraints() */
|
|
|
|
|
|
|
|
|
|
int MRangeApplyLocalDistributionConstraints(
|
|
|
|
mrange_t *Range, /* I (modified) */
|
|
mjob_t *J, /* I */
|
|
mnode_t *N) /* I */
|
|
|
|
{
|
|
int rindex;
|
|
int MinTPN;
|
|
|
|
int RangeAdjusted = FALSE;
|
|
|
|
mrm_t *R;
|
|
|
|
if ((Range == NULL) || (J == NULL))
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
R = (J->RM != NULL) ? J->RM : &MRM[0];
|
|
|
|
switch (R->Type)
|
|
{
|
|
case mrmtLL:
|
|
|
|
if (J->Req[0]->TasksPerNode > 1)
|
|
MinTPN = J->Req[0]->TasksPerNode;
|
|
else
|
|
MinTPN = 1;
|
|
|
|
if ((J->Req[0]->BlockingFactor != 1) && (J->Request.NC > 0))
|
|
{
|
|
MinTPN = MAX(MinTPN,J->Request.TC / J->Request.NC);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
MinTPN = 1;
|
|
|
|
break;
|
|
} /* END switch(R->Type) */
|
|
|
|
for (rindex = 0;Range[rindex].EndTime > 0;rindex++)
|
|
{
|
|
switch(R->Type)
|
|
{
|
|
case mrmtLL:
|
|
|
|
if (Range[rindex].TaskCount < MinTPN)
|
|
{
|
|
Range[rindex].TaskCount = 0;
|
|
|
|
RangeAdjusted = TRUE;
|
|
|
|
DBG(2,fSCHED) DPrint("INFO: range[%d] taskcount for node %s violates task distribution policies (%d < %d)\n",
|
|
rindex,
|
|
N->Name,
|
|
Range[rindex].TaskCount,
|
|
MinTPN);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* NO-OP */
|
|
|
|
break;
|
|
} /* END switch(R->Type) */
|
|
} /* END for (rindex) */
|
|
|
|
if (RangeAdjusted == TRUE)
|
|
{
|
|
int rindex2;
|
|
|
|
/* remove empty ranges */
|
|
|
|
rindex2 = 0;
|
|
|
|
for (rindex = 0;Range[rindex].EndTime > 0;rindex++)
|
|
{
|
|
if (Range[rindex].TaskCount > 0)
|
|
{
|
|
if (rindex != rindex2)
|
|
memcpy(&Range[rindex2],&Range[rindex],sizeof(mrange_t));
|
|
|
|
rindex2++;
|
|
}
|
|
} /* END for (rindex) */
|
|
|
|
Range[rindex2].EndTime = 0;
|
|
} /* END if (RangeAdjusted == TRUE) */
|
|
|
|
return(SUCCESS);
|
|
} /* END MRangeApplyLocalDistributionConstraints() */
|
|
|
|
|
|
|
|
|
|
int MResAdjustGResUsage(
|
|
|
|
mres_t *R, /* I */
|
|
int TC) /* I */
|
|
|
|
{
|
|
mrange_t tmpRange[2];
|
|
|
|
if (R == NULL)
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
tmpRange[0].StartTime = R->StartTime;
|
|
tmpRange[0].EndTime = R->EndTime;
|
|
tmpRange[0].TaskCount = TC;
|
|
|
|
tmpRange[1].EndTime = 0;
|
|
|
|
if (TC > 0)
|
|
{
|
|
/*
|
|
MRLMerge(MRange,tmpRange,TC,NULL);
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
MRLSubtract(MRange,tmpRange);
|
|
*/
|
|
}
|
|
|
|
return(SUCCESS);
|
|
} /* END MResAdjustGResUsage() */
|
|
|
|
|
|
|
|
|
|
int MResQSortHLEndTimeComp(
|
|
|
|
int *A, /* I */
|
|
int *B) /* I */
|
|
|
|
{
|
|
static int tmp;
|
|
|
|
/* order low to high */
|
|
|
|
tmp = MRes[*A]->EndTime - MRes[*B]->EndTime;
|
|
|
|
return(tmp);
|
|
} /* END MResQSortComp() */
|
|
|
|
|
|
|
|
|
|
int MResInitialize(
|
|
|
|
mres_t **RP, /* I (modified) */
|
|
char *RName) /* I (optional) */
|
|
|
|
{
|
|
if (RP == NULL)
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
if (*RP == NULL)
|
|
{
|
|
*RP = calloc(1,sizeof(mres_t));
|
|
}
|
|
else
|
|
{
|
|
memset(*RP,0,sizeof(mres_t));
|
|
}
|
|
|
|
if (RName != NULL)
|
|
MUStrCpy((*RP)->Name,RName,sizeof((*RP)->Name));
|
|
else
|
|
(*RP)->Name[0] = '\1';
|
|
|
|
return(SUCCESS);
|
|
} /* END MResInitialize() */
|
|
|
|
|
|
|
|
|
|
int MResGetRID(
|
|
|
|
mres_t *R,
|
|
char *RBase,
|
|
char *RUName)
|
|
|
|
{
|
|
mres_t *tmpR;
|
|
|
|
int index;
|
|
int rindex;
|
|
|
|
int tmpI;
|
|
|
|
char tmpName[MAX_MNAME];
|
|
|
|
if ((R == NULL) || (RBase == NULL) || (RUName == NULL))
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
index = 0;
|
|
|
|
sprintf(tmpName,"%s.",
|
|
RBase);
|
|
|
|
for (rindex = 0;rindex < MAX_MRES;rindex++)
|
|
{
|
|
tmpR = MRes[rindex];
|
|
|
|
if ((tmpR == NULL) || (tmpR->Name[0] == '\0'))
|
|
break;
|
|
|
|
if (tmpR->Name[0] == '\1')
|
|
continue;
|
|
|
|
if (strncmp(tmpR->Name,tmpName,strlen(tmpName)))
|
|
continue;
|
|
|
|
tmpI = strtol(&tmpR->Name[strlen(tmpName)],NULL,0);
|
|
|
|
index = MAX(index,tmpI + 1);
|
|
} /* for (rindex) */
|
|
|
|
sprintf(RUName,"%s.%d",
|
|
RBase,
|
|
index);
|
|
|
|
DBG(6,fSTRUCT) DPrint("INFO: unique reservation ID '%s' selected\n",
|
|
RUName);
|
|
|
|
return(SUCCESS);
|
|
} /* END MResGetRID() */
|
|
|
|
|
|
|
|
|
|
int MResJCreate(
|
|
|
|
mjob_t *J, /* I: job reserving nodes */
|
|
mnodelist_t MNodeList, /* I: nodes to be reserved */
|
|
long StartTime, /* I: time reservation starts */
|
|
int ResType, /* I: purpose of reservation */
|
|
mres_t **RP) /* O: reservation pointer (optional) */
|
|
|
|
{
|
|
int rindex;
|
|
|
|
int rqindex;
|
|
int nindex;
|
|
|
|
mres_t *R;
|
|
mnode_t *N;
|
|
mreq_t *RQ;
|
|
|
|
int RC;
|
|
|
|
double NPFactor;
|
|
|
|
int TaskCount;
|
|
|
|
const char *FName = "MResJCreate";
|
|
|
|
DBG(3,fSTRUCT) DPrint("%s(%s,MNodeList,%s,%s,Res)\n",
|
|
FName,
|
|
(J != NULL) ? J->Name : "NULL",
|
|
MULToTString(StartTime - MSched.Time),
|
|
MJRType[ResType]);
|
|
|
|
if ((J == NULL) || (J->Req[0] == NULL))
|
|
{
|
|
DBG(1,fSTRUCT) DPrint("ERROR: invalid job passed to %s()\n",
|
|
FName);
|
|
|
|
return(FAILURE);
|
|
}
|
|
|
|
RQ = J->Req[0];
|
|
|
|
/* verify nodelist */
|
|
|
|
if ((MNodeList == NULL) &&
|
|
((RQ == NULL) || (RQ->NodeList == NULL)))
|
|
{
|
|
DBG(1,fSTRUCT) DPrint("ERROR: invalid nodelist passed to %s()\n",
|
|
FName);
|
|
|
|
return(FAILURE);
|
|
}
|
|
|
|
MTRAPJOB(J,FName);
|
|
|
|
/* find first available reservation slot */
|
|
|
|
for (rindex = 0;rindex < MAX_MRES;rindex++)
|
|
{
|
|
R = MRes[rindex];
|
|
|
|
if ((R == NULL) || (R->Name[0] <= '\1'))
|
|
break;
|
|
} /* END for (rindex) */
|
|
|
|
if (rindex >= MAX_MRES)
|
|
{
|
|
DBG(1,fSTRUCT) DPrint("ERROR: reservation overflow on reservation for job '%s'\n",
|
|
J->Name);
|
|
|
|
if (MSched.Mode == msmNormal)
|
|
{
|
|
MOSSyslog(LOG_ERR,"ERROR: reservation overflow on job '%s'",
|
|
J->Name);
|
|
}
|
|
|
|
return(FAILURE);
|
|
}
|
|
|
|
if (MResInitialize(&MRes[rindex],NULL) == FAILURE)
|
|
{
|
|
DBG(1,fSTRUCT) DPrint("ERROR: cannot initialize reservation for job '%s'\n",
|
|
J->Name);
|
|
|
|
return(FAILURE);
|
|
}
|
|
|
|
R = MRes[rindex];
|
|
|
|
R->Index = rindex;
|
|
|
|
if (RP != NULL)
|
|
*RP = R;
|
|
|
|
/* link job to reservation */
|
|
|
|
if (J->R != NULL)
|
|
{
|
|
DBG(0,fSTRUCT) DPrint("ERROR: reservation created for reserved job '%s' (existing reservation '%s' deleted)\n",
|
|
J->Name,
|
|
J->R->Name);
|
|
|
|
MResDestroy(&J->R);
|
|
}
|
|
|
|
J->R = R;
|
|
|
|
R->J = J;
|
|
R->Mode = 0;
|
|
|
|
/* build reservation */
|
|
|
|
strcpy(R->Name,J->Name);
|
|
|
|
R->Type = mrtJob;
|
|
|
|
/* get partition number from first node */
|
|
|
|
R->StartTime = StartTime;
|
|
|
|
NPFactor = 9999997.0;
|
|
|
|
if (MNodeList != NULL)
|
|
{
|
|
double tmpD;
|
|
|
|
/* get partition number from first node */
|
|
|
|
if (MNodeList[0][0].N != NULL)
|
|
R->PtIndex = MNodeList[0][0].N->PtIndex;
|
|
else
|
|
R->PtIndex = 0;
|
|
|
|
for (rqindex = 0;MNodeList[rqindex][0].N != NULL;rqindex++)
|
|
{
|
|
MUNLGetMinAVal((mnalloc_t *)&MNodeList[rqindex][0],mnaSpeed,NULL,(void **)&tmpD);
|
|
|
|
NPFactor = MIN(NPFactor,tmpD);
|
|
} /* END for (rqindex) */
|
|
}
|
|
else
|
|
{
|
|
double tmpD;
|
|
|
|
/* get partition number from first node */
|
|
|
|
RQ = J->Req[0];
|
|
|
|
if (RQ->NodeList[0].N != NULL)
|
|
R->PtIndex = RQ->NodeList[0].N->PtIndex;
|
|
else
|
|
R->PtIndex = 1;
|
|
|
|
for (rqindex = 0;J->Req[rqindex] != NULL;rqindex++)
|
|
{
|
|
RQ = J->Req[rqindex];
|
|
|
|
if (RQ->NodeList != NULL)
|
|
{
|
|
if (MUNLGetMinAVal(
|
|
RQ->NodeList,
|
|
mnaSpeed,
|
|
NULL,
|
|
(void *)&tmpD) == FAILURE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
NPFactor = MIN(NPFactor,tmpD);
|
|
}
|
|
} /* END for (rqindex) */
|
|
} /* END (MNodeList != NULL) */
|
|
|
|
if ((NPFactor <= 0.0001) || (NPFactor >= 9999990.0))
|
|
{
|
|
NPFactor = 1.0;
|
|
}
|
|
|
|
R->EndTime = MAX(
|
|
StartTime + (int)((double)J->SpecWCLimit[0] / NPFactor),
|
|
MSched.Time + 1);
|
|
|
|
R->EndTime = MIN(R->EndTime,MAX_MTIME - 1);
|
|
|
|
if (StartTime == 0)
|
|
{
|
|
DBG(0,fSTRUCT) DPrint("ERROR: invalid StartTime specified for reservation on job res '%s'\n",
|
|
R->Name);
|
|
|
|
StartTime = 1;
|
|
}
|
|
|
|
/* NOTE: only handle single task description per job */
|
|
|
|
RQ = J->Req[0];
|
|
|
|
MREInsert(MRE,R->StartTime,R->EndTime,rindex,&RQ->DRes,MAX_MRES << 2);
|
|
|
|
/* link nodes to reservation */
|
|
|
|
DBG(6,fSTRUCT) DPrint("INFO: linking nodes to reservation '%s'\n",
|
|
R->Name);
|
|
|
|
R->NodeCount = 0;
|
|
R->TaskCount = 0;
|
|
R->AllocPC = MJobGetProcCount(J);
|
|
|
|
/* copy ACL/CL info */
|
|
|
|
memcpy(R->ACL,J->Cred.ACL,sizeof(R->ACL));
|
|
memcpy(R->CL,J->Cred.CL,sizeof(R->CL));
|
|
|
|
MACLSet(R->ACL,maJob,J->Name,mcmpSEQ,nmNeutralAffinity,0,0);
|
|
|
|
nindex = 0;
|
|
rqindex = 0;
|
|
|
|
while (1)
|
|
{
|
|
RQ = J->Req[rqindex];
|
|
|
|
/* NYI : must handle class ACL management for multi-req jobs */
|
|
|
|
if (MNodeList == NULL)
|
|
{
|
|
if ((RQ == NULL) ||
|
|
(RQ->NodeList == NULL) ||
|
|
(rqindex >= MAX_MREQ_PER_JOB))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ((RQ->NodeList[nindex].N == NULL) ||
|
|
(RQ->NodeList[nindex].TC == 0))
|
|
{
|
|
rqindex++;
|
|
|
|
nindex = 0;
|
|
|
|
continue;
|
|
}
|
|
|
|
N = RQ->NodeList[nindex].N;
|
|
TaskCount = RQ->NodeList[nindex].TC;
|
|
|
|
nindex++;
|
|
}
|
|
else
|
|
{
|
|
if (rqindex >= MAX_MREQ_PER_JOB)
|
|
break;
|
|
|
|
if ((MNodeList[rqindex][0].N == NULL) ||
|
|
(MNodeList[rqindex][0].TC == 0))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ((MNodeList[rqindex][nindex].N == NULL) ||
|
|
(MNodeList[rqindex][nindex].TC == 0))
|
|
{
|
|
rqindex++;
|
|
|
|
nindex = 0;
|
|
|
|
continue;
|
|
}
|
|
|
|
N = MNodeList[rqindex][nindex].N;
|
|
TaskCount = MNodeList[rqindex][nindex].TC;
|
|
|
|
nindex++;
|
|
} /* END else (MNodeList == NULL) */
|
|
|
|
if ((N->Name[0] == '\0') || (N->Name[0] == '\1'))
|
|
{
|
|
DBG(0,fSTRUCT) DPrint("ERROR: job '%s' reservation nodelist contains invalid node at index %d\n",
|
|
J->Name,
|
|
nindex - 1);
|
|
|
|
MResDestroy(&J->R);
|
|
|
|
return(FAILURE);
|
|
}
|
|
|
|
if ((N->PtIndex != R->PtIndex) &&
|
|
(N->PtIndex != 0) &&
|
|
!(J->Flags & (1 << mjfSpan)))
|
|
{
|
|
DBG(0,fSTRUCT) DPrint("ERROR: job '%s' reservation spans partitions (node %s: %d node %s: %d)\n",
|
|
J->Name,
|
|
(MNodeList != NULL) ?
|
|
MNodeList[rqindex][0].N->Name :
|
|
RQ->NodeList[0].N->Name,
|
|
R->PtIndex,
|
|
N->Name,
|
|
N->PtIndex);
|
|
|
|
MResDestroy(&J->R);
|
|
|
|
return(FAILURE);
|
|
}
|
|
|
|
MTRAPNODE(N,FName);
|
|
|
|
if ((RQ->NAccessPolicy == mnacSingleJob) ||
|
|
(RQ->NAccessPolicy == mnacSingleTask))
|
|
{
|
|
memset(&R->DRes,0,sizeof(R->DRes));
|
|
|
|
R->DRes.Procs = -1;
|
|
|
|
RC = 1;
|
|
}
|
|
else
|
|
{
|
|
memcpy(&R->DRes,&RQ->DRes,sizeof(R->DRes));
|
|
|
|
RC = TaskCount;
|
|
}
|
|
|
|
R->NodeCount ++;
|
|
R->TaskCount += RC;
|
|
|
|
if (MResAddNode(R,N,RC,0) == FAILURE)
|
|
{
|
|
MResDestroy(&R);
|
|
|
|
return(FAILURE);
|
|
}
|
|
} /* END while (1) */
|
|
|
|
J->RType = ResType;
|
|
|
|
if (MSched.InitialLoad != TRUE)
|
|
{
|
|
MResAdjustDRes(J->Name,0);
|
|
}
|
|
|
|
R->CTime = MSched.Time;
|
|
R->MTime = MSched.Time;
|
|
|
|
return(SUCCESS);
|
|
} /* END MResJCreate() */
|
|
|
|
|
|
|
|
|
|
int MResFreeTable()
|
|
|
|
{
|
|
int rindex;
|
|
|
|
mres_t *R;
|
|
|
|
for (rindex = 0;rindex < MAX_MRES;rindex++)
|
|
{
|
|
R = MRes[rindex];
|
|
|
|
if (R == NULL)
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
if (R == (mres_t *)1)
|
|
continue;
|
|
|
|
if ((R->Name[0] == '\0') || (R->Name[0] == '\1'))
|
|
continue;
|
|
|
|
MResDestroy(&R);
|
|
|
|
MUFree((char **)&MRes[rindex]);
|
|
} /* END for (rindex) */
|
|
|
|
return(SUCCESS);
|
|
} /* END MResFreeTable() */
|
|
|
|
|
|
|
|
|
|
int MREInsert(
|
|
|
|
mre_t *RE, /* I (array modified) */
|
|
long StartTime, /* I */
|
|
long EndTime, /* I */
|
|
int ResIndex, /* I */
|
|
mcres_t *DRes, /* I */
|
|
int TableSize) /* I */
|
|
|
|
{
|
|
int index;
|
|
int startindex;
|
|
int EndLocated;
|
|
|
|
if (RE == NULL)
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
/* NOTE: only one start/end pair per job/user RE at creation time */
|
|
|
|
if (RE[0].Type == mreNONE)
|
|
{
|
|
RE[0].Time = StartTime;
|
|
RE[0].Type = mreStart;
|
|
RE[0].Index = ResIndex;
|
|
memcpy(&RE[0].DRes,DRes,sizeof(RE[0].DRes));
|
|
|
|
RE[1].Time = EndTime;
|
|
RE[1].Type = mreEnd;
|
|
RE[1].Index = ResIndex;
|
|
memcpy(&RE[1].DRes,DRes,sizeof(RE[0].DRes));
|
|
|
|
RE[2].Type = mreNONE;
|
|
}
|
|
else
|
|
{
|
|
/* locate last res-event */
|
|
|
|
for (index = 0;index < TableSize;index++)
|
|
{
|
|
if (RE[index].Type == mreNONE)
|
|
{
|
|
/* terminate list at new end point */
|
|
|
|
RE[MIN(index + 2,TableSize)].Type = mreNONE;
|
|
|
|
break;
|
|
}
|
|
} /* END for (index) */
|
|
|
|
if (index == TableSize)
|
|
{
|
|
DBG(3,fSCHED) DPrint("ALERT: RE table overflow detected - increase %s\n",
|
|
MParam[pResDepth]);
|
|
|
|
return(FAILURE);
|
|
}
|
|
|
|
/* insert end event */
|
|
|
|
startindex = MAX(0,index - 1);
|
|
|
|
EndLocated = FALSE;
|
|
|
|
for (index = startindex;index >= 0;index--)
|
|
{
|
|
if (EndTime >= RE[index].Time)
|
|
{
|
|
/* insert end event */
|
|
|
|
RE[index + 2].Time = EndTime;
|
|
RE[index + 2].Type = mreEnd;
|
|
RE[index + 2].Index = ResIndex;
|
|
memcpy(&RE[index + 2].DRes,DRes,sizeof(RE[0].DRes));
|
|
|
|
EndLocated = TRUE;
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
memcpy(&RE[index + 2],&RE[index],sizeof(RE[0]));
|
|
}
|
|
} /* END for (index) */
|
|
|
|
if (EndLocated == FALSE)
|
|
{
|
|
RE[1].Time = EndTime;
|
|
RE[1].Type = mreEnd;
|
|
RE[1].Index = ResIndex;
|
|
memcpy(&RE[1].DRes,DRes,sizeof(RE[0].DRes));
|
|
}
|
|
|
|
/* insert start event */
|
|
|
|
startindex = index;
|
|
|
|
for (index = startindex;index >= 0;index--)
|
|
{
|
|
if (StartTime >= RE[index].Time)
|
|
{
|
|
/* insert start event */
|
|
|
|
RE[index + 1].Time = StartTime;
|
|
RE[index + 1].Type = mreStart;
|
|
RE[index + 1].Index = ResIndex;
|
|
memcpy(&RE[index + 1].DRes,DRes,sizeof(RE[0].DRes));
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
memcpy(&RE[index + 1],&RE[index],sizeof(RE[0]));
|
|
}
|
|
} /* END for (index) */
|
|
|
|
/* prepend start event if necessary */
|
|
|
|
if (StartTime < RE[0].Time)
|
|
{
|
|
RE[0].Time = StartTime;
|
|
RE[0].Type = mreStart;
|
|
RE[0].Index = ResIndex;
|
|
memcpy(&RE[0].DRes,DRes,sizeof(RE[0].DRes));
|
|
}
|
|
} /* END else (RE[0].Type == mreNONE) */
|
|
|
|
return(SUCCESS);
|
|
} /* END MREInsert() */
|
|
|
|
|
|
|
|
|
|
int MRERelease(
|
|
|
|
mre_t *RE, /* I */
|
|
int ResIndex,
|
|
int TableSize)
|
|
|
|
{
|
|
int index;
|
|
int Offset;
|
|
|
|
if (RE == NULL)
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
Offset = 0;
|
|
|
|
for (index = 0;index < TableSize;index++)
|
|
{
|
|
if (RE[index].Type == mreNONE)
|
|
break;
|
|
|
|
if (RE[index].Index == ResIndex)
|
|
{
|
|
Offset++;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (Offset > 0)
|
|
{
|
|
memcpy(&RE[index - Offset],&RE[index],sizeof(RE[0]));
|
|
}
|
|
} /* END for (index) */
|
|
|
|
RE[index - Offset].Type = mreNONE;
|
|
|
|
return(SUCCESS);
|
|
} /* END MRERelease() */
|
|
|
|
|
|
|
|
|
|
int MResShowState(
|
|
|
|
mres_t *R,
|
|
int Flags,
|
|
char *Buffer,
|
|
int BufSize,
|
|
int Mode) /* I */
|
|
|
|
{
|
|
char *BPtr;
|
|
int BSpace;
|
|
|
|
char tmpLine[MAX_MLINE];
|
|
|
|
const char *FName = "MResShowState";
|
|
|
|
DBG(5,fSTRUCT) DPrint("%s(%s,%d,Buffer,BufSize,%d)\n",
|
|
FName,
|
|
(R != NULL) ? "NONE" : R->Name,
|
|
Flags,
|
|
Mode);
|
|
|
|
if (Buffer == NULL)
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
BPtr = Buffer;
|
|
BSpace = BufSize;
|
|
|
|
if (Mode == mSet)
|
|
{
|
|
Buffer[0] = '\0';
|
|
}
|
|
else
|
|
{
|
|
int len;
|
|
|
|
len = strlen(Buffer);
|
|
|
|
BPtr += len;
|
|
BSpace -= len;
|
|
}
|
|
|
|
if (R == NULL)
|
|
{
|
|
/* create header */
|
|
|
|
MUSNPrintF(&BPtr,&BSpace,"%-20s %10s %3.3s %11s %11s %11s %4s %4s %4s\n",
|
|
"ResID",
|
|
"Type",
|
|
"Par",
|
|
"StartTime",
|
|
"EndTime",
|
|
"Duration",
|
|
"Node",
|
|
"Task",
|
|
"Proc");
|
|
|
|
MUSNPrintF(&BPtr,&BSpace,"%-20s %10s %3.3s %11s %11s %11s %4s %4s %4s\n",
|
|
"-----",
|
|
"----",
|
|
"---",
|
|
"---------",
|
|
"-------",
|
|
"--------",
|
|
"----",
|
|
"----",
|
|
"----");
|
|
|
|
return(SUCCESS);
|
|
} /* END if (R == NULL) */
|
|
|
|
/* show base data */
|
|
|
|
{
|
|
char StartTime[MAX_MNAME];
|
|
char EndTime[MAX_MNAME];
|
|
char Duration[MAX_MNAME];
|
|
|
|
strcpy(StartTime,MULToTString(R->StartTime - MSched.Time));
|
|
strcpy(EndTime,MULToTString(R->EndTime - MSched.Time));
|
|
strcpy(Duration,MULToTString(R->EndTime - R->StartTime));
|
|
|
|
MUSNPrintF(&BPtr,&BSpace,"%-20s %10s %3.3s %11s %11s %11s %4d %4d %4d\n",
|
|
R->Name,
|
|
MResType[R->Type],
|
|
(R->PtIndex >= 0) ? MAList[ePartition][R->PtIndex] : "ALL",
|
|
StartTime,
|
|
EndTime,
|
|
Duration,
|
|
R->NodeCount,
|
|
R->TaskCount,
|
|
R->AllocPC);
|
|
} /* END BLOCK */
|
|
|
|
/* display reservation flags */
|
|
|
|
if ((R->Flags != 0) || (Flags & (1 << mcmVerbose)))
|
|
{
|
|
MUSNPrintF(&BPtr,&BSpace," Flags: %s\n",
|
|
MUBMToString(R->Flags,MResFlags,' ',tmpLine,NONE));
|
|
}
|
|
|
|
/* display reservation ACL/CL */
|
|
|
|
MACLListShow(R->ACL,-1,(1 << mfmHuman)|(1 << mfmVerbose),tmpLine);
|
|
|
|
if (tmpLine[0] != '\0')
|
|
{
|
|
MUSNPrintF(&BPtr,&BSpace," ACL: %s\n",
|
|
tmpLine);
|
|
}
|
|
|
|
MACLListShow(R->CL,-1,(1 << mfmHuman),tmpLine);
|
|
|
|
if (tmpLine[0] != '\0')
|
|
{
|
|
MUSNPrintF(&BPtr,&BSpace," CL: %s\n",
|
|
tmpLine);
|
|
}
|
|
|
|
MResAToString(R,mraOwner,tmpLine,0,0);
|
|
|
|
if (tmpLine[0] != '\0')
|
|
{
|
|
MUSNPrintF(&BPtr,&BSpace," Owner: %s\n",
|
|
tmpLine);
|
|
}
|
|
|
|
/* display accounting creds */
|
|
|
|
if (R->Type == mrtUser)
|
|
{
|
|
if ((R->A != NULL) ||
|
|
(R->G != NULL) ||
|
|
(R->U != NULL) ||
|
|
(Flags & (1 << mcmVerbose)))
|
|
{
|
|
MUSNPrintF(&BPtr,&BSpace," Accounting Creds: %s%s %s%s %s%s",
|
|
(R->A != NULL) ? "Account:" : "",
|
|
(R->A != NULL) ? R->A->Name : "",
|
|
(R->G != NULL) ? "Group:" : "",
|
|
(R->G != NULL) ? R->G->Name : "",
|
|
(R->U != NULL) ? "User:" : "",
|
|
(R->U != NULL) ? R->U->Name : "");
|
|
} /* END if (R->A != NULL) ... ) */
|
|
} /* END if (R->Type == mrtUser) */
|
|
|
|
/* display per task dedicated resources */
|
|
|
|
if ((R->Type == mrtUser) ||
|
|
((R->Type == mrtJob) && (R->DRes.Procs == -1)))
|
|
{
|
|
MUSNPrintF(&BPtr,&BSpace," Task Resources: %s\n",
|
|
MUCResToString(&R->DRes,0,0,NULL));
|
|
}
|
|
|
|
/* display reservation attributes */
|
|
|
|
tmpLine[0] = '\0';
|
|
|
|
if (R->RegEx != NULL)
|
|
{
|
|
snprintf(tmpLine,MAX_MLINE,"%s HostList='%s'",
|
|
tmpLine,
|
|
R->RegEx);
|
|
}
|
|
|
|
if (R->Priority > 0)
|
|
{
|
|
sprintf(tmpLine,"%s Priority=%ld",
|
|
tmpLine,
|
|
R->Priority);
|
|
}
|
|
|
|
if (R->SystemID != NULL)
|
|
{
|
|
snprintf(tmpLine,MAX_MLINE,"%s SystemID=%s",
|
|
tmpLine,
|
|
R->SystemID);
|
|
}
|
|
|
|
if (R->MaxTasks > 0)
|
|
{
|
|
sprintf(tmpLine,"%s MaxTasks=%d",
|
|
tmpLine,
|
|
R->MaxTasks);
|
|
}
|
|
|
|
if (R->CBHost[0] != '\0')
|
|
{
|
|
int index;
|
|
|
|
char tmpCBLine[MAX_MLINE];
|
|
|
|
tmpCBLine[0] = '\0';
|
|
|
|
for (index = 0;MCBType[index] != NULL;index++)
|
|
{
|
|
if (R->CBType & (1 << index))
|
|
{
|
|
if (tmpCBLine[0] != '\0')
|
|
strcat(tmpCBLine,",");
|
|
|
|
strcat(tmpCBLine,MCBType[index]);
|
|
}
|
|
} /* END for (index) */
|
|
|
|
sprintf(tmpLine,"%s CBServer=%s:%d/%s",
|
|
tmpLine,
|
|
R->CBHost,
|
|
R->CBPort,
|
|
tmpCBLine);
|
|
} /* END if (R->CBHost[0] != '\0') */
|
|
|
|
if (tmpLine[0] != '\0')
|
|
{
|
|
/* NOTE: skip leading whitespace */
|
|
|
|
MUSNPrintF(&BPtr,&BSpace," Attributes (%s)\n",
|
|
&tmpLine[3]);
|
|
}
|
|
|
|
/* display statistics */
|
|
|
|
if ((R->Type != mrtJob) &&
|
|
((R->TAPS + R->CAPS + R->TIPS + R->CIPS) > 0.0))
|
|
{
|
|
double E;
|
|
|
|
if ((R->TAPS + R->CAPS) > 0.0)
|
|
{
|
|
E = (1.0 - (double)(R->TIPS + R->CIPS) /
|
|
(R->TAPS + R->CAPS + R->TIPS + R->CIPS)) * 100.0;
|
|
}
|
|
else
|
|
{
|
|
E = 0.0;
|
|
}
|
|
|
|
MUSNPrintF(&BPtr,&BSpace," Active PH: %.2f/%-.2f (%.2f%c)\n",
|
|
(R->TAPS + R->CAPS) / 3600.0,
|
|
(R->TAPS + R->CAPS + R->TIPS + R->CIPS) / 3600.0,
|
|
E,
|
|
'%');
|
|
} /* END if (R->Type != mrtJob) && ...) */
|
|
|
|
/* display SR attributes */
|
|
|
|
if (R->Flags & (1 << mrfStandingRes))
|
|
{
|
|
int srindex;
|
|
int dindex;
|
|
|
|
char tmpLine[MAX_MLINE];
|
|
char tmpTString[MAX_MNAME];
|
|
|
|
sres_t *S;
|
|
sres_t tmpSR;
|
|
|
|
for (srindex = 0;srindex < MAX_MSRES;srindex++)
|
|
{
|
|
S = &SRes[srindex];
|
|
|
|
if (strncmp(S->Name,R->Name,strlen(S->Name)))
|
|
continue;
|
|
|
|
for (dindex = 0;dindex < MAX_SRES_DEPTH;dindex++)
|
|
{
|
|
if (S->R[dindex] == R)
|
|
{
|
|
MSRGetCurrentValues(S,&OSRes[srindex],&tmpSR);
|
|
|
|
if (tmpSR.StartTime == -1)
|
|
strcpy(tmpTString,MULToTString(0));
|
|
else
|
|
strcpy(tmpTString,MULToTString(tmpSR.StartTime));
|
|
|
|
if (S->Days == 255)
|
|
{
|
|
strcpy(tmpLine,"ALL");
|
|
}
|
|
else
|
|
{
|
|
int index;
|
|
|
|
tmpLine[0] = '\0';
|
|
|
|
for (index = 0;MWeekDay[index] != NULL;index++)
|
|
{
|
|
if (S->Days & (1 << index))
|
|
{
|
|
if (tmpLine[0] != '\0')
|
|
strcat(tmpLine,",");
|
|
|
|
strcat(tmpLine,MWeekDay[index]);
|
|
}
|
|
} /* END for (index) */
|
|
} /* END else (S->Days == 255) */
|
|
|
|
MUSNPrintF(&BPtr,&BSpace," SRAttributes (TaskCount: %d StartTime: %s EndTime: %s Days: %s)\n",
|
|
tmpSR.TaskCount,
|
|
tmpTString,
|
|
(tmpSR.EndTime == -1) ?
|
|
MULToTString(86400) :
|
|
MULToTString((tmpSR.EndTime > 0) ? tmpSR.EndTime : 86400),
|
|
(tmpLine[0] != '\0') ? tmpLine : ALL);
|
|
|
|
break;
|
|
} /* END if (SRes[srindex] == R) */
|
|
} /* END for (dindex) */
|
|
} /* END for (srindex) */
|
|
} /* END if (R->Flags & (1 << mrfStandingRes)) */
|
|
|
|
/* display extension attributes */
|
|
|
|
if ((R->xd != NULL) && (X.XResShow != (int (*)())0))
|
|
{
|
|
if (((*X.XResShow)(X.xd,R,tmpLine) == SUCCESS) &&
|
|
(tmpLine[0] != '\0'))
|
|
{
|
|
MUStrNCat(&BPtr,&BSpace,tmpLine);
|
|
}
|
|
}
|
|
|
|
return(SUCCESS);
|
|
} /* END MResShowState() */
|
|
|
|
|
|
|
|
|
|
int MResAdjustDRes(
|
|
|
|
char *JName, /* I (optional) */
|
|
int ForceEval) /* I (boolean) */
|
|
|
|
{
|
|
mres_t *JR;
|
|
mres_t *R;
|
|
|
|
mjob_t *J;
|
|
mnode_t *N;
|
|
|
|
int JRC;
|
|
|
|
int nindex;
|
|
int crindex;
|
|
|
|
int jrindex1;
|
|
int jrindex2;
|
|
|
|
int oreindex;
|
|
int nreindex;
|
|
int creindex;
|
|
int jreindex;
|
|
int treindex;
|
|
|
|
int NameFound; /* (boolean) */
|
|
|
|
long Overlap;
|
|
long InitialJStartTime;
|
|
long CRStartTime;
|
|
long CREndTime;
|
|
long CROverlap;
|
|
|
|
static short CRC[MAX_MRES_DEPTH];
|
|
static short CRI[MAX_MRES_DEPTH];
|
|
static short CRESI[MAX_MRES_DEPTH];
|
|
static short CREEI[MAX_MRES_DEPTH];
|
|
|
|
static mre_t mCRE[MAX_MRES_DEPTH];
|
|
|
|
static mre_t *CRE = NULL; /* container RE */
|
|
static mre_t *ORE = NULL; /* old RE */
|
|
static mre_t *NRE = NULL; /* new RE */
|
|
static mre_t *JRE = NULL; /* job RE */
|
|
|
|
static int DRSize;
|
|
|
|
long JRETime;
|
|
|
|
mcres_t BR;
|
|
mcres_t IBR; /* blocked resources */
|
|
|
|
static mcres_t ZRes; /* null CRes */
|
|
|
|
int OPC;
|
|
int NPC;
|
|
|
|
const char *FName = "MResAdjustDRes";
|
|
|
|
DBG(4,fCORE) DPrint("%s(%s,%s)\n",
|
|
FName,
|
|
(JName == NULL) ? "NULL" : JName,
|
|
(ForceEval == TRUE) ? "TRUE" : "FALSE");
|
|
|
|
/* NOTE: not thread safe */
|
|
|
|
/* must calculate BRes for each 'container' reservation at */
|
|
/* each 'job' event boundary. collapse container reservations */
|
|
/* where possible using memcmp. Insert all container reservation */
|
|
/* events at BRes calculation completion */
|
|
|
|
if (CRE == NULL || ORE == NULL || NRE == NULL || JRE == NULL )
|
|
{
|
|
memset(&ZRes,0,sizeof(ZRes));
|
|
|
|
/* changed the mallocs to callocs because there is code below */
|
|
/* that requires things to be zero'ed */
|
|
if (CRE == NULL ) {
|
|
CRE = (mre_t *)calloc((MSched.ResDepth << 1),sizeof(mre_t));
|
|
}
|
|
if (ORE == NULL ){
|
|
ORE = (mre_t *)calloc((MSched.ResDepth << 1),sizeof(mre_t));
|
|
}
|
|
if (NRE == NULL ){
|
|
NRE = (mre_t *)calloc((MSched.ResDepth << 1),sizeof(mre_t));
|
|
}
|
|
if (JRE == NULL ){
|
|
JRE = (mre_t *)calloc((MSched.ResDepth << 1),sizeof(mre_t));
|
|
}
|
|
|
|
DRSize = sizeof(mcres_t);
|
|
} /* END if (CRE == NULL) */
|
|
|
|
if (CRE == NULL || ORE == NULL || NRE == NULL || JRE == NULL )
|
|
{
|
|
DBG(4,fCORE) DPrint("ALERT: cannot allocate memory in %s\n",
|
|
FName);
|
|
|
|
return(FAILURE);
|
|
}
|
|
|
|
for (nindex = 0;nindex < MAX_MNODE;nindex++)
|
|
{
|
|
N = MNode[nindex];
|
|
|
|
if ((N == NULL) || (N->Name[0] == '\0'))
|
|
break;
|
|
|
|
if (N->Name[0] == '\1')
|
|
continue;
|
|
|
|
if (N->ResMTime < MSched.Time)
|
|
continue;
|
|
|
|
CRI[0] = -1;
|
|
|
|
NameFound = FALSE;
|
|
|
|
jreindex = 0;
|
|
|
|
JRETime = 0;
|
|
|
|
for (nreindex = 0;N->RE[nreindex].Type != mreNONE;nreindex++)
|
|
{
|
|
if (nreindex >= (MSched.ResDepth << 1))
|
|
break;
|
|
|
|
if (jreindex >= (MSched.ResDepth << 1) - 1)
|
|
break;
|
|
|
|
if ((N->R[N->RE[nreindex].Index] == NULL) || (N->R[N->RE[nreindex].Index] == (mrsv_t *)1))
|
|
continue;
|
|
|
|
if (N->R[N->RE[nreindex].Index]->Type == mrtJob)
|
|
{
|
|
JRETime = N->RE[nreindex].Time;
|
|
|
|
/* job reservation located */
|
|
|
|
memcpy(&JRE[jreindex],&N->RE[nreindex],sizeof(JRE[0]));
|
|
|
|
J = (mjob_t *)(N->R[N->RE[nreindex].Index]->J);
|
|
|
|
if ((NameFound == FALSE) && (JName != NULL))
|
|
{
|
|
if (!strcmp(J->Name,JName))
|
|
{
|
|
MTRAPJOB(J,FName);
|
|
|
|
NameFound = TRUE;
|
|
}
|
|
}
|
|
|
|
jreindex++;
|
|
}
|
|
else
|
|
{
|
|
/* record each unique container reservation */
|
|
|
|
for (crindex = 0;crindex < MSched.ResDepth;crindex++)
|
|
{
|
|
if (N->RE[nreindex].Index == CRI[crindex])
|
|
{
|
|
/* verify job res event matches CR split start time */
|
|
/* if not, both event should block same (MAX) resources */
|
|
|
|
if (JRETime != N->RE[nreindex].Time)
|
|
{
|
|
/* look for matching job re entries */
|
|
|
|
for (treindex = nreindex + 1;N->RE[treindex].Type != mreNONE;treindex++)
|
|
{
|
|
if (N->RE[treindex].Time != N->RE[nreindex].Time)
|
|
break;
|
|
|
|
if (N->R[N->RE[nreindex].Index]->Type == mrtJob)
|
|
{
|
|
JRETime = N->RE[nreindex].Time;
|
|
|
|
break;
|
|
}
|
|
} /* END for (treindex) */
|
|
} /* END if (JRETime != N->RE[nreindex].Time) */
|
|
|
|
if (JRETime != N->RE[nreindex].Time)
|
|
{
|
|
/* no matching job res event found */
|
|
|
|
/* must update both start and end events */
|
|
|
|
if (N->RE[nreindex].Type == mreStart)
|
|
{
|
|
OPC = (mCRE[crindex].DRes.Procs != -1) ?
|
|
mCRE[crindex].DRes.Procs :
|
|
N->CRes.Procs;
|
|
|
|
NPC = (N->RE[nreindex].DRes.Procs != -1) ?
|
|
N->RE[nreindex].DRes.Procs :
|
|
N->CRes.Procs;
|
|
|
|
if (OPC > NPC)
|
|
{
|
|
/* if previous event tuple is larger, locate outstanding end event */
|
|
|
|
for (treindex = nreindex + 1;N->RE[treindex].Type != mreNONE;treindex++)
|
|
{
|
|
if ((N->RE[treindex].Index != N->RE[nreindex].Index) ||
|
|
(N->RE[treindex].Type != mreEnd))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* end event found */
|
|
|
|
break;
|
|
} /* END for (treindex) */
|
|
|
|
memcpy(
|
|
&N->RE[nreindex].DRes,
|
|
&mCRE[crindex].DRes,
|
|
sizeof(N->RE[0].DRes));
|
|
|
|
memcpy(
|
|
&N->RE[treindex].DRes,
|
|
&mCRE[crindex].DRes,
|
|
sizeof(N->RE[0].DRes));
|
|
|
|
if ((N->RE[CRESI[crindex]].DRes.Procs == -1) ||
|
|
(N->RE[CREEI[crindex]].DRes.Procs == -1))
|
|
{
|
|
fprintf(stderr,"ALERT: negative blocked resources detected\n");
|
|
}
|
|
}
|
|
else if (OPC < NPC)
|
|
{
|
|
memcpy(
|
|
&mCRE[crindex].DRes,
|
|
&N->RE[nreindex].DRes,
|
|
sizeof(mCRE[crindex].DRes));
|
|
|
|
memcpy(
|
|
&N->RE[CRESI[crindex]].DRes,
|
|
&mCRE[crindex].DRes,
|
|
sizeof(N->RE[0].DRes));
|
|
|
|
memcpy(
|
|
&N->RE[CREEI[crindex]].DRes,
|
|
&mCRE[crindex].DRes,
|
|
sizeof(N->RE[0].DRes));
|
|
|
|
if ((N->RE[CRESI[crindex]].DRes.Procs == -1) ||
|
|
(N->RE[CREEI[crindex]].DRes.Procs == -1))
|
|
{
|
|
fprintf(stderr,"ALERT: negative blocked resources detected\n");
|
|
}
|
|
}
|
|
|
|
CRESI[crindex] = nreindex;
|
|
}
|
|
else
|
|
{
|
|
CREEI[crindex] = nreindex;
|
|
}
|
|
} /* END if (JRETime != N->RE[nreindex].Time) */
|
|
|
|
break;
|
|
}
|
|
|
|
if (CRI[crindex] == -1)
|
|
{
|
|
CRI[crindex] = N->RE[nreindex].Index;
|
|
CRC[crindex] = N->RC[N->RE[nreindex].Index];
|
|
CRESI[crindex] = nreindex;
|
|
|
|
memcpy(&mCRE[crindex],&N->RE[nreindex],sizeof(mCRE[crindex]));
|
|
|
|
CRI[crindex + 1] = -1;
|
|
|
|
break;
|
|
}
|
|
} /* END for (crindex) */
|
|
} /* END else (R[N->RE[nreindex]...) */
|
|
} /* END for (nreindex) */
|
|
|
|
JRE[jreindex].Type = mreNONE;
|
|
JRE[jreindex].Index = 0;
|
|
|
|
InitialJStartTime = (jreindex > 0) ?
|
|
N->R[JRE[0].Index]->StartTime :
|
|
MAX_MTIME;
|
|
|
|
if (((JName != NULL) && (NameFound == FALSE)) ||
|
|
(jreindex == 0))
|
|
{
|
|
/* requested job name not found on node */
|
|
|
|
/* issue, must update reservation utilization in MResDestroy()
|
|
* situations where job RE's have just been removed. ie,
|
|
* job RE impact on inclusive reservations must be handled
|
|
*/
|
|
|
|
if (ForceEval != TRUE)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
MTRAPNODE(N,FName);
|
|
|
|
DBG(6,fSCHED)
|
|
{
|
|
MRECheck(N,"MResAdjustDRes-Start",TRUE);
|
|
}
|
|
|
|
memcpy(ORE,JRE,sizeof(ORE[0]) * (jreindex + 1));
|
|
|
|
DBG(6,fSTRUCT) DPrint("INFO: updating cr reservations for job '%s'\n",
|
|
(JName != NULL) ? JName : "NULL");
|
|
|
|
/* CR: Container Reservation */
|
|
|
|
for (crindex = 0;CRI[crindex] != -1;crindex++)
|
|
{
|
|
creindex = 0;
|
|
|
|
memset(CRE,0,sizeof(mre_t) * (MSched.ResDepth << 1));
|
|
|
|
R = N->R[CRI[crindex]];
|
|
|
|
CRStartTime = MAX(MSched.Time,R->StartTime);
|
|
|
|
CREndTime = R->EndTime;
|
|
|
|
/* blocked resources =~ dedicated resource per task * taskcount */
|
|
|
|
memset(&IBR,0,sizeof(BR));
|
|
|
|
MCResAdd(&IBR,&N->CRes,&R->DRes,CRC[crindex],FALSE);
|
|
|
|
if (CRStartTime < InitialJStartTime)
|
|
{
|
|
CRE[creindex].Index = CRI[crindex];
|
|
CRE[creindex].Time = CRStartTime;
|
|
memcpy(&CRE[creindex].DRes,&IBR,sizeof(IBR));
|
|
CRE[creindex].Type = mreStart;
|
|
|
|
creindex++;
|
|
|
|
CRE[creindex].Index = CRI[crindex];
|
|
CRE[creindex].Time = MIN(CREndTime,InitialJStartTime);
|
|
memcpy(&CRE[creindex].DRes,&IBR,sizeof(IBR));
|
|
CRE[creindex].Type = mreEnd;
|
|
|
|
CRStartTime = InitialJStartTime;
|
|
|
|
creindex++;
|
|
}
|
|
|
|
for (jrindex1 = 0;JRE[jrindex1].Type != mreNONE;jrindex1++)
|
|
{
|
|
/* locate smallest event interval */
|
|
|
|
for (jrindex2 = 0;JRE[jrindex2].Type != mreNONE;jrindex2++)
|
|
{
|
|
if (JRE[jrindex2].Time > CRStartTime)
|
|
{
|
|
CREndTime = MIN(CREndTime,JRE[jrindex2].Time);
|
|
|
|
break;
|
|
}
|
|
} /* END for (jrindex2) */
|
|
|
|
Overlap =
|
|
MIN(CREndTime,R->EndTime) -
|
|
MAX(CRStartTime,R->StartTime);
|
|
|
|
if (Overlap <= 0)
|
|
{
|
|
if (CRStartTime >= R->EndTime)
|
|
break;
|
|
else
|
|
continue;
|
|
}
|
|
|
|
memcpy(&BR,&IBR,sizeof(BR));
|
|
|
|
for (jrindex2 = 0;JRE[jrindex2].Type != mreNONE;jrindex2++)
|
|
{
|
|
if (JRE[jrindex2].Type != mreStart)
|
|
continue;
|
|
|
|
JR = N->R[JRE[jrindex2].Index];
|
|
|
|
if (JRE[jrindex2].Time >= CREndTime)
|
|
break;
|
|
|
|
Overlap =
|
|
MIN(CREndTime,JR->EndTime) -
|
|
MAX(CRStartTime,JR->StartTime);
|
|
|
|
if (Overlap <= 0)
|
|
{
|
|
if (JR->StartTime > CREndTime)
|
|
break;
|
|
else
|
|
continue;
|
|
}
|
|
|
|
JRC = N->RC[JRE[jrindex2].Index];
|
|
|
|
J = (mjob_t *)JR->J;
|
|
|
|
/* if job reservation overlaps container reservation component */
|
|
|
|
switch(R->Type)
|
|
{
|
|
case mrtUser:
|
|
|
|
CROverlap =
|
|
MIN(R->EndTime,JR->EndTime) -
|
|
MAX(R->StartTime,JR->StartTime);
|
|
|
|
if (MResCheckJAccess(R,J,CROverlap,NULL,NULL) == TRUE)
|
|
{
|
|
if (JRE[jrindex2].Type == mreStart)
|
|
{
|
|
/* remove dedicated job resources from container blocked resources */
|
|
|
|
MCResRemove(&BR,&N->CRes,&JR->DRes,JRC,FALSE);
|
|
}
|
|
else if (JRE[jrindex2].Type == mreEnd)
|
|
{
|
|
/* remove dedicated job resources from container blocked resources */
|
|
|
|
MCResAdd(&BR,&N->CRes,&JR->DRes,JRC,FALSE);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* NO-OP */
|
|
|
|
break;
|
|
} /* END switch (CR->Type) */
|
|
} /* END for (jrindex2) */
|
|
|
|
if (CRStartTime == R->EndTime)
|
|
break;
|
|
|
|
MUCResGetMax(&BR,&BR,&ZRes);
|
|
|
|
if ((creindex > 0) &&
|
|
(memcmp(&BR,&CRE[creindex - 1].DRes,sizeof(BR)) == 0))
|
|
{
|
|
/* merge ranges */
|
|
|
|
CRE[creindex - 1].Time = CREndTime;
|
|
}
|
|
else
|
|
{
|
|
/* create new range */
|
|
|
|
CRE[creindex].Index = CRI[crindex];
|
|
CRE[creindex].Time = CRStartTime;
|
|
memcpy(&CRE[creindex].DRes,&BR,sizeof(CRE[0].DRes));
|
|
CRE[creindex].Type = mreStart;
|
|
|
|
creindex++;
|
|
|
|
CRE[creindex].Index = CRI[crindex];
|
|
CRE[creindex].Time = CREndTime;
|
|
memcpy(&CRE[creindex].DRes,&BR,sizeof(CRE[0].DRes));
|
|
CRE[creindex].Type = mreEnd;
|
|
|
|
creindex++;
|
|
}
|
|
|
|
if (CREndTime >= R->EndTime)
|
|
break;
|
|
|
|
/* advance start/end time for new CRes */
|
|
|
|
CRStartTime = CREndTime;
|
|
CREndTime = R->EndTime;
|
|
} /* END for (jrindex1) */
|
|
|
|
if (creindex == 0)
|
|
{
|
|
/* no job overlap, use original CR events */
|
|
|
|
CRE[creindex].Index = CRI[crindex];
|
|
CRE[creindex].Time = CRStartTime;
|
|
memcpy(&CRE[creindex].DRes,&IBR,sizeof(CRE[0].DRes));
|
|
CRE[creindex].Type = mreStart;
|
|
|
|
creindex++;
|
|
|
|
CRE[creindex].Index = CRI[crindex];
|
|
CRE[creindex].Time = CREndTime;
|
|
memcpy(&CRE[creindex].DRes,&IBR,sizeof(CRE[0].DRes));
|
|
CRE[creindex].Type = mreEnd;
|
|
|
|
creindex++;
|
|
}
|
|
|
|
/* merge CR events with JR events */
|
|
|
|
CRE[creindex].Type = mreNONE;
|
|
|
|
creindex = 0;
|
|
oreindex = 0;
|
|
nreindex = 0;
|
|
|
|
while ((CRE[creindex].Type != mreNONE) ||
|
|
(ORE[oreindex].Type != mreNONE))
|
|
{
|
|
if ((creindex >= (MSched.ResDepth << 1)) ||
|
|
(oreindex >= (MSched.ResDepth << 1)) ||
|
|
(nreindex >= (MSched.ResDepth << 1)))
|
|
{
|
|
DBG(1,fSTRUCT) DPrint("ALERT: node reservation event overflow (N: %d C: %d O: %d) - increase %s\n",
|
|
nreindex,
|
|
creindex,
|
|
oreindex,
|
|
MParam[pResDepth]);
|
|
|
|
break;
|
|
}
|
|
|
|
if ((ORE[oreindex].Type == mreNONE) ||
|
|
((CRE[creindex].Type != mreNONE) &&
|
|
(CRE[creindex].Time < ORE[oreindex].Time)))
|
|
{
|
|
memcpy(&NRE[nreindex],&CRE[creindex],sizeof(NRE[0]));
|
|
|
|
nreindex++;
|
|
|
|
creindex++;
|
|
}
|
|
else
|
|
{
|
|
memcpy(&NRE[nreindex],&ORE[oreindex],sizeof(NRE[0]));
|
|
|
|
nreindex++;
|
|
|
|
oreindex++;
|
|
}
|
|
} /* END while ((CRE[creindex].Type != mreNONE) || ...) */
|
|
|
|
memcpy(ORE,NRE,(sizeof(ORE[0]) * nreindex));
|
|
|
|
ORE[nreindex].Type = mreNONE;
|
|
} /* END for (crindex) */
|
|
|
|
memcpy(N->RE,ORE,sizeof(N->RE[0]) * (nreindex + 1));
|
|
|
|
/* perform sanity check on RE table */
|
|
|
|
CRStartTime = 0;
|
|
|
|
for (nreindex = 0;N->RE[nreindex].Type != mreNONE;nreindex++)
|
|
{
|
|
if (nreindex >= (MSched.ResDepth << 1))
|
|
break;
|
|
|
|
DBG(6,fSTRUCT) DPrint("INFO: N[%s]->RE[%02d] (%s %s)\n",
|
|
N->Name,
|
|
nreindex,
|
|
N->R[N->RE[nreindex].Index]->Name,
|
|
MULToTString(N->RE[nreindex].Time - MSched.Time));
|
|
|
|
if (CRStartTime > N->RE[nreindex].Time)
|
|
{
|
|
DBG(2,fSTRUCT) DPrint("ALERT: node %s RE table is corrupt. RE[%d] '%s' at %s is out of time order\n",
|
|
N->Name,
|
|
nreindex,
|
|
N->R[N->RE[nreindex].Index]->Name,
|
|
MULToTString(N->RE[nreindex].Time - MSched.Time));
|
|
}
|
|
|
|
CRStartTime = N->RE[nreindex].Time;
|
|
} /* END for (reindex) */
|
|
|
|
DBG(6,fSCHED)
|
|
{
|
|
MRECheck(N,"MResAdjustDRes-End",TRUE);
|
|
}
|
|
} /* END for (nindex) */
|
|
|
|
return(SUCCESS);
|
|
} /* END MResAdjustDRes() */
|
|
|
|
|
|
|
|
|
|
|
|
int MResDiagnoseState(
|
|
|
|
mres_t *R, /* I */
|
|
int Flags, /* I */
|
|
char *SBuffer, /* O */
|
|
int SBufSize, /* I */
|
|
int Mode) /* I */
|
|
|
|
{
|
|
int ResNC;
|
|
int ResPC;
|
|
int ResTC;
|
|
int NodeRC;
|
|
|
|
char *BPtr;
|
|
int BSpace;
|
|
|
|
mcres_t *D;
|
|
|
|
mcres_t ZRes;
|
|
|
|
int cindex;
|
|
int rindex;
|
|
int nindex;
|
|
int nrindex;
|
|
int reindex;
|
|
|
|
int IsCorrupt; /* (boolean) */
|
|
|
|
int Count[MAX_MRES_DEPTH];
|
|
int TCount[MAX_MRES_DEPTH];
|
|
|
|
int RCount;
|
|
int RECount;
|
|
|
|
mnode_t *N;
|
|
mjob_t *J;
|
|
|
|
const char *FName = "MResDiagnoseState";
|
|
|
|
DBG(5,fSTRUCT) DPrint("%s(%s,%d,SBuffer,SBufSize,%d)\n",
|
|
FName,
|
|
(R != NULL) ? "NONE" : R->Name,
|
|
Flags,
|
|
Mode);
|
|
|
|
if ((R == NULL) || (SBuffer == NULL))
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
BPtr = SBuffer;
|
|
BSpace = SBufSize;
|
|
|
|
if (Mode == mSet)
|
|
{
|
|
BPtr[0] = '\0';
|
|
}
|
|
else
|
|
{
|
|
int len;
|
|
|
|
len = strlen(SBuffer);
|
|
|
|
BPtr += len;
|
|
BSpace -= len;
|
|
}
|
|
|
|
if ((R->Type == mrtUser) ||
|
|
((R->Type == mrtJob) && (R->DRes.Procs == -1)))
|
|
{
|
|
/* NO-OP */
|
|
} /* END if ((R->Type == mrtUser) || ... ) */
|
|
else if (R->Type == mrtJob)
|
|
{
|
|
mres_t *tmpR;
|
|
|
|
/* job reservation detected */
|
|
|
|
/* check if reservation is duplicated */
|
|
|
|
for (rindex = 0;rindex < R->Index;rindex++)
|
|
{
|
|
tmpR = MRes[rindex];
|
|
|
|
if ((tmpR == NULL) || (tmpR->Name[0] == '\0'))
|
|
break;
|
|
|
|
if (!strcmp(R->Name,tmpR->Name))
|
|
{
|
|
MUSNPrintF(&BPtr,&BSpace,"WARNING: res '%s' (%d) is duplicated at index %d\n",
|
|
R->Name,
|
|
R->Index,
|
|
rindex);
|
|
|
|
break;
|
|
}
|
|
} /* END for (rindex) */
|
|
|
|
/* verify associated job exists */
|
|
|
|
if (MJobFind(R->Name,&J,0) == FAILURE)
|
|
{
|
|
MUSNPrintF(&BPtr,&BSpace,"WARNING: cannot find job associated with res '%s'\n",
|
|
R->Name);
|
|
}
|
|
else if (J->R != R)
|
|
{
|
|
MUSNPrintF(&BPtr,&BSpace,"WARNING: job '%s' does not link to reservation\n",
|
|
J->Name);
|
|
}
|
|
} /* END else if (R->Type == mrtJob) */
|
|
|
|
/* check nodes to validate reservation */
|
|
|
|
ResNC = 0;
|
|
ResPC = 0;
|
|
ResTC = 0;
|
|
|
|
D = &R->DRes;
|
|
|
|
memset(&ZRes,0,sizeof(ZRes));
|
|
|
|
for (nindex = 0;nindex < MAX_MNODE;nindex++)
|
|
{
|
|
N = MNode[nindex];
|
|
|
|
if ((N == NULL) || (N->Name[0] == '\0'))
|
|
break;
|
|
|
|
if (N->Name[0] == '\1')
|
|
continue;
|
|
|
|
memset(Count,0,sizeof(Count));
|
|
memset(TCount,0,sizeof(TCount));
|
|
|
|
IsCorrupt = FALSE;
|
|
|
|
RCount = 0;
|
|
|
|
for (nrindex = 0;N->R[nrindex] != NULL;nrindex++)
|
|
{
|
|
if (N->R[nrindex] != (mres_t *)1)
|
|
RCount++;
|
|
}
|
|
|
|
RECount = 0;
|
|
|
|
for (reindex = 0;reindex < (MSched.ResDepth << 1);reindex++)
|
|
{
|
|
if (N->RE[reindex].Type == mreNONE)
|
|
break;
|
|
|
|
if (N->RE[reindex].Type == mreStart)
|
|
Count[N->RE[reindex].Index]++;
|
|
else if (N->RE[reindex].Type == mreEnd)
|
|
Count[N->RE[reindex].Index]--;
|
|
|
|
TCount[N->RE[reindex].Index]++;
|
|
|
|
RECount++;
|
|
|
|
if ((Count[N->RE[reindex].Index] < 0) ||
|
|
(Count[N->RE[reindex].Index] > 1))
|
|
{
|
|
IsCorrupt = TRUE;
|
|
}
|
|
} /* END for (reindex) */
|
|
|
|
if (IsCorrupt == FALSE)
|
|
{
|
|
if ((RECount < (RCount << 1)) ||
|
|
((RECount % 2) == 1))
|
|
{
|
|
IsCorrupt = TRUE;
|
|
}
|
|
else
|
|
{
|
|
for (cindex = 0;cindex < MSched.ResDepth;cindex++)
|
|
{
|
|
if (N->R[cindex] == NULL)
|
|
break;
|
|
|
|
if (N->R[cindex] == (mres_t *)1)
|
|
continue;
|
|
|
|
if ((Count[cindex] != 0) || (TCount[cindex] < 2))
|
|
{
|
|
IsCorrupt = TRUE;
|
|
|
|
break;
|
|
}
|
|
} /* END for (cindex) */
|
|
}
|
|
}
|
|
|
|
if (IsCorrupt == TRUE)
|
|
{
|
|
MUSNPrintF(&BPtr,&BSpace,"WARNING: RE table on node %s is corrupt\n",
|
|
N->Name);
|
|
|
|
memset(Count,0,sizeof(Count));
|
|
|
|
for (nrindex = 0;nrindex < MSched.ResDepth;nrindex++)
|
|
{
|
|
if (N->R[nrindex] == NULL)
|
|
break;
|
|
|
|
MUSNPrintF(&BPtr,&BSpace," R[%02d] '%s'\n",
|
|
nrindex,
|
|
(N->R[nrindex] != (mres_t *)1) ? N->R[nrindex]->Name : "EMPTY");
|
|
} /* END for (nrindex) */
|
|
|
|
for (reindex = 0;reindex < (MSched.ResDepth << 1);reindex++)
|
|
{
|
|
if (N->RE[reindex].Type == mreNONE)
|
|
break;
|
|
|
|
MUSNPrintF(&BPtr,&BSpace," RE[%02d] %10s %5s %12s %s\n",
|
|
reindex,
|
|
MULToTString(N->RE[reindex].Time - MSched.Time),
|
|
(N->RE[reindex].Type == mreStart) ?
|
|
"Start" :
|
|
"End",
|
|
(N->R[N->RE[reindex].Index] > (mres_t *)1) ?
|
|
N->R[N->RE[reindex].Index]->Name :
|
|
"EMPTY",
|
|
MUCResToString(&N->RE[reindex].DRes,0,0,NULL));
|
|
|
|
if (N->RE[reindex].Type == mreStart)
|
|
Count[N->RE[reindex].Index]++;
|
|
else if (N->RE[reindex].Type == mreEnd)
|
|
Count[N->RE[reindex].Index]--;
|
|
|
|
if ((N->R[N->RE[reindex].Index]->Type == mrtUser) &&
|
|
(!memcmp(&N->RE[reindex].DRes,&ZRes,sizeof(N->RE[reindex].DRes)) ||
|
|
(N->RE[reindex].DRes.Procs == 0)))
|
|
{
|
|
/* empty dedicated resource structure */
|
|
|
|
MUSNPrintF(&BPtr,&BSpace,"WARNING: RE[%02d] user reservation has empty resource structure\n",
|
|
reindex);
|
|
|
|
break;
|
|
}
|
|
|
|
if ((Count[N->RE[reindex].Index] < 0) ||
|
|
(Count[N->RE[reindex].Index] > 1))
|
|
{
|
|
MUSNPrintF(&BPtr,&BSpace,"WARNING: RE[%02d] %s\n",
|
|
reindex,
|
|
(Count[N->RE[reindex].Index] < 0) ?
|
|
"reservation end event located without accompanying start event" :
|
|
"reservation start event located without accompanying end event");
|
|
|
|
Count[N->RE[reindex].Index] = (Count[N->RE[reindex].Index] < 0) ? 0 : 1;
|
|
}
|
|
} /* END for (reindex) */
|
|
|
|
for (cindex = 0;cindex < MSched.ResDepth;cindex++)
|
|
{
|
|
if (N->R[cindex] == NULL)
|
|
break;
|
|
|
|
if (N->R[cindex] == (mres_t *)1)
|
|
continue;
|
|
|
|
if (Count[cindex] != 0)
|
|
{
|
|
MUSNPrintF(&BPtr,&BSpace,"WARNING: R[%02d] %s %s\n",
|
|
cindex,
|
|
N->R[cindex]->Name,
|
|
"reservation start event located but no accompanying end event found");
|
|
}
|
|
|
|
if (TCount[cindex] < 2)
|
|
{
|
|
MUSNPrintF(&BPtr,&BSpace,"WARNING: R[%02d] %s %s\n",
|
|
cindex,
|
|
N->R[cindex]->Name,
|
|
"reservation not referenced by event");
|
|
}
|
|
} /* END for (cindex) */
|
|
} /* END if (IsCorrupt == TRUE) */
|
|
|
|
NodeRC = 0;
|
|
|
|
for (rindex = 0;rindex < MSched.ResDepth;rindex++)
|
|
{
|
|
if (N->R[rindex] == (mres_t *)1)
|
|
continue;
|
|
|
|
if (N->R[rindex] == NULL)
|
|
break;
|
|
|
|
if (N->R[rindex] != R)
|
|
continue;
|
|
|
|
ResNC++;
|
|
|
|
for (reindex = 0;reindex < (MSched.ResDepth << 1);reindex++)
|
|
{
|
|
if (N->RE[reindex].Type == 0)
|
|
break;
|
|
|
|
if (N->R[N->RE[reindex].Index] != R)
|
|
continue;
|
|
|
|
D = &N->RE[reindex].DRes;
|
|
|
|
break;
|
|
} /* END for (reindex) */
|
|
|
|
ResPC += (D->Procs == -1) ?
|
|
N->CRes.Procs :
|
|
(D->Procs * N->RC[rindex]);
|
|
|
|
ResTC += N->RC[rindex];
|
|
|
|
if (NodeRC++ > 0)
|
|
{
|
|
MUSNPrintF(&BPtr,&BSpace,"WARNING: node '%s' has multiple links to reservation '%s' (clearing extra links)\n",
|
|
N->Name,
|
|
R->Name);
|
|
|
|
N->R[rindex] = (mres_t *)1;
|
|
} /* END if (NodeRC++ > 0) */
|
|
} /* END for (rindex) */
|
|
} /* END for (nindex) */
|
|
|
|
if (ResPC != R->AllocPC)
|
|
{
|
|
MUSNPrintF(&BPtr,&BSpace,"WARNING: reservation '%s' has %d proc(s) allocated but %d detected\n",
|
|
R->Name,
|
|
R->AllocPC,
|
|
ResPC);
|
|
}
|
|
|
|
if (ResTC != R->TaskCount)
|
|
{
|
|
MUSNPrintF(&BPtr,&BSpace,"WARNING: reservation '%s' has %d task(s) allocated but %d detected\n",
|
|
R->Name,
|
|
R->TaskCount,
|
|
ResTC);
|
|
}
|
|
|
|
if (ResNC != R->NodeCount)
|
|
{
|
|
MUSNPrintF(&BPtr,&BSpace,"WARNING: reservation '%s' has %d node(s) allocated but %d detected\n",
|
|
R->Name,
|
|
R->NodeCount,
|
|
ResNC);
|
|
}
|
|
|
|
return(SUCCESS);
|
|
} /* END MResDiagnoseState() */
|
|
|
|
|
|
|
|
|
|
int MRLLimitTC(
|
|
|
|
mrange_t *ARL, /* I/O (available) */
|
|
mrange_t *URL, /* O (utilized) */
|
|
mrange_t *DRL, /* O */
|
|
int TCMax) /* I */
|
|
|
|
{
|
|
int index1;
|
|
int index2;
|
|
int cindex;
|
|
|
|
mrange_t *C;
|
|
|
|
mrange_t tmpRL[MAX_MRANGE];
|
|
|
|
long ATime;
|
|
long UTime;
|
|
|
|
int AType;
|
|
int UType;
|
|
|
|
long ETime;
|
|
|
|
int OldTC;
|
|
int TC;
|
|
|
|
int IsInstantA; /* boolean */
|
|
int IsInstantU; /* boolean */
|
|
|
|
int ATC;
|
|
int FTC;
|
|
|
|
if ((ARL == NULL) || (URL == NULL))
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
/* initialize variables */
|
|
|
|
if (DRL != NULL)
|
|
C = DRL;
|
|
else
|
|
C = tmpRL;
|
|
|
|
index1 = 0;
|
|
index2 = 0;
|
|
cindex = 0;
|
|
|
|
AType = mreStart;
|
|
UType = mreStart;
|
|
|
|
ATime = (ARL[0].EndTime == 0) ? MAX_MTIME + 1 : ARL[0].StartTime;
|
|
UTime = (URL[0].EndTime == 0) ? MAX_MTIME + 1 : URL[0].StartTime;
|
|
|
|
IsInstantA = ((ARL[index1].StartTime == ARL[index1].EndTime) && (ARL[index1].EndTime > 0)) ?
|
|
TRUE :
|
|
FALSE;
|
|
|
|
IsInstantU = ((URL[index2].StartTime == URL[index2].EndTime) && (URL[index2].EndTime > 0)) ?
|
|
TRUE :
|
|
FALSE;
|
|
|
|
FTC = TCMax;
|
|
ATC = 0;
|
|
|
|
TC = 0;
|
|
|
|
while (TRUE)
|
|
{
|
|
if ((ARL[index1].EndTime == 0) && (URL[index2].EndTime == 0))
|
|
{
|
|
C[cindex].EndTime = 0;
|
|
|
|
break;
|
|
}
|
|
|
|
OldTC = TC;
|
|
|
|
ETime = MIN(ATime,UTime);
|
|
|
|
if (ATime == ETime)
|
|
{
|
|
if (AType == mreStart)
|
|
{
|
|
ATC += ARL[index1].TaskCount;
|
|
|
|
AType = mreEnd;
|
|
ATime = ARL[index1].EndTime;
|
|
}
|
|
else
|
|
{
|
|
ATC -= ARL[index1].TaskCount;
|
|
|
|
index1++;
|
|
|
|
IsInstantA = ((ARL[index1].StartTime == ARL[index1].EndTime) && (ARL[index1].EndTime > 0)) ?
|
|
TRUE :
|
|
FALSE;
|
|
|
|
AType = mreStart;
|
|
ATime = (ARL[index1].EndTime == 0) ? MAX_MTIME + 1: ARL[index1].StartTime;
|
|
}
|
|
}
|
|
|
|
if (UTime == ETime)
|
|
{
|
|
if (UType == mreStart)
|
|
{
|
|
FTC -= URL[index2].TaskCount;
|
|
|
|
UType = mreEnd;
|
|
UTime = URL[index2].EndTime;
|
|
}
|
|
else
|
|
{
|
|
FTC += URL[index2].TaskCount;
|
|
|
|
index2++;
|
|
|
|
IsInstantU = ((URL[index2].StartTime == URL[index2].EndTime) && (URL[index2].EndTime > 0)) ?
|
|
TRUE :
|
|
FALSE;
|
|
|
|
UType = mreStart;
|
|
UTime = (URL[index2].EndTime == 0) ? MAX_MTIME + 1: URL[index2].StartTime;
|
|
}
|
|
}
|
|
|
|
TC = MIN(ATC,FTC);
|
|
|
|
if (TC != OldTC)
|
|
{
|
|
if (OldTC > 0)
|
|
{
|
|
if ((ETime > C[cindex].StartTime) ||
|
|
(IsInstantA == TRUE) ||
|
|
(IsInstantU == TRUE))
|
|
{
|
|
/* terminate existing range */
|
|
|
|
C[cindex].EndTime = ETime;
|
|
|
|
cindex++;
|
|
}
|
|
}
|
|
|
|
if (TC > 0)
|
|
{
|
|
/* start new range */
|
|
|
|
C[cindex].StartTime = ETime;
|
|
C[cindex].TaskCount = TC;
|
|
}
|
|
|
|
if (((ATime == ETime) && (IsInstantA == TRUE)) ||
|
|
((UTime == ETime) && (IsInstantU == TRUE)))
|
|
{
|
|
/* terminate instant range */
|
|
|
|
C[cindex].EndTime = ETime;
|
|
|
|
cindex++;
|
|
|
|
if ((ATime == ETime) && (IsInstantA == TRUE))
|
|
{
|
|
ATC += ARL[index1].TaskCount;
|
|
|
|
index1++;
|
|
|
|
IsInstantA = ((ARL[index1].StartTime == ARL[index1].EndTime) && (ARL[index1].EndTime > 0)) ?
|
|
TRUE :
|
|
FALSE;
|
|
|
|
AType = mreStart;
|
|
ATime = (ARL[index1].EndTime == 0) ? MAX_MTIME + 1: ARL[index1].StartTime;
|
|
}
|
|
|
|
if ((UTime == ETime) && (IsInstantU == TRUE))
|
|
{
|
|
FTC -= URL[index2].TaskCount;
|
|
|
|
index2++;
|
|
|
|
IsInstantU = ((URL[index2].StartTime == URL[index2].EndTime) && (URL[index2].EndTime > 0)) ?
|
|
TRUE :
|
|
FALSE;
|
|
|
|
UType = mreStart;
|
|
UTime = (URL[index2].EndTime == 0) ? MAX_MTIME + 2: URL[index2].StartTime;
|
|
}
|
|
|
|
TC = MIN(ATC,FTC);
|
|
|
|
if (TC > 0)
|
|
{
|
|
/* start new range */
|
|
|
|
C[cindex].StartTime = ETime;
|
|
C[cindex].TaskCount = TC;
|
|
}
|
|
}
|
|
} /* END if (TC != OldTC) */
|
|
} /* END while(TRUE) */
|
|
|
|
if (DRL == NULL)
|
|
{
|
|
memcpy(ARL,C,sizeof(mrange_t) * (cindex + 1));
|
|
}
|
|
|
|
return(SUCCESS);
|
|
} /* END MRLGetMinTC() */
|
|
|
|
|
|
|
|
|
|
int MRLSubtract(
|
|
|
|
mrange_t *R1, /* I/O */
|
|
mrange_t *R2) /* I */
|
|
|
|
{
|
|
int index1;
|
|
int index2;
|
|
int cindex;
|
|
|
|
mrange_t C[MAX_MRANGE];
|
|
|
|
enum { rNone = 0, rStart, rEnd, rInstant };
|
|
|
|
int R1Time;
|
|
int R2Time;
|
|
|
|
int R1State;
|
|
int R2State;
|
|
|
|
int ETime;
|
|
|
|
int TaskCount;
|
|
|
|
/* initialize variables */
|
|
|
|
index1 = 0;
|
|
index2 = 0;
|
|
cindex = 0;
|
|
|
|
R1State = rStart;
|
|
R2State = rStart;
|
|
|
|
TaskCount = 0;
|
|
|
|
while ((R1[index1].EndTime != 0) || (R2[index2].EndTime != 0))
|
|
{
|
|
if (R1[index1].EndTime == 0)
|
|
{
|
|
R1State = rNone;
|
|
R1Time = MAX_MTIME + 1;
|
|
}
|
|
else
|
|
{
|
|
R1Time = (R1State != rEnd) ? R1[index1].StartTime : R1[index1].EndTime;
|
|
|
|
if (R1[index1].StartTime == R1[index1].EndTime)
|
|
R1State = rInstant;
|
|
}
|
|
|
|
if (R2[index2].EndTime == 0)
|
|
{
|
|
R2State = rNone;
|
|
R2Time = MAX_MTIME + 1;
|
|
}
|
|
else
|
|
{
|
|
R2Time = (R2State != rEnd) ? R2[index2].StartTime : R2[index2].EndTime;
|
|
|
|
if (R2[index2].StartTime == R2[index2].EndTime)
|
|
R1State = rInstant;
|
|
}
|
|
|
|
ETime = MIN(R1Time,R2Time);
|
|
|
|
/* handle instant states */
|
|
|
|
if (((R1State == rInstant) && (R1Time == ETime)) ||
|
|
((R2State == rInstant) && (R2Time == ETime)))
|
|
{
|
|
if ((R1State == rInstant) && (R1Time == ETime))
|
|
{
|
|
/* R1 instant, R2 ??? */
|
|
|
|
if ((R2State == mreEnd) && (R2Time == ETime))
|
|
{
|
|
/* R1 instant, R2 overlapping end */
|
|
|
|
/* implied open range */
|
|
|
|
C[cindex].EndTime = ETime;
|
|
cindex++;
|
|
|
|
TaskCount += R2[index2].TaskCount;
|
|
|
|
index2++;
|
|
|
|
/* start instant range */
|
|
|
|
C[cindex].StartTime = ETime;
|
|
C[cindex].EndTime = ETime;
|
|
|
|
/* R1 instant, R2 ??? (new) */
|
|
/* if instant R2 exists, it must be this range */
|
|
|
|
if (R2[index2].EndTime == 0)
|
|
R2Time = MAX_MTIME + 1;
|
|
else
|
|
R2Time = R2[index2].StartTime;
|
|
|
|
if (R2Time == ETime)
|
|
{
|
|
C[cindex].TaskCount = R1[index1].TaskCount + R2[index2].TaskCount;
|
|
|
|
if (R2Time == R2[index2].EndTime)
|
|
{
|
|
/* R1 instant, R2 instant */
|
|
|
|
index2++;
|
|
|
|
R2State = rStart;
|
|
}
|
|
else
|
|
{
|
|
/* R1 instant, R2 start */
|
|
|
|
/* DO NOTHING */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* R1 instant only */
|
|
|
|
C[cindex].TaskCount = R1[index1].TaskCount;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* implied closed range */
|
|
|
|
/* start instant range */
|
|
|
|
C[cindex].StartTime = ETime;
|
|
C[cindex].EndTime = ETime;
|
|
|
|
if (R2State == rInstant)
|
|
{
|
|
/* R1 instant, R2 instant */
|
|
|
|
C[cindex].TaskCount = R1[index1].TaskCount + R2[index2].TaskCount;
|
|
|
|
index2++;
|
|
|
|
R2State = rStart;
|
|
}
|
|
else if ((R2State == rStart) && (R2Time == ETime))
|
|
{
|
|
/* R1 instant, R2 start */
|
|
|
|
C[cindex].TaskCount = R1[index1].TaskCount + R2[index2].TaskCount;
|
|
}
|
|
else
|
|
{
|
|
/* R1 instant only */
|
|
|
|
C[cindex].TaskCount = TaskCount + R1[index1].TaskCount;
|
|
}
|
|
}
|
|
|
|
index1++;
|
|
|
|
R1State = rStart;
|
|
}
|
|
else
|
|
{
|
|
/* R1 ???, R2 instant */
|
|
/* R1 not instant */
|
|
|
|
if ((R1State == mreEnd) && (R1Time == ETime))
|
|
{
|
|
/* R1 overlapping end, R2 instant */
|
|
|
|
/* implied open range */
|
|
|
|
C[cindex].EndTime = ETime;
|
|
cindex++;
|
|
|
|
TaskCount -= R1[index1].TaskCount;
|
|
|
|
index1++;
|
|
|
|
/* start instant range */
|
|
|
|
C[cindex].StartTime = ETime;
|
|
C[cindex].EndTime = ETime;
|
|
|
|
/* R2 instant, R1 ??? (new) */
|
|
/* if instant R1 exists, it must be this range */
|
|
|
|
if (R1[index1].EndTime == 0)
|
|
R1Time = MAX_MTIME + 1;
|
|
else
|
|
R1Time = R1[index1].StartTime;
|
|
|
|
if (R1Time == ETime)
|
|
{
|
|
C[cindex].TaskCount = R1[index1].TaskCount + R2[index2].TaskCount;
|
|
|
|
if (R1Time == R1[index1].EndTime)
|
|
{
|
|
/* R1 instant, R2 instant */
|
|
|
|
index1++;
|
|
|
|
R1State = rStart;
|
|
}
|
|
else
|
|
{
|
|
/* R2 instant, R1 start */
|
|
|
|
/* DO NOTHING */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* R2 instant only */
|
|
|
|
C[cindex].TaskCount = R2[index2].TaskCount;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* implied closed range */
|
|
|
|
/* start instant range */
|
|
|
|
C[cindex].StartTime = ETime;
|
|
C[cindex].EndTime = ETime;
|
|
|
|
if ((R1State == rStart) && (R1Time == ETime))
|
|
{
|
|
/* R2 instant, R1 start */
|
|
|
|
C[cindex].TaskCount = R2[index2].TaskCount + R1[index1].TaskCount;
|
|
}
|
|
else
|
|
{
|
|
/* R2 instant only */
|
|
|
|
C[cindex].TaskCount = TaskCount + R2[index2].TaskCount;
|
|
}
|
|
}
|
|
|
|
index2++;
|
|
|
|
R2State = rStart;
|
|
}
|
|
|
|
cindex++;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (((R1State == rEnd) && (R1Time == ETime)) ||
|
|
((R2State == rEnd) && (R2Time == ETime)))
|
|
{
|
|
/* implied open range */
|
|
|
|
C[cindex].EndTime = ETime;
|
|
cindex++;
|
|
|
|
if ((R1State == rEnd) && (R1Time == ETime))
|
|
{
|
|
TaskCount -= R1[index1].TaskCount;
|
|
|
|
index1++;
|
|
|
|
R1State = rStart;
|
|
}
|
|
|
|
if ((R2State == rEnd) && (R2Time == ETime))
|
|
{
|
|
TaskCount -= R2[index2].TaskCount;
|
|
|
|
index2++;
|
|
|
|
R2State = rStart;
|
|
}
|
|
|
|
if (TaskCount > 0)
|
|
{
|
|
C[cindex].StartTime = ETime;
|
|
C[cindex].TaskCount = TaskCount;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (((R1State == rStart) && (R1Time == ETime)) ||
|
|
((R2State == rStart) && (R2Time == ETime)))
|
|
{
|
|
if ((TaskCount > 0) && (ETime > C[cindex].StartTime))
|
|
{
|
|
C[cindex].EndTime = ETime;
|
|
cindex++;
|
|
}
|
|
|
|
if ((R1State == rStart) && (R1Time == ETime))
|
|
{
|
|
TaskCount += R1[index1].TaskCount;
|
|
|
|
R1State = rEnd;
|
|
}
|
|
|
|
if ((R2State == rStart) && (R2Time == ETime))
|
|
{
|
|
TaskCount += R2[index2].TaskCount;
|
|
|
|
R2State = rEnd;
|
|
}
|
|
|
|
if (TaskCount > 0)
|
|
{
|
|
C[cindex].StartTime = ETime;
|
|
C[cindex].TaskCount = TaskCount;
|
|
}
|
|
|
|
continue;
|
|
} /* END if (((R1State == rStart) && (R1Time == ETime)) || ... */
|
|
} /* END while ((R1[index1].EndTime != 0) || (R2[index2].EndTime != 0)) */
|
|
|
|
/* terminate range */
|
|
|
|
C[cindex].EndTime = 0;
|
|
|
|
memcpy(R1,C,sizeof(mrange_t) * (cindex + 1));
|
|
|
|
return(SUCCESS);
|
|
} /* END MRLSubtract() */
|
|
|
|
|
|
|
|
|
|
int MResDiagGrid(
|
|
|
|
char *SBuffer,
|
|
int SBufSize,
|
|
int Mode)
|
|
|
|
{
|
|
int rindex;
|
|
|
|
int len;
|
|
|
|
char *BPtr;
|
|
int BSpace;
|
|
|
|
char TimeLine[MAX_MLINE];
|
|
|
|
const char *FName = "MResDiagGrid";
|
|
|
|
DBG(5,fSTRUCT) DPrint("%s(SBuffer,%d,%d)\n",
|
|
FName,
|
|
SBufSize,
|
|
Mode);
|
|
|
|
if (SBuffer == NULL)
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
len = strlen(SBuffer);
|
|
|
|
BPtr = SBuffer + len;
|
|
BSpace = SBufSize - len;
|
|
|
|
if (MPar[0].MaxMetaTasks > 0)
|
|
{
|
|
MUSNPrintF(&BPtr,&BSpace,"\nMeta Tasks\n\n");
|
|
|
|
MUSNPrintF(&BPtr,&BSpace,"%5s %10s -> %10s\n",
|
|
"Tasks",
|
|
"StartTime",
|
|
"EndTime");
|
|
|
|
MUSNPrintF(&BPtr,&BSpace,"%5s %10s -- %10s\n",
|
|
"-----",
|
|
"----------",
|
|
"----------");
|
|
|
|
if (MRange[0].EndTime == 0)
|
|
{
|
|
strcpy(TimeLine,MULToTString(0));
|
|
|
|
MUSNPrintF(&BPtr,&BSpace,"%5d %10s -> %10s\n",
|
|
0,
|
|
TimeLine,
|
|
MULToTString(MAX_MTIME));
|
|
}
|
|
else
|
|
{
|
|
for (rindex = 0;rindex < (MAX_MRES_DEPTH << 1);rindex++)
|
|
{
|
|
if (MRange[rindex].EndTime == 0)
|
|
break;
|
|
|
|
strcpy(TimeLine,MULToTString(MRange[rindex].StartTime - MSched.Time));
|
|
|
|
MUSNPrintF(&BPtr,&BSpace,"%5d %10s -> %10s\n",
|
|
MRange[rindex].TaskCount,
|
|
TimeLine,
|
|
MULToTString(MRange[rindex].EndTime - MSched.Time));
|
|
} /* END for (rindex) */
|
|
} /* END else (MRange[0].EndTime == 0) */
|
|
} /* END if (MPar[0].MaxMetaTasks > 0) */
|
|
|
|
return(SUCCESS);
|
|
} /* END MResDiagGrid() */
|
|
|
|
|
|
|
|
|
|
int MResToString(
|
|
|
|
mres_t *R, /* I */
|
|
int *RC, /* I (optional) */
|
|
char *SBuf, /* O */
|
|
int SBufSize,
|
|
int *RAList) /* I (optional) */
|
|
|
|
{
|
|
mxml_t *E = NULL;
|
|
|
|
if ((R == NULL) || (SBuf == NULL))
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
if (MXMLCreateE(&E,(char *)MXO[mxoRsv]) == FAILURE)
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
MResToXML(R,E,RAList);
|
|
|
|
MXMLToString(E,SBuf,SBufSize,NULL,TRUE);
|
|
|
|
return(SUCCESS);
|
|
} /* END MResToString() */
|
|
|
|
|
|
|
|
|
|
int MNResToString(
|
|
|
|
mnode_t *N, /* I */
|
|
mres_t *R, /* I (optional) */
|
|
mxml_t **EP, /* O (optional) */
|
|
char *SBuf, /* O (optional) */
|
|
int SBufSize) /* I */
|
|
|
|
{
|
|
int rindex;
|
|
|
|
static int DNRAList[] = {
|
|
mnraDRes,
|
|
mnraEnd,
|
|
mnraName,
|
|
mnraState,
|
|
mnraStart,
|
|
mnraTC,
|
|
mnraType,
|
|
-1 };
|
|
|
|
mxml_t *NE = NULL;
|
|
|
|
if ((N == NULL) ||
|
|
((SBuf == NULL) && (EP == NULL)))
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
if (MXMLCreateE(&NE,"node") == FAILURE)
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
MXMLSetAttr(NE,"name",(void *)N->Name,mdfString);
|
|
|
|
for (rindex = 0;rindex < MSched.ResDepth;rindex++)
|
|
{
|
|
mxml_t *NRE = NULL;
|
|
|
|
if (N->R[rindex] == NULL)
|
|
break;
|
|
|
|
if (N->R[rindex] == (mres_t *)1)
|
|
continue;
|
|
|
|
if ((R != NULL) && (N->R[rindex] != R))
|
|
continue;
|
|
|
|
/* create XML */
|
|
|
|
if (MXMLCreateE(&NRE,"nres") == FAILURE)
|
|
{
|
|
break;
|
|
}
|
|
|
|
MNResToXML(N,rindex,NRE,(int *)DNRAList);
|
|
|
|
MXMLAddE(NE,NRE);
|
|
} /* END for (rindex) */
|
|
|
|
if (SBuf != NULL)
|
|
{
|
|
MXMLToString(NE,SBuf,SBufSize,NULL,TRUE);
|
|
}
|
|
|
|
if (EP != NULL)
|
|
{
|
|
*EP = NE;
|
|
}
|
|
else
|
|
{
|
|
MXMLDestroyE(&NE);
|
|
}
|
|
|
|
return(SUCCESS);
|
|
} /* END MNResToString() */
|
|
|
|
|
|
|
|
|
|
int MNResToXML(
|
|
|
|
mnode_t *N, /* I */
|
|
int RIndex, /* I */
|
|
mxml_t *E, /* O */
|
|
int *AList) /* I (optional) */
|
|
|
|
{
|
|
mres_t *R;
|
|
char tmpState[MAX_MNAME];
|
|
|
|
char tmpLine[MAX_MLINE];
|
|
|
|
if ((N == NULL) || (N->R[RIndex] == NULL) || (E == NULL))
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
R = N->R[RIndex];
|
|
|
|
MXMLSetAttr(E,(char *)MNResAttr[mnraName],R->Name,mdfString);
|
|
|
|
sprintf(tmpLine,"%ld",
|
|
R->StartTime);
|
|
|
|
MXMLSetAttr(E,(char *)MNResAttr[mnraStart],tmpLine,mdfString);
|
|
|
|
sprintf(tmpLine,"%ld",
|
|
R->EndTime);
|
|
|
|
MXMLSetAttr(E,(char *)MNResAttr[mnraEnd],tmpLine,mdfString);
|
|
|
|
switch(R->Type)
|
|
{
|
|
case mrtJob:
|
|
|
|
{
|
|
mjob_t *J;
|
|
|
|
strcpy(tmpLine,"Job");
|
|
|
|
if ((J = (mjob_t *)R->J) == NULL)
|
|
{
|
|
DBG(2,fUI) DPrint("ERROR: cannot locate host job '%s'\n",
|
|
R->Name);
|
|
|
|
strcpy(tmpState,MJobState[mjsUnknown]);
|
|
}
|
|
else
|
|
{
|
|
strcpy(tmpState,MJobState[J->State]);
|
|
}
|
|
} /* END BLOCK */
|
|
|
|
break;
|
|
|
|
case mrtUser:
|
|
|
|
strcpy(tmpLine,"User");
|
|
|
|
strcpy(tmpState,"N/A");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
strcpy(tmpLine,"?");
|
|
|
|
strcpy(tmpState,"N/A");
|
|
|
|
break;
|
|
} /* END switch(R->Type) */
|
|
|
|
MXMLSetAttr(E,(char *)MNResAttr[mnraState],tmpState,mdfString);
|
|
|
|
MXMLSetAttr(E,(char *)MNResAttr[mnraType],tmpLine,mdfString);
|
|
|
|
sprintf(tmpLine,"%d",
|
|
N->RC[RIndex]);
|
|
|
|
MXMLSetAttr(E,(char *)MNResAttr[mnraTC],tmpLine,mdfString);
|
|
|
|
/* NOTE: mnraDRes not handled */
|
|
|
|
return(SUCCESS);
|
|
} /* END MNResToString() */
|
|
|
|
|
|
|
|
|
|
int MRLSFromA(
|
|
|
|
long MinDuration, /* I */
|
|
mrange_t *ARL, /* I */
|
|
mrange_t *SRL) /* O */
|
|
|
|
{
|
|
int aindex;
|
|
int sindex;
|
|
|
|
int rindex;
|
|
|
|
long RangeTime;
|
|
|
|
int TaskCount;
|
|
|
|
/* NOTE: assumes intra-node ranges */
|
|
|
|
const char *FName = "MRLSFromA";
|
|
|
|
DBG(6,fSCHED) DPrint("%s(%ld,ARL,SRL)\n",
|
|
FName,
|
|
MinDuration);
|
|
|
|
if ((ARL == NULL) || (SRL == NULL))
|
|
{
|
|
return(FAILURE);
|
|
}
|
|
|
|
sindex = 0;
|
|
|
|
for (aindex = 0;ARL[aindex].EndTime > 0;aindex++)
|
|
{
|
|
/* NOTE: must take into account connected ranges */
|
|
|
|
RangeTime = ARL[aindex].EndTime - ARL[aindex].StartTime;
|
|
|
|
TaskCount = ARL[aindex].TaskCount;
|
|
|
|
for (rindex = aindex + 1;ARL[rindex].EndTime != 0;rindex++)
|
|
{
|
|
if (ARL[rindex - 1].EndTime != ARL[rindex].StartTime)
|
|
break;
|
|
|
|
RangeTime += ARL[rindex].EndTime - ARL[rindex].StartTime;
|
|
TaskCount = MIN(TaskCount,ARL[rindex].TaskCount);
|
|
|
|
if (RangeTime > (ARL[aindex].EndTime - ARL[aindex].StartTime + MinDuration))
|
|
break;
|
|
} /* END for (rindex) */
|
|
|
|
if (RangeTime >= MinDuration)
|
|
{
|
|
SRL[sindex].StartTime = ARL[aindex].StartTime;
|
|
SRL[sindex].EndTime = MIN(ARL[aindex].EndTime,ARL[aindex].StartTime + RangeTime - MinDuration);
|
|
SRL[sindex].TaskCount = TaskCount;
|
|
SRL[sindex].NodeCount = 1;
|
|
|
|
sindex++;
|
|
}
|
|
} /* END for (aindex) */
|
|
|
|
SRL[sindex].EndTime = 0;
|
|
|
|
if ((SRL[0].EndTime == 0) || (SRL[0].TaskCount == 0))
|
|
{
|
|
if ((SRL[0].TaskCount == 0) && (SRL[0].EndTime != 0))
|
|
{
|
|
DBG(0,fSCHED) DPrint("ALERT: corrupt range found in %s (initial range empty)\n",
|
|
FName);
|
|
|
|
for (aindex = 0;ARL[aindex].EndTime > 0;aindex++)
|
|
{
|
|
DBG(2,fSCHED) DPrint("INFO: ARL[%d] %ld - %ld %d %d\n",
|
|
aindex,
|
|
ARL[aindex].StartTime,
|
|
ARL[aindex].EndTime,
|
|
ARL[aindex].TaskCount,
|
|
ARL[aindex].NodeCount);
|
|
} /* END for (aindex) */
|
|
}
|
|
|
|
return(FAILURE);
|
|
} /* END if ((SRL[0].EndTime == 0) || (SRL[0].TaskCount == 0)) */
|
|
|
|
return(SUCCESS);
|
|
} /* MRLSFromA() */
|
|
|
|
|
|
/* END MRes.c */
|
|
|