1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (c) 2022-2024 Oracle. All Rights Reserved. 4 * Author: Darrick J. Wong <djwong@kernel.org> 5 */ 6 #include "xfs.h" 7 #include "xfs_fs.h" 8 #include "xfs_shared.h" 9 #include "xfs_format.h" 10 #include "xfs_log_format.h" 11 #include "xfs_trans.h" 12 #include "xfs_trans_resv.h" 13 #include "xfs_mount.h" 14 #include "xfs_defer.h" 15 #include "xfs_btree.h" 16 #include "xfs_buf_mem.h" 17 #include "xfs_btree_mem.h" 18 #include "xfs_error.h" 19 #include "scrub/scrub.h" 20 #include "scrub/rcbag_btree.h" 21 #include "scrub/rcbag.h" 22 #include "scrub/trace.h" 23 24 struct rcbag { 25 struct xfs_mount *mp; 26 struct xfbtree xfbtree; 27 uint64_t nr_items; 28 }; 29 30 int 31 rcbag_init( 32 struct xfs_mount *mp, 33 struct xfs_buftarg *btp, 34 struct rcbag **bagp) 35 { 36 struct rcbag *bag; 37 int error; 38 39 bag = kzalloc(sizeof(struct rcbag), XCHK_GFP_FLAGS); 40 if (!bag) 41 return -ENOMEM; 42 43 bag->nr_items = 0; 44 bag->mp = mp; 45 46 error = rcbagbt_mem_init(mp, &bag->xfbtree, btp); 47 if (error) 48 goto out_bag; 49 50 *bagp = bag; 51 return 0; 52 53 out_bag: 54 kfree(bag); 55 return error; 56 } 57 58 void 59 rcbag_free( 60 struct rcbag **bagp) 61 { 62 struct rcbag *bag = *bagp; 63 64 xfbtree_destroy(&bag->xfbtree); 65 kfree(bag); 66 *bagp = NULL; 67 } 68 69 /* Track an rmap in the refcount bag. */ 70 int 71 rcbag_add( 72 struct rcbag *bag, 73 struct xfs_trans *tp, 74 const struct xfs_rmap_irec *rmap) 75 { 76 struct rcbag_rec bagrec; 77 struct xfs_mount *mp = bag->mp; 78 struct xfs_btree_cur *cur; 79 int has; 80 int error; 81 82 cur = rcbagbt_mem_cursor(mp, tp, &bag->xfbtree); 83 error = rcbagbt_lookup_eq(cur, rmap, &has); 84 if (error) 85 goto out_cur; 86 87 if (has) { 88 error = rcbagbt_get_rec(cur, &bagrec, &has); 89 if (error) 90 goto out_cur; 91 if (!has) { 92 error = -EFSCORRUPTED; 93 goto out_cur; 94 } 95 96 bagrec.rbg_refcount++; 97 error = rcbagbt_update(cur, &bagrec); 98 if (error) 99 goto out_cur; 100 } else { 101 bagrec.rbg_startblock = rmap->rm_startblock; 102 bagrec.rbg_blockcount = rmap->rm_blockcount; 103 bagrec.rbg_refcount = 1; 104 105 error = rcbagbt_insert(cur, &bagrec, &has); 106 if (error) 107 goto out_cur; 108 if (!has) { 109 error = -EFSCORRUPTED; 110 goto out_cur; 111 } 112 } 113 114 xfs_btree_del_cursor(cur, 0); 115 116 error = xfbtree_trans_commit(&bag->xfbtree, tp); 117 if (error) 118 return error; 119 120 bag->nr_items++; 121 return 0; 122 123 out_cur: 124 xfs_btree_del_cursor(cur, error); 125 xfbtree_trans_cancel(&bag->xfbtree, tp); 126 return error; 127 } 128 129 /* Return the number of records in the bag. */ 130 uint64_t 131 rcbag_count( 132 const struct rcbag *rcbag) 133 { 134 return rcbag->nr_items; 135 } 136 137 static inline uint32_t rcbag_rec_next_bno(const struct rcbag_rec *r) 138 { 139 return r->rbg_startblock + r->rbg_blockcount; 140 } 141 142 /* 143 * Find the next block where the refcount changes, given the next rmap we 144 * looked at and the ones we're already tracking. 145 */ 146 int 147 rcbag_next_edge( 148 struct rcbag *bag, 149 struct xfs_trans *tp, 150 const struct xfs_rmap_irec *next_rmap, 151 bool next_valid, 152 uint32_t *next_bnop) 153 { 154 struct rcbag_rec bagrec; 155 struct xfs_mount *mp = bag->mp; 156 struct xfs_btree_cur *cur; 157 uint32_t next_bno = NULLAGBLOCK; 158 int has; 159 int error; 160 161 if (next_valid) 162 next_bno = next_rmap->rm_startblock; 163 164 cur = rcbagbt_mem_cursor(mp, tp, &bag->xfbtree); 165 error = xfs_btree_goto_left_edge(cur); 166 if (error) 167 goto out_cur; 168 169 while (true) { 170 error = xfs_btree_increment(cur, 0, &has); 171 if (error) 172 goto out_cur; 173 if (!has) 174 break; 175 176 error = rcbagbt_get_rec(cur, &bagrec, &has); 177 if (error) 178 goto out_cur; 179 if (!has) { 180 error = -EFSCORRUPTED; 181 goto out_cur; 182 } 183 184 next_bno = min(next_bno, rcbag_rec_next_bno(&bagrec)); 185 } 186 187 /* 188 * We should have found /something/ because either next_rrm is the next 189 * interesting rmap to look at after emitting this refcount extent, or 190 * there are other rmaps in rmap_bag contributing to the current 191 * sharing count. But if something is seriously wrong, bail out. 192 */ 193 if (next_bno == NULLAGBLOCK) { 194 error = -EFSCORRUPTED; 195 goto out_cur; 196 } 197 198 xfs_btree_del_cursor(cur, 0); 199 200 *next_bnop = next_bno; 201 return 0; 202 203 out_cur: 204 xfs_btree_del_cursor(cur, error); 205 return error; 206 } 207 208 /* Pop all refcount bag records that end at next_bno */ 209 int 210 rcbag_remove_ending_at( 211 struct rcbag *bag, 212 struct xfs_trans *tp, 213 uint32_t next_bno) 214 { 215 struct rcbag_rec bagrec; 216 struct xfs_mount *mp = bag->mp; 217 struct xfs_btree_cur *cur; 218 int has; 219 int error; 220 221 /* go to the right edge of the tree */ 222 cur = rcbagbt_mem_cursor(mp, tp, &bag->xfbtree); 223 memset(&cur->bc_rec, 0xFF, sizeof(cur->bc_rec)); 224 error = xfs_btree_lookup(cur, XFS_LOOKUP_GE, &has); 225 if (error) 226 goto out_cur; 227 228 while (true) { 229 error = xfs_btree_decrement(cur, 0, &has); 230 if (error) 231 goto out_cur; 232 if (!has) 233 break; 234 235 error = rcbagbt_get_rec(cur, &bagrec, &has); 236 if (error) 237 goto out_cur; 238 if (!has) { 239 error = -EFSCORRUPTED; 240 goto out_cur; 241 } 242 243 if (rcbag_rec_next_bno(&bagrec) != next_bno) 244 continue; 245 246 error = xfs_btree_delete(cur, &has); 247 if (error) 248 goto out_cur; 249 if (!has) { 250 error = -EFSCORRUPTED; 251 goto out_cur; 252 } 253 254 bag->nr_items -= bagrec.rbg_refcount; 255 } 256 257 xfs_btree_del_cursor(cur, 0); 258 return xfbtree_trans_commit(&bag->xfbtree, tp); 259 out_cur: 260 xfs_btree_del_cursor(cur, error); 261 xfbtree_trans_cancel(&bag->xfbtree, tp); 262 return error; 263 } 264 265 /* Dump the rcbag. */ 266 void 267 rcbag_dump( 268 struct rcbag *bag, 269 struct xfs_trans *tp) 270 { 271 struct rcbag_rec bagrec; 272 struct xfs_mount *mp = bag->mp; 273 struct xfs_btree_cur *cur; 274 unsigned long long nr = 0; 275 int has; 276 int error; 277 278 cur = rcbagbt_mem_cursor(mp, tp, &bag->xfbtree); 279 error = xfs_btree_goto_left_edge(cur); 280 if (error) 281 goto out_cur; 282 283 while (true) { 284 error = xfs_btree_increment(cur, 0, &has); 285 if (error) 286 goto out_cur; 287 if (!has) 288 break; 289 290 error = rcbagbt_get_rec(cur, &bagrec, &has); 291 if (error) 292 goto out_cur; 293 if (!has) { 294 error = -EFSCORRUPTED; 295 goto out_cur; 296 } 297 298 xfs_err(bag->mp, "[%llu]: bno 0x%x fsbcount 0x%x refcount 0x%llx\n", 299 nr++, 300 (unsigned int)bagrec.rbg_startblock, 301 (unsigned int)bagrec.rbg_blockcount, 302 (unsigned long long)bagrec.rbg_refcount); 303 } 304 305 out_cur: 306 xfs_btree_del_cursor(cur, error); 307 } 308