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