1329502Smav/* 2329502Smav * CDDL HEADER START 3329502Smav * 4329502Smav * This file and its contents are supplied under the terms of the 5329502Smav * Common Development and Distribution License ("CDDL"), version 1.0. 6329502Smav * You may only use this file in accordance with the terms of version 7329502Smav * 1.0 of the CDDL. 8329502Smav * 9329502Smav * A full copy of the text of the CDDL should have accompanied this 10329502Smav * source. A copy of the CDDL is also available via the Internet at 11329502Smav * http://www.illumos.org/license/CDDL. 12329502Smav * 13329502Smav * CDDL HEADER END 14329502Smav */ 15329502Smav 16329502Smav/* 17339104Smav * Copyright (c) 2015, 2017 by Delphix. All rights reserved. 18329502Smav */ 19329502Smav 20329502Smav#include <sys/dmu_tx.h> 21329502Smav#include <sys/dsl_pool.h> 22329502Smav#include <sys/spa.h> 23329502Smav#include <sys/vdev_impl.h> 24329502Smav#include <sys/vdev_indirect_mapping.h> 25329502Smav#include <sys/zfeature.h> 26329502Smav#include <sys/dmu_objset.h> 27329502Smav 28329502Smavstatic boolean_t 29329502Smavvdev_indirect_mapping_verify(vdev_indirect_mapping_t *vim) 30329502Smav{ 31329502Smav ASSERT(vim != NULL); 32329502Smav 33329502Smav ASSERT(vim->vim_object != 0); 34329502Smav ASSERT(vim->vim_objset != NULL); 35329502Smav ASSERT(vim->vim_phys != NULL); 36329502Smav ASSERT(vim->vim_dbuf != NULL); 37329502Smav 38329502Smav EQUIV(vim->vim_phys->vimp_num_entries > 0, 39329502Smav vim->vim_entries != NULL); 40329502Smav if (vim->vim_phys->vimp_num_entries > 0) { 41329502Smav vdev_indirect_mapping_entry_phys_t *last_entry = 42329502Smav &vim->vim_entries[vim->vim_phys->vimp_num_entries - 1]; 43329502Smav uint64_t offset = DVA_MAPPING_GET_SRC_OFFSET(last_entry); 44329502Smav uint64_t size = DVA_GET_ASIZE(&last_entry->vimep_dst); 45329502Smav 46329502Smav ASSERT3U(vim->vim_phys->vimp_max_offset, >=, offset + size); 47329502Smav } 48329502Smav if (vim->vim_havecounts) { 49329502Smav ASSERT(vim->vim_phys->vimp_counts_object != 0); 50329502Smav } 51329502Smav 52329502Smav return (B_TRUE); 53329502Smav} 54329502Smav 55329502Smavuint64_t 56329502Smavvdev_indirect_mapping_num_entries(vdev_indirect_mapping_t *vim) 57329502Smav{ 58329502Smav ASSERT(vdev_indirect_mapping_verify(vim)); 59329502Smav 60329502Smav return (vim->vim_phys->vimp_num_entries); 61329502Smav} 62329502Smav 63329502Smavuint64_t 64329502Smavvdev_indirect_mapping_max_offset(vdev_indirect_mapping_t *vim) 65329502Smav{ 66329502Smav ASSERT(vdev_indirect_mapping_verify(vim)); 67329502Smav 68329502Smav return (vim->vim_phys->vimp_max_offset); 69329502Smav} 70329502Smav 71329502Smavuint64_t 72329502Smavvdev_indirect_mapping_object(vdev_indirect_mapping_t *vim) 73329502Smav{ 74329502Smav ASSERT(vdev_indirect_mapping_verify(vim)); 75329502Smav 76329502Smav return (vim->vim_object); 77329502Smav} 78329502Smav 79329502Smavuint64_t 80329502Smavvdev_indirect_mapping_bytes_mapped(vdev_indirect_mapping_t *vim) 81329502Smav{ 82329502Smav ASSERT(vdev_indirect_mapping_verify(vim)); 83329502Smav 84329502Smav return (vim->vim_phys->vimp_bytes_mapped); 85329502Smav} 86329502Smav 87329502Smav/* 88329502Smav * The length (in bytes) of the mapping object array in memory and 89329502Smav * (logically) on disk. 90329502Smav * 91329502Smav * Note that unlike most of our accessor functions, 92329502Smav * we don't assert that the struct is consistent; therefore it can be 93329502Smav * called while there may be concurrent changes, if we don't care about 94329502Smav * the value being immediately stale (e.g. from spa_removal_get_stats()). 95329502Smav */ 96329502Smavuint64_t 97329502Smavvdev_indirect_mapping_size(vdev_indirect_mapping_t *vim) 98329502Smav{ 99329502Smav return (vim->vim_phys->vimp_num_entries * sizeof (*vim->vim_entries)); 100329502Smav} 101329502Smav 102329502Smav/* 103329502Smav * Compare an offset with an indirect mapping entry; there are three 104329502Smav * possible scenarios: 105329502Smav * 106329502Smav * 1. The offset is "less than" the mapping entry; meaning the 107329502Smav * offset is less than the source offset of the mapping entry. In 108329502Smav * this case, there is no overlap between the offset and the 109329502Smav * mapping entry and -1 will be returned. 110329502Smav * 111329502Smav * 2. The offset is "greater than" the mapping entry; meaning the 112329502Smav * offset is greater than the mapping entry's source offset plus 113329502Smav * the entry's size. In this case, there is no overlap between 114329502Smav * the offset and the mapping entry and 1 will be returned. 115329502Smav * 116329502Smav * NOTE: If the offset is actually equal to the entry's offset 117329502Smav * plus size, this is considered to be "greater" than the entry, 118329502Smav * and this case applies (i.e. 1 will be returned). Thus, the 119329502Smav * entry's "range" can be considered to be inclusive at its 120329502Smav * start, but exclusive at its end: e.g. [src, src + size). 121329502Smav * 122329502Smav * 3. The last case to consider is if the offset actually falls 123329502Smav * within the mapping entry's range. If this is the case, the 124329502Smav * offset is considered to be "equal to" the mapping entry and 125329502Smav * 0 will be returned. 126329502Smav * 127329502Smav * NOTE: If the offset is equal to the entry's source offset, 128329502Smav * this case applies and 0 will be returned. If the offset is 129329502Smav * equal to the entry's source plus its size, this case does 130329502Smav * *not* apply (see "NOTE" above for scenario 2), and 1 will be 131329502Smav * returned. 132329502Smav */ 133329502Smavstatic int 134329502Smavdva_mapping_overlap_compare(const void *v_key, const void *v_array_elem) 135329502Smav{ 136329732Smav const uint64_t *key = v_key; 137329732Smav const vdev_indirect_mapping_entry_phys_t *array_elem = 138329502Smav v_array_elem; 139329502Smav uint64_t src_offset = DVA_MAPPING_GET_SRC_OFFSET(array_elem); 140329502Smav 141329502Smav if (*key < src_offset) { 142329502Smav return (-1); 143329502Smav } else if (*key < src_offset + DVA_GET_ASIZE(&array_elem->vimep_dst)) { 144329502Smav return (0); 145329502Smav } else { 146329502Smav return (1); 147329502Smav } 148329502Smav} 149329502Smav 150329502Smav/* 151329502Smav * Returns the mapping entry for the given offset. 152329502Smav * 153329502Smav * It's possible that the given offset will not be in the mapping table 154329502Smav * (i.e. no mapping entries contain this offset), in which case, the 155329502Smav * return value value depends on the "next_if_missing" parameter. 156329502Smav * 157329502Smav * If the offset is not found in the table and "next_if_missing" is 158329502Smav * B_FALSE, then NULL will always be returned. The behavior is intended 159329502Smav * to allow consumers to get the entry corresponding to the offset 160329502Smav * parameter, iff the offset overlaps with an entry in the table. 161329502Smav * 162329502Smav * If the offset is not found in the table and "next_if_missing" is 163329502Smav * B_TRUE, then the entry nearest to the given offset will be returned, 164329502Smav * such that the entry's source offset is greater than the offset 165329502Smav * passed in (i.e. the "next" mapping entry in the table is returned, if 166329502Smav * the offset is missing from the table). If there are no entries whose 167329502Smav * source offset is greater than the passed in offset, NULL is returned. 168329502Smav */ 169329502Smavstatic vdev_indirect_mapping_entry_phys_t * 170329502Smavvdev_indirect_mapping_entry_for_offset_impl(vdev_indirect_mapping_t *vim, 171329502Smav uint64_t offset, boolean_t next_if_missing) 172329502Smav{ 173329502Smav ASSERT(vdev_indirect_mapping_verify(vim)); 174329502Smav ASSERT(vim->vim_phys->vimp_num_entries > 0); 175329502Smav 176329502Smav vdev_indirect_mapping_entry_phys_t *entry = NULL; 177329502Smav 178329502Smav uint64_t last = vim->vim_phys->vimp_num_entries - 1; 179329502Smav uint64_t base = 0; 180329502Smav 181329502Smav /* 182329502Smav * We don't define these inside of the while loop because we use 183329502Smav * their value in the case that offset isn't in the mapping. 184329502Smav */ 185329502Smav uint64_t mid; 186329502Smav int result; 187329502Smav 188329502Smav while (last >= base) { 189329502Smav mid = base + ((last - base) >> 1); 190329502Smav 191329502Smav result = dva_mapping_overlap_compare(&offset, 192329502Smav &vim->vim_entries[mid]); 193329502Smav 194329502Smav if (result == 0) { 195329502Smav entry = &vim->vim_entries[mid]; 196329502Smav break; 197329502Smav } else if (result < 0) { 198329502Smav last = mid - 1; 199329502Smav } else { 200329502Smav base = mid + 1; 201329502Smav } 202329502Smav } 203329502Smav 204329502Smav if (entry == NULL && next_if_missing) { 205329502Smav ASSERT3U(base, ==, last + 1); 206329502Smav ASSERT(mid == base || mid == last); 207329502Smav ASSERT3S(result, !=, 0); 208329502Smav 209329502Smav /* 210329502Smav * The offset we're looking for isn't actually contained 211329502Smav * in the mapping table, thus we need to return the 212329502Smav * closest mapping entry that is greater than the 213329502Smav * offset. We reuse the result of the last comparison, 214329502Smav * comparing the mapping entry at index "mid" and the 215329502Smav * offset. The offset is guaranteed to lie between 216329502Smav * indices one less than "mid", and one greater than 217329502Smav * "mid"; we just need to determine if offset is greater 218329502Smav * than, or less than the mapping entry contained at 219329502Smav * index "mid". 220329502Smav */ 221329502Smav 222329502Smav uint64_t index; 223329502Smav if (result < 0) 224329502Smav index = mid; 225329502Smav else 226329502Smav index = mid + 1; 227329502Smav 228329502Smav ASSERT3U(index, <=, vim->vim_phys->vimp_num_entries); 229329502Smav 230329502Smav if (index == vim->vim_phys->vimp_num_entries) { 231329502Smav /* 232329502Smav * If "index" is past the end of the entries 233329502Smav * array, then not only is the offset not in the 234329502Smav * mapping table, but it's actually greater than 235329502Smav * all entries in the table. In this case, we 236329502Smav * can't return a mapping entry greater than the 237329502Smav * offset (since none exist), so we return NULL. 238329502Smav */ 239329502Smav 240329502Smav ASSERT3S(dva_mapping_overlap_compare(&offset, 241329502Smav &vim->vim_entries[index - 1]), >, 0); 242329502Smav 243329502Smav return (NULL); 244329502Smav } else { 245329502Smav /* 246329502Smav * Just to be safe, we verify the offset falls 247329502Smav * in between the mapping entries at index and 248329502Smav * one less than index. Since we know the offset 249329502Smav * doesn't overlap an entry, and we're supposed 250329502Smav * to return the entry just greater than the 251329502Smav * offset, both of the following tests must be 252329502Smav * true. 253329502Smav */ 254329502Smav ASSERT3S(dva_mapping_overlap_compare(&offset, 255329502Smav &vim->vim_entries[index]), <, 0); 256329502Smav IMPLY(index >= 1, dva_mapping_overlap_compare(&offset, 257329502Smav &vim->vim_entries[index - 1]) > 0); 258329502Smav 259329502Smav return (&vim->vim_entries[index]); 260329502Smav } 261329502Smav } else { 262329502Smav return (entry); 263329502Smav } 264329502Smav} 265329502Smav 266329502Smavvdev_indirect_mapping_entry_phys_t * 267329502Smavvdev_indirect_mapping_entry_for_offset(vdev_indirect_mapping_t *vim, 268329502Smav uint64_t offset) 269329502Smav{ 270329502Smav return (vdev_indirect_mapping_entry_for_offset_impl(vim, offset, 271329502Smav B_FALSE)); 272329502Smav} 273329502Smav 274329502Smavvdev_indirect_mapping_entry_phys_t * 275329502Smavvdev_indirect_mapping_entry_for_offset_or_next(vdev_indirect_mapping_t *vim, 276329502Smav uint64_t offset) 277329502Smav{ 278329502Smav return (vdev_indirect_mapping_entry_for_offset_impl(vim, offset, 279329502Smav B_TRUE)); 280329502Smav} 281329502Smav 282329502Smav 283329502Smavvoid 284329502Smavvdev_indirect_mapping_close(vdev_indirect_mapping_t *vim) 285329502Smav{ 286329502Smav ASSERT(vdev_indirect_mapping_verify(vim)); 287329502Smav 288329502Smav if (vim->vim_phys->vimp_num_entries > 0) { 289329502Smav uint64_t map_size = vdev_indirect_mapping_size(vim); 290329502Smav kmem_free(vim->vim_entries, map_size); 291329502Smav vim->vim_entries = NULL; 292329502Smav } 293329502Smav 294329502Smav dmu_buf_rele(vim->vim_dbuf, vim); 295329502Smav 296329502Smav vim->vim_objset = NULL; 297329502Smav vim->vim_object = 0; 298329502Smav vim->vim_dbuf = NULL; 299329502Smav vim->vim_phys = NULL; 300329502Smav 301329502Smav kmem_free(vim, sizeof (*vim)); 302329502Smav} 303329502Smav 304329502Smavuint64_t 305329502Smavvdev_indirect_mapping_alloc(objset_t *os, dmu_tx_t *tx) 306329502Smav{ 307329502Smav uint64_t object; 308329502Smav ASSERT(dmu_tx_is_syncing(tx)); 309329502Smav uint64_t bonus_size = VDEV_INDIRECT_MAPPING_SIZE_V0; 310329502Smav 311329502Smav if (spa_feature_is_enabled(os->os_spa, SPA_FEATURE_OBSOLETE_COUNTS)) { 312329502Smav bonus_size = sizeof (vdev_indirect_mapping_phys_t); 313329502Smav } 314329502Smav 315329502Smav object = dmu_object_alloc(os, 316329502Smav DMU_OTN_UINT64_METADATA, SPA_OLD_MAXBLOCKSIZE, 317329502Smav DMU_OTN_UINT64_METADATA, bonus_size, 318329502Smav tx); 319329502Smav 320329502Smav if (spa_feature_is_enabled(os->os_spa, SPA_FEATURE_OBSOLETE_COUNTS)) { 321329502Smav dmu_buf_t *dbuf; 322329502Smav vdev_indirect_mapping_phys_t *vimp; 323329502Smav 324329502Smav VERIFY0(dmu_bonus_hold(os, object, FTAG, &dbuf)); 325329502Smav dmu_buf_will_dirty(dbuf, tx); 326329502Smav vimp = dbuf->db_data; 327329502Smav vimp->vimp_counts_object = dmu_object_alloc(os, 328329502Smav DMU_OTN_UINT32_METADATA, SPA_OLD_MAXBLOCKSIZE, 329329502Smav DMU_OT_NONE, 0, tx); 330329502Smav spa_feature_incr(os->os_spa, SPA_FEATURE_OBSOLETE_COUNTS, tx); 331329502Smav dmu_buf_rele(dbuf, FTAG); 332329502Smav } 333329502Smav 334329502Smav return (object); 335329502Smav} 336329502Smav 337329502Smav 338329502Smavvdev_indirect_mapping_t * 339329502Smavvdev_indirect_mapping_open(objset_t *os, uint64_t mapping_object) 340329502Smav{ 341329502Smav vdev_indirect_mapping_t *vim = kmem_zalloc(sizeof (*vim), KM_SLEEP); 342329502Smav dmu_object_info_t doi; 343329502Smav VERIFY0(dmu_object_info(os, mapping_object, &doi)); 344329502Smav 345329502Smav vim->vim_objset = os; 346329502Smav vim->vim_object = mapping_object; 347329502Smav 348329502Smav VERIFY0(dmu_bonus_hold(os, vim->vim_object, vim, 349329502Smav &vim->vim_dbuf)); 350329502Smav vim->vim_phys = vim->vim_dbuf->db_data; 351329502Smav 352329502Smav vim->vim_havecounts = 353329502Smav (doi.doi_bonus_size > VDEV_INDIRECT_MAPPING_SIZE_V0); 354329502Smav 355329502Smav if (vim->vim_phys->vimp_num_entries > 0) { 356329502Smav uint64_t map_size = vdev_indirect_mapping_size(vim); 357329502Smav vim->vim_entries = kmem_alloc(map_size, KM_SLEEP); 358329502Smav VERIFY0(dmu_read(os, vim->vim_object, 0, map_size, 359329502Smav vim->vim_entries, DMU_READ_PREFETCH)); 360329502Smav } 361329502Smav 362329502Smav ASSERT(vdev_indirect_mapping_verify(vim)); 363329502Smav 364329502Smav return (vim); 365329502Smav} 366329502Smav 367329502Smavvoid 368329502Smavvdev_indirect_mapping_free(objset_t *os, uint64_t object, dmu_tx_t *tx) 369329502Smav{ 370329502Smav vdev_indirect_mapping_t *vim = vdev_indirect_mapping_open(os, object); 371329502Smav if (vim->vim_havecounts) { 372329502Smav VERIFY0(dmu_object_free(os, vim->vim_phys->vimp_counts_object, 373329502Smav tx)); 374329502Smav spa_feature_decr(os->os_spa, SPA_FEATURE_OBSOLETE_COUNTS, tx); 375329502Smav } 376329502Smav vdev_indirect_mapping_close(vim); 377329502Smav 378329502Smav VERIFY0(dmu_object_free(os, object, tx)); 379329502Smav} 380329502Smav 381329502Smav/* 382329502Smav * Append the list of vdev_indirect_mapping_entry_t's to the on-disk 383329502Smav * mapping object. Also remove the entries from the list and free them. 384329502Smav * This also implicitly extends the max_offset of the mapping (to the end 385329502Smav * of the last entry). 386329502Smav */ 387329502Smavvoid 388329502Smavvdev_indirect_mapping_add_entries(vdev_indirect_mapping_t *vim, 389329502Smav list_t *list, dmu_tx_t *tx) 390329502Smav{ 391329502Smav vdev_indirect_mapping_entry_phys_t *mapbuf; 392329502Smav uint64_t old_size; 393329502Smav uint32_t *countbuf = NULL; 394329502Smav vdev_indirect_mapping_entry_phys_t *old_entries; 395329502Smav uint64_t old_count; 396329502Smav uint64_t entries_written = 0; 397329502Smav 398329502Smav ASSERT(vdev_indirect_mapping_verify(vim)); 399329502Smav ASSERT(dmu_tx_is_syncing(tx)); 400329502Smav ASSERT(dsl_pool_sync_context(dmu_tx_pool(tx))); 401329502Smav ASSERT(!list_is_empty(list)); 402329502Smav 403329502Smav old_size = vdev_indirect_mapping_size(vim); 404329502Smav old_entries = vim->vim_entries; 405329502Smav old_count = vim->vim_phys->vimp_num_entries; 406329502Smav 407329502Smav dmu_buf_will_dirty(vim->vim_dbuf, tx); 408329502Smav 409329502Smav mapbuf = zio_buf_alloc(SPA_OLD_MAXBLOCKSIZE); 410329502Smav if (vim->vim_havecounts) { 411329502Smav countbuf = zio_buf_alloc(SPA_OLD_MAXBLOCKSIZE); 412329502Smav ASSERT(spa_feature_is_active(vim->vim_objset->os_spa, 413329502Smav SPA_FEATURE_OBSOLETE_COUNTS)); 414329502Smav } 415329502Smav while (!list_is_empty(list)) { 416329502Smav uint64_t i; 417329502Smav /* 418329502Smav * Write entries from the list to the 419329502Smav * vdev_im_object in batches of size SPA_OLD_MAXBLOCKSIZE. 420329502Smav */ 421329502Smav for (i = 0; i < SPA_OLD_MAXBLOCKSIZE / sizeof (*mapbuf); i++) { 422329502Smav vdev_indirect_mapping_entry_t *entry = 423329502Smav list_remove_head(list); 424329502Smav if (entry == NULL) 425329502Smav break; 426329502Smav 427329502Smav uint64_t size = 428329502Smav DVA_GET_ASIZE(&entry->vime_mapping.vimep_dst); 429329502Smav uint64_t src_offset = 430329502Smav DVA_MAPPING_GET_SRC_OFFSET(&entry->vime_mapping); 431329502Smav 432329502Smav /* 433329502Smav * We shouldn't be adding an entry which is fully 434329502Smav * obsolete. 435329502Smav */ 436329502Smav ASSERT3U(entry->vime_obsolete_count, <, size); 437329502Smav IMPLY(entry->vime_obsolete_count != 0, 438329502Smav vim->vim_havecounts); 439329502Smav 440329502Smav mapbuf[i] = entry->vime_mapping; 441329502Smav if (vim->vim_havecounts) 442329502Smav countbuf[i] = entry->vime_obsolete_count; 443329502Smav 444329502Smav vim->vim_phys->vimp_bytes_mapped += size; 445329502Smav ASSERT3U(src_offset, >=, 446329502Smav vim->vim_phys->vimp_max_offset); 447329502Smav vim->vim_phys->vimp_max_offset = src_offset + size; 448329502Smav 449329502Smav entries_written++; 450329502Smav 451329502Smav kmem_free(entry, sizeof (*entry)); 452329502Smav } 453329502Smav dmu_write(vim->vim_objset, vim->vim_object, 454329502Smav vim->vim_phys->vimp_num_entries * sizeof (*mapbuf), 455329502Smav i * sizeof (*mapbuf), 456329502Smav mapbuf, tx); 457329502Smav if (vim->vim_havecounts) { 458329502Smav dmu_write(vim->vim_objset, 459329502Smav vim->vim_phys->vimp_counts_object, 460329502Smav vim->vim_phys->vimp_num_entries * 461329502Smav sizeof (*countbuf), 462329502Smav i * sizeof (*countbuf), countbuf, tx); 463329502Smav } 464329502Smav vim->vim_phys->vimp_num_entries += i; 465329502Smav } 466329502Smav zio_buf_free(mapbuf, SPA_OLD_MAXBLOCKSIZE); 467329502Smav if (vim->vim_havecounts) 468329502Smav zio_buf_free(countbuf, SPA_OLD_MAXBLOCKSIZE); 469329502Smav 470329502Smav /* 471329502Smav * Update the entry array to reflect the new entries. First, copy 472329502Smav * over any old entries then read back the new entries we just wrote. 473329502Smav */ 474329502Smav uint64_t new_size = vdev_indirect_mapping_size(vim); 475329502Smav ASSERT3U(new_size, >, old_size); 476329502Smav ASSERT3U(new_size - old_size, ==, 477329502Smav entries_written * sizeof (vdev_indirect_mapping_entry_phys_t)); 478329502Smav vim->vim_entries = kmem_alloc(new_size, KM_SLEEP); 479329502Smav if (old_size > 0) { 480329502Smav bcopy(old_entries, vim->vim_entries, old_size); 481329502Smav kmem_free(old_entries, old_size); 482329502Smav } 483329502Smav VERIFY0(dmu_read(vim->vim_objset, vim->vim_object, old_size, 484329502Smav new_size - old_size, &vim->vim_entries[old_count], 485329502Smav DMU_READ_PREFETCH)); 486329502Smav 487329502Smav zfs_dbgmsg("txg %llu: wrote %llu entries to " 488329502Smav "indirect mapping obj %llu; max offset=0x%llx", 489329502Smav (u_longlong_t)dmu_tx_get_txg(tx), 490329502Smav (u_longlong_t)entries_written, 491329502Smav (u_longlong_t)vim->vim_object, 492329502Smav (u_longlong_t)vim->vim_phys->vimp_max_offset); 493329502Smav} 494329502Smav 495329502Smav/* 496329502Smav * Increment the relevant counts for the specified offset and length. 497329502Smav * The counts array must be obtained from 498329502Smav * vdev_indirect_mapping_load_obsolete_counts(). 499329502Smav */ 500329502Smavvoid 501329502Smavvdev_indirect_mapping_increment_obsolete_count(vdev_indirect_mapping_t *vim, 502329502Smav uint64_t offset, uint64_t length, uint32_t *counts) 503329502Smav{ 504329502Smav vdev_indirect_mapping_entry_phys_t *mapping; 505329502Smav uint64_t index; 506329502Smav 507329502Smav mapping = vdev_indirect_mapping_entry_for_offset(vim, offset); 508329502Smav 509329502Smav ASSERT(length > 0); 510329502Smav ASSERT3P(mapping, !=, NULL); 511329502Smav 512329502Smav index = mapping - vim->vim_entries; 513329502Smav 514329502Smav while (length > 0) { 515329502Smav ASSERT3U(index, <, vdev_indirect_mapping_num_entries(vim)); 516329502Smav 517329502Smav uint64_t size = DVA_GET_ASIZE(&mapping->vimep_dst); 518329502Smav uint64_t inner_offset = offset - 519329502Smav DVA_MAPPING_GET_SRC_OFFSET(mapping); 520329502Smav VERIFY3U(inner_offset, <, size); 521329502Smav uint64_t inner_size = MIN(length, size - inner_offset); 522329502Smav 523329502Smav VERIFY3U(counts[index] + inner_size, <=, size); 524329502Smav counts[index] += inner_size; 525329502Smav 526329502Smav offset += inner_size; 527329502Smav length -= inner_size; 528329502Smav mapping++; 529329502Smav index++; 530329502Smav } 531329502Smav} 532329502Smav 533329502Smavtypedef struct load_obsolete_space_map_arg { 534329502Smav vdev_indirect_mapping_t *losma_vim; 535329502Smav uint32_t *losma_counts; 536329502Smav} load_obsolete_space_map_arg_t; 537329502Smav 538329502Smavstatic int 539339104Smavload_obsolete_sm_callback(space_map_entry_t *sme, void *arg) 540329502Smav{ 541329502Smav load_obsolete_space_map_arg_t *losma = arg; 542339104Smav ASSERT3S(sme->sme_type, ==, SM_ALLOC); 543329502Smav 544329502Smav vdev_indirect_mapping_increment_obsolete_count(losma->losma_vim, 545339104Smav sme->sme_offset, sme->sme_run, losma->losma_counts); 546329502Smav 547329502Smav return (0); 548329502Smav} 549329502Smav 550329502Smav/* 551329502Smav * Modify the counts (increment them) based on the spacemap. 552329502Smav */ 553329502Smavvoid 554329502Smavvdev_indirect_mapping_load_obsolete_spacemap(vdev_indirect_mapping_t *vim, 555329502Smav uint32_t *counts, space_map_t *obsolete_space_sm) 556329502Smav{ 557329502Smav load_obsolete_space_map_arg_t losma; 558329502Smav losma.losma_counts = counts; 559329502Smav losma.losma_vim = vim; 560329502Smav VERIFY0(space_map_iterate(obsolete_space_sm, 561329502Smav load_obsolete_sm_callback, &losma)); 562329502Smav} 563329502Smav 564329502Smav/* 565329502Smav * Read the obsolete counts from disk, returning them in an array. 566329502Smav */ 567329502Smavuint32_t * 568329502Smavvdev_indirect_mapping_load_obsolete_counts(vdev_indirect_mapping_t *vim) 569329502Smav{ 570329502Smav ASSERT(vdev_indirect_mapping_verify(vim)); 571329502Smav 572329502Smav uint64_t counts_size = 573329502Smav vim->vim_phys->vimp_num_entries * sizeof (uint32_t); 574329502Smav uint32_t *counts = kmem_alloc(counts_size, KM_SLEEP); 575329502Smav if (vim->vim_havecounts) { 576329502Smav VERIFY0(dmu_read(vim->vim_objset, 577329502Smav vim->vim_phys->vimp_counts_object, 578329502Smav 0, counts_size, 579329502Smav counts, DMU_READ_PREFETCH)); 580329502Smav } else { 581329502Smav bzero(counts, counts_size); 582329502Smav } 583329502Smav return (counts); 584329502Smav} 585329502Smav 586329502Smavextern void 587329502Smavvdev_indirect_mapping_free_obsolete_counts(vdev_indirect_mapping_t *vim, 588329502Smav uint32_t *counts) 589329502Smav{ 590329502Smav ASSERT(vdev_indirect_mapping_verify(vim)); 591329502Smav 592329502Smav kmem_free(counts, vim->vim_phys->vimp_num_entries * sizeof (uint32_t)); 593329502Smav} 594