Track errno and errstr in the kthread
[akaros.git] / kern / src / ucq.c
index fe4e4c5..fc456f4 100644 (file)
@@ -20,8 +20,11 @@ void send_ucq_msg(struct ucq *ucq, struct proc *p, struct event_msg *msg)
 
        assert(is_user_rwaddr(ucq, sizeof(struct ucq)));
        /* So we can try to send ucqs to _Ss before they initialize */
-       if (!ucq->ucq_ready)
+       if (!ucq->ucq_ready) {
+               if (__proc_is_mcp(p))
+                       warn("proc %d is _M with an uninitialized ucq %p\n", p->pid, ucq);
                return;
+       }
        /* Bypass fetching/incrementing the counter if we're overflowing, helps
         * prevent wraparound issues on the counter (only 12 bits of counter) */
        if (ucq->prod_overflow)
@@ -36,9 +39,9 @@ void send_ucq_msg(struct ucq *ucq, struct proc *p, struct event_msg *msg)
        if (PGOFF(my_slot) > 3000)
                warn("Abnormally high counter, there's probably something wrong!");
 grab_lock:
-       /* TODO: use a hashlock, instead of the proc lock.  think about irqsave.  do
-        * we send events from IRQ context?  The proc_lock isn't irqsave */
-       spin_lock(&p->proc_lock);
+       /* Lock, for this proc/ucq.  Using an irqsave, since we may want to send ucq
+        * messages from irq context. */
+       hash_lock_irqsave(p->ucq_hashlock, (long)ucq);
        /* Grab a potential slot (again, preventing another DoS) */
        my_slot = (uintptr_t)atomic_fetch_and_add(&ucq->prod_idx, 1);
        if (slot_is_good(my_slot))
@@ -59,12 +62,15 @@ grab_lock:
        if (!new_page) {
                /* Warn if we have a ridiculous amount of pages in the ucq */
                if (atomic_fetch_and_add(&ucq->nr_extra_pgs, 1) > UCQ_WARN_THRESH)
-                       warn("Over %d pages in ucq %08p!\n", UCQ_WARN_THRESH, ucq);
-               /* TODO: use a proc_lock/mm_lock or call the external one.  Need to do
-                * this for now since we don't have hash locks yet HASH LOCK */
-               new_page = (struct ucq_page*)__do_mmap(p, 0, PGSIZE,
-                                                      PROT_READ | PROT_WRITE,
-                                                      MAP_ANON | MAP_POPULATE, 0, 0);
+                       warn("Over %d pages in ucq %p for pid %d!\n", UCQ_WARN_THRESH,
+                            ucq, p->pid);
+               /* Giant warning: don't ask for anything other than anonymous memory at
+                * a non-fixed location.  o/w, it may cause a TLB shootdown, which grabs
+                * the proc_lock, and potentially deadlock the system. */
+               new_page = (struct ucq_page*)do_mmap(p, 0, PGSIZE,
+                                                    PROT_READ | PROT_WRITE,
+                                                    MAP_ANONYMOUS | MAP_POPULATE |
+                                                    MAP_PRIVATE, NULL, 0);
                assert(new_page);
                assert(!PGOFF(new_page));
        } else {
@@ -88,7 +94,7 @@ unlock_lock:
        /* At this point, any normal (non-locking) producers can succeed in getting
         * a slot.  The ones that failed earlier will fight for the lock, then
         * quickly proceed when they get a good slot */
-       spin_unlock(&p->proc_lock);     /* TODO HASH LOCK */
+       hash_unlock_irqsave(p->ucq_hashlock, (long)ucq);
        /* Fall through to having a slot */
 have_slot:
        /* Sanity check on our slot. */
@@ -110,7 +116,7 @@ error_addr_unlock:
        /* Had a bad addr while holding the lock.  This is a bit more serious */
        warn("Bad addr in ucq page management!");
        ucq->prod_overflow = FALSE;
-       spin_unlock(&p->proc_lock);     /* TODO HASH LOCK */
+       hash_unlock_irqsave(p->ucq_hashlock, (long)ucq);
        /* Fall-through to normal error out */
 error_addr:
        warn("Invalid user address, not sending a message");
@@ -120,3 +126,37 @@ error_addr:
         * itself. */
        return;
 }
+
+/* Debugging */
+#include <smp.h>
+#include <pmap.h>
+
+/* Prints the status and up to 25 of the previous messages for the UCQ. */
+void print_ucq(struct proc *p, struct ucq *ucq)
+{
+       struct ucq_page *ucq_pg;
+       uintptr_t old_proc = switch_to(p);
+
+       printk("UCQ %p\n", ucq);
+       printk("prod_idx: %p, cons_idx: %p\n", atomic_read(&ucq->prod_idx),
+              atomic_read(&ucq->cons_idx));
+       printk("spare_pg: %p, nr_extra_pgs: %d\n", atomic_read(&ucq->spare_pg),
+              atomic_read(&ucq->nr_extra_pgs));
+       printk("prod_overflow: %d\n", ucq->prod_overflow);
+       /* Try to see our previous ucqs */
+       for (uintptr_t i = atomic_read(&ucq->prod_idx), count = 0;
+            slot_is_good(i) && count < 25;  i--, count++) {
+               /* only attempt to print messages on the same page */
+               if (PTE_ADDR(i) != PTE_ADDR(atomic_read(&ucq->prod_idx)))
+                       break;
+               printk("Prod idx %p message ready is %p\n", i, slot2msg(i)->ready);
+       }
+       /* look at the chain, starting from cons_idx */
+       ucq_pg = (struct ucq_page*)PTE_ADDR(atomic_read(&ucq->cons_idx));
+       for (int i = 0; i < 10 && ucq_pg; i++) {
+               printk("#%02d: Cons page: %p, nr_cons: %d, next page: %p\n", i,
+                      ucq_pg, ucq_pg->header.nr_cons, ucq_pg->header.cons_next_pg);
+               ucq_pg = (struct ucq_page*)(ucq_pg->header.cons_next_pg);
+       }
+       switch_back(p, old_proc);
+}