Richard W.M. Jones
2019-Sep-28 09:25 UTC
[Libguestfs] [PATCH nbdkit 1/2] common/include: Add function for subtracting struct timeval.
--- common/include/test-tvdiff.c | 75 +++++++++++++++++++++++++++++------- common/include/tvdiff.h | 13 ++++++- 2 files changed, 74 insertions(+), 14 deletions(-) diff --git a/common/include/test-tvdiff.c b/common/include/test-tvdiff.c index 9cbcfc0..abefb2e 100644 --- a/common/include/test-tvdiff.c +++ b/common/include/test-tvdiff.c @@ -36,7 +36,6 @@ #include <stdlib.h> #include <stdint.h> #include <inttypes.h> -#include <assert.h> #include <sys/time.h> #include "tvdiff.h" @@ -45,37 +44,87 @@ * implementation. */ +#define TEST_TVDIFF(tv1, tv2, expected) \ + do { \ + int64_t actual = tvdiff_usec (&(tv1), &(tv2)); \ + \ + if (actual != (expected)) { \ + fprintf (stderr, \ + "%s: unexpected result %" PRIi64 ", expecting %" PRIi64 "\n", \ + argv[0], actual, (int64_t) (expected)); \ + errors++; \ + } \ + } while (0) + +#define TEST_SUBTRACT(tv1, tv2, exp_sec, exp_usec) \ + do { \ + struct timeval z; \ + \ + subtract_timeval (&tv1, &tv2, &z); \ + if (z.tv_sec != (exp_sec) || z.tv_usec != (exp_usec)) { \ + fprintf (stderr, \ + "%s: unexpected (%ld, %d), expecting (%ld, %d)\n", \ + argv[0], \ + (long) z.tv_sec, (int) z.tv_usec, \ + (long) (exp_sec), (int) (exp_usec)); \ + errors++; \ + } \ + } while (0) + int -main (void) +main (int argc, char *argv[]) { struct timeval tv1, tv2; + unsigned errors = 0; tv1.tv_sec = 1000; tv1.tv_usec = 1; - assert (tvdiff_usec (&tv1, &tv1) == 0); + TEST_TVDIFF (tv1, tv1, 0); + TEST_SUBTRACT (tv1, tv1, 0, 0); + tv2.tv_sec = 1000; tv2.tv_usec = 2; - assert (tvdiff_usec (&tv1, &tv2) == 1); - assert (tvdiff_usec (&tv2, &tv1) == -1); + TEST_TVDIFF (tv1, tv2, 1); + TEST_SUBTRACT (tv1, tv2, 0, 1); + TEST_TVDIFF (tv2, tv1, -1); + TEST_SUBTRACT (tv2, tv1, 0, -1); + tv2.tv_sec = 1000; tv2.tv_usec = 3; - assert (tvdiff_usec (&tv1, &tv2) == 2); - assert (tvdiff_usec (&tv2, &tv1) == -2); + TEST_TVDIFF (tv1, tv2, 2); + TEST_SUBTRACT (tv1, tv2, 0, 2); + TEST_TVDIFF (tv2, tv1, -2); + TEST_SUBTRACT (tv2, tv1, 0, -2); + tv2.tv_sec = 1001; tv2.tv_usec = 0; - assert (tvdiff_usec (&tv1, &tv2) == 999999); - assert (tvdiff_usec (&tv2, &tv1) == -999999); + TEST_TVDIFF (tv1, tv2, 999999); + TEST_SUBTRACT (tv1, tv2, 0, 999999); + TEST_TVDIFF (tv2, tv1, -999999); + TEST_SUBTRACT (tv2, tv1, 0, -999999); tv1.tv_sec = 1000; tv1.tv_usec = 999999; tv2.tv_sec = 1001; tv2.tv_usec = 1; - assert (tvdiff_usec (&tv1, &tv2) == 2); - assert (tvdiff_usec (&tv2, &tv1) == -2); + TEST_TVDIFF (tv1, tv2, 2); + TEST_SUBTRACT (tv1, tv2, 0, 2); + TEST_TVDIFF (tv2, tv1, -2); + TEST_SUBTRACT (tv2, tv1, 0, -2); + + tv1.tv_sec = 1000; + tv1.tv_usec = 1; + tv2.tv_sec = 1001; + tv2.tv_usec = 2; + TEST_TVDIFF (tv1, tv2, 1000001); + TEST_SUBTRACT (tv1, tv2, 1, 1); + TEST_TVDIFF (tv2, tv1, -1000001); + TEST_SUBTRACT (tv2, tv1, -1, -1); /* Test that an arbitrary tv is equal to itself. */ gettimeofday (&tv1, NULL); - assert (tvdiff_usec (&tv1, &tv1) == 0); + TEST_TVDIFF (tv1, tv1, 0); + TEST_SUBTRACT (tv1, tv1, 0, 0); - exit (EXIT_SUCCESS); + exit (errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE); } diff --git a/common/include/tvdiff.h b/common/include/tvdiff.h index db86b66..cec8345 100644 --- a/common/include/tvdiff.h +++ b/common/include/tvdiff.h @@ -40,7 +40,7 @@ #include <sys/time.h> /* Return the number of µs (microseconds) in y - x. */ -static int64_t +static inline int64_t tvdiff_usec (const struct timeval *x, const struct timeval *y) { int64_t usec; @@ -50,4 +50,15 @@ tvdiff_usec (const struct timeval *x, const struct timeval *y) return usec; } +/* Return timeval difference as another struct timeval. z = y - x. */ +static inline void +subtract_timeval (const struct timeval *x, const struct timeval *y, + struct timeval *z) +{ + int64_t usec = tvdiff_usec (x, y); + + z->tv_sec = usec / 1000000; + z->tv_usec = usec % 1000000; +} + #endif /* NBDKIT_TVDIFF_H */ -- 2.23.0
Richard W.M. Jones
2019-Sep-28 09:25 UTC
[Libguestfs] [PATCH nbdkit 2/2] reflection: Add mode for reflecting server time.
Either wallclock time, uptime or time since client connection can be reflected back to the client in a big endian binary structure. $ nbdkit reflection time --run 'nbdsh --connect $uri -c "sys.stdout.buffer.write(h.pread(12,0))" | hexdump -C' 00000000 00 00 00 00 5d 8f 24 c7 00 04 24 01 \ | / / $ date --date="@$(( 0x5d8f24c7 ))" Sat 28 Sep 10:15:51 BST 2019 $ nbdkit reflection uptime --run 'nbdsh --connect $uri -c "sys.stdout.buffer.write(h.pread(12,0))" | hexdump -C' 00000000 00 00 00 00 00 00 00 00 00 00 60 4b | | 0x604b is about 25ms $ nbdkit reflection conntime --run 'nbdsh --connect $uri -c "sys.stdout.buffer.write(h.pread(12,0))" | hexdump -C' 00000000 00 00 00 00 00 00 00 00 00 00 00 e0 | 0xe0 is about 200μs Suggested by Eric Blake. --- plugins/reflection/Makefile.am | 1 + .../reflection/nbdkit-reflection-plugin.pod | 41 ++++++++- plugins/reflection/reflection.c | 90 ++++++++++++++++++- 3 files changed, 128 insertions(+), 4 deletions(-) diff --git a/plugins/reflection/Makefile.am b/plugins/reflection/Makefile.am index 40aa786..9544d98 100644 --- a/plugins/reflection/Makefile.am +++ b/plugins/reflection/Makefile.am @@ -42,6 +42,7 @@ nbdkit_reflection_plugin_la_SOURCES = \ nbdkit_reflection_plugin_la_CPPFLAGS = \ -I$(top_srcdir)/include \ + -I$(top_srcdir)/common/include \ $(NULL) nbdkit_reflection_plugin_la_CFLAGS = $(WARNINGS_CFLAGS) nbdkit_reflection_plugin_la_LDFLAGS = \ diff --git a/plugins/reflection/nbdkit-reflection-plugin.pod b/plugins/reflection/nbdkit-reflection-plugin.pod index f971cef..09deb26 100644 --- a/plugins/reflection/nbdkit-reflection-plugin.pod +++ b/plugins/reflection/nbdkit-reflection-plugin.pod @@ -4,7 +4,8 @@ nbdkit-reflection-plugin - reflect client info back to the client =head1 SYNOPSIS - nbdkit reflection [mode=]exportname|base64exportname|address + nbdkit reflection [mode=]exportname|base64exportname|address| + time|uptime|conntime =head1 DESCRIPTION @@ -22,6 +23,10 @@ than this. C<mode=address> creates a disk which contains the client's IP address and port number as a string. +C<mode=time>, C<mode=uptime> and C<mode=conntime> report server +wallclock time, nbdkit uptime, or time since the connection was opened +respectively and may be used to measure latency. + The plugin only supports read-only access. To make the disk writable, add L<nbdkit-cow-filter(1)> on top. @@ -103,6 +108,40 @@ name cannot contain ASCII NUL characters. This is the default mode. +=item [B<mode=>]B<time> + +Reflect server wallclock time as seconds and microseconds since the +Epoch (see L<gettimeofday(2)>): + + ┌────────┬────────┬────────────┬──────────────────────┐ + │ offset │ length │ format │ field │ + ╞════════╪════════╪════════════╪══════════════════════╡ + │ 0 │ 8 │ 64 bit int │ seconds │ + │ │ │ big endian │ │ + ├────────┼────────┼────────────┼──────────────────────┤ + │ 8 │ 4 │ 32 bit int │ microseconds │ + │ │ │ big endian │ │ + └────────┴────────┴────────────┴──────────────────────┘ + +To be able to read this atomically you must read the whole 12 bytes in +a single request. + +Note that exposing server time may be insecure. It is safer to use +C<mode=uptime> or C<mode=conntime> instead. + +=item [B<mode=>]B<uptime> + +Reflect nbdkit uptime in seconds and microseconds (ie. both fields are +C<0> immediately after nbdkit starts, although a client would never be +able to observe this). The format is exactly the same as for +C<mode=time> above. + +=item [B<mode=>]B<conntime> + +Reflect time since the NBD client connection was opened in seconds and +milliseconds. The format is exactly the same as for C<mode=time> +above. + C<mode=> is a magic config key and may be omitted in most cases. See L<nbdkit(1)/Magic parameters>. diff --git a/plugins/reflection/reflection.c b/plugins/reflection/reflection.c index 6fd1962..1459503 100644 --- a/plugins/reflection/reflection.c +++ b/plugins/reflection/reflection.c @@ -35,6 +35,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/time.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> @@ -48,14 +49,29 @@ #include <nbdkit-plugin.h> +#include "byte-swapping.h" +#include "tvdiff.h" + /* The mode. */ enum mode { MODE_EXPORTNAME, MODE_BASE64EXPORTNAME, MODE_ADDRESS, + MODE_TIME, + MODE_UPTIME, + MODE_CONNTIME, }; static enum mode mode = MODE_EXPORTNAME; +/* Plugin load time. */ +static struct timeval load_t; + +static void +reflection_load (void) +{ + gettimeofday (&load_t, NULL); +} + static int reflection_config (const char *key, const char *value) { @@ -73,9 +89,14 @@ reflection_config (const char *key, const char *value) return -1; #endif } - else if (strcasecmp (value, "address") == 0) { + else if (strcasecmp (value, "address") == 0) mode = MODE_ADDRESS; - } + else if (strcasecmp (value, "time") == 0) + mode = MODE_TIME; + else if (strcasecmp (value, "uptime") == 0) + mode = MODE_UPTIME; + else if (strcasecmp (value, "conntime") == 0) + mode = MODE_CONNTIME; else { nbdkit_error ("unknown mode: '%s'", value); return -1; @@ -90,7 +111,8 @@ reflection_config (const char *key, const char *value) } #define reflection_config_help \ - "mode=exportname|base64exportname|address Plugin mode (default exportname)." + "mode=exportname|base64exportname|address|time|uptime\n" \ + " Plugin mode (default exportname)." /* Provide a way to detect if the base64 feature is supported. */ static void @@ -105,6 +127,7 @@ reflection_dump_plugin (void) struct handle { void *data; /* Block device data. */ size_t len; /* Length of data in bytes. */ + struct timeval conn_t; /* Time since connection was opened. */ }; static int @@ -279,6 +302,19 @@ reflection_open (int readonly) } return h; + case MODE_TIME: + case MODE_UPTIME: + case MODE_CONNTIME: + gettimeofday (&h->conn_t, NULL); + h->len = 12; + h->data = malloc (h->len); + if (h->data == NULL) { + nbdkit_error ("malloc: %m"); + free (h); + return NULL; + } + return h; + default: abort (); } @@ -320,6 +356,13 @@ reflection_can_multi_conn (void *handle) */ case MODE_ADDRESS: return 0; + /* All time modes will read different values at different times, + * so all of them are unsafe for multi-conn. + */ + case MODE_TIME: + case MODE_UPTIME: + case MODE_CONNTIME: + return 0; /* Keep GCC happy. */ default: @@ -337,6 +380,42 @@ reflection_can_cache (void *handle) return NBDKIT_CACHE_NATIVE; } +static void +update_time (struct handle *h) +{ + struct timeval tv; + int64_t secs; + int32_t usecs; + char *p; + + gettimeofday (&tv, NULL); + + switch (mode) { + case MODE_TIME: + break; + + case MODE_UPTIME: + subtract_timeval (&load_t, &tv, &tv); + break; + + case MODE_CONNTIME: + subtract_timeval (&h->conn_t, &tv, &tv); + break; + + default: + abort (); + } + + /* Pack the result into the output buffer. */ + secs = tv.tv_sec; + usecs = tv.tv_usec; + secs = htobe64 (secs); + usecs = htobe32 (usecs); + p = h->data; + memcpy (&p[0], &secs, 8); + memcpy (&p[8], &usecs, 4); +} + /* Read data. */ static int reflection_pread (void *handle, void *buf, uint32_t count, uint64_t offset, @@ -344,6 +423,10 @@ reflection_pread (void *handle, void *buf, uint32_t count, uint64_t offset, { struct handle *h = handle; + /* For the time modes we update the data on every read. */ + if (mode == MODE_TIME || mode == MODE_UPTIME || mode == MODE_CONNTIME) + update_time (h); + memcpy (buf, h->data + offset, count); return 0; } @@ -351,6 +434,7 @@ reflection_pread (void *handle, void *buf, uint32_t count, uint64_t offset, static struct nbdkit_plugin plugin = { .name = "reflection", .version = PACKAGE_VERSION, + .load = reflection_load, .config = reflection_config, .config_help = reflection_config_help, .dump_plugin = reflection_dump_plugin, -- 2.23.0
Richard W.M. Jones
2019-Sep-28 11:21 UTC
Re: [Libguestfs] [PATCH nbdkit 2/2] reflection: Add mode for reflecting server time.
On Sat, Sep 28, 2019 at 10:25:17AM +0100, Richard W.M. Jones wrote:> Either wallclock time, uptime or time since client connection can be > reflected back to the client in a big endian binary structure. > > $ nbdkit reflection time --run 'nbdsh --connect $uri -c "sys.stdout.buffer.write(h.pread(12,0))" | hexdump -C' > 00000000 00 00 00 00 5d 8f 24 c7 00 04 24 01 > \ | / / > $ date --date="@$(( 0x5d8f24c7 ))" > Sat 28 Sep 10:15:51 BST 2019 > > $ nbdkit reflection uptime --run 'nbdsh --connect $uri -c "sys.stdout.buffer.write(h.pread(12,0))" | hexdump -C' > 00000000 00 00 00 00 00 00 00 00 00 00 60 4b > | | > 0x604b is about 25ms > > $ nbdkit reflection conntime --run 'nbdsh --connect $uri -c "sys.stdout.buffer.write(h.pread(12,0))" | hexdump -C' > 00000000 00 00 00 00 00 00 00 00 00 00 00 e0 > | > 0xe0 is about 200μsThere's an argument that this doesn't belong in the reflection plugin because it's not reflecting client information back (except maybe conntime). It was very convenient to add it here though. We might consider also: - Create a new plugin ("info"?) and add these features here, or - Rename the reflection plugin to info plugin, or - Do nothing Rich.> Suggested by Eric Blake. > --- > plugins/reflection/Makefile.am | 1 + > .../reflection/nbdkit-reflection-plugin.pod | 41 ++++++++- > plugins/reflection/reflection.c | 90 ++++++++++++++++++- > 3 files changed, 128 insertions(+), 4 deletions(-) > > diff --git a/plugins/reflection/Makefile.am b/plugins/reflection/Makefile.am > index 40aa786..9544d98 100644 > --- a/plugins/reflection/Makefile.am > +++ b/plugins/reflection/Makefile.am > @@ -42,6 +42,7 @@ nbdkit_reflection_plugin_la_SOURCES = \ > > nbdkit_reflection_plugin_la_CPPFLAGS = \ > -I$(top_srcdir)/include \ > + -I$(top_srcdir)/common/include \ > $(NULL) > nbdkit_reflection_plugin_la_CFLAGS = $(WARNINGS_CFLAGS) > nbdkit_reflection_plugin_la_LDFLAGS = \ > diff --git a/plugins/reflection/nbdkit-reflection-plugin.pod b/plugins/reflection/nbdkit-reflection-plugin.pod > index f971cef..09deb26 100644 > --- a/plugins/reflection/nbdkit-reflection-plugin.pod > +++ b/plugins/reflection/nbdkit-reflection-plugin.pod > @@ -4,7 +4,8 @@ nbdkit-reflection-plugin - reflect client info back to the client > > =head1 SYNOPSIS > > - nbdkit reflection [mode=]exportname|base64exportname|address > + nbdkit reflection [mode=]exportname|base64exportname|address| > + time|uptime|conntime > > =head1 DESCRIPTION > > @@ -22,6 +23,10 @@ than this. > C<mode=address> creates a disk which contains the client's IP address > and port number as a string. > > +C<mode=time>, C<mode=uptime> and C<mode=conntime> report server > +wallclock time, nbdkit uptime, or time since the connection was opened > +respectively and may be used to measure latency. > + > The plugin only supports read-only access. To make the disk writable, > add L<nbdkit-cow-filter(1)> on top. > > @@ -103,6 +108,40 @@ name cannot contain ASCII NUL characters. > > This is the default mode. > > +=item [B<mode=>]B<time> > + > +Reflect server wallclock time as seconds and microseconds since the > +Epoch (see L<gettimeofday(2)>): > + > + ┌────────┬────────┬────────────┬──────────────────────┐ > + │ offset │ length │ format │ field │ > + ╞════════╪════════╪════════════╪══════════════════════╡ > + │ 0 │ 8 │ 64 bit int │ seconds │ > + │ │ │ big endian │ │ > + ├────────┼────────┼────────────┼──────────────────────┤ > + │ 8 │ 4 │ 32 bit int │ microseconds │ > + │ │ │ big endian │ │ > + └────────┴────────┴────────────┴──────────────────────┘ > + > +To be able to read this atomically you must read the whole 12 bytes in > +a single request. > + > +Note that exposing server time may be insecure. It is safer to use > +C<mode=uptime> or C<mode=conntime> instead. > + > +=item [B<mode=>]B<uptime> > + > +Reflect nbdkit uptime in seconds and microseconds (ie. both fields are > +C<0> immediately after nbdkit starts, although a client would never be > +able to observe this). The format is exactly the same as for > +C<mode=time> above. > + > +=item [B<mode=>]B<conntime> > + > +Reflect time since the NBD client connection was opened in seconds and > +milliseconds. The format is exactly the same as for C<mode=time> > +above. > + > C<mode=> is a magic config key and may be omitted in most cases. > See L<nbdkit(1)/Magic parameters>. > > diff --git a/plugins/reflection/reflection.c b/plugins/reflection/reflection.c > index 6fd1962..1459503 100644 > --- a/plugins/reflection/reflection.c > +++ b/plugins/reflection/reflection.c > @@ -35,6 +35,7 @@ > #include <stdio.h> > #include <stdlib.h> > #include <string.h> > +#include <sys/time.h> > #include <sys/socket.h> > #include <netinet/in.h> > #include <arpa/inet.h> > @@ -48,14 +49,29 @@ > > #include <nbdkit-plugin.h> > > +#include "byte-swapping.h" > +#include "tvdiff.h" > + > /* The mode. */ > enum mode { > MODE_EXPORTNAME, > MODE_BASE64EXPORTNAME, > MODE_ADDRESS, > + MODE_TIME, > + MODE_UPTIME, > + MODE_CONNTIME, > }; > static enum mode mode = MODE_EXPORTNAME; > > +/* Plugin load time. */ > +static struct timeval load_t; > + > +static void > +reflection_load (void) > +{ > + gettimeofday (&load_t, NULL); > +} > + > static int > reflection_config (const char *key, const char *value) > { > @@ -73,9 +89,14 @@ reflection_config (const char *key, const char *value) > return -1; > #endif > } > - else if (strcasecmp (value, "address") == 0) { > + else if (strcasecmp (value, "address") == 0) > mode = MODE_ADDRESS; > - } > + else if (strcasecmp (value, "time") == 0) > + mode = MODE_TIME; > + else if (strcasecmp (value, "uptime") == 0) > + mode = MODE_UPTIME; > + else if (strcasecmp (value, "conntime") == 0) > + mode = MODE_CONNTIME; > else { > nbdkit_error ("unknown mode: '%s'", value); > return -1; > @@ -90,7 +111,8 @@ reflection_config (const char *key, const char *value) > } > > #define reflection_config_help \ > - "mode=exportname|base64exportname|address Plugin mode (default exportname)." > + "mode=exportname|base64exportname|address|time|uptime\n" \ > + " Plugin mode (default exportname)." > > /* Provide a way to detect if the base64 feature is supported. */ > static void > @@ -105,6 +127,7 @@ reflection_dump_plugin (void) > struct handle { > void *data; /* Block device data. */ > size_t len; /* Length of data in bytes. */ > + struct timeval conn_t; /* Time since connection was opened. */ > }; > > static int > @@ -279,6 +302,19 @@ reflection_open (int readonly) > } > return h; > > + case MODE_TIME: > + case MODE_UPTIME: > + case MODE_CONNTIME: > + gettimeofday (&h->conn_t, NULL); > + h->len = 12; > + h->data = malloc (h->len); > + if (h->data == NULL) { > + nbdkit_error ("malloc: %m"); > + free (h); > + return NULL; > + } > + return h; > + > default: > abort (); > } > @@ -320,6 +356,13 @@ reflection_can_multi_conn (void *handle) > */ > case MODE_ADDRESS: > return 0; > + /* All time modes will read different values at different times, > + * so all of them are unsafe for multi-conn. > + */ > + case MODE_TIME: > + case MODE_UPTIME: > + case MODE_CONNTIME: > + return 0; > > /* Keep GCC happy. */ > default: > @@ -337,6 +380,42 @@ reflection_can_cache (void *handle) > return NBDKIT_CACHE_NATIVE; > } > > +static void > +update_time (struct handle *h) > +{ > + struct timeval tv; > + int64_t secs; > + int32_t usecs; > + char *p; > + > + gettimeofday (&tv, NULL); > + > + switch (mode) { > + case MODE_TIME: > + break; > + > + case MODE_UPTIME: > + subtract_timeval (&load_t, &tv, &tv); > + break; > + > + case MODE_CONNTIME: > + subtract_timeval (&h->conn_t, &tv, &tv); > + break; > + > + default: > + abort (); > + } > + > + /* Pack the result into the output buffer. */ > + secs = tv.tv_sec; > + usecs = tv.tv_usec; > + secs = htobe64 (secs); > + usecs = htobe32 (usecs); > + p = h->data; > + memcpy (&p[0], &secs, 8); > + memcpy (&p[8], &usecs, 4); > +} > + > /* Read data. */ > static int > reflection_pread (void *handle, void *buf, uint32_t count, uint64_t offset, > @@ -344,6 +423,10 @@ reflection_pread (void *handle, void *buf, uint32_t count, uint64_t offset, > { > struct handle *h = handle; > > + /* For the time modes we update the data on every read. */ > + if (mode == MODE_TIME || mode == MODE_UPTIME || mode == MODE_CONNTIME) > + update_time (h); > + > memcpy (buf, h->data + offset, count); > return 0; > } > @@ -351,6 +434,7 @@ reflection_pread (void *handle, void *buf, uint32_t count, uint64_t offset, > static struct nbdkit_plugin plugin = { > .name = "reflection", > .version = PACKAGE_VERSION, > + .load = reflection_load, > .config = reflection_config, > .config_help = reflection_config_help, > .dump_plugin = reflection_dump_plugin, > -- > 2.23.0 > > _______________________________________________ > Libguestfs mailing list > Libguestfs@redhat.com > https://www.redhat.com/mailman/listinfo/libguestfs-- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://people.redhat.com/~rjones/virt-top
Possibly Parallel Threads
- [PATCH nbdkit v2 0/4] info: Add mode for sending back server time.
- [PATCH nbdkit v2 1/4] common/include: Add function for subtracting struct timeval.
- [PATCH nbdkit] common/include/tvdiff.h: Add formal specification.
- [PATCH nbdkit v2 3/4] info: Add mode for sending back server time.
- [PATCH nbdkit 2/2] reflection: Add mode for reflecting server time.