#if !defined(_PLUGIN_H_) #define _PLUGIN_H_ 1 #include #include #include "decision.h" #include "resreq.h" #include "sched/types.h" typedef void* plugin_data_t; /* hooker of plugin, the first parameter should be xce_sched_plugin_t */ typedef int (*plugin_update_config_hook_t)(void*, xce_conf4sched_t*); typedef int (*plugin_update_queue_hook_t)(void*, xce_sched_queue_t*); typedef int (*plugin_update_job_hook_t)(void*, xce_sched_job_t*); typedef int (*plugin_update_worker_hook_t)(void*, xce_sched_worker_t*); typedef int (*plugin_job_state_change_hook_t)(void*, xce_sched_job_t*, uint32_t); typedef int (*plugin_commit_decision_hook_t)(void*, xce_decision_t*); typedef int (*plugin_job_election_hook_t)(void*, xce_sched_job_t*); typedef int (*plugin_prepare_allocation_hook_t)(void*, xce_sched_ctx_t*); typedef int (*plugin_job_rusage_hook_t)(void*, xce_sched_job_t*, xce_job_rusages_t*); /* initialize function of plugin */ typedef int (*plugin_init_fn_t)(void*); typedef void (*plugin_fini_fn_t)(void*); /* plugin function to elect job to be scheduler */ typedef void* (*plugin_job_election_fn_t)(void*); /* plugin callback when a job has been elected */ typedef int (*plugin_begin_session_fn_t)(void*); typedef int (*plugin_end_session_fn_t)(void*); /* allocation function of job */ typedef int (*plugin_job_allocate_fn_t)(void*, xce_sched_ctx_t*); typedef struct { plugin_update_config_hook_t config_hook; plugin_update_queue_hook_t queue_hook; plugin_update_job_hook_t job_hook; plugin_job_state_change_hook_t jstate_hook; plugin_update_worker_hook_t worker_hook; plugin_commit_decision_hook_t commit_hook; plugin_job_election_hook_t job_election_hook; plugin_prepare_allocation_hook_t prepare_allocation_hook; plugin_job_rusage_hook_t rusage_hook; } plugin_hooks_t; /* Forward declaration -- xce_job_picker_iface_t is fully defined below * (Req 1 AC 2). Only needed here as a pointer field on xce_sched_plugin_t * (picker plugin registration category, scheduler-core task 1.2); * re-declared as the same typedef once the struct body is complete below * -- same idiom as cluster_t across myconf.h/persist.h/ha.h/cluster_def.h. */ typedef struct xce_job_picker_iface_s xce_job_picker_iface_t; /* xce_sched_core_t is defined in core.h -- forward-declared here (re-declared * again, harmlessly, near plugin_register_core()'s own declaration below) * so plugin_begin_session()/plugin_end_session() can be declared against it * ahead of that later block. */ typedef struct xce_sched_core_s xce_sched_core_t; typedef struct { char* name; void* sched; uint32_t plugin_id; plugin_init_fn_t init_fn; plugin_fini_fn_t fini_fn; plugin_begin_session_fn_t begin_session_fn; plugin_end_session_fn_t end_session_fn; plugin_job_election_fn_t job_elect_fn; plugin_job_allocate_fn_t job_alloc_fn; plugin_hooks_t hooks; plugin_data_t data; /* Picker plugin registration category (task 1.2): non-NULL once this * plugin participates in the xce_job_picker_iface_t contract -- either * because it was authored against the new interface directly, or * because plugin_register() adapted it from the deprecated job_elect_fn * hook via the compatibility wrapper (Req 5 "Migration from old * job_elect_fn"). Stays NULL for plugin categories that are neither * (allocator/limit plugins never set job_elect_fn). */ xce_job_picker_iface_t* picker_iface; } xce_sched_plugin_t; extern int plugin_register(void*, void*); extern void plugin_unregister(void*); extern int plugin_config_update(void*, xce_conf4sched_t*); extern int plugin_queue_update(void*, xce_sched_queue_t*); extern int plugin_job_update(void*, xce_sched_job_t*); extern int plugin_worker_update(void*, xce_sched_worker_t*); extern int plugin_job_state_change(void*, xce_sched_job_t*, uint32_t); /* plugin_begin_session()/plugin_end_session() - relocated to xce_sched_core_t* * (bug-1681, mirroring sched_account_jobs()'s identical scheduler-sim- * decision-parity task 1.1 relocation): the per-plugin begin/end_session_fn * iteration only ever touches core->plugins, never anything else on * xce_scheduler_t* -- so, like plugin_register_core() below, these are * callable from a driver with no xce_scheduler_t* (the simulation driver). * See driver.c's xce_sched_driver_run_normal_mode_round() for the normal- * mode call site and sim_loop.c's sched_sim_driver_run() for the mirrored * simulation-mode one. */ extern int plugin_begin_session(xce_sched_core_t*); extern int plugin_end_session(xce_sched_core_t*); extern int plugin_commit_decision(void*, xce_decision_t*); extern int plugin_prepare_allocation(void*, xce_sched_ctx_t*); extern int plugin_job_election(void*, xce_sched_job_t*); extern int plugin_job_rusage_update(void*, xce_sched_job_t*, xce_job_rusages_t*); extern int register_fcfs(void*); extern int register_builtin_fcfs(void*); extern int register_limit(void*); /* * limit_new_plugin - factory half of register_limit()'s template-build * step (plugins/limit.c), extracted so a caller with no xce_scheduler_t* * (e.g. the simulation driver, via plugin_register_core() below) can * obtain a freshly calloc'd, fully populated `xce_sched_plugin_t` for the * limit plugin -- same hooks/init_fn/fini_fn template register_limit() * itself uses, just not yet registered anywhere. Mirrors * builtin_fcfs_new_plugin()'s identical extraction (below) exactly * (scheduler-sim-diagnostics spec, task 6; bug-1825). Returns NULL on * allocation failure. Caller owns the returned plugin (frees it only on a * subsequent registration failure -- once registered, the plugin registry * owns it, same lifetime rule every other plugin category in this * codebase already follows). */ extern xce_sched_plugin_t* limit_new_plugin(void); /* * limit_seed_config - bug-1825 remediation round 2 (scheduler-sim- * diagnostics spec, task 6): honestly-typed config-seed entry point for a * caller that holds a real xce_sched_core_t* but no real xce_scheduler_t* * -- the simulation driver (sim_limit.c's sim_bootstrap_limit_plugin()), * whose xce_sched_sim_driver_t (sim.h) has no xce_scheduler_t to point * plugin->sched at. Takes `core` directly with no cast and never touches * plugin->sched -- see the function's own doc comment (plugins/limit.c) * for why a plugin->sched type-pun is unsafe here (core.h's * xce_core_cache_clear_cb_t doc comment documents the real production bug * this avoids repeating). Delegates to the same seed logic * limit_update_config() (the normal-mode config_hook) itself uses, so the * two entry points cannot silently diverge in behavior. */ extern int limit_seed_config(xce_sched_plugin_t* plugin, xce_sched_core_t* core, xce_conf4sched_t* config); /* limit_get_configured_limits/limit_get_usage_instances - scheduler-sim- * diagnostics spec, Requirement Set 1 (Limit Plugin Accessor), task 1. * Read-only crossing of the plugin-private-state boundary: each looks up * the limit plugin ("xce_sched_limit") in core->plugins and returns the * requested field of its private limit_plugin_data_t (plugins/limit.c) -- * a raw kvmgr handle, iterable via iter_make()/iter_next() -- or NULL if * the plugin is not registered or not yet initialized (plugin->data == * NULL). Never allocates, mutates, or triggers plugin hooks; performs no * locking beyond what key_find() already does internally. NULL is a * semantic signal only ("nothing is configured yet"), not an error -- the * caller decides what to display for it. */ extern var limit_get_configured_limits(xce_sched_core_t* core); extern var limit_get_usage_instances(xce_sched_core_t* core); extern int register_seqjob_allocator(void*); extern int register_parjob_allocator(void*); /* --------------------------------------------------------------------- * Job picker plugin interface (xce_job_picker_iface_t) * * scheduler-core spec, Req 1 AC 2 / AC 5, Req 2 AC 6. * * Supersedes plugin_job_election_hook_t / plugin_job_election_fn_t * (job_elect_fn above) as the job-selection contract: the core scheduling * loop calls picker->next_job(picker) to get the next schedulable job (or * NULL) and carries zero knowledge of project/queue/job iteration or * ordering — all of that lives behind this interface (Req 1 AC 1). * job_elect_fn-only plugins keep working during the transition via a * compatibility wrapper (registered in plugin.c, scheduler-core task 1.2); * new pickers implement xce_job_picker_iface_t directly. * --------------------------------------------------------------------- */ /* xce_sched_core_t is defined in core.h, added by the scheduler-core spec's * Phase 2 core/driver extraction. Forward-declared here (same idiom as * cluster_t in myconf.h/persist.h/ha.h) to avoid a circular include — * init() below only ever holds/passes a pointer to it. */ typedef struct xce_sched_core_s xce_sched_core_t; /* * builtin_fcfs_new_plugin - factory half of register_builtin_fcfs()'s * template-build step (plugins/builtin_fcfs.c), extracted so a caller with * no xce_scheduler_t* (e.g. the simulation driver, via * plugin_register_core() below) can obtain a freshly calloc'd, fully * populated `xce_sched_plugin_t` for builtin_fcfs -- same picker_iface/ * init_fn/fini_fn template register_builtin_fcfs() itself uses, just not * yet registered anywhere. Returns NULL on allocation failure. Caller owns * the returned plugin (frees it only on a subsequent registration failure * -- once registered, the plugin registry owns it, same lifetime rule * every other plugin category in this codebase already follows). */ extern xce_sched_plugin_t* builtin_fcfs_new_plugin(void); /* * plugin_register_core - Req 5 AC 17 (scheduler-core spec, task 11.2): the * "core.h-scoped plugin registration path" sim.h's own doc comment on * sched_sim_driver_start() anticipated but did not build (task 11.1's * documented "Known scope boundary"). plugin_register() above requires a * genuine `xce_scheduler_t*` (it dereferences `sched->core.plugins` and * stores `plugin->sched = sched` for plugin_unregister()'s later use) -- * unavailable to a driver like the simulation driver that deliberately has * no such type. This is the SAME registration mechanism minus that one * dependency: run the plugin's init_fn (same "driver thread, at plugin * register" timing plugin.h's own hook-category doc comment documents), * then key_add() it into `core->plugins` directly -- identical steps * plugin_register() itself performs, in the same order, just addressed at * xce_sched_core_t* instead of xce_scheduler_t*. `plugin->sched` is left * NULL (no xce_scheduler_t* exists to store) -- safe, since * plugin_unregister() is a no-op on a NULL `plugin->sched` and nothing * calls it for a plugin registered this way (no sim-driver teardown path * exists yet). Returns the assigned plugin_id (>= 0) on success, or a * negative errno: -EINVAL for a NULL core/plugin, whatever init_fn * returned if it failed, or key_add()'s own negative return. */ extern int plugin_register_core(xce_sched_core_t*, xce_sched_plugin_t*); /* * Hook categories and the thread each category runs in (Req 1 AC 3): * * Lifecycle hooks (MUST) — driver thread, at plugin register/teardown: * init One-time setup; receives a core reference for state queries. * Called exactly once, before any other hook. * fini Teardown; frees all picker-owned resources. * * State-change hooks (MUST) — driver thread; called synchronously, only * from inside core_push_events(), which itself is only invoked between * sessions (picker thread blocked on cbuf_get(&wbuf), allocator thread * blocked on cbuf_get(&jbuf)). No mid-session event injection: * on_queue_update Add/update a queue in the picker's internal * ordering. <- QUEUE_UPDATE event * on_job_push Add a newly pending job to the candidate set. * <- JOBS_PUSH event * on_job_state_change Drop the job from candidates if it left pending; * re-add if it returned to pending. * <- JOB_STATE_CHANGE event * on_job_update Reorder the job in candidates if its priority or * queue changed. <- JOB_UPDATE event * * Session hooks (MUST) — picker thread, at scheduling-round boundaries: * begin_session Reset the iteration cursor. Called at the start of * every scheduling round. Session-aware requirement: a * picker MUST reset all iteration state here — never * carry it over from the previous session. * next_job Return the next schedulable job per the picker's * ordering, or NULL once exhausted. Cancel-safe * requirement: next_job() runs in the dedicated picker * thread, which is torn down via pthread_cancel — the * iteration loop inside next_job() (and any blocking * point it reaches, e.g. cbuf_get(&wbuf) between * sessions) MUST have defined pthread_cancel * cancellation points (pthread_testcancel() or an * existing cancellation-point call). * end_session End-of-round notification; called before the core * sleeps between rounds. * * Post-allocation hooks (MUST) — allocator thread, after each allocation * attempt for a job next_job() returned: * on_dispatch Job was allocated; remove it from candidates * permanently. * on_pend Job was pended (or preempted back to pend); keep it for * the next round, may deprioritize. * * Optional hooks (MAY be NULL) — same thread as the triggering event * above (on_config: driver thread inside core_push_events(); on_job_rusage: * driver thread inside core_push_events()): * on_config Rebuild picker strategy from config. NULL if the * picker is config-agnostic. * on_job_rusage Track per-job resource usage. NULL if the picker is * not resource-aware. * * Contract (Req 1 AC 5, Req 2 AC 6): * - Cancel-safe: next_job() has defined pthread_cancel cancellation * points. * - Session-aware: begin_session() resets all iteration state. * - Hook implementations never block indefinitely — the picker thread * holds no lock the allocator thread needs. * - next_job() never returns the same job twice within one session. * * Preconditions / postconditions: * - init() is called exactly once before any other hook. * - begin_session() precedes every next_job() sequence. * - next_job() returns NULL exactly when no ready job remains for the * current session. */ typedef struct xce_job_picker_iface_s { int (*init)(void* picker, xce_sched_core_t* core); void (*fini)(void* picker); int (*begin_session)(void* picker); xce_sched_job_t* (*next_job)(void* picker); int (*end_session)(void* picker); int (*on_dispatch)(void* picker, uint64_t jobid); int (*on_pend)(void* picker, uint64_t jobid); int (*on_queue_update)(void* picker, xce_sched_queue_t* queue); int (*on_job_push)(void* picker, xce_sched_job_t* job); int (*on_job_state_change)(void* picker, xce_sched_job_t* job, uint32_t state); int (*on_job_update)(void* picker, xce_sched_job_t* job); int (*on_config)(void* picker, xce_conf4sched_t* config); /* MAY be NULL */ int (*on_job_rusage)(void* picker, xce_sched_job_t* job, xce_job_rusages_t* rusages); /* MAY be NULL */ } xce_job_picker_iface_t; #endif