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