#include "debug.h"
#include "lightrec-private.h"
#include "memmanager.h"
+#include "reaper.h"
+#include "recompiler.h"
#include <stdbool.h>
#include <stdlib.h>
u32 offset = lut_offset(block->pc);
if (block->function) {
- memset(&state->code_lut[offset], 0,
- block->nb_ops * sizeof(*state->code_lut));
+ memset(lut_address(state, offset), 0,
+ block->nb_ops * lut_elm_size(state));
}
}
pr_err("Block at PC 0x%x is not in cache\n", block->pc);
}
-void lightrec_free_block_cache(struct blockcache *cache)
+static bool lightrec_block_is_old(const struct lightrec_state *state,
+ const struct block *block)
+{
+ u32 diff = state->current_cycle - block->precompile_date;
+
+ return diff > (1 << 27); /* About 4 seconds */
+}
+
+static void lightrec_free_blocks(struct blockcache *cache,
+ const struct block *except, bool all)
{
+ struct lightrec_state *state = cache->state;
struct block *block, *next;
+ bool outdated = all;
unsigned int i;
+ u8 old_flags;
for (i = 0; i < LUT_SIZE; i++) {
for (block = cache->lut[i]; block; block = next) {
next = block->next;
- lightrec_free_block(cache->state, block);
+
+ if (except && block == except)
+ continue;
+
+ if (!all) {
+ outdated = lightrec_block_is_old(state, block) ||
+ lightrec_block_is_outdated(state, block);
+ }
+
+ if (!outdated)
+ continue;
+
+ old_flags = block_set_flags(block, BLOCK_IS_DEAD);
+
+ if (!(old_flags & BLOCK_IS_DEAD)) {
+ if (ENABLE_THREADED_COMPILER)
+ lightrec_recompiler_remove(state->rec, block);
+
+ pr_debug("Freeing outdated block at PC 0x%08x\n", block->pc);
+ remove_from_code_lut(cache, block);
+ lightrec_unregister_block(cache, block);
+ lightrec_free_block(state, block);
+ }
}
}
+}
+
+void lightrec_remove_outdated_blocks(struct blockcache *cache,
+ const struct block *except)
+{
+ pr_info("Running out of code space. Cleaning block cache...\n");
+ lightrec_free_blocks(cache, except, false);
+}
+
+void lightrec_free_block_cache(struct blockcache *cache)
+{
+ lightrec_free_blocks(cache, NULL, true);
lightrec_free(cache->state, MEM_FOR_LIGHTREC, sizeof(*cache), cache);
}
return hash;
}
+static void lightrec_reset_lut_offset(struct lightrec_state *state, void *d)
+{
+ u32 pc = (u32)(uintptr_t) d;
+ struct block *block;
+ void *addr;
+
+ block = lightrec_find_block(state->block_cache, pc);
+ if (!block)
+ return;
+
+ if (block_has_flag(block, BLOCK_IS_DEAD))
+ return;
+
+ addr = block->function ?: state->get_next_block;
+ lut_write(state, lut_offset(pc), addr);
+}
+
bool lightrec_block_is_outdated(struct lightrec_state *state, struct block *block)
{
- void **lut_entry = &state->code_lut[lut_offset(block->pc)];
+ u32 offset = lut_offset(block->pc);
bool outdated;
- if (*lut_entry)
+ if (lut_read(state, offset))
return false;
outdated = block->hash != lightrec_calculate_block_hash(block);
if (likely(!outdated)) {
/* The block was marked as outdated, but the content is still
* the same */
- if (block->function)
- *lut_entry = block->function;
- else
- *lut_entry = state->get_next_block;
+
+ if (ENABLE_THREADED_COMPILER) {
+ /*
+ * When compiling a block that covers ours, the threaded
+ * compiler will set the LUT entries of the various
+ * entry points. Therefore we cannot write the LUT here,
+ * as we would risk overwriting the new entry points.
+ * Leave it to the reaper to re-install the LUT entries.
+ */
+
+ lightrec_reaper_add(state->reaper,
+ lightrec_reset_lut_offset,
+ (void *)(uintptr_t) block->pc);
+ } else if (block->function) {
+ lut_write(state, offset, block->function);
+ } else {
+ lut_write(state, offset, state->get_next_block);
+ }
}
return outdated;