tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Periodic clock synchronization in vmt(4)



Hello,

I'd like to change the vmt(4) driver so that it synchronizes the clock
of the virtual machine on a periodic basis.  This is necessary so that
the clock remains synchronized when the *host* is suspended (very
common occurrence in laptops, as you'd imagine) because in this case
VMware does not notify the guest of such event.

Could anyone please review the attached patch?  It appears to work as
intended, but the sysctl API is really confusing and I don't know if
I got all details right.

Thanks.
Index: sys/arch/x86/x86/vmt.c
===================================================================
RCS file: /cvsroot/src/sys/arch/x86/x86/vmt.c,v
retrieving revision 1.7
diff -u -p -r1.7 vmt.c
--- sys/arch/x86/x86/vmt.c      21 Oct 2011 10:10:28 -0000      1.7
+++ sys/arch/x86/x86/vmt.c      14 Mar 2013 01:12:40 -0000
@@ -36,6 +36,7 @@
 #include <sys/socket.h>
 #include <sys/timetc.h>
 #include <sys/module.h>
+#include <sys/sysctl.h>
 
 #include <net/if.h>
 #include <netinet/in.h>
@@ -182,6 +183,7 @@ struct vmt_event {
 struct vmt_softc {
        device_t                sc_dev;
 
+       struct sysctllog        *sc_log;
        struct vm_rpc           sc_tclo_rpc;
        bool                    sc_tclo_rpc_open;
        char                    *sc_rpc_buf;
@@ -193,6 +195,10 @@ struct vmt_softc {
        struct callout          sc_tick;
        struct callout          sc_tclo_tick;
 
+#define VMT_CLOCK_SYNC_PERIOD_SECONDS 60
+       int                     sc_clock_sync_period_seconds;
+       struct callout          sc_clock_sync_tick;
+
        struct vmt_event        sc_ev_power;
        struct vmt_event        sc_ev_reset;
        struct vmt_event        sc_ev_sleep;
@@ -204,6 +210,10 @@ struct vmt_softc {
 CFATTACH_DECL_NEW(vmt, sizeof(struct vmt_softc),
        vmt_match, vmt_attach, vmt_detach, NULL);
 
+static int vmt_sysctl_setup_root(device_t);
+static int vmt_sysctl_setup_clock_sync(device_t, const struct sysctlnode *);
+static int vmt_sysctl_update_clock_sync_period(SYSCTLFN_PROTO);
+
 static void vm_cmd(struct vm_backdoor *);
 static void vm_ins(struct vm_backdoor *);
 static void vm_outs(struct vm_backdoor *);
@@ -230,6 +240,7 @@ static void vmt_sync_guest_clock(struct 
 
 static void vmt_tick(void *);
 static void vmt_tclo_tick(void *);
+static void vmt_clock_sync_tick(void *);
 static bool vmt_shutdown(device_t, int);
 static void vmt_pswitch_event(void *);
 
@@ -294,14 +305,27 @@ vmt_type(void)
 static void
 vmt_attach(device_t parent, device_t self, void *aux)
 {
+       int rv;
        struct vmt_softc *sc = device_private(self);
 
        aprint_naive("\n");
        aprint_normal(": %s\n", vmt_type());
 
        sc->sc_dev = self;
+       sc->sc_log = NULL;
+
        callout_init(&sc->sc_tick, 0);
        callout_init(&sc->sc_tclo_tick, 0);
+       callout_init(&sc->sc_clock_sync_tick, 0);
+
+       sc->sc_clock_sync_period_seconds = VMT_CLOCK_SYNC_PERIOD_SECONDS;
+
+       rv = vmt_sysctl_setup_root(self);
+       if (rv != 0) {
+               aprint_error_dev(self, "failed to initialize sysctl "
+                   "(err %d)\n", rv);
+               goto free;
+       }
 
        sc->sc_rpc_buf = kmem_alloc(VMT_RPC_BUFLEN, KM_SLEEP);
        if (sc->sc_rpc_buf == NULL) {
@@ -346,6 +370,10 @@ vmt_attach(device_t parent, device_t sel
        callout_schedule(&sc->sc_tclo_tick, hz);
        sc->sc_tclo_ping = 1;
 
+       callout_setfunc(&sc->sc_clock_sync_tick, vmt_clock_sync_tick, sc);
+       callout_schedule(&sc->sc_clock_sync_tick,
+           mstohz(sc->sc_clock_sync_period_seconds * 1000));
+
        vmt_sync_guest_clock(sc);
 
        return;
@@ -354,6 +382,8 @@ free:
        if (sc->sc_rpc_buf)
                kmem_free(sc->sc_rpc_buf, VMT_RPC_BUFLEN);
        pmf_device_register(self, NULL, NULL);
+       if (sc->sc_log)
+               sysctl_teardown(&sc->sc_log);
 }
 
 static int
@@ -376,12 +406,110 @@ vmt_detach(device_t self, int flags)
        callout_halt(&sc->sc_tclo_tick, NULL);
        callout_destroy(&sc->sc_tclo_tick);
 
+       callout_halt(&sc->sc_clock_sync_tick, NULL);
+       callout_destroy(&sc->sc_clock_sync_tick);
+
        if (sc->sc_rpc_buf)
                kmem_free(sc->sc_rpc_buf, VMT_RPC_BUFLEN);
 
+       if (sc->sc_log) {
+               sysctl_teardown(&sc->sc_log);
+               sc->sc_log = NULL;
+       }
+
        return 0;
 }
 
+static int
+vmt_sysctl_setup_root(device_t self)
+{
+       const struct sysctlnode *machdep_node, *vmt_node;
+       struct vmt_softc *sc = device_private(self);
+       int rv;
+
+       rv = sysctl_createv(&sc->sc_log, 0, NULL, &machdep_node,
+           CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
+           NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
+       if (rv != 0)
+               goto fail;
+
+       rv = sysctl_createv(&sc->sc_log, 0, &machdep_node, &vmt_node,
+           0, CTLTYPE_NODE, "vmt", NULL,
+           NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
+       if (rv != 0)
+               goto fail;
+
+       rv = vmt_sysctl_setup_clock_sync(self, vmt_node);
+       if (rv != 0)
+               goto fail;
+
+       return 0;
+
+fail:
+       sysctl_teardown(&sc->sc_log);
+       sc->sc_log = NULL;
+
+       return rv;
+}
+
+static int
+vmt_sysctl_setup_clock_sync(device_t self, const struct sysctlnode *root_node)
+{
+       const struct sysctlnode *node, *period_node;
+       struct vmt_softc *sc = device_private(self);
+       int rv;
+
+       rv = sysctl_createv(&sc->sc_log, 0, &root_node, &node,
+           0, CTLTYPE_NODE, "clock_sync", NULL,
+           NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
+       if (rv != 0)
+               return rv;
+
+       rv = sysctl_createv(&sc->sc_log, 0, &node, &period_node,
+           CTLFLAG_READWRITE, CTLTYPE_INT, "period",
+           "Period, in seconds, at which to update the guest's clock",
+           vmt_sysctl_update_clock_sync_period, 0, (void *)sc, 0,
+           CTL_CREATE, CTL_EOL);
+       return rv;
+}
+
+static int
+vmt_sysctl_update_clock_sync_period(SYSCTLFN_ARGS)
+{
+       int error, period;
+       struct sysctlnode node;
+       struct vmt_softc *sc;
+
+       node = *rnode;
+       sc = (struct vmt_softc *)node.sysctl_data;
+
+       period = sc->sc_clock_sync_period_seconds;
+       node.sysctl_data = &period;
+       error = sysctl_lookup(SYSCTLFN_CALL(&node));
+       if (error || newp == NULL)
+               return error;
+
+       if (sc->sc_clock_sync_period_seconds != period) {
+               callout_halt(&sc->sc_clock_sync_tick, NULL);
+               sc->sc_clock_sync_period_seconds = period;
+               if (sc->sc_clock_sync_period_seconds > 0)
+                       callout_schedule(&sc->sc_clock_sync_tick,
+                           mstohz(sc->sc_clock_sync_period_seconds * 1000));
+       }
+       return 0;
+}
+
+static void
+vmt_clock_sync_tick(void *xarg)
+{
+       struct vmt_softc *sc = xarg;
+
+       vmt_sync_guest_clock(sc);
+
+       callout_schedule(&sc->sc_clock_sync_tick,
+           mstohz(sc->sc_clock_sync_period_seconds * 1000));
+}
+
 static void
 vmt_update_guest_uptime(struct vmt_softc *sc)
 {
Index: share/man/man4/man4.x86/vmt.4
===================================================================
RCS file: /cvsroot/src/share/man/man4/man4.x86/vmt.4,v
retrieving revision 1.3
diff -u -p -r1.3 vmt.4
--- share/man/man4/man4.x86/vmt.4       18 Oct 2011 14:25:06 -0000      1.3
+++ share/man/man4/man4.x86/vmt.4       14 Mar 2013 01:12:40 -0000
@@ -15,7 +15,7 @@
 .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 .\" ACTION OF CONTRACT, NEGLIGENCE OR TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.Dd October 18, 2011
+.Dd March 13, 2013
 .Dt VMT 4 x86
 .Os
 .Sh NAME
@@ -44,6 +44,24 @@ host.
 .Pp
 .Nm
 reports the guest's hostname and first non-loopback IP address to the host.
+.Ss Clock synchronization
+The
+.Nm
+driver synchronizes the virtual machine's clock with the host clock in the
+following situations:
+.Bl -bullet
+.It
+When the virtual machine resumes after having been suspended.
+.It
+Periodically with the interval indicated by the
+.Va machdep.vmt.clock_sync.period
+.Xr sysctl 8
+variable.
+This is done so that the virtual machine can keep its clock synchronized
+when the host is suspended, because in this case the
+.Nm
+driver receives no notification of such event.
+.El
 .Sh SEE ALSO
 .\" .Xr cpu 4 ,
 .Xr powerd 8


Home | Main Index | Thread Index | Old Index