@@ -127,6 +127,12 @@ static __u64 free_blocks_count;
127127static int nsegments_per_clean = 2 ;
128128static 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 */
131137static int pm_width = 60 ;
132138static 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