copy: Introduce COPY_NOCOW_AFTER and use it when copying images

When dealing with copying COW images, we have to make a tradeoff:

- Either we don't touch the NOCOW bit on the copied file COW and get
  an instant copy because we're able to reflink, but we might get
  reduced performance if the source file was COW as COW files and lots
  of random writes don't play well together.
- Or we force NOCOW for the copied file, which means we have to do a
  full copy as reflinking from COW files to NOCOW files or vice versa
  is not supported.

In exec-invoke.c, we've opted for the first option. In nspawn.c and
discover-image.c, we've opted for the second option.

In nspawn, this applies to the --ephemeral option to make ephemeral
copies. In discover-image.c, this applies to cloning images into
/var/lib/machines. Both these features might be used to run many
machines of the same original image. We really don't want to force
a full copy onto users in these scenarios when they're expecting
reflink behavior, leading to them running out of disk space. Instead,
degraded performance in their machines is a much less severe issue,
which they will discover on their own if it affects them, at which
point they can make their original image NOCOW at which point they'll
get both the reflinks and better performance.

Given the above reasoning, let's switch nspawn.c and discover-image.c
to use COPY_NOCOW_AFTER as well instead of enabling NOCOW upfront and
forcing a copy if the original source image is COW.
This commit is contained in:
Daan De Meyer
2024-09-04 19:23:16 +02:00
parent 8af3b12fe0
commit b1cfa93080
5 changed files with 21 additions and 27 deletions
+2 -17
View File
@@ -2881,26 +2881,11 @@ static int setup_ephemeral(
if (*root_image) {
log_debug("Making ephemeral copy of %s to %s", *root_image, new_root);
fd = copy_file(*root_image,
new_root,
O_EXCL,
0600,
COPY_LOCK_BSD|
COPY_REFLINK|
COPY_CRTIME);
fd = copy_file(*root_image, new_root, O_EXCL, 0600,
COPY_LOCK_BSD|COPY_REFLINK|COPY_CRTIME|COPY_NOCOW_AFTER);
if (fd < 0)
return log_debug_errno(fd, "Failed to copy image %s to %s: %m",
*root_image, new_root);
/* A root image might be subject to lots of random writes so let's try to disable COW on it
* which tends to not perform well in combination with lots of random writes.
*
* Note: btrfs actually isn't impressed by us setting the flag after making the reflink'ed
* copy, but we at least want to make the intention clear.
*/
r = chattr_fd(fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
if (r < 0)
log_debug_errno(r, "Failed to disable copy-on-write for %s, ignoring: %m", new_root);
} else {
assert(*root_directory);