Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Lock checking framework and cleanup of linux module code to use palacios interfaces...
[palacios.git] / linux_module / lockcheck.c
diff --git a/linux_module/lockcheck.c b/linux_module/lockcheck.c
new file mode 100644 (file)
index 0000000..1c55e0e
--- /dev/null
@@ -0,0 +1,366 @@
+#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);
+  
+}