chiark / gitweb /
importd: automatically grow /var/lib/machines/ loopback filesystem during downloads
authorLennart Poettering <lennart@poettering.net>
Mon, 2 Mar 2015 23:13:12 +0000 (00:13 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 2 Mar 2015 23:13:12 +0000 (00:13 +0100)
If /var/lib/machines is mounted as btrfs loopback file system in
/var/lib/machines.raw with this change we automatically grow the file
system as it fills up. After each 10M we write to it during imports, we
check the free disk space, and if the fill level grows beyond 66% we
increase the size of the file system to 3x the fill level (thus lowering
it to 33%).

src/import/import-dkr.c
src/import/import-job.c
src/import/import-job.h
src/import/import-raw.c
src/import/import-tar.c
src/machine/machined-dbus.c
src/shared/btrfs-util.c
src/shared/btrfs-util.h
src/shared/machine-pool.c
src/shared/machine-pool.h

index fb72f6cee326bd653592f172301641d0f6975019..2d4e9b398f9e5f680d6408f9e559ace09519675e 100644 (file)
@@ -28,6 +28,7 @@
 #include "btrfs-util.h"
 #include "utf8.h"
 #include "mkdir.h"
+#include "path-util.h"
 #include "import-util.h"
 #include "curl-util.h"
 #include "aufs-util.h"
@@ -72,6 +73,7 @@ struct DkrImport {
 
         char *local;
         bool force_local;
+        bool grow_machine_directory;
 
         char *temp_path;
         char *final_path;
@@ -156,6 +158,8 @@ int dkr_import_new(
         if (!i->image_root)
                 return -ENOMEM;
 
+        i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
+
         i->index_url = strdup(index_url);
         if (!i->index_url)
                 return -ENOMEM;
@@ -561,6 +565,7 @@ static int dkr_import_pull_layer(DkrImport *i) {
         i->layer_job->on_finished = dkr_import_job_on_finished;
         i->layer_job->on_open_disk = dkr_import_job_on_open_disk;
         i->layer_job->on_progress = dkr_import_job_on_progress;
+        i->layer_job->grow_machine_directory = i->grow_machine_directory;
 
         r = import_job_begin(i->layer_job);
         if (r < 0)
index 809486500ba55cf6d6b045e5b1fda78956560d3f..980b639b5db8d219d70ccda35af0e0d010b574a6 100644 (file)
 #include <sys/xattr.h>
 
 #include "strv.h"
+#include "machine-pool.h"
 #include "import-job.h"
 
+/* Grow the /var/lib/machines directory after each 10MiB written */
+#define IMPORT_GROW_INTERVAL_BYTES (UINT64_C(10) * UINT64_C(1024) * UINT64_C(1024))
+
 ImportJob* import_job_unref(ImportJob *j) {
         if (!j)
                 return NULL;
@@ -197,6 +201,11 @@ static int import_job_write_uncompressed(ImportJob *j, void *p, size_t sz) {
 
         if (j->disk_fd >= 0) {
 
+                if (j->grow_machine_directory && j->written_since_last_grow >= IMPORT_GROW_INTERVAL_BYTES) {
+                        j->written_since_last_grow = 0;
+                        grow_machine_directory();
+                }
+
                 if (j->allow_sparse)
                         n = sparse_write(j->disk_fd, p, sz, 64);
                 else
@@ -219,6 +228,7 @@ static int import_job_write_uncompressed(ImportJob *j, void *p, size_t sz) {
         }
 
         j->written_uncompressed += sz;
+        j->written_since_last_grow += sz;
 
         return 0;
 }
@@ -667,6 +677,9 @@ int import_job_begin(ImportJob *j) {
         if (j->state != IMPORT_JOB_INIT)
                 return -EBUSY;
 
+        if (j->grow_machine_directory)
+                grow_machine_directory();
+
         r = curl_glue_make(&j->curl, j->url, j);
         if (r < 0)
                 return r;
index dcf89cb28c45314a92879b4df88ef546770e1405..2c01d723dbb71487ed8304fdd3764d1617370dcc 100644 (file)
@@ -107,6 +107,9 @@ struct ImportJob {
         gcry_md_hd_t checksum_context;
 
         char *checksum;
+
+        bool grow_machine_directory;
+        uint64_t written_since_last_grow;
 };
 
 int import_job_new(ImportJob **job, const char *url, CurlGlue *glue, void *userdata);
index 8d99f1085cb1111519dcd0db29b4fa8760c9543e..89c064cb3da193559b2d8c576dd6e6918c854754 100644 (file)
@@ -31,6 +31,7 @@
 #include "util.h"
 #include "macro.h"
 #include "mkdir.h"
+#include "path-util.h"
 #include "import-util.h"
 #include "curl-util.h"
 #include "qcow2-util.h"
@@ -61,6 +62,7 @@ struct RawImport {
 
         char *local;
         bool force_local;
+        bool grow_machine_directory;
 
         char *temp_path;
         char *final_path;
@@ -115,6 +117,8 @@ int raw_import_new(
         if (!i->image_root)
                 return -ENOMEM;
 
+        i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
+
         if (event)
                 i->event = sd_event_ref(event);
         else {
@@ -480,6 +484,7 @@ int raw_import_pull(RawImport *i, const char *url, const char *local, bool force
         i->raw_job->on_open_disk = raw_import_job_on_open_disk;
         i->raw_job->on_progress = raw_import_job_on_progress;
         i->raw_job->calc_checksum = verify != IMPORT_VERIFY_NO;
+        i->raw_job->grow_machine_directory = i->grow_machine_directory;
 
         r = import_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags);
         if (r < 0)
index 493252a13296655df3f79fcbb43b47eae82e799f..472e336247e432d12dacb81ae5e5b17a89ba001a 100644 (file)
@@ -30,6 +30,7 @@
 #include "util.h"
 #include "macro.h"
 #include "mkdir.h"
+#include "path-util.h"
 #include "import-util.h"
 #include "curl-util.h"
 #include "import-job.h"
@@ -58,6 +59,7 @@ struct TarImport {
 
         char *local;
         bool force_local;
+        bool grow_machine_directory;
 
         pid_t tar_pid;
 
@@ -121,6 +123,8 @@ int tar_import_new(
         if (!i->image_root)
                 return -ENOMEM;
 
+        i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
+
         if (event)
                 i->event = sd_event_ref(event);
         else {
@@ -376,6 +380,7 @@ int tar_import_pull(TarImport *i, const char *url, const char *local, bool force
         i->tar_job->on_open_disk = tar_import_job_on_open_disk;
         i->tar_job->on_progress = tar_import_job_on_progress;
         i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO;
+        i->tar_job->grow_machine_directory = i->grow_machine_directory;
 
         r = import_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
         if (r < 0)
index 5ab40b04104005f3fd6399bd5ed9e24bfab7b4d7..adba8122f3a37b5bb28747b9c7410584d2c3f0e8 100644 (file)
@@ -805,14 +805,16 @@ static int method_set_pool_limit(sd_bus *bus, sd_bus_message *message, void *use
         if (r < 0)
                 return r;
 
-        r = btrfs_resize_loopback("/var/lib/machines", limit);
-        if (r < 0 && r != -ENODEV)
+        r = btrfs_resize_loopback("/var/lib/machines", limit, false);
+        if (r == -ENOTTY)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs.");
+        if (r < 0 && r != -ENODEV) /* ignore ENODEV, as that's what is returned if the file system is not on loopback */
                 return sd_bus_error_set_errnof(error, r, "Failed to adjust loopback limit: %m");
 
         r = btrfs_quota_limit("/var/lib/machines", limit);
         if (r == -ENOTTY)
                 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs.");
-        else if (r < 0)
+        if (r < 0)
                 return sd_bus_error_set_errnof(error, r, "Failed to adjust quota limit: %m");
 
         return sd_bus_reply_method_return(message, NULL);
index 52fc5f4a177ded0122d20b694c7df9408c753f03..256c5a6995ddf6f1796989ee771891464d2140d1 100644 (file)
@@ -705,7 +705,7 @@ int btrfs_quota_limit(const char *path, uint64_t referred_max) {
         return btrfs_quota_limit_fd(fd, referred_max);
 }
 
-int btrfs_resize_loopback_fd(int fd, uint64_t new_size) {
+int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
         struct btrfs_ioctl_vol_args args = {};
         _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL;
         _cleanup_close_ int loop_fd = -1, backing_fd = -1;
@@ -726,6 +726,8 @@ int btrfs_resize_loopback_fd(int fd, uint64_t new_size) {
         if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0)
                 return -ENOMEM;
         r = read_one_line_file(p, &backing);
+        if (r == -ENOENT)
+                return -ENODEV;
         if (r < 0)
                 return r;
         if (isempty(backing) || !path_is_absolute(backing))
@@ -743,6 +745,9 @@ int btrfs_resize_loopback_fd(int fd, uint64_t new_size) {
         if (new_size == (uint64_t) st.st_size)
                 return 0;
 
+        if (grow_only && new_size < (uint64_t) st.st_size)
+                return -EINVAL;
+
         if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0)
                 return -ENOMEM;
         loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY);
@@ -770,15 +775,19 @@ int btrfs_resize_loopback_fd(int fd, uint64_t new_size) {
                         return -errno;
         }
 
-        return 0;
+        /* Make sure the free disk space is correctly updated for both file systems */
+        (void) fsync(fd);
+        (void) fsync(backing_fd);
+
+        return 1;
 }
 
-int btrfs_resize_loopback(const char *p, uint64_t new_size) {
+int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) {
         _cleanup_close_ int fd = -1;
 
         fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
         if (fd < 0)
                 return -errno;
 
-        return btrfs_resize_loopback_fd(fd, new_size);
+        return btrfs_resize_loopback_fd(fd, new_size, grow_only);
 }
index e654a3fea16782217e6e2a3d7e7b3812051e17d1..a2c246b8d604b84b237b4e23881657440b147df2 100644 (file)
@@ -72,5 +72,5 @@ int btrfs_quota_enable(const char *path, bool b);
 int btrfs_quota_limit_fd(int fd, uint64_t referred_max);
 int btrfs_quota_limit(const char *path, uint64_t referred_max);
 
-int btrfs_resize_loopback_fd(int fd, uint64_t size);
-int btrfs_resize_loopback(const char *path, uint64_t size);
+int btrfs_resize_loopback_fd(int fd, uint64_t size, bool grow_only);
+int btrfs_resize_loopback(const char *path, uint64_t size, bool grow_only);
index b74252dbc8ec2125db4ca31b9c4fe3d12d81d930..3eafb9443a1cdec322122580aac6a076b4f2b134 100644 (file)
@@ -169,6 +169,7 @@ int setup_machine_directory(uint64_t size, sd_bus_error *error) {
         _cleanup_free_ char* loopdev = NULL;
         char tmpdir[] = "/tmp/import-mount.XXXXXX", *mntdir = NULL;
         bool tmpdir_made = false, mntdir_made = false, mntdir_mounted = false;
+        char buf[FORMAT_BYTES_MAX];
         int r, nr = -1;
 
         /* btrfs cannot handle file systems < 16M, hence use this as minimum */
@@ -274,6 +275,10 @@ int setup_machine_directory(uint64_t size, sd_bus_error *error) {
                 goto fail;
         }
 
+        (void) syncfs(fd);
+
+        log_info("Set up /var/lib/machines as btrfs loopback file system of size %s mounted on /var/lib/machines.raw.", format_bytes(buf, sizeof(buf), size));
+
         (void) umount2(mntdir, MNT_DETACH);
         (void) rmdir(mntdir);
         (void) rmdir(tmpdir);
@@ -299,3 +304,73 @@ fail:
 
         return r;
 }
+
+static int sync_path(const char *p) {
+        _cleanup_close_ int fd = -1;
+
+        fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+        if (fd < 0)
+                return -errno;
+
+        if (syncfs(fd) < 0)
+                return -errno;
+
+        return 0;
+}
+
+int grow_machine_directory(void) {
+        char buf[FORMAT_BYTES_MAX];
+        struct statvfs a, b;
+        uint64_t old_size, new_size, max_add;
+        int r;
+
+        /* Ensure the disk space data is accurate */
+        sync_path("/var/lib/machines");
+        sync_path("/var/lib/machines.raw");
+
+        if (statvfs("/var/lib/machines.raw", &a) < 0)
+                return -errno;
+
+        if (statvfs("/var/lib/machines", &b) < 0)
+                return -errno;
+
+        /* Don't grow if not enough disk space is available on the host */
+        if (((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) <= VAR_LIB_MACHINES_FREE_MIN)
+                return 0;
+
+        /* Don't grow if at least 1/3th of the fs is still free */
+        if (b.f_bavail > b.f_blocks / 3)
+                return 0;
+
+        /* Calculate how much we are willing to add at maximum */
+        max_add = ((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) - VAR_LIB_MACHINES_FREE_MIN;
+
+        /* Calculate the old size */
+        old_size = (uint64_t) b.f_blocks * (uint64_t) b.f_bsize;
+
+        /* Calculate the new size as three times the size of what is used right now */
+        new_size = ((uint64_t) b.f_blocks - (uint64_t) b.f_bavail) * (uint64_t) b.f_bsize * 3;
+
+        /* Always, grow at least to the start size */
+        if (new_size < VAR_LIB_MACHINES_SIZE_START)
+                new_size = VAR_LIB_MACHINES_SIZE_START;
+
+        /* If the new size is smaller than the old size, don't grow */
+        if (new_size < old_size)
+                return 0;
+
+        /* Ensure we never add more than the maximum */
+        if (new_size > old_size + max_add)
+                new_size = old_size + max_add;
+
+        r = btrfs_resize_loopback("/var/lib/machines", new_size, true);
+        if (r <= 0)
+                return r;
+
+        r = btrfs_quota_limit("/var/lib/machines", new_size);
+        if (r < 0)
+                return r;
+
+        log_info("Grew /var/lib/machines btrfs loopback file system to %s.", format_bytes(buf, sizeof(buf), new_size));
+        return 1;
+}
index 9c9849f6183a274c30d04f2a0f5c3ad7f851569b..06c5d40583b25c3206cd7dcabf74cdc040ff19cd 100644 (file)
@@ -24,3 +24,4 @@
 #include "sd-bus.h"
 
 int setup_machine_directory(uint64_t size, sd_bus_error *error);
+int grow_machine_directory(void);