Rename RCU CB context to 'cannot block' context
[akaros.git] / kern / src / atomic.c
index 6a30d8f..70d2805 100644 (file)
@@ -1,7 +1,3 @@
-#ifdef __SHARC__
-#pragma nosharc
-#endif
-
 #include <arch/arch.h>
 #include <arch/kdebug.h>
 
@@ -12,6 +8,8 @@
 #include <assert.h>
 #include <hashtable.h>
 #include <smp.h>
+#include <kmalloc.h>
+#include <kdebug.h>
 
 static void increase_lock_depth(uint32_t coreid)
 {
@@ -23,46 +21,98 @@ static void decrease_lock_depth(uint32_t coreid)
        per_cpu_info[coreid].lock_depth--;
 }
 
-#ifdef __CONFIG_SPINLOCK_DEBUG__
+#ifdef CONFIG_SPINLOCK_DEBUG
+
+/* Put locks you want to ignore here. */
+static uintptr_t blacklist_locks[] = {
+       //0xffffffffc03bd000,
+};
+
+/* Could do this on the output side, though noisly locks will crowd us out */
+static bool can_trace(spinlock_t *lock)
+{
+       for (int i = 0; i < ARRAY_SIZE(blacklist_locks); i++) {
+               if (blacklist_locks[i] == (uintptr_t)lock)
+                       return FALSE;
+       }
+       return TRUE;
+}
+
+/* spinlock and trylock call this after locking */
+static __always_inline void post_lock(spinlock_t *lock, uint32_t coreid)
+{
+       struct per_cpu_info *pcpui = &per_cpu_info[coreid];
+       if ((pcpui->__lock_checking_enabled == 1) && can_trace(lock))
+               pcpui_trace_locks(pcpui, lock);
+       lock->call_site = get_caller_pc();
+       lock->calling_core = coreid;
+       /* TODO consider merging this with __ctx_depth (unused field) */
+       increase_lock_depth(lock->calling_core);
+}
+
 void spin_lock(spinlock_t *lock)
 {
-       uint32_t coreid = core_id();
+       uint32_t coreid = core_id_early();
        struct per_cpu_info *pcpui = &per_cpu_info[coreid];
        /* Short circuit our lock checking, so we can print or do other things to
-        * announce the failure that require locks. */
-       if (pcpui->__lock_depth_disabled)
+        * announce the failure that require locks.  Also avoids anything else
+        * requiring pcpui initialization. */
+       if (pcpui->__lock_checking_enabled != 1)
                goto lock;
        if (lock->irq_okay) {
                if (!can_spinwait_irq(pcpui)) {
-                       pcpui->__lock_depth_disabled++;
+                       pcpui->__lock_checking_enabled--;
                        print_kctx_depths("IRQOK");
-                       panic("Lock %08p tried to spin when it shouldn't\n", lock);
-                       pcpui->__lock_depth_disabled--;
+                       panic("Lock %p tried to spin when it shouldn't\n", lock);
+                       pcpui->__lock_checking_enabled++;
                }
        } else {
                if (!can_spinwait_noirq(pcpui)) {
-                       pcpui->__lock_depth_disabled++;
+                       pcpui->__lock_checking_enabled--;
                        print_kctx_depths("NOIRQ");
-                       panic("Lock %08p tried to spin when it shouldn't\n", lock);
-                       pcpui->__lock_depth_disabled--;
+                       panic("Lock %p tried to spin when it shouldn't\n", lock);
+                       pcpui->__lock_checking_enabled++;
                }
        }
 lock:
        __spin_lock(lock);
-       lock->call_site = get_caller_pc();
-       lock->calling_core = coreid;
-       /* TODO consider merging this with __ctx_depth (unused field) */
-       increase_lock_depth(lock->calling_core);
        /* Memory barriers are handled by the particular arches */
+       post_lock(lock, coreid);
+}
+
+/* Trylock doesn't check for irq/noirq, in case we want to try and lock a
+ * non-irqsave lock from irq context. */
+bool spin_trylock(spinlock_t *lock)
+{
+       uint32_t coreid = core_id_early();
+       bool ret = __spin_trylock(lock);
+       if (ret)
+               post_lock(lock, coreid);
+       return ret;
 }
 
 void spin_unlock(spinlock_t *lock)
 {
        decrease_lock_depth(lock->calling_core);
        /* Memory barriers are handled by the particular arches */
+       assert(spin_locked(lock));
        __spin_unlock(lock);
 }
-#endif /* __CONFIG_SPINLOCK_DEBUG__ */
+
+void spinlock_debug(spinlock_t *lock)
+{
+       uintptr_t pc = lock->call_site;
+
+       if (!pc) {
+               printk("Lock %p: never locked\n", lock);
+               return;
+       }
+       printk("Lock %p: currently %slocked.  Last locked at [<%p>] in %s on core %d\n",
+              lock, spin_locked(lock) ? "" : "un", pc, get_fn_name(pc),
+              lock->calling_core);
+}
+
+#endif /* CONFIG_SPINLOCK_DEBUG */
 
 /* Inits a hashlock. */
 void hashlock_init(struct hashlock *hl, unsigned int nr_entries)
@@ -176,7 +226,7 @@ int commit_checklist_wait(checklist_t* list, checklist_mask_t* mask)
                cpu_relax();
 
        // list is ours and clear, set it to the settings of our list
-       COPY_BITMASK(list->mask.bits, mask->bits, mask->size); 
+       COPY_BITMASK(list->mask.bits, mask->bits, mask->size);
        return 0;
 }
 
@@ -193,21 +243,21 @@ int commit_checklist_nowait(checklist_t* list, checklist_mask_t* mask)
 // what if two different actors are waiting on the list, but for different reasons?
 // part of the problem is we are doing both set and check via the same path
 //
-// aside: we made this a lot more difficult than the usual barriers or even 
+// aside: we made this a lot more difficult than the usual barriers or even
 // the RCU grace-period checkers, since we have to worry about this construct
 // being used by others before we are done with it.
 //
 // how about this: if we want to wait on this later, we just don't release the
 // lock.  if we release it, then we don't care who comes in and grabs and starts
-// checking the list.  
-//     - regardless, there are going to be issues with people looking for a free 
-//     item.  even if they grab the lock, they may end up waiting a while and 
-//     wantint to bail (like test for a while, give up, move on, etc).  
+// checking the list.
+//     - regardless, there are going to be issues with people looking for a free
+//     item.  even if they grab the lock, they may end up waiting a while and
+//     wantint to bail (like test for a while, give up, move on, etc).
 //     - still limited in that only the setter can check, and only one person
 //     can spinwait / check for completion.  if someone else tries to wait (wanting
 //     completion), they may miss it if someone else comes in and grabs the lock
 //     to use it for a new checklist
-//             - if we had the ability to sleep and get woken up, we could have a 
+//             - if we had the ability to sleep and get woken up, we could have a
 //             queue.  actually, we could do a queue anyway, but they all spin
 //             and it's the bosses responsibility to *wake* them
 
@@ -244,6 +294,12 @@ int checklist_is_clear(checklist_t* list)
        return BITMASK_IS_CLEAR(list->mask.bits, list->mask.size);
 }
 
+// no synch guarantees - just looks at the list
+int checklist_is_full(checklist_t* list)
+{
+       return BITMASK_IS_FULL(list->mask.bits, list->mask.size);
+}
+
 // no synch guarantees - just resets the list to empty
 void reset_checklist(checklist_t* list)
 {