[v2,3/6] libxl: add infrastructure to track and query 'retired' domids
diff mbox series

Message ID 20200109114816.2293-4-pdurrant@amazon.com
State Superseded
Headers show
Series
  • xl/libxl: domid allocation/preservation changes
Related show

Commit Message

Paul Durrant Jan. 9, 2020, 11:48 a.m. UTC
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(+)

Patch
diff mbox series

diff --git a/tools/libxl/libxl_domain.c b/tools/libxl/libxl_domain.c
index 5714501778..7f255f184c 100644
--- a/tools/libxl/libxl_domain.c
+++ b/tools/libxl/libxl_domain.c
@@ -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);
diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
index cb23490c59..fcac8a93c5 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -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
 
 /*