#include <linux/kthread.h>
#include <asm/uaccess.h>
#include <linux/smp.h>
+#include <linux/vmalloc.h>
+
+#include <asm/i387.h>
#include <palacios/vmm.h>
#include <palacios/vmm_host_events.h>
+
+#ifdef V3_CONFIG_HOST_LAZY_FPU_SWITCH
+#include <interfaces/vmm_lazy_fpu.h>
+#endif
+
#include "palacios.h"
+#include "mm.h"
+#include "memcheck.h"
+#include "lockcheck.h"
-#include "mm.h"
// The following can be used to track heap bugs
// zero memory after allocation
u32 pg_frees = 0;
u32 mallocs = 0;
u32 frees = 0;
-
+u32 vmallocs = 0;
+u32 vfrees = 0;
static struct v3_vm_info * irq_to_guest_map[256];
/**
* Prints a message to the console.
*/
-void palacios_print(const char *fmt, ...) {
+void palacios_print_scoped(void * vm, int vcore, const char *fmt, ...) {
#if V3_PRINTK_OLD_STYLE_OUTPUT
va_list ap;
char *buf;
unsigned int cpu = palacios_get_cpu();
+ struct v3_guest *guest = (struct v3_guest *)vm;
buf = print_buffer[cpu];
}
#endif
- printk(KERN_INFO "palacios (pcore %u): %s",cpu,buf);
-
+ if (guest) {
+ if (vcore>=0) {
+ printk(KERN_INFO "palacios (pcore %u vm %s vcore %u): %s",
+ cpu,
+ guest->name,
+ vcore,
+ buf);
+ } else {
+ printk(KERN_INFO "palacios (pcore %u vm %s): %s",
+ cpu,
+ guest->name,
+ buf);
+ }
+ } else {
+ printk(KERN_INFO "palacios (pcore %u): %s",
+ cpu,
+ buf);
+ }
+
return;
#endif
}
-
/*
* Allocates a contiguous region of pages of the requested size.
* Returns the physical address of the first page in the region.
*/
-void *palacios_allocate_pages(int num_pages, unsigned int alignment) {
+void *palacios_allocate_pages(int num_pages, unsigned int alignment, int node_id, int constraints) {
void * pg_addr = NULL;
- pg_addr = (void *)alloc_palacios_pgs(num_pages, alignment);
+ if (num_pages<=0) {
+ ERROR("ALERT ALERT Attempt to allocate zero or fewer pages (%d pages, alignment %d, node %d, constraints 0x%x)\n",num_pages, alignment, node_id, constraints);
+ return NULL;
+ }
+
+ pg_addr = (void *)alloc_palacios_pgs(num_pages, alignment, node_id, constraints);
if (!pg_addr) {
- ERROR("ALERT ALERT Page allocation has FAILED Warning\n");
+ ERROR("ALERT ALERT Page allocation has FAILED Warning (%d pages, alignment %d, node %d, constraints 0x%x)\n",num_pages, alignment, node_id, constraints);
return NULL;
}
pg_allocs += num_pages;
+ MEMCHECK_ALLOC_PAGES(pg_addr,num_pages*4096);
+
return pg_addr;
}
*/
void palacios_free_pages(void * page_paddr, int num_pages) {
+ if (!page_paddr) {
+ ERROR("Ignoring free pages: 0x%p (0x%lx)for %d pages\n", page_paddr, (uintptr_t)page_paddr, num_pages);
+ dump_stack();
+ }
pg_frees += num_pages;
free_palacios_pgs((uintptr_t)page_paddr, num_pages);
+ MEMCHECK_FREE_PAGES(page_paddr,num_pages*4096);
+
}
void *
-palacios_alloc_extended(unsigned int size, unsigned int flags) {
+palacios_alloc_extended(unsigned int size, unsigned int flags, int node) {
void * addr = NULL;
- addr = kmalloc(size+2*ALLOC_PAD, flags);
+ if (size==0) {
+ // note that modern kernels will respond to a zero byte
+ // kmalloc and return the address 0x10... In Palacios,
+ // we will simply not allow 0 byte allocs at all, of any kind
+ ERROR("ALERT ALERT attempt to kmalloc zero bytes rejected\n");
+ return NULL;
+ }
+
+ if (node==-1) {
+ addr = kmalloc(size+2*ALLOC_PAD, flags);
+ } else {
+ addr = kmalloc_node(size+2*ALLOC_PAD, flags, node);
+ }
if (!addr) {
ERROR("ALERT ALERT kmalloc has FAILED FAILED FAILED\n");
memset(addr,0,size+2*ALLOC_PAD);
#endif
+ MEMCHECK_KMALLOC(addr,size+2*ALLOC_PAD);
+
return addr+ALLOC_PAD;
}
+void *
+palacios_valloc(unsigned int size)
+{
+ void * addr = NULL;
+
+ if (size==0) {
+ ERROR("ALERT ALERT attempt to vmalloc zero bytes rejected\n");
+ return NULL;
+ }
+
+ addr = vmalloc(size);
+
+ if (!addr) {
+ ERROR("ALERT ALERT vmalloc has FAILED FAILED FAILED\n");
+ return NULL;
+ }
+
+ vmallocs++;
+
+ MEMCHECK_VMALLOC(addr,size);
+
+ return addr;
+}
+
+void palacios_vfree(void *p)
+{
+ vfree(p);
+ vfrees++;
+ MEMCHECK_VFREE(p);
+}
/**
* Allocates 'size' bytes of kernel memory.
// module, both in places where interrupts are off and where they are on
// a GFP_KERNEL call, when done with interrupts off can lead to DEADLOCK
if (irqs_disabled()) {
- return palacios_alloc_extended(size,GFP_ATOMIC);
+ return palacios_alloc_extended(size,GFP_ATOMIC,-1);
} else {
- return palacios_alloc_extended(size,GFP_KERNEL);
+ return palacios_alloc_extended(size,GFP_KERNEL,-1);
}
}
void * addr
)
{
+ if (!addr) {
+ ERROR("Ignoring free : 0x%p\n", addr);
+ dump_stack();
+ }
frees++;
kfree(addr-ALLOC_PAD);
- return;
+ MEMCHECK_KFREE(addr-ALLOC_PAD);
}
/**
return;
}
+
+#define MAX_THREAD_NAME 32
+
struct lnx_thread_arg {
int (*fn)(void * arg);
void * arg;
- char * name;
+ char name[MAX_THREAD_NAME];
};
static int lnx_thread_target(void * arg) {
allow_signal(SIGKILL);
*/
+#ifdef V3_CONFIG_HOST_LAZY_FPU_SWITCH
+ // We are a kernel thread that needs FPU save/restore state
+ // vcores definitely need this, all the other threads get it too,
+ // but they just won't use it
+ fpu_alloc(&(current->thread.fpu));
+#endif
ret = thread_info->fn(thread_info->arg);
-
INFO("Palacios Thread (%s) EXITING\n", thread_info->name);
palacios_free(thread_info);
// handle cleanup
+ // We rely on do_exit to free the fpu data
+ // since we could get switched at any point until the thread is done...
+
do_exit(ret);
-
+
return 0; // should not get here.
}
thread_info->fn = fn;
thread_info->arg = arg;
- thread_info->name = thread_name;
+ strncpy(thread_info->name,thread_name,MAX_THREAD_NAME);
+ thread_info->name[MAX_THREAD_NAME-1] =0;
- return kthread_run( lnx_thread_target, thread_info, thread_name );
+ return kthread_run( lnx_thread_target, thread_info, thread_info->name );
}
thread_info->fn = fn;
thread_info->arg = arg;
- thread_info->name = thread_name;
-
+ strncpy(thread_info->name,thread_name,MAX_THREAD_NAME);
+ thread_info->name[MAX_THREAD_NAME-1] =0;
- thread = kthread_create( lnx_thread_target, thread_info, thread_name );
+ thread = kthread_create( lnx_thread_target, thread_info, thread_info->name );
if (IS_ERR(thread)) {
- WARNING("Palacios error creating thread: %s\n", thread_name);
+ WARNING("Palacios error creating thread: %s\n", thread_info->name);
palacios_free(thread_info);
return NULL;
}
* Given now immediately if there is no other thread that is runnable
* And there is no real bound on how long it will yield
*/
-void palacios_yield_cpu_timed(unsigned int us)
+void palacios_sleep_cpu(unsigned int us)
{
- unsigned int uspj = 1000000U/HZ;
-
- unsigned int jiffies = us/uspj + ((us%uspj) !=0); // ceiling
-
set_current_state(TASK_INTERRUPTIBLE);
-
- schedule_timeout(jiffies);
-
+ if (us) {
+ unsigned int uspj = 1000000U/HZ;
+ unsigned int jiffies = us/uspj + ((us%uspj) !=0); // ceiling
+ schedule_timeout(jiffies);
+ } else {
+ schedule();
+ }
+ return;
}
+void palacios_wakeup_cpu(void *thread)
+{
+ wake_up_process(thread);
+ return;
+}
/**
* Allocates a mutex.
if (lock) {
spin_lock_init(lock);
+ LOCKCHECK_ALLOC(lock);
} else {
ERROR("ALERT ALERT Unable to allocate lock\n");
return NULL;
return lock;
}
+void palacios_mutex_init(void *mutex)
+{
+ spinlock_t *lock = (spinlock_t*)mutex;
+
+ if (lock) {
+ spin_lock_init(lock);
+ LOCKCHECK_ALLOC(lock);
+ }
+}
+
+void palacios_mutex_deinit(void *mutex)
+{
+ spinlock_t *lock = (spinlock_t*)mutex;
+
+ if (lock) {
+ // no actual spin_lock_deinit on linux
+ // our purpose here is to drive the lock checker
+ LOCKCHECK_FREE(lock);
+ }
+}
+
+
/**
* Frees a mutex.
*/
void
palacios_mutex_free(void * mutex) {
palacios_free(mutex);
+ LOCKCHECK_FREE(mutex);
}
/**
*/
void
palacios_mutex_lock(void * mutex, int must_spin) {
+
+ LOCKCHECK_LOCK_PRE(mutex);
spin_lock((spinlock_t *)mutex);
+ LOCKCHECK_LOCK_POST(mutex);
}
unsigned long flags;
+ LOCKCHECK_LOCK_IRQSAVE_PRE(mutex,flags);
spin_lock_irqsave((spinlock_t *)mutex,flags);
+ LOCKCHECK_LOCK_IRQSAVE_POST(mutex,flags);
return (void *)flags;
}
void * mutex
)
{
+ LOCKCHECK_UNLOCK_PRE(mutex);
spin_unlock((spinlock_t *)mutex);
+ LOCKCHECK_UNLOCK_POST(mutex);
}
void
palacios_mutex_unlock_irqrestore(void *mutex, void *flags)
{
+ LOCKCHECK_UNLOCK_IRQRESTORE_PRE(mutex,(unsigned long)flags);
// This is correct, flags is opaque
spin_unlock_irqrestore((spinlock_t *)mutex,(unsigned long)flags);
+ LOCKCHECK_UNLOCK_IRQRESTORE_POST(mutex,(unsigned long)flags);
+}
+
+void palacios_used_fpu(void)
+{
+ struct thread_info *cur = current_thread_info();
+
+ // We assume we are not preemptible here...
+ cur->status |= TS_USEDFPU;
+ clts();
+ // After this, FP Save should be handled by Linux if it
+ // switches to a different task and that task uses FPU
+}
+
+inline int ists(void)
+{
+ return read_cr0() & X86_CR0_TS;
+
+}
+void palacios_need_fpu(void)
+{
+ // We assume we are not preemptible here...
+ if (ists()) {
+ // we have been switched back to from somewhere else...
+ // Do a restore now - this will also do a clts()
+ math_state_restore();
+ }
}
+
/**
* Structure used by the Palacios hypervisor to interface with the host kernel.
*/
static struct v3_os_hooks palacios_os_hooks = {
- .print = palacios_print,
+ .print = palacios_print_scoped,
.allocate_pages = palacios_allocate_pages,
.free_pages = palacios_free_pages,
.malloc = palacios_alloc,
.get_cpu_khz = palacios_get_cpu_khz,
.start_kernel_thread = palacios_start_kernel_thread,
.yield_cpu = palacios_yield_cpu,
- .yield_cpu_timed = palacios_yield_cpu_timed,
+ .sleep_cpu = palacios_sleep_cpu,
+ .wakeup_cpu = palacios_wakeup_cpu,
.mutex_alloc = palacios_mutex_alloc,
.mutex_free = palacios_mutex_free,
.mutex_lock = palacios_mutex_lock,
};
+#ifdef V3_CONFIG_HOST_LAZY_FPU_SWITCH
+// Note that this host interface is defined here since it's
+// intertwined with thread creation...
+static struct v3_lazy_fpu_iface palacios_fpu_hooks = {
+ .used_fpu = palacios_used_fpu,
+ .need_fpu = palacios_need_fpu
+};
+
+#endif
-int palacios_vmm_init( void )
+int palacios_vmm_init( char *options )
{
int num_cpus = num_online_cpus();
char * cpu_mask = NULL;
INFO("palacios_init starting - calling init_v3\n");
- Init_V3(&palacios_os_hooks, cpu_mask, num_cpus);
+ Init_V3(&palacios_os_hooks, cpu_mask, num_cpus, options);
+
+#ifdef V3_CONFIG_HOST_LAZY_FPU_SWITCH
+ V3_Init_Lazy_FPU(&palacios_fpu_hooks);
+#endif
return 0;