--- /dev/null
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/spinlock.h>
+
+#include "palacios.h"
+
+#include "lockcheck.h"
+
+// How far up the stack to track the caller
+// 0 => palacios_...
+// 1 => v3_lock...
+// 2 => caller of v3_lock..
+// ...
+#define STEP_BACK_DEPTH_FIRST 1
+#define STEP_BACK_DEPTH_LAST 4
+#define STEP_BACK_DEPTH (STEP_BACK_DEPTH_LAST-STEP_BACK_DEPTH_FIRST+1)
+
+// show when multiple locks are held simultaneously
+// This is the minimum number
+#define WARN_MULTIPLE_THRESHOLD 3
+
+typedef struct {
+ int inuse; // nonzero if this is in use
+ void *lock; // the lock
+ void *allocator[STEP_BACK_DEPTH];
+ // who allocated this
+ int lockcount; // how many times it's been locked/unlocked (lock=+1, unlock=-1)
+ int irqcount; // how many times interrupts have been turned off (+1/-1)
+ void *lastlocker[STEP_BACK_DEPTH];
+ // who last locked
+ void *lastunlocker[STEP_BACK_DEPTH];
+ // who last unlocked
+ void *lastirqlocker[STEP_BACK_DEPTH];
+ // who last locked
+ unsigned long lastlockflags; // their flags
+ void *lastirqunlocker[STEP_BACK_DEPTH]
+ ; // who last unlocked
+ unsigned long lastunlockflags; // their flags
+} lockcheck_state_t;
+
+
+static spinlock_t lock;
+
+static lockcheck_state_t state[NUM_LOCKS];
+
+static lockcheck_state_t *get_lock_entry(void)
+{
+ int i;
+ unsigned long f;
+ lockcheck_state_t *l;
+
+ spin_lock_irqsave(&lock,f);
+
+ for (i=0;i<NUM_LOCKS;i++) {
+ l=&(state[i]);
+ if (!(l->inuse)) {
+ l->inuse=1;
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&lock,f);
+
+ if (i<NUM_LOCKS) {
+ return l;
+ } else {
+ return 0;
+ }
+}
+
+
+static lockcheck_state_t *find_lock_entry(void *lock)
+{
+ int i;
+ lockcheck_state_t *l;
+
+ for (i=0;i<NUM_LOCKS;i++) {
+ l=&(state[i]);
+ if (l->inuse && l->lock == lock) {
+ return l;
+ }
+ }
+ return 0;
+}
+
+
+static void free_lock_entry(lockcheck_state_t *l)
+{
+ l->inuse=0;
+}
+
+
+
+void palacios_lockcheck_init()
+{
+ memset(state,0,sizeof(lockcheck_state_t)*NUM_LOCKS);
+ spin_lock_init(&lock);
+ DEBUG("LOCKCHECK: LOCK CHECKING INITED\n");
+}
+
+//
+// This needs to be defined explictly since the intrinsic does not take a var
+//
+#define backtrace(t) \
+ t[0]=__builtin_return_address(STEP_BACK_DEPTH_FIRST); \
+ t[1]=__builtin_return_address(STEP_BACK_DEPTH_FIRST+1); \
+ t[2]=__builtin_return_address(STEP_BACK_DEPTH_FIRST+2); \
+ t[3]=__builtin_return_address(STEP_BACK_DEPTH_FIRST+3);
+
+//
+// For printing a backtrace
+//
+//
+#define backtrace_format "%pS << %pS << %pS << %pS"
+#define backtrace_expand(t) ((t)[0]),((t)[1]),((t)[2]),((t)[3])
+
+
+static void clear_trace(void **trace)
+{
+ int i;
+
+ for (i=0;i<STEP_BACK_DEPTH;i++) {
+ trace[i]=0;
+ }
+}
+
+
+static void printlock(char *prefix, lockcheck_state_t *l)
+{
+ if (l->lock) {
+ DEBUG("LOCKCHECK: %s: lock 0x%p, allocator="
+ backtrace_format
+ ", lockcount=%d, lastlocker="
+ backtrace_format
+ ", lastunlocker="
+ backtrace_format
+ ", irqcount=%d, lastirqlocker="
+ backtrace_format
+ ", lastlockflags=%lu, lastirqunlocker="
+ backtrace_format
+ ", lastunlockflags=%lu\n",
+ prefix,l->lock,
+ backtrace_expand(l->allocator),
+ l->lockcount,
+ backtrace_expand(l->lastlocker),
+ backtrace_expand(l->lastunlocker),
+ l->irqcount,
+ backtrace_expand(l->lastirqlocker),
+ l->lastlockflags,
+ backtrace_expand(l->lastirqunlocker),
+ l->lastunlockflags);
+ }
+}
+
+
+
+static void find_multiple_locks_held(void)
+{
+ int i;
+ int have=0;
+ lockcheck_state_t *l;
+ char buf[64];
+
+ for (i=0;i<NUM_LOCKS;i++) {
+ l=&(state[i]);
+ if (l->inuse && l->lockcount>0) {
+ have++;
+ if (have>=WARN_MULTIPLE_THRESHOLD) {
+ break;
+ }
+ }
+ }
+
+ if (have>=WARN_MULTIPLE_THRESHOLD) {
+ have=0;
+ for (i=0;i<NUM_LOCKS;i++) {
+ l=&(state[i]);
+ if (l->inuse && l->lockcount>0) {
+ snprintf(buf,64,"MULTIPLE LOCKS HELD (%d)",have);
+ printlock(buf,l);
+ have++;
+ }
+ }
+ }
+
+}
+
+static void find_multiple_irqs_held(void)
+{
+ int i;
+ int have=0;
+ lockcheck_state_t *l;
+ char buf[64];
+
+ for (i=0;i<NUM_LOCKS;i++) {
+ l=&(state[i]);
+ if (l->inuse && l->irqcount>0) {
+ have++;
+ if (have>=WARN_MULTIPLE_THRESHOLD) {
+ break;
+ }
+ }
+ }
+
+ if (have>=WARN_MULTIPLE_THRESHOLD) {
+ have=0;
+ for (i=0;i<NUM_LOCKS;i++) {
+ l=&(state[i]);
+ if (l->inuse && l->irqcount>0) {
+ snprintf(buf,64,"MULTIPLE IRQS HELD (%d)",have);
+ printlock(buf,l);
+ have++;
+ }
+ }
+ }
+
+}
+
+
+void palacios_lockcheck_deinit()
+{
+ int i;
+ lockcheck_state_t *l;
+
+ for (i=0;i<NUM_LOCKS;i++) {
+ l=&(state[i]);
+ if (l->lock) {
+ printlock("ALLOCATED LOCK AT DEINIT",l);
+ if ((l->lockcount)) {
+ printlock("BAD LOCK COUNT AT DEINIT",l);
+ }
+ if ((l->irqcount)) {
+ printlock("BAD IRQ COUNT AT DEINIT",l);
+ }
+ }
+ }
+ INFO("LOCKCHECK: DEINITED\n");
+}
+
+
+void palacios_lockcheck_alloc(void *lock)
+{
+ lockcheck_state_t *l=get_lock_entry();
+
+ if (!l) {
+ DEBUG("LOCKCHECK: UNABLE TO ALLOCATE TRACKING DATA FOR LOCK 0x%p\n",lock);
+ }
+ l->lock=lock;
+ backtrace(l->allocator);
+ l->lockcount=l->irqcount=0;
+ clear_trace(l->lastlocker);
+ clear_trace(l->lastunlocker);
+ clear_trace(l->lastirqlocker);
+ clear_trace(l->lastirqunlocker);
+ //INFO("LOCKCHECK: LOCK ALLOCATE 0x%p\n",lock);
+ printlock("NEW LOCK", l);
+ //dump_stack();
+}
+
+void palacios_lockcheck_free(void *lock)
+{
+ lockcheck_state_t *l=find_lock_entry(lock);
+
+ if (!l){
+ DEBUG("LOCKCHECK: FREEING UNTRACKED LOCK 0x%p\n",lock);
+ return;
+ }
+
+ if ((l->lockcount)) {
+ printlock("BAD LOCK COUNT AT FREE",l);
+ }
+
+ if ((l->irqcount)) {
+ printlock("BAD IRQ COUNT AT FREE",l);
+ }
+ free_lock_entry(l);
+}
+
+void palacios_lockcheck_lock(void *lock)
+{
+ lockcheck_state_t *l=find_lock_entry(lock);
+
+ if (!l) {
+ DEBUG("LOCKCHECK: LOCKING UNTRACKED LOCK 0x%p\n",lock);
+ return;
+ }
+
+ if (l->lockcount!=0) {
+ printlock("BAD LOCKCOUNT AT LOCK",l);
+ }
+ if (l->irqcount!=0) {
+ printlock("BAD IRQCOUNT AT LOCK",l);
+ }
+
+ l->lockcount++;
+ backtrace(l->lastlocker);
+
+ find_multiple_locks_held();
+
+}
+void palacios_lockcheck_unlock(void *lock)
+{
+ lockcheck_state_t *l=find_lock_entry(lock);
+
+ if (!l) {
+ DEBUG("LOCKCHECK: UNLOCKING UNTRACKED LOCK 0x%p\n",lock);
+ return;
+ }
+
+ if (l->lockcount!=1) {
+ printlock("LOCKCHECK: BAD LOCKCOUNT AT UNLOCK",l);
+ }
+ if (l->irqcount!=0) {
+ printlock("LOCKCHECK: BAD IRQCOUNT AT UNLOCK",l);
+ }
+
+ l->lockcount--;
+ backtrace(l->lastunlocker);
+}
+
+void palacios_lockcheck_lock_irqsave(void *lock,unsigned long flags)
+{
+ lockcheck_state_t *l=find_lock_entry(lock);
+
+ if (!l) {
+ DEBUG("LOCKCHECK: IRQ LOCKING UNTRACKED LOCK 0x%p\n",lock);
+ return;
+ }
+
+ if (l->lockcount!=0) {
+ printlock("BAD LOCKCOUNT AT IRQ LOCK",l);
+ }
+ if (l->irqcount!=0) {
+ printlock("BAD IRQCOUNT AT IRQ LOCK",l);
+ }
+
+ l->irqcount++;
+ l->lastlockflags=flags;
+ backtrace(l->lastirqlocker);
+
+
+ find_multiple_irqs_held();
+
+}
+
+void palacios_lockcheck_unlock_irqrestore(void *lock,unsigned long flags)
+{
+ lockcheck_state_t *l=find_lock_entry(lock);
+
+ if (!l) {
+ DEBUG("LOCKCHECK: IRQ UNLOCKING UNTRACKED LOCK 0x%p\n",lock);
+ return;
+ }
+
+ if (l->lockcount!=0) {
+ printlock("LOCKCHECK: BAD LOCKCOUNT AT IRQ UNLOCK",l);
+ }
+ if (l->irqcount!=1) {
+ printlock("LOCKCHECK: BAD IRQCOUNT AT IRQ UNLOCK",l);
+ }
+
+ l->irqcount--;
+ l->lastunlockflags = flags;
+ backtrace(l->lastirqunlocker);
+
+}