simplify & consolidate ctrl layer process memory caching mechanisms - move all non-necessarily-synchronous caching to background memory streaming thread cache

This commit is contained in:
Ryan Fleury
2024-01-25 10:04:03 -08:00
parent b14b9f0344
commit 38b113f1d0
4 changed files with 123 additions and 253 deletions
+113 -190
View File
@@ -754,189 +754,12 @@ ctrl_process_read(CTRL_MachineID machine_id, CTRL_Handle process, Rng1U64 range,
return actual_bytes_read;
}
internal CTRL_ProcessMemorySlice
ctrl_query_cached_data_from_process_vaddr_range(Arena *arena, CTRL_MachineID machine_id, CTRL_Handle process, Rng1U64 range)
{
CTRL_ProcessMemorySlice result = {0};
if(range.max > range.min &&
dim_1u64(range) <= MB(256) &&
range.min <= 0x000FFFFFFFFFFFFFull &&
range.max <= 0x000FFFFFFFFFFFFFull)
{
HS_Scope *scope = hs_scope_open();
CTRL_ProcessMemoryCache *cache = &ctrl_state->process_memory_cache;
//- rjf: unpack address range
Rng1U64 page_range = r1u64(AlignDownPow2(range.min, KB(4)), AlignPow2(range.max, KB(4)));
//- rjf: setup output memory for read
void *read_out = push_array(arena, U8, dim_1u64(page_range));
//- rjf: unpack process/machine params
U64 hash = ctrl_hash_from_string(str8_struct(&process));
U64 slot_idx = hash%cache->slots_count;
U64 stripe_idx = slot_idx%cache->stripes_count;
CTRL_ProcessMemoryCacheSlot *slot = &cache->slots[slot_idx];
CTRL_ProcessMemoryCacheStripe *stripe = &cache->stripes[stripe_idx];
//- rjf: cache lookup & fill loop
for(U64 page_vaddr = page_range.min; page_vaddr < page_range.max; page_vaddr += KB(4))
{
// rjf: unpack page base address
U64 lvl5_idx = (page_vaddr&0x00000000000FF000ull) >> 12;
U64 lvl4_idx = (page_vaddr&0x000000000FF00000ull) >> 20;
U64 lvl3_idx = (page_vaddr&0x0000000FF0000000ull) >> 28;
U64 lvl2_idx = (page_vaddr&0x00000FF000000000ull) >> 36;
U64 lvl1_idx = (page_vaddr&0x000FF00000000000ull) >> 44;
// rjf: try to find node & read from it
B32 node_found = 0;
B32 page_found = 0;
B32 page_stale = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
CTRL_ProcessMemoryCacheNode *node = 0;
for(CTRL_ProcessMemoryCacheNode *n = slot->first; n != 0; n = n->next)
{
if(ctrl_handle_match(process, n->process) && n->machine_id == machine_id)
{
node = n;
break;
}
}
if(node != 0)
{
node_found = 1;
CTRL_ProcessMemoryCacheNode1 *node1 = node->children[lvl1_idx];
CTRL_ProcessMemoryCacheNode2 *node2 = node1 ? node1->children[lvl2_idx] : 0;
CTRL_ProcessMemoryCacheNode3 *node3 = node2 ? node2->children[lvl3_idx] : 0;
CTRL_ProcessMemoryCacheNode4 *node4 = node3 ? node3->children[lvl4_idx] : 0;
U128 page_hash = node4 ? node4->page_hashes[lvl5_idx] : u128_zero();
B32 stale = (node4 && node4->page_memgen_idxs[lvl5_idx] < ctrl_memgen_idx());
if(!u128_match(page_hash, u128_zero()))
{
page_stale = stale;
String8 page_data = hs_data_from_hash(scope, page_hash);
if(page_data.size >= KB(4))
{
page_found = 1;
MemoryCopy((U8*)read_out + (page_vaddr-page_range.min), page_data.str, KB(4));
}
else
{
page_stale = 1;
}
}
}
}
// rjf: either node or page not found? -> need a hard-lock & fill
if(!node_found || !page_found || page_stale) OS_MutexScopeW(stripe->rw_mutex)
{
CTRL_ProcessMemoryCacheNode *node = 0;
for(CTRL_ProcessMemoryCacheNode *n = slot->first; n != 0; n = n->next)
{
if(ctrl_handle_match(process, n->process) && n->machine_id == machine_id)
{
node = n;
break;
}
}
if(node == 0)
{
Arena *node_arena = arena_alloc();
node = push_array(node_arena, CTRL_ProcessMemoryCacheNode, 1);
node->arena = node_arena;
node->machine_id = machine_id;
node->process = process;
node->range_hash_slots_count = 1024;
node->range_hash_slots = push_array(node_arena, CTRL_ProcessMemoryRangeHashSlot, node->range_hash_slots_count);
DLLPushBack(slot->first, slot->last, node);
}
if(!page_found || page_stale)
{
CTRL_ProcessMemoryCacheNode1 *node1 = node->children[lvl1_idx];
if(node1 == 0)
{
node1 = push_array(node->arena, CTRL_ProcessMemoryCacheNode1, 1);
node->children[lvl1_idx] = node1;
}
CTRL_ProcessMemoryCacheNode2 *node2 = node1->children[lvl2_idx];
if(node2 == 0)
{
node2 = push_array(node->arena, CTRL_ProcessMemoryCacheNode2, 1);
node1->children[lvl2_idx] = node2;
}
CTRL_ProcessMemoryCacheNode3 *node3 = node2->children[lvl3_idx];
if(node3 == 0)
{
node3 = push_array(node->arena, CTRL_ProcessMemoryCacheNode3, 1);
node2->children[lvl3_idx] = node3;
}
CTRL_ProcessMemoryCacheNode4 *node4 = node3->children[lvl4_idx];
if(node4 == 0)
{
node4 = push_array(node->arena, CTRL_ProcessMemoryCacheNode4, 1);
node3->children[lvl4_idx] = node4;
}
Arena *page_arena = arena_alloc__sized(KB(8), KB(8));
void *page_base = push_array_no_zero(page_arena, U8, KB(4));
U64 actual_read_size = ctrl_process_read(machine_id, process, r1u64(page_vaddr, page_vaddr+KB(4)), page_base);
if(actual_read_size >= KB(4))
{
node4->page_memgen_idxs[lvl5_idx] = ctrl_memgen_idx();
if(page_stale)
{
MemoryCopy((U8*)read_out + (page_vaddr-page_range.min), page_base, KB(4));
}
U64 page_key_data[] =
{
(U64)machine_id,
(U64)process.u64[0],
page_vaddr,
page_vaddr+KB(4),
};
U128 page_key = hs_hash_from_data(str8((U8 *)page_key_data, sizeof(page_key_data)));
U128 page_hash = hs_submit_data(page_key, &page_arena, str8((U8 *)page_base, KB(4)));
node4->page_hashes[lvl5_idx] = page_hash;
}
else
{
arena_release(page_arena);
}
}
}
}
//- rjf: fill result by skipping/chopping read memory
U64 byte_in_page_idx = (range.min&0x0000000000000FFFull) >> 0;
result.data.str = (U8*)read_out + byte_in_page_idx;
result.data.size = dim_1u64(range);
hs_scope_close(scope);
}
return result;
}
internal CTRL_ProcessMemorySlice
ctrl_query_cached_zero_terminated_data_from_process_vaddr_limit(Arena *arena, CTRL_MachineID machine_id, CTRL_Handle process, U64 vaddr, U64 limit, U64 endt_us)
{
CTRL_ProcessMemorySlice result = ctrl_query_cached_data_from_process_vaddr_range(arena, machine_id, process, r1u64(vaddr, vaddr+limit));
for(U64 idx = 0; idx < result.data.size; idx += 1)
{
if(result.data.str[idx] == 0)
{
result.data.size = idx;
break;
}
}
return result;
}
internal B32
ctrl_process_write_data(CTRL_MachineID machine_id, CTRL_Handle process, U64 vaddr, String8 data)
ctrl_process_write(CTRL_MachineID machine_id, CTRL_Handle process, Rng1U64 range, void *src)
{
ProfBeginFunction();
B32 result = demon_write_memory(ctrl_demon_handle_from_ctrl(process), vaddr, data.str, data.size);
U64 size = dim_1u64(range);
B32 result = demon_write_memory(ctrl_demon_handle_from_ctrl(process), range.min, src, size);
if(result)
{
ins_atomic_u64_inc_eval(&ctrl_state->memgen_idx);
@@ -945,6 +768,23 @@ ctrl_process_write_data(CTRL_MachineID machine_id, CTRL_Handle process, U64 vadd
return result;
}
//- rjf: process memory cache interaction
internal U128
ctrl_hash_store_key_from_process_vaddr_range(CTRL_MachineID machine_id, CTRL_Handle process, Rng1U64 range, B32 zero_terminated)
{
U64 key_hash_data[] =
{
(U64)machine_id,
(U64)process.u64[0],
range.min,
range.max,
(U64)zero_terminated,
};
U128 key = hs_hash_from_data(str8((U8*)key_hash_data, sizeof(key_hash_data)));
return key;
}
internal U128
ctrl_stored_hash_from_process_vaddr_range(CTRL_MachineID machine_id, CTRL_Handle process, Rng1U64 range, B32 zero_terminated)
{
@@ -1056,6 +896,98 @@ ctrl_stored_hash_from_process_vaddr_range(CTRL_MachineID machine_id, CTRL_Handle
return result;
}
//- rjf: process memory cache reading helpers
internal CTRL_ProcessMemorySlice
ctrl_query_cached_data_from_process_vaddr_range(Arena *arena, CTRL_MachineID machine_id, CTRL_Handle process, Rng1U64 range)
{
CTRL_ProcessMemorySlice result = {0};
if(range.max > range.min &&
dim_1u64(range) <= MB(256) &&
range.min <= 0x000FFFFFFFFFFFFFull &&
range.max <= 0x000FFFFFFFFFFFFFull)
{
Temp scratch = scratch_begin(&arena, 1);
HS_Scope *scope = hs_scope_open();
CTRL_ProcessMemoryCache *cache = &ctrl_state->process_memory_cache;
//- rjf: unpack address range, prepare per-touched-page info
U64 page_size = KB(4);
Rng1U64 page_range = r1u64(AlignDownPow2(range.min, page_size), AlignPow2(range.max, page_size));
U64 page_count = dim_1u64(page_range)/page_size;
U128 *page_hashes = push_array(scratch.arena, U128, page_count);
U128 *page_last_hashes = push_array(scratch.arena, U128, page_count);
//- rjf: gather hashes for each page
for(U64 page_idx = 0; page_idx < page_count; page_idx += 1)
{
U64 page_base_vaddr = page_range.min + page_idx*page_size;
U128 page_hash = ctrl_stored_hash_from_process_vaddr_range(machine_id, process, r1u64(page_base_vaddr, page_base_vaddr+page_size), 0);
page_hashes[page_idx] = page_hash;
}
//- rjf: gather last hashes for each page
for(U64 page_idx = 0; page_idx < page_count; page_idx += 1)
{
U64 page_base_vaddr = page_range.min + page_idx*page_size;
U128 page_key = ctrl_hash_store_key_from_process_vaddr_range(machine_id, process, r1u64(page_base_vaddr, page_base_vaddr+page_size), 0);
U128 page_last_hash = hs_hash_from_key(page_key, 1);
page_last_hashes[page_idx] = page_last_hash;
}
//- rjf: setup output buffer
void *read_out = push_array(arena, U8, dim_1u64(range));
//- rjf: iterate pages, fill output
{
U64 write_off = 0;
for(U64 page_idx = 0; page_idx < page_count; page_idx += 1)
{
String8 data = hs_data_from_hash(scope, page_hashes[page_idx]);
Rng1U64 data_vaddr_range = r1u64(page_range.min + page_idx*page_size, page_range.min + page_idx*page_size+data.size);
String8 in_range_data = data;
if(page_idx == page_count-1 && data_vaddr_range.max > range.max)
{
in_range_data = str8_chop(in_range_data, data_vaddr_range.max-range.max);
}
if(page_idx == 0 && range.min > data_vaddr_range.min)
{
in_range_data = str8_skip(in_range_data, range.min-data_vaddr_range.min);
}
MemoryCopy((U8*)read_out+write_off, in_range_data.str, in_range_data.size);
write_off += in_range_data.size;
if(data.size < page_size)
{
write_off += page_size-data.size;
}
}
}
//- rjf: fill result
result.data.str = (U8*)read_out;
result.data.size = dim_1u64(range);
hs_scope_close(scope);
scratch_end(scratch);
}
return result;
}
internal CTRL_ProcessMemorySlice
ctrl_query_cached_zero_terminated_data_from_process_vaddr_limit(Arena *arena, CTRL_MachineID machine_id, CTRL_Handle process, U64 vaddr, U64 limit, U64 endt_us)
{
CTRL_ProcessMemorySlice result = ctrl_query_cached_data_from_process_vaddr_range(arena, machine_id, process, r1u64(vaddr, vaddr+limit));
for(U64 idx = 0; idx < result.data.size; idx += 1)
{
if(result.data.str[idx] == 0)
{
result.data.size = idx;
break;
}
}
return result;
}
//- rjf: register reading/writing
internal void *
@@ -3175,6 +3107,7 @@ ctrl_mem_stream_thread__entry_point(void *p)
Rng1U64 vaddr_range = {0};
B32 zero_terminated = 0;
ctrl_u2ms_dequeue_req(&machine_id, &process, &vaddr_range, &zero_terminated);
U128 key = ctrl_hash_store_key_from_process_vaddr_range(machine_id, process, vaddr_range, zero_terminated);
//- rjf: unpack process memory cache key
U64 process_hash = ctrl_hash_from_string(str8_struct(&process));
@@ -3247,16 +3180,6 @@ ctrl_mem_stream_thread__entry_point(void *p)
}
}
//- rjf: determine key for this region
U64 key_hash_data[] =
{
(U64)machine_id,
(U64)process.u64[0],
vaddr_range.min,
vaddr_range.min + zero_terminated_size,
};
U128 key = hs_hash_from_data(str8((U8 *)key_hash_data, sizeof(key_hash_data)));
//- rjf: read successful -> submit to hash store
U128 hash = {0};
if(got_task && range_base != 0)