xref: /freebsd/sys/contrib/openzfs/module/zfs/vdev_indirect_mapping.c (revision 22649d4dba730d46244fd2dff4fd174903c8379f)
1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3  * This file and its contents are supplied under the terms of the
4  * Common Development and Distribution License ("CDDL"), version 1.0.
5  * You may only use this file in accordance with the terms of version
6  * 1.0 of the CDDL.
7  *
8  * A full copy of the text of the CDDL should have accompanied this
9  * source.  A copy of the CDDL is also available via the Internet at
10  * https://opensource.org/license/CDDL-1.0.
11  */
12 
13 /*
14  * Copyright (c) 2015, 2017 by Delphix. All rights reserved.
15  */
16 
17 #include <sys/dmu_tx.h>
18 #include <sys/dsl_pool.h>
19 #include <sys/spa.h>
20 #include <sys/vdev_impl.h>
21 #include <sys/vdev_indirect_mapping.h>
22 #include <sys/zfeature.h>
23 #include <sys/dmu_objset.h>
24 
25 #ifdef ZFS_DEBUG
26 static boolean_t
vdev_indirect_mapping_verify(vdev_indirect_mapping_t * vim)27 vdev_indirect_mapping_verify(vdev_indirect_mapping_t *vim)
28 {
29 	ASSERT(vim != NULL);
30 
31 	ASSERT(vim->vim_object != 0);
32 	ASSERT(vim->vim_objset != NULL);
33 	ASSERT(vim->vim_phys != NULL);
34 	ASSERT(vim->vim_dbuf != NULL);
35 
36 	EQUIV(vim->vim_phys->vimp_num_entries > 0,
37 	    vim->vim_entries != NULL);
38 	if (vim->vim_phys->vimp_num_entries > 0) {
39 		vdev_indirect_mapping_entry_phys_t *last_entry __maybe_unused =
40 		    &vim->vim_entries[vim->vim_phys->vimp_num_entries - 1];
41 		uint64_t offset __maybe_unused =
42 		    DVA_MAPPING_GET_SRC_OFFSET(last_entry);
43 		uint64_t size __maybe_unused =
44 		    DVA_GET_ASIZE(&last_entry->vimep_dst);
45 
46 		ASSERT3U(vim->vim_phys->vimp_max_offset, >=, offset + size);
47 	}
48 	if (vim->vim_havecounts) {
49 		ASSERT(vim->vim_phys->vimp_counts_object != 0);
50 	}
51 
52 	return (B_TRUE);
53 }
54 #else
55 #define	vdev_indirect_mapping_verify(vim) ((void) sizeof (vim), B_TRUE)
56 #endif
57 
58 uint64_t
vdev_indirect_mapping_num_entries(vdev_indirect_mapping_t * vim)59 vdev_indirect_mapping_num_entries(vdev_indirect_mapping_t *vim)
60 {
61 	ASSERT(vdev_indirect_mapping_verify(vim));
62 
63 	return (vim->vim_phys->vimp_num_entries);
64 }
65 
66 uint64_t
vdev_indirect_mapping_max_offset(vdev_indirect_mapping_t * vim)67 vdev_indirect_mapping_max_offset(vdev_indirect_mapping_t *vim)
68 {
69 	ASSERT(vdev_indirect_mapping_verify(vim));
70 
71 	return (vim->vim_phys->vimp_max_offset);
72 }
73 
74 uint64_t
vdev_indirect_mapping_object(vdev_indirect_mapping_t * vim)75 vdev_indirect_mapping_object(vdev_indirect_mapping_t *vim)
76 {
77 	ASSERT(vdev_indirect_mapping_verify(vim));
78 
79 	return (vim->vim_object);
80 }
81 
82 uint64_t
vdev_indirect_mapping_bytes_mapped(vdev_indirect_mapping_t * vim)83 vdev_indirect_mapping_bytes_mapped(vdev_indirect_mapping_t *vim)
84 {
85 	ASSERT(vdev_indirect_mapping_verify(vim));
86 
87 	return (vim->vim_phys->vimp_bytes_mapped);
88 }
89 
90 /*
91  * The length (in bytes) of the mapping object array in memory and
92  * (logically) on disk.
93  *
94  * Note that unlike most of our accessor functions,
95  * we don't assert that the struct is consistent; therefore it can be
96  * called while there may be concurrent changes, if we don't care about
97  * the value being immediately stale (e.g. from spa_removal_get_stats()).
98  */
99 uint64_t
vdev_indirect_mapping_size(vdev_indirect_mapping_t * vim)100 vdev_indirect_mapping_size(vdev_indirect_mapping_t *vim)
101 {
102 	return (vim->vim_phys->vimp_num_entries * sizeof (*vim->vim_entries));
103 }
104 
105 /*
106  * Compare an offset with an indirect mapping entry; there are three
107  * possible scenarios:
108  *
109  *     1. The offset is "less than" the mapping entry; meaning the
110  *        offset is less than the source offset of the mapping entry. In
111  *        this case, there is no overlap between the offset and the
112  *        mapping entry and -1 will be returned.
113  *
114  *     2. The offset is "greater than" the mapping entry; meaning the
115  *        offset is greater than the mapping entry's source offset plus
116  *        the entry's size. In this case, there is no overlap between
117  *        the offset and the mapping entry and 1 will be returned.
118  *
119  *        NOTE: If the offset is actually equal to the entry's offset
120  *        plus size, this is considered to be "greater" than the entry,
121  *        and this case applies (i.e. 1 will be returned). Thus, the
122  *        entry's "range" can be considered to be inclusive at its
123  *        start, but exclusive at its end: e.g. [src, src + size).
124  *
125  *     3. The last case to consider is if the offset actually falls
126  *        within the mapping entry's range. If this is the case, the
127  *        offset is considered to be "equal to" the mapping entry and
128  *        0 will be returned.
129  *
130  *        NOTE: If the offset is equal to the entry's source offset,
131  *        this case applies and 0 will be returned. If the offset is
132  *        equal to the entry's source plus its size, this case does
133  *        *not* apply (see "NOTE" above for scenario 2), and 1 will be
134  *        returned.
135  */
136 static int
dva_mapping_overlap_compare(const void * v_key,const void * v_array_elem)137 dva_mapping_overlap_compare(const void *v_key, const void *v_array_elem)
138 {
139 	const uint64_t * const key = v_key;
140 	const vdev_indirect_mapping_entry_phys_t * const array_elem =
141 	    v_array_elem;
142 	uint64_t src_offset = DVA_MAPPING_GET_SRC_OFFSET(array_elem);
143 
144 	if (*key < src_offset) {
145 		return (-1);
146 	} else if (*key < src_offset + DVA_GET_ASIZE(&array_elem->vimep_dst)) {
147 		return (0);
148 	} else {
149 		return (1);
150 	}
151 }
152 
153 /*
154  * Returns the mapping entry for the given offset.
155  *
156  * It's possible that the given offset will not be in the mapping table
157  * (i.e. no mapping entries contain this offset), in which case, the
158  * return value value depends on the "next_if_missing" parameter.
159  *
160  * If the offset is not found in the table and "next_if_missing" is
161  * B_FALSE, then NULL will always be returned. The behavior is intended
162  * to allow consumers to get the entry corresponding to the offset
163  * parameter, iff the offset overlaps with an entry in the table.
164  *
165  * If the offset is not found in the table and "next_if_missing" is
166  * B_TRUE, then the entry nearest to the given offset will be returned,
167  * such that the entry's source offset is greater than the offset
168  * passed in (i.e. the "next" mapping entry in the table is returned, if
169  * the offset is missing from the table). If there are no entries whose
170  * source offset is greater than the passed in offset, NULL is returned.
171  */
172 static vdev_indirect_mapping_entry_phys_t *
vdev_indirect_mapping_entry_for_offset_impl(vdev_indirect_mapping_t * vim,uint64_t offset,boolean_t next_if_missing)173 vdev_indirect_mapping_entry_for_offset_impl(vdev_indirect_mapping_t *vim,
174     uint64_t offset, boolean_t next_if_missing)
175 {
176 	ASSERT(vdev_indirect_mapping_verify(vim));
177 	ASSERT(vim->vim_phys->vimp_num_entries > 0);
178 
179 	vdev_indirect_mapping_entry_phys_t *entry = NULL;
180 
181 	uint64_t last = vim->vim_phys->vimp_num_entries - 1;
182 	uint64_t base = 0;
183 
184 	/*
185 	 * We don't define these inside of the while loop because we use
186 	 * their value in the case that offset isn't in the mapping.
187 	 */
188 	uint64_t mid;
189 	int result;
190 
191 	while (last >= base) {
192 		mid = base + ((last - base) >> 1);
193 
194 		result = dva_mapping_overlap_compare(&offset,
195 		    &vim->vim_entries[mid]);
196 
197 		if (result == 0) {
198 			entry = &vim->vim_entries[mid];
199 			break;
200 		} else if (result < 0) {
201 			last = mid - 1;
202 		} else {
203 			base = mid + 1;
204 		}
205 	}
206 
207 	if (entry == NULL && next_if_missing) {
208 		ASSERT3U(base, ==, last + 1);
209 		ASSERT(mid == base || mid == last);
210 		ASSERT3S(result, !=, 0);
211 
212 		/*
213 		 * The offset we're looking for isn't actually contained
214 		 * in the mapping table, thus we need to return the
215 		 * closest mapping entry that is greater than the
216 		 * offset. We reuse the result of the last comparison,
217 		 * comparing the mapping entry at index "mid" and the
218 		 * offset. The offset is guaranteed to lie between
219 		 * indices one less than "mid", and one greater than
220 		 * "mid"; we just need to determine if offset is greater
221 		 * than, or less than the mapping entry contained at
222 		 * index "mid".
223 		 */
224 
225 		uint64_t index;
226 		if (result < 0)
227 			index = mid;
228 		else
229 			index = mid + 1;
230 
231 		ASSERT3U(index, <=, vim->vim_phys->vimp_num_entries);
232 
233 		if (index == vim->vim_phys->vimp_num_entries) {
234 			/*
235 			 * If "index" is past the end of the entries
236 			 * array, then not only is the offset not in the
237 			 * mapping table, but it's actually greater than
238 			 * all entries in the table. In this case, we
239 			 * can't return a mapping entry greater than the
240 			 * offset (since none exist), so we return NULL.
241 			 */
242 
243 			ASSERT3S(dva_mapping_overlap_compare(&offset,
244 			    &vim->vim_entries[index - 1]), >, 0);
245 
246 			return (NULL);
247 		} else {
248 			/*
249 			 * Just to be safe, we verify the offset falls
250 			 * in between the mapping entries at index and
251 			 * one less than index. Since we know the offset
252 			 * doesn't overlap an entry, and we're supposed
253 			 * to return the entry just greater than the
254 			 * offset, both of the following tests must be
255 			 * true.
256 			 */
257 			ASSERT3S(dva_mapping_overlap_compare(&offset,
258 			    &vim->vim_entries[index]), <, 0);
259 			IMPLY(index >= 1, dva_mapping_overlap_compare(&offset,
260 			    &vim->vim_entries[index - 1]) > 0);
261 
262 			return (&vim->vim_entries[index]);
263 		}
264 	} else {
265 		return (entry);
266 	}
267 }
268 
269 vdev_indirect_mapping_entry_phys_t *
vdev_indirect_mapping_entry_for_offset(vdev_indirect_mapping_t * vim,uint64_t offset)270 vdev_indirect_mapping_entry_for_offset(vdev_indirect_mapping_t *vim,
271     uint64_t offset)
272 {
273 	return (vdev_indirect_mapping_entry_for_offset_impl(vim, offset,
274 	    B_FALSE));
275 }
276 
277 vdev_indirect_mapping_entry_phys_t *
vdev_indirect_mapping_entry_for_offset_or_next(vdev_indirect_mapping_t * vim,uint64_t offset)278 vdev_indirect_mapping_entry_for_offset_or_next(vdev_indirect_mapping_t *vim,
279     uint64_t offset)
280 {
281 	return (vdev_indirect_mapping_entry_for_offset_impl(vim, offset,
282 	    B_TRUE));
283 }
284 
285 void
vdev_indirect_mapping_close(vdev_indirect_mapping_t * vim)286 vdev_indirect_mapping_close(vdev_indirect_mapping_t *vim)
287 {
288 	ASSERT(vdev_indirect_mapping_verify(vim));
289 
290 	if (vim->vim_phys->vimp_num_entries > 0) {
291 		uint64_t map_size = vdev_indirect_mapping_size(vim);
292 		vmem_free(vim->vim_entries, map_size);
293 		vim->vim_entries = NULL;
294 	}
295 
296 	dmu_buf_rele(vim->vim_dbuf, vim);
297 
298 	vim->vim_objset = NULL;
299 	vim->vim_object = 0;
300 	vim->vim_dbuf = NULL;
301 	vim->vim_phys = NULL;
302 
303 	kmem_free(vim, sizeof (*vim));
304 }
305 
306 uint64_t
vdev_indirect_mapping_alloc(objset_t * os,dmu_tx_t * tx)307 vdev_indirect_mapping_alloc(objset_t *os, dmu_tx_t *tx)
308 {
309 	uint64_t object;
310 	ASSERT(dmu_tx_is_syncing(tx));
311 	uint64_t bonus_size = VDEV_INDIRECT_MAPPING_SIZE_V0;
312 
313 	if (spa_feature_is_enabled(os->os_spa, SPA_FEATURE_OBSOLETE_COUNTS)) {
314 		bonus_size = sizeof (vdev_indirect_mapping_phys_t);
315 	}
316 
317 	object = dmu_object_alloc(os,
318 	    DMU_OTN_UINT64_METADATA, SPA_OLD_MAXBLOCKSIZE,
319 	    DMU_OTN_UINT64_METADATA, bonus_size,
320 	    tx);
321 
322 	if (spa_feature_is_enabled(os->os_spa, SPA_FEATURE_OBSOLETE_COUNTS)) {
323 		dmu_buf_t *dbuf;
324 		vdev_indirect_mapping_phys_t *vimp;
325 
326 		VERIFY0(dmu_bonus_hold(os, object, FTAG, &dbuf));
327 		dmu_buf_will_dirty(dbuf, tx);
328 		vimp = dbuf->db_data;
329 		vimp->vimp_counts_object = dmu_object_alloc(os,
330 		    DMU_OTN_UINT32_METADATA, SPA_OLD_MAXBLOCKSIZE,
331 		    DMU_OT_NONE, 0, tx);
332 		spa_feature_incr(os->os_spa, SPA_FEATURE_OBSOLETE_COUNTS, tx);
333 		dmu_buf_rele(dbuf, FTAG);
334 	}
335 
336 	return (object);
337 }
338 
339 
340 vdev_indirect_mapping_t *
vdev_indirect_mapping_open(objset_t * os,uint64_t mapping_object)341 vdev_indirect_mapping_open(objset_t *os, uint64_t mapping_object)
342 {
343 	vdev_indirect_mapping_t *vim = kmem_zalloc(sizeof (*vim), KM_SLEEP);
344 	dmu_object_info_t doi;
345 	VERIFY0(dmu_object_info(os, mapping_object, &doi));
346 
347 	vim->vim_objset = os;
348 	vim->vim_object = mapping_object;
349 
350 	VERIFY0(dmu_bonus_hold(os, vim->vim_object, vim,
351 	    &vim->vim_dbuf));
352 	vim->vim_phys = vim->vim_dbuf->db_data;
353 
354 	vim->vim_havecounts =
355 	    (doi.doi_bonus_size > VDEV_INDIRECT_MAPPING_SIZE_V0);
356 
357 	if (vim->vim_phys->vimp_num_entries > 0) {
358 		uint64_t map_size = vdev_indirect_mapping_size(vim);
359 		vim->vim_entries = vmem_alloc(map_size, KM_SLEEP);
360 		VERIFY0(dmu_read(os, vim->vim_object, 0, map_size,
361 		    vim->vim_entries, DMU_READ_PREFETCH));
362 	}
363 
364 	ASSERT(vdev_indirect_mapping_verify(vim));
365 
366 	return (vim);
367 }
368 
369 void
vdev_indirect_mapping_free(objset_t * os,uint64_t object,dmu_tx_t * tx)370 vdev_indirect_mapping_free(objset_t *os, uint64_t object, dmu_tx_t *tx)
371 {
372 	vdev_indirect_mapping_t *vim = vdev_indirect_mapping_open(os, object);
373 	if (vim->vim_havecounts) {
374 		VERIFY0(dmu_object_free(os, vim->vim_phys->vimp_counts_object,
375 		    tx));
376 		spa_feature_decr(os->os_spa, SPA_FEATURE_OBSOLETE_COUNTS, tx);
377 	}
378 	vdev_indirect_mapping_close(vim);
379 
380 	VERIFY0(dmu_object_free(os, object, tx));
381 }
382 
383 /*
384  * Append the list of vdev_indirect_mapping_entry_t's to the on-disk
385  * mapping object.  Also remove the entries from the list and free them.
386  * This also implicitly extends the max_offset of the mapping (to the end
387  * of the last entry).
388  */
389 void
vdev_indirect_mapping_add_entries(vdev_indirect_mapping_t * vim,list_t * list,dmu_tx_t * tx)390 vdev_indirect_mapping_add_entries(vdev_indirect_mapping_t *vim,
391     list_t *list, dmu_tx_t *tx)
392 {
393 	vdev_indirect_mapping_entry_phys_t *mapbuf;
394 	uint64_t old_size;
395 	uint32_t *countbuf = NULL;
396 	vdev_indirect_mapping_entry_phys_t *old_entries;
397 	uint64_t old_count;
398 	uint64_t entries_written = 0;
399 
400 	ASSERT(vdev_indirect_mapping_verify(vim));
401 	ASSERT(dmu_tx_is_syncing(tx));
402 	ASSERT(dsl_pool_sync_context(dmu_tx_pool(tx)));
403 	ASSERT(!list_is_empty(list));
404 
405 	old_size = vdev_indirect_mapping_size(vim);
406 	old_entries = vim->vim_entries;
407 	old_count = vim->vim_phys->vimp_num_entries;
408 
409 	dmu_buf_will_dirty(vim->vim_dbuf, tx);
410 
411 	mapbuf = vmem_alloc(SPA_OLD_MAXBLOCKSIZE, KM_SLEEP);
412 	if (vim->vim_havecounts) {
413 		countbuf = vmem_alloc(SPA_OLD_MAXBLOCKSIZE, KM_SLEEP);
414 		ASSERT(spa_feature_is_active(vim->vim_objset->os_spa,
415 		    SPA_FEATURE_OBSOLETE_COUNTS));
416 	}
417 	while (!list_is_empty(list)) {
418 		uint64_t i;
419 		/*
420 		 * Write entries from the list to the
421 		 * vdev_im_object in batches of size SPA_OLD_MAXBLOCKSIZE.
422 		 */
423 		for (i = 0; i < SPA_OLD_MAXBLOCKSIZE / sizeof (*mapbuf); i++) {
424 			vdev_indirect_mapping_entry_t *entry =
425 			    list_remove_head(list);
426 			if (entry == NULL)
427 				break;
428 
429 			uint64_t size =
430 			    DVA_GET_ASIZE(&entry->vime_mapping.vimep_dst);
431 			uint64_t src_offset =
432 			    DVA_MAPPING_GET_SRC_OFFSET(&entry->vime_mapping);
433 
434 			/*
435 			 * We shouldn't be adding an entry which is fully
436 			 * obsolete.
437 			 */
438 			ASSERT3U(entry->vime_obsolete_count, <, size);
439 			IMPLY(entry->vime_obsolete_count != 0,
440 			    vim->vim_havecounts);
441 
442 			mapbuf[i] = entry->vime_mapping;
443 			if (vim->vim_havecounts)
444 				countbuf[i] = entry->vime_obsolete_count;
445 
446 			vim->vim_phys->vimp_bytes_mapped += size;
447 			ASSERT3U(src_offset, >=,
448 			    vim->vim_phys->vimp_max_offset);
449 			vim->vim_phys->vimp_max_offset = src_offset + size;
450 
451 			entries_written++;
452 
453 			vmem_free(entry, sizeof (*entry));
454 		}
455 		dmu_write(vim->vim_objset, vim->vim_object,
456 		    vim->vim_phys->vimp_num_entries * sizeof (*mapbuf),
457 		    i * sizeof (*mapbuf),
458 		    mapbuf, tx, DMU_READ_NO_PREFETCH);
459 		if (vim->vim_havecounts) {
460 			dmu_write(vim->vim_objset,
461 			    vim->vim_phys->vimp_counts_object,
462 			    vim->vim_phys->vimp_num_entries *
463 			    sizeof (*countbuf),
464 			    i * sizeof (*countbuf), countbuf, tx,
465 			    DMU_READ_NO_PREFETCH);
466 		}
467 		vim->vim_phys->vimp_num_entries += i;
468 	}
469 	vmem_free(mapbuf, SPA_OLD_MAXBLOCKSIZE);
470 	if (vim->vim_havecounts)
471 		vmem_free(countbuf, SPA_OLD_MAXBLOCKSIZE);
472 
473 	/*
474 	 * Update the entry array to reflect the new entries. First, copy
475 	 * over any old entries then read back the new entries we just wrote.
476 	 */
477 	uint64_t new_size = vdev_indirect_mapping_size(vim);
478 	ASSERT3U(new_size, >, old_size);
479 	ASSERT3U(new_size - old_size, ==,
480 	    entries_written * sizeof (vdev_indirect_mapping_entry_phys_t));
481 	vim->vim_entries = vmem_alloc(new_size, KM_SLEEP);
482 	if (old_size > 0) {
483 		memcpy(vim->vim_entries, old_entries, old_size);
484 		vmem_free(old_entries, old_size);
485 	}
486 	VERIFY0(dmu_read(vim->vim_objset, vim->vim_object, old_size,
487 	    new_size - old_size, &vim->vim_entries[old_count],
488 	    DMU_READ_PREFETCH));
489 
490 	zfs_dbgmsg("txg %llu: wrote %llu entries to "
491 	    "indirect mapping obj %llu; max offset=0x%llx",
492 	    (u_longlong_t)dmu_tx_get_txg(tx),
493 	    (u_longlong_t)entries_written,
494 	    (u_longlong_t)vim->vim_object,
495 	    (u_longlong_t)vim->vim_phys->vimp_max_offset);
496 }
497 
498 /*
499  * Increment the relevant counts for the specified offset and length.
500  * The counts array must be obtained from
501  * vdev_indirect_mapping_load_obsolete_counts().
502  */
503 void
vdev_indirect_mapping_increment_obsolete_count(vdev_indirect_mapping_t * vim,uint64_t offset,uint64_t length,uint32_t * counts)504 vdev_indirect_mapping_increment_obsolete_count(vdev_indirect_mapping_t *vim,
505     uint64_t offset, uint64_t length, uint32_t *counts)
506 {
507 	vdev_indirect_mapping_entry_phys_t *mapping;
508 	uint64_t index;
509 
510 	mapping = vdev_indirect_mapping_entry_for_offset(vim,  offset);
511 
512 	ASSERT(length > 0);
513 	ASSERT3P(mapping, !=, NULL);
514 
515 	index = mapping - vim->vim_entries;
516 
517 	while (length > 0) {
518 		ASSERT3U(index, <, vdev_indirect_mapping_num_entries(vim));
519 
520 		uint64_t size = DVA_GET_ASIZE(&mapping->vimep_dst);
521 		uint64_t inner_offset = offset -
522 		    DVA_MAPPING_GET_SRC_OFFSET(mapping);
523 		VERIFY3U(inner_offset, <, size);
524 		uint64_t inner_size = MIN(length, size - inner_offset);
525 
526 		VERIFY3U(counts[index] + inner_size, <=, size);
527 		counts[index] += inner_size;
528 
529 		offset += inner_size;
530 		length -= inner_size;
531 		mapping++;
532 		index++;
533 	}
534 }
535 
536 typedef struct load_obsolete_space_map_arg {
537 	vdev_indirect_mapping_t	*losma_vim;
538 	uint32_t		*losma_counts;
539 } load_obsolete_space_map_arg_t;
540 
541 static int
load_obsolete_sm_callback(space_map_entry_t * sme,void * arg)542 load_obsolete_sm_callback(space_map_entry_t *sme, void *arg)
543 {
544 	load_obsolete_space_map_arg_t *losma = arg;
545 	ASSERT3S(sme->sme_type, ==, SM_ALLOC);
546 
547 	vdev_indirect_mapping_increment_obsolete_count(losma->losma_vim,
548 	    sme->sme_offset, sme->sme_run, losma->losma_counts);
549 
550 	return (0);
551 }
552 
553 /*
554  * Modify the counts (increment them) based on the spacemap.
555  */
556 void
vdev_indirect_mapping_load_obsolete_spacemap(vdev_indirect_mapping_t * vim,uint32_t * counts,space_map_t * obsolete_space_sm)557 vdev_indirect_mapping_load_obsolete_spacemap(vdev_indirect_mapping_t *vim,
558     uint32_t *counts, space_map_t *obsolete_space_sm)
559 {
560 	load_obsolete_space_map_arg_t losma;
561 	losma.losma_counts = counts;
562 	losma.losma_vim = vim;
563 	VERIFY0(space_map_iterate(obsolete_space_sm,
564 	    space_map_length(obsolete_space_sm),
565 	    load_obsolete_sm_callback, &losma));
566 }
567 
568 /*
569  * Read the obsolete counts from disk, returning them in an array.
570  */
571 uint32_t *
vdev_indirect_mapping_load_obsolete_counts(vdev_indirect_mapping_t * vim)572 vdev_indirect_mapping_load_obsolete_counts(vdev_indirect_mapping_t *vim)
573 {
574 	ASSERT(vdev_indirect_mapping_verify(vim));
575 
576 	uint64_t counts_size =
577 	    vim->vim_phys->vimp_num_entries * sizeof (uint32_t);
578 	uint32_t *counts = vmem_alloc(counts_size, KM_SLEEP);
579 	if (vim->vim_havecounts) {
580 		VERIFY0(dmu_read(vim->vim_objset,
581 		    vim->vim_phys->vimp_counts_object,
582 		    0, counts_size,
583 		    counts, DMU_READ_PREFETCH));
584 	} else {
585 		memset(counts, 0, counts_size);
586 	}
587 	return (counts);
588 }
589 
590 extern void
vdev_indirect_mapping_free_obsolete_counts(vdev_indirect_mapping_t * vim,uint32_t * counts)591 vdev_indirect_mapping_free_obsolete_counts(vdev_indirect_mapping_t *vim,
592     uint32_t *counts)
593 {
594 	ASSERT(vdev_indirect_mapping_verify(vim));
595 
596 	vmem_free(counts, vim->vim_phys->vimp_num_entries * sizeof (uint32_t));
597 }
598 
599 #if defined(_KERNEL)
600 EXPORT_SYMBOL(vdev_indirect_mapping_add_entries);
601 EXPORT_SYMBOL(vdev_indirect_mapping_alloc);
602 EXPORT_SYMBOL(vdev_indirect_mapping_bytes_mapped);
603 EXPORT_SYMBOL(vdev_indirect_mapping_close);
604 EXPORT_SYMBOL(vdev_indirect_mapping_entry_for_offset);
605 EXPORT_SYMBOL(vdev_indirect_mapping_entry_for_offset_or_next);
606 EXPORT_SYMBOL(vdev_indirect_mapping_free);
607 EXPORT_SYMBOL(vdev_indirect_mapping_free_obsolete_counts);
608 EXPORT_SYMBOL(vdev_indirect_mapping_increment_obsolete_count);
609 EXPORT_SYMBOL(vdev_indirect_mapping_load_obsolete_counts);
610 EXPORT_SYMBOL(vdev_indirect_mapping_load_obsolete_spacemap);
611 EXPORT_SYMBOL(vdev_indirect_mapping_max_offset);
612 EXPORT_SYMBOL(vdev_indirect_mapping_num_entries);
613 EXPORT_SYMBOL(vdev_indirect_mapping_object);
614 EXPORT_SYMBOL(vdev_indirect_mapping_open);
615 EXPORT_SYMBOL(vdev_indirect_mapping_size);
616 #endif
617