==> src/jobs.c <==1/* Job items (plan F5): leases queued per slot, each job's setup first, items published as OP tickets. */
2#include "driver_int.h"
3
4#include <stdlib.h>
5#include <string.h>
6
7/* Job item tickets in flight per slot: two per node keep every node fed (Metal: `fanout`, one command buffer). */
8uint32_t drv_job_cap(const mesh_manifest *m, uint32_t exec) {
9 if (exec == MESH_BACKEND_METAL) return m->fanout > 1 ? m->fanout : 1;
10 uint32_t c = 2 * m->nodes;
11 if (c < 2) c = 2;
12 if (c > MESH_PROG_RING / 2) c = MESH_PROG_RING / 2;
13 return c;
14}
15
16void drv_lease_free(dlease *l) {
17 free(l->setup);
18 mj_ranges_free(&l->items);
19 memset(l, 0, sizeof *l);
20}
21
22/* An item's outcome to the jobs thread. */
23void drv_job_done(mesh_driver *d, uint64_t lease, uint64_t job, uint64_t item, uint32_t status, uint32_t result, const char *out) {
24 mesh_job_event e = mesh_job_event_new(MESH_JEV_DONE);
25 e.res.lease = lease;
26 e.res.job = job;
27 e.res.item = item;
28 e.res.status = status;
29 e.res.result = result;
30 e.res.out_len = strlen(out);
31 e.res.out = mesh_xmalloc(e.res.out_len ? e.res.out_len : 1);
32 memcpy(e.res.out, out, e.res.out_len);
33 if (mesh_chan_send(d->sh->jobs, &e) != MESH_CHAN_OK) mesh_job_event_free(&e);
34}
35
36/* Every item of a lease (at most a million per range, as the Rust) reported with status and why. */
37void drv_jobs_returned(mesh_driver *d, const dlease *l, uint32_t status, const char *why) {
38 for (size_t r = 0; r < l->items.n; r++) {
39 uint64_t a = l->items.v[r][0], b = l->items.v[r][1];
40 uint64_t end = b < a + 1000000 ? b : a + 1000000;
41 for (uint64_t item = a; item < end; item++) drv_job_done(d, l->lease, l->job, item, status, 0, why);
42 }
43}
44
45/* Slot s's next job items: its leases in order, each job's setup first (nothing of another job in flight),
46 * the item's 2^32-window base in the view's `job_base`, at most job_cap tickets in flight. */
47void drv_publish_jobs(mesh_driver *d, uint32_t s) {
48 mesh_mailbox mb = d->sh->mb;
49 dslot *x = d->slots[s];
50 uint32_t cap = drv_job_cap(&x->image.manifest, x->image.exec);
51 djobslot *j = &d->jobw[s];
52 dlease *failed = NULL;
53 char **why = NULL;
54 size_t nfailed = 0;
55 while (j->n) {
56 dlease *front = &j->q[0];
57 uint64_t item;
58 if (!mj_ranges_first(&front->items, &item)) {
59 drv_lease_free(front);
60 memmove(j->q, j->q + 1, (j->n - 1) * sizeof *j->q);
61 j->n--;
62 continue;
63 }
64 uint64_t job = front->job, window = item & ~0xffffffffull;
65 if (!j->has_ctx || j->ctx_job != job || j->ctx_window != window) {
66 /* Another job or window: what is in flight completes first. */
67 if (j->inflight > 0 || j->setup.busy) break;
68 if (!j->has_ctx || j->ctx_job != job) {
69 mesh_setup_step st = mesh_setup_state_step(&j->setup, job, front->setup, front->nsetup);
70 if (st.k == MESH_SETUP_WAIT) break;
71 if (st.k == MESH_SETUP_PUBLISH) {
72 /* One setup task at a time: the next is published when this one completes. */
73 mesh_kind k = mesh_kind_of(MESH_TK_JOB_SETUP);
74 k.job = job;
75 k.op = st.op;
76 k.arg = st.arg;
77 if (mesh_pring_can_publish(&x->ring) && mesh_pring_publish(&x->ring, mb, st.op, st.arg, k, NULL)) mesh_setup_state_published(&j->setup);
78 break;
79 }
80 if (st.k == MESH_SETUP_FAILED) {
81 /* The setup failed: so does every queued item of the job here (a try each). */
82 char *e = mesh_xstrdup(st.err);
83 size_t k = 0;
84 for (size_t i = 0; i < j->n; i++) {
85 if (j->q[i].job == job) {
86 failed = mesh_xrealloc(failed, (nfailed + 1) * sizeof *failed);
87 why = mesh_xrealloc(why, (nfailed + 1) * sizeof *why);
88 failed[nfailed] = j->q[i];
89 why[nfailed] = mesh_xstrdup(e);
90 nfailed++;
91 } else j->q[k++] = j->q[i];
92 }
93 j->n = k;
94 free(e);
95 mesh_setup_state_reset(&j->setup);
96 continue;
97 }
98 }
99 if (x->image.manifest.has_job_base) mesh_mailbox_set64(mb, (size_t)x->view_off + x->image.manifest.job_base, window);
100 j->has_ctx = true;
101 j->ctx_job = job;
102 j->ctx_window = window;
103 }
104 if (j->inflight >= cap || !mesh_pring_can_publish(&x->ring)) break;
105 mj_ranges_take(&front->items, item);
106 mesh_kind k = mesh_kind_of(MESH_TK_JOB);
107 k.lease = front->lease;
108 k.job = job;
109 k.item = item;
110 if (!mesh_pring_publish(&x->ring, mb, front->op, (uint32_t)item, k, NULL)) { mj_ranges_add(&front->items, item); break; }
111 j->inflight++;
112 }
113 for (size_t i = 0; i < nfailed; i++) {
114 drv_jobs_returned(d, &failed[i], MESH_JOBS_RS_FAIL, why[i]);
115 drv_lease_free(&failed[i]);
116 free(why[i]);
117 }
118 free(failed);
119 free(why);
120}