summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Gerdts <mike.gerdts@joyent.com>2018-03-16 20:42:29 +0000
committerMike Gerdts <mike.gerdts@joyent.com>2018-03-23 16:25:44 +0000
commit7892cb3dceef9f2cbcd853e66fb05601a94bbba2 (patch)
tree21b5151c94e3edfd63934d6ca52659ae42702057
parent929399046cc464d1c38a4d4facf93476166a5662 (diff)
downloadillumos-joyent-7892cb3dceef9f2cbcd853e66fb05601a94bbba2.tar.gz
OS-6720 uart-ignorant guest sends bhyve into a spin
OS-6715 extraneous rxfifo_available() in uart_sock_drain() Reviewed by: Patrick Mooney <patrick.mooney@joyent.com> Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com> Reviewed by: John Levon <john.levon@joyent.com> Approved by: John Levon <john.levon@joyent.com>
-rw-r--r--usr/contrib/freebsd/sys/queue.h787
-rw-r--r--usr/src/cmd/bhyve/Makefile33
-rw-r--r--usr/src/cmd/bhyve/bhyverun.c9
-rw-r--r--usr/src/cmd/bhyve/mevent.c199
-rw-r--r--usr/src/cmd/bhyve/mevent_test.c28
-rw-r--r--usr/src/cmd/bhyve/test/Makefile18
-rw-r--r--usr/src/cmd/bhyve/test/Makefile.com60
-rw-r--r--usr/src/cmd/bhyve/test/Makefile.subdirs29
-rw-r--r--usr/src/cmd/bhyve/test/Makefile.targ55
-rw-r--r--usr/src/cmd/bhyve/test/scripts/Makefile28
-rw-r--r--usr/src/cmd/bhyve/test/scripts/bhyvetest.ksh231
-rw-r--r--usr/src/cmd/bhyve/test/tst/Makefile18
-rw-r--r--usr/src/cmd/bhyve/test/tst/mevent/Makefile30
-rw-r--r--usr/src/cmd/bhyve/test/tst/mevent/lists.delete.c172
-rw-r--r--usr/src/cmd/bhyve/test/tst/mevent/mevent.c57
-rw-r--r--usr/src/cmd/bhyve/test/tst/mevent/read.disable.c163
-rw-r--r--usr/src/cmd/bhyve/test/tst/mevent/read.pause.c152
-rw-r--r--usr/src/cmd/bhyve/test/tst/mevent/read.requeue.c108
-rw-r--r--usr/src/cmd/bhyve/test/tst/mevent/testlib.c69
-rw-r--r--usr/src/cmd/bhyve/test/tst/mevent/testlib.h88
-rw-r--r--usr/src/cmd/bhyve/uart_emul.c98
21 files changed, 2326 insertions, 106 deletions
diff --git a/usr/contrib/freebsd/sys/queue.h b/usr/contrib/freebsd/sys/queue.h
new file mode 100644
index 0000000000..f26c492af1
--- /dev/null
+++ b/usr/contrib/freebsd/sys/queue.h
@@ -0,0 +1,787 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ * $FreeBSD$
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+#include <sys/cdefs.h>
+
+/*
+ * This file defines four types of data structures: singly-linked lists,
+ * singly-linked tail queues, lists and tail queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may be traversed in either direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ *
+ * Below is a summary of implemented functions where:
+ * + means the macro is available
+ * - means the macro is not available
+ * s means the macro is available but is slow (runs in O(n) time)
+ *
+ * SLIST LIST STAILQ TAILQ
+ * _HEAD + + + +
+ * _CLASS_HEAD + + + +
+ * _HEAD_INITIALIZER + + + +
+ * _ENTRY + + + +
+ * _CLASS_ENTRY + + + +
+ * _INIT + + + +
+ * _EMPTY + + + +
+ * _FIRST + + + +
+ * _NEXT + + + +
+ * _PREV - + - +
+ * _LAST - - + +
+ * _FOREACH + + + +
+ * _FOREACH_FROM + + + +
+ * _FOREACH_SAFE + + + +
+ * _FOREACH_FROM_SAFE + + + +
+ * _FOREACH_REVERSE - - - +
+ * _FOREACH_REVERSE_FROM - - - +
+ * _FOREACH_REVERSE_SAFE - - - +
+ * _FOREACH_REVERSE_FROM_SAFE - - - +
+ * _INSERT_HEAD + + + +
+ * _INSERT_BEFORE - + - +
+ * _INSERT_AFTER + + + +
+ * _INSERT_TAIL - - + +
+ * _CONCAT s s + +
+ * _REMOVE_AFTER + - + -
+ * _REMOVE_HEAD + - + -
+ * _REMOVE s + s +
+ * _SWAP + + + +
+ *
+ */
+#ifdef QUEUE_MACRO_DEBUG
+/* Store the last 2 places the queue element or head was altered */
+struct qm_trace {
+ unsigned long lastline;
+ unsigned long prevline;
+ const char *lastfile;
+ const char *prevfile;
+};
+
+#define TRACEBUF struct qm_trace trace;
+#define TRACEBUF_INITIALIZER { __LINE__, 0, __FILE__, NULL } ,
+#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
+#define QMD_SAVELINK(name, link) void **name = (void *)&(link)
+
+#define QMD_TRACE_HEAD(head) do { \
+ (head)->trace.prevline = (head)->trace.lastline; \
+ (head)->trace.prevfile = (head)->trace.lastfile; \
+ (head)->trace.lastline = __LINE__; \
+ (head)->trace.lastfile = __FILE__; \
+} while (0)
+
+#define QMD_TRACE_ELEM(elem) do { \
+ (elem)->trace.prevline = (elem)->trace.lastline; \
+ (elem)->trace.prevfile = (elem)->trace.lastfile; \
+ (elem)->trace.lastline = __LINE__; \
+ (elem)->trace.lastfile = __FILE__; \
+} while (0)
+
+#else
+#define QMD_TRACE_ELEM(elem)
+#define QMD_TRACE_HEAD(head)
+#define QMD_SAVELINK(name, link)
+#define TRACEBUF
+#define TRACEBUF_INITIALIZER
+#define TRASHIT(x)
+#endif /* QUEUE_MACRO_DEBUG */
+
+#ifdef __cplusplus
+/*
+ * In C++ there can be structure lists and class lists:
+ */
+#define QUEUE_TYPEOF(type) type
+#else
+#define QUEUE_TYPEOF(type) struct type
+#endif
+
+/*
+ * Singly-linked List declarations.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_CLASS_HEAD(name, type) \
+struct name { \
+ class type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+#define SLIST_CLASS_ENTRY(type) \
+struct { \
+ class type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_CONCAT(head1, head2, type, field) do { \
+ QUEUE_TYPEOF(type) *curelm = SLIST_FIRST(head1); \
+ if (curelm == NULL) { \
+ if ((SLIST_FIRST(head1) = SLIST_FIRST(head2)) != NULL) \
+ SLIST_INIT(head2); \
+ } else if (SLIST_FIRST(head2) != NULL) { \
+ while (SLIST_NEXT(curelm, field) != NULL) \
+ curelm = SLIST_NEXT(curelm, field); \
+ SLIST_NEXT(curelm, field) = SLIST_FIRST(head2); \
+ SLIST_INIT(head2); \
+ } \
+} while (0)
+
+#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
+
+#define SLIST_FIRST(head) ((head)->slh_first)
+
+#define SLIST_FOREACH(var, head, field) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var); \
+ (var) = SLIST_NEXT((var), field))
+
+#define SLIST_FOREACH_FROM(var, head, field) \
+ for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
+ (var); \
+ (var) = SLIST_NEXT((var), field))
+
+#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var) && ((tvar) = SLIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
+ for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
+ (var) && ((tvar) = SLIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
+ for ((varp) = &SLIST_FIRST((head)); \
+ ((var) = *(varp)) != NULL; \
+ (varp) = &SLIST_NEXT((var), field))
+
+#define SLIST_INIT(head) do { \
+ SLIST_FIRST((head)) = NULL; \
+} while (0)
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
+ SLIST_NEXT((slistelm), field) = (elm); \
+} while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
+ SLIST_FIRST((head)) = (elm); \
+} while (0)
+
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ QMD_SAVELINK(oldnext, (elm)->field.sle_next); \
+ if (SLIST_FIRST((head)) == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ QUEUE_TYPEOF(type) *curelm = SLIST_FIRST(head); \
+ while (SLIST_NEXT(curelm, field) != (elm)) \
+ curelm = SLIST_NEXT(curelm, field); \
+ SLIST_REMOVE_AFTER(curelm, field); \
+ } \
+ TRASHIT(*oldnext); \
+} while (0)
+
+#define SLIST_REMOVE_AFTER(elm, field) do { \
+ SLIST_NEXT(elm, field) = \
+ SLIST_NEXT(SLIST_NEXT(elm, field), field); \
+} while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
+} while (0)
+
+#define SLIST_SWAP(head1, head2, type) do { \
+ QUEUE_TYPEOF(type) *swap_first = SLIST_FIRST(head1); \
+ SLIST_FIRST(head1) = SLIST_FIRST(head2); \
+ SLIST_FIRST(head2) = swap_first; \
+} while (0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define STAILQ_HEAD(name, type) \
+struct name { \
+ struct type *stqh_first;/* first element */ \
+ struct type **stqh_last;/* addr of last next element */ \
+}
+
+#define STAILQ_CLASS_HEAD(name, type) \
+struct name { \
+ class type *stqh_first; /* first element */ \
+ class type **stqh_last; /* addr of last next element */ \
+}
+
+#define STAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).stqh_first }
+
+#define STAILQ_ENTRY(type) \
+struct { \
+ struct type *stqe_next; /* next element */ \
+}
+
+#define STAILQ_CLASS_ENTRY(type) \
+struct { \
+ class type *stqe_next; /* next element */ \
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define STAILQ_CONCAT(head1, head2) do { \
+ if (!STAILQ_EMPTY((head2))) { \
+ *(head1)->stqh_last = (head2)->stqh_first; \
+ (head1)->stqh_last = (head2)->stqh_last; \
+ STAILQ_INIT((head2)); \
+ } \
+} while (0)
+
+#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
+
+#define STAILQ_FIRST(head) ((head)->stqh_first)
+
+#define STAILQ_FOREACH(var, head, field) \
+ for((var) = STAILQ_FIRST((head)); \
+ (var); \
+ (var) = STAILQ_NEXT((var), field))
+
+#define STAILQ_FOREACH_FROM(var, head, field) \
+ for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
+ (var); \
+ (var) = STAILQ_NEXT((var), field))
+
+#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = STAILQ_FIRST((head)); \
+ (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
+ for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
+ (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define STAILQ_INIT(head) do { \
+ STAILQ_FIRST((head)) = NULL; \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+} while (0)
+
+#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_NEXT((tqelm), field) = (elm); \
+} while (0)
+
+#define STAILQ_INSERT_HEAD(head, elm, field) do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_FIRST((head)) = (elm); \
+} while (0)
+
+#define STAILQ_INSERT_TAIL(head, elm, field) do { \
+ STAILQ_NEXT((elm), field) = NULL; \
+ *(head)->stqh_last = (elm); \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+} while (0)
+
+#define STAILQ_LAST(head, type, field) \
+ (STAILQ_EMPTY((head)) ? NULL : \
+ __containerof((head)->stqh_last, \
+ QUEUE_TYPEOF(type), field.stqe_next))
+
+#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+
+#define STAILQ_REMOVE(head, elm, type, field) do { \
+ QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \
+ if (STAILQ_FIRST((head)) == (elm)) { \
+ STAILQ_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ QUEUE_TYPEOF(type) *curelm = STAILQ_FIRST(head); \
+ while (STAILQ_NEXT(curelm, field) != (elm)) \
+ curelm = STAILQ_NEXT(curelm, field); \
+ STAILQ_REMOVE_AFTER(head, curelm, field); \
+ } \
+ TRASHIT(*oldnext); \
+} while (0)
+
+#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
+ if ((STAILQ_NEXT(elm, field) = \
+ STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+} while (0)
+
+#define STAILQ_REMOVE_HEAD(head, field) do { \
+ if ((STAILQ_FIRST((head)) = \
+ STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+} while (0)
+
+#define STAILQ_SWAP(head1, head2, type) do { \
+ QUEUE_TYPEOF(type) *swap_first = STAILQ_FIRST(head1); \
+ QUEUE_TYPEOF(type) **swap_last = (head1)->stqh_last; \
+ STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \
+ (head1)->stqh_last = (head2)->stqh_last; \
+ STAILQ_FIRST(head2) = swap_first; \
+ (head2)->stqh_last = swap_last; \
+ if (STAILQ_EMPTY(head1)) \
+ (head1)->stqh_last = &STAILQ_FIRST(head1); \
+ if (STAILQ_EMPTY(head2)) \
+ (head2)->stqh_last = &STAILQ_FIRST(head2); \
+} while (0)
+
+
+/*
+ * List declarations.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_CLASS_HEAD(name, type) \
+struct name { \
+ class type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+#define LIST_CLASS_ENTRY(type) \
+struct { \
+ class type *le_next; /* next element */ \
+ class type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List functions.
+ */
+
+#if (defined(_KERNEL) && defined(INVARIANTS))
+#define QMD_LIST_CHECK_HEAD(head, field) do { \
+ if (LIST_FIRST((head)) != NULL && \
+ LIST_FIRST((head))->field.le_prev != \
+ &LIST_FIRST((head))) \
+ panic("Bad list head %p first->prev != head", (head)); \
+} while (0)
+
+#define QMD_LIST_CHECK_NEXT(elm, field) do { \
+ if (LIST_NEXT((elm), field) != NULL && \
+ LIST_NEXT((elm), field)->field.le_prev != \
+ &((elm)->field.le_next)) \
+ panic("Bad link elm %p next->prev != elm", (elm)); \
+} while (0)
+
+#define QMD_LIST_CHECK_PREV(elm, field) do { \
+ if (*(elm)->field.le_prev != (elm)) \
+ panic("Bad link elm %p prev->next != elm", (elm)); \
+} while (0)
+#else
+#define QMD_LIST_CHECK_HEAD(head, field)
+#define QMD_LIST_CHECK_NEXT(elm, field)
+#define QMD_LIST_CHECK_PREV(elm, field)
+#endif /* (_KERNEL && INVARIANTS) */
+
+#define LIST_CONCAT(head1, head2, type, field) do { \
+ QUEUE_TYPEOF(type) *curelm = LIST_FIRST(head1); \
+ if (curelm == NULL) { \
+ if ((LIST_FIRST(head1) = LIST_FIRST(head2)) != NULL) { \
+ LIST_FIRST(head2)->field.le_prev = \
+ &LIST_FIRST((head1)); \
+ LIST_INIT(head2); \
+ } \
+ } else if (LIST_FIRST(head2) != NULL) { \
+ while (LIST_NEXT(curelm, field) != NULL) \
+ curelm = LIST_NEXT(curelm, field); \
+ LIST_NEXT(curelm, field) = LIST_FIRST(head2); \
+ LIST_FIRST(head2)->field.le_prev = &LIST_NEXT(curelm, field); \
+ LIST_INIT(head2); \
+ } \
+} while (0)
+
+#define LIST_EMPTY(head) ((head)->lh_first == NULL)
+
+#define LIST_FIRST(head) ((head)->lh_first)
+
+#define LIST_FOREACH(var, head, field) \
+ for ((var) = LIST_FIRST((head)); \
+ (var); \
+ (var) = LIST_NEXT((var), field))
+
+#define LIST_FOREACH_FROM(var, head, field) \
+ for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
+ (var); \
+ (var) = LIST_NEXT((var), field))
+
+#define LIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = LIST_FIRST((head)); \
+ (var) && ((tvar) = LIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
+ for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
+ (var) && ((tvar) = LIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define LIST_INIT(head) do { \
+ LIST_FIRST((head)) = NULL; \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ QMD_LIST_CHECK_NEXT(listelm, field); \
+ if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
+ LIST_NEXT((listelm), field)->field.le_prev = \
+ &LIST_NEXT((elm), field); \
+ LIST_NEXT((listelm), field) = (elm); \
+ (elm)->field.le_prev = &LIST_NEXT((listelm), field); \
+} while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ QMD_LIST_CHECK_PREV(listelm, field); \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ LIST_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &LIST_NEXT((elm), field); \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ QMD_LIST_CHECK_HEAD((head), field); \
+ if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
+ LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
+ LIST_FIRST((head)) = (elm); \
+ (elm)->field.le_prev = &LIST_FIRST((head)); \
+} while (0)
+
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_PREV(elm, head, type, field) \
+ ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \
+ __containerof((elm)->field.le_prev, \
+ QUEUE_TYPEOF(type), field.le_next))
+
+#define LIST_REMOVE(elm, field) do { \
+ QMD_SAVELINK(oldnext, (elm)->field.le_next); \
+ QMD_SAVELINK(oldprev, (elm)->field.le_prev); \
+ QMD_LIST_CHECK_NEXT(elm, field); \
+ QMD_LIST_CHECK_PREV(elm, field); \
+ if (LIST_NEXT((elm), field) != NULL) \
+ LIST_NEXT((elm), field)->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = LIST_NEXT((elm), field); \
+ TRASHIT(*oldnext); \
+ TRASHIT(*oldprev); \
+} while (0)
+
+#define LIST_SWAP(head1, head2, type, field) do { \
+ QUEUE_TYPEOF(type) *swap_tmp = LIST_FIRST(head1); \
+ LIST_FIRST((head1)) = LIST_FIRST((head2)); \
+ LIST_FIRST((head2)) = swap_tmp; \
+ if ((swap_tmp = LIST_FIRST((head1))) != NULL) \
+ swap_tmp->field.le_prev = &LIST_FIRST((head1)); \
+ if ((swap_tmp = LIST_FIRST((head2))) != NULL) \
+ swap_tmp->field.le_prev = &LIST_FIRST((head2)); \
+} while (0)
+
+/*
+ * Tail queue declarations.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+ TRACEBUF \
+}
+
+#define TAILQ_CLASS_HEAD(name, type) \
+struct name { \
+ class type *tqh_first; /* first element */ \
+ class type **tqh_last; /* addr of last next element */ \
+ TRACEBUF \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first, TRACEBUF_INITIALIZER }
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+ TRACEBUF \
+}
+
+#define TAILQ_CLASS_ENTRY(type) \
+struct { \
+ class type *tqe_next; /* next element */ \
+ class type **tqe_prev; /* address of previous next element */ \
+ TRACEBUF \
+}
+
+/*
+ * Tail queue functions.
+ */
+#if (defined(_KERNEL) && defined(INVARIANTS))
+#define QMD_TAILQ_CHECK_HEAD(head, field) do { \
+ if (!TAILQ_EMPTY(head) && \
+ TAILQ_FIRST((head))->field.tqe_prev != \
+ &TAILQ_FIRST((head))) \
+ panic("Bad tailq head %p first->prev != head", (head)); \
+} while (0)
+
+#define QMD_TAILQ_CHECK_TAIL(head, field) do { \
+ if (*(head)->tqh_last != NULL) \
+ panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \
+} while (0)
+
+#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \
+ if (TAILQ_NEXT((elm), field) != NULL && \
+ TAILQ_NEXT((elm), field)->field.tqe_prev != \
+ &((elm)->field.tqe_next)) \
+ panic("Bad link elm %p next->prev != elm", (elm)); \
+} while (0)
+
+#define QMD_TAILQ_CHECK_PREV(elm, field) do { \
+ if (*(elm)->field.tqe_prev != (elm)) \
+ panic("Bad link elm %p prev->next != elm", (elm)); \
+} while (0)
+#else
+#define QMD_TAILQ_CHECK_HEAD(head, field)
+#define QMD_TAILQ_CHECK_TAIL(head, headname)
+#define QMD_TAILQ_CHECK_NEXT(elm, field)
+#define QMD_TAILQ_CHECK_PREV(elm, field)
+#endif /* (_KERNEL && INVARIANTS) */
+
+#define TAILQ_CONCAT(head1, head2, field) do { \
+ if (!TAILQ_EMPTY(head2)) { \
+ *(head1)->tqh_last = (head2)->tqh_first; \
+ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ TAILQ_INIT((head2)); \
+ QMD_TRACE_HEAD(head1); \
+ QMD_TRACE_HEAD(head2); \
+ } \
+} while (0)
+
+#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+
+#define TAILQ_FOREACH(var, head, field) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var); \
+ (var) = TAILQ_NEXT((var), field))
+
+#define TAILQ_FOREACH_FROM(var, head, field) \
+ for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
+ (var); \
+ (var) = TAILQ_NEXT((var), field))
+
+#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
+ for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
+ (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var); \
+ (var) = TAILQ_PREV((var), headname, field))
+
+#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \
+ for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
+ (var); \
+ (var) = TAILQ_PREV((var), headname, field))
+
+#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
+ (var) = (tvar))
+
+#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \
+ for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
+ (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
+ (var) = (tvar))
+
+#define TAILQ_INIT(head) do { \
+ TAILQ_FIRST((head)) = NULL; \
+ (head)->tqh_last = &TAILQ_FIRST((head)); \
+ QMD_TRACE_HEAD(head); \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ QMD_TAILQ_CHECK_NEXT(listelm, field); \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else { \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_HEAD(head); \
+ } \
+ TAILQ_NEXT((listelm), field) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+ QMD_TRACE_ELEM(&(listelm)->field); \
+} while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ QMD_TAILQ_CHECK_PREV(listelm, field); \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ TAILQ_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+ QMD_TRACE_ELEM(&(listelm)->field); \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ QMD_TAILQ_CHECK_HEAD(head, field); \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
+ TAILQ_FIRST((head))->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ TAILQ_FIRST((head)) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
+ QMD_TRACE_HEAD(head); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ QMD_TAILQ_CHECK_TAIL(head, field); \
+ TAILQ_NEXT((elm), field) = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_HEAD(head); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+} while (0)
+
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \
+ QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \
+ QMD_TAILQ_CHECK_NEXT(elm, field); \
+ QMD_TAILQ_CHECK_PREV(elm, field); \
+ if ((TAILQ_NEXT((elm), field)) != NULL) \
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else { \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ QMD_TRACE_HEAD(head); \
+ } \
+ *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
+ TRASHIT(*oldnext); \
+ TRASHIT(*oldprev); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+} while (0)
+
+#define TAILQ_SWAP(head1, head2, type, field) do { \
+ QUEUE_TYPEOF(type) *swap_first = (head1)->tqh_first; \
+ QUEUE_TYPEOF(type) **swap_last = (head1)->tqh_last; \
+ (head1)->tqh_first = (head2)->tqh_first; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ (head2)->tqh_first = swap_first; \
+ (head2)->tqh_last = swap_last; \
+ if ((swap_first = (head1)->tqh_first) != NULL) \
+ swap_first->field.tqe_prev = &(head1)->tqh_first; \
+ else \
+ (head1)->tqh_last = &(head1)->tqh_first; \
+ if ((swap_first = (head2)->tqh_first) != NULL) \
+ swap_first->field.tqe_prev = &(head2)->tqh_first; \
+ else \
+ (head2)->tqh_last = &(head2)->tqh_first; \
+} while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/usr/src/cmd/bhyve/Makefile b/usr/src/cmd/bhyve/Makefile
index 7975589c66..ee5a139e0c 100644
--- a/usr/src/cmd/bhyve/Makefile
+++ b/usr/src/cmd/bhyve/Makefile
@@ -21,6 +21,14 @@ include ../Makefile.cmd
include ../Makefile.cmd.64
include ../Makefile.ctf
+SUBDIRS = test
+
+all := TARGET = all
+install := TARGET = install
+clean := TARGET = clean
+clobber := TARGET = clobber
+lint := TARGET = lint
+
SRCS = acpi.c \
atkbdc.c \
bhyvegc.c \
@@ -34,6 +42,7 @@ SRCS = acpi.c \
inout.c \
ioapic.c \
mem.c \
+ mevent.c \
mptbl.c \
pci_ahci.c \
pci_e82545.c \
@@ -74,6 +83,11 @@ OBJS = $(SRCS:.c=.o)
CLEANFILES = $(PROG)
CLOBBERFILES = $(ROOTUSRSBINPROG) $(ZHYVE)
+MEVENT_TEST_PROG = mevent_test
+MEVENT_TEST_SRCS = mevent.c mevent_test.c
+MEVENT_TEST_OBJS = $(MEVENT_TEST_SRCS:.c=.o)
+CLEANFILES += $(MEVENT_TEST_PROG) $(MEVENT_TEST_OBJS)
+
CFLAGS += $(CCVERBOSE) -_gcc=-Wimplicit-function-declaration -_gcc=-Wno-parentheses
CPPFLAGS = -I$(COMPAT)/freebsd -I$(CONTRIB)/freebsd \
-I$(COMPAT)/freebsd/amd64 -I$(CONTRIB)/freebsd/amd64 \
@@ -96,20 +110,31 @@ POST_PROCESS += ; $(GENSETDEFS) $@
# Real main is in zhyve.c
bhyverun.o := CPPFLAGS += -Dmain=bhyve_main
-all: $(PROG)
+all: $(PROG) $(MEVENT_TEST_PROG) $(SUBDIRS)
$(PROG): $(OBJS)
$(LINK.c) -o $@ $(OBJS) $(LDFLAGS) $(LDLIBS)
$(POST_PROCESS)
-install: all $(ROOTUSRSBINPROG)
+$(MEVENT_TEST_PROG): $(MEVENT_TEST_OBJS)
+ $(LINK.c) -o $@ $(MEVENT_TEST_OBJS) -lsocket
+
+install: all $(ROOTUSRSBINPROG) $(SUBDIRS)
$(RM) $(ZHYVE)
$(LN) $(ROOTUSRSBIN)/$(PROG) $(ZHYVE)
-clean:
+clean: $(SUBDIRS)
$(RM) $(OBJS) $(CLEANFILES)
-lint: lint_SRCS
+clobber: clean $(SUBDIRS)
+ $(RM) $(CLOBBERFILES)
+
+lint: lint_SRCS $(SUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
include ../Makefile.targ
diff --git a/usr/src/cmd/bhyve/bhyverun.c b/usr/src/cmd/bhyve/bhyverun.c
index dbc2414e23..56240356ae 100644
--- a/usr/src/cmd/bhyve/bhyverun.c
+++ b/usr/src/cmd/bhyve/bhyverun.c
@@ -36,7 +36,7 @@
* http://www.illumos.org/license/CDDL.
*
* Copyright 2015 Pluribus Networks Inc.
- * Copyright 2017 Joyent, Inc.
+ * Copyright 2018 Joyent, Inc.
*/
#include <sys/cdefs.h>
@@ -85,9 +85,7 @@ __FBSDID("$FreeBSD$");
#include "fwctl.h"
#include "ioapic.h"
#include "mem.h"
-#ifdef __FreeBSD__
#include "mevent.h"
-#endif
#include "mptbl.h"
#include "pci_emul.h"
#include "pci_irq.h"
@@ -1124,15 +1122,14 @@ main(int argc, char *argv[])
/*
* Head off to the main event dispatch loop
*/
-#ifdef __FreeBSD__
mevent_dispatch();
-#else
+#ifndef __FreeBSD
if (vm_started_cb != NULL) {
vm_started_cb();
}
+#endif
pthread_exit(NULL);
-#endif
exit(1);
}
diff --git a/usr/src/cmd/bhyve/mevent.c b/usr/src/cmd/bhyve/mevent.c
index adc047db71..bd36c03393 100644
--- a/usr/src/cmd/bhyve/mevent.c
+++ b/usr/src/cmd/bhyve/mevent.c
@@ -27,6 +27,10 @@
*/
/*
+ * Copyright 2018 Joyent, Inc.
+ */
+
+/*
* Micro event library for FreeBSD, designed for a single i/o thread
* using kqueue, and having events be persistent by default.
*/
@@ -47,7 +51,14 @@ __FBSDID("$FreeBSD$");
#ifndef WITHOUT_CAPSICUM
#include <sys/capsicum.h>
#endif
+#ifdef __FreeBSD__
#include <sys/event.h>
+#else
+#include <port.h>
+#include <sys/poll.h>
+#include <sys/siginfo.h>
+#include <sys/queue.h>
+#endif
#include <sys/time.h>
#include <pthread.h>
@@ -73,12 +84,21 @@ struct mevent {
void (*me_func)(int, enum ev_type, void *);
#define me_msecs me_fd
int me_fd;
+#ifdef __FreeBSD__
int me_timid;
+#else
+ timer_t me_timid;
+#endif
enum ev_type me_type;
void *me_param;
int me_cq;
int me_state;
int me_closefd;
+#ifndef __FreeBSD__
+ port_notify_t me_notify;
+ struct sigevent me_sigev;
+ boolean_t me_auto_requeue;
+#endif
LIST_ENTRY(mevent) me_list;
};
@@ -124,7 +144,7 @@ mevent_notify(void)
write(mevent_pipefd[1], &c, 1);
}
}
-
+#ifdef __FreeBSD__
static int
mevent_kq_filter(struct mevent *mevp)
{
@@ -244,6 +264,155 @@ mevent_handle(struct kevent *kev, int numev)
}
}
+#else /* __FreeBSD__ */
+
+static void
+mevent_update_one(struct mevent *mevp)
+{
+ int portfd = mevp->me_notify.portnfy_port;
+
+ switch (mevp->me_type) {
+ case EVF_READ:
+ case EVF_WRITE:
+ mevp->me_auto_requeue = B_FALSE;
+
+ switch (mevp->me_state) {
+ case MEV_ADD:
+ case MEV_ENABLE:
+ {
+ int events;
+
+ events = (mevp->me_type == EVF_READ) ? POLLIN : POLLOUT;
+
+ if (port_associate(portfd, PORT_SOURCE_FD, mevp->me_fd,
+ events, mevp) != 0) {
+ (void) fprintf(stderr,
+ "port_associate fd %d %p failed: %s\n",
+ mevp->me_fd, mevp, strerror(errno));
+ }
+ return;
+ }
+ case MEV_DISABLE:
+ case MEV_DEL_PENDING:
+ /*
+ * A disable that comes in while an event is being
+ * handled will result in an ENOENT.
+ */
+ if (port_dissociate(portfd, PORT_SOURCE_FD,
+ mevp->me_fd) != 0 && errno != ENOENT) {
+ (void) fprintf(stderr, "port_dissociate "
+ "portfd %d fd %d mevp %p failed: %s\n",
+ portfd, mevp->me_fd, mevp, strerror(errno));
+ }
+ return;
+ default:
+ goto abort;
+ }
+
+ case EVF_TIMER:
+ mevp->me_auto_requeue = B_TRUE;
+
+ switch (mevp->me_state) {
+ case MEV_ADD:
+ case MEV_ENABLE:
+ {
+ struct itimerspec it = { 0 };
+
+ mevp->me_sigev.sigev_notify = SIGEV_PORT;
+ mevp->me_sigev.sigev_value.sival_ptr = &mevp->me_notify;
+
+ if (timer_create(CLOCK_REALTIME, &mevp->me_sigev,
+ &mevp->me_timid) != 0) {
+ (void) fprintf(stderr,
+ "timer_create failed: %s", strerror(errno));
+ return;
+ }
+
+ /* The first timeout */
+ it.it_value.tv_sec = mevp->me_msecs / MILLISEC;
+ it.it_value.tv_nsec =
+ MSEC2NSEC(mevp->me_msecs % MILLISEC);
+ /* Repeat at the same interval */
+ it.it_interval = it.it_value;
+
+ if (timer_settime(mevp->me_timid, 0, &it, NULL) != 0) {
+ (void) fprintf(stderr, "timer_settime failed: "
+ "%s", strerror(errno));
+ }
+ return;
+ }
+ case MEV_DISABLE:
+ case MEV_DEL_PENDING:
+ if (timer_delete(mevp->me_timid) != 0) {
+ (void) fprintf(stderr, "timer_delete failed: "
+ "%s", strerror(errno));
+ }
+ return;
+ default:
+ goto abort;
+ }
+ default:
+ /* EVF_SIGNAL not yet implemented. */
+ goto abort;
+ }
+
+abort:
+ (void) fprintf(stderr, "%s: unhandled type %d state %d\n", __func__,
+ mevp->me_type, mevp->me_state);
+ abort();
+}
+
+static void
+mevent_update_pending(int portfd)
+{
+ struct mevent *mevp, *tmpp;
+
+ mevent_qlock();
+
+ LIST_FOREACH_SAFE(mevp, &change_head, me_list, tmpp) {
+ mevp->me_notify.portnfy_port = portfd;
+ mevp->me_notify.portnfy_user = mevp;
+ if (mevp->me_closefd) {
+ /*
+ * A close of the file descriptor will remove the
+ * event
+ */
+ (void) close(mevp->me_fd);
+ mevp->me_fd = -1;
+ } else {
+ mevent_update_one(mevp);
+ }
+
+ mevp->me_cq = 0;
+ LIST_REMOVE(mevp, me_list);
+
+ if (mevp->me_state == MEV_DEL_PENDING) {
+ free(mevp);
+ } else {
+ LIST_INSERT_HEAD(&global_head, mevp, me_list);
+ }
+ }
+
+ mevent_qunlock();
+}
+
+static void
+mevent_handle_pe(port_event_t *pe)
+{
+ struct mevent *mevp = pe->portev_user;
+
+ mevent_qunlock();
+
+ (*mevp->me_func)(mevp->me_fd, mevp->me_type, mevp->me_param);
+
+ mevent_qlock();
+ if (!mevp->me_cq && !mevp->me_auto_requeue) {
+ mevent_update_one(mevp);
+ }
+ mevent_qunlock();
+}
+#endif
+
struct mevent *
mevent_add(int tfd, enum ev_type type,
void (*func)(int, enum ev_type, void *), void *param)
@@ -400,11 +569,16 @@ mevent_set_name(void)
void
mevent_dispatch(void)
{
+#ifdef __FreeBSD__
struct kevent changelist[MEVENT_MAX];
struct kevent eventlist[MEVENT_MAX];
struct mevent *pipev;
int mfd;
int numev;
+#else
+ struct mevent *pipev;
+ int portfd;
+#endif
int ret;
#ifndef WITHOUT_CAPSICUM
cap_rights_t rights;
@@ -413,8 +587,13 @@ mevent_dispatch(void)
mevent_tid = pthread_self();
mevent_set_name();
+#ifdef __FreeBSD__
mfd = kqueue();
assert(mfd > 0);
+#else
+ portfd = port_create();
+ assert(portfd >= 0);
+#endif
#ifndef WITHOUT_CAPSICUM
cap_rights_init(&rights, CAP_KQUEUE);
@@ -448,6 +627,7 @@ mevent_dispatch(void)
assert(pipev != NULL);
for (;;) {
+#ifdef __FreeBSD__
/*
* Build changelist if required.
* XXX the changelist can be put into the blocking call
@@ -474,5 +654,22 @@ mevent_dispatch(void)
* Handle reported events
*/
mevent_handle(eventlist, ret);
+
+#else /* __FreeBSD__ */
+ port_event_t pev;
+
+ /* Handle any pending updates */
+ mevent_update_pending(portfd);
+
+ /* Block awaiting events */
+ ret = port_get(portfd, &pev, NULL);
+ if (ret != 0 && errno != EINTR) {
+ perror("Error return from port_get");
+ continue;
+ }
+
+ /* Handle reported event */
+ mevent_handle_pe(&pev);
+#endif /* __FreeBSD__ */
}
}
diff --git a/usr/src/cmd/bhyve/mevent_test.c b/usr/src/cmd/bhyve/mevent_test.c
index 9c68ff7874..a57aceec55 100644
--- a/usr/src/cmd/bhyve/mevent_test.c
+++ b/usr/src/cmd/bhyve/mevent_test.c
@@ -27,6 +27,10 @@
*/
/*
+ * Copyright 2018 Joyent, Inc.
+ */
+
+/*
* Test program for the micro event library. Set up a simple TCP echo
* service.
*
@@ -35,10 +39,14 @@
#include <sys/types.h>
#include <sys/stdint.h>
+#ifdef __FreeBSD__
#include <sys/sysctl.h>
+#endif
#include <sys/socket.h>
#include <netinet/in.h>
+#ifdef __FreeBSD__
#include <machine/cpufunc.h>
+#endif
#include <stdio.h>
#include <stdlib.h>
@@ -66,20 +74,29 @@ uint64_t tevbuf[TEVSZ];
static void
timer_print(void)
{
- uint64_t min, max, diff, sum, tsc_freq;
+ uint64_t min, max, diff, sum;
+#ifdef __FreeBSD__
+ uint64_t tsc_freq;
size_t len;
+#endif
int j;
min = UINT64_MAX;
max = 0;
sum = 0;
+#ifdef __FreeBSD__
len = sizeof(tsc_freq);
sysctlbyname("machdep.tsc_freq", &tsc_freq, &len, NULL, 0);
+#endif
for (j = 1; j < TEVSZ; j++) {
+#ifdef __FreeBSD__
/* Convert a tsc diff into microseconds */
diff = (tevbuf[j] - tevbuf[j-1]) * 1000000 / tsc_freq;
+#else
+ diff = (tevbuf[j] - tevbuf[j-1]) / 1000;
+#endif
sum += diff;
if (min > diff)
min = diff;
@@ -99,7 +116,11 @@ timer_callback(int fd, enum ev_type type, void *param)
if (i >= TEVSZ)
abort();
+#ifdef __FreeBSD__
tevbuf[i++] = rdtsc();
+#else
+ tevbuf[i++] = gethrtime();
+#endif
if (i == TEVSZ) {
mevent_delete(tevp);
@@ -195,14 +216,15 @@ acceptor(void *param)
pthread_t tid;
int news;
int s;
- static int first;
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
exit(1);
}
+#ifdef __FreeBSD__
sin.sin_len = sizeof(sin);
+#endif
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_port = htons(TEST_PORT);
@@ -246,6 +268,7 @@ acceptor(void *param)
return (NULL);
}
+int
main()
{
pthread_t tid;
@@ -253,4 +276,5 @@ main()
pthread_create(&tid, NULL, acceptor, NULL);
mevent_dispatch();
+ return (0);
}
diff --git a/usr/src/cmd/bhyve/test/Makefile b/usr/src/cmd/bhyve/test/Makefile
new file mode 100644
index 0000000000..7dbee0c5f3
--- /dev/null
+++ b/usr/src/cmd/bhyve/test/Makefile
@@ -0,0 +1,18 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2018 Joyent, Inc.
+#
+
+SUBDIRS = scripts tst
+
+include Makefile.subdirs
diff --git a/usr/src/cmd/bhyve/test/Makefile.com b/usr/src/cmd/bhyve/test/Makefile.com
new file mode 100644
index 0000000000..3c719bcea7
--- /dev/null
+++ b/usr/src/cmd/bhyve/test/Makefile.com
@@ -0,0 +1,60 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2018 Joyent, Inc.
+#
+
+include $(SRC)/Makefile.master
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/cmd/Makefile.cmd.64
+
+#
+# Force c99 for everything
+#
+CSTD= $(CSTD_GNU99)
+C99MODE= -xc99=%all
+C99LMODE= -Xc99=%all
+
+CFLAGS += $(CCVERBOSE) -_gcc=-Wimplicit-function-declaration \
+ -_gcc=-Wno-parentheses
+CFLAGS64 += $(CCVERBOSE) -_gcc=-Wimplicit-function-declaration \
+ -_gcc=-Wno-parentheses
+CPPFLAGS = -I$(SRC)/cmd/bhyve \
+ -I$(COMPAT)/freebsd -I$(CONTRIB)/freebsd \
+ -I$(CONTRIB)/freebsd/dev/usb/controller \
+ -I$(CONTRIB)/freebsd/dev/mii \
+ $(CPPFLAGS.master) \
+ -I$(ROOT)/usr/platform/i86pc/include \
+ -I$(SRC)/uts/i86pc/io/vmm \
+ -I$(SRC)/uts/common \
+ -I$(SRC)/uts/i86pc \
+ -I$(SRC)/lib/libdladm/common \
+ -DWITHOUT_CAPSICUM
+CPPFLAGS += -I$(COMPAT)/freebsd/amd64 -I$(CONTRIB)/freebsd/amd64
+
+CLEANFILES += $(EXETESTS)
+CLOBBERFILES += $(ROOTTESTS)
+
+#
+# Install related definitions
+#
+ROOTOPTPKG = $(ROOT)/opt/bhyvetest
+ROOTBIN = $(ROOTOPTPKG)/bin
+ROOTTST = $(ROOTOPTPKG)/tst
+ROOTTSTDIR = $(ROOTTST)/$(TSTDIR)
+ROOTTSTEXES = $(EXETESTS:%=$(ROOTTSTDIR)/%)
+ROOTTSTSH = $(SHTESTS:%=$(ROOTTSTDIR)/%)
+ROOTOUT = $(OUTFILES:%=$(ROOTTSTDIR)/%)
+ROOTTESTS = $(ROOTTSTEXES) $(ROOTTSTSH) $(ROOTOUT)
+FILEMODE = 0555
+LDLIBS = $(LDLIBS.cmd)
+LINTEXE = $(EXETESTS:%.exe=%.exe.ln)
diff --git a/usr/src/cmd/bhyve/test/Makefile.subdirs b/usr/src/cmd/bhyve/test/Makefile.subdirs
new file mode 100644
index 0000000000..45f0aa67fa
--- /dev/null
+++ b/usr/src/cmd/bhyve/test/Makefile.subdirs
@@ -0,0 +1,29 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2018 Joyent, Inc.
+#
+
+.KEEP_STATE:
+
+all := TARGET += all
+clean := TARGET += clean
+clobber := TARGET += clobber
+install := TARGET += install
+lint := TARGET += lint
+
+all clean clobber install lint: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/cmd/bhyve/test/Makefile.targ b/usr/src/cmd/bhyve/test/Makefile.targ
new file mode 100644
index 0000000000..e3ec55cfdb
--- /dev/null
+++ b/usr/src/cmd/bhyve/test/Makefile.targ
@@ -0,0 +1,55 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2018 Joyent, Inc.
+#
+
+$(ROOTOPTPKG):
+ $(INS.dir)
+
+$(ROOTBIN): $(ROOTOPTPKG)
+ $(INS.dir)
+
+$(ROOTBIN)/%: %.ksh $(ROOTBIN)
+ $(INS.rename)
+
+$(ROOTTST): $(ROOTOPTPKG)
+ $(INS.dir)
+
+$(ROOTTSTDIR): $(ROOTTST)
+ $(INS.dir)
+
+$(ROOTTSTDIR)/%.ksh: %.ksh $(ROOTTSTDIR)
+ $(INS.file)
+
+$(ROOTTSTDIR)/%.out: %.out $(ROOTTSTDIR)
+ $(INS.file)
+
+%.exe: %.o $(SUPOBJS)
+ $(LINK.c) -o $@ $< $(SUPOBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+$(ROOTTSTDIR)/%.exe: %.exe $(ROOTTSTDIR)
+ $(INS.file)
+
+all: install
+
+%.exe.ln: %.c $(SUPOBJS)
+ $(LINT.c) $< $(LDLIBS)
+
+lint: $(LINTEXE)
+
+clean:
+ -$(RM) *.o $(CLEANFILES)
+
+clobber: clean
+ -$(RM) $(CLOBBERFILES)
diff --git a/usr/src/cmd/bhyve/test/scripts/Makefile b/usr/src/cmd/bhyve/test/scripts/Makefile
new file mode 100644
index 0000000000..d28a5edb8f
--- /dev/null
+++ b/usr/src/cmd/bhyve/test/scripts/Makefile
@@ -0,0 +1,28 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2018 Joyent, Inc.
+#
+
+include ../Makefile.com
+
+SRCS = bhyvetest
+SCRIPTS = $(SRCS:%=$(ROOTBIN)/%)
+
+SCRIPTS := FILEMODE = 0555
+CLOBBERFILES = $(SCRIPTS)
+
+install: $(SCRIPTS)
+
+lint:
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/bhyve/test/scripts/bhyvetest.ksh b/usr/src/cmd/bhyve/test/scripts/bhyvetest.ksh
new file mode 100644
index 0000000000..95b7743417
--- /dev/null
+++ b/usr/src/cmd/bhyve/test/scripts/bhyvetest.ksh
@@ -0,0 +1,231 @@
+#!/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2018 Joyent, Inc.
+#
+
+#
+# bhyve test suite driver
+#
+unalias -a
+
+bt_arg0=$(basename $0)
+bt_root="$(cd $(dirname $0)/..; pwd -P)"
+bt_ksh="/usr/bin/ksh"
+bt_outdir=
+bt_keep=
+bt_all=
+bt_tnum=0
+bt_tfail=0
+bt_tsuc=0
+
+function usage
+{
+ typeset msg="$*"
+ [[ -z "$msg" ]] || echo "$msg" 2>&1
+ cat <<USAGE >&2
+Usage: $bt_arg0 [ -o dir ] [ -k ] [ -a | test ... ]
+
+ -o dir Sets 'dir' as the output directory
+ -a Runs all tests, ignores tests passed in
+ -k Keep output from all tests, not just failures
+ -m mdb binary to test
+USAGE
+ exit 2
+}
+
+function fatal
+{
+ typeset msg="$*"
+ [[ -z "$msg" ]] && msg="failed"
+ echo "$bt_arg0: $msg" >&2
+ exit 1
+}
+
+function setup_outdir
+{
+ bt_outdir="$bt_outdir/$bt_arg0.$$"
+ mkdir -p $bt_outdir || fatal "failed to make output dir $bt_outdir"
+}
+
+function run_single
+{
+ typeset name=$1
+ typeset expect base ext exe command odir res reason
+ typeset iserr
+
+ [[ -z "$name" ]] && fail "missing test to run"
+ base=${name##*/}
+ ext=${base##*.}
+ expect=${base%%.*}
+ odir="$bt_outdir/current"
+ [[ -z "$ext" ]] && fatal "found test without ext: $name"
+ [[ -z "$expect" ]] && fatal "found test without prefix: $name"
+
+ if [[ "$expect" == "err" || "$expect" == "ecreate" ]]; then
+ iserr="yup"
+ else
+ iserr=""
+ fi
+
+ case "$ext" in
+ "ksh")
+ command="$bt_ksh ./$base"
+ ;;
+ "exe")
+ command="./$base"
+ ;;
+ "out")
+ #
+ # This is the file format for checking output against.
+ #
+ return 0
+ ;;
+ *)
+ echo "skipping test $name (unknown extensino)"
+ return 0
+ ;;
+ esac
+
+ echo "Executing test $name ... \c"
+ mkdir -p "$odir" >/dev/null || fatal "can't make output directory"
+ cd $(dirname $name) || fatal "failed to enter test directory"
+ $command > "$odir/stdout" 2>"$odir/stderr"
+ res=$?
+ cd - > /dev/null || fatal "failed to leave test directory"
+
+ if [[ -f "$name.out" ]] && \
+ ! diff "$name.out" "$odir/stdout" >/dev/null; then
+ cp $name.out $odir/$base.out
+ reason="stdout mismatch"
+ elif [[ -n "$iserr" && $res -eq 0 ]]; then
+ reason="test exited $res, not non-zero"
+ elif [[ -z "$iserr" && $res -ne 0 ]]; then
+ reason="test exited $res, not zero"
+ fi
+
+ if [[ -n "$reason" ]]; then
+ echo "$reason"
+ ((bt_tfail++))
+ mv "$odir" "$bt_outdir/failure.$bt_tfail" || fatal \
+ "failed to move test output directory"
+ cp "$name" "$bt_outdir/failure.$bt_tfail/$(basename $name)" || \
+ fatal "failed to copy test into output directory"
+ else
+ echo "passed"
+ ((bt_tsuc++))
+ mv "$odir" "$bt_outdir/success.$bt_tsuc" || fatal \
+ "failed to move test directory"
+ fi
+
+ ((bt_tnum++))
+}
+
+function run_all
+{
+ typeset tests t dir
+
+ tests=$(ls -1 $bt_root/tst/*/*.@(ksh|exe))
+ for t in $tests; do
+ run_single $t
+ done
+}
+
+function welcome
+{
+ cat <<WELCOME
+Starting tests...
+output directory: $bt_outdir
+WELCOME
+}
+
+function cleanup
+{
+ [[ -n "$bt_keep" ]] && return
+ rm -rf "$bt_outdir"/success.* || fatal \
+ "failed to remove successful test cases"
+ if [[ $bt_tfail -eq 0 ]]; then
+ rmdir "$bt_outdir" || fatal \
+ "failed to remove test output directory"
+ fi
+}
+
+function goodbye
+{
+ cat <<EOF
+
+-------------
+Results
+-------------
+
+Tests passed: $bt_tsuc
+Tests failed: $bt_tfail
+Tests ran: $bt_tnum
+
+EOF
+ if [[ $bt_tfail -eq 0 ]]; then
+ echo "Congrats, some tiny parts of bhyve aren't completely" \
+ "broken, the tests pass".
+ else
+ echo "Some tests failed, you have some work to do."
+ fi
+}
+
+while getopts ":ahko:m:" c $@; do
+ case "$c" in
+ a)
+ bt_all="y"
+ ;;
+ k)
+ bt_keep="y"
+ ;;
+ o)
+ bt_outdir="$OPTARG"
+ ;;
+ h)
+ usage
+ ;;
+ :)
+ usage "option requires an argument -- $OPTARG"
+ ;;
+ *)
+ usage "invalid option -- $OPTARG"
+ ;;
+ esac
+done
+
+shift $((OPTIND-1))
+
+[[ -z "$bt_all" && $# == 0 ]] && usage "no tests to run"
+
+[[ -z "$bt_outdir" ]] && bt_outdir="$PWD"
+
+setup_outdir
+welcome
+
+if [[ ! -z "$bt_all" ]]; then
+ run_all
+else
+ for t in $@; do
+ [[ -f $t ]] || fatal "cannot find test $t"
+ run_single $t
+ done
+fi
+
+goodbye
+cleanup
+
+#
+# Exit 1 if we have tests that return non-zero
+#
+[[ $bt_tfai -eq 0 ]]
diff --git a/usr/src/cmd/bhyve/test/tst/Makefile b/usr/src/cmd/bhyve/test/tst/Makefile
new file mode 100644
index 0000000000..f6a6ec96fc
--- /dev/null
+++ b/usr/src/cmd/bhyve/test/tst/Makefile
@@ -0,0 +1,18 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2018 Joyent, Inc.
+#
+
+SUBDIRS = mevent
+
+include ../Makefile.subdirs
diff --git a/usr/src/cmd/bhyve/test/tst/mevent/Makefile b/usr/src/cmd/bhyve/test/tst/mevent/Makefile
new file mode 100644
index 0000000000..047886bc6a
--- /dev/null
+++ b/usr/src/cmd/bhyve/test/tst/mevent/Makefile
@@ -0,0 +1,30 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2018 Joyent, Inc.
+#
+
+TSTDIR = mevent
+EXETESTS = \
+ lists.delete.exe \
+ read.disable.exe \
+ read.pause.exe \
+ read.requeue.exe \
+
+SHTESTS =
+SUPOBJS = mevent.o testlib.o
+
+include ../../Makefile.com
+
+install: $(ROOTTESTS)
+
+include ../../Makefile.targ
diff --git a/usr/src/cmd/bhyve/test/tst/mevent/lists.delete.c b/usr/src/cmd/bhyve/test/tst/mevent/lists.delete.c
new file mode 100644
index 0000000000..d09ac133a3
--- /dev/null
+++ b/usr/src/cmd/bhyve/test/tst/mevent/lists.delete.c
@@ -0,0 +1,172 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Joyent, Inc.
+ */
+
+/*
+ * Test: lists.delete
+ * Assertion: mevent_delete() causes the total number of events to decrease
+ *
+ * Strategy: 1. Create a pipe.
+ * 2. Call mevent_add() to be notified of writes to the pipe. The
+ * callback will do nothing other than generate an error if it
+ * is called.
+ * 3. Create another pipe and add a read event watcher to it. The
+ * callback will signal a cv when called. A write to the pipe
+ * followed by a wait on the cv will ensure that async
+ * operations in mevent.c are complete. See flush_and_wait().
+ * 4. Call flush_and_wait(), then get event count.
+ * 5. Delete the event created in step 2.
+ * 6. Call flush_and_wait(), then get event count.
+ * 7. Verify result in step 6 is one less than result in step 4.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "testlib.h"
+#include "mevent.h"
+
+static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
+
+static int
+get_count(void)
+{
+ int global = -1, change = -1, del_pending = -1;
+ int total;
+
+ test_mevent_count_lists(&global, &change, &del_pending);
+ ASSERT_INT_NEQ(("count not set"), global, -1);
+ ASSERT_INT_NEQ(("count not set"), change, -1);
+ ASSERT_INT_NEQ(("count not set"), change, -1);
+ ASSERT_INT_EQ(("pending delete not processed"), del_pending, 0);
+
+ total = global + change + del_pending;
+
+ VERBOSE(("count = %d (%d + %d + %d)", total, global, change,
+ del_pending));
+
+ return (total);
+}
+
+static void
+not_called_cb(int fd, enum ev_type ev, void *arg)
+{
+ FAIL(("this callback should never be called"));
+}
+
+static void
+flush_cb(int fd, enum ev_type ev, void *arg)
+{
+ char buf[32];
+
+ /* Drain the pipe */
+ while (read(fd, buf, sizeof (buf)) > 0)
+ ;
+
+ pthread_mutex_lock(&mtx);
+ pthread_cond_signal(&cv);
+ pthread_mutex_unlock(&mtx);
+}
+
+void
+flush_and_wait(int fd)
+{
+ uint8_t msg = 42;
+
+ /*
+ * Lock taken ahead of waking flush_cb so this thread doesn't race
+ * with the event thread.
+ */
+ pthread_mutex_lock(&mtx);
+ if (write(fd, &msg, sizeof (msg)) != sizeof (msg)) {
+ FAIL(("bad write"));
+ }
+
+ /* Wait for it to be read */
+ pthread_cond_wait(&cv, &mtx);
+ pthread_mutex_unlock(&mtx);
+}
+
+int
+main(int argc, const char *argv[])
+{
+ int unused_pipe[2];
+ int flush_pipe[2];
+ struct mevent *unused_evp, *flush_evp;
+ int count1, count2;
+
+ start_test(argv[0], 5);
+ start_event_thread();
+
+ /*
+ * Create first pipe and related event
+ */
+ if (pipe(unused_pipe) != 0) {
+ FAIL_ERRNO("pipe");
+ }
+ VERBOSE(("unused_pipe[] = { %d, %d }", unused_pipe[0], unused_pipe[1]));
+ if (fcntl(unused_pipe[0], F_SETFL, O_NONBLOCK) != 0) {
+ FAIL_ERRNO("set pipe nonblocking");
+ }
+ unused_evp = mevent_add(unused_pipe[0], EVF_READ, not_called_cb, NULL);
+ ASSERT_PTR_NEQ(("mevent_add"), unused_evp, NULL);
+
+ /*
+ * Create flush pipe and related event
+ */
+ if (pipe(flush_pipe) != 0) {
+ FAIL_ERRNO("pipe");
+ }
+ VERBOSE(("flush_pipe[] = { %d, %d }", flush_pipe[0],
+ flush_pipe[1]));
+ if (fcntl(flush_pipe[0], F_SETFL, O_NONBLOCK) != 0) {
+ FAIL_ERRNO("set pipe nonblocking");
+ }
+ flush_evp = mevent_add(flush_pipe[0], EVF_READ, flush_cb, NULL);
+ ASSERT_PTR_NEQ(("mevent_add"), flush_evp, NULL);
+
+ /* Get count before delete. */
+ flush_and_wait(flush_pipe[1]);
+ count1 = get_count();
+
+ /*
+ * Delete the first event and flush a read after the delete is
+ * complete.
+ */
+ if (mevent_delete(unused_evp) != 0) {
+ FAIL_ERRNO("mevent_delete");
+ }
+
+ /*
+ * Verify count decreased.
+ */
+ flush_and_wait(flush_pipe[1]);
+ count2 = get_count();
+ if (count1 - 1 != count2 ) {
+ FAIL(("mevent_delete() did not decrease count by 1: "
+ "was %d, now %d", count1, count2));
+ }
+
+ PASS();
+}
diff --git a/usr/src/cmd/bhyve/test/tst/mevent/mevent.c b/usr/src/cmd/bhyve/test/tst/mevent/mevent.c
new file mode 100644
index 0000000000..17b6546847
--- /dev/null
+++ b/usr/src/cmd/bhyve/test/tst/mevent/mevent.c
@@ -0,0 +1,57 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Joyent, Inc.
+ */
+
+#include "../../../mevent.c"
+#include "testlib.h"
+
+/*
+ * Returns by reference the number of events on the global and change lists.
+ *
+ * Used by tests that wish to ensure that the event count changes as suggested
+ * by mevent_add() and mevent_delete(). Note that a delete does not immediately
+ * delete an event. Events that are pending delete are included in the change
+ * list until the next pass through the change list to process pending changes.
+ */
+void
+test_mevent_count_lists(int *ret_global, int *ret_change, int *ret_del_pending)
+{
+ struct mevent *mevp;
+ int global = 0;
+ int change = 0;
+ int del_pending = 0;
+
+ mevent_qlock();
+
+ LIST_FOREACH(mevp, &global_head, me_list) {
+ global++;
+ VERBOSE(("on global: type %d fd %d state %d", mevp->me_type,
+ mevp->me_fd, mevp->me_state));
+ }
+
+ LIST_FOREACH(mevp, &change_head, me_list) {
+ change++;
+ if (mevp->me_state == MEV_DEL_PENDING) {
+ del_pending++;
+ }
+ VERBOSE(("on change: type %d fd %d state %d", mevp->me_type,
+ mevp->me_fd, mevp->me_state));
+ }
+
+ mevent_qunlock();
+
+ *ret_global = global;
+ *ret_change = change;
+ *ret_del_pending = del_pending;
+}
diff --git a/usr/src/cmd/bhyve/test/tst/mevent/read.disable.c b/usr/src/cmd/bhyve/test/tst/mevent/read.disable.c
new file mode 100644
index 0000000000..d23b1af96c
--- /dev/null
+++ b/usr/src/cmd/bhyve/test/tst/mevent/read.disable.c
@@ -0,0 +1,163 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Joyent, Inc.
+ */
+
+/*
+ * Test: read.cancel
+ * Assertion: A read is not requeued if mevent_disable() is called while it is
+ * being handled.
+ *
+ * Strategy: 1. Create a pipe
+ * 2. Call mevent_add() to be notified of writes to the pipe. The
+ * callback will signal a cv.
+ * 3. Write to the pipe then wait for a wakeup.
+ * 4. From the read event callback, disable the event then awaken
+ * the main thread.
+ * 5. In the main thread, add a timer event that will awaken the
+ * main thread after a short delay.
+ * 5. Write to the pipe and wait to be awoken. The wakeup should
+ * come from the timer event, not the read event.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "testlib.h"
+#include "mevent.h"
+
+typedef enum {
+ CB_NONE,
+ CB_READ,
+ CB_TIMER,
+} lastwake_t;
+
+static lastwake_t lastwake = CB_NONE;
+
+static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
+
+static struct mevent *read_event;
+
+static void
+munch(int fd, enum ev_type ev, void *arg)
+{
+ ssize_t nbytes;
+ char buf[32] = { 0 };
+ int err;
+
+ if ((nbytes = read(fd, buf, sizeof (buf))) < 0) {
+ FAIL_ERRNO("bad read");
+ }
+ VERBOSE(("read %ld bytes '%s'", nbytes, buf));
+
+ err = mevent_disable(read_event);
+ ASSERT_INT_EQ(("mevent_disable: ", strerror(err)), err, 0);
+
+ pthread_mutex_lock(&mtx);
+
+ ASSERT_INT_EQ(("wrong lastwake"), lastwake, CB_NONE);
+ lastwake = CB_READ;
+
+ pthread_cond_signal(&cv);
+ VERBOSE(("wakeup"));
+
+ pthread_mutex_unlock(&mtx);
+}
+
+static void
+tick(int ms, enum ev_type ev, void *arg)
+{
+ pthread_mutex_lock(&mtx);
+
+ ASSERT_INT_EQ(("wrong lastwake"), lastwake, CB_READ);
+ lastwake = CB_TIMER;
+
+ pthread_cond_signal(&cv);
+ VERBOSE(("wakeup"));
+
+ pthread_mutex_unlock(&mtx);
+}
+
+int
+main(int argc, const char *argv[])
+{
+ int pipefds[2];
+ struct mevent *timer;
+ ssize_t written;
+ char *msgs[] = { "first", "second" };
+ char *msg;
+
+ start_test(argv[0], 5);
+ start_event_thread();
+
+ if (pipe(pipefds) != 0) {
+ FAIL_ERRNO("pipe");
+ }
+ if (fcntl(pipefds[0], F_SETFL, O_NONBLOCK) != 0) {
+ FAIL_ERRNO("set pipe nonblocking");
+ }
+
+ /*
+ * First write
+ */
+ msg = msgs[0];
+ read_event = mevent_add(pipefds[0], EVF_READ, munch, msg);
+ ASSERT_PTR_NEQ(("mevent_add pipefd"), read_event, NULL);
+
+ pthread_mutex_lock(&mtx);
+ written = write(pipefds[1], msg, strlen(msg));
+ if (written < 0) {
+ FAIL_ERRNO("bad write");
+ }
+ ASSERT_INT64_EQ(("write '%s' failed", msg), written, strlen(msg));
+
+ /*
+ * Wait for it to be read
+ */
+ pthread_cond_wait(&cv, &mtx);
+ ASSERT_INT_EQ(("wrong lastwake"), lastwake, CB_READ);
+ pthread_mutex_unlock(&mtx);
+
+ /*
+ * Add timer, second write.
+ */
+ msg = msgs[1];
+ timer = mevent_add(50, EVF_TIMER, tick, msg);
+ ASSERT_PTR_NEQ(("mevent_add timer"), timer, NULL);
+
+ pthread_mutex_lock(&mtx);
+ written = write(pipefds[1], msg, strlen(msg));
+ if (written < 0) {
+ FAIL_ERRNO("bad write");
+ }
+ ASSERT_INT64_EQ(("write '%s' failed", msg), written, strlen(msg));
+
+ /*
+ * Wait for timer to expire
+ */
+ pthread_cond_wait(&cv, &mtx);
+ ASSERT_INT_EQ(("wrong lastwake"), lastwake, CB_TIMER);
+ pthread_mutex_unlock(&mtx);
+
+ PASS();
+}
diff --git a/usr/src/cmd/bhyve/test/tst/mevent/read.pause.c b/usr/src/cmd/bhyve/test/tst/mevent/read.pause.c
new file mode 100644
index 0000000000..c877f014f6
--- /dev/null
+++ b/usr/src/cmd/bhyve/test/tst/mevent/read.pause.c
@@ -0,0 +1,152 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Joyent, Inc.
+ */
+
+/*
+ * Test: read.pause
+ * Assertion: mevent_disable() can be used to pause reads.
+ *
+ * Strategy: 1. Create a pipe
+ * 2. Call mevent_add() to be notified of writes to the pipe. The
+ * callback will signal a cv.
+ * 3. In a loop, write to the pipe then wait on the cv.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "testlib.h"
+#include "mevent.h"
+
+static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
+
+static char cookie[] = "Chocolate chip with fudge stripes";
+
+/*
+ * After this many bytes are sent, writes will get batched up, progress will be
+ * made on the write side via an interval timer
+ */
+const int pauseat = 8;
+
+static void
+munch(int fd, enum ev_type ev, void *arg)
+{
+ static int i = 0;
+ char buf[sizeof (cookie)] = { 0 };
+ ssize_t nbytes;
+ ssize_t expected;
+
+ ASSERT_INT_EQ(("bad event"), ev, EVF_READ);
+ ASSERT_PTR_EQ(("bad cookie"), arg, cookie);
+
+ /*
+ * For the first while, expect data to come a byte at a time. After the
+ * pause, we should get a burst with the rest of the data.
+ */
+ if (i > pauseat) {
+ expected = strlen(cookie) - pauseat - 1;
+ } else {
+ expected = 1;
+ }
+
+ if ((nbytes = read(fd, buf, sizeof (buf))) < 0) {
+ FAIL_ERRNO("bad read");
+ }
+ VERBOSE(("read %ld bytes '%s'", nbytes, buf));
+
+ ASSERT_INT64_EQ(("wanted a byte of cookie"), nbytes, expected);
+
+ if (expected == 1) {
+ ASSERT_CHAR_EQ(("bad byte %d of cookie", i), buf[0], cookie[i]);
+ } else {
+ ASSERT_STR_EQ(("bad last half of cookie"), buf, &cookie[i]);
+ }
+
+ pthread_mutex_lock(&mtx);
+ pthread_cond_signal(&cv);
+ VERBOSE(("wakeup"));
+ pthread_mutex_unlock(&mtx);
+
+ i++;
+}
+
+static void
+tick(int ms, enum ev_type ev, void *arg)
+{
+ pthread_mutex_lock(&mtx);
+ pthread_cond_signal(&cv);
+ VERBOSE(("wakeup"));
+ pthread_mutex_unlock(&mtx);
+}
+
+int
+main(int argc, const char *argv[])
+{
+ int pipefds[2];
+ struct mevent *evp, *timer;
+ ssize_t written;
+
+ start_test(argv[0], 5);
+ start_event_thread();
+
+ if (pipe(pipefds) != 0) {
+ FAIL_ERRNO("pipe");
+ }
+ if (fcntl(pipefds[0], F_SETFL, O_NONBLOCK) != 0) {
+ FAIL_ERRNO("set pipe nonblocking");
+ }
+
+ evp = mevent_add(pipefds[0], EVF_READ, munch, cookie);
+ ASSERT_PTR_NEQ(("mevent_add pipefd"), evp, NULL);
+
+ for (int i = 0; cookie[i] != 0; i++) {
+ pthread_mutex_lock(&mtx);
+ written = write(pipefds[1], cookie + i, 1);
+ if (written < 0) {
+ FAIL_ERRNO("bad write");
+ }
+ ASSERT_INT64_EQ(("write byte %d of cookie", i), written, 1);
+
+ /* Wait for it to be read */
+ pthread_cond_wait(&cv, &mtx);
+ pthread_mutex_unlock(&mtx);
+
+ if (i == pauseat) {
+ timer = mevent_add(10, EVF_TIMER, tick,
+ &cookie[pauseat]);
+ ASSERT_PTR_NEQ(("mevent_add timer"), timer, NULL);
+ VERBOSE(("disable munch"));
+ mevent_disable(evp);
+ }
+ }
+
+ pthread_mutex_lock(&mtx);
+
+ mevent_enable(evp);
+
+ pthread_cond_wait(&cv, &mtx);
+ pthread_mutex_unlock(&mtx);
+
+ PASS();
+}
diff --git a/usr/src/cmd/bhyve/test/tst/mevent/read.requeue.c b/usr/src/cmd/bhyve/test/tst/mevent/read.requeue.c
new file mode 100644
index 0000000000..ddc3e27235
--- /dev/null
+++ b/usr/src/cmd/bhyve/test/tst/mevent/read.requeue.c
@@ -0,0 +1,108 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Joyent, Inc.
+ */
+
+/*
+ * Test: read.requeue
+ * Assertion: A sequence of writes turns into a sequence of events.
+ *
+ * Strategy: 1. Create a pipe
+ * 2. Call mevent_add() to be notified of writes to the pipe. The
+ * callback will signal a cv.
+ * 3. In a loop, write to the pipe then wait on the cv.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "testlib.h"
+#include "mevent.h"
+
+static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
+
+static char *cookie = "Chocolate chip with fudge stripes";
+
+static void
+munch(int fd, enum ev_type ev, void *arg)
+{
+ static int i = 0;
+ char buf[8] = { 0 };
+ ssize_t nbytes;
+
+ ASSERT_INT_EQ(("bad event"), ev, EVF_READ);
+ ASSERT_PTR_EQ(("bad cookie"), arg, cookie);
+
+ if ((nbytes = read(fd, buf, sizeof (buf))) < 0) {
+ ASSERT_INT64_EQ(("bad read: %s", strerror(errno)), nbytes, 1);
+ }
+ VERBOSE(("read %ld bytes '%s'", nbytes, buf));
+
+ ASSERT_INT64_EQ(("wanted a byte of cookie"), nbytes, 1);
+
+ ASSERT_CHAR_EQ(("bad byte %d of cookie", i), buf[0], cookie[i]);
+
+ pthread_mutex_lock(&mtx);
+ pthread_cond_signal(&cv);
+ VERBOSE(("wakeup"));
+ pthread_mutex_unlock(&mtx);
+
+ i++;
+}
+
+int
+main(int argc, const char *argv[])
+{
+ int pipefds[2];
+ struct mevent *evp;
+
+ start_test(argv[0], 5);
+ start_event_thread();
+
+ if (pipe(pipefds) != 0) {
+ FAIL_ERRNO("pipe");
+ }
+ if (fcntl(pipefds[0], F_SETFL, O_NONBLOCK) != 0) {
+ FAIL_ERRNO("set pipe nonblocking");
+ }
+
+ evp = mevent_add(pipefds[0], EVF_READ, munch, cookie);
+ ASSERT_PTR_NEQ(("mevent_add"), evp, NULL);
+
+ for (int i = 0; cookie[i] != '\0'; i++) {
+ ssize_t written;
+
+ pthread_mutex_lock(&mtx);
+ written = write(pipefds[1], cookie + i, 1);
+ if (written < 0) {
+ FAIL_ERRNO("bad write");
+ }
+ ASSERT_INT64_EQ(("write byte %d of cookie", i), written, 1);
+
+ /* Wait for it to be read */
+ pthread_cond_wait(&cv, &mtx);
+ pthread_mutex_unlock(&mtx);
+ }
+
+ PASS();
+}
diff --git a/usr/src/cmd/bhyve/test/tst/mevent/testlib.c b/usr/src/cmd/bhyve/test/tst/mevent/testlib.c
new file mode 100644
index 0000000000..af756d1509
--- /dev/null
+++ b/usr/src/cmd/bhyve/test/tst/mevent/testlib.c
@@ -0,0 +1,69 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Joyent, Inc.
+ */
+
+#include <pthread.h>
+#include <signal.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include "testlib.h"
+#include "mevent.h"
+
+const char *testlib_prog;
+boolean_t testlib_verbose;
+
+static void
+timed_out(int signo) {
+ ASSERT_INT_EQ(("timeout signal"), signo, SIGALRM);
+
+ FAIL(("Timed out"));
+}
+
+void
+start_test(const char *argv0, uint32_t timeout)
+{
+ char *val;
+
+ testlib_prog = strrchr(argv0, '/');
+ if (testlib_prog == NULL) {
+ testlib_prog = argv0;
+ } else {
+ testlib_prog++;
+ }
+
+ testlib_verbose = ((val = getenv("TEST_VERBOSE")) != NULL) &&
+ val[0] != '\0';
+
+ signal(SIGALRM, timed_out);
+ alarm(timeout);
+}
+
+/* ARGSUSED */
+static void *
+event_thread(void *arg)
+{
+ mevent_dispatch();
+ return (NULL);
+}
+
+void
+start_event_thread(void)
+{
+ pthread_t tid;
+
+ if (pthread_create(&tid, NULL, event_thread, NULL) != 0) {
+ FAIL_ERRNO("pthread_create");
+ }
+}
diff --git a/usr/src/cmd/bhyve/test/tst/mevent/testlib.h b/usr/src/cmd/bhyve/test/tst/mevent/testlib.h
new file mode 100644
index 0000000000..80949f3cc7
--- /dev/null
+++ b/usr/src/cmd/bhyve/test/tst/mevent/testlib.h
@@ -0,0 +1,88 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Joyent, Inc.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "mevent.h"
+
+#define EXIT_PASS 0
+#define EXIT_FAIL 1
+
+#define VERBOSE(msg) \
+ if (testlib_verbose) { \
+ (void) printf("VERBOSE %s: %s:%d %s: ", testlib_prog, \
+ __FILE__, __LINE__, __func__); \
+ (void) printf msg; \
+ (void) printf("\n"); \
+ }
+
+#define FAIL_PROLOGUE() \
+ (void) printf("FAIL %s: %s:%d: ", testlib_prog, __FILE__, __LINE__)
+
+#define FAIL(msg) \
+ { \
+ FAIL_PROLOGUE(); \
+ (void) printf msg; \
+ (void) printf("\n"); \
+ exit(EXIT_FAIL); \
+ }
+
+#define FAIL_ERRNO(msg) FAIL((msg ": %s", strerror(errno)))
+
+#define PASS() \
+ { \
+ (void) printf("PASS %s\n", testlib_prog); \
+ exit(EXIT_PASS); \
+ }
+
+#define ASSERT_CMP(msg, got, cmp, exp, nfmt) \
+ if (!(got cmp exp)) { \
+ FAIL_PROLOGUE(); \
+ (void) printf msg; \
+ (void) printf(": %s=" nfmt " %s %s=" nfmt "\n", \
+ #got, got, #cmp, #exp, exp); \
+ exit(EXIT_FAIL); \
+ }
+
+#define ASSERT_CHAR_EQ(msg, got, exp) ASSERT_CMP(msg, got, ==, exp, "%c")
+#define ASSERT_INT_EQ(msg, got, exp) ASSERT_CMP(msg, got, ==, exp, "%d")
+#define ASSERT_INT_NEQ(msg, got, exp) ASSERT_CMP(msg, got, !=, exp, "%d")
+#define ASSERT_INT64_EQ(msg, got, exp) ASSERT_CMP(msg, got, ==, exp, "%ld")
+#define ASSERT_PTR_EQ(msg, got, exp) ASSERT_CMP(msg, got, ==, exp, "%p")
+#define ASSERT_PTR_NEQ(msg, got, exp) ASSERT_CMP(msg, got, !=, exp, "%p")
+
+#define ASSERT_STR_EQ(msg, got, exp) \
+ if (strcmp(got, exp) != 0) { \
+ FAIL_PROLOGUE(); \
+ (void) printf msg; \
+ (void) printf(": %s='%s' != %s='%s'\n", \
+ #got, got, #exp, exp); \
+ exit(EXIT_FAIL); \
+ }
+
+extern const char *testlib_prog;
+extern boolean_t testlib_verbose;
+
+extern void start_test(const char *, uint32_t);
+extern void start_event_thread(void);
+extern void test_mevent_count_lists(int *, int *, int *);
diff --git a/usr/src/cmd/bhyve/uart_emul.c b/usr/src/cmd/bhyve/uart_emul.c
index b1bf2dbfec..167a9bc42a 100644
--- a/usr/src/cmd/bhyve/uart_emul.c
+++ b/usr/src/cmd/bhyve/uart_emul.c
@@ -26,7 +26,6 @@
*
* $FreeBSD$
*
- * Copyright (c) 2018, Joyent, Inc.
*/
/*
* This file and its contents are supplied under the terms of the
@@ -39,7 +38,7 @@
* http://www.illumos.org/license/CDDL.
*
* Copyright 2015 Pluribus Networks Inc.
- * Copyright 2017 Joyent, Inc.
+ * Copyright 2018 Joyent, Inc.
*/
#include <sys/cdefs.h>
@@ -73,14 +72,7 @@ __FBSDID("$FreeBSD$");
#include <poll.h>
#endif
-#ifndef __FreeBSD__
-#include <bhyve.h>
-
-#include "bhyverun.h"
-#endif
-#ifdef __FreeBSD__
#include "mevent.h"
-#endif
#include "uart_emul.h"
#define COM1_BASE 0x3F8
@@ -134,7 +126,7 @@ struct ttyfd {
struct uart_softc {
pthread_mutex_t mtx; /* protects all softc elements */
- uint8_t data; /* Data register (R/W) */
+ uint8_t data; /* Data register (R/W) */
uint8_t ier; /* Interrupt enable register (R/W) */
uint8_t lcr; /* Line control register (R/W) */
uint8_t mcr; /* Modem control register (R/W) */
@@ -147,9 +139,7 @@ struct uart_softc {
uint8_t dlh; /* Baudrate divisor latch MSB */
struct fifo rxfifo;
-#ifdef __FreeBSD__
struct mevent *mev;
-#endif
struct ttyfd tty;
#ifndef __FreeBSD__
@@ -167,12 +157,8 @@ struct uart_softc {
uart_intr_func_t intr_deassert;
};
-#ifdef __FreeBSD__
static void uart_drain(int fd, enum ev_type ev, void *arg);
-#else
-static void uart_tty_drain(struct uart_softc *sc);
static int uart_sock_drain(struct uart_softc *sc);
-#endif
static void
ttyclose(void)
@@ -230,9 +216,7 @@ rxfifo_reset(struct uart_softc *sc, int size)
char flushbuf[32];
struct fifo *fifo;
ssize_t nread;
-#ifdef __FreeBSD__
int error;
-#endif
fifo = &sc->rxfifo;
bzero(fifo, sizeof(struct fifo));
@@ -248,14 +232,12 @@ rxfifo_reset(struct uart_softc *sc, int size)
break;
}
-#ifdef __FreeBSD__
/*
* Enable mevent to trigger when new characters are available
* on the tty fd.
*/
error = mevent_enable(sc->mev);
assert(error == 0);
-#endif
}
}
@@ -272,9 +254,7 @@ static int
rxfifo_putchar(struct uart_softc *sc, uint8_t ch)
{
struct fifo *fifo;
-#ifdef __FreeBSD__
int error;
-#endif
fifo = &sc->rxfifo;
@@ -284,13 +264,11 @@ rxfifo_putchar(struct uart_softc *sc, uint8_t ch)
fifo->num++;
if (!rxfifo_available(sc)) {
if (sc->tty.opened) {
-#ifdef __FreeBSD__
/*
* Disable mevent callback if the FIFO is full.
*/
error = mevent_disable(sc->mev);
assert(error == 0);
-#endif
}
}
return (0);
@@ -302,10 +280,7 @@ static int
rxfifo_getchar(struct uart_softc *sc)
{
struct fifo *fifo;
- int c, wasfull;
-#ifdef __FreeBSD__
- int error;
-#endif
+ int c, error, wasfull;
wasfull = 0;
fifo = &sc->rxfifo;
@@ -317,10 +292,8 @@ rxfifo_getchar(struct uart_softc *sc)
fifo->num--;
if (wasfull) {
if (sc->tty.opened) {
-#ifdef __FreeBSD__
error = mevent_enable(sc->mev);
assert(error == 0);
-#endif
}
}
return (c);
@@ -340,10 +313,8 @@ static void
uart_opentty(struct uart_softc *sc)
{
ttyopen(&sc->tty);
-#ifdef __FreeBSD__
sc->mev = mevent_add(sc->tty.fd, EVF_READ, uart_drain, sc);
assert(sc->mev != NULL);
-#endif
}
static uint8_t
@@ -431,7 +402,6 @@ uart_toggle_intr(struct uart_softc *sc)
(*sc->intr_assert)(sc->arg);
}
-#ifdef __FreeBSD__
static void
uart_drain(int fd, enum ev_type ev, void *arg)
{
@@ -462,30 +432,6 @@ uart_drain(int fd, enum ev_type ev, void *arg)
pthread_mutex_unlock(&sc->mtx);
}
-#else
-static void
-uart_tty_drain(struct uart_softc *sc)
-{
- int ch;
-
- /*
- * Take the softc lock to protect against concurrent
- * access from a vCPU i/o exit
- */
- pthread_mutex_lock(&sc->mtx);
-
- if ((sc->mcr & MCR_LOOPBACK) != 0) {
- (void) ttyread(&sc->tty);
- } else {
- while (rxfifo_available(sc) &&
- ((ch = ttyread(&sc->tty)) != -1)) {
- rxfifo_putchar(sc, ch);
- }
- uart_toggle_intr(sc);
- }
-
- pthread_mutex_unlock(&sc->mtx);
-}
static int
uart_sock_drain(struct uart_softc *sc)
@@ -518,9 +464,7 @@ uart_sock_drain(struct uart_softc *sc)
break;
}
- if (rxfifo_available(sc)) {
- rxfifo_putchar(sc, ch);
- }
+ rxfifo_putchar(sc, ch);
}
uart_toggle_intr(sc);
}
@@ -529,7 +473,6 @@ uart_sock_drain(struct uart_softc *sc)
return (ret);
}
-#endif
void
uart_write(struct uart_softc *sc, int offset, uint8_t value)
@@ -739,29 +682,6 @@ done:
}
#ifndef __FreeBSD__
-static void *
-uart_tty_thread(void *param)
-{
- struct uart_softc *sc = param;
- pollfd_t pollset;
-
- pollset.fd = sc->tty.fd;
- pollset.events = POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND;
-
- for (;;) {
- if (poll(&pollset, 1, -1) < 0) {
- if (errno != EINTR) {
- perror("poll failed");
- break;
- }
- continue;
- }
- uart_tty_drain(sc);
- }
-
- return (NULL);
-}
-
static int
uart_sock_accept_client(struct uart_softc *sc)
{
@@ -1036,9 +956,6 @@ uart_sock_backend(struct uart_softc *sc, const char *inopts)
int
uart_set_backend(struct uart_softc *sc, const char *opts)
{
-#ifndef __FreeBSD__
- int error;
-#endif
int retval;
#ifndef WITHOUT_CAPSICUM
cap_rights_t rights;
@@ -1087,13 +1004,8 @@ uart_set_backend(struct uart_softc *sc, const char *opts)
}
#endif
- if (retval == 0) {
+ if (retval == 0)
uart_opentty(sc);
-#ifndef __FreeBSD__
- error = pthread_create(NULL, NULL, uart_tty_thread, sc);
- assert(error == 0);
-#endif
- }
return (retval);
}