@@ -1268,6 +1268,137 @@ static void dm_destroy_cb(libxl__egc *egc,
libxl__devices_destroy(egc, &dis->drs);
}
+static unsigned int libxl__get_max_retirement(void)
+{
+ const char *env_max_retirement = getenv("LIBXL_DOMID_MAX_RETIREMENT");
+
+ return env_max_retirement ? strtol(env_max_retirement, NULL, 0) :
+ LIBXL_DOMID_MAX_RETIREMENT;
+}
+
+static int libxl__open_domid_history(libxl__gc *gc)
+{
+ const char *name;
+ int fd;
+ int ret;
+
+ name = GCSPRINTF("%s/domid-history", libxl__run_dir_path());
+
+ fd = open(name, O_RDWR|O_CREAT, 0644);
+ if (fd < 0) {
+ LOGE(ERROR, "unexpected error while trying open %s, errno=%d",
+ name, errno);
+ goto fail;
+ }
+
+ for (;;) {
+ ret = flock(fd, LOCK_EX);
+ if (!ret)
+ break;
+ if (errno != EINTR) {
+ /* All other errno: EBADF, EINVAL, ENOLCK, EWOULDBLOCK */
+ LOGE(ERROR,
+ "unexpected error while trying to lock %s, fd=%d, errno=%d",
+ name, fd, errno);
+ goto fail;
+ }
+ }
+
+ return fd;
+
+fail:
+ if (fd >= 0)
+ close(fd);
+
+ return -1;
+}
+
+/* Write a domid retirement record */
+static void libxl__retire_domid(libxl__gc *gc, uint32_t domid)
+{
+ long max_retirement = libxl__get_max_retirement();
+ int fd;
+ FILE *f;
+ long roff, woff;
+ char line[64];
+ struct timespec ts;
+
+ fd = libxl__open_domid_history(gc);
+ if (fd < 0)
+ return;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ /* Purge old retirement records */
+
+ f = fdopen(fd, "r+");
+ woff = ftell(f);
+
+ while (fgets(line, sizeof(line), f)) {
+ unsigned long sec;
+ unsigned int ignored;
+
+ roff = ftell(f);
+
+ if (sscanf(line, "%lu %u", &sec, &ignored) != 2)
+ continue; /* Purge malformed lines */
+
+ if (ts.tv_sec - sec > max_retirement)
+ continue;
+
+ fseek(f, woff, SEEK_SET);
+ fputs(line, f);
+ woff = ftell(f);
+
+ fseek(f, roff, SEEK_SET);
+ }
+
+ fseek(f, woff, SEEK_SET);
+ fprintf(f, "%lu %u\n", ts.tv_sec, domid);
+ woff = ftell(f);
+ fflush(f);
+
+ ftruncate(fd, woff); /* may now be fewer records */
+
+ close(fd);
+}
+
+bool libxl__is_retired_domid(libxl__gc *gc, uint32_t domid)
+{
+ long max_retirement = libxl__get_max_retirement();
+ bool retired = false;
+ int fd;
+ FILE *f;
+ char line[64];
+ struct timespec ts;
+
+ fd = libxl__open_domid_history(gc);
+ if (fd < 0)
+ return false;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ f = fdopen(fd, "r");
+
+ while (fgets(line, sizeof(line), f)) {
+ unsigned long sec;
+ unsigned int check;
+
+ if (sscanf(line, "%lu %u", &sec, &check) != 2)
+ continue;
+
+ if (check == domid &&
+ ts.tv_sec - sec <= max_retirement) {
+ retired = true;
+ break;
+ }
+ }
+
+ close(fd);
+
+ return retired;
+}
+
static void devices_destroy_cb(libxl__egc *egc,
libxl__devices_remove_state *drs,
int rc)
@@ -1331,6 +1462,7 @@ static void devices_destroy_cb(libxl__egc *egc,
if (!ctx->xch) goto badchild;
if (!dis->soft_reset) {
+ libxl__retire_domid(gc, domid);
rc = xc_domain_destroy(ctx->xch, domid);
} else {
rc = xc_domain_pause(ctx->xch, domid);
@@ -4770,6 +4770,16 @@ _hidden int libxl__domain_pvcontrol(libxl__egc *egc,
libxl__xswait_state *pvcontrol,
domid_t domid, const char *cmd);
+/*
+ * Maximum number of seconds a domid remains in retirement after domain
+ * destruction. This can be overidden by the environment variable of the
+ * same name.
+ */
+#define LIBXL_DOMID_MAX_RETIREMENT 60
+
+/* Check whether a domid is in retirement */
+bool libxl__is_retired_domid(libxl__gc *gc, uint32_t domid);
+
#endif
/*
A domid is considered retired if the domain it represents was destroyed less than a specified number of seconds ago. The number can be set using the environment variable LIBXL_DOMID_MAX_RETIREMENT. If the variable does not exist then a default value of 60s is used. Whenever a domain is destroyed, a time-stamped record will be written into a history file (/var/run/xen/domid-history). To avoid the history file growing too large, any records with time-stamps that indicate that the domid has exceeded maximum retirement will also be purged. A new utility function, libxl__is_retired_domid(), has been added. This function reads the same history file checking whether a specified domid has a record that does not exceed maximum retirement. Since this utility function does not write to the file, no records are actually purged by it. NOTE: Since the history file is hosted by a tmpfs file system, it is automatically purged on boot thus allowing safe use of CLOCK_MONOTONIC as a time source. Signed-off-by: Paul Durrant <pdurrant@amazon.com> --- Cc: Ian Jackson <ian.jackson@eu.citrix.com> Cc: Wei Liu <wl@xen.org> Cc: Anthony PERARD <anthony.perard@citrix.com> v2: - New in v2 --- tools/libxl/libxl_domain.c | 132 +++++++++++++++++++++++++++++++++++ tools/libxl/libxl_internal.h | 10 +++ 2 files changed, 142 insertions(+)