/** * @brief * cmp_events - sort jobs/resvs into a timeline of the next event * to happen: running jobs ending, advanced reservations starting * or ending * * @param[in] v1 - resource_resv 1 * @param[in] v2 - resource_resv 2 * * @return int * @retval -1 : if v1 < v2 * @retval 0 : if v1 == v2 * @retval 1 : if v1 > v2 */ int cmp_events(const void *v1, const void *v2) { resource_resv *r1, *r2; time_t t1, t2; int run1, run2; /* are r1 and r2 in runnable states? */ int end_event1 = 0, end_event2 = 0; /* are r1 and r2 end events? */ r1 = *((resource_resv **) v1); r2 = *((resource_resv **) v2); if (r1->start != UNSPECIFIED && r2->start ==UNSPECIFIED) return -1; if (r1->start == UNSPECIFIED && r2->start ==UNSPECIFIED) return 0; if (r1->start == UNSPECIFIED && r2->start !=UNSPECIFIED) return 1; run1 = in_runnable_state(r1); run2 = in_runnable_state(r2); if (r1->start >= r1->server->server_time && run1) t1 = r1->start; else { end_event1 = 1; t1 = r1->end; } if (r2->start >= r2->server->server_time && run2) t2 = r2->start; else { end_event2 = 1; t2 = r2->end; } if (t1 < t2) return -1; else if (t1 == t2) { /* if event times are equal, this means that events which consume * resources and release resources happen at the same time. We need to * make sure events which release resources come first, so the events * which consume them, can indeed do that. */ if (end_event1) return -1; else if (end_event2) return 1; else return 0; } else return 1; }
/** * @brief * create_events - creates an timed_event list from running jobs * and confirmed reservations * * @param[in] sinfo - server universe to act upon * * @return timed_event list * */ timed_event * create_events(server_info *sinfo) { timed_event *events = NULL; timed_event *te = NULL; resource_resv **all = NULL; int errflag = 0; int i = 0; time_t end = 0; /* all_resresv is sorted such that the timed events are in the front of * the array. Once the first non-timed event is reached, we're done */ all = sinfo->all_resresv; /* sort the all resersv list so all the timed events are in the front */ qsort(all, count_array((void **)all), sizeof(resource_resv *), cmp_events); for (i = 0; all[i] != NULL && is_timed(all[i]); i++) { if (all[i]->is_resv && all[i]->resv && all[i]->resv->resv_state == RESV_BEING_ALTERED) continue; /* only add a run event for a job or reservation if they're * in a runnable state (i.e. don't add it if they're running) */ if (in_runnable_state(all[i])) { te = create_event(TIMED_RUN_EVENT, all[i]->start, all[i], NULL, NULL); if (te == NULL) { errflag++; break; } events = add_timed_event(events, te); } if (sinfo->use_hard_duration) end = all[i]->start + all[i]->hard_duration; else end = all[i]->end; te = create_event(TIMED_END_EVENT, end, all[i], NULL, NULL); if (te == NULL) { errflag++; break; } events = add_timed_event(events, te); } /* A malloc error was encountered, free all allocated memory and return */ if (errflag > 0) { free_timed_event_list(events); return 0; } return events; }
/** * @brief * entrypoint into job sort used by qsort * * 1. Sort all preemption priority jobs in the front * 2. Sort all preempted jobs in ascending order of their preemption time * 3. Sort all starving jobs after the high priority jobs * 4. Sort jobs according to their fairshare usage. * 5. sort by unique rank to stabilize the sort * * @param[in] v1 - resource_resv 1 * @param[in] v2 - resource_resv 2 * * @return -1,0,1 : based on sorting function. */ int cmp_sort(const void *v1, const void *v2) { int cmp; resource_resv *r1; resource_resv *r2; r1 = *((resource_resv **) v1); r2 = *((resource_resv **) v2); if (r1 != NULL && r2 == NULL) return -1; if (r1 == NULL && r2 == NULL) return 0; if (r1 == NULL && r2 != NULL) return 1; if (in_runnable_state(r1) && !in_runnable_state(r2)) return -1; else if (in_runnable_state(r2) && !in_runnable_state(r1)) return 1; /* both jobs are runnable */ else { /* sort based on preemption */ cmp = cmp_preemption(r1, r2); if (cmp != 0) return cmp; cmp = cmp_job_preemption_time_asc(&r1, &r2); if (cmp != 0) return cmp; #ifndef NAS /* localmod 041 */ if (r1->is_job && r1->server->policy->help_starving_jobs) { cmp = cmp_starving_jobs(&r1, &r2); if (cmp != 0) return cmp; } #endif /* localmod 041 */ /* sort on the basis of job sort formula */ cmp = cmp_job_sort_formula(&r1, &r2); if (cmp != 0) return cmp; #ifndef NAS /* localmod 041 */ if (r1->server->policy->fair_share) { cmp = cmp_fairshare(&r1, &r2); if (cmp != 0) return cmp; } #endif /* localmod 041 */ /* normal resource based sort */ cmp = multi_sort(r1, r2); if (cmp != 0) return cmp; /* stabilize the sort */ else { if (r1->qrank < r2->qrank) return -1; else if (r1->qrank > r2->qrank) return 1; if (r1->rank < r2->rank) return -1; else if (r1->rank > r2->rank) return 1; else { return 0; } } } }
/** * @brief * update_resresv_on_end - update a resource_resv structure when * it ends * * @param[out] resresv - the resresv to update * @param[in] job_state - the new state if resresv is a job * * @return nothing * */ void update_resresv_on_end(resource_resv *resresv, char *job_state) { queue_info *resv_queue; resource_resv *next_occr = NULL; time_t next_occr_time; char logbuf[MAX_LOG_SIZE]; int ret; int i; if (resresv == NULL) return; /* now that it isn't running, it might be runnable again */ resresv->can_not_run = 0; /* unless of course it's a job and its queue is in an ineligible state */ if (resresv->is_job && resresv->job != NULL && !resresv->job->queue->is_ok_to_run) resresv->can_not_run = 1; /* no longer running... clear start and end times */ resresv->start = UNSPECIFIED; resresv->end = UNSPECIFIED; if (resresv->is_job && resresv->job != NULL) { set_job_state(job_state, resresv->job); if (resresv->job->is_suspended) { #ifndef NAS /* localmod 005 */ int i; #endif /* localmod 005 */ nspec **ns = resresv->nspec_arr; resresv->job->is_susp_sched = 1; for (i = 0; ns[i] != NULL; i++) ns[i]->ninfo->num_susp_jobs++; } resresv->job->is_provisioning = 0; /* free resources allocated to job since it's now been requeued */ if (resresv->job->is_queued && !resresv->job->is_checkpointed) { free(resresv->ninfo_arr); resresv->ninfo_arr = NULL; free_nspecs(resresv->nspec_arr); resresv->nspec_arr = NULL; free_resource_req_list(resresv->job->resused); resresv->job->resused = NULL; if (resresv->nodepart_name != NULL) { free(resresv->nodepart_name); resresv->nodepart_name = NULL; } free_selspec(resresv->execselect); resresv->execselect = NULL; } } else if (resresv->is_resv && resresv->resv !=NULL) { resresv->resv->resv_state = RESV_DELETED; resv_queue = find_queue_info(resresv->server->queues, resresv->resv->queuename); if (resv_queue != NULL) { resv_queue->is_started = 0; ret = is_ok_to_run_queue(resresv->server->policy, resv_queue); if (ret == SUCCESS) resv_queue->is_ok_to_run = 1; else resv_queue->is_ok_to_run = 0; if (resresv->resv->is_standing) { /* This occurrence is over, move resv pointers of all jobs that are * left to next occurrence if one exists */ if (resresv->resv->resv_idx < resresv->resv->count) { next_occr_time = get_occurrence(resresv->resv->rrule, resresv->resv->req_start, resresv->resv->timezone, 2); if (next_occr_time >= 0) { next_occr = find_resource_resv_by_time(resresv->server->resvs, resresv->name, next_occr_time); if (next_occr != NULL) { if (resv_queue->jobs != NULL) { for (i = 0; resv_queue->jobs[i] != NULL; i++) { if (in_runnable_state(resv_queue->jobs[i])) resv_queue->jobs[i]->job->resv = next_occr; } } } else { snprintf(logbuf, MAX_LOG_SIZE, "Can't find occurrence of standing reservation at time %ld", next_occr_time); schdlog(PBSEVENT_DEBUG, PBS_EVENTCLASS_SERVER, LOG_DEBUG, resresv->name, logbuf); } } } } } } }