Home | History | Annotate | Line # | Download | only in lfs_cleanerd
      1 /*      $NetBSD: coalesce.c,v 1.34 2026/04/23 16:26:05 perseant Exp $  */
      2 
      3 /*-
      4  * Copyright (c) 2002, 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 #include <sys/param.h>
     33 #include <sys/mount.h>
     34 #include <sys/time.h>
     35 #include <sys/resource.h>
     36 #include <sys/types.h>
     37 #include <sys/wait.h>
     38 #include <sys/mman.h>
     39 
     40 #include <ufs/lfs/lfs.h>
     41 
     42 #include <fcntl.h>
     43 #include <signal.h>
     44 #include <stdio.h>
     45 #include <stdlib.h>
     46 #include <string.h>
     47 #include <time.h>
     48 #include <unistd.h>
     49 #include <util.h>
     50 #include <errno.h>
     51 #include <err.h>
     52 #include <assert.h>
     53 
     54 #include <syslog.h>
     55 
     56 #include "bufcache.h"
     57 #include "vnode.h"
     58 #include "cleaner.h"
     59 #include "kernelops.h"
     60 
     61 extern int debug, do_mmap;
     62 
     63 enum coalesce_returncodes {
     64 	COALESCE_OK = 0,
     65 	COALESCE_NOINODE,
     66 	COALESCE_TOOSMALL,
     67 	COALESCE_BADSIZE,
     68 	COALESCE_BADBLOCKSIZE,
     69 	COALESCE_NOMEM,
     70 	COALESCE_BADBMAPV,
     71 	COALESCE_BADMARKV,
     72 	COALESCE_NOTWORTHIT,
     73 	COALESCE_NOTHINGLEFT,
     74 	COALESCE_EIO,
     75 
     76 	COALESCE_MAXERROR
     77 };
     78 
     79 const char *coalesce_return[] = {
     80 	"Successfully coalesced",
     81 	"File not in use or inode not found",
     82 	"Not large enough to coalesce",
     83 	"Negative size",
     84 	"Not enough blocks to account for size",
     85 	"Malloc failed",
     86 	"LFCNBMAPV failed",
     87 	"Not broken enough to fix",
     88 	"Too many blocks not found",
     89 	"Too many blocks found in active segments",
     90 	"I/O error",
     91 
     92 	"No such error"
     93 };
     94 
     95 #define INODES_AT_A_TIME 1024
     96 
     97 static void flush(struct clfs *fs, struct lfs_inode_array *liap, int *totals)
     98 {
     99 	int j;
    100 
    101 	if (kops.ko_fcntl(fs->clfs_ifilefd, LFCNREWRITEFILE, liap) < 0) {
    102 		syslog(LOG_WARNING, "%s: coalesce: LFCNREWRITEFILE: %m",
    103 		       lfs_sb_getfsmnt(fs));
    104 		++totals[COALESCE_BADBMAPV];
    105 	}
    106 	/* XXX stats */
    107 
    108 	for (j = 0; j < liap->len; j++) {
    109 		/* XXX check individual errors */
    110 		++totals[COALESCE_OK];
    111 	}
    112 
    113 	liap->len = 0;
    114 }
    115 
    116 /*
    117  * Try coalescing every inode in the filesystem.
    118  * Return the number of inodes actually altered.
    119  */
    120 int clean_all_inodes(struct clfs *fs)
    121 {
    122 	ino_t i, base, maxino;
    123 	int totals[COALESCE_MAXERROR];
    124 	struct stat st;
    125 	struct lfs_filestat_req lfr;
    126 	struct lfs_filestats fss[INODES_AT_A_TIME];
    127 	struct lfs_inode_array lia;
    128 	ino_t ia[INODES_AT_A_TIME];
    129 	int blocksperseg;
    130 
    131 	memset(totals, 0, sizeof(totals));
    132 	lia.inodes = &ia[0];
    133 	lfr.fss = &fss[0];
    134 	blocksperseg = lfs_sb_getssize(fs) / lfs_sb_getbsize(fs);
    135 
    136 	fstat(fs->clfs_ifilefd, &st);
    137 	maxino = lfs_sb_getifpb(fs) * (st.st_size >> lfs_sb_getbshift(fs)) -
    138 		lfs_sb_getsegtabsz(fs) - lfs_sb_getcleansz(fs);
    139 
    140 	for (base = 0; base < maxino; base += INODES_AT_A_TIME) {
    141 		lia.len = 0;
    142 		for (i = 0; base + i < maxino; i++)
    143 			ia[lia.len++] = base + i;
    144 
    145 		/* See which files need treatment */
    146 		if (kops.ko_fcntl(fs->clfs_ifilefd, LFCNFILESTATS, &lfr) < 0) {
    147 			syslog(LOG_WARNING, "%s: coalesce: LFCNFILESTATS: %m",
    148 			       lfs_sb_getfsmnt(fs));
    149 			++totals[COALESCE_BADBMAPV];
    150 		}
    151 
    152 		for (i = 0; i < INODES_AT_A_TIME; i++) {
    153 			if (fss[i].nblk < 2) {
    154 				++totals[COALESCE_TOOSMALL];
    155 				continue;
    156 			}
    157 			if (fss[i].dc_count < 2 * howmany(fss[i].nblk,
    158 							  blocksperseg)) {
    159 				++totals[COALESCE_NOTWORTHIT];
    160 				continue;
    161 			}
    162 			ia[lia.len++] = fss[i].ino;
    163 			if (lia.len == INODES_AT_A_TIME)
    164 				flush(fs, &lia, totals);
    165 		}
    166 	}
    167 	if (lia.len > 0)
    168 		flush(fs, &lia, totals);
    169 
    170 	for (i = 0; i < COALESCE_MAXERROR; i++)
    171 		if (totals[i])
    172 			syslog(LOG_DEBUG, "%s: %d", coalesce_return[i],
    173 			       totals[i]);
    174 
    175 	return totals[COALESCE_OK];
    176 }
    177 
    178 /*
    179  * Fork a child process to coalesce this fs.
    180  */
    181 int
    182 fork_coalesce(struct clfs *fs)
    183 {
    184 	static pid_t childpid;
    185 	int num;
    186 
    187 	/*
    188 	 * If already running a coalescing child, don't start a new one.
    189 	 */
    190 	if (childpid) {
    191 		if (waitpid(childpid, NULL, WNOHANG) == childpid)
    192 			childpid = 0;
    193 	}
    194 	if (childpid && kill(childpid, 0) >= 0) {
    195 		/* already running a coalesce process */
    196 		if (debug)
    197 			syslog(LOG_DEBUG, "coalescing already in progress");
    198 		return 0;
    199 	}
    200 
    201 	/*
    202 	 * Fork a child and let the child coalease
    203 	 */
    204 	childpid = fork();
    205 	if (childpid < 0) {
    206 		syslog(LOG_ERR, "%s: fork to coaleasce: %m", lfs_sb_getfsmnt(fs));
    207 		return 0;
    208 	} else if (childpid == 0) {
    209 		syslog(LOG_NOTICE, "%s: new coalescing process, pid %d",
    210 		       lfs_sb_getfsmnt(fs), getpid());
    211 		num = clean_all_inodes(fs);
    212 		syslog(LOG_NOTICE, "%s: coalesced %d discontiguous inodes",
    213 		       lfs_sb_getfsmnt(fs), num);
    214 		exit(0);
    215 	}
    216 
    217 	return 0;
    218 }
    219