Stage M-0: kernel-Hermes arena, pending queues and member lists grow on demand; Q_SLOT its own constant (FABRIC-3.9.0 §7.0, M-0/M-0b)
- The static sk_hermes_msgs[32] is replaced by a directory of fixed-size segments (SK_HERMES_ARENA_SEGMENT, growth granularity only). Messages never move -- callers, including the drain across vm_interpret_kind(), hold message pointers across later allocations, so copy-on-grow (as §7.0 first proposed) would dangle them. Each message carries its index; numbering unchanged. - Pending-queue rings and channel member lists heap-grown (doubling); member lists freed on destroy; reassembly scratch sized per call. SK_HERMES_MSG_MAX / _PENDING_MAX / _MEMBER_MAX are gone. - Q_SLOT = Q48_ONE / 32 as its own constant (M-0b), value unchanged. - Every grow failure is printed, logged at LOG_ERROR, counted (sk_hermes_grow_failures()) and refused whole; a send whose queue can't grow releases its message. Probe (reverted), identical on amd64/aarch64/riscv64: 70 messages in one queue (arena 32 -> 96), sender refused by its reservoir, 72 popped intact in order, 100 members; a live Hera drain on a full arena grew it mid-drain and released/popped the right message; each grow path's injected failure refused whole and counted. 3-ISA acceptance passes; boot parity, dict_hash set and the live ledger line byte-identical to before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
316be9fc4b
commit
d8358a3011
@@ -883,9 +883,14 @@ static void kernel_main_deep(BootInfo *boot_info) {
|
||||
uint64_t reservoir0 = stadium_reservoir_peek(alloc_test_id);
|
||||
uint64_t expected_n = reservoir0 / SK_HERMES_Q_SLOT;
|
||||
uint64_t got_n = 0;
|
||||
SkHermesMessage *msgs[SK_HERMES_MSG_MAX];
|
||||
/* Stage M-0: the arena has no fixed size any more, so this list
|
||||
* is sized from the reservoir (expected_n) plus one, and every
|
||||
* loop below is bounded by msgs_cap -- the refusal past
|
||||
* expected_n must now come from the reservoir, not the arena. */
|
||||
uint64_t msgs_cap = expected_n + 1;
|
||||
SkHermesMessage **msgs = (SkHermesMessage **)kmalloc((size_t)msgs_cap * sizeof(SkHermesMessage *));
|
||||
SkHermesMessage *msg;
|
||||
int ok = 1;
|
||||
int ok = msgs ? 1 : 0;
|
||||
size_t i;
|
||||
uint64_t held0, pulled0, returned0, consumed0;
|
||||
uint64_t held1, pulled1, returned1, consumed1;
|
||||
@@ -896,7 +901,7 @@ static void kernel_main_deep(BootInfo *boot_info) {
|
||||
* only thing that has ever called these functions. */
|
||||
sk_hermes_ledger(&held0, &pulled0, &returned0, &consumed0);
|
||||
|
||||
while (got_n < SK_HERMES_MSG_MAX && sk_hermes_alloc(alloc_test_id, &msg) == 0) {
|
||||
while (msgs && got_n < msgs_cap && sk_hermes_alloc(alloc_test_id, &msg) == 0) {
|
||||
msgs[got_n] = msg;
|
||||
got_n++;
|
||||
}
|
||||
@@ -958,7 +963,7 @@ static void kernel_main_deep(BootInfo *boot_info) {
|
||||
uint64_t held3, pulled3, returned3, consumed3;
|
||||
uint64_t held4, pulled4, returned4, consumed4;
|
||||
uint64_t n2 = 0;
|
||||
while (n2 < SK_HERMES_MSG_MAX && sk_hermes_alloc(alloc_test_id, &msg) == 0) {
|
||||
while (msgs && n2 < msgs_cap && sk_hermes_alloc(alloc_test_id, &msg) == 0) {
|
||||
msgs[n2] = msg;
|
||||
n2++;
|
||||
}
|
||||
@@ -998,7 +1003,7 @@ static void kernel_main_deep(BootInfo *boot_info) {
|
||||
uint64_t expected_b = stadium_reservoir_peek(alloc_test_id) / SK_HERMES_Q_SLOT;
|
||||
int sb = 1;
|
||||
if (!stadium_conserved(alloc_test_id)) sb = 0; /* before */
|
||||
while (nb < SK_HERMES_MSG_MAX && sk_hermes_alloc(alloc_test_id, &msg) == 0) {
|
||||
while (msgs && nb < msgs_cap && sk_hermes_alloc(alloc_test_id, &msg) == 0) {
|
||||
msgs[nb++] = msg;
|
||||
}
|
||||
if (nb != expected_b || nb == 0) sb = 0;
|
||||
@@ -1025,7 +1030,7 @@ static void kernel_main_deep(BootInfo *boot_info) {
|
||||
int sc = 1;
|
||||
uint64_t k = 0;
|
||||
if (!sk_hermes_scan_check() || sk_hermes_scan_held(&live) != 0 || live != 0) sc = 0;
|
||||
while (k < 4 && sk_hermes_alloc(alloc_test_id, &msg) == 0) msgs[k++] = msg;
|
||||
while (msgs && k < 4 && k < msgs_cap && sk_hermes_alloc(alloc_test_id, &msg) == 0) msgs[k++] = msg;
|
||||
if (k != 4) sc = 0;
|
||||
s1 = sk_hermes_scan_held(&live);
|
||||
sk_hermes_ledger(&h, &p_, &r, &c);
|
||||
@@ -1073,6 +1078,9 @@ static void kernel_main_deep(BootInfo *boot_info) {
|
||||
print_uint(" pulled(final)=", pulled2);
|
||||
print_uint(" returned(final)=", returned2);
|
||||
print_uint(" consumed(final)=", consumed2);
|
||||
print_uint(" arena_capacity=", (uint64_t)sk_hermes_arena_capacity());
|
||||
print_uint(" grow_failures=", sk_hermes_grow_failures());
|
||||
if (msgs) kfree(msgs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
#include "starkernel/q48_16.h"
|
||||
#include "starkernel/kmalloc.h"
|
||||
#include "console.h"
|
||||
#include "log.h" /* Stage M-0: grow failures are logged at LOG_ERROR */
|
||||
#include "vm.h" /* task 3.4 -- VM struct fields, vm_interpret() */
|
||||
#include "starkernel/capsule_vm_switch_signal.h" /* FABRIC-3.8.md §3.13.3 -- wait stamps */
|
||||
|
||||
@@ -72,7 +73,60 @@ static void console_put_u64(uint64_t v) {
|
||||
console_puts(&buf[i]);
|
||||
}
|
||||
|
||||
static SkHermesMessage sk_hermes_msgs[SK_HERMES_MSG_MAX];
|
||||
/* FABRIC-3.9.0.md §7.0 (Stage M-0): the message arena grows on demand (was
|
||||
* a static sk_hermes_msgs[32]). A directory of SK_HERMES_ARENA_SEGMENT-sized
|
||||
* segments: messages never move once allocated (see kernel_hermes.h), only
|
||||
* the directory is allocate-copy-free. A message's identity is its arena
|
||||
* index, lowest free first -- the same numbering as the fixed array. */
|
||||
static SkHermesMessage **sk_hermes_segs = (SkHermesMessage **)0;
|
||||
static size_t sk_hermes_nsegs = 0;
|
||||
static size_t sk_hermes_segs_cap = 0;
|
||||
static uint64_t sk_hermes_grow_fail = 0;
|
||||
|
||||
uint64_t sk_hermes_grow_failures(void) { return sk_hermes_grow_fail; }
|
||||
|
||||
size_t sk_hermes_arena_capacity(void) { return sk_hermes_nsegs * SK_HERMES_ARENA_SEGMENT; }
|
||||
|
||||
/* Every grow failure: logged (persisted), printed in full, counted. The
|
||||
* caller then refuses whole. */
|
||||
static void sk_hermes_grow_failed(const char *what) {
|
||||
sk_hermes_grow_fail++;
|
||||
console_puts("Kernel-Hermes: can't grow ");
|
||||
console_println(what);
|
||||
log_message(LOG_ERROR, "Kernel-Hermes: can't grow %s", what);
|
||||
}
|
||||
|
||||
static SkHermesMessage *msg_at(size_t idx) {
|
||||
return &sk_hermes_segs[idx / SK_HERMES_ARENA_SEGMENT][idx % SK_HERMES_ARENA_SEGMENT];
|
||||
}
|
||||
|
||||
/* Add one segment (and grow the directory first if it's full). */
|
||||
static int sk_hermes_arena_grow(void) {
|
||||
SkHermesMessage *seg;
|
||||
|
||||
if (sk_hermes_nsegs == sk_hermes_segs_cap) {
|
||||
size_t ncap = sk_hermes_segs_cap ? sk_hermes_segs_cap * 2 : 4;
|
||||
SkHermesMessage **dir = (SkHermesMessage **)kmalloc(ncap * sizeof(SkHermesMessage *));
|
||||
size_t i;
|
||||
if (!dir) {
|
||||
sk_hermes_grow_failed("message arena directory");
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < sk_hermes_nsegs; i++) dir[i] = sk_hermes_segs[i];
|
||||
if (sk_hermes_segs) kfree(sk_hermes_segs);
|
||||
sk_hermes_segs = dir;
|
||||
sk_hermes_segs_cap = ncap;
|
||||
}
|
||||
|
||||
seg = (SkHermesMessage *)kmalloc(SK_HERMES_ARENA_SEGMENT * sizeof(SkHermesMessage));
|
||||
if (!seg) {
|
||||
sk_hermes_grow_failed("message arena");
|
||||
return -1;
|
||||
}
|
||||
memset(seg, 0, SK_HERMES_ARENA_SEGMENT * sizeof(SkHermesMessage));
|
||||
sk_hermes_segs[sk_hermes_nsegs++] = seg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Task 2.4 (item 28), FABRIC-3.5.md SXXXVII.3/SXL.4's four counters.
|
||||
* `consumed` is declared now but touched nowhere yet -- task 2.5 adds
|
||||
@@ -113,11 +167,12 @@ uint64_t sk_hermes_audit_failure_count(void) {
|
||||
uint64_t sk_hermes_scan_held(size_t *live_count) {
|
||||
uint64_t sum = 0;
|
||||
size_t n = 0;
|
||||
int i;
|
||||
size_t i, len = sk_hermes_arena_capacity();
|
||||
|
||||
for (i = 0; i < SK_HERMES_MSG_MAX; i++) {
|
||||
if (!sk_hermes_msgs[i].in_use || sk_hermes_msgs[i].stadium_cell < 0) continue;
|
||||
sum += stadium_cells()[sk_hermes_msgs[i].stadium_cell].header.heat;
|
||||
for (i = 0; i < len; i++) {
|
||||
SkHermesMessage *m = msg_at(i);
|
||||
if (!m->in_use || m->stadium_cell < 0) continue;
|
||||
sum += stadium_cells()[m->stadium_cell].header.heat;
|
||||
n++;
|
||||
}
|
||||
if (live_count) *live_count = n;
|
||||
@@ -128,16 +183,20 @@ int sk_hermes_scan_check(void) {
|
||||
return sk_hermes_scan_held((size_t *)0) == sk_hermes_held;
|
||||
}
|
||||
|
||||
static int sk_hermes_find_free_slot(void) {
|
||||
int i;
|
||||
for (i = 0; i < SK_HERMES_MSG_MAX; i++) {
|
||||
if (!sk_hermes_msgs[i].in_use) return i;
|
||||
/* Lowest free index; grows the arena by a segment when every slot is in
|
||||
* use. -1 only if that grow fails (logged, counted). */
|
||||
static long sk_hermes_find_free_slot(void) {
|
||||
size_t i, len = sk_hermes_arena_capacity();
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!msg_at(i)->in_use) return (long)i;
|
||||
}
|
||||
return -1;
|
||||
if (sk_hermes_arena_grow() != 0) return -1;
|
||||
return (long)len; /* first slot of the new segment */
|
||||
}
|
||||
|
||||
int sk_hermes_alloc(VMUuid vm_id, SkHermesMessage **out_msg) {
|
||||
int slot;
|
||||
long slot;
|
||||
SkHermesMessage *m;
|
||||
uint64_t pulled;
|
||||
StadiumPatronHeader candidate;
|
||||
size_t cell;
|
||||
@@ -150,7 +209,7 @@ int sk_hermes_alloc(VMUuid vm_id, SkHermesMessage **out_msg) {
|
||||
if (stadium_reservoir_peek(vm_id) < SK_HERMES_Q_SLOT) return -1;
|
||||
|
||||
slot = sk_hermes_find_free_slot();
|
||||
if (slot < 0) return -1; /* arena full, reservoir untouched */
|
||||
if (slot < 0) return -1; /* arena couldn't grow (logged, counted), reservoir untouched */
|
||||
|
||||
pulled = stadium_reservoir_pull(vm_id, SK_HERMES_Q_SLOT);
|
||||
if (pulled < SK_HERMES_Q_SLOT) {
|
||||
@@ -174,7 +233,7 @@ int sk_hermes_alloc(VMUuid vm_id, SkHermesMessage **out_msg) {
|
||||
candidate.ttl = 0; /* decay/TTL is task 2.5's scope, not this one's */
|
||||
candidate.link = 0;
|
||||
candidate.contains = STADIUM_CONTAINS_NONE;
|
||||
candidate.mass = 1; /* message body lives in sk_hermes_msgs[], not here */
|
||||
candidate.mass = 1; /* message body lives in the arena, not here */
|
||||
candidate.flags = 0;
|
||||
candidate.behaviour = (uint8_t)STADIUM_BEHAVIOUR_DELIVER;
|
||||
|
||||
@@ -188,11 +247,13 @@ int sk_hermes_alloc(VMUuid vm_id, SkHermesMessage **out_msg) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&sk_hermes_msgs[slot], 0, sizeof(SkHermesMessage));
|
||||
sk_hermes_msgs[slot].in_use = 1;
|
||||
sk_hermes_msgs[slot].stadium_cell = (int32_t)cell;
|
||||
sk_hermes_msgs[slot].owner = vm_id;
|
||||
*out_msg = &sk_hermes_msgs[slot];
|
||||
m = msg_at((size_t)slot);
|
||||
memset(m, 0, sizeof(SkHermesMessage));
|
||||
m->in_use = 1;
|
||||
m->stadium_cell = (int32_t)cell;
|
||||
m->owner = vm_id;
|
||||
m->index = (uint32_t)slot;
|
||||
*out_msg = m;
|
||||
|
||||
/* Task 2.4: the one site where held/pulled increment -- only reached
|
||||
* once every refusal path above has already returned. */
|
||||
@@ -336,6 +397,11 @@ int sk_hermes_channel_destroy(int channel_id) {
|
||||
if (channel_id == SK_HERMES_CHANNEL_COMMON) return -1; /* permanent, SXLV.1 */
|
||||
if (!channel_valid(channel_id)) return -1;
|
||||
|
||||
/* Stage M-0: the member list is heap-grown -- free it, or create()'s
|
||||
* memset of this slot would leak it. */
|
||||
if (sk_hermes_channels[channel_id].membership.members) {
|
||||
kfree(sk_hermes_channels[channel_id].membership.members);
|
||||
}
|
||||
memset(&sk_hermes_channels[channel_id], 0, sizeof(SkHermesChannel));
|
||||
return 0;
|
||||
}
|
||||
@@ -350,7 +416,20 @@ int sk_hermes_channel_subscribe(int channel_id, VMUuid vm_id) {
|
||||
for (i = 0; i < m->count; i++) {
|
||||
if (vm_uuid_equal(m->members[i], vm_id)) return -1; /* already a member */
|
||||
}
|
||||
if (m->count >= SK_HERMES_MEMBER_MAX) return -1;
|
||||
if (m->count == m->cap) {
|
||||
/* Stage M-0: grow the member list (was a fixed [64]) -- doubling,
|
||||
* allocate-copy-free, all-or-nothing. */
|
||||
size_t ncap = m->cap ? m->cap * 2 : 8;
|
||||
VMUuid *grown = (VMUuid *)kmalloc(ncap * sizeof(VMUuid));
|
||||
if (!grown) {
|
||||
sk_hermes_grow_failed("channel member list");
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < m->count; i++) grown[i] = m->members[i];
|
||||
if (m->members) kfree(m->members);
|
||||
m->members = grown;
|
||||
m->cap = ncap;
|
||||
}
|
||||
|
||||
m->members[m->count] = vm_id;
|
||||
m->count++;
|
||||
@@ -396,17 +475,21 @@ int sk_hermes_channel_member_count(int channel_id) {
|
||||
}
|
||||
|
||||
/* Task 3.3: per-subscriber pending queue table. See kernel_hermes.h's own
|
||||
* doc comments on sk_hermes_queues_boot_init()/SK_HERMES_PENDING_MAX for
|
||||
* scope and sizing reasoning. A queue is found-or-created lazily by
|
||||
* doc comment on sk_hermes_queues_boot_init() for scope and sizing
|
||||
* reasoning (each queue's depth grows on demand since Stage M-0; the table
|
||||
* of queues is still RAM-sized at boot -- S-3's per-VM-tables item). A
|
||||
* queue is found-or-created lazily by
|
||||
* vm_id, same shape as stadium.c's quota_slot_for_vm() and session.c's
|
||||
* session_find() -- not pre-populated at birth like channel membership
|
||||
* is, since not every VM ever receives a message. */
|
||||
typedef struct {
|
||||
VMUuid vm_id;
|
||||
int in_use;
|
||||
int slots[SK_HERMES_PENDING_MAX]; /* sk_hermes_msgs[] indices, circular FIFO */
|
||||
size_t head;
|
||||
size_t count;
|
||||
VMUuid vm_id;
|
||||
int in_use;
|
||||
uint32_t *slots; /* arena indices, circular FIFO; Stage M-0: heap-grown
|
||||
* on push (was a fixed [32]) */
|
||||
size_t cap;
|
||||
size_t head;
|
||||
size_t count;
|
||||
} SkHermesPendingQueue;
|
||||
|
||||
static SkHermesPendingQueue *sk_hermes_queues = (SkHermesPendingQueue *)0;
|
||||
@@ -460,7 +543,15 @@ static SkHermesPendingQueue *find_or_create_queue(VMUuid vm_id) {
|
||||
}
|
||||
if (free_slot < 0) return (SkHermesPendingQueue *)0; /* table full */
|
||||
|
||||
memset(&sk_hermes_queues[free_slot], 0, sizeof(SkHermesPendingQueue));
|
||||
{
|
||||
/* Stage M-0: a reused table entry keeps its grown ring buffer
|
||||
* (empty, count 0) rather than the memset leaking it. */
|
||||
uint32_t *keep = sk_hermes_queues[free_slot].slots;
|
||||
size_t keep_cap = sk_hermes_queues[free_slot].cap;
|
||||
memset(&sk_hermes_queues[free_slot], 0, sizeof(SkHermesPendingQueue));
|
||||
sk_hermes_queues[free_slot].slots = keep;
|
||||
sk_hermes_queues[free_slot].cap = keep_cap;
|
||||
}
|
||||
sk_hermes_queues[free_slot].vm_id = vm_id;
|
||||
sk_hermes_queues[free_slot].in_use = 1;
|
||||
return &sk_hermes_queues[free_slot];
|
||||
@@ -473,10 +564,25 @@ static SkHermesPendingQueue *find_or_create_queue(VMUuid vm_id) {
|
||||
* nothing is pending anywhere, the overwhelmingly common case. */
|
||||
static int sk_hermes_pending_total = 0;
|
||||
|
||||
static int queue_push(SkHermesPendingQueue *q, int msg_index) {
|
||||
static int queue_push(SkHermesPendingQueue *q, uint32_t msg_index) {
|
||||
size_t tail;
|
||||
if (q->count >= SK_HERMES_PENDING_MAX) return -1;
|
||||
tail = (q->head + q->count) % SK_HERMES_PENDING_MAX;
|
||||
if (q->count == q->cap) {
|
||||
/* Stage M-0: grow the ring (doubling), unwrapped into FIFO order
|
||||
* with head = 0. All-or-nothing: the old ring stays if this fails. */
|
||||
size_t ncap = q->cap ? q->cap * 2 : 8;
|
||||
uint32_t *grown = (uint32_t *)kmalloc(ncap * sizeof(uint32_t));
|
||||
size_t i;
|
||||
if (!grown) {
|
||||
sk_hermes_grow_failed("pending queue");
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < q->count; i++) grown[i] = q->slots[(q->head + i) % q->cap];
|
||||
if (q->slots) kfree(q->slots);
|
||||
q->slots = grown;
|
||||
q->cap = ncap;
|
||||
q->head = 0;
|
||||
}
|
||||
tail = (q->head + q->count) % q->cap;
|
||||
q->slots[tail] = msg_index;
|
||||
q->count++;
|
||||
sk_hermes_pending_total++;
|
||||
@@ -487,7 +593,7 @@ int sk_hermes_send_one(VMUuid from, VMUuid to, uint32_t type, uint32_t channel_i
|
||||
void *payload_addr, uint32_t payload_len) {
|
||||
SkHermesMessage *msg;
|
||||
SkHermesPendingQueue *q;
|
||||
int idx;
|
||||
uint32_t idx;
|
||||
|
||||
/* Task 3.5: the one-block bound, enforced uniformly here whether or
|
||||
* not payload_addr happens to be a chunk carrier -- see
|
||||
@@ -526,9 +632,9 @@ int sk_hermes_send_one(VMUuid from, VMUuid to, uint32_t type, uint32_t channel_i
|
||||
return -1;
|
||||
}
|
||||
|
||||
idx = (int)(msg - sk_hermes_msgs);
|
||||
idx = msg->index;
|
||||
if (queue_push(q, idx) != 0) {
|
||||
sk_hermes_release(msg); /* roll back -- destination queue full */
|
||||
sk_hermes_release(msg); /* roll back -- queue couldn't grow (logged, counted) */
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -573,13 +679,13 @@ int sk_hermes_pending_count(VMUuid vm_id) {
|
||||
SkHermesMessage *sk_hermes_pending_peek(VMUuid vm_id) {
|
||||
SkHermesPendingQueue *q = find_queue(vm_id);
|
||||
if (!q || q->count == 0) return (SkHermesMessage *)0;
|
||||
return &sk_hermes_msgs[q->slots[q->head]];
|
||||
return msg_at(q->slots[q->head]);
|
||||
}
|
||||
|
||||
int sk_hermes_pending_pop(VMUuid vm_id) {
|
||||
SkHermesPendingQueue *q = find_queue(vm_id);
|
||||
if (!q || q->count == 0) return -1;
|
||||
q->head = (q->head + 1) % SK_HERMES_PENDING_MAX;
|
||||
q->head = (q->head + 1) % q->cap;
|
||||
q->count--;
|
||||
sk_hermes_pending_total--;
|
||||
return 0;
|
||||
@@ -692,18 +798,40 @@ uint32_t sk_hermes_chunk_count(uint32_t len) {
|
||||
return (len + SK_HERMES_CHUNK_MAX_SLICE - 1) / SK_HERMES_CHUNK_MAX_SLICE;
|
||||
}
|
||||
|
||||
static int reassemble_with(SkHermesMessage **chunks, int n_chunks,
|
||||
uint8_t *out_buf, uint32_t out_buf_cap,
|
||||
uint32_t *out_len, int *present);
|
||||
|
||||
/* Stage M-0: present[] is sized to n_chunks per call (was a fixed
|
||||
* [SK_HERMES_MSG_MAX], which also capped n_chunks at 32). One exit path so
|
||||
* the scratch is always freed; the validation itself is unchanged, in
|
||||
* reassemble_with() below. */
|
||||
int sk_hermes_reassemble(SkHermesMessage **chunks, int n_chunks,
|
||||
uint8_t *out_buf, uint32_t out_buf_cap,
|
||||
uint32_t *out_len) {
|
||||
int present[SK_HERMES_MSG_MAX];
|
||||
int *present;
|
||||
int rc;
|
||||
|
||||
if (!chunks || n_chunks <= 0 || !out_buf || !out_len) return -1;
|
||||
|
||||
present = (int *)kmalloc((size_t)n_chunks * sizeof(int));
|
||||
if (!present) {
|
||||
sk_hermes_grow_failed("reassembly scratch");
|
||||
return -1;
|
||||
}
|
||||
rc = reassemble_with(chunks, n_chunks, out_buf, out_buf_cap, out_len, present);
|
||||
kfree(present);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int reassemble_with(SkHermesMessage **chunks, int n_chunks,
|
||||
uint8_t *out_buf, uint32_t out_buf_cap,
|
||||
uint32_t *out_len, int *present) {
|
||||
int i;
|
||||
uint32_t msg_id;
|
||||
uint32_t total;
|
||||
uint32_t last_slice_len = 0;
|
||||
|
||||
if (!chunks || n_chunks <= 0 || !out_buf || !out_len) return -1;
|
||||
if (n_chunks > SK_HERMES_MSG_MAX) return -1; /* guard before indexing present[] */
|
||||
|
||||
for (i = 0; i < n_chunks; i++) present[i] = 0;
|
||||
|
||||
if (!chunks[0] || !chunks[0]->payload_addr ||
|
||||
|
||||
Reference in New Issue
Block a user