@@ -49,6 +49,7 @@
#include <linux/fsnotify.h>
#include <linux/irq_work.h>
#include <linux/workqueue.h>
+#include <linux/sort.h>
#include <asm/setup.h> /* COMMAND_LINE_SIZE and kaslr_offset() */
@@ -6001,12 +6002,73 @@ struct trace_mod_entry {
struct trace_scratch {
unsigned long kaslr_addr;
- unsigned long nr_entries;
+ unsigned int nr_entries;
+ int oldest_removed;
struct trace_mod_entry entries[];
};
static DEFINE_MUTEX(scratch_mutex);
+/* Encode LRU list at the lowest 8bits and use 9th bit as removed flag. */
+#define MOD_INDEX_MASK GENMASK(7, 0)
+#define MOD_REMOVED_MASK BIT(8)
+#define MOD_INDEX_LAST MOD_INDEX_MASK
+
+static int decode_next_removed(struct trace_mod_entry *entry)
+{
+ if (WARN_ON_ONCE(!(entry->mod_addr & MOD_REMOVED_MASK)))
+ return -1;
+
+ if ((entry->mod_addr & MOD_INDEX_MASK) == MOD_INDEX_MASK)
+ return -1;
+
+ return (int)(entry->mod_addr & MOD_INDEX_MASK);
+}
+
+static int encode_last_removed(struct trace_scratch *tscratch, int last)
+{
+ struct trace_mod_entry *entry;
+ int idx = tscratch->oldest_removed;
+
+ if (WARN_ON_ONCE(last < 0 || last >= tscratch->nr_entries))
+ return -1;
+
+ if (idx < 0) {
+ tscratch->oldest_removed = last;
+ } else {
+ do {
+ entry = &tscratch->entries[idx];
+ idx = entry->mod_addr & MOD_INDEX_MASK;
+ } while (idx != MOD_INDEX_LAST);
+ entry->mod_addr &= ~MOD_INDEX_MASK;
+ entry->mod_addr |= last;
+ }
+ entry = &tscratch->entries[last];
+ entry->mod_addr |= MOD_REMOVED_MASK | MOD_INDEX_LAST;
+ return 0;
+}
+
+unsigned long trace_adjust_address(struct trace_array *tr, unsigned long addr)
+{
+ struct trace_scratch *tscratch;
+ int i;
+
+ /* If we don't have last boot delta, return the address */
+ if (!(tr->flags & TRACE_ARRAY_FL_LAST_BOOT))
+ return addr;
+
+ tscratch = tr->scratch;
+ if (tscratch && tr->module_delta && tscratch->entries[0].mod_addr < addr) {
+ /* Note that entries are sorted */
+ for (i = 0; i < tr->nr_modules; i++)
+ if (addr < tscratch->entries[i].mod_addr)
+ break;
+ return addr + tr->module_delta[i - 1];
+ }
+
+ return addr + tr->text_delta;
+}
+
static int save_mod(struct module *mod, void *data)
{
struct trace_array *tr = data;
@@ -6021,12 +6083,18 @@ static int save_mod(struct module *mod, void *data)
return -1;
size = tr->scratch_size;
- if (struct_size(tscratch, entries, tscratch->nr_entries + 1) > size)
- return -1;
+ if (struct_size(tscratch, entries, tscratch->nr_entries + 1) > size) {
+ int idx = tscratch->oldest_removed;
- entry = &tscratch->entries[tscratch->nr_entries];
+ if (idx < 0)
+ return -1;
+ entry = &tscratch->entries[idx];
+ tscratch->oldest_removed = decode_next_removed(entry);
+ } else {
+ entry = &tscratch->entries[tscratch->nr_entries];
- tscratch->nr_entries++;
+ tscratch->nr_entries++;
+ }
entry->mod_addr = (unsigned long)mod->mem[MOD_TEXT].base;
strscpy(entry->mod_name, mod->name);
@@ -6063,14 +6131,17 @@ static void update_last_data(struct trace_array *tr)
*/
tracing_reset_all_cpus(&tr->array_buffer);
- /* Using current data now */
- tr->text_delta = 0;
-
if (!tr->scratch)
return;
tscratch = tr->scratch;
+ /* Using current data now */
+ tr->text_delta = 0;
+ kfree(tr->module_delta);
+ tr->module_delta = NULL;
+ tr->nr_modules = 0;
+
/* Set the persistent ring buffer meta data to this address */
#ifdef CONFIG_RANDOMIZE_BASE
tscratch->kaslr_addr = kaslr_offset();
@@ -9349,10 +9420,37 @@ static struct dentry *trace_instance_dir;
static void
init_tracer_tracefs(struct trace_array *tr, struct dentry *d_tracer);
+static int make_mod_delta(struct module *mod, void *data)
+{
+ struct trace_scratch *tscratch;
+ struct trace_mod_entry *entry;
+ struct trace_array *tr = data;
+ int i;
+
+ tscratch = tr->scratch;
+ for (i = 0; i < tr->nr_modules; i++) {
+ entry = &tscratch->entries[i];
+ if (!strcmp(mod->name, entry->mod_name)) {
+ tr->module_delta[i] = (unsigned long)mod->mem[MOD_TEXT].base - entry->mod_addr;
+ break;
+ }
+ }
+ return 0;
+}
+
+static int mod_addr_comp(const void *a, const void *b, const void *data)
+{
+ const struct trace_mod_entry *e1 = a;
+ const struct trace_mod_entry *e2 = b;
+
+ return e1->mod_addr > e2->mod_addr ? 1 : -1;
+}
+
static void setup_trace_scratch(struct trace_array *tr, void *scratch, unsigned int size)
{
struct trace_scratch *tscratch = scratch;
struct trace_mod_entry *entry;
+ int i, nr_entries;
if (!scratch)
return;
@@ -9369,7 +9467,7 @@ static void setup_trace_scratch(struct trace_array *tr, void *scratch, unsigned
goto reset;
/* Check if each module name is a valid string */
- for (int i = 0; i < tscratch->nr_entries; i++) {
+ for (i = 0; i < tscratch->nr_entries; i++) {
int n;
entry = &tscratch->entries[i];
@@ -9383,6 +9481,22 @@ static void setup_trace_scratch(struct trace_array *tr, void *scratch, unsigned
if (n == MODULE_NAME_LEN)
goto reset;
}
+ nr_entries = i;
+ /* Allocate module delta array */
+ tr->module_delta = kcalloc(nr_entries, sizeof(long), GFP_KERNEL);
+ if (!tr->module_delta) {
+ pr_info("module_delta allocation failed. Not able to decode module address.");
+ goto reset;
+ }
+ tr->nr_modules = nr_entries;
+
+ /* Sort module table by base address. */
+ sort_r(tscratch->entries, nr_entries, sizeof(struct trace_mod_entry),
+ mod_addr_comp, NULL, NULL);
+
+ /* Scan modules */
+ module_for_each_mod(make_mod_delta, tr);
+
return;
reset:
/* Invalid trace modules */
@@ -10100,14 +10214,50 @@ static bool trace_array_active(struct trace_array *tr)
static void trace_module_record(struct module *mod)
{
struct trace_array *tr;
+ unsigned long flags;
list_for_each_entry(tr, &ftrace_trace_arrays, list) {
+ flags = tr->flags & (TRACE_ARRAY_FL_BOOT | TRACE_ARRAY_FL_LAST_BOOT);
/* Update any persistent trace array that has already been started */
- if ((tr->flags & (TRACE_ARRAY_FL_BOOT | TRACE_ARRAY_FL_LAST_BOOT)) ==
- TRACE_ARRAY_FL_BOOT) {
+ if (flags == TRACE_ARRAY_FL_BOOT) {
/* Only update if the trace array is active */
if (trace_array_active(tr))
save_mod(mod, tr);
+ } else if (flags & TRACE_ARRAY_FL_LAST_BOOT) {
+ /* Update delta if the module loaded in previous boot */
+ make_mod_delta(mod, tr);
+ }
+ }
+}
+
+static void mark_mod_removed(struct trace_array *tr, struct module *mod)
+{
+ struct trace_scratch *tscratch;
+ struct trace_mod_entry *entry;
+ int i;
+
+ tscratch = tr->scratch;
+ for (i = 0; i < tscratch->nr_entries; i++) {
+ entry = &tscratch->entries[i];
+ if (!strcmp(entry->mod_name, mod->name)) {
+ encode_last_removed(tscratch, i);
+ break;
+ }
+ }
+}
+
+static void trace_module_mark_remove(struct module *mod)
+{
+ struct trace_array *tr;
+ unsigned long flags;
+
+ list_for_each_entry(tr, &ftrace_trace_arrays, list) {
+ flags = tr->flags & (TRACE_ARRAY_FL_BOOT | TRACE_ARRAY_FL_LAST_BOOT);
+ /* Update any persistent trace array that has already been started */
+ if (flags == TRACE_ARRAY_FL_BOOT) {
+ /* Only update if the trace array is active */
+ if (trace_array_active(tr))
+ mark_mod_removed(tr, mod);
}
}
}
@@ -10124,6 +10274,7 @@ static int trace_module_notify(struct notifier_block *self,
break;
case MODULE_STATE_GOING:
trace_module_remove_evals(mod);
+ trace_module_mark_remove(mod);
break;
}
@@ -349,6 +349,8 @@ struct trace_array {
unsigned long range_addr_start;
unsigned long range_addr_size;
long text_delta;
+ int nr_modules;
+ long *module_delta;
void *scratch; /* pointer in persistent memory */
int scratch_size;
@@ -465,6 +467,8 @@ extern int tracing_set_clock(struct trace_array *tr, const char *clockstr);
extern bool trace_clock_in_ns(struct trace_array *tr);
+extern unsigned long trace_adjust_address(struct trace_array *tr, unsigned long addr);
+
/*
* The global tracer (top) should be the first trace array added,
* but we check the flag anyway.
@@ -1248,7 +1248,6 @@ static enum print_line_t trace_stack_print(struct trace_iterator *iter,
struct trace_seq *s = &iter->seq;
unsigned long *p;
unsigned long *end;
- long delta = iter->tr->text_delta;
trace_assign_type(field, iter->ent);
end = (unsigned long *)((long)iter->ent + iter->ent_size);
@@ -1265,7 +1264,7 @@ static enum print_line_t trace_stack_print(struct trace_iterator *iter,
trace_seq_puts(s, "[FTRACE TRAMPOLINE]\n");
continue;
}
- seq_print_ip_sym(s, (*p) + delta, flags);
+ seq_print_ip_sym(s, trace_adjust_address(iter->tr, *p), flags);
trace_seq_putc(s, '\n');
}