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