int main(int argc, char** argv) { /* this program should only be started from the kernel for tests */ printf("[user] Attempting to read ucq messages from test_ucq(). " "Don't call this manually.\n"); /* Map into a known, extremely ghetto location. The kernel knows to look * here. */ struct ucq *ucq = mmap((void*)USTACKTOP, PGSIZE, PROT_WRITE | PROT_READ, MAP_POPULATE, -1, 0); assert((uintptr_t)ucq == USTACKTOP); /* Now init it */ uintptr_t two_pages = (uintptr_t)mmap(0, PGSIZE * 2, PROT_WRITE | PROT_READ, MAP_POPULATE | MAP_ANONYMOUS, -1, 0); assert(two_pages); ucq_init_raw(ucq, two_pages, two_pages + PGSIZE); printf("[user] UCQ %08p initialized\n", ucq); /* try to get a simple message */ struct event_msg msg; /* 1: Spin til we can get a message (0 on success breaks) */ while (get_ucq_msg(ucq, &msg)) cpu_relax(); printf("[user] Got simple message type %d(7) with A2 %08p(0xdeadbeef)\n", msg.ev_type, msg.ev_arg2); /* 2: get a bunch */ for (int i = 0; i < 5000; i++) { while (get_ucq_msg(ucq, &msg)) cpu_relax(); assert(msg.ev_type == i); } printf("[user] #2 Received a bunch! Last one was %d(4999), " "extra pages %d(6, if #3 is 1000 and was blasted already)\n", msg.ev_type, atomic_read(&ucq->nr_extra_pgs)); /* 3: test chaining */ while (atomic_read(&ucq->nr_extra_pgs) < 2) cpu_relax(); printf("[user] #3 There's now a couple pages (%d), trying to receive...\n", atomic_read(&ucq->nr_extra_pgs)); /* this assumes 1000 is enough for a couple pages */ for (int i = 0; i < 1000; i++) { while (get_ucq_msg(ucq, &msg)) cpu_relax(); assert(msg.ev_type == i); } printf("[user] Done, extra pages: %d(0)\n", atomic_read(&ucq->nr_extra_pgs)); int extra = 0; while (!get_ucq_msg(ucq, &msg)) { printf("[user] got %d extra messages in the ucq, type %d\n", ++extra, msg.ev_type); } printf("[user] Spinning...\n"); while(1); return 0; }
/* Attempts to handle a message. Returns 1 if we dequeued a msg, 0 o/w. */ int handle_one_mbox_msg(struct event_mbox *ev_mbox) { struct event_msg local_msg; unsigned int ev_type; /* get_ucq returns 0 on success, -1 on empty */ if (get_ucq_msg(&ev_mbox->ev_msgs, &local_msg) == -1) return 0; ev_type = local_msg.ev_type; assert(ev_type < MAX_NR_EVENT); printd("[event] UCQ (mbox %08p), ev_type: %d\n", ev_mbox, ev_type); if (ev_handlers[ev_type]) ev_handlers[ev_type](&local_msg, ev_type); return 1; }
/* Somewhat ghetto helper, for the lazy. If all you care about is an event * number, this will see if the event happened or not. It will try for a * message, but if there is none, it will go for a bit. Note that multiple * bit messages will turn into just one bit. */ unsigned int get_event_type(struct event_mbox *ev_mbox) { struct event_msg local_msg = {0}; /* UCQ returns 0 on success, so this will dequeue and return the type. */ if (!get_ucq_msg(&ev_mbox->ev_msgs, &local_msg)) { return local_msg.ev_type; } if (BITMASK_IS_CLEAR(&ev_mbox->ev_bitmap, MAX_NR_EVENT)) return EV_NONE; /* aka, 0 */ for (int i = 0; i < MAX_NR_EVENT; i++) { if (GET_BITMASK_BIT(ev_mbox->ev_bitmap, i)) { CLR_BITMASK_BIT_ATOMIC(ev_mbox->ev_bitmap, i); return i; } } return EV_NONE; }