[CRIU] [PATCH 2/3] time: Export delta time between checkpoint and restore stage

Cyrill Gorcunov gorcunov at gmail.com
Tue Feb 2 07:53:46 PST 2016


For programs with time sensitive behaviour we calculate
the delta between dump and restore stages and export
it in CRTOOLS_CLOCK_DELTA environment variable.

Note that this variable won't be set if images
are too old and don't have dump time record.

Also this while we exporting the variable
it's up to other tools to adjust the kernel
clock settings (actually there is no interface
in vanilla kernel for such action but in pcs7
we've added own cgroup interface for this
need).

https://jira.sw.ru/browse/PSBM-41406

Signed-off-by: Cyrill Gorcunov <gorcunov at virtuozzo.com>
---
 cr-restore.c       |   6 +++
 include/restorer.h |   1 +
 include/time.h     |  61 +++++++++++++++++++++++++++++
 pie/restorer.c     |   1 +
 time.c             | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 179 insertions(+)

diff --git a/cr-restore.c b/cr-restore.c
index 973f06e..63a463b 100644
--- a/cr-restore.c
+++ b/cr-restore.c
@@ -146,6 +146,9 @@ static int crtools_prepare_shared(void)
 	if (prepare_cgroup())
 		return -1;
 
+	if (prepare_dump_time())
+		return -1;
+
 	return 0;
 }
 
@@ -2163,6 +2166,9 @@ int cr_restore_tasks(void)
 	if (kerndat_init_rst())
 		goto err;
 
+	if (init_restore_time())
+		return -1;
+
 	timing_start(TIME_RESTORE);
 
 	if (cpu_init() < 0)
diff --git a/include/restorer.h b/include/restorer.h
index 4c4377c..7540360 100644
--- a/include/restorer.h
+++ b/include/restorer.h
@@ -17,6 +17,7 @@
 
 #include "posix-timer.h"
 #include "timerfd.h"
+#include "time.h"
 #include "shmem.h"
 #include "sigframe.h"
 #include "parasite-vdso.h"
diff --git a/include/time.h b/include/time.h
index 8ae3516..7f0ce49 100644
--- a/include/time.h
+++ b/include/time.h
@@ -3,6 +3,9 @@
 
 #include <time.h>
 
+#include "compiler.h"
+#include "asm/int.h"
+
 #include "protobuf/time.pb-c.h"
 
 /* realtime, monotonic, boottime */
@@ -18,7 +21,65 @@ typedef struct {
 	Timespec		ts[NR_CLOCKSET_CLOCKS];
 } pb_clockset_t;
 
+#define MSEC_PER_SEC	1000L
+#define USEC_PER_MSEC	1000L
+#define NSEC_PER_USEC	1000L
+#define NSEC_PER_MSEC	1000000L
+#define USEC_PER_SEC	1000000L
+#define NSEC_PER_SEC	1000000000L
+#define FSEC_PER_SEC	1000000000000000L
+
+static inline void set_normalized_timespec(struct timespec *ts, time_t sec, s64 nsec)
+{
+	while (nsec >= NSEC_PER_SEC) {
+		asm("" : "+rm"(nsec));
+		nsec -= NSEC_PER_SEC;
+		++sec;
+	}
+	while (nsec < 0) {
+		asm("" : "+rm"(nsec));
+		nsec += NSEC_PER_SEC;
+		--sec;
+	}
+	ts->tv_sec = sec;
+	ts->tv_nsec = nsec;
+}
+
+static inline struct timespec timespec_add(struct timespec lhs,
+					   struct timespec rhs)
+{
+	struct timespec ts_delta;
+	set_normalized_timespec(&ts_delta, lhs.tv_sec + rhs.tv_sec,
+				lhs.tv_nsec + rhs.tv_nsec);
+	return ts_delta;
+}
+
+static inline struct timespec timespec_sub(struct timespec lhs,
+					   struct timespec rhs)
+{
+	struct timespec ts_delta;
+	set_normalized_timespec(&ts_delta, lhs.tv_sec - rhs.tv_sec,
+				lhs.tv_nsec - rhs.tv_nsec);
+	return ts_delta;
+}
+
+static inline struct timespec *
+ts_from_clockset(clockset_t *clockset, clockid_t clockid)
+{
+	if (clockid == CLOCK_REALTIME)
+		return &clockset->ts[0];
+	else if (clockid == CLOCK_MONOTONIC)
+		return &clockset->ts[1];
+	else if (clockid == CLOCK_BOOTTIME)
+		return &clockset->ts[2];
+
+	return NULL;
+}
+
+extern void clockset_from_dump_time(clockset_t *clockset);
 extern int clockset_get(clockset_t *clockset);
 extern int dump_time_write(void);
+extern int init_restore_time(void);
+extern int prepare_dump_time(void);
 
 #endif /* __CR_TIME_H__ */
diff --git a/pie/restorer.c b/pie/restorer.c
index d99c92d..c4d3a3a 100644
--- a/pie/restorer.c
+++ b/pie/restorer.c
@@ -31,6 +31,7 @@
 #include "restorer.h"
 #include "aio.h"
 #include "seccomp.h"
+#include "time.h"
 
 #include "protobuf/creds.pb-c.h"
 #include "protobuf/mm.pb-c.h"
diff --git a/time.c b/time.c
index 8cef4f0..accd74e 100644
--- a/time.c
+++ b/time.c
@@ -2,6 +2,7 @@
 #include <stdlib.h>
 #include <stdarg.h>
 #include <unistd.h>
+#include <string.h>
 #include <errno.h>
 
 #include "log.h"
@@ -13,6 +14,9 @@
 #undef	LOG_PREFIX
 #define LOG_PREFIX "time: "
 
+static clockset_t restore_time;
+static clockset_t dump_time;
+
 static void clockset_to_pb(clockset_t *clockset, pb_clockset_t *pb_clockset)
 {
 	size_t i;
@@ -25,6 +29,21 @@ static void clockset_to_pb(clockset_t *clockset, pb_clockset_t *pb_clockset)
 	}
 }
 
+static void pb_to_clockset(pb_clockset_t *pb_clockset, clockset_t *clockset)
+{
+	size_t i;
+
+	for (i = 0; i < NR_CLOCKSET_CLOCKS; i++) {
+		clockset->ts[i].tv_sec = pb_clockset->ts[i].tv_sec;
+		clockset->ts[i].tv_nsec = pb_clockset->ts[i].tv_nsec;
+	}
+}
+
+void clockset_from_dump_time(clockset_t *clockset)
+{
+	*clockset = dump_time;
+}
+
 int clockset_get(clockset_t *clockset)
 {
 	static const struct {
@@ -86,6 +105,97 @@ int dump_time_write(void)
 	dump_time_entry.clocks	= &clockset_entry;
 	dump_time_entry.date	= str;
 
+	pr_debug("dump_time (real %llu %llu mono %llu %llu boot %llu %llu), %s\n",
+		 (unsigned long long)clockset.ts[0].tv_sec,
+		 (unsigned long long)clockset.ts[0].tv_nsec,
+		 (unsigned long long)clockset.ts[1].tv_sec,
+		 (unsigned long long)clockset.ts[1].tv_nsec,
+		 (unsigned long long)clockset.ts[2].tv_sec,
+		 (unsigned long long)clockset.ts[2].tv_nsec,
+		 str);
+
 	return pb_write_one(img_from_set(glob_imgset, CR_FD_DUMP_TIME),
 			    &dump_time_entry, PB_DUMP_TIME);
 }
+
+static int collect_dump_time(void *o, ProtobufCMessage *base)
+{
+	DumpTimeEntry *e = pb_msg(base, DumpTimeEntry);
+	pb_clockset_t pb_clockset = {
+		.ts[0] = *e->clocks->clock_real,
+		.ts[1] = *e->clocks->clock_mono,
+		.ts[2] = *e->clocks->clock_boot,
+	};
+
+	static const char envname[] = "CRTOOLS_CLOCK_DELTA";
+	clockset_t delta;
+	char buf[128];
+
+	pb_to_clockset(&pb_clockset, &dump_time);
+
+	pr_debug("dump_time (real %llu %llu mono %llu %llu boot %llu %llu), %s\n",
+		 (unsigned long long)dump_time.ts[0].tv_sec,
+		 (unsigned long long)dump_time.ts[0].tv_nsec,
+		 (unsigned long long)dump_time.ts[1].tv_sec,
+		 (unsigned long long)dump_time.ts[1].tv_nsec,
+		 (unsigned long long)dump_time.ts[2].tv_sec,
+		 (unsigned long long)dump_time.ts[2].tv_nsec,
+		 e->date);
+
+	/*
+	 * Warn a user if clocks were moved back while
+	 * we have been offline.
+	 */
+	if (dump_time.ts[0].tv_sec &&
+	    dump_time.ts[0].tv_sec < restore_time.ts[0].tv_sec) {
+		pr_warn("Run time clock moved backward, "
+			"some applications may break (dump %llu -> now %llu)\n",
+			(unsigned long long)dump_time.ts[0].tv_sec,
+			(unsigned long long)restore_time.ts[0].tv_sec);
+	} else if (!dump_time.ts[0].tv_sec) {
+		pr_debug("No dump time present, assume transparen migration\n");
+		dump_time = restore_time;
+	}
+
+	/*
+	 * Export times via environment variables so scripts
+	 * can workaround time shifting problems if needed.
+	 */
+
+	delta.ts[0] = timespec_sub(restore_time.ts[0], dump_time.ts[0]);
+	delta.ts[1] = timespec_sub(restore_time.ts[1], dump_time.ts[1]);
+	delta.ts[2] = timespec_sub(restore_time.ts[2], dump_time.ts[2]);
+
+	snprintf(buf, sizeof(buf),
+		 "realtime %llu:%llu monotonic %llu:%llu boot %llu:%llu",
+		 (unsigned long long)delta.ts[0].tv_sec,
+		 (unsigned long long)delta.ts[0].tv_nsec,
+		 (unsigned long long)delta.ts[1].tv_sec,
+		 (unsigned long long)delta.ts[1].tv_nsec,
+		 (unsigned long long)delta.ts[2].tv_sec,
+		 (unsigned long long)delta.ts[2].tv_nsec);
+	pr_debug("Exporting delta (%s)\n", buf);
+
+	if (setenv(envname, buf, 1)) {
+		pr_perror("Can't set %s variable", envname);
+		return -1;
+	}
+
+	return 0;
+}
+
+static struct collect_image_info dump_time_cinfo = {
+	.fd_type	= CR_FD_DUMP_TIME,
+	.pb_type	= PB_DUMP_TIME,
+	.collect	= collect_dump_time,
+};
+
+int init_restore_time(void)
+{
+	return clockset_get(&restore_time);
+}
+
+int prepare_dump_time(void)
+{
+	return collect_image(&dump_time_cinfo);
+}
-- 
2.5.0



More information about the CRIU mailing list