summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgrehan <grehan@FreeBSD.org>2013-09-19 04:48:26 +0000
committergrehan <grehan@FreeBSD.org>2013-09-19 04:48:26 +0000
commit9239c077f48bfb9780d2363774dc281f97039207 (patch)
tree2ff892f5419849df594045ba931626781c581a50
parentbc1d6700f21955a8477239bfa33a11db0f8917c8 (diff)
downloadFreeBSD-src-9239c077f48bfb9780d2363774dc281f97039207.zip
FreeBSD-src-9239c077f48bfb9780d2363774dc281f97039207.tar.gz
Add simplistic periodic timer support to mevent using kqueue's
timer support. This should be enough for the emulation of h/w periodic timers (and no more) e.g. some of the 8254's more esoteric modes that happen to be used by non-FreeBSD o/s's. Approved by: re@ (blanket)
-rw-r--r--usr.sbin/bhyve/mevent.c33
-rw-r--r--usr.sbin/bhyve/mevent.h3
-rw-r--r--usr.sbin/bhyve/mevent_test.c76
3 files changed, 104 insertions, 8 deletions
diff --git a/usr.sbin/bhyve/mevent.c b/usr.sbin/bhyve/mevent.c
index a6109db..eff0610 100644
--- a/usr.sbin/bhyve/mevent.c
+++ b/usr.sbin/bhyve/mevent.c
@@ -59,12 +59,15 @@ __FBSDID("$FreeBSD$");
extern char *vmname;
static pthread_t mevent_tid;
+static int mevent_timid = 43;
static int mevent_pipefd[2];
static pthread_mutex_t mevent_lmutex = PTHREAD_MUTEX_INITIALIZER;
struct mevent {
void (*me_func)(int, enum ev_type, void *);
+#define me_msecs me_fd
int me_fd;
+ int me_timid;
enum ev_type me_type;
void *me_param;
int me_cq;
@@ -129,6 +132,9 @@ mevent_kq_filter(struct mevent *mevp)
if (mevp->me_type == EVF_WRITE)
retval = EVFILT_WRITE;
+ if (mevp->me_type == EVF_TIMER)
+ retval = EVFILT_TIMER;
+
return (retval);
}
@@ -140,6 +146,8 @@ mevent_kq_flags(struct mevent *mevp)
switch (mevp->me_state) {
case MEV_ENABLE:
ret = EV_ADD;
+ if (mevp->me_type == EVF_TIMER)
+ ret |= EV_ENABLE;
break;
case MEV_DISABLE:
ret = EV_DISABLE;
@@ -177,11 +185,16 @@ mevent_build(int mfd, struct kevent *kev)
*/
close(mevp->me_fd);
} else {
- kev[i].ident = mevp->me_fd;
+ if (mevp->me_type == EVF_TIMER) {
+ kev[i].ident = mevp->me_timid;
+ kev[i].data = mevp->me_msecs;
+ } else {
+ kev[i].ident = mevp->me_fd;
+ kev[i].data = 0;
+ }
kev[i].filter = mevent_kq_filter(mevp);
kev[i].flags = mevent_kq_flags(mevp);
kev[i].fflags = mevent_kq_fflags(mevp);
- kev[i].data = 0;
kev[i].udata = mevp;
i++;
}
@@ -219,12 +232,12 @@ mevent_handle(struct kevent *kev, int numev)
}
struct mevent *
-mevent_add(int fd, enum ev_type type,
+mevent_add(int tfd, enum ev_type type,
void (*func)(int, enum ev_type, void *), void *param)
{
struct mevent *lp, *mevp;
- if (fd < 0 || func == NULL) {
+ if (tfd < 0 || func == NULL) {
return (NULL);
}
@@ -236,13 +249,15 @@ mevent_add(int fd, enum ev_type type,
* Verify that the fd/type tuple is not present in any list
*/
LIST_FOREACH(lp, &global_head, me_list) {
- if (lp->me_fd == fd && lp->me_type == type) {
+ if (type != EVF_TIMER && lp->me_fd == tfd &&
+ lp->me_type == type) {
goto exit;
}
}
LIST_FOREACH(lp, &change_head, me_list) {
- if (lp->me_fd == fd && lp->me_type == type) {
+ if (type != EVF_TIMER && lp->me_fd == tfd &&
+ lp->me_type == type) {
goto exit;
}
}
@@ -256,7 +271,11 @@ mevent_add(int fd, enum ev_type type,
}
memset(mevp, 0, sizeof(struct mevent));
- mevp->me_fd = fd;
+ if (type == EVF_TIMER) {
+ mevp->me_msecs = tfd;
+ mevp->me_timid = mevent_timid++;
+ } else
+ mevp->me_fd = tfd;
mevp->me_type = type;
mevp->me_func = func;
mevp->me_param = param;
diff --git a/usr.sbin/bhyve/mevent.h b/usr.sbin/bhyve/mevent.h
index 32a9d74..6c0f656 100644
--- a/usr.sbin/bhyve/mevent.h
+++ b/usr.sbin/bhyve/mevent.h
@@ -31,7 +31,8 @@
enum ev_type {
EVF_READ,
- EVF_WRITE
+ EVF_WRITE,
+ EVF_TIMER
};
struct mevent;
diff --git a/usr.sbin/bhyve/mevent_test.c b/usr.sbin/bhyve/mevent_test.c
index c72a497..9c68ff7 100644
--- a/usr.sbin/bhyve/mevent_test.c
+++ b/usr.sbin/bhyve/mevent_test.c
@@ -34,12 +34,16 @@
*/
#include <sys/types.h>
+#include <sys/stdint.h>
+#include <sys/sysctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
+#include <machine/cpufunc.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
+#include <unistd.h>
#include "mevent.h"
@@ -48,8 +52,62 @@
static pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t accept_condvar = PTHREAD_COND_INITIALIZER;
+static struct mevent *tevp;
+
+char *vmname = "test vm";
+
+
#define MEVENT_ECHO
+/* Number of timer events to capture */
+#define TEVSZ 4096
+uint64_t tevbuf[TEVSZ];
+
+static void
+timer_print(void)
+{
+ uint64_t min, max, diff, sum, tsc_freq;
+ size_t len;
+ int j;
+
+ min = UINT64_MAX;
+ max = 0;
+ sum = 0;
+
+ len = sizeof(tsc_freq);
+ sysctlbyname("machdep.tsc_freq", &tsc_freq, &len, NULL, 0);
+
+ for (j = 1; j < TEVSZ; j++) {
+ /* Convert a tsc diff into microseconds */
+ diff = (tevbuf[j] - tevbuf[j-1]) * 1000000 / tsc_freq;
+ sum += diff;
+ if (min > diff)
+ min = diff;
+ if (max < diff)
+ max = diff;
+ }
+
+ printf("timers done: usecs, min %ld, max %ld, mean %ld\n", min, max,
+ sum/(TEVSZ - 1));
+}
+
+static void
+timer_callback(int fd, enum ev_type type, void *param)
+{
+ static int i;
+
+ if (i >= TEVSZ)
+ abort();
+
+ tevbuf[i++] = rdtsc();
+
+ if (i == TEVSZ) {
+ mevent_delete(tevp);
+ timer_print();
+ }
+}
+
+
#ifdef MEVENT_ECHO
struct esync {
pthread_mutex_t e_mt;
@@ -101,6 +159,8 @@ echoer(void *param)
pthread_mutex_unlock(&sync.e_mt);
pthread_mutex_destroy(&sync.e_mt);
pthread_cond_destroy(&sync.e_cond);
+
+ return (NULL);
}
#else
@@ -115,6 +175,8 @@ echoer(void *param)
while ((len = read(fd, buf, sizeof(buf))) > 0) {
write(1, buf, len);
}
+
+ return (NULL);
}
#endif /* MEVENT_ECHO */
@@ -133,6 +195,7 @@ acceptor(void *param)
pthread_t tid;
int news;
int s;
+ static int first;
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
@@ -163,11 +226,24 @@ acceptor(void *param)
if (news < 0) {
perror("accept error");
} else {
+ static int first = 1;
+
+ if (first) {
+ /*
+ * Start a timer
+ */
+ first = 0;
+ tevp = mevent_add(1, EVF_TIMER, timer_callback,
+ NULL);
+ }
+
printf("incoming connection, spawning thread\n");
pthread_create(&tid, NULL, echoer,
(void *)(uintptr_t)news);
}
}
+
+ return (NULL);
}
main()
OpenPOWER on IntegriCloud