USER_PANIC("Network polling not available without Arranet!\n");
}
+ void poll_ahci(struct waitset_chanstate *) __attribute__((weak));
+ void poll_ahci(struct waitset_chanstate *chan)
+ {
+ errval_t err = waitset_chan_trigger(chan);
+ assert(err_is_ok(err)); // should not be able to fail
+ }
+
-/// Helper function that knows how to poll the given channel, based on its type
-static void poll_channel(struct waitset_chanstate *chan)
-{
- switch (chan->chantype) {
+/// Check polled channels
+void poll_channels_disabled(dispatcher_handle_t handle) {
+ struct dispatcher_generic *dp = get_dispatcher_generic(handle);
+ struct waitset_chanstate *chan;
+
+ if (!dp->polled_channels)
+ return;
+ chan = dp->polled_channels;
+ do {
+ switch (chan->chantype) {
#ifdef CONFIG_INTERCONNECT_DRIVER_UMP
- case CHANTYPE_UMP_IN:
- ump_endpoint_poll(chan);
- break;
+ case CHANTYPE_UMP_IN: {
+ if (ump_endpoint_poll(chan)) {
+ errval_t err = waitset_chan_trigger_disabled(chan, handle);
+ assert(err_is_ok(err)); // should not fail
+ if (!dp->polled_channels) // restart scan
+ return;
+ chan = dp->polled_channels;
+ continue;
+ } else
+ chan = chan->polled_next;
+ } break;
#endif // CONFIG_INTERCONNECT_DRIVER_UMP
-
- case CHANTYPE_LWIP_SOCKET:
- arranet_polling_loop_proxy();
- break;
-
- case CHANTYPE_AHCI:
- poll_ahci(chan);
- break;
-
- default:
- assert(!"invalid channel type to poll!");
- }
+ case CHANTYPE_LWIP_SOCKET:
+ arranet_polling_loop_proxy();
+ break;
-
++ case CHANTYPE_AHCI:
++ poll_ahci(chan);
++ break;
+ default:
+ assert(!"invalid channel type to poll!");
+ }
+ } while (chan != dp->polled_channels);
}
-// pollcycles_*: arch-specific implementation for polling.
-// Used by get_next_event().
-//
-// pollcycles_reset() -- return the number of pollcycles we want to poll for
-// pollcycles_update() -- update the pollcycles variable. This is needed for
-// implementations where we don't have a cycle counter
-// and we just count the number of polling operations
-// performed
-// pollcycles_expired() -- check if pollcycles have expired
-//
-// We might want to move them to architecture-specific files, and/or create a
-// cleaner interface. For now, I just wanted to keep them out of
-// get_next_event()
-
-#if defined(__ARM_ARCH_7A__) && defined(__GNUC__) \
- && __GNUC__ == 4 && __GNUC_MINOR__ <= 6 && __GNUC_PATCHLEVEL__ <= 3
-static __attribute__((noinline, unused))
-#else
-static inline
-#endif
-cycles_t pollcycles_reset(void)
+/// Re-register a channel (if persistent)
+static void reregister_channel(struct waitset *ws, struct waitset_chanstate *chan,
+ dispatcher_handle_t handle)
{
- cycles_t pollcycles;
-#if defined(__arm__) && !defined(__gem5__)
- reset_cycle_counter();
- pollcycles = waitset_poll_cycles;
-#elif defined(__arm__) && defined(__gem5__)
- pollcycles = 0;
-#elif defined(__aarch64__) && defined(__gem5__)
- pollcycles = 0;
-#else
- pollcycles = cyclecount() + waitset_poll_cycles;
-#endif
- return pollcycles;
-}
+ assert(chan->waitset == ws);
+ if (chan->state == CHAN_PENDING) {
+ dequeue(&ws->pending, chan);
+ } else {
+ assert(chan->state == CHAN_WAITING);
+ dequeue(&ws->waiting, chan);
+ }
-#if defined(__ARM_ARCH_7A__) && defined(__GNUC__) \
- && __GNUC__ == 4 && __GNUC_MINOR__ <= 6 && __GNUC_PATCHLEVEL__ <= 3
-static __attribute__((noinline, unused))
-#else
-static inline
-#endif
-cycles_t pollcycles_update(cycles_t pollcycles)
-{
- cycles_t ret = pollcycles;
- #if defined(__arm__) && defined(__gem5__)
- ret++;
- #elif defined(__aarch64__) && defined(__gem5__)
- ret++;
- #endif
- return ret;
+ chan->token = 0;
- if (chan->chantype == CHANTYPE_UMP_IN) {
++ if (chan->chantype == CHANTYPE_UMP_IN
++ || chan->chantype == CHANTYPE_LWIP_SOCKET
++ || chan->chantype == CHANTYPE_AHCI) {
+ enqueue(&ws->polled, chan);
+ enqueue_polled(&get_dispatcher_generic(handle)->polled_channels, chan);
+ chan->state = CHAN_POLLED;
+ } else {
+ enqueue(&ws->idle, chan);
+ chan->state = CHAN_IDLE;
+ }
}
-#if defined(__ARM_ARCH_7A__) && defined(__GNUC__) \
- && __GNUC__ == 4 && __GNUC_MINOR__ <= 6 && __GNUC_PATCHLEVEL__ <= 3
-static __attribute__((noinline, unused))
-#else
-static inline
-#endif
-bool pollcycles_expired(cycles_t pollcycles)
+/// Find a thread that is able to receive an event
+static struct thread * find_recipient(struct waitset *ws,
+ struct waitset_chanstate *channel, struct thread *me)
{
- bool ret;
- #if defined(__arm__) && !defined(__gem5__)
- ret = (cyclecount() > pollcycles || is_cycle_counter_overflow());
- #elif defined(__arm__) && defined(__gem5__)
- ret = pollcycles >= POLL_COUNT;
- #elif defined(__aarch64__) && defined(__gem5__)
- ret = pollcycles >= POLL_COUNT;
- #else
- ret = cyclecount() > pollcycles;
- #endif
- return ret;
+ struct thread *t = ws->waiting_threads;
+
+ if (!t)
+ return NULL;
+ do {
+ if (waitset_check_token(channel, t))
+ return t;
+ t = t->next;
+ } while (t != ws->waiting_threads);
+ return ws->waiting_threads;
}
-static errval_t get_next_event_debug(struct waitset *ws,
- struct event_closure *retclosure, bool debug)
+/// Wake up other thread if there's more pending events
+static void wake_up_other_thread(dispatcher_handle_t handle, struct waitset *ws)
{
- struct waitset_chanstate *chan;
- bool was_polling = false;
- cycles_t pollcycles;
-
- assert(ws != NULL);
- assert(retclosure != NULL);
-
- // unconditionally disable ourselves and check for events
- // if we decide we have to start polling, we'll jump back up here
- goto check_for_events;
-
- /* ------------ POLLING LOOP; RUNS WHILE ENABLED ------------ */
-polling_loop:
- was_polling = true;
- assert(ws->polling); // this thread is polling
- // get the amount of cycles we want to poll for
- pollcycles = pollcycles_reset();
-
- // while there are no pending events, poll channels
- while (ws->polled != NULL && ws->pending == NULL) {
- struct waitset_chanstate *nextchan = NULL;
- // NB: Polling policy is to return as soon as a pending event
- // appears, not bother looking at the rest of the polling queue
- for (chan = ws->polled;
- chan != NULL && chan->waitset == ws && chan->state == CHAN_POLLED
- && ws->pending == NULL;
- chan = nextchan) {
-
- nextchan = chan->next;
- poll_channel(chan);
- // update pollcycles
- pollcycles = pollcycles_update(pollcycles);
- // yield the thread if we exceed the cycle count limit
- if (ws->pending == NULL && pollcycles_expired(pollcycles)) {
- if (debug) {
- if (strcmp(disp_name(), "netd") != 0) {
- // Print the callback trace so that we know which call is leading
- // the schedule removal and
- printf("%s: callstack: %p %p %p %p\n", disp_name(),
- __builtin_return_address(0),
- __builtin_return_address(1),
- __builtin_return_address(2),
- __builtin_return_address(3));
- }
-
- }
- thread_yield();
- pollcycles = pollcycles_reset();
- }
- }
+ if (ws->pending && ws->waiting_threads) {
+ struct thread *t;
- // ensure that we restart polling from the place we left off here,
- // if the next channel is a valid one
- if (nextchan != NULL && nextchan->waitset == ws
- && nextchan->state == CHAN_POLLED) {
- ws->polled = nextchan;
- }
+ t = thread_unblock_one_disabled(handle, &ws->waiting_threads, NULL);
+ assert_disabled(t == NULL); // shouldn't see a remote thread
}
+}
- /* ------------ STATE MACHINERY; RUNS WHILE DISABLED ------------ */
-check_for_events: ;
- dispatcher_handle_t handle = disp_disable();
+/**
+ * \brief Get next pending event
+ *
+ * Check if there is a pending event that matches current thread and return it.
+ * Pending events are in a pending queue and in a waiting queue.
+ * A pending event then will be removed from a pending/waiting queue and become
+ * unregistered or, if it's persistent, will be re-registered to an idle queue
+ * or a polled queue (UMP channels) of a waitset.
+ * If there's no pending event, block this thread.
+ * If there's a pending event but it doesn't match our thread, don't remove it
+ * from a pending queue and wake up a matching thread.
+ * If there's no matching thread, add it to a waiting queue.
+ *
+ * \param ws Waitset with sources of events
+ * \param retchannel Holder of returned event
+ * \param retclosure Holder of returned closure
+ * \param waitfor Specific event that we're waiting for (can be NULL)
+ * \param handle Dispatcher's handle
+ * \param debug Debug mode (not used)
+ */
- // are there any pending events on the waitset?
- chan = get_pending_event_disabled(ws);
- if (chan != NULL) {
- // if we need to poll, and we have a blocked thread, wake it up to do so
- if (was_polling && ws->polled != NULL && ws->waiting_threads != NULL) {
- // start a blocked thread polling
- struct thread *t;
- t = thread_unblock_one_disabled(handle, &ws->waiting_threads, NULL);
- assert_disabled(t == NULL); // shouldn't see a remote thread
- } else if (was_polling) {
- // I'm stopping polling, and there is nobody else
- assert_disabled(ws->polling);
- ws->polling = false;
+errval_t get_next_event_disabled(struct waitset *ws,
+ struct waitset_chanstate **retchannel, struct event_closure *retclosure,
+ struct waitset_chanstate *waitfor, dispatcher_handle_t handle, bool debug)
+{
+ struct waitset_chanstate * chan;
+
+ for (;;) {
+ chan = get_pending_event_disabled(ws, waitfor); // get our event
+ if (chan) {
+ *retchannel = chan;
+ *retclosure = chan->closure;
+ chan->wait_for = NULL;
+ chan->token = 0;
+ if (chan->persistent)
+ reregister_channel(ws, chan, handle);
+ else
+ waitset_chan_deregister_disabled(chan, handle);
+ wake_up_other_thread(handle, ws);
+ return SYS_ERR_OK;
}
- disp_enable(handle);
-
- *retclosure = chan->closure;
- return SYS_ERR_OK;
- }
-
- // If we got here and there are channels to poll but no-one is polling,
- // then either we never polled, or we lost a race on the channel we picked.
- // Either way, we'd better start polling again.
- if (ws->polled != NULL && (was_polling || !ws->polling)) {
- if (!was_polling) {
- ws->polling = true;
+ chan = ws->pending; // check a pending queue
+ if (!chan) { // if nothing then wait
+ thread_block_disabled(handle, &ws->waiting_threads);
+ disp_disable();
+ } else { // something but it's not our event
+ if (!ws->waiting_threads) { // no other thread interested in
+ dequeue(&ws->pending, chan);
+ enqueue(&ws->waiting, chan);
+ chan->state = CHAN_WAITING;
+ chan->waitset = ws;
+ } else {
+ // find a matching thread
+ struct thread *t;
+ for (t = ws->waiting_threads; t; ) {
+ if (waitset_check_token(chan, t)) { // match found, wake it
+ ws->waiting_threads = t;
+ t = thread_unblock_one_disabled(handle,
+ &ws->waiting_threads, chan);
+ assert_disabled(t == NULL); // shouldn't see a remote thread
+ break;
+ }
+ t = t->next;
+ if (t == ws->waiting_threads) { // no recipient found
+ dequeue(&ws->pending, chan);
+ enqueue(&ws->waiting, chan);
+ chan->state = CHAN_WAITING;
+ chan->waitset = ws;
+ break;
+ }
+ }
+ }
}
- disp_enable(handle);
- goto polling_loop;
- }
-
- // otherwise block awaiting an event
- chan = thread_block_disabled(handle, &ws->waiting_threads);
-
- if (chan == NULL) {
- // not a real event, just a wakeup to get us to start polling!
- assert(ws->polling);
- goto polling_loop;
- } else {
- *retclosure = chan->closure;
- return SYS_ERR_OK;
}
}