]> CyberLeo.Net >> Repos - FreeBSD/releng/10.1.git/blob - sys/cddl/contrib/opensolaris/uts/common/fs/zfs/trim_map.c
Update base system file(1) to 5.22 to address multiple denial of
[FreeBSD/releng/10.1.git] / sys / cddl / contrib / opensolaris / uts / common / fs / zfs / trim_map.c
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
23  * All rights reserved.
24  */
25
26 #include <sys/zfs_context.h>
27 #include <sys/spa_impl.h>
28 #include <sys/vdev_impl.h>
29 #include <sys/trim_map.h>
30 #include <sys/time.h>
31
32 /*
33  * Calculate the zio end, upgrading based on ashift which would be
34  * done by zio_vdev_io_start.
35  *
36  * This makes free range consolidation much more effective
37  * than it would otherwise be as well as ensuring that entire
38  * blocks are invalidated by writes.
39  */
40 #define TRIM_ZIO_END(vd, offset, size)  (offset +               \
41         P2ROUNDUP(size, 1ULL << vd->vdev_top->vdev_ashift))
42
43 #define TRIM_MAP_SINC(tm, size)                                 \
44         atomic_add_64(&(tm)->tm_bytes, (size))
45
46 #define TRIM_MAP_SDEC(tm, size)                                 \
47         atomic_add_64(&(tm)->tm_bytes, -(size))
48
49 #define TRIM_MAP_QINC(tm)                                       \
50         atomic_inc_64(&(tm)->tm_pending);                       \
51
52 #define TRIM_MAP_QDEC(tm)                                       \
53         atomic_dec_64(&(tm)->tm_pending);
54
55 typedef struct trim_map {
56         list_t          tm_head;                /* List of segments sorted by txg. */
57         avl_tree_t      tm_queued_frees;        /* AVL tree of segments waiting for TRIM. */
58         avl_tree_t      tm_inflight_frees;      /* AVL tree of in-flight TRIMs. */
59         avl_tree_t      tm_inflight_writes;     /* AVL tree of in-flight writes. */
60         list_t          tm_pending_writes;      /* Writes blocked on in-flight frees. */
61         kmutex_t        tm_lock;
62         uint64_t        tm_pending;             /* Count of pending TRIMs. */
63         uint64_t        tm_bytes;               /* Total size in bytes of queued TRIMs. */
64 } trim_map_t;
65
66 typedef struct trim_seg {
67         avl_node_t      ts_node;        /* AVL node. */
68         list_node_t     ts_next;        /* List element. */
69         uint64_t        ts_start;       /* Starting offset of this segment. */
70         uint64_t        ts_end;         /* Ending offset (non-inclusive). */
71         uint64_t        ts_txg;         /* Segment creation txg. */
72         hrtime_t        ts_time;        /* Segment creation time. */
73 } trim_seg_t;
74
75 extern boolean_t zfs_trim_enabled;
76
77 static u_int trim_txg_delay = 32;
78 static u_int trim_timeout = 30;
79 static u_int trim_max_interval = 1;
80 /* Limit outstanding TRIMs to 2G (max size for a single TRIM request) */
81 static uint64_t trim_vdev_max_bytes = 2147483648;
82 /* Limit outstanding TRIMs to 64 (max ranges for a single TRIM request) */      
83 static u_int trim_vdev_max_pending = 64;
84
85 SYSCTL_DECL(_vfs_zfs);
86 SYSCTL_NODE(_vfs_zfs, OID_AUTO, trim, CTLFLAG_RD, 0, "ZFS TRIM");
87
88 TUNABLE_INT("vfs.zfs.trim.txg_delay", &trim_txg_delay);
89 SYSCTL_UINT(_vfs_zfs_trim, OID_AUTO, txg_delay, CTLFLAG_RWTUN, &trim_txg_delay,
90     0, "Delay TRIMs by up to this many TXGs");
91
92 TUNABLE_INT("vfs.zfs.trim.timeout", &trim_timeout);
93 SYSCTL_UINT(_vfs_zfs_trim, OID_AUTO, timeout, CTLFLAG_RWTUN, &trim_timeout, 0,
94     "Delay TRIMs by up to this many seconds");
95
96 TUNABLE_INT("vfs.zfs.trim.max_interval", &trim_max_interval);
97 SYSCTL_UINT(_vfs_zfs_trim, OID_AUTO, max_interval, CTLFLAG_RWTUN,
98     &trim_max_interval, 0,
99     "Maximum interval between TRIM queue processing (seconds)");
100
101 SYSCTL_DECL(_vfs_zfs_vdev);
102 TUNABLE_QUAD("vfs.zfs.vdev.trim_max_bytes", &trim_vdev_max_bytes);
103 SYSCTL_QUAD(_vfs_zfs_vdev, OID_AUTO, trim_max_bytes, CTLFLAG_RWTUN,
104     &trim_vdev_max_bytes, 0,
105     "Maximum pending TRIM bytes for a vdev");
106
107 TUNABLE_INT("vfs.zfs.vdev.trim_max_pending", &trim_vdev_max_pending);
108 SYSCTL_UINT(_vfs_zfs_vdev, OID_AUTO, trim_max_pending, CTLFLAG_RWTUN,
109     &trim_vdev_max_pending, 0,
110     "Maximum pending TRIM segments for a vdev");
111
112
113 static void trim_map_vdev_commit_done(spa_t *spa, vdev_t *vd);
114
115 static int
116 trim_map_seg_compare(const void *x1, const void *x2)
117 {
118         const trim_seg_t *s1 = x1;
119         const trim_seg_t *s2 = x2;
120
121         if (s1->ts_start < s2->ts_start) {
122                 if (s1->ts_end > s2->ts_start)
123                         return (0);
124                 return (-1);
125         }
126         if (s1->ts_start > s2->ts_start) {
127                 if (s1->ts_start < s2->ts_end)
128                         return (0);
129                 return (1);
130         }
131         return (0);
132 }
133
134 static int
135 trim_map_zio_compare(const void *x1, const void *x2)
136 {
137         const zio_t *z1 = x1;
138         const zio_t *z2 = x2;
139
140         if (z1->io_offset < z2->io_offset) {
141                 if (z1->io_offset + z1->io_size > z2->io_offset)
142                         return (0);
143                 return (-1);
144         }
145         if (z1->io_offset > z2->io_offset) {
146                 if (z1->io_offset < z2->io_offset + z2->io_size)
147                         return (0);
148                 return (1);
149         }
150         return (0);
151 }
152
153 void
154 trim_map_create(vdev_t *vd)
155 {
156         trim_map_t *tm;
157
158         ASSERT(zfs_trim_enabled && !vd->vdev_notrim &&
159                 vd->vdev_ops->vdev_op_leaf);
160
161         tm = kmem_zalloc(sizeof (*tm), KM_SLEEP);
162         mutex_init(&tm->tm_lock, NULL, MUTEX_DEFAULT, NULL);
163         list_create(&tm->tm_head, sizeof (trim_seg_t),
164             offsetof(trim_seg_t, ts_next));
165         list_create(&tm->tm_pending_writes, sizeof (zio_t),
166             offsetof(zio_t, io_trim_link));
167         avl_create(&tm->tm_queued_frees, trim_map_seg_compare,
168             sizeof (trim_seg_t), offsetof(trim_seg_t, ts_node));
169         avl_create(&tm->tm_inflight_frees, trim_map_seg_compare,
170             sizeof (trim_seg_t), offsetof(trim_seg_t, ts_node));
171         avl_create(&tm->tm_inflight_writes, trim_map_zio_compare,
172             sizeof (zio_t), offsetof(zio_t, io_trim_node));
173         vd->vdev_trimmap = tm;
174 }
175
176 void
177 trim_map_destroy(vdev_t *vd)
178 {
179         trim_map_t *tm;
180         trim_seg_t *ts;
181
182         ASSERT(vd->vdev_ops->vdev_op_leaf);
183
184         if (!zfs_trim_enabled)
185                 return;
186
187         tm = vd->vdev_trimmap;
188         if (tm == NULL)
189                 return;
190
191         /*
192          * We may have been called before trim_map_vdev_commit_done()
193          * had a chance to run, so do it now to prune the remaining
194          * inflight frees.
195          */
196         trim_map_vdev_commit_done(vd->vdev_spa, vd);
197
198         mutex_enter(&tm->tm_lock);
199         while ((ts = list_head(&tm->tm_head)) != NULL) {
200                 avl_remove(&tm->tm_queued_frees, ts);
201                 list_remove(&tm->tm_head, ts);
202                 kmem_free(ts, sizeof (*ts));
203                 TRIM_MAP_SDEC(tm, ts->ts_end - ts->ts_start);
204                 TRIM_MAP_QDEC(tm);
205         }
206         mutex_exit(&tm->tm_lock);
207
208         avl_destroy(&tm->tm_queued_frees);
209         avl_destroy(&tm->tm_inflight_frees);
210         avl_destroy(&tm->tm_inflight_writes);
211         list_destroy(&tm->tm_pending_writes);
212         list_destroy(&tm->tm_head);
213         mutex_destroy(&tm->tm_lock);
214         kmem_free(tm, sizeof (*tm));
215         vd->vdev_trimmap = NULL;
216 }
217
218 static void
219 trim_map_segment_add(trim_map_t *tm, uint64_t start, uint64_t end, uint64_t txg)
220 {
221         avl_index_t where;
222         trim_seg_t tsearch, *ts_before, *ts_after, *ts;
223         boolean_t merge_before, merge_after;
224         hrtime_t time;
225
226         ASSERT(MUTEX_HELD(&tm->tm_lock));
227         VERIFY(start < end);
228
229         time = gethrtime();
230         tsearch.ts_start = start;
231         tsearch.ts_end = end;
232
233         ts = avl_find(&tm->tm_queued_frees, &tsearch, &where);
234         if (ts != NULL) {
235                 if (start < ts->ts_start)
236                         trim_map_segment_add(tm, start, ts->ts_start, txg);
237                 if (end > ts->ts_end)
238                         trim_map_segment_add(tm, ts->ts_end, end, txg);
239                 return;
240         }
241
242         ts_before = avl_nearest(&tm->tm_queued_frees, where, AVL_BEFORE);
243         ts_after = avl_nearest(&tm->tm_queued_frees, where, AVL_AFTER);
244
245         merge_before = (ts_before != NULL && ts_before->ts_end == start);
246         merge_after = (ts_after != NULL && ts_after->ts_start == end);
247
248         if (merge_before && merge_after) {
249                 TRIM_MAP_SINC(tm, ts_after->ts_start - ts_before->ts_end);
250                 TRIM_MAP_QDEC(tm);
251                 avl_remove(&tm->tm_queued_frees, ts_before);
252                 list_remove(&tm->tm_head, ts_before);
253                 ts_after->ts_start = ts_before->ts_start;
254                 ts_after->ts_txg = txg;
255                 ts_after->ts_time = time;
256                 kmem_free(ts_before, sizeof (*ts_before));
257         } else if (merge_before) {
258                 TRIM_MAP_SINC(tm, end - ts_before->ts_end);
259                 ts_before->ts_end = end;
260                 ts_before->ts_txg = txg;
261                 ts_before->ts_time = time;
262         } else if (merge_after) {
263                 TRIM_MAP_SINC(tm, ts_after->ts_start - start);
264                 ts_after->ts_start = start;
265                 ts_after->ts_txg = txg;
266                 ts_after->ts_time = time;
267         } else {
268                 TRIM_MAP_SINC(tm, end - start);
269                 TRIM_MAP_QINC(tm);
270                 ts = kmem_alloc(sizeof (*ts), KM_SLEEP);
271                 ts->ts_start = start;
272                 ts->ts_end = end;
273                 ts->ts_txg = txg;
274                 ts->ts_time = time;
275                 avl_insert(&tm->tm_queued_frees, ts, where);
276                 list_insert_tail(&tm->tm_head, ts);
277         }
278 }
279
280 static void
281 trim_map_segment_remove(trim_map_t *tm, trim_seg_t *ts, uint64_t start,
282     uint64_t end)
283 {
284         trim_seg_t *nts;
285         boolean_t left_over, right_over;
286
287         ASSERT(MUTEX_HELD(&tm->tm_lock));
288
289         left_over = (ts->ts_start < start);
290         right_over = (ts->ts_end > end);
291
292         TRIM_MAP_SDEC(tm, end - start);
293         if (left_over && right_over) {
294                 nts = kmem_alloc(sizeof (*nts), KM_SLEEP);
295                 nts->ts_start = end;
296                 nts->ts_end = ts->ts_end;
297                 nts->ts_txg = ts->ts_txg;
298                 nts->ts_time = ts->ts_time;
299                 ts->ts_end = start;
300                 avl_insert_here(&tm->tm_queued_frees, nts, ts, AVL_AFTER);
301                 list_insert_after(&tm->tm_head, ts, nts);
302                 TRIM_MAP_QINC(tm);
303         } else if (left_over) {
304                 ts->ts_end = start;
305         } else if (right_over) {
306                 ts->ts_start = end;
307         } else {
308                 avl_remove(&tm->tm_queued_frees, ts);
309                 list_remove(&tm->tm_head, ts);
310                 TRIM_MAP_QDEC(tm);
311                 kmem_free(ts, sizeof (*ts));
312         }
313 }
314
315 static void
316 trim_map_free_locked(trim_map_t *tm, uint64_t start, uint64_t end, uint64_t txg)
317 {
318         zio_t zsearch, *zs;
319
320         ASSERT(MUTEX_HELD(&tm->tm_lock));
321
322         zsearch.io_offset = start;
323         zsearch.io_size = end - start;
324
325         zs = avl_find(&tm->tm_inflight_writes, &zsearch, NULL);
326         if (zs == NULL) {
327                 trim_map_segment_add(tm, start, end, txg);
328                 return;
329         }
330         if (start < zs->io_offset)
331                 trim_map_free_locked(tm, start, zs->io_offset, txg);
332         if (zs->io_offset + zs->io_size < end)
333                 trim_map_free_locked(tm, zs->io_offset + zs->io_size, end, txg);
334 }
335
336 void
337 trim_map_free(vdev_t *vd, uint64_t offset, uint64_t size, uint64_t txg)
338 {
339         trim_map_t *tm = vd->vdev_trimmap;
340
341         if (!zfs_trim_enabled || vd->vdev_notrim || tm == NULL)
342                 return;
343
344         mutex_enter(&tm->tm_lock);
345         trim_map_free_locked(tm, offset, TRIM_ZIO_END(vd, offset, size), txg);
346         mutex_exit(&tm->tm_lock);
347 }
348
349 boolean_t
350 trim_map_write_start(zio_t *zio)
351 {
352         vdev_t *vd = zio->io_vd;
353         trim_map_t *tm = vd->vdev_trimmap;
354         trim_seg_t tsearch, *ts;
355         boolean_t left_over, right_over;
356         uint64_t start, end;
357
358         if (!zfs_trim_enabled || vd->vdev_notrim || tm == NULL)
359                 return (B_TRUE);
360
361         start = zio->io_offset;
362         end = TRIM_ZIO_END(zio->io_vd, start, zio->io_size);
363         tsearch.ts_start = start;
364         tsearch.ts_end = end;
365
366         mutex_enter(&tm->tm_lock);
367
368         /*
369          * Checking for colliding in-flight frees.
370          */
371         ts = avl_find(&tm->tm_inflight_frees, &tsearch, NULL);
372         if (ts != NULL) {
373                 list_insert_tail(&tm->tm_pending_writes, zio);
374                 mutex_exit(&tm->tm_lock);
375                 return (B_FALSE);
376         }
377
378         ts = avl_find(&tm->tm_queued_frees, &tsearch, NULL);
379         if (ts != NULL) {
380                 /*
381                  * Loop until all overlapping segments are removed.
382                  */
383                 do {
384                         trim_map_segment_remove(tm, ts, start, end);
385                         ts = avl_find(&tm->tm_queued_frees, &tsearch, NULL);
386                 } while (ts != NULL);
387         }
388         avl_add(&tm->tm_inflight_writes, zio);
389
390         mutex_exit(&tm->tm_lock);
391
392         return (B_TRUE);
393 }
394
395 void
396 trim_map_write_done(zio_t *zio)
397 {
398         vdev_t *vd = zio->io_vd;
399         trim_map_t *tm = vd->vdev_trimmap;
400
401         /*
402          * Don't check for vdev_notrim, since the write could have
403          * started before vdev_notrim was set.
404          */
405         if (!zfs_trim_enabled || tm == NULL)
406                 return;
407
408         mutex_enter(&tm->tm_lock);
409         /*
410          * Don't fail if the write isn't in the tree, since the write
411          * could have started after vdev_notrim was set.
412          */
413         if (zio->io_trim_node.avl_child[0] ||
414             zio->io_trim_node.avl_child[1] ||
415             AVL_XPARENT(&zio->io_trim_node) ||
416             tm->tm_inflight_writes.avl_root == &zio->io_trim_node)
417                 avl_remove(&tm->tm_inflight_writes, zio);
418         mutex_exit(&tm->tm_lock);
419 }
420
421 /*
422  * Return the oldest segment (the one with the lowest txg / time) or NULL if:
423  * 1. The list is empty
424  * 2. The first element's txg is greater than txgsafe
425  * 3. The first element's txg is not greater than the txg argument and the
426  *    the first element's time is not greater than time argument
427  */
428 static trim_seg_t *
429 trim_map_first(trim_map_t *tm, uint64_t txg, uint64_t txgsafe, hrtime_t time)
430 {
431         trim_seg_t *ts;
432
433         ASSERT(MUTEX_HELD(&tm->tm_lock));
434         VERIFY(txgsafe >= txg);
435
436         ts = list_head(&tm->tm_head);
437         if (ts != NULL && ts->ts_txg <= txgsafe &&
438             (ts->ts_txg <= txg || ts->ts_time <= time ||
439             tm->tm_bytes > trim_vdev_max_bytes ||
440             tm->tm_pending > trim_vdev_max_pending))
441                 return (ts);
442         return (NULL);
443 }
444
445 static void
446 trim_map_vdev_commit(spa_t *spa, zio_t *zio, vdev_t *vd)
447 {
448         trim_map_t *tm = vd->vdev_trimmap;
449         trim_seg_t *ts;
450         uint64_t size, offset, txgtarget, txgsafe;
451         hrtime_t timelimit;
452
453         ASSERT(vd->vdev_ops->vdev_op_leaf);
454
455         if (tm == NULL)
456                 return;
457
458         timelimit = gethrtime() - trim_timeout * NANOSEC;
459         if (vd->vdev_isl2cache) {
460                 txgsafe = UINT64_MAX;
461                 txgtarget = UINT64_MAX;
462         } else {
463                 txgsafe = MIN(spa_last_synced_txg(spa), spa_freeze_txg(spa));
464                 if (txgsafe > trim_txg_delay)
465                         txgtarget = txgsafe - trim_txg_delay;
466                 else
467                         txgtarget = 0;
468         }
469
470         mutex_enter(&tm->tm_lock);
471         /* Loop until we have sent all outstanding free's */
472         while ((ts = trim_map_first(tm, txgtarget, txgsafe, timelimit))
473             != NULL) {
474                 list_remove(&tm->tm_head, ts);
475                 avl_remove(&tm->tm_queued_frees, ts);
476                 avl_add(&tm->tm_inflight_frees, ts);
477                 size = ts->ts_end - ts->ts_start;
478                 offset = ts->ts_start;
479                 TRIM_MAP_SDEC(tm, size);
480                 TRIM_MAP_QDEC(tm);
481                 /*
482                  * We drop the lock while we call zio_nowait as the IO
483                  * scheduler can result in a different IO being run e.g.
484                  * a write which would result in a recursive lock.
485                  */
486                 mutex_exit(&tm->tm_lock);
487
488                 zio_nowait(zio_trim(zio, spa, vd, offset, size));
489
490                 mutex_enter(&tm->tm_lock);
491                 ts = trim_map_first(tm, txgtarget, txgsafe, timelimit);
492         }
493         mutex_exit(&tm->tm_lock);
494 }
495
496 static void
497 trim_map_vdev_commit_done(spa_t *spa, vdev_t *vd)
498 {
499         trim_map_t *tm = vd->vdev_trimmap;
500         trim_seg_t *ts;
501         list_t pending_writes;
502         zio_t *zio;
503         uint64_t start, size;
504         void *cookie;
505
506         ASSERT(vd->vdev_ops->vdev_op_leaf);
507
508         if (tm == NULL)
509                 return;
510
511         mutex_enter(&tm->tm_lock);
512         if (!avl_is_empty(&tm->tm_inflight_frees)) {
513                 cookie = NULL;
514                 while ((ts = avl_destroy_nodes(&tm->tm_inflight_frees,
515                     &cookie)) != NULL) {
516                         kmem_free(ts, sizeof (*ts));
517                 }
518         }
519         list_create(&pending_writes, sizeof (zio_t), offsetof(zio_t,
520             io_trim_link));
521         list_move_tail(&pending_writes, &tm->tm_pending_writes);
522         mutex_exit(&tm->tm_lock);
523
524         while ((zio = list_remove_head(&pending_writes)) != NULL) {
525                 zio_vdev_io_reissue(zio);
526                 zio_execute(zio);
527         }
528         list_destroy(&pending_writes);
529 }
530
531 static void
532 trim_map_commit(spa_t *spa, zio_t *zio, vdev_t *vd)
533 {
534         int c;
535
536         if (vd == NULL)
537                 return;
538
539         if (vd->vdev_ops->vdev_op_leaf) {
540                 trim_map_vdev_commit(spa, zio, vd);
541         } else {
542                 for (c = 0; c < vd->vdev_children; c++)
543                         trim_map_commit(spa, zio, vd->vdev_child[c]);
544         }
545 }
546
547 static void
548 trim_map_commit_done(spa_t *spa, vdev_t *vd)
549 {
550         int c;
551
552         if (vd == NULL)
553                 return;
554
555         if (vd->vdev_ops->vdev_op_leaf) {
556                 trim_map_vdev_commit_done(spa, vd);
557         } else {
558                 for (c = 0; c < vd->vdev_children; c++)
559                         trim_map_commit_done(spa, vd->vdev_child[c]);
560         }
561 }
562
563 static void
564 trim_thread(void *arg)
565 {
566         spa_t *spa = arg;
567         zio_t *zio;
568
569 #ifdef _KERNEL
570         (void) snprintf(curthread->td_name, sizeof(curthread->td_name),
571             "trim %s", spa_name(spa));
572 #endif
573
574         for (;;) {
575                 mutex_enter(&spa->spa_trim_lock);
576                 if (spa->spa_trim_thread == NULL) {
577                         spa->spa_trim_thread = curthread;
578                         cv_signal(&spa->spa_trim_cv);
579                         mutex_exit(&spa->spa_trim_lock);
580                         thread_exit();
581                 }
582
583                 (void) cv_timedwait(&spa->spa_trim_cv, &spa->spa_trim_lock,
584                     hz * trim_max_interval);
585                 mutex_exit(&spa->spa_trim_lock);
586
587                 zio = zio_root(spa, NULL, NULL, ZIO_FLAG_CANFAIL);
588
589                 spa_config_enter(spa, SCL_STATE, FTAG, RW_READER);
590                 trim_map_commit(spa, zio, spa->spa_root_vdev);
591                 (void) zio_wait(zio);
592                 trim_map_commit_done(spa, spa->spa_root_vdev);
593                 spa_config_exit(spa, SCL_STATE, FTAG);
594         }
595 }
596
597 void
598 trim_thread_create(spa_t *spa)
599 {
600
601         if (!zfs_trim_enabled)
602                 return;
603
604         mutex_init(&spa->spa_trim_lock, NULL, MUTEX_DEFAULT, NULL);
605         cv_init(&spa->spa_trim_cv, NULL, CV_DEFAULT, NULL);
606         mutex_enter(&spa->spa_trim_lock);
607         spa->spa_trim_thread = thread_create(NULL, 0, trim_thread, spa, 0, &p0,
608             TS_RUN, minclsyspri);
609         mutex_exit(&spa->spa_trim_lock);
610 }
611
612 void
613 trim_thread_destroy(spa_t *spa)
614 {
615
616         if (!zfs_trim_enabled)
617                 return;
618         if (spa->spa_trim_thread == NULL)
619                 return;
620
621         mutex_enter(&spa->spa_trim_lock);
622         /* Setting spa_trim_thread to NULL tells the thread to stop. */
623         spa->spa_trim_thread = NULL;
624         cv_signal(&spa->spa_trim_cv);
625         /* The thread will set it back to != NULL on exit. */
626         while (spa->spa_trim_thread == NULL)
627                 cv_wait(&spa->spa_trim_cv, &spa->spa_trim_lock);
628         spa->spa_trim_thread = NULL;
629         mutex_exit(&spa->spa_trim_lock);
630
631         cv_destroy(&spa->spa_trim_cv);
632         mutex_destroy(&spa->spa_trim_lock);
633 }
634
635 void
636 trim_thread_wakeup(spa_t *spa)
637 {
638
639         if (!zfs_trim_enabled)
640                 return;
641         if (spa->spa_trim_thread == NULL)
642                 return;
643
644         mutex_enter(&spa->spa_trim_lock);
645         cv_signal(&spa->spa_trim_cv);
646         mutex_exit(&spa->spa_trim_lock);
647 }