trim_map.c revision 244187
1202719Sgabor/*
2202719Sgabor * CDDL HEADER START
3202719Sgabor *
4202719Sgabor * The contents of this file are subject to the terms of the
5202719Sgabor * Common Development and Distribution License (the "License").
6202719Sgabor * You may not use this file except in compliance with the License.
7202719Sgabor *
8202719Sgabor * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9202719Sgabor * or http://www.opensolaris.org/os/licensing.
10202719Sgabor * See the License for the specific language governing permissions
11202719Sgabor * and limitations under the License.
12202719Sgabor *
13202719Sgabor * When distributing Covered Code, include this CDDL HEADER in each
14202719Sgabor * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15202719Sgabor * If applicable, add the following below this CDDL HEADER, with the
16202719Sgabor * fields enclosed by brackets "[]" replaced with your own identifying
17202719Sgabor * information: Portions Copyright [yyyy] [name of copyright owner]
18202719Sgabor *
19202719Sgabor * CDDL HEADER END
20202719Sgabor */
21202719Sgabor/*
22202719Sgabor * Copyright (c) 2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
23202719Sgabor * All rights reserved.
24202719Sgabor */
25202719Sgabor
26202719Sgabor#include <sys/zfs_context.h>
27202719Sgabor#include <sys/spa_impl.h>
28202719Sgabor#include <sys/vdev_impl.h>
29202719Sgabor#include <sys/trim_map.h>
30202719Sgabor
31202719Sgabor/*
32202719Sgabor * Calculate the zio end, upgrading based on ashift which would be
33202719Sgabor * done by zio_vdev_io_start.
34202719Sgabor *
35202719Sgabor * This makes free range consolidation much more effective
36202719Sgabor * than it would otherwise be as well as ensuring that entire
37202719Sgabor * blocks are invalidated by writes.
38202719Sgabor */
39202719Sgabor#define	TRIM_ZIO_END(zio)	((zio)->io_offset +		\
40202719Sgabor 	P2ROUNDUP((zio)->io_size, 1ULL << (zio)->io_vd->vdev_top->vdev_ashift))
41202719Sgabor
42202719Sgabortypedef struct trim_map {
43202719Sgabor	list_t		tm_head;		/* List of segments sorted by txg. */
44202719Sgabor	avl_tree_t	tm_queued_frees;	/* AVL tree of segments waiting for TRIM. */
45202719Sgabor	avl_tree_t	tm_inflight_frees;	/* AVL tree of in-flight TRIMs. */
46202719Sgabor	avl_tree_t	tm_inflight_writes;	/* AVL tree of in-flight writes. */
47202719Sgabor	list_t		tm_pending_writes;	/* Writes blocked on in-flight frees. */
48202719Sgabor	kmutex_t	tm_lock;
49202719Sgabor} trim_map_t;
50202719Sgabor
51202719Sgabortypedef struct trim_seg {
52202719Sgabor	avl_node_t	ts_node;	/* AVL node. */
53202719Sgabor	list_node_t	ts_next;	/* List element. */
54202719Sgabor	uint64_t	ts_start;	/* Starting offset of this segment. */
55202719Sgabor	uint64_t	ts_end;		/* Ending offset (non-inclusive). */
56202719Sgabor	uint64_t	ts_txg;		/* Segment creation txg. */
57202719Sgabor} trim_seg_t;
58202719Sgabor
59202719Sgaborextern boolean_t zfs_notrim;
60202719Sgabor
61202719SgaborSYSCTL_DECL(_vfs_zfs);
62202719Sgabor/* Delay TRIMs by that many TXGs. */
63202719Sgaborstatic int trim_txg_limit = 64;
64202719SgaborTUNABLE_INT("vfs.zfs.trim_txg_limit", &trim_txg_limit);
65202719SgaborSYSCTL_INT(_vfs_zfs, OID_AUTO, trim_txg_limit, CTLFLAG_RW, &trim_txg_limit, 0,
66202719Sgabor    "Delay TRIMs by that many TXGs.");
67202719Sgabor
68202719Sgaborstatic void trim_map_vdev_commit_done(spa_t *spa, vdev_t *vd);
69202719Sgabor
70202719Sgaborstatic int
71202719Sgabortrim_map_seg_compare(const void *x1, const void *x2)
72202719Sgabor{
73202719Sgabor	const trim_seg_t *s1 = x1;
74202719Sgabor	const trim_seg_t *s2 = x2;
75202719Sgabor
76202719Sgabor	if (s1->ts_start < s2->ts_start) {
77202719Sgabor		if (s1->ts_end > s2->ts_start)
78202719Sgabor			return (0);
79202719Sgabor		return (-1);
80202719Sgabor	}
81202719Sgabor	if (s1->ts_start > s2->ts_start) {
82202719Sgabor		if (s1->ts_start < s2->ts_end)
83202719Sgabor			return (0);
84202719Sgabor		return (1);
85202719Sgabor	}
86202719Sgabor	return (0);
87202719Sgabor}
88202719Sgabor
89202719Sgaborstatic int
90202719Sgabortrim_map_zio_compare(const void *x1, const void *x2)
91202719Sgabor{
92202719Sgabor	const zio_t *z1 = x1;
93202719Sgabor	const zio_t *z2 = x2;
94202719Sgabor
95202719Sgabor	if (z1->io_offset < z2->io_offset) {
96202719Sgabor		if (z1->io_offset + z1->io_size > z2->io_offset)
97202719Sgabor			return (0);
98202719Sgabor		return (-1);
99202719Sgabor	}
100202719Sgabor	if (z1->io_offset > z2->io_offset) {
101202719Sgabor		if (z1->io_offset < z2->io_offset + z2->io_size)
102202719Sgabor			return (0);
103202719Sgabor		return (1);
104202719Sgabor	}
105202719Sgabor	return (0);
106202719Sgabor}
107202719Sgabor
108202719Sgaborvoid
109202719Sgabortrim_map_create(vdev_t *vd)
110202719Sgabor{
111202719Sgabor	trim_map_t *tm;
112202719Sgabor
113202719Sgabor	ASSERT(vd->vdev_ops->vdev_op_leaf);
114202719Sgabor
115202719Sgabor	if (zfs_notrim)
116202719Sgabor		return;
117202719Sgabor
118202719Sgabor	tm = kmem_zalloc(sizeof (*tm), KM_SLEEP);
119202719Sgabor	mutex_init(&tm->tm_lock, NULL, MUTEX_DEFAULT, NULL);
120202719Sgabor	list_create(&tm->tm_head, sizeof (trim_seg_t),
121202719Sgabor	    offsetof(trim_seg_t, ts_next));
122202719Sgabor	list_create(&tm->tm_pending_writes, sizeof (zio_t),
123202719Sgabor	    offsetof(zio_t, io_trim_link));
124202719Sgabor	avl_create(&tm->tm_queued_frees, trim_map_seg_compare,
125202719Sgabor	    sizeof (trim_seg_t), offsetof(trim_seg_t, ts_node));
126202719Sgabor	avl_create(&tm->tm_inflight_frees, trim_map_seg_compare,
127202719Sgabor	    sizeof (trim_seg_t), offsetof(trim_seg_t, ts_node));
128202719Sgabor	avl_create(&tm->tm_inflight_writes, trim_map_zio_compare,
129202719Sgabor	    sizeof (zio_t), offsetof(zio_t, io_trim_node));
130202719Sgabor	vd->vdev_trimmap = tm;
131202719Sgabor}
132202719Sgabor
133202719Sgaborvoid
134202719Sgabortrim_map_destroy(vdev_t *vd)
135202719Sgabor{
136202719Sgabor	trim_map_t *tm;
137202719Sgabor	trim_seg_t *ts;
138202719Sgabor
139202719Sgabor	ASSERT(vd->vdev_ops->vdev_op_leaf);
140202719Sgabor
141202719Sgabor	if (zfs_notrim)
142202719Sgabor		return;
143202719Sgabor
144202719Sgabor	tm = vd->vdev_trimmap;
145202719Sgabor	if (tm == NULL)
146202719Sgabor		return;
147202719Sgabor
148202719Sgabor	/*
149202719Sgabor	 * We may have been called before trim_map_vdev_commit_done()
150202719Sgabor	 * had a chance to run, so do it now to prune the remaining
151202719Sgabor	 * inflight frees.
152202719Sgabor	 */
153202719Sgabor	trim_map_vdev_commit_done(vd->vdev_spa, vd);
154202719Sgabor
155202719Sgabor	mutex_enter(&tm->tm_lock);
156202719Sgabor	while ((ts = list_head(&tm->tm_head)) != NULL) {
157202719Sgabor		avl_remove(&tm->tm_queued_frees, ts);
158202719Sgabor		list_remove(&tm->tm_head, ts);
159202719Sgabor		kmem_free(ts, sizeof (*ts));
160202719Sgabor	}
161202719Sgabor	mutex_exit(&tm->tm_lock);
162202719Sgabor
163202719Sgabor	avl_destroy(&tm->tm_queued_frees);
164202719Sgabor	avl_destroy(&tm->tm_inflight_frees);
165202719Sgabor	avl_destroy(&tm->tm_inflight_writes);
166202719Sgabor	list_destroy(&tm->tm_pending_writes);
167202719Sgabor	list_destroy(&tm->tm_head);
168202719Sgabor	mutex_destroy(&tm->tm_lock);
169202719Sgabor	kmem_free(tm, sizeof (*tm));
170202719Sgabor	vd->vdev_trimmap = NULL;
171202719Sgabor}
172202719Sgabor
173202719Sgaborstatic void
174202719Sgabortrim_map_segment_add(trim_map_t *tm, uint64_t start, uint64_t end, uint64_t txg)
175202719Sgabor{
176202719Sgabor	avl_index_t where;
177202719Sgabor	trim_seg_t tsearch, *ts_before, *ts_after, *ts;
178202719Sgabor	boolean_t merge_before, merge_after;
179202719Sgabor
180202719Sgabor	ASSERT(MUTEX_HELD(&tm->tm_lock));
181202719Sgabor	VERIFY(start < end);
182202719Sgabor
183202719Sgabor	tsearch.ts_start = start;
184202719Sgabor	tsearch.ts_end = end;
185202719Sgabor
186202719Sgabor	ts = avl_find(&tm->tm_queued_frees, &tsearch, &where);
187202719Sgabor	if (ts != NULL) {
188202719Sgabor		if (start < ts->ts_start)
189202719Sgabor			trim_map_segment_add(tm, start, ts->ts_start, txg);
190202719Sgabor		if (end > ts->ts_end)
191202719Sgabor			trim_map_segment_add(tm, ts->ts_end, end, txg);
192202719Sgabor		return;
193202719Sgabor	}
194202719Sgabor
195202719Sgabor	ts_before = avl_nearest(&tm->tm_queued_frees, where, AVL_BEFORE);
196202719Sgabor	ts_after = avl_nearest(&tm->tm_queued_frees, where, AVL_AFTER);
197202719Sgabor
198202719Sgabor	merge_before = (ts_before != NULL && ts_before->ts_end == start &&
199202719Sgabor	    ts_before->ts_txg == txg);
200202719Sgabor	merge_after = (ts_after != NULL && ts_after->ts_start == end &&
201202719Sgabor	    ts_after->ts_txg == txg);
202202719Sgabor
203202719Sgabor	if (merge_before && merge_after) {
204202719Sgabor		avl_remove(&tm->tm_queued_frees, ts_before);
205202719Sgabor		list_remove(&tm->tm_head, ts_before);
206202719Sgabor		ts_after->ts_start = ts_before->ts_start;
207202719Sgabor		kmem_free(ts_before, sizeof (*ts_before));
208202719Sgabor	} else if (merge_before) {
209202719Sgabor		ts_before->ts_end = end;
210202719Sgabor	} else if (merge_after) {
211202719Sgabor		ts_after->ts_start = start;
212202719Sgabor	} else {
213202719Sgabor		ts = kmem_alloc(sizeof (*ts), KM_SLEEP);
214202719Sgabor		ts->ts_start = start;
215202719Sgabor		ts->ts_end = end;
216202719Sgabor		ts->ts_txg = txg;
217202719Sgabor		avl_insert(&tm->tm_queued_frees, ts, where);
218202719Sgabor		list_insert_tail(&tm->tm_head, ts);
219202719Sgabor	}
220202719Sgabor}
221202719Sgabor
222202719Sgaborstatic void
223202719Sgabortrim_map_segment_remove(trim_map_t *tm, trim_seg_t *ts, uint64_t start,
224202719Sgabor    uint64_t end)
225202719Sgabor{
226202719Sgabor	trim_seg_t *nts;
227202719Sgabor	boolean_t left_over, right_over;
228202719Sgabor
229202719Sgabor	ASSERT(MUTEX_HELD(&tm->tm_lock));
230202719Sgabor
231202719Sgabor	left_over = (ts->ts_start < start);
232202719Sgabor	right_over = (ts->ts_end > end);
233202719Sgabor
234202719Sgabor	if (left_over && right_over) {
235202719Sgabor		nts = kmem_alloc(sizeof (*nts), KM_SLEEP);
236202719Sgabor		nts->ts_start = end;
237202719Sgabor		nts->ts_end = ts->ts_end;
238202719Sgabor		nts->ts_txg = ts->ts_txg;
239202719Sgabor		ts->ts_end = start;
240202719Sgabor		avl_insert_here(&tm->tm_queued_frees, nts, ts, AVL_AFTER);
241202719Sgabor		list_insert_after(&tm->tm_head, ts, nts);
242202719Sgabor	} else if (left_over) {
243202719Sgabor		ts->ts_end = start;
244202719Sgabor	} else if (right_over) {
245202719Sgabor		ts->ts_start = end;
246202719Sgabor	} else {
247202719Sgabor		avl_remove(&tm->tm_queued_frees, ts);
248202719Sgabor		list_remove(&tm->tm_head, ts);
249202719Sgabor		kmem_free(ts, sizeof (*ts));
250202719Sgabor	}
251202719Sgabor}
252202719Sgabor
253202719Sgaborstatic void
254202719Sgabortrim_map_free_locked(trim_map_t *tm, uint64_t start, uint64_t end, uint64_t txg)
255202719Sgabor{
256202719Sgabor	zio_t zsearch, *zs;
257202719Sgabor
258202719Sgabor	ASSERT(MUTEX_HELD(&tm->tm_lock));
259202719Sgabor
260202719Sgabor	zsearch.io_offset = start;
261202719Sgabor	zsearch.io_size = end - start;
262202719Sgabor
263202719Sgabor	zs = avl_find(&tm->tm_inflight_writes, &zsearch, NULL);
264202719Sgabor	if (zs == NULL) {
265202719Sgabor		trim_map_segment_add(tm, start, end, txg);
266202719Sgabor		return;
267202719Sgabor	}
268202719Sgabor	if (start < zs->io_offset)
269202719Sgabor		trim_map_free_locked(tm, start, zs->io_offset, txg);
270202719Sgabor	if (zs->io_offset + zs->io_size < end)
271202719Sgabor		trim_map_free_locked(tm, zs->io_offset + zs->io_size, end, txg);
272202719Sgabor}
273202719Sgabor
274202719Sgaborvoid
275202719Sgabortrim_map_free(zio_t *zio)
276202719Sgabor{
277202719Sgabor	vdev_t *vd = zio->io_vd;
278202719Sgabor	trim_map_t *tm = vd->vdev_trimmap;
279202719Sgabor
280202719Sgabor	if (zfs_notrim || vd->vdev_notrim || tm == NULL)
281202719Sgabor		return;
282202719Sgabor
283202719Sgabor	mutex_enter(&tm->tm_lock);
284202719Sgabor	trim_map_free_locked(tm, zio->io_offset, TRIM_ZIO_END(zio),
285202719Sgabor	    vd->vdev_spa->spa_syncing_txg);
286202719Sgabor	mutex_exit(&tm->tm_lock);
287202719Sgabor}
288202719Sgabor
289202719Sgaborboolean_t
290202719Sgabortrim_map_write_start(zio_t *zio)
291202719Sgabor{
292202719Sgabor	vdev_t *vd = zio->io_vd;
293202719Sgabor	trim_map_t *tm = vd->vdev_trimmap;
294202719Sgabor	trim_seg_t tsearch, *ts;
295202719Sgabor	boolean_t left_over, right_over;
296202719Sgabor	uint64_t start, end;
297202719Sgabor
298202719Sgabor	if (zfs_notrim || vd->vdev_notrim || tm == NULL)
299202719Sgabor		return (B_TRUE);
300202719Sgabor
301202719Sgabor	start = zio->io_offset;
302202719Sgabor	end = TRIM_ZIO_END(zio);
303202719Sgabor	tsearch.ts_start = start;
304202719Sgabor	tsearch.ts_end = end;
305202719Sgabor
306202719Sgabor	mutex_enter(&tm->tm_lock);
307202719Sgabor
308202719Sgabor	/*
309202719Sgabor	 * Checking for colliding in-flight frees.
310202719Sgabor	 */
311202719Sgabor	ts = avl_find(&tm->tm_inflight_frees, &tsearch, NULL);
312202719Sgabor	if (ts != NULL) {
313202719Sgabor		list_insert_tail(&tm->tm_pending_writes, zio);
314202719Sgabor		mutex_exit(&tm->tm_lock);
315202719Sgabor		return (B_FALSE);
316202719Sgabor	}
317202719Sgabor
318202719Sgabor	ts = avl_find(&tm->tm_queued_frees, &tsearch, NULL);
319202719Sgabor	if (ts != NULL) {
320202719Sgabor		/*
321202719Sgabor		 * Loop until all overlapping segments are removed.
322202719Sgabor		 */
323202719Sgabor		do {
324202719Sgabor			trim_map_segment_remove(tm, ts, start, end);
325202719Sgabor			ts = avl_find(&tm->tm_queued_frees, &tsearch, NULL);
326202719Sgabor		} while (ts != NULL);
327202719Sgabor	}
328202719Sgabor	avl_add(&tm->tm_inflight_writes, zio);
329202719Sgabor
330202719Sgabor	mutex_exit(&tm->tm_lock);
331202719Sgabor
332202719Sgabor	return (B_TRUE);
333202719Sgabor}
334202719Sgabor
335202719Sgaborvoid
336202719Sgabortrim_map_write_done(zio_t *zio)
337202719Sgabor{
338202719Sgabor	vdev_t *vd = zio->io_vd;
339202719Sgabor	trim_map_t *tm = vd->vdev_trimmap;
340202719Sgabor
341202719Sgabor	/*
342202719Sgabor	 * Don't check for vdev_notrim, since the write could have
343202719Sgabor	 * started before vdev_notrim was set.
344202719Sgabor	 */
345202719Sgabor	if (zfs_notrim || tm == NULL)
346202719Sgabor		return;
347202719Sgabor
348202719Sgabor	mutex_enter(&tm->tm_lock);
349202719Sgabor	/*
350202719Sgabor	 * Don't fail if the write isn't in the tree, since the write
351202719Sgabor	 * could have started after vdev_notrim was set.
352202719Sgabor	 */
353202719Sgabor	if (zio->io_trim_node.avl_child[0] ||
354202719Sgabor	    zio->io_trim_node.avl_child[1] ||
355202719Sgabor	    AVL_XPARENT(&zio->io_trim_node) ||
356202719Sgabor	    tm->tm_inflight_writes.avl_root == &zio->io_trim_node)
357202719Sgabor		avl_remove(&tm->tm_inflight_writes, zio);
358202719Sgabor	mutex_exit(&tm->tm_lock);
359202719Sgabor}
360202719Sgabor
361202719Sgabor/*
362202719Sgabor * Return the oldest segment (the one with the lowest txg) or false if
363202719Sgabor * the list is empty or the first element's txg is greater than txg given
364202719Sgabor * as function argument.
365202719Sgabor */
366202719Sgaborstatic trim_seg_t *
367202719Sgabortrim_map_first(trim_map_t *tm, uint64_t txg)
368202719Sgabor{
369202719Sgabor	trim_seg_t *ts;
370202719Sgabor
371202719Sgabor	ASSERT(MUTEX_HELD(&tm->tm_lock));
372202719Sgabor
373202719Sgabor	ts = list_head(&tm->tm_head);
374202719Sgabor	if (ts != NULL && ts->ts_txg <= txg)
375202719Sgabor		return (ts);
376202719Sgabor	return (NULL);
377202719Sgabor}
378202719Sgabor
379202719Sgaborstatic void
380202719Sgabortrim_map_vdev_commit(spa_t *spa, zio_t *zio, vdev_t *vd)
381202719Sgabor{
382202719Sgabor	trim_map_t *tm = vd->vdev_trimmap;
383202719Sgabor	trim_seg_t *ts;
384202719Sgabor	uint64_t start, size, txglimit;
385202719Sgabor
386202719Sgabor	ASSERT(vd->vdev_ops->vdev_op_leaf);
387202719Sgabor
388202719Sgabor	if (tm == NULL)
389202719Sgabor		return;
390202719Sgabor
391202719Sgabor	txglimit = MIN(spa->spa_syncing_txg, spa_freeze_txg(spa)) -
392202719Sgabor	    trim_txg_limit;
393202719Sgabor
394202719Sgabor	mutex_enter(&tm->tm_lock);
395202719Sgabor	/*
396202719Sgabor	 * Loop until we send all frees up to the txglimit.
397202719Sgabor	 */
398202719Sgabor	while ((ts = trim_map_first(tm, txglimit)) != NULL) {
399202719Sgabor		list_remove(&tm->tm_head, ts);
400202719Sgabor		avl_remove(&tm->tm_queued_frees, ts);
401202719Sgabor		avl_add(&tm->tm_inflight_frees, ts);
402202719Sgabor		zio_nowait(zio_trim(zio, spa, vd, ts->ts_start,
403202719Sgabor		    ts->ts_end - ts->ts_start));
404202719Sgabor	}
405202719Sgabor	mutex_exit(&tm->tm_lock);
406202719Sgabor}
407202719Sgabor
408202719Sgaborstatic void
409202719Sgabortrim_map_vdev_commit_done(spa_t *spa, vdev_t *vd)
410202719Sgabor{
411202719Sgabor	trim_map_t *tm = vd->vdev_trimmap;
412202719Sgabor	trim_seg_t *ts;
413202719Sgabor	list_t pending_writes;
414202719Sgabor	zio_t *zio;
415202719Sgabor	uint64_t start, size;
416202719Sgabor	void *cookie;
417202719Sgabor
418	ASSERT(vd->vdev_ops->vdev_op_leaf);
419
420	if (tm == NULL)
421		return;
422
423	mutex_enter(&tm->tm_lock);
424	if (!avl_is_empty(&tm->tm_inflight_frees)) {
425		cookie = NULL;
426		while ((ts = avl_destroy_nodes(&tm->tm_inflight_frees,
427		    &cookie)) != NULL) {
428			kmem_free(ts, sizeof (*ts));
429		}
430	}
431	list_create(&pending_writes, sizeof (zio_t), offsetof(zio_t,
432	    io_trim_link));
433	list_move_tail(&pending_writes, &tm->tm_pending_writes);
434	mutex_exit(&tm->tm_lock);
435
436	while ((zio = list_remove_head(&pending_writes)) != NULL) {
437		zio_vdev_io_reissue(zio);
438		zio_execute(zio);
439	}
440	list_destroy(&pending_writes);
441}
442
443static void
444trim_map_commit(spa_t *spa, zio_t *zio, vdev_t *vd)
445{
446	int c;
447
448	if (vd == NULL || spa->spa_syncing_txg <= trim_txg_limit)
449		return;
450
451	if (vd->vdev_ops->vdev_op_leaf) {
452		trim_map_vdev_commit(spa, zio, vd);
453	} else {
454		for (c = 0; c < vd->vdev_children; c++)
455			trim_map_commit(spa, zio, vd->vdev_child[c]);
456	}
457}
458
459static void
460trim_map_commit_done(spa_t *spa, vdev_t *vd)
461{
462	int c;
463
464	if (vd == NULL)
465		return;
466
467	if (vd->vdev_ops->vdev_op_leaf) {
468		trim_map_vdev_commit_done(spa, vd);
469	} else {
470		for (c = 0; c < vd->vdev_children; c++)
471			trim_map_commit_done(spa, vd->vdev_child[c]);
472	}
473}
474
475static void
476trim_thread(void *arg)
477{
478	spa_t *spa = arg;
479	zio_t *zio;
480
481	for (;;) {
482		mutex_enter(&spa->spa_trim_lock);
483		if (spa->spa_trim_thread == NULL) {
484			spa->spa_trim_thread = curthread;
485			cv_signal(&spa->spa_trim_cv);
486			mutex_exit(&spa->spa_trim_lock);
487			thread_exit();
488		}
489		cv_wait(&spa->spa_trim_cv, &spa->spa_trim_lock);
490		mutex_exit(&spa->spa_trim_lock);
491
492		zio = zio_root(spa, NULL, NULL, ZIO_FLAG_CANFAIL);
493
494		spa_config_enter(spa, SCL_STATE, FTAG, RW_READER);
495		trim_map_commit(spa, zio, spa->spa_root_vdev);
496		(void) zio_wait(zio);
497		trim_map_commit_done(spa, spa->spa_root_vdev);
498		spa_config_exit(spa, SCL_STATE, FTAG);
499	}
500}
501
502void
503trim_thread_create(spa_t *spa)
504{
505
506	if (zfs_notrim)
507		return;
508
509	mutex_init(&spa->spa_trim_lock, NULL, MUTEX_DEFAULT, NULL);
510	cv_init(&spa->spa_trim_cv, NULL, CV_DEFAULT, NULL);
511	mutex_enter(&spa->spa_trim_lock);
512	spa->spa_trim_thread = thread_create(NULL, 0, trim_thread, spa, 0, &p0,
513	    TS_RUN, minclsyspri);
514	mutex_exit(&spa->spa_trim_lock);
515}
516
517void
518trim_thread_destroy(spa_t *spa)
519{
520
521	if (zfs_notrim)
522		return;
523	if (spa->spa_trim_thread == NULL)
524		return;
525
526	mutex_enter(&spa->spa_trim_lock);
527	/* Setting spa_trim_thread to NULL tells the thread to stop. */
528	spa->spa_trim_thread = NULL;
529	cv_signal(&spa->spa_trim_cv);
530	/* The thread will set it back to != NULL on exit. */
531	while (spa->spa_trim_thread == NULL)
532		cv_wait(&spa->spa_trim_cv, &spa->spa_trim_lock);
533	spa->spa_trim_thread = NULL;
534	mutex_exit(&spa->spa_trim_lock);
535
536	cv_destroy(&spa->spa_trim_cv);
537	mutex_destroy(&spa->spa_trim_lock);
538}
539
540void
541trim_thread_wakeup(spa_t *spa)
542{
543
544	if (zfs_notrim)
545		return;
546	if (spa->spa_trim_thread == NULL)
547		return;
548
549	mutex_enter(&spa->spa_trim_lock);
550	cv_signal(&spa->spa_trim_cv);
551	mutex_exit(&spa->spa_trim_lock);
552}
553