1 /* $NetBSD: lfs_cleanerd.c,v 1.61 2026/04/23 16:26:05 perseant Exp $ */ 2 3 /*- 4 * Copyright (c) 2005, 2026 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Konrad E. Schroder <perseant (at) hhhh.org>. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * The cleaner daemon for the NetBSD Log-structured File System. 34 * Only tested for use with version 2 LFSs. 35 */ 36 37 #include <sys/syslog.h> 38 #include <sys/param.h> 39 #include <sys/mount.h> 40 #include <sys/stat.h> 41 #include <ufs/lfs/lfs.h> 42 43 #include <assert.h> 44 #include <err.h> 45 #include <errno.h> 46 #include <fcntl.h> 47 #include <semaphore.h> 48 #include <stdbool.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 #include <time.h> 54 #include <util.h> 55 56 #include "cleaner.h" 57 #include "kernelops.h" 58 #include "mount_lfs.h" 59 60 /* 61 * Global variables. 62 */ 63 /* XXX these top few should really be fs-specific */ 64 int use_fs_idle; /* Use fs idle rather than cpu idle time */ 65 int use_bytes; /* Use bytes written rather than segments cleaned */ 66 double load_threshold; /* How idle is idle (CPU idle) */ 67 int atatime; /* How many segments (bytes) to clean at a time */ 68 69 int nfss; /* Number of filesystems monitored by this cleanerd */ 70 struct clfs **fsp; /* Array of extended filesystem structures */ 71 int segwait_timeout; /* Time to wait in lfs_segwait() */ 72 int do_quit; /* Quit after one cleaning loop */ 73 int do_coalesce; /* Coalesce filesystem */ 74 int do_small; /* Use small writes through markv */ 75 char *do_asdevice; /* Use this as the raw device */ 76 char *copylog_filename; /* File to use for fs debugging analysis */ 77 int inval_segment; /* Segment to invalidate */ 78 int stat_report; /* Report statistics for this period of cycles */ 79 int debug; /* Turn on debugging */ 80 struct cleaner_stats { 81 double util_tot; 82 double util_sos; 83 off_t bytes_read; 84 off_t bytes_written; 85 off_t segs_cleaned; 86 off_t segs_empty; 87 off_t segs_error; 88 } cleaner_stats; 89 90 extern u_int32_t cksum(void *, size_t); 91 extern u_int32_t lfs_sb_cksum(struct dlfs *); 92 extern u_int32_t lfs_cksum_part(void *, size_t, u_int32_t); 93 94 /* Ugh */ 95 #define FSMNT_SIZE MAX(sizeof(((struct dlfs *)0)->dlfs_fsmnt), \ 96 sizeof(((struct dlfs64 *)0)->dlfs_fsmnt)) 97 98 99 /* Compat */ 100 void pwarn(const char *unused, ...) { /* Does nothing */ }; 101 102 /* 103 * Log a message if debugging is turned on. 104 */ 105 void 106 dlog(const char *fmt, ...) 107 { 108 va_list ap; 109 110 if (debug == 0) 111 return; 112 113 va_start(ap, fmt); 114 vsyslog(LOG_DEBUG, fmt, ap); 115 va_end(ap); 116 } 117 118 /* 119 * Remove the specified filesystem from the list, due to its having 120 * become unmounted or other error condition. 121 */ 122 void 123 handle_error(struct clfs **cfsp, int n) 124 { 125 syslog(LOG_NOTICE, "%s: detaching cleaner", lfs_sb_getfsmnt(cfsp[n])); 126 free(cfsp[n]); 127 if (n != nfss - 1) 128 cfsp[n] = cfsp[nfss - 1]; 129 --nfss; 130 } 131 132 /* 133 * Reinitialize a filesystem if, e.g., its size changed. 134 */ 135 int 136 reinit_fs(struct clfs *fs) 137 { 138 char fsname[FSMNT_SIZE]; 139 140 memcpy(fsname, lfs_sb_getfsmnt(fs), sizeof(fsname)); 141 fsname[sizeof(fsname) - 1] = '\0'; 142 143 kops.ko_close(fs->clfs_ifilefd); 144 kops.ko_close(fs->clfs_devfd); 145 free(fs->clfs_dev); 146 free(fs->clfs_segtab); 147 free(fs->clfs_segtabp); 148 149 return init_fs(fs, fsname); 150 } 151 152 153 /* 154 * Set up the file descriptors, including the Ifile descriptor. 155 * If we can't get the Ifile, this is not an LFS (or the kernel is 156 * too old to support the fcntl). 157 * XXX Merge this and init_unmounted_fs, switching on whether 158 * XXX "fsname" is a dir or a char special device. Should 159 * XXX also be able to read unmounted devices out of fstab, the way 160 * XXX fsck does. 161 */ 162 int 163 init_fs(struct clfs *fs, char *fsname) 164 { 165 char mnttmp[FSMNT_SIZE]; 166 struct statvfs sf; 167 int rootfd; 168 int i; 169 void *sbuf; 170 size_t mlen; 171 172 if (do_asdevice != NULL) { 173 fs->clfs_dev = strndup(do_asdevice,strlen(do_asdevice) + 2); 174 if (fs->clfs_dev == NULL) { 175 syslog(LOG_ERR, "couldn't malloc device name string: %m"); 176 return -1; 177 } 178 } else { 179 /* 180 * Get the raw device from the block device. 181 * XXX this is ugly. Is there a way to discover the raw device 182 * XXX for a given mount point? 183 */ 184 if (kops.ko_statvfs(fsname, &sf, ST_WAIT) < 0) 185 return -1; 186 mlen = strlen(sf.f_mntfromname) + 2; 187 fs->clfs_dev = malloc(mlen); 188 if (fs->clfs_dev == NULL) { 189 syslog(LOG_ERR, "couldn't malloc device name string: %m"); 190 return -1; 191 } 192 if (getdiskrawname(fs->clfs_dev, mlen, sf.f_mntfromname) == NULL) { 193 syslog(LOG_ERR, "couldn't convert '%s' to raw name: %m", 194 sf.f_mntfromname); 195 return -1; 196 } 197 } 198 if ((fs->clfs_devfd = kops.ko_open(fs->clfs_dev, O_RDONLY, 0)) < 0) { 199 syslog(LOG_ERR, "couldn't open device %s for reading: %m", 200 fs->clfs_dev); 201 return -1; 202 } 203 204 /* Find the Ifile and open it */ 205 if ((rootfd = kops.ko_open(fsname, O_RDONLY, 0)) < 0) 206 return -2; 207 if (kops.ko_fcntl(rootfd, LFCNIFILEFH, &fs->clfs_ifilefh) < 0) 208 return -3; 209 if ((fs->clfs_ifilefd = kops.ko_fhopen(&fs->clfs_ifilefh, 210 sizeof(fs->clfs_ifilefh), O_RDONLY)) < 0) 211 return -4; 212 kops.ko_close(rootfd); 213 214 sbuf = malloc(LFS_SBPAD); 215 if (sbuf == NULL) { 216 syslog(LOG_ERR, "couldn't malloc superblock buffer"); 217 return -1; 218 } 219 220 /* Load in the superblock */ 221 if (kops.ko_pread(fs->clfs_devfd, sbuf, LFS_SBPAD, LFS_LABELPAD) < 0) { 222 free(sbuf); 223 return -1; 224 } 225 226 __CTASSERT(sizeof(struct dlfs) == sizeof(struct dlfs64)); 227 memcpy(&fs->lfs_dlfs_u, sbuf, sizeof(struct dlfs)); 228 free(sbuf); 229 230 /* If it is not LFS, complain and exit! */ 231 switch (fs->lfs_dlfs_u.u_32.dlfs_magic) { 232 case LFS_MAGIC: 233 fs->lfs_is64 = false; 234 fs->lfs_dobyteswap = false; 235 break; 236 case LFS_MAGIC_SWAPPED: 237 fs->lfs_is64 = false; 238 fs->lfs_dobyteswap = true; 239 break; 240 case LFS64_MAGIC: 241 fs->lfs_is64 = true; 242 fs->lfs_dobyteswap = false; 243 break; 244 case LFS64_MAGIC_SWAPPED: 245 fs->lfs_is64 = true; 246 fs->lfs_dobyteswap = true; 247 break; 248 default: 249 syslog(LOG_ERR, "%s: not LFS", fsname); 250 return -1; 251 } 252 /* XXX: can this ever need to be set? does the cleaner even care? */ 253 fs->lfs_hasolddirfmt = 0; 254 255 /* If this is not a version 2 filesystem, complain and exit */ 256 if (lfs_sb_getversion(fs) != 2) { 257 syslog(LOG_ERR, "%s: not a version 2 LFS", fsname); 258 return -1; 259 } 260 261 /* Assume fsname is the mounted name */ 262 strncpy(mnttmp, fsname, sizeof(mnttmp)); 263 mnttmp[sizeof(mnttmp) - 1] = '\0'; 264 lfs_sb_setfsmnt(fs, mnttmp); 265 266 /* Allocate and clear segtab */ 267 fs->clfs_segtab = (struct clfs_seguse *)malloc(lfs_sb_getnseg(fs) * 268 sizeof(*fs->clfs_segtab)); 269 fs->clfs_segtabp = (struct clfs_seguse **)malloc(lfs_sb_getnseg(fs) * 270 sizeof(*fs->clfs_segtabp)); 271 if (fs->clfs_segtab == NULL || fs->clfs_segtabp == NULL) { 272 syslog(LOG_ERR, "%s: couldn't malloc segment table: %m", 273 fs->clfs_dev); 274 return -1; 275 } 276 277 for (i = 0; i < lfs_sb_getnseg(fs); i++) { 278 fs->clfs_segtabp[i] = &(fs->clfs_segtab[i]); 279 fs->clfs_segtab[i].flags = 0x0; 280 } 281 282 syslog(LOG_NOTICE, "%s: attaching cleaner", fsname); 283 return 0; 284 } 285 286 void 287 calc_cb(struct clfs *fs, int sn, struct clfs_seguse *t) 288 { 289 time_t now; 290 int64_t age, benefit, cost; 291 292 time(&now); 293 age = (now < t->lastmod ? 0 : now - t->lastmod); 294 295 /* Under no circumstances clean active or already-clean segments */ 296 if ((t->flags & SEGUSE_ACTIVE) || !(t->flags & SEGUSE_DIRTY)) { 297 t->priority = 0; 298 return; 299 } 300 301 /* 302 * If the segment is empty, there is no reason to clean it. 303 * Clear its error condition, if any, since we are never going to 304 * try to parse this one. 305 */ 306 if (t->nbytes == 0) { 307 t->flags &= ~SEGUSE_ERROR; /* Strip error once empty */ 308 t->priority = 0; 309 return; 310 } 311 312 if (t->flags & SEGUSE_ERROR) { /* No good if not already empty */ 313 /* No benefit */ 314 t->priority = 0; 315 return; 316 } 317 318 if (t->nbytes > lfs_sb_getssize(fs)) { 319 /* Another type of error */ 320 syslog(LOG_WARNING, "segment %d: bad seguse count %d", 321 sn, t->nbytes); 322 t->flags |= SEGUSE_ERROR; 323 t->priority = 0; 324 return; 325 } 326 327 /* 328 * The non-degenerate case. Use Rosenblum's cost-benefit algorithm. 329 * Calculate the benefit from cleaning this segment (one segment, 330 * minus fragmentation, dirty blocks and a segment summary block) 331 * and weigh that against the cost (bytes read plus bytes written). 332 * We count the summary headers as "dirty" to avoid cleaning very 333 * old and very full segments. 334 */ 335 benefit = (int64_t)lfs_sb_getssize(fs) - t->nbytes - 336 (t->nsums + 1) * lfs_sb_getfsize(fs); 337 if (lfs_sb_getbsize(fs) > lfs_sb_getfsize(fs)) /* fragmentation */ 338 benefit -= (lfs_sb_getbsize(fs) / 2); 339 if (benefit <= 0) { 340 t->priority = 0; 341 return; 342 } 343 344 cost = lfs_sb_getssize(fs) + t->nbytes; 345 t->priority = (256 * benefit * age) / cost; 346 347 return; 348 } 349 350 /* 351 * Comparator for sort_segments: cost-benefit equation. 352 */ 353 static int 354 cb_comparator(const void *va, const void *vb) 355 { 356 const struct clfs_seguse *a, *b; 357 358 a = *(const struct clfs_seguse * const *)va; 359 b = *(const struct clfs_seguse * const *)vb; 360 return a->priority > b->priority ? -1 : 1; 361 } 362 363 static void 364 add_stats(int nsegs, struct lfs_write_stats *lws) 365 { 366 /* XXX */ 367 } 368 369 /* 370 * Clean a segment and mark it invalid. 371 */ 372 int 373 invalidate_segment(struct clfs *fs, int sn) 374 { 375 int r; 376 struct lfs_segnum_array lsa; 377 378 dlog("%s: inval seg %d", lfs_sb_getfsmnt(fs), sn); 379 380 /* 381 * Use LFCNREWRITESEG to move the blocks. 382 */ 383 lsa.len = 1; 384 lsa.segments = &sn; 385 if ((r = kops.ko_fcntl(fs->clfs_ifilefd, LFCNREWRITESEGS, &lsa)) < 0) { 386 syslog(LOG_WARNING, "%s: rewriteseg returned %d (%m) " 387 "for seg %d", lfs_sb_getfsmnt(fs), r, sn); 388 return r; 389 } 390 391 /* Record stats */ 392 add_stats(1, &lsa.stats); 393 394 /* 395 * Finally call invalidate to invalidate the segment. 396 */ 397 if ((r = kops.ko_fcntl(fs->clfs_ifilefd, LFCNINVAL, &sn)) < 0) { 398 syslog(LOG_WARNING, "%s: inval returned %d (%m) " 399 "for seg %d", lfs_sb_getfsmnt(fs), r, sn); 400 return r; 401 } 402 403 return 0; 404 } 405 406 #define NRECENT 10 407 static int recent[NRECENT]; 408 static int recent_last = 0; 409 410 static SEGUSE seguse_array[LFS_SEGUSE_MAXCNT]; 411 412 /* 413 * Select segments to clean, and call the fcntl to clean them. 414 */ 415 static int 416 clean_fs(struct clfs *fs, const CLEANERINFO64 *cip) 417 { 418 int i, j, ngood, sn, bic, r, npos; 419 SEGUSE *sup; 420 static BLOCK_INFO *bip; 421 off_t totbytes; 422 off_t nb; 423 off_t goal; 424 double util; 425 struct lfs_segnum_array sna; 426 int segtab[LFS_REWRITE_MAXCNT]; 427 struct lfs_seguse_array sua; 428 429 /* Read the segment table into our private structure */ 430 sua.len = LFS_SEGUSE_MAXCNT; 431 sua.seguse = seguse_array; 432 npos = 0; 433 434 for (i = 0; i < lfs_sb_getnseg(fs); i+= LFS_SEGUSE_MAXCNT) { 435 sua.start = i; 436 if (fcntl(fs->clfs_ifilefd, LFCNSEGUSE, &sua) < 0) { 437 dlog("%s: fcntl returned %d", lfs_sb_getfsmnt(fs), 438 errno); 439 break; 440 } 441 442 for (j = 0; j < LFS_SEGUSE_MAXCNT && i + j < lfs_sb_getnseg(fs); j++) { 443 sup = &sua.seguse[j]; 444 fs->clfs_segtab[i + j].nbytes = sup->su_nbytes; 445 fs->clfs_segtab[i + j].nsums = sup->su_nsums; 446 fs->clfs_segtab[i + j].lastmod = sup->su_lastmod; 447 /* Keep error status but renew other flags */ 448 fs->clfs_segtab[i + j].flags &= SEGUSE_ERROR; 449 fs->clfs_segtab[i + j].flags |= sup->su_flags; 450 451 /* Compute cost-benefit coefficient */ 452 calc_cb(fs, i + j, fs->clfs_segtab + i + j); 453 if (fs->clfs_segtab[i + j].priority > 0) 454 ++npos; 455 } 456 } 457 458 /* Lower the priority of recently treated segments */ 459 for (i = 0; i < NRECENT; i++) 460 fs->clfs_segtab[recent[i]].priority /= 2; 461 462 /* Sort segments based on cleanliness, fulness, and condition */ 463 heapsort(fs->clfs_segtabp, lfs_sb_getnseg(fs), sizeof(struct clfs_seguse *), 464 cb_comparator); 465 466 /* If no segment is cleanable, just return */ 467 if (fs->clfs_segtabp[0]->priority == 0) { 468 dlog("%s: no segment cleanable", lfs_sb_getfsmnt(fs)); 469 return 0; 470 } 471 472 /* Load some segments' blocks into bip */ 473 bic = 0; 474 fs->clfs_nactive = 0; 475 ngood = 0; 476 sna.len = 0; 477 sna.segments = segtab; 478 if (use_bytes) { 479 /* Set attainable goal */ 480 goal = lfs_sb_getssize(fs) * atatime; 481 if (goal > (cip->clean - 1) * lfs_sb_getssize(fs) / 2) 482 goal = MAX((cip->clean - 1) * lfs_sb_getssize(fs), 483 lfs_sb_getssize(fs)) / 2; 484 485 dlog("%s: cleaning with goal %" PRId64 486 " bytes (%d segs clean, %d cleanable)", 487 lfs_sb_getfsmnt(fs), goal, cip->clean, npos); 488 syslog(LOG_INFO, "%s: cleaning with goal %" PRId64 489 " bytes (%d segs clean, %d cleanable)", 490 lfs_sb_getfsmnt(fs), goal, cip->clean, npos); 491 totbytes = 0; 492 for (i = 0; i < lfs_sb_getnseg(fs) && totbytes < goal; i++) { 493 if (fs->clfs_segtabp[i]->priority == 0) 494 break; 495 /* Upper bound on number of segments at once */ 496 if (ngood * lfs_sb_getssize(fs) > 4 * goal) 497 break; 498 sn = (fs->clfs_segtabp[i] - fs->clfs_segtab); 499 dlog("%s: add seg %d prio %" PRIu64 500 " containing %ld bytes", 501 lfs_sb_getfsmnt(fs), sn, fs->clfs_segtabp[i]->priority, 502 fs->clfs_segtabp[i]->nbytes); 503 sna.segments[sna.len++] = sn; 504 recent[recent_last] = sn; 505 recent_last = (recent_last + 1) % NRECENT; 506 totbytes += fs->clfs_segtabp[i]->nbytes; 507 ++ngood; 508 } 509 } else { 510 /* Set attainable goal */ 511 goal = atatime; 512 if (goal > cip->clean - 1) 513 goal = MAX(cip->clean - 1, 1); 514 515 dlog("%s: cleaning with goal %d segments (%d clean, %d cleanable)", 516 lfs_sb_getfsmnt(fs), (int)goal, cip->clean, npos); 517 for (i = 0; i < lfs_sb_getnseg(fs) && ngood < goal; i++) { 518 if (fs->clfs_segtabp[i]->priority == 0) 519 break; 520 sn = (fs->clfs_segtabp[i] - fs->clfs_segtab); 521 dlog("%s: add seg %d prio %" PRIu64, 522 lfs_sb_getfsmnt(fs), sn, fs->clfs_segtabp[i]->priority); 523 sna.segments[sna.len++] = sn; 524 recent[recent_last] = sn; 525 recent_last = (recent_last + 1) % NRECENT; 526 ++ngood; 527 } 528 } 529 530 /* If there is nothing to do, try again later. */ 531 if (sna.len == 0) { 532 dlog("%s: no blocks to clean in %d cleanable segments", 533 lfs_sb_getfsmnt(fs), (int)ngood); 534 return 0; 535 } 536 537 /* Record statistics */ 538 for (i = nb = 0; i < bic; i++) 539 nb += bip[i].bi_size; 540 util = ((double)nb) / (fs->clfs_nactive * lfs_sb_getssize(fs)); 541 cleaner_stats.util_tot += util; 542 cleaner_stats.util_sos += util * util; 543 cleaner_stats.bytes_written += nb; 544 545 /* 546 * Use LFCNREWRITESEGS to move the blocks. 547 */ 548 if ((r = kops.ko_fcntl(fs->clfs_ifilefd, LFCNREWRITESEGS, &sna)) < 0) { 549 int oerrno = errno; 550 syslog(LOG_WARNING, "%s: fcntl returned %d (errno %d, %m)", 551 lfs_sb_getfsmnt(fs), r, errno); 552 if (oerrno != EAGAIN && oerrno != ESHUTDOWN) { 553 syslog(LOG_DEBUG, "%s: errno %d, returning", 554 lfs_sb_getfsmnt(fs), oerrno); 555 return r; 556 } 557 if (oerrno == ESHUTDOWN) { 558 syslog(LOG_NOTICE, "%s: filesystem unmounted", 559 lfs_sb_getfsmnt(fs)); 560 return r; 561 } 562 } 563 564 /* Even with an error, some blocks may have been written */ 565 add_stats(sna.len, &sna.stats); 566 567 /* 568 * Call reclaim to prompt cleaning of the segments. 569 */ 570 struct lfs_write_stats reclaim_stats; 571 if ((r = kops.ko_fcntl(fs->clfs_ifilefd, LFCNRECLAIM, &reclaim_stats)) < 0) 572 dlog("%s: reclaim failed with errno=%d", 573 lfs_sb_getfsmnt(fs), errno); 574 575 /* 576 * Report progress (or lack thereof) 577 */ 578 double cost = (sna.stats.offset + reclaim_stats.offset) 579 * lfs_sb_getfsize(fs); 580 double revenue = sna.len * lfs_sb_getssize(fs); 581 syslog(LOG_INFO, "%s: wrote %d direct and %d total frags" 582 " to clean %d segs (%d%% recovery)", 583 lfs_sb_getfsmnt(fs), sna.stats.direct, sna.stats.offset, 584 (int)sna.len, (int)(revenue == 0.0 ? 0 585 : 100 * (revenue - cost) / revenue)); 586 if (cost > revenue) 587 syslog(LOG_WARNING, "%s: cleaner not making forward progress", 588 lfs_sb_getfsmnt(fs)); 589 590 return 0; 591 } 592 593 /* 594 * Read the cleanerinfo block and apply cleaning policy to determine whether 595 * the given filesystem needs to be cleaned. Returns 1 if it does, 0 if it 596 * does not, or -1 on error. 597 */ 598 static int 599 needs_cleaning(struct clfs *fs, CLEANERINFO64 *cip) 600 { 601 struct stat st; 602 daddr_t fsb_per_seg, max_free_segs; 603 time_t now; 604 double loadavg; 605 606 /* If this fs is "on hold", don't clean it. */ 607 if (fs->clfs_onhold) { 608 #if defined(__GNUC__) && \ 609 (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) && \ 610 defined(__OPTIMIZE_SIZE__) 611 /* 612 * XXX: Work around apparent bug with GCC >= 4.8 and -Os: it 613 * claims that ci.clean is uninitialized in clean_fs (at one 614 * of the several uses of it, which is neither the first nor 615 * last use) -- this doesn't happen with plain -O2. 616 * 617 * Hopefully in the future further rearrangements will allow 618 * removing this hack. 619 */ 620 cip->clean = 0; 621 #endif 622 return 0; 623 } 624 625 /* Read the cleanerinfo from the Ifile. */ 626 if (fcntl(fs->clfs_ifilefd, LFCNCLEANERINFO, cip) < 0) { 627 syslog(LOG_ERR, "%s: failed to read cleaner info", lfs_sb_getfsmnt(fs)); 628 } 629 dlog("Read cleanerinfo: %d clean %d dirty %ld bfree %ld avail flags 0x%x", 630 (int)cip->clean, 631 (int)cip->dirty, 632 (long)cip->bfree, 633 (long)cip->avail, 634 (unsigned)cip->flags); 635 636 /* 637 * If the number of segments changed under us, reinit. 638 * We don't have to start over from scratch, however, 639 * since we don't hold any buffers. 640 */ 641 if (lfs_sb_getnseg(fs) != cip->clean + cip->dirty) { 642 if (reinit_fs(fs) < 0) { 643 /* The normal case for unmount */ 644 syslog(LOG_NOTICE, "%s: filesystem unmounted", lfs_sb_getfsmnt(fs)); 645 return -1; 646 } 647 syslog(LOG_NOTICE, "%s: nsegs changed", lfs_sb_getfsmnt(fs)); 648 } 649 650 /* Compute theoretical "free segments" maximum based on usage */ 651 fsb_per_seg = lfs_segtod(fs, 1); 652 max_free_segs = MAX(cip->bfree, 0) / fsb_per_seg + lfs_sb_getminfreeseg(fs); 653 654 dlog("%s: bfree = %d, avail = %d, clean = %d/%d", 655 lfs_sb_getfsmnt(fs), cip->bfree, cip->avail, cip->clean, 656 lfs_sb_getnseg(fs)); 657 658 /* If the writer is waiting on us, clean it */ 659 if (cip->clean <= lfs_sb_getminfreeseg(fs) || 660 (cip->flags & LFS_CLEANER_MUST_CLEAN)) { 661 dlog("%s: must clean", lfs_sb_getfsmnt(fs)); 662 return 1; 663 } 664 665 /* If there are enough segments, don't clean it */ 666 if (cip->bfree - cip->avail <= fsb_per_seg && 667 cip->avail > fsb_per_seg) { 668 dlog("%s: enough clean", lfs_sb_getfsmnt(fs)); 669 return 0; 670 } 671 672 /* If we are in dire straits, clean it */ 673 if (cip->bfree - cip->avail > fsb_per_seg && 674 cip->avail <= fsb_per_seg) { 675 dlog("%s: dire straits", lfs_sb_getfsmnt(fs)); 676 return 1; 677 } 678 679 /* If under busy threshold, clean regardless of load */ 680 if (cip->clean < max_free_segs * BUSY_LIM) { 681 dlog("%s: busy clean", lfs_sb_getfsmnt(fs)); 682 return 1; 683 } 684 685 /* Check busy status; clean if idle and under idle limit */ 686 if (use_fs_idle) { 687 /* Filesystem idle */ 688 time(&now); 689 if (fstat(fs->clfs_ifilefd, &st) < 0) { 690 syslog(LOG_ERR, "%s: failed to stat ifile", 691 lfs_sb_getfsmnt(fs)); 692 return -1; 693 } 694 if (now - st.st_mtime > segwait_timeout && 695 cip->clean < max_free_segs * IDLE_LIM) { 696 dlog("%s: idle clean with fs idle %d", 697 lfs_sb_getfsmnt(fs), (int)(now - st.st_mtime)); 698 return 1; 699 } 700 } else { 701 /* CPU idle - use one-minute load avg */ 702 if (getloadavg(&loadavg, 1) == -1) { 703 syslog(LOG_ERR, "%s: failed to get load avg", 704 lfs_sb_getfsmnt(fs)); 705 return -1; 706 } 707 if (loadavg < load_threshold && 708 cip->clean < max_free_segs * IDLE_LIM) { 709 dlog("%s: idle clean with load %f", 710 lfs_sb_getfsmnt(fs), loadavg); 711 return 1; 712 } 713 } 714 715 dlog("%s: default no clean", lfs_sb_getfsmnt(fs)); 716 return 0; 717 } 718 719 /* 720 * Report statistics. If the signal was SIGUSR2, clear the statistics too. 721 * If the signal was SIGINT, exit. 722 */ 723 static void 724 sig_report(int sig) 725 { 726 double avg = 0.0, stddev; 727 728 avg = cleaner_stats.util_tot / MAX(cleaner_stats.segs_cleaned, 1.0); 729 stddev = cleaner_stats.util_sos / MAX(cleaner_stats.segs_cleaned - 730 avg * avg, 1.0); 731 syslog(LOG_INFO, "bytes read: %" PRId64, cleaner_stats.bytes_read); 732 syslog(LOG_INFO, "bytes written: %" PRId64, cleaner_stats.bytes_written); 733 syslog(LOG_INFO, "segments cleaned: %" PRId64, cleaner_stats.segs_cleaned); 734 #if 0 735 /* "Empty segments" is meaningless, since the kernel handles those */ 736 syslog(LOG_INFO, "empty segments: %" PRId64, cleaner_stats.segs_empty); 737 #endif 738 syslog(LOG_INFO, "error segments: %" PRId64, cleaner_stats.segs_error); 739 syslog(LOG_INFO, "utilization total: %g", cleaner_stats.util_tot); 740 syslog(LOG_INFO, "utilization sos: %g", cleaner_stats.util_sos); 741 syslog(LOG_INFO, "utilization avg: %4.2f", avg); 742 syslog(LOG_INFO, "utilization sdev: %9.6f", stddev); 743 744 if (sig == SIGUSR2) 745 memset(&cleaner_stats, 0, sizeof(cleaner_stats)); 746 if (sig == SIGINT) 747 exit(0); 748 } 749 750 static void 751 sig_exit(int sig) 752 { 753 exit(0); 754 } 755 756 static void 757 usage(void) 758 { 759 fprintf(stderr, "usage: lfs_cleanerd [-bcdfmqsJ] [-i segnum] [-l load]\n" 760 "\t[-n nsegs] [-r report_freq] [-t timeout] fs_name ...\n"); 761 exit(1); 762 } 763 764 #ifndef LFS_CLEANER_AS_LIB 765 /* 766 * Main. 767 */ 768 int 769 main(int argc, char **argv) 770 { 771 772 return lfs_cleaner_main(argc, argv); 773 } 774 #endif 775 776 int 777 lfs_cleaner_main(int argc, char **argv) 778 { 779 int i, opt, error, r, loopcount, nodetach; 780 struct timeval tv; 781 #ifdef LFS_CLEANER_AS_LIB 782 sem_t *semaddr = NULL; 783 #endif 784 CLEANERINFO64 ci; 785 #ifndef USE_CLIENT_SERVER 786 char *cp, *pidname; 787 #endif 788 789 /* 790 * Set up defaults 791 */ 792 atatime = 1; 793 segwait_timeout = 300; /* Five minutes */ 794 load_threshold = 0.2; 795 stat_report = 0; 796 inval_segment = -1; 797 copylog_filename = NULL; 798 nodetach = 0; 799 do_asdevice = NULL; 800 801 /* 802 * Parse command-line arguments 803 */ 804 while ((opt = getopt(argc, argv, "bC:cdDfi:J:l:mn:qr:sS:t:")) != -1) { 805 switch (opt) { 806 case 'b': /* Use bytes written, not segments read */ 807 use_bytes = 1; 808 break; 809 case 'C': /* copy log */ 810 copylog_filename = optarg; 811 break; 812 case 'c': /* Coalesce files */ 813 do_coalesce++; 814 break; 815 case 'd': /* Debug mode. */ 816 nodetach++; 817 debug++; 818 break; 819 case 'D': /* stay-on-foreground */ 820 nodetach++; 821 break; 822 case 'f': /* Use fs idle time rather than cpu idle */ 823 use_fs_idle = 1; 824 break; 825 case 'i': /* Invalidate this segment */ 826 inval_segment = atoi(optarg); 827 break; 828 case 'l': /* Load below which to clean */ 829 load_threshold = atof(optarg); 830 break; 831 case 'm': /* [compat only] */ 832 break; 833 case 'n': /* How many segs to clean at once */ 834 atatime = atoi(optarg); 835 break; 836 case 'q': /* Quit after one run */ 837 do_quit = 1; 838 break; 839 case 'r': /* Report every stat_report segments */ 840 stat_report = atoi(optarg); 841 break; 842 case 's': /* Small writes */ 843 do_small = 1; 844 break; 845 #ifdef LFS_CLEANER_AS_LIB 846 case 'S': /* semaphore */ 847 semaddr = (void*)(uintptr_t)strtoull(optarg,NULL,0); 848 break; 849 #endif 850 case 't': /* timeout */ 851 segwait_timeout = atoi(optarg); 852 break; 853 case 'J': /* do as a device */ 854 do_asdevice = optarg; 855 break; 856 default: 857 usage(); 858 /* NOTREACHED */ 859 } 860 } 861 argc -= optind; 862 argv += optind; 863 864 if (argc < 1) 865 usage(); 866 if (inval_segment >= 0 && argc != 1) { 867 errx(1, "lfs_cleanerd: may only specify one filesystem when " 868 "using -i flag"); 869 } 870 871 if (do_coalesce) { 872 errx(1, "lfs_cleanerd: -c disabled due to reports of file " 873 "corruption; you may re-enable it by rebuilding the " 874 "cleaner"); 875 } 876 877 /* 878 * Set up daemon mode or foreground mode 879 */ 880 if (nodetach) { 881 openlog("lfs_cleanerd", LOG_NDELAY | LOG_PID | LOG_PERROR, 882 LOG_DAEMON); 883 signal(SIGINT, sig_report); 884 } else { 885 if (daemon(0, 0) == -1) 886 err(1, "lfs_cleanerd: couldn't become a daemon!"); 887 openlog("lfs_cleanerd", LOG_NDELAY | LOG_PID, LOG_DAEMON); 888 signal(SIGINT, sig_exit); 889 } 890 891 /* 892 * Look for an already-running master daemon. If there is one, 893 * send it our filesystems to add to its list and exit. 894 * If there is none, become the master. 895 */ 896 #ifdef USE_CLIENT_SERVER 897 try_to_become_master(argc, argv); 898 #else 899 /* XXX think about this */ 900 asprintf(&pidname, "lfs_cleanerd:m:%s", argv[0]); 901 if (pidname == NULL) { 902 syslog(LOG_ERR, "malloc failed: %m"); 903 exit(1); 904 } 905 for (cp = pidname; cp != NULL; cp = strchr(cp, '/')) 906 *cp = '|'; 907 pidfile(pidname); 908 #endif 909 910 /* 911 * Signals mean daemon should report its statistics 912 */ 913 memset(&cleaner_stats, 0, sizeof(cleaner_stats)); 914 signal(SIGUSR1, sig_report); 915 signal(SIGUSR2, sig_report); 916 917 /* 918 * Initialize cleaning structures, open devices, etc. 919 */ 920 nfss = argc; 921 fsp = (struct clfs **)malloc(nfss * sizeof(*fsp)); 922 if (fsp == NULL) { 923 syslog(LOG_ERR, "couldn't allocate fs table: %m"); 924 exit(1); 925 } 926 for (i = 0; i < nfss; i++) { 927 fsp[i] = (struct clfs *)calloc(1, sizeof(**fsp)); 928 if ((r = init_fs(fsp[i], argv[i])) < 0) { 929 syslog(LOG_ERR, "%s: couldn't init: error code %d", 930 argv[i], r); 931 handle_error(fsp, i); 932 --i; /* Do the new #i over again */ 933 } 934 } 935 936 /* 937 * If asked to coalesce, do so and exit. 938 */ 939 if (do_coalesce) { 940 for (i = 0; i < nfss; i++) 941 clean_all_inodes(fsp[i]); 942 exit(0); 943 } 944 945 /* 946 * If asked to invalidate a segment, do that and exit. 947 */ 948 if (inval_segment >= 0) { 949 invalidate_segment(fsp[0], inval_segment); 950 exit(0); 951 } 952 953 /* 954 * Main cleaning loop. 955 */ 956 loopcount = 0; 957 #ifdef LFS_CLEANER_AS_LIB 958 if (semaddr) 959 sem_post(semaddr); 960 #endif 961 error = 0; 962 while (nfss > 0) { 963 int cleaned_one; 964 do { 965 #ifdef USE_CLIENT_SERVER 966 check_control_socket(); 967 #endif 968 cleaned_one = 0; 969 for (i = 0; i < nfss; i++) { 970 if ((error = needs_cleaning(fsp[i], &ci)) < 0) { 971 syslog(LOG_DEBUG, "%s: needs_cleaning returned %d", 972 getprogname(), error); 973 handle_error(fsp, i); 974 continue; 975 } 976 if (error == 0) /* No need to clean */ 977 continue; 978 979 if ((error = clean_fs(fsp[i], &ci)) < 0) { 980 syslog(LOG_DEBUG, "%s: clean_fs returned %d", 981 getprogname(), error); 982 handle_error(fsp, i); 983 continue; 984 } 985 ++cleaned_one; 986 } 987 ++loopcount; 988 if (stat_report && loopcount % stat_report == 0) 989 sig_report(0); 990 if (do_quit) 991 exit(0); 992 } while(cleaned_one); 993 tv.tv_sec = segwait_timeout; 994 tv.tv_usec = 0; 995 /* XXX: why couldn't others work if fsp socket is shutdown? */ 996 error = kops.ko_fcntl(fsp[0]->clfs_ifilefd,LFCNSEGWAITALL,&tv); 997 if (error) { 998 if (errno == ESHUTDOWN) { 999 for (i = 0; i < nfss; i++) { 1000 syslog(LOG_INFO, "%s: shutdown", 1001 getprogname()); 1002 handle_error(fsp, i); 1003 assert(nfss == 0); 1004 } 1005 } else { 1006 #ifdef LFS_CLEANER_AS_LIB 1007 error = ESHUTDOWN; 1008 break; 1009 #else 1010 err(1, "LFCNSEGWAITALL"); 1011 #endif 1012 } 1013 } 1014 } 1015 1016 /* NOTREACHED */ 1017 return error; 1018 } 1019