Change all references of num_cpus -> num_cores
[akaros.git] / kern / src / smp.c
index 0932120..d872459 100644 (file)
-#ifdef __DEPUTY__
-#pragma nodeputy
-#endif
-
-#include <arch/x86.h>
-#include <arch/smp.h>
-#include <arch/console.h>
-#include <arch/apic.h>
+/*
+ * Copyright (c) 2009 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ */
 
+#include <arch/arch.h>
 #include <atomic.h>
-#include <ros/error.h>
+#include <smp.h>
+#include <error.h>
 #include <stdio.h>
 #include <string.h>
 #include <assert.h>
 #include <pmap.h>
-#include <env.h>
+#include <process.h>
+#include <schedule.h>
 #include <trap.h>
+#include <trace.h>
+#include <kdebug.h>
+#include <kmalloc.h>
 
-volatile uint8_t num_cpus = 0xee;
-uintptr_t smp_stack_top;
-per_cpu_info_t per_cpu_info[MAX_NUM_CPUS];
+struct per_cpu_info per_cpu_info[MAX_NUM_CORES];
 
-/*************************** IPI Wrapper Stuff ********************************/
-// checklists to protect the global interrupt_handlers for 0xf0, f1, f2, f3, f4
-// need to be global, since there is no function that will always exist for them
-handler_wrapper_t             handler_wrappers[NUM_HANDLER_WRAPPERS];
 // tracks number of global waits on smp_calls, must be <= NUM_HANDLER_WRAPPERS
-uint32_t outstanding_calls = 0; 
-
-#define DECLARE_HANDLER_CHECKLISTS(vector)                          \
-       INIT_CHECKLIST(f##vector##_cpu_list, MAX_NUM_CPUS);
+atomic_t outstanding_calls = 0;
 
-#define INIT_HANDLER_WRAPPER(v)                                     \
-{                                                                   \
-       handler_wrappers[(v)].vector = 0xf##v;                          \
-       handler_wrappers[(v)].cpu_list = &f##v##_cpu_list;              \
-       handler_wrappers[(v)].cpu_list->mask.size = num_cpus;           \
+/* Helper for running a proc (if we should).  Lots of repetition with
+ * proc_restartcore */
+static void try_run_proc(void)
+{
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       /* There was a process running here, and we should return to it. */
+       if (pcpui->owning_proc) {
+               assert(!pcpui->cur_kthread->sysc);
+               assert(pcpui->cur_ctx);
+               __proc_startcore(pcpui->owning_proc, pcpui->cur_ctx);
+               assert(0);
+       } else {
+               /* Make sure we have abandoned core.  It's possible to have an owner
+                * without a current (smp_idle, __startcore, __death). */
+               abandon_core();
+       }
 }
 
-DECLARE_HANDLER_CHECKLISTS(0);
-DECLARE_HANDLER_CHECKLISTS(1);
-DECLARE_HANDLER_CHECKLISTS(2);
-DECLARE_HANDLER_CHECKLISTS(3);
-DECLARE_HANDLER_CHECKLISTS(4);
-
-static void init_smp_call_function(void)
+/* All cores end up calling this whenever there is nothing left to do or they
+ * don't know explicitly what to do.  Non-zero cores call it when they are done
+ * booting.  Other cases include after getting a DEATH IPI.
+ *
+ * All cores attempt to run the context of any owning proc.  Barring that, they
+ * halt and wake up when interrupted, do any work on their work queue, then halt
+ * again.  In between, the ksched gets a chance to tell it to do something else,
+ * or perhaps to halt in another manner. */
+static void __attribute__((noinline, noreturn)) __smp_idle(void)
 {
-       INIT_HANDLER_WRAPPER(0);
-       INIT_HANDLER_WRAPPER(1);
-       INIT_HANDLER_WRAPPER(2);
-       INIT_HANDLER_WRAPPER(3);
-       INIT_HANDLER_WRAPPER(4);
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       clear_rkmsg(pcpui);
+       pcpui->cur_kthread->is_ktask = FALSE;
+       enable_irq();   /* one-shot change to get any IRQs before we halt later */
+       while (1) {
+               disable_irq();
+               process_routine_kmsg();
+               try_run_proc();
+               cpu_bored();            /* call out to the ksched */
+               /* cpu_halt() atomically turns on interrupts and halts the core.
+                * Important to do this, since we could have a RKM come in via an
+                * interrupt right while PRKM is returning, and we wouldn't catch
+                * it. */
+               __set_cpu_state(pcpui, CPU_STATE_IDLE);
+               cpu_halt();
+               /* interrupts are back on now (given our current semantics) */
+       }
+       assert(0);
 }
 
-/******************************************************************************/
-
-static void smp_mtrr_handler(trapframe_t *tf, void* data)
+void smp_idle(void)
 {
-       setup_default_mtrrs((barrier_t*)data);
+       #ifdef CONFIG_RESET_STACKS
+       set_stack_pointer(get_stack_top());
+       set_frame_pointer(0);
+       #endif /* CONFIG_RESET_STACKS */
+       __smp_idle();
+       assert(0);
 }
 
-void smp_boot(void)
+/* Arch-independent per-cpu initialization.  This will call the arch dependent
+ * init first. */
+void smp_percpu_init(void)
 {
-       // this needs to be set in smp_entry too...
-       #define trampoline_pg 0x00001000
-       page_t *smp_stack;
-       // NEED TO GRAB A LOWMEM FREE PAGE FOR AP BOOTUP CODE
-       // page1 (2nd page) is reserved, hardcoded in pmap.c
-       extern smp_entry(), smp_entry_end(), smp_boot_lock(), smp_semaphore();
-       memset(KADDR(trampoline_pg), 0, PGSIZE);
-       memcpy(KADDR(trampoline_pg), &smp_entry, &smp_entry_end - &smp_entry);
-
-       // This mapping allows access to the trampoline with paging on and off
-       // via trampoline_pg
-       page_insert(boot_pgdir, pa2page(trampoline_pg), (void*)trampoline_pg, PTE_W);
-
-       // Allocate a stack for the cores starting up.  One for all, must share
-       if (page_alloc(&smp_stack))
-               panic("No memory for SMP boot stack!");
-       smp_stack->pp_ref++;
-       smp_stack_top = (uintptr_t)(page2kva(smp_stack) + PGSIZE);
-
-       // Start the IPI process (INIT, wait, SIPI, wait, SIPI, wait)
-       send_init_ipi();
-       // SDM 3A is a little wonky wrt the proper delays.  These are my best guess.
-       udelay(10000);
-       // first SIPI
-       send_startup_ipi(0x01);
-       /* BOCHS does not like this second SIPI.
-       // second SIPI
-       udelay(200);
-       send_startup_ipi(0x01);
-       */
-       udelay(100000);
-
-       // Each core will also increment smp_semaphore, and decrement when it is done,
-       // all in smp_entry.  It's purpose is to keep Core0 from competing for the
-       // smp_boot_lock.  So long as one AP increments the sem before the final
-       // LAPIC timer goes off, all available cores will be initialized.
-       while(*(volatile uint32_t*)(&smp_semaphore - &smp_entry + trampoline_pg));
-
-       // From here on, no other cores are coming up.  Grab the lock to ensure it.
-       // Another core could be in it's prelock phase and be trying to grab the lock
-       // forever....
-       // The lock exists on the trampoline, so it can be grabbed right away in
-       // real mode.  If core0 wins the race and blocks other CPUs from coming up
-       // it can crash the machine if the other cores are allowed to proceed with
-       // booting.  Specifically, it's when they turn on paging and have that temp
-       // mapping pulled out from under them.  Now, if a core loses, it will spin
-       // on the trampoline (which we must be careful to not deallocate)
-       spin_lock((uint32_t*)(&smp_boot_lock - &smp_entry + trampoline_pg));
-       cprintf("Num_Cpus Detected: %d\n", num_cpus);
-
-       // Remove the mapping of the page used by the trampoline
-       page_remove(boot_pgdir, (void*)trampoline_pg);
-       // It had a refcount of 2 earlier, so we need to dec once more to free it
-       // but only if all cores are in (or we reset / reinit those that failed)
-       // TODO after we parse ACPI tables
-       if (num_cpus == 8) // TODO - ghetto coded for our 8 way SMPs
-               page_decref(pa2page(trampoline_pg));
-       // Remove the page table used for that mapping
-       pagetable_remove(boot_pgdir, (void*)trampoline_pg);
-       // Dealloc the temp shared stack
-       page_decref(smp_stack);
-
-       // Set up the generic remote function call facility
-       init_smp_call_function();
-
-       // Set up all cores to use the proper MTRRs
-       barrier_t generic_barrier;
-       init_barrier(&generic_barrier, num_cpus); // barrier used by smp_mtrr_handler
-       smp_call_function_all(smp_mtrr_handler, &generic_barrier, 0);
-
-       // Should probably flush everyone's TLB at this point, to get rid of
-       // temp mappings that were removed.  TODO
+       uint32_t coreid = core_id();
+       struct per_cpu_info *pcpui = &per_cpu_info[coreid];
+       void *trace_buf;
+       struct kthread *kthread;
+       /* Don't initialize __ctx_depth here, since it is already 1 (at least on
+        * x86), since this runs in irq context. */
+       /* Do this first */
+       __arch_pcpu_init(coreid);
+       /* init our kthread (tracks our currently running context) */
+       kthread = __kthread_zalloc();
+       kthread->stacktop = get_stack_top();    /* assumes we're on the 1st page */
+       pcpui->cur_kthread = kthread;
+       per_cpu_info[coreid].spare = 0;
+       /* Init relevant lists */
+       spinlock_init_irqsave(&per_cpu_info[coreid].immed_amsg_lock);
+       STAILQ_INIT(&per_cpu_info[coreid].immed_amsgs);
+       spinlock_init_irqsave(&per_cpu_info[coreid].routine_amsg_lock);
+       STAILQ_INIT(&per_cpu_info[coreid].routine_amsgs);
+       /* Initialize the per-core timer chain */
+       init_timer_chain(&per_cpu_info[coreid].tchain, set_pcpu_alarm_interrupt);
+#ifdef CONFIG_KTHREAD_POISON
+       *kstack_bottom_addr(kthread->stacktop) = 0xdeadbeef;
+#endif /* CONFIG_KTHREAD_POISON */
+       /* Init generic tracing ring */
+       trace_buf = kpage_alloc_addr();
+       assert(trace_buf);
+       trace_ring_init(&pcpui->traces, trace_buf, PGSIZE,
+                       sizeof(struct pcpu_trace_event));
+       for (int i = 0; i < NR_CPU_STATES; i++)
+               pcpui->state_ticks[i] = 0;
+       pcpui->last_tick_cnt = read_tsc();
+       /* Core 0 is in the KERNEL state, called from smp_boot.  The other cores are
+        * too, at least on x86, where we were called from asm (woken by POKE). */
+       pcpui->cpu_state = CPU_STATE_KERNEL;
+       /* Enable full lock debugging, after all pcpui work is done */
+       pcpui->__lock_checking_enabled = 1;
 }
 
-/*
- * This is called from smp_entry by each core to finish the core bootstrapping.
- * There is a spinlock around this entire function in smp_entry, for a few reasons,
- * the most important being that all cores use the same stack when entering here.
- */
-uint32_t smp_main(void)
+/* it's actually okay to set the state to the existing state.  originally, it
+ * was a bug in the state tracking, but it is possible, at least on x86, to have
+ * a halted core (state IDLE) get woken up by an IRQ that does not trigger the
+ * IRQ handling state.  for example, there is the I_POKE_CORE ipi.  smp_idle
+ * will just sleep again, and reset the state from IDLE to IDLE. */
+void __set_cpu_state(struct per_cpu_info *pcpui, int state)
 {
-       /*
-       // Print some diagnostics.  Uncomment if there're issues.
-       cprintf("Good morning Vietnam!\n");
-       cprintf("This core's Default APIC ID: 0x%08x\n", lapic_get_default_id());
-       cprintf("This core's Current APIC ID: 0x%08x\n", lapic_get_id());
-       if (read_msr(IA32_APIC_BASE) & 0x00000100)
-               cprintf("I am the Boot Strap Processor\n");
-       else
-               cprintf("I am an Application Processor\n");
-       cprintf("Num_Cpus: %d\n\n", num_cpus);
-       */
-
-       // Get a per-core kernel stack
-       page_t *my_stack;
-       if (page_alloc(&my_stack))
-               panic("Unable to alloc a per-core stack!");
-       my_stack->pp_ref++;
-       memset(page2kva(my_stack), 0, PGSIZE);
-
-       // Set up a gdt / gdt_pd for this core, stored at the top of the stack
-       // This is necessary, eagle-eyed readers know why
-       // GDT should be 4-byte aligned.  TS isn't aligned.  Not sure if it matters.
-       pseudodesc_t *my_gdt_pd = page2kva(my_stack) + PGSIZE -
-               sizeof(pseudodesc_t) - sizeof(segdesc_t)*SEG_COUNT;
-       segdesc_t *my_gdt = page2kva(my_stack) + PGSIZE -
-               sizeof(segdesc_t)*SEG_COUNT;
-       // TS also needs to be permanent
-       taskstate_t *my_ts = page2kva(my_stack) + PGSIZE -
-               sizeof(pseudodesc_t) - sizeof(segdesc_t)*SEG_COUNT -
-               sizeof(taskstate_t);
-       // Usable portion of the KSTACK grows down from here
-       // Won't actually start using this stack til our first interrupt
-       // (issues with changing the stack pointer and then trying to "return")
-       uintptr_t my_stack_top = (uintptr_t)my_ts;
-       
-       // Set up MSR for SYSENTER 
-       write_msr(MSR_IA32_SYSENTER_CS, GD_KT);
-       write_msr(MSR_IA32_SYSENTER_ESP, my_stack_top);
-       write_msr(MSR_IA32_SYSENTER_EIP, (uint32_t) &sysenter_handler);
-
-       // Build and load the gdt / gdt_pd
-       memcpy(my_gdt, gdt, sizeof(segdesc_t)*SEG_COUNT);
-       *my_gdt_pd = (pseudodesc_t) {
-               sizeof(segdesc_t)*SEG_COUNT - 1, (uintptr_t) my_gdt };
-       asm volatile("lgdt %0" : : "m"(*my_gdt_pd));
-
-       // Need to set the TSS so we know where to trap on this core
-       my_ts->ts_esp0 = my_stack_top;
-       my_ts->ts_ss0 = GD_KD;
-       // Initialize the TSS field of my_gdt.
-       my_gdt[GD_TSS >> 3] = SEG16(STS_T32A, (uint32_t) (my_ts), sizeof(taskstate_t), 0);
-       my_gdt[GD_TSS >> 3].sd_s = 0;
-       // Load the TSS
-       ltr(GD_TSS);
-
-       // Loads the same IDT used by the other cores
-       asm volatile("lidt idt_pd");
-
-       // APIC setup
-       // set LINT0 to receive ExtINTs (KVM's default).  At reset they are 0x1000.
-       write_mmreg32(LAPIC_LVT_LINT0, 0x700);
-       // mask it to shut it up for now.  Doesn't seem to matter yet, since both
-       // KVM and Bochs seem to only route the PIC to core0.
-       mask_lapic_lvt(LAPIC_LVT_LINT0);
-       // and then turn it on
-       lapic_enable();
-
-       // set a default logical id for now
-       lapic_set_logid(lapic_get_id());
-
-       return my_stack_top; // will be loaded in smp_entry.S
+       uint64_t now_ticks;
+       assert(!irq_is_enabled());
+       /* TODO: could put in an option to enable/disable state tracking. */
+       now_ticks = read_tsc();
+       pcpui->state_ticks[pcpui->cpu_state] += now_ticks - pcpui->last_tick_cnt;
+       /* TODO: if the state was user, we could account for the vcore's time,
+        * similar to the total_ticks in struct vcore.  the difference is that the
+        * total_ticks tracks the vcore's virtual time, while this tracks user time.
+        * something like vcore->user_ticks. */
+       pcpui->cpu_state = state;
+       pcpui->last_tick_cnt = now_ticks;
 }
 
-/* All non-zero cores call this at the end of their boot process.  They halt,
- * and wake up when interrupted, do any work on their work queue, then halt
- * when there is nothing to do.  
- */
-void smp_idle(void)
+void reset_cpu_state_ticks(int coreid)
 {
-       enable_irq();
-       while (1) {
-               process_workqueue();
-               // consider races with work added after we started leaving the last func
-               cpu_halt();
+       struct per_cpu_info *pcpui = &per_cpu_info[coreid];
+       uint64_t now_ticks;
+       if (coreid >= num_cores)
+               return;
+       /* need to update last_tick_cnt, so the current value doesn't get added in
+        * next time we update */
+       now_ticks = read_tsc();
+       for (int i = 0; i < NR_CPU_STATES; i++) {
+               pcpui->state_ticks[i] = 0;
+               pcpui->last_tick_cnt = now_ticks;
        }
 }
 
-static int smp_call_function(uint8_t type, uint8_t dest, isr_t handler, void* data,
-                              handler_wrapper_t** wait_wrapper)
-{
-       extern handler_t interrupt_handlers[];
-       int8_t state = 0;
-       uint32_t wrapper_num;
-       handler_wrapper_t* wrapper;
-
-       // prevents us from ever having more than NUM_HANDLER_WRAPPERS callers in
-       // the process of competing for vectors.  not decremented until both after
-       // the while(1) loop and after it's been waited on.
-       atomic_inc(&outstanding_calls);
-       if (outstanding_calls > NUM_HANDLER_WRAPPERS) {
-               atomic_dec(&outstanding_calls);
-               return E_BUSY;
-       }
-       
-       // assumes our cores are numbered in order
-       if ((type == 4) && (dest >= num_cpus))
-               panic("Destination CPU does not exist!");
+/* PCPUI Trace Rings: */
 
-       // build the mask based on the type and destination
-       INIT_CHECKLIST_MASK(cpu_mask, MAX_NUM_CPUS);
-       // set checklist mask's size dynamically to the num cpus actually present
-       cpu_mask.size = num_cpus;
-       switch (type) {
-               case 1: // self
-                       SET_BITMASK_BIT(cpu_mask.bits, lapic_get_id());
-                       break;
-               case 2: // all
-                       FILL_BITMASK(cpu_mask.bits, num_cpus);
-                       break;
-               case 3: // all but self
-                       FILL_BITMASK(cpu_mask.bits, num_cpus);
-                       CLR_BITMASK_BIT(cpu_mask.bits, lapic_get_id());
-                       break;
-               case 4: // physical mode
-                       // note this only supports sending to one specific physical id
-                       // (only sets one bit, so if multiple cores have the same phys id
-                       // the first one through will set this).
-                       SET_BITMASK_BIT(cpu_mask.bits, dest);
-                       break;
-               case 5: // logical mode
-                       // TODO
-                       warn("Logical mode bitmask handler protection not implemented!");
-                       break;
-               default:
-                       panic("Invalid type for cross-core function call!");
-       }
+static void pcpui_trace_kmsg_handler(void *event, void *data)
+{
+       struct pcpu_trace_event *te = (struct pcpu_trace_event*)event;
+       char *func_name;
+       uintptr_t addr;
+       addr = te->arg1;
+       func_name = get_fn_name(addr);
+       printk("\tKMSG %p: %s\n", addr, func_name);
+       kfree(func_name);
+}
 
-       // Find an available vector/wrapper.  Starts with this core's id (mod the
-       // number of wrappers).  Walk through on conflict.
-       // Commit returns an error if it wanted to give up for some reason,
-       // like taking too long to acquire the lock or clear the mask, at which
-       // point, we try the next one.
-       // When we are done, wrapper points to the one we finally got.
-       // this wrapper_num trick doesn't work as well if you send a bunch in a row
-       // and wait, since you always check your main one (which is currently busy).
-       wrapper_num = lapic_get_id() % NUM_HANDLER_WRAPPERS;
-       while(1) {
-               wrapper = &handler_wrappers[wrapper_num];
-               if (!commit_checklist_wait(wrapper->cpu_list, &cpu_mask))
-                       break;
-               wrapper_num = (wrapper_num + 1) % NUM_HANDLER_WRAPPERS;
-               /*
-               uint32_t count = 0;
-               // instead of deadlock, smp_call can fail with this.  makes it harder
-               // to use (have to check your return value).  consider putting a delay
-               // here too (like if wrapper_num == initial_wrapper_num)
-               if (count++ > NUM_HANDLER_WRAPPERS * 1000) // note 1000 isn't enough...
-                       return E_BUSY;
-               */
-       }
+static void pcpui_trace_locks_handler(void *event, void *data)
+{
+       struct pcpu_trace_event *te = (struct pcpu_trace_event*)event;
+       char *func_name;
+       uintptr_t lock_addr = te->arg1;
+       if (lock_addr > KERN_LOAD_ADDR)
+               func_name = get_fn_name(lock_addr);
+       else
+               func_name = "Dynamic lock";
+       printk("Time %uus, lock %p (%s)\n", te->arg0, lock_addr, func_name);
+       printk("\t");
+       spinlock_debug((spinlock_t*)lock_addr);
+       if (lock_addr > KERN_LOAD_ADDR)
+               kfree(func_name);
+}
 
-       // Wanting to wait is expressed by having a non-NULL handler_wrapper_t**
-       // passed in.  Pass out our reference to wrapper, to wait later.
-       // If we don't want to wait, release the checklist (though it is still not
-       // clear, so it can't be used til everyone checks in).
-       if (wait_wrapper)
-               *wait_wrapper = wrapper;
-       else {
-               release_checklist(wrapper->cpu_list);
-               atomic_dec(&outstanding_calls);
-       }
+/* Add specific trace handlers here: */
+trace_handler_t pcpui_tr_handlers[PCPUI_NR_TYPES] = {
+                                  0,
+                                  pcpui_trace_kmsg_handler,
+                                  pcpui_trace_locks_handler,
+                                  };
 
-       // now register our handler to run
-       register_interrupt_handler(interrupt_handlers, wrapper->vector, handler, data);
-       // WRITE MEMORY BARRIER HERE
-       enable_irqsave(&state);
-       // Send the proper type of IPI.  I made up these numbers.
-       switch (type) {
-               case 1:
-                       send_self_ipi(wrapper->vector);
-                       break;
-               case 2:
-                       send_broadcast_ipi(wrapper->vector);
-                       break;
-               case 3:
-                       send_all_others_ipi(wrapper->vector);
-                       break;
-               case 4: // physical mode
-                       send_ipi(dest, 0, wrapper->vector);
-                       break;
-               case 5: // logical mode
-                       send_ipi(dest, 1, wrapper->vector);
-                       break;
-               default:
-                       panic("Invalid type for cross-core function call!");
-       }
-       // wait long enough to receive our own broadcast (PROBABLY WORKS) TODO
-       lapic_wait_to_send();
-       disable_irqsave(&state);
-       return 0;
+/* Generic handler for the pcpui ring.  Will switch out to the appropriate
+ * type's handler */
+static void pcpui_trace_fn(void *event, void *data)
+{
+       struct pcpu_trace_event *te = (struct pcpu_trace_event*)event;
+       int desired_type = (int)(long)data;
+       if (te->type >= PCPUI_NR_TYPES)
+               printk("Bad trace type %d\n", te->type);
+       /* desired_type == 0 means all types */
+       if (desired_type && desired_type != te->type)
+               return;
+       if (pcpui_tr_handlers[te->type])
+               pcpui_tr_handlers[te->type](event, data);
 }
 
-// Wrapper functions.  Add more as they are needed.
-int smp_call_function_self(isr_t handler, void* data,
-                           handler_wrapper_t** wait_wrapper)
+void pcpui_tr_foreach(int coreid, int type)
 {
-       return smp_call_function(1, 0, handler, data, wait_wrapper);
+       struct trace_ring *tr = &per_cpu_info[coreid].traces;
+       assert(tr);
+       printk("\n\nTrace Ring on Core %d\n--------------\n", coreid);
+       trace_ring_foreach(tr, pcpui_trace_fn, (void*)(long)type);
 }
 
-int smp_call_function_all(isr_t handler, void* data,
-                          handler_wrapper_t** wait_wrapper)
+void pcpui_tr_foreach_all(int type)
 {
-       return smp_call_function(2, 0, handler, data, wait_wrapper);
+       for (int i = 0; i < num_cores; i++)
+               pcpui_tr_foreach(i, type);
 }
 
-int smp_call_function_single(uint8_t dest, isr_t handler, void* data,
-                             handler_wrapper_t** wait_wrapper)
+void pcpui_tr_reset_all(void)
 {
-       return smp_call_function(4, dest, handler, data, wait_wrapper);
+       for (int i = 0; i < num_cores; i++)
+               trace_ring_reset(&per_cpu_info[i].traces);
 }
 
-// If you want to wait, pass the address of a pointer up above, then call
-// this to do the actual waiting.  Be somewhat careful about uninitialized 
-// or old wrapper pointers.
-int smp_call_wait(handler_wrapper_t* wrapper)
+void pcpui_tr_reset_and_clear_all(void)
 {
-       if (wrapper) {
-               waiton_checklist(wrapper->cpu_list);
-               return 0;
-       } else {
-               warn("Attempting to wait on null wrapper!  Check your return values!");
-               return E_FAIL;
-       }
+       for (int i = 0; i < num_cores; i++)
+               trace_ring_reset_and_clear(&per_cpu_info[i].traces);
 }
-