summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorMike Belopuhov <mikeb@cvs.openbsd.org>2017-02-08 16:15:53 +0000
committerMike Belopuhov <mikeb@cvs.openbsd.org>2017-02-08 16:15:53 +0000
commit831d3b3ec9b67afc77d3f6d1a829e501cc02322d (patch)
tree0912b5ba218915bd14fefc9eef130b151127de6a /sys/dev
parenta4d04d4c3b95eb335f51ce7f72b13190bd46c4a2 (diff)
Introduce Xen interrupt barriers
intr_barrier(9) is useful to make sure that after an interrupt is masked, the interrupt handler for the device has finished executing before proceeding with further device configuration. However, since Xen interrupt handlers run in the thread context, we need to make sure that they have finished as well. By scheduling a xen_barrier_task modelled after (or rather copied ;) ifq_barrier_task we can ensure that the interrupt handler is no longer running.
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/pv/xen.c45
-rw-r--r--sys/dev/pv/xenvar.h3
2 files changed, 46 insertions, 2 deletions
diff --git a/sys/dev/pv/xen.c b/sys/dev/pv/xen.c
index 1e93de05e83..3ceda5540a7 100644
--- a/sys/dev/pv/xen.c
+++ b/sys/dev/pv/xen.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: xen.c,v 1.77 2017/02/08 16:08:06 mikeb Exp $ */
+/* $OpenBSD: xen.c,v 1.78 2017/02/08 16:15:52 mikeb Exp $ */
/*
* Copyright (c) 2015 Mike Belopuhov
@@ -696,6 +696,49 @@ xen_intr_schedule(xen_intr_handle_t xih)
}
}
+static void
+xen_barrier_task(void *arg)
+{
+ int *notdone = arg;
+
+ *notdone = 0;
+ wakeup_one(notdone);
+}
+
+/*
+ * This code achieves two goals: 1) makes sure that *after* masking
+ * the interrupt source we're not getting more task_adds: intr_barrier
+ * will take care of that, and 2) makes sure that the interrupt task
+ * is finished executing the current task and won't be called again:
+ * it sets up a barrier task to await completion of the current task
+ * and relies on the interrupt masking to prevent submission of new
+ * tasks in the future.
+ */
+void
+xen_intr_barrier(xen_intr_handle_t xih)
+{
+ struct xen_softc *sc = xen_sc;
+ struct xen_intsrc *xi;
+ struct sleep_state sls;
+ int notdone = 1;
+ struct task t = TASK_INITIALIZER(xen_barrier_task, &notdone);
+
+ /*
+ * XXX This will need to be revised once intr_barrier starts
+ * using an argument.
+ */
+ intr_barrier(NULL);
+
+ if ((xi = xen_intsrc_acquire(sc, (evtchn_port_t)xih)) != NULL) {
+ task_add(xi->xi_taskq, &t);
+ while (notdone) {
+ sleep_setup(&sls, &notdone, PWAIT, "xenbar");
+ sleep_finish(&sls, notdone);
+ }
+ xen_intsrc_release(sc, xi);
+ }
+}
+
void
xen_intr_signal(xen_intr_handle_t xih)
{
diff --git a/sys/dev/pv/xenvar.h b/sys/dev/pv/xenvar.h
index 48c0a6b3d46..61a4dcb920b 100644
--- a/sys/dev/pv/xenvar.h
+++ b/sys/dev/pv/xenvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: xenvar.h,v 1.48 2017/02/06 21:58:29 mikeb Exp $ */
+/* $OpenBSD: xenvar.h,v 1.49 2017/02/08 16:15:52 mikeb Exp $ */
/*
* Copyright (c) 2015 Mike Belopuhov
@@ -144,6 +144,7 @@ void xen_intr(void);
void xen_intr_ack(void);
void xen_intr_signal(xen_intr_handle_t);
void xen_intr_schedule(xen_intr_handle_t);
+void xen_intr_barrier(xen_intr_handle_t);
int xen_intr_establish(evtchn_port_t, xen_intr_handle_t *, int,
void (*)(void *), void *, char *);
int xen_intr_disestablish(xen_intr_handle_t);