Skip to content

Commit 24c45c4

Browse files
committed
nilfs-resize: force file system update in active segment only situation
In order to shrink the file system even when there are only active segments in the area to be truncated, force arcive segments to be evicted by creating a file (called a balloon file) with the required size on the root directory of the file system and then deleting it. [ adjusted for v2.2.y ] Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
1 parent 2e1f086 commit 24c45c4

1 file changed

Lines changed: 165 additions & 2 deletions

File tree

sbin/nilfs-resize/nilfs-resize.c

Lines changed: 165 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ static __u64 free_blocks_count;
127127
static int nsegments_per_clean = 2;
128128
static struct timespec clean_interval = { 0, 100000000 }; /* 100 msec */
129129

130+
/* balloon file (file for forcing active segments to move) */
131+
#define NILFS_RESIZE_BALLOON_FILENAME_FMT ".nilfs-balloon-%u"
132+
#define NILFS_RESIZE_BALLOON_FILENAME_BUFSZ 32
133+
134+
#define NILFS_RESIZE_BALLOON_MAX_CHUNKSIZE 16392 /* chunk size (bytes) */
135+
130136
/* progress meter */
131137
static int pm_width = 60;
132138
static int pm_barwidth;
@@ -516,6 +522,132 @@ static void nilfs_resize_restore_alloc_range(struct nilfs *nilfs)
516522
nilfs_set_alloc_range(nilfs, 0, fs_devsize);
517523
}
518524

525+
/**
526+
* nilfs_resize_prod_fs - force the file system update to move active segments
527+
* @nilfs: nilfs object
528+
* @nblk_write: write data size (in blocks)
529+
*
530+
* This function creates a temporary file (balloon file) with a random pattern
531+
* of size @nblk_write on the root directory that @nilfs holds, writes it to
532+
* the log via nilfs_sync(), and then deletes it and also deletes checkpoints
533+
* that contain the ballon file.
534+
*
535+
* Return: 0 on success, -1 on error.
536+
*/
537+
static int nilfs_resize_prod_fs(struct nilfs *nilfs, unsigned long nblk_write)
538+
{
539+
char filename[NILFS_RESIZE_BALLOON_FILENAME_BUFSZ];
540+
ssize_t rest = nblk_write * blocksize;
541+
const char *dev = nilfs_get_dev(nilfs);
542+
const char *srcdev = "/dev/urandom";
543+
int dirfd = nilfs_get_root_fd(nilfs);
544+
int out_fd = -1, in_fd = -1;
545+
nilfs_cno_t cno, scno, ecno;
546+
sigset_t newset, sigset;
547+
int read_failed = 0;
548+
int ret, res = -1;
549+
unsigned char *data_buf;
550+
551+
if (!nblk_write)
552+
return 0;
553+
554+
data_buf = malloc(NILFS_RESIZE_BALLOON_MAX_CHUNKSIZE);
555+
if (unlikely(!data_buf))
556+
return -1;
557+
558+
in_fd = open(srcdev, O_RDONLY | O_CLOEXEC);
559+
if (in_fd < 0)
560+
verbose_err("cannot open %s - use calculated values", srcdev);
561+
562+
ret = nilfs_sync(nilfs, &scno);
563+
if (unlikely(ret < 0))
564+
scno = 0;
565+
566+
/* Block signals */
567+
sigemptyset(&newset);
568+
sigaddset(&newset, SIGINT);
569+
sigaddset(&newset, SIGTERM);
570+
ret = sigprocmask(SIG_BLOCK, &newset, &sigset);
571+
if (unlikely(ret < 0)) {
572+
err("cannot block signals");
573+
goto failed;
574+
}
575+
576+
/* Write a balloon file to the filesystem */
577+
snprintf(filename, NILFS_RESIZE_BALLOON_FILENAME_BUFSZ,
578+
NILFS_RESIZE_BALLOON_FILENAME_FMT, (unsigned)getpid());
579+
580+
out_fd = openat(dirfd, filename, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC,
581+
S_IWUSR | S_IRUSR);
582+
if (unlikely(out_fd < 0)) {
583+
err("failed to create balloon file on %s", dev);
584+
goto failed_unblock_signals;
585+
}
586+
587+
while (rest > 0) {
588+
size_t request = min_t(size_t, rest,
589+
NILFS_RESIZE_BALLOON_MAX_CHUNKSIZE);
590+
ssize_t count;
591+
unsigned char *cp;
592+
593+
if (in_fd >= 0 && !read_failed) {
594+
count = read(in_fd, data_buf, request);
595+
if (likely(count > 0)) {
596+
request = count;
597+
goto inflate_balloon;
598+
}
599+
verbose_err("failed to read balloon data from %s", srcdev);
600+
read_failed = 1;
601+
}
602+
/*
603+
* Fallback path.
604+
*
605+
* Since cryptographically-secure random numbers are not required,
606+
* use the old pseudo-random number function rand() instead of
607+
* getrandom() or anything else that depends on the environment.
608+
*/
609+
for (cp = data_buf; cp < data_buf + request; cp++)
610+
*cp = rand() & 0xff;
611+
612+
inflate_balloon:
613+
count = write(out_fd, data_buf, request);
614+
if (unlikely(count < 0)) {
615+
err("failed to inflate balloon file on %s", dev);
616+
break;
617+
}
618+
rest -= count;
619+
}
620+
621+
ret = nilfs_sync(nilfs, &ecno);
622+
if (unlikely(ret < 0))
623+
ecno = 0;
624+
close(out_fd);
625+
626+
/* Delete the balloon file */
627+
ret = unlinkat(dirfd, filename, 0);
628+
if (unlikely(ret < 0))
629+
verbose_err("Balloon file deletion on %s failed", dev);
630+
631+
/* Delete checkpoints created during prodding */
632+
if (likely(ecno > 0)) {
633+
for (cno = (scno ? scno + 1 : ecno); cno <= ecno; cno++) {
634+
nilfs_delete_checkpoint(nilfs, cno);
635+
}
636+
}
637+
nilfs_sync(nilfs, &cno);
638+
res = 0;
639+
640+
failed_unblock_signals:
641+
sigprocmask(SIG_SETMASK, &sigset, NULL); /* Unblock signals */
642+
643+
nilfs_resize_update_sustat(nilfs);
644+
failed:
645+
if (in_fd >= 0)
646+
close(in_fd);
647+
free(data_buf);
648+
return res;
649+
}
650+
519651
/**
520652
* nilfs_resize_find_movable_segments - find movable segments within a
521653
* specified range
@@ -977,6 +1109,7 @@ static int nilfs_resize_reclaim_nibble(struct nilfs *nilfs,
9771109
unsigned long nc;
9781110
unsigned long long end2 = end;
9791111
int log_cursor_updated = 0;
1112+
int prodded = 0;
9801113
int ret;
9811114

9821115
segnum = start;
@@ -1016,10 +1149,40 @@ static int nilfs_resize_reclaim_nibble(struct nilfs *nilfs,
10161149
if (!log_cursor_updated) {
10171150
ret = nilfs_resize_try_update_log_cursor(
10181151
nilfs, "No movable segment");
1019-
if (!ret) {
1152+
if (ret < 0)
1153+
goto failed;
1154+
segnum = start;
1155+
end = end2;
1156+
log_cursor_updated = 1;
1157+
goto retry;
1158+
}
1159+
if (!prodded) {
1160+
unsigned long nblocks, max_blocks;
1161+
1162+
nfound = nilfs_resize_find_active_segments(
1163+
nilfs, start, end2, segnumv, 2, &nblocks);
1164+
if (unlikely(nfound < 0))
1165+
goto failed;
1166+
1167+
if (nfound > 0) {
1168+
max_blocks = blocks_per_segment * 2;
1169+
assert(max_blocks > nblocks);
1170+
1171+
verbose_msg("Active segment%s blocking but there are "
1172+
"no movable segments.\n"
1173+
"Try forcing a filesystem update.\n",
1174+
nfound == 1 ? " is" : "s are");
1175+
ret = nilfs_resize_prod_fs(nilfs, max_blocks - nblocks);
1176+
if (unlikely(ret < 0)) {
1177+
err("forced filesystem update failed");
1178+
goto failed;
1179+
}
1180+
verbose_msg("Succeeded - retry moving segments.\n");
1181+
10201182
segnum = start;
10211183
end = end2;
1022-
log_cursor_updated = 1;
1184+
log_cursor_updated = 0;
1185+
prodded = 1;
10231186
goto retry;
10241187
}
10251188
}

0 commit comments

Comments
 (0)