fix(scheduler): global limits on per-host resources now read the correct want (bug-1789)
sched_fill_limit_requests() used to read a limit's underlying resource's want from the same vector its own virtual-resource entry gets written to. That's wrong whenever the two differ in scope -- e.g. a GLOBAL limit built on top of a per-host resource like "slots" (limits.yaml's ug1_normal_slots): the limit's own vres entry belongs in alloc_req->shared, but "slots"'s want is only ever staged into alloc_req->host by seqjob/parjob_prepare_allocation(), so the old code always read a zero and silently never enforced the cap. Now both the read source and write destination are derived independently from each resource's own XCE_RESF_GLOBAL flag (vres->res for the source, vres->vres for the destination) -- the same flag calc_wanted_global_resvals()/calc_wanted_host_resvals() (resreq.c) already dispatch on, so this is the existing rule applied consistently rather than a new one. Also drops sched_fill_limit_requests()'s unused xce_scheduler_t* parameter. Full scheduler suite: 76/77 (the one failure, test_job_rusage_reconciles_ mem_reservation_against_usage, is bug-1762, pre-existing and unrelated). Live-verified against a fresh single-node deploy. Filed bug-1790 to track a known, deliberately out-of-scope residual gap: a future global limit on a per-host resource other than "slots" would need calc_wanted_host_resvals() to also run during the global phase for that resource, which nothing currently does. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GLfB5uWRdmx2WiZSFC7Fm7
This commit is contained in:
parent
22cfe61642
commit
924ac32c1d
28 changed files with 332 additions and 84 deletions
File diff suppressed because one or more lines are too long
|
|
@ -323,6 +323,6 @@ extern int sched_try_alloc_on_host_(xce_sched_worker_t*,
|
|||
extern int get_resvals(zarray_t*, int, int*, float*);
|
||||
extern int put_resvals(zarray_t*, int, int*, float*);
|
||||
extern request_res_t* make_resource_request(char*, float);
|
||||
extern int sched_fill_limit_requests(xce_scheduler_t*, xvec_idval_t*, xlist_t*);
|
||||
extern int sched_fill_limit_requests(xce_sched_alloc_req_t*, xlist_t*);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -644,8 +644,7 @@ int plugin_prepare_allocation(void* arg, xce_sched_ctx_t* ctx)
|
|||
if (ctx->worker == NULL) {
|
||||
/* sequence/parallel job plugin should fill the basic rusages based on
|
||||
* resreq */
|
||||
sched_fill_limit_requests(sched, &(ctx->alloc_req.shared),
|
||||
&(obj->glimits));
|
||||
sched_fill_limit_requests(&(ctx->alloc_req), &(obj->glimits));
|
||||
} else {
|
||||
xlist_t* hlimits = NULL;
|
||||
|
||||
|
|
@ -653,7 +652,7 @@ int plugin_prepare_allocation(void* arg, xce_sched_ctx_t* ctx)
|
|||
* resreq */
|
||||
hlimits = (xlist_t*)zarray_get(&(obj->hlimits), ctx->worker->idx);
|
||||
if (hlimits != NULL) {
|
||||
sched_fill_limit_requests(sched, &(ctx->alloc_req.host), hlimits);
|
||||
sched_fill_limit_requests(&(ctx->alloc_req), hlimits);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -909,22 +909,53 @@ static float get_resval_by_id(xvec_idval_t* vec, int idx)
|
|||
return 0.0;
|
||||
}
|
||||
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* vec,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < limits->count; i++) {
|
||||
xce_vres_t* vres = (xce_vres_t*)xlist_get(limits, i);
|
||||
float val = 0.0;
|
||||
xce_vres_t* vres = (xce_vres_t*)xlist_get(limits, i);
|
||||
xvec_idval_t* src;
|
||||
xvec_idval_t* dst;
|
||||
float val = 0.0;
|
||||
|
||||
if (vres == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
val = get_resval_by_id(vec, vres->res->idx);
|
||||
/* bug-1789: a limit's own scope (global vs host -- which of
|
||||
* alloc_req->shared/.host its OWN virtual resource entry,
|
||||
* vres->vres, gets written into) is independent of its
|
||||
* UNDERLYING resource's (vres->res) own nature. "slots" is a
|
||||
* per-host resource (not XCE_RESF_GLOBAL) even when the limit
|
||||
* built on top of it is global (e.g. limits.yaml's
|
||||
* ug1_normal_slots, summing slots across the whole cluster) --
|
||||
* reading its want from `dst` itself (as this used to, via a
|
||||
* caller-supplied `vec` equal to `dst`) silently missed every
|
||||
* time for such a limit, since seqjob_prepare_allocation()/
|
||||
* parjob_prepare_allocation() only ever stage a per-host
|
||||
* resource's want into alloc_req->host, never alloc_req->shared,
|
||||
* regardless of which phase is running.
|
||||
*
|
||||
* Both `src` (where the underlying resource's want lives) and
|
||||
* `dst` (where THIS limit's own vres entry belongs) are fully
|
||||
* derivable from each resource's own XCE_RESF_GLOBAL flag --
|
||||
* `vres->res`'s flag is the SAME one calc_wanted_global_resvals()/
|
||||
* calc_wanted_host_resvals() (resreq.c) dispatch on to decide
|
||||
* where THEY write a resource's want; `vres->vres`'s flag was set
|
||||
* by instantiate_limit()->add_vres() from the exact same
|
||||
* is_host_based_limit() call that decided whether this limit
|
||||
* landed in obj->glimits or obj->hlimits in the first place -- so
|
||||
* no caller-supplied vec is needed at all. */
|
||||
src = ((vres->res->flags & XCE_RESF_GLOBAL) != 0) ? &(alloc_req->shared)
|
||||
: &(alloc_req->host);
|
||||
dst = ((vres->vres->flags & XCE_RESF_GLOBAL) != 0) ? &(alloc_req->shared)
|
||||
: &(alloc_req->host);
|
||||
|
||||
xvec_idval_store(vec, vres->vres->idx, val);
|
||||
val = get_resval_by_id(src, vres->res->idx);
|
||||
|
||||
xvec_idval_store(dst, vres->vres->idx, val);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -240,11 +240,10 @@ int sched_publish_pend_reason(xce_scheduler_t* sched, xce_preason_set_t* r)
|
|||
* plugin.c's plugin_prepare_allocation() references it; never actually
|
||||
* invoked here (this test never calls plugin_prepare_allocation()). See
|
||||
* file header comment. */
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* vec,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)vec;
|
||||
(void)alloc_req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -174,11 +174,10 @@ int sched_reconcile_job_resource_usage(xce_sched_core_t* c,
|
|||
|
||||
void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; }
|
||||
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* vec,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)vec;
|
||||
(void)alloc_req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,11 +128,10 @@ void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; }
|
|||
/* plugin.c's plugin_prepare_allocation() (a category unrelated to picker
|
||||
* resolution) calls this -- stub matching test_plugin_picker_registration.c's
|
||||
* own. */
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* req,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)req;
|
||||
(void)alloc_req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,11 +37,10 @@
|
|||
* same way test_handlers.c stubs sched_init_config/sched_get_job: this
|
||||
* test never exercises plugin_prepare_allocation, so a no-op body is
|
||||
* sufficient to satisfy the linker. */
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* req,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)req;
|
||||
(void)alloc_req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,11 +147,10 @@ int sched_publish_pend_reason(xce_scheduler_t* sched, xce_preason_set_t* r)
|
|||
/* Link-satisfying stub for resource.c's sched_fill_limit_requests() --
|
||||
* plugin.c's plugin_prepare_allocation() references it; never actually
|
||||
* invoked here. */
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* vec,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)vec;
|
||||
(void)alloc_req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,11 +115,10 @@ int sched_reconcile_job_resource_usage(xce_sched_core_t* c,
|
|||
|
||||
void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; }
|
||||
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* vec,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)vec;
|
||||
(void)alloc_req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -164,11 +164,10 @@ int sched_reconcile_job_resource_usage(xce_sched_core_t* c,
|
|||
|
||||
void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; }
|
||||
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* vec,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)vec;
|
||||
(void)alloc_req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,11 +132,10 @@ int sched_reconcile_job_resource_usage(xce_sched_core_t* c,
|
|||
|
||||
void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; }
|
||||
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* vec,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)vec;
|
||||
(void)alloc_req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,11 +129,10 @@ int sched_reconcile_job_resource_usage(xce_sched_core_t* c,
|
|||
|
||||
void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; }
|
||||
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* vec,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)vec;
|
||||
(void)alloc_req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,11 +124,10 @@ int sched_reconcile_job_resource_usage(xce_sched_core_t* c,
|
|||
|
||||
void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; }
|
||||
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* vec,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)vec;
|
||||
(void)alloc_req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,11 +126,10 @@ int sched_reconcile_job_resource_usage(xce_sched_core_t* c,
|
|||
|
||||
void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; }
|
||||
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* vec,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)vec;
|
||||
(void)alloc_req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,11 +95,10 @@ int sched_reconcile_job_resource_usage(xce_sched_core_t* c,
|
|||
|
||||
void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; }
|
||||
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* vec,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)vec;
|
||||
(void)alloc_req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,11 +91,10 @@ int sched_reconcile_job_resource_usage(xce_sched_core_t* c,
|
|||
|
||||
void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; }
|
||||
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* vec,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)vec;
|
||||
(void)alloc_req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,11 +104,10 @@ int sched_reconcile_job_resource_usage(xce_sched_core_t* c,
|
|||
|
||||
void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; }
|
||||
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* vec,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)vec;
|
||||
(void)alloc_req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -145,11 +145,10 @@ int sched_reconcile_job_resource_usage(xce_sched_core_t* c,
|
|||
|
||||
void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; }
|
||||
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* vec,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)vec;
|
||||
(void)alloc_req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -816,6 +816,222 @@ static void test_ctx_uncommit_host_reverses_provisional_debit(void)
|
|||
TEST_ASSERT_EQUAL_FLOAT(0.0f, ((float*)uncomit->data)[UNCOMMIT_RESIDX]);
|
||||
}
|
||||
|
||||
/*
|
||||
* bug-1789: sched_fill_limit_requests() (resource.c) computes, for each
|
||||
* matched limit, how many units of the limit's own virtual resource
|
||||
* (vres->vres) a job wants -- by reading how many units of the limit's
|
||||
* UNDERLYING resource (vres->res) the job already requested. Both the
|
||||
* READ source and the WRITE destination are one of alloc_req->shared/
|
||||
* .host, each independently chosen by that resource's own
|
||||
* XCE_RESF_GLOBAL flag: vres->res's flag decides the read source, the
|
||||
* SAME flag calc_wanted_global_resvals()/calc_wanted_host_resvals()
|
||||
* (resreq.c) dispatch on to decide where THEY write it; vres->vres's own
|
||||
* flag (set by instantiate_limit()->add_vres() from the same
|
||||
* is_host_based_limit() call that put this limit in obj->glimits vs
|
||||
* obj->hlimits in the first place) decides the write destination -- no
|
||||
* caller-supplied vector needed for either side. These two flags are
|
||||
* independent: "slots" is per-host (not XCE_RESF_GLOBAL) even when the
|
||||
* limit built on it is GLOBAL (limits.yaml's ug1_normal_slots, summing
|
||||
* slots across the whole cluster) -- seqjob_prepare_allocation()/
|
||||
* parjob_prepare_allocation() only ever stage a per-host resource's want
|
||||
* into alloc_req->host, never alloc_req->shared, regardless of phase.
|
||||
* This test proves the GLOBAL-limit/per-host-resource combination
|
||||
* (bug-1789's actual reported case): the limit's own vres is
|
||||
* GLOBAL-flagged (write target alloc_req->shared), but its underlying
|
||||
* resource is not (its want must be read from alloc_req->host).
|
||||
*/
|
||||
static void
|
||||
test_fill_limit_requests_global_limit_on_host_resource_reads_host(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_resource_t slots_res_in = { 0 };
|
||||
xce_resource_t limit_res_in = { 0 };
|
||||
xce_resource_t* slots_res;
|
||||
xce_resource_t* limit_res;
|
||||
xce_sched_alloc_req_t alloc_req;
|
||||
xlist_t limits;
|
||||
xce_vres_t vres;
|
||||
int rc;
|
||||
|
||||
memset(&sched, 0, sizeof(sched));
|
||||
TEST_ASSERT_EQUAL_INT(0, xce_sched_core_init(&(sched.core)));
|
||||
|
||||
/* "slots" -- deliberately NOT XCE_RESF_GLOBAL, matching the real
|
||||
* makeResource() call every host-capacity resource in this codebase
|
||||
* goes through. */
|
||||
slots_res_in.name = strdup("slots");
|
||||
slots_res = makeResource(sched.core.resmgr, &slots_res_in);
|
||||
TEST_ASSERT_NOT_NULL(slots_res);
|
||||
|
||||
/* the LIMIT's own virtual resource -- GLOBAL, exactly as
|
||||
* instantiate_limit()->add_vres() would flag it for a limit like
|
||||
* ug1_normal_slots (is_host_based_limit() == 0). */
|
||||
limit_res_in.name = strdup("limit=ug1_normal_slots");
|
||||
limit_res_in.flags = XCE_RESF_GLOBAL;
|
||||
limit_res = makeResource(sched.core.resmgr, &limit_res_in);
|
||||
TEST_ASSERT_NOT_NULL(limit_res);
|
||||
|
||||
memset(&vres, 0, sizeof(vres));
|
||||
vres.res = slots_res;
|
||||
vres.vres = limit_res;
|
||||
|
||||
rc = xlist_init(&limits, 4);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
rc = xlist_store(&limits, &vres);
|
||||
TEST_ASSERT_TRUE(rc >= 0);
|
||||
|
||||
rc = xvec_idval_init(&(alloc_req.shared), 8);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
rc = xvec_idval_init(&(alloc_req.host), 8);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
/* pre-staged into .host, as seqjob/parjob_prepare_allocation() always
|
||||
* do for "slots" regardless of phase -- .shared (this limit's own
|
||||
* write target, derived from limit_res's GLOBAL flag) starts empty. */
|
||||
rc = xvec_idval_store(&(alloc_req.host), slots_res->idx, 6.0f);
|
||||
TEST_ASSERT_TRUE(rc >= 0);
|
||||
|
||||
rc = sched_fill_limit_requests(&alloc_req, &limits);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(1, alloc_req.shared.count);
|
||||
TEST_ASSERT_EQUAL_INT(limit_res->idx, ((int*)alloc_req.shared.idxs.data)[0]);
|
||||
TEST_ASSERT_EQUAL_FLOAT(6.0f, ((float*)alloc_req.shared.vals.data)[0]);
|
||||
/* .host stays exactly as pre-staged -- the limit's own vres entry
|
||||
* went to .shared, nothing new was written to .host. */
|
||||
TEST_ASSERT_EQUAL_INT(1, alloc_req.host.count);
|
||||
|
||||
xlist_fini(&limits);
|
||||
}
|
||||
|
||||
/*
|
||||
* bug-1789 (already-working sibling case, unaffected by the fix): a
|
||||
* HOST-scoped limit (its own vres NOT GLOBAL-flagged, so its write
|
||||
* target is alloc_req->host) on a per-host resource (e.g. "mem", also
|
||||
* not XCE_RESF_GLOBAL) reads from alloc_req->host too -- same vector as
|
||||
* its own write target, exactly as before this fix.
|
||||
*/
|
||||
static void
|
||||
test_fill_limit_requests_host_limit_on_host_resource_reads_host(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_resource_t mem_res_in = { 0 };
|
||||
xce_resource_t limit_res_in = { 0 };
|
||||
xce_resource_t* mem_res;
|
||||
xce_resource_t* limit_res;
|
||||
xce_sched_alloc_req_t alloc_req;
|
||||
xlist_t limits;
|
||||
xce_vres_t vres;
|
||||
int rc;
|
||||
|
||||
memset(&sched, 0, sizeof(sched));
|
||||
TEST_ASSERT_EQUAL_INT(0, xce_sched_core_init(&(sched.core)));
|
||||
|
||||
mem_res_in.name = strdup(SCHED_MEM_RESOURCE_NAME);
|
||||
mem_res = makeResource(sched.core.resmgr, &mem_res_in);
|
||||
TEST_ASSERT_NOT_NULL(mem_res);
|
||||
|
||||
/* the LIMIT's own virtual resource -- NOT GLOBAL (host-scoped limit,
|
||||
* is_host_based_limit() == 1); flags left at the struct's zero
|
||||
* default is sufficient, mirroring add_vres()'s XCE_RESF_DEFAULT_HOST
|
||||
* (not GLOBAL) branch. */
|
||||
limit_res_in.name = strdup("limit=mem_cap");
|
||||
limit_res = makeResource(sched.core.resmgr, &limit_res_in);
|
||||
TEST_ASSERT_NOT_NULL(limit_res);
|
||||
|
||||
memset(&vres, 0, sizeof(vres));
|
||||
vres.res = mem_res;
|
||||
vres.vres = limit_res;
|
||||
|
||||
rc = xlist_init(&limits, 4);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
rc = xlist_store(&limits, &vres);
|
||||
TEST_ASSERT_TRUE(rc >= 0);
|
||||
|
||||
rc = xvec_idval_init(&(alloc_req.shared), 8);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
rc = xvec_idval_init(&(alloc_req.host), 8);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
/* pre-staged, as calc_wanted_host_resvals() would have already done
|
||||
* for a real "mem" rusage want. */
|
||||
rc = xvec_idval_store(&(alloc_req.host), mem_res->idx, 3.0f);
|
||||
TEST_ASSERT_TRUE(rc >= 0);
|
||||
|
||||
rc = sched_fill_limit_requests(&alloc_req, &limits);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(2, alloc_req.host.count);
|
||||
TEST_ASSERT_EQUAL_INT(limit_res->idx, ((int*)alloc_req.host.idxs.data)[1]);
|
||||
TEST_ASSERT_EQUAL_FLOAT(3.0f, ((float*)alloc_req.host.vals.data)[1]);
|
||||
|
||||
xlist_fini(&limits);
|
||||
}
|
||||
|
||||
/*
|
||||
* bug-1789 (symmetric case, proves the dispatch is a genuine flag-based
|
||||
* rule and not a "slots" special case): a HOST-scoped limit (its own
|
||||
* vres NOT GLOBAL-flagged, so its write target is alloc_req->host) on a
|
||||
* resource that IS XCE_RESF_GLOBAL (e.g. a pooled license count) must
|
||||
* read from alloc_req->shared instead -- the opposite vector from its
|
||||
* write target, mirroring exactly how calc_wanted_global_resvals() is
|
||||
* the only one of the pair that ever populates a GLOBAL-flagged
|
||||
* resource's want.
|
||||
*/
|
||||
static void
|
||||
test_fill_limit_requests_host_limit_on_global_resource_reads_shared(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_resource_t license_res_in = { 0 };
|
||||
xce_resource_t limit_res_in = { 0 };
|
||||
xce_resource_t* license_res;
|
||||
xce_resource_t* limit_res;
|
||||
xce_sched_alloc_req_t alloc_req;
|
||||
xlist_t limits;
|
||||
xce_vres_t vres;
|
||||
int rc;
|
||||
|
||||
memset(&sched, 0, sizeof(sched));
|
||||
TEST_ASSERT_EQUAL_INT(0, xce_sched_core_init(&(sched.core)));
|
||||
|
||||
license_res_in.name = strdup("licenses");
|
||||
license_res_in.flags = XCE_RESF_GLOBAL;
|
||||
license_res = makeResource(sched.core.resmgr, &license_res_in);
|
||||
TEST_ASSERT_NOT_NULL(license_res);
|
||||
|
||||
/* the LIMIT's own virtual resource -- NOT GLOBAL (host-scoped limit),
|
||||
* same rationale as the sibling test above. */
|
||||
limit_res_in.name = strdup("limit=license_cap");
|
||||
limit_res = makeResource(sched.core.resmgr, &limit_res_in);
|
||||
TEST_ASSERT_NOT_NULL(limit_res);
|
||||
|
||||
memset(&vres, 0, sizeof(vres));
|
||||
vres.res = license_res;
|
||||
vres.vres = limit_res;
|
||||
|
||||
rc = xlist_init(&limits, 4);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
rc = xlist_store(&limits, &vres);
|
||||
TEST_ASSERT_TRUE(rc >= 0);
|
||||
|
||||
rc = xvec_idval_init(&(alloc_req.shared), 8);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
rc = xvec_idval_init(&(alloc_req.host), 8);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
/* pre-staged into .shared (this job's earlier global-phase pass), NOT
|
||||
* .host -- .host (this limit's own write target, derived from
|
||||
* limit_res's non-GLOBAL flag) starts empty. */
|
||||
rc = xvec_idval_store(&(alloc_req.shared), license_res->idx, 5.0f);
|
||||
TEST_ASSERT_TRUE(rc >= 0);
|
||||
|
||||
rc = sched_fill_limit_requests(&alloc_req, &limits);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(1, alloc_req.host.count);
|
||||
TEST_ASSERT_EQUAL_INT(limit_res->idx, ((int*)alloc_req.host.idxs.data)[0]);
|
||||
TEST_ASSERT_EQUAL_FLOAT(5.0f, ((float*)alloc_req.host.vals.data)[0]);
|
||||
|
||||
xlist_fini(&limits);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
|
|
@ -834,5 +1050,8 @@ int main(void)
|
|||
RUN_TEST(test_reconcile_job_resource_usage_noop_when_mem_not_a_registered_resource);
|
||||
RUN_TEST(test_reconcile_job_resource_usage_null_args_return_einval);
|
||||
RUN_TEST(test_ctx_uncommit_host_reverses_provisional_debit);
|
||||
RUN_TEST(test_fill_limit_requests_global_limit_on_host_resource_reads_host);
|
||||
RUN_TEST(test_fill_limit_requests_host_limit_on_host_resource_reads_host);
|
||||
RUN_TEST(test_fill_limit_requests_host_limit_on_global_resource_reads_shared);
|
||||
return UNITY_END();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,11 +123,10 @@ int sched_reconcile_job_resource_usage(xce_sched_core_t* c,
|
|||
(void)c; (void)a; (void)r; return 0;
|
||||
}
|
||||
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* vec,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)vec;
|
||||
(void)alloc_req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -196,11 +196,10 @@ void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; }
|
|||
/* plugin_prepare_allocation()'s (plugin.c) linker dependency -- never
|
||||
* invoked (this fixture never drives real resource-matching allocation --
|
||||
* see this file's header comment). */
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* vec,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)vec;
|
||||
(void)alloc_req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,11 +144,10 @@ void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; }
|
|||
/* plugin_prepare_allocation()'s (plugin.c, the "limit" plugin category)
|
||||
* linker dependency -- never invoked (this file never drives real
|
||||
* allocation, Req 5 AC 17 / this file's own header comment). */
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* vec,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)vec;
|
||||
(void)alloc_req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,11 +150,10 @@ int sched_reconcile_job_resource_usage(xce_sched_core_t* c,
|
|||
|
||||
void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; }
|
||||
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* vec,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)vec;
|
||||
(void)alloc_req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,11 +109,10 @@ int sched_reconcile_job_resource_usage(xce_sched_core_t* c,
|
|||
|
||||
void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; }
|
||||
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* vec,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)vec;
|
||||
(void)alloc_req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,11 +180,10 @@ int sched_reconcile_job_resource_usage(xce_sched_core_t* c,
|
|||
/* plugin_prepare_allocation()'s (plugin.c) linker dependency -- never
|
||||
* invoked (this fixture never drives real resource-matching allocation --
|
||||
* same rationale test_sim_equivalence.c's own copy documents). */
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* vec,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)vec;
|
||||
(void)alloc_req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,11 +136,10 @@ int sched_reconcile_job_resource_usage(xce_sched_core_t* c,
|
|||
/* plugin_prepare_allocation()'s (plugin.c, the "limit" plugin category)
|
||||
* linker dependency -- never invoked (this file never drives real
|
||||
* allocation). */
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* vec,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)vec;
|
||||
(void)alloc_req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,11 +135,10 @@ void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; }
|
|||
/* plugin_prepare_allocation()'s (plugin.c, the "limit" plugin category)
|
||||
* linker dependency -- never invoked (this file never drives real
|
||||
* allocation). */
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* vec,
|
||||
int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)vec;
|
||||
(void)alloc_req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue