1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
14 */
15
16 #include <sys/dmu_tx.h>
17 #include <sys/zap.h>
18 #include <sys/dsl_synctask.h>
19 #include <sys/spa_impl.h>
20 #include <sys/cos_impl.h>
21 #include <sys/vdev_impl.h>
22 #include <sys/dbuf.h>
23 #include <sys/debug.h>
24 #include <sys/zfeature.h>
25 #include "zfs_prop.h"
26
27 /* Static name of the CoS property list */
28 #define COS_ARRAY "COS_ARRAY"
29
30 static int spa_alloc_cos_nosync(spa_t *spa, const char *cosname,
31 uint64_t cosid);
32 static int cos_set_common(cos_t *cos, const char *strval, uint64_t ival,
33 cos_prop_t prop);
34 static int cos_get_common(cos_t *cos, char **value, uint64_t *oval,
35 cos_prop_t prop);
36
37 typedef boolean_t (*cos_func_t)(cos_t *, void *);
38
39 void
40 spa_cos_enter(spa_t *spa)
41 {
42 mutex_enter(&spa->spa_cos_props_lock);
43 }
44
45 void
46 spa_cos_exit(spa_t *spa)
47 {
48 mutex_exit(&spa->spa_cos_props_lock);
49 }
50
51 static boolean_t
52 cos_match_guid(cos_t *cos, void *match_data)
53 {
54 uint64_t guid = (uint64_t)(unsigned long)match_data;
55 return (cos->cos_guid == guid);
56 }
57
58 static boolean_t
59 cos_match_name(cos_t *cos, void *match_data)
60 {
61 const char *name = (const char *)match_data;
62 return (strncmp(cos->cos_name, name, MAXCOSNAMELEN-1) == 0);
63 }
64
65 static cos_t *
66 spa_foreach_cos(spa_t *spa, cos_func_t cos_f, void *data)
67 {
68 cos_t *cos, *next_cos;
69
70 for (cos = list_head(&spa->spa_cos_list); cos != NULL;
71 cos = next_cos) {
72 next_cos = list_next(&spa->spa_cos_list, cos);
73 if (cos_f(cos, data))
74 break;
75 }
76
77 return (cos);
78 }
79
80 cos_t *
81 spa_lookup_cos_by_guid(spa_t *spa, uint64_t guid)
82 {
83 return (spa_foreach_cos(spa, cos_match_guid,
84 (void *)(unsigned long)guid));
85 }
86
87 cos_t *
88 spa_lookup_cos_by_name(spa_t *spa, const char *name)
89 {
90 if (name == NULL)
91 return (NULL);
92 return (spa_foreach_cos(spa, cos_match_name, (void *)name));
93 }
94
95 uint64_t
96 cos_refcount(cos_t *cos)
97 {
98 return (cos->cos_refcnt);
99 }
100
101 void
102 cos_hold(cos_t *cos)
103 {
104 atomic_inc_64(&cos->cos_refcnt);
105 }
106
107 void
108 cos_rele(cos_t *cos)
109 {
110 ASSERT(cos->cos_refcnt);
111 atomic_dec_64(&cos->cos_refcnt);
112 }
113
114 static int
115 cos_set_common(cos_t *cos, const char *strval, uint64_t ival, cos_prop_t prop)
116 {
117 zio_priority_t p;
118
119 switch (prop) {
120 case COS_PROP_NAME:
121 (void) snprintf(cos->cos_name, MAXCOSNAMELEN,
122 "%s", strval);
123 break;
124
125 case COS_PROP_PREFERRED_READ:
126 cos->cos_preferred_read = (boolean_t)ival;
127 break;
128
129 case COS_PROP_READ_MINACTIVE:
130 case COS_PROP_AREAD_MINACTIVE:
131 case COS_PROP_WRITE_MINACTIVE:
132 case COS_PROP_AWRITE_MINACTIVE:
133 case COS_PROP_SCRUB_MINACTIVE:
134 case COS_PROP_RESILVER_MINACTIVE:
135 p = COS_PROP_TO_ZIO_PRIO_MIN(prop);
136 ASSERT(ZIO_PRIORITY_QUEUEABLE_VALID(p));
137 cos->cos_min_active[p] = ival;
138 break;
139
140 case COS_PROP_READ_MAXACTIVE:
141 case COS_PROP_AREAD_MAXACTIVE:
142 case COS_PROP_WRITE_MAXACTIVE:
143 case COS_PROP_AWRITE_MAXACTIVE:
144 case COS_PROP_SCRUB_MAXACTIVE:
145 case COS_PROP_RESILVER_MAXACTIVE:
146 p = COS_PROP_TO_ZIO_PRIO_MAX(prop);
147 ASSERT(ZIO_PRIORITY_QUEUEABLE_VALID(p));
148 cos->cos_max_active[p] = ival;
149 break;
150
151 default:
152 return (SET_ERROR(ENOTSUP));
153 }
154
155 return (0);
156 }
157
158 /*ARGSUSED*/
159 static int
160 cos_get_common(cos_t *cos, char **value, uint64_t *oval, cos_prop_t prop)
161 {
162 zio_priority_t p;
163
164 switch (prop) {
165 case COS_PROP_GUID:
166 *oval = cos->cos_guid;
167 break;
168
169 case COS_PROP_NAME:
170 if (cos->cos_name[0] != '\0')
171 *value = spa_strdup(cos->cos_name);
172 break;
173
174 case COS_PROP_PREFERRED_READ:
175 *oval = cos->cos_preferred_read;
176 break;
177
178 case COS_PROP_READ_MINACTIVE:
179 case COS_PROP_AREAD_MINACTIVE:
180 case COS_PROP_WRITE_MINACTIVE:
181 case COS_PROP_AWRITE_MINACTIVE:
182 case COS_PROP_SCRUB_MINACTIVE:
183 case COS_PROP_RESILVER_MINACTIVE:
184 p = COS_PROP_TO_ZIO_PRIO_MIN(prop);
185 ASSERT(ZIO_PRIORITY_QUEUEABLE_VALID(p));
186 *oval = cos->cos_min_active[p];
187 break;
188
189 case COS_PROP_READ_MAXACTIVE:
190 case COS_PROP_AREAD_MAXACTIVE:
191 case COS_PROP_WRITE_MAXACTIVE:
192 case COS_PROP_AWRITE_MAXACTIVE:
193 case COS_PROP_SCRUB_MAXACTIVE:
194 case COS_PROP_RESILVER_MAXACTIVE:
195 p = COS_PROP_TO_ZIO_PRIO_MAX(prop);
196 ASSERT(ZIO_PRIORITY_QUEUEABLE_VALID(p));
197 *oval = cos->cos_max_active[p];
198 break;
199
200 default:
201 return (SET_ERROR(ENOTSUP));
202 }
203
204 return (0);
205 }
206
207 /* ARGSUSED */
208 static boolean_t
209 cos_count(cos_t *cos, void *cntp)
210 {
211 spa_t *spa = cos->cos_spa;
212 size_t *counterp = cntp;
213
214 ASSERT(MUTEX_HELD(&spa->spa_cos_props_lock));
215
216 (*counterp)++;
217
218 /*
219 * The B_FALSE is an indication to spa_foreach_cos()
220 * to continue iterating along the cos list
221 */
222 return (B_FALSE);
223 }
224
225 static void
226 cos_sync_classes(spa_t *spa, uint64_t obj, dmu_tx_t *tx)
227 {
228 dmu_buf_t *db;
229 nvlist_t *nvl;
230 nvlist_t **nvl_arr;
231 cos_t *cos;
232 char *buf;
233 size_t bufsize = 0;
234 size_t packedsize = 0;
235 uint_t nvl_arr_sz;
236 size_t num_classes = 0;
237 int i;
238
239 if (list_is_empty(&spa->spa_cos_list))
240 goto empty;
241
242 (void) spa_foreach_cos(spa, cos_count, &num_classes);
243 nvl_arr_sz = num_classes * sizeof (void *);
244
245 nvl_arr = kmem_alloc(nvl_arr_sz, KM_SLEEP);
246
247 VERIFY(0 == nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP));
248
249 for (i = 0, cos = list_head(&spa->spa_cos_list); cos != NULL;
250 cos = list_next(&spa->spa_cos_list, cos), i++) {
251 const char *propname;
252
253 VERIFY(0 == nvlist_alloc(&nvl_arr[i], NV_UNIQUE_NAME,
254 KM_SLEEP));
255
256 propname = cos_prop_to_name(COS_PROP_GUID);
257 VERIFY(0 == nvlist_add_uint64(nvl_arr[i], propname,
258 cos->cos_guid));
259 propname = cos_prop_to_name(COS_PROP_NAME);
260 VERIFY(0 == nvlist_add_string(nvl_arr[i], propname,
261 cos->cos_name));
262 propname = cos_prop_to_name(COS_PROP_PREFERRED_READ);
263 VERIFY(0 == nvlist_add_uint64(nvl_arr[i], propname,
264 cos->cos_preferred_read));
265
266 for (zio_priority_t p = ZIO_PRIORITY_SYNC_READ;
267 p < ZIO_PRIORITY_NUM_QUEUEABLE; p++) {
268 uint64_t val = cos->cos_min_active[p];
269 int prop_id = COS_ZIO_PRIO_TO_PROP_MIN(p);
270
271 ASSERT(COS_PROP_MIN_VALID(prop_id));
272 propname = cos_prop_to_name(prop_id);
273 VERIFY(0 == nvlist_add_uint64(nvl_arr[i],
274 propname, val));
275
276 val = cos->cos_max_active[p];
277 prop_id = COS_ZIO_PRIO_TO_PROP_MAX(p);
278 ASSERT(COS_PROP_MAX_VALID(prop_id));
279 propname = cos_prop_to_name(prop_id);
280
281 VERIFY(0 == nvlist_add_uint64(nvl_arr[i],
282 propname, val));
283 }
284 }
285
286 VERIFY(0 == nvlist_add_nvlist_array(nvl, COS_ARRAY,
287 nvl_arr, num_classes));
288 VERIFY(0 == nvlist_size(nvl, &packedsize, NV_ENCODE_XDR));
289
290 bufsize = P2ROUNDUP((uint64_t)packedsize, SPA_MINBLOCKSIZE);
291 buf = kmem_alloc(bufsize, KM_SLEEP);
292
293 bzero(buf + packedsize, bufsize - packedsize);
294
295 VERIFY(0 == nvlist_pack(nvl, &buf, &packedsize, NV_ENCODE_XDR,
296 KM_SLEEP));
297
298 dmu_write(spa->spa_meta_objset, obj, 0, bufsize, buf, tx);
299
300 kmem_free(buf, bufsize);
301 for (i = 0; i < num_classes; i++)
302 nvlist_free(nvl_arr[i]);
303 kmem_free(nvl_arr, nvl_arr_sz);
304 nvlist_free(nvl);
305
306 empty:
307 VERIFY(0 == dmu_bonus_hold(spa->spa_meta_objset, obj, FTAG, &db));
308 dmu_buf_will_dirty(db, tx);
309 *(uint64_t *)db->db_data = (uint64_t)packedsize;
310 dmu_buf_rele(db, FTAG);
311 }
312
313 typedef enum {
314 COS_FEATURE_NONE,
315 COS_FEATURE_INCR,
316 COS_FEATURE_DECR
317 } cos_feature_action_t;
318
319 typedef struct {
320 spa_t *spa;
321 cos_feature_action_t action;
322 } cos_sync_arg_t;
323
324 static void
325 cos_sync_props(void *arg1, dmu_tx_t *tx)
326 {
327 cos_sync_arg_t *arg = (cos_sync_arg_t *)arg1;
328 spa_t *spa = arg->spa;
329 objset_t *mos = arg->spa->spa_meta_objset;
330
331 switch (arg->action) {
332 case COS_FEATURE_INCR:
333 spa_feature_incr(spa, SPA_FEATURE_COS_PROPS, tx);
334 break;
335 case COS_FEATURE_DECR:
336 spa_feature_decr(spa, SPA_FEATURE_COS_PROPS, tx);
337 break;
338 case COS_FEATURE_NONE:
339 default:
340 break;
341 }
342
343 spa_cos_enter(spa);
344
345 if (spa->spa_cos_props_object == 0) {
346 VERIFY((spa->spa_cos_props_object =
347 dmu_object_alloc(mos, DMU_OT_PACKED_NVLIST, 0,
348 DMU_OT_PACKED_NVLIST_SIZE, 8, tx)) > 0);
349
350 VERIFY(zap_update(mos,
351 DMU_POOL_DIRECTORY_OBJECT,
352 DMU_POOL_COS_PROPS,
353 8, 1, &spa->spa_cos_props_object, tx) == 0);
354 }
355 cos_sync_classes(spa, spa->spa_cos_props_object, tx);
356
357 spa_cos_exit(spa);
358
359 /* done with the argument */
360 kmem_free(arg, sizeof (cos_sync_arg_t));
361 }
362
363 int
364 spa_load_cos_props(spa_t *spa)
365 {
366 objset_t *mos = spa->spa_meta_objset;
367 dmu_buf_t *db;
368 nvlist_t *nvl = NULL;
369 nvlist_t **nvl_arr;
370 uint64_t packedsize;
371 void *buf = NULL;
372 uint_t n;
373 int i;
374
375 if (spa->spa_cos_props_object == 0)
376 return (SET_ERROR(ENOENT));
377
378 spa_cos_enter(spa);
379
380 VERIFY(0 == dmu_bonus_hold(mos, spa->spa_cos_props_object, FTAG, &db));
381 packedsize = *(uint64_t *)db->db_data;
382 dmu_buf_rele(db, FTAG);
383
384 if (packedsize == 0) {
385 spa_cos_exit(spa);
386 return (0);
387 }
388
389 buf = kmem_alloc(packedsize, KM_SLEEP);
390
391 VERIFY(0 == dmu_read(mos, spa->spa_cos_props_object,
392 0, packedsize, buf, DMU_READ_NO_PREFETCH));
393
394 VERIFY(0 == nvlist_unpack(buf, packedsize, &nvl, 0));
395 VERIFY(0 == nvlist_lookup_nvlist_array(nvl, COS_ARRAY,
396 &nvl_arr, &n));
397
398 for (i = 0; i < n; i++) {
399 cos_t *cos = NULL;
400 char *strval;
401 uint64_t u64;
402 const char *propname;
403
404 propname = cos_prop_to_name(COS_PROP_GUID);
405 if (nvlist_lookup_uint64(nvl_arr[i], propname, &u64) == 0) {
406 cos = spa_lookup_cos_by_guid(spa, u64);
407 if (cos == NULL) {
408 propname = cos_prop_to_name(COS_PROP_NAME);
409 if (nvlist_lookup_string(nvl_arr[i], propname,
410 &strval) != 0)
411 continue;
412
413 if (spa_alloc_cos_nosync(spa, strval, u64) != 0)
414 continue;
415 cos = spa_lookup_cos_by_name(spa, strval);
416 }
417 }
418
419 if (cos == NULL)
420 continue;
421
422 propname = cos_prop_to_name(COS_PROP_PREFERRED_READ);
423 if (nvlist_lookup_uint64(nvl_arr[i], propname, &u64) == 0)
424 cos->cos_preferred_read = u64;
425
426
427 for (zio_priority_t p = ZIO_PRIORITY_SYNC_READ;
428 p < ZIO_PRIORITY_NUM_QUEUEABLE; p++) {
429 int prop_id = COS_ZIO_PRIO_TO_PROP_MIN(p);
430 const char *pname;
431
432 ASSERT(COS_PROP_MIN_VALID(prop_id));
433 pname = cos_prop_to_name(prop_id);
434
435 if (nvlist_lookup_uint64(nvl_arr[i], pname, &u64) == 0)
436 cos->cos_min_active[p] = u64;
437
438 prop_id = COS_ZIO_PRIO_TO_PROP_MAX(p);
439 ASSERT(COS_PROP_MAX_VALID(prop_id));
440 pname = cos_prop_to_name(prop_id);
441
442 if (nvlist_lookup_uint64(nvl_arr[i], pname, &u64) == 0)
443 cos->cos_max_active[p] = u64;
444 }
445 }
446
447 spa_cos_exit(spa);
448
449 nvlist_free(nvl);
450 kmem_free(buf, packedsize);
451
452 return (0);
453 }
454
455 int
456 cos_prop_validate(spa_t *spa, uint64_t id, nvlist_t *props)
457 {
458 nvpair_t *elem;
459 int error = 0;
460
461 elem = NULL;
462 while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
463 cos_prop_t prop;
464 char *propname;
465 char *strval;
466 uint64_t intval;
467 cos_t *cos;
468
469 propname = nvpair_name(elem);
470
471 if ((prop = cos_name_to_prop(propname)) == ZPROP_INVAL)
472 return (SET_ERROR(EINVAL));
473
474 switch (prop) {
475 case COS_PROP_NAME:
476 error = nvpair_value_string(elem, &strval);
477 if (error)
478 break;
479
480 /*
481 * Cannot exceed max length of cos name
482 * and cannot be same as existing one
483 */
484 if ((strnlen(strval, MAXCOSNAMELEN) == MAXCOSNAMELEN) ||
485 (((cos = spa_lookup_cos_by_name(spa, strval)) !=
486 NULL) && cos->cos_guid != id))
487 error = EINVAL;
488 break;
489 case COS_PROP_PREFERRED_READ:
490 error = nvpair_value_uint64(elem, &intval);
491 if (!error && intval > 100)
492 error = EINVAL;
493 break;
494
495 case COS_PROP_READ_MINACTIVE:
496 case COS_PROP_AREAD_MINACTIVE:
497 case COS_PROP_WRITE_MINACTIVE:
498 case COS_PROP_AWRITE_MINACTIVE:
499 case COS_PROP_SCRUB_MINACTIVE:
500 case COS_PROP_RESILVER_MINACTIVE:
501 case COS_PROP_READ_MAXACTIVE:
502 case COS_PROP_AREAD_MAXACTIVE:
503 case COS_PROP_WRITE_MAXACTIVE:
504 case COS_PROP_AWRITE_MAXACTIVE:
505 case COS_PROP_SCRUB_MAXACTIVE:
506 case COS_PROP_RESILVER_MAXACTIVE:
507 error = nvpair_value_uint64(elem, &intval);
508 if (!error && intval > 1000)
509 error = EINVAL;
510 break;
511
512 default:
513 error = EINVAL;
514 }
515
516 if (error)
517 break;
518 }
519
520 return (error);
521 }
522
523 /* ARGSUSED */
524 int
525 cos_sync_task_do(spa_t *spa, cos_feature_action_t action)
526 {
527 int err = 0;
528 cos_sync_arg_t *arg = kmem_alloc(sizeof (cos_sync_arg_t), KM_SLEEP);
529 /* argument allocated and initialized here and freed in the callback */
530 arg->spa = spa;
531 arg->action = action;
532 err = dsl_sync_task(spa->spa_name, NULL, cos_sync_props, arg, 3,
533 ZFS_SPACE_CHECK_RESERVED);
534 return (err);
535 }
536
537 int
538 spa_cos_prop_set(spa_t *spa, const char *cosname, nvlist_t *nvp)
539 {
540 cos_t *cos;
541 nvpair_t *elem;
542 boolean_t need_sync = B_FALSE;
543 cos_prop_t prop;
544 uint64_t intval;
545 char *strval;
546 zprop_type_t proptype;
547
548 ASSERT(spa_writeable(spa));
549
550 spa_cos_enter(spa);
551 cos = spa_lookup_cos_by_name(spa, cosname);
552 if (cos == NULL) {
553 spa_cos_exit(spa);
554 return (ENOENT);
555 }
556
557 elem = NULL;
558 while ((elem = nvlist_next_nvpair(nvp, elem)) != NULL) {
559 if ((prop = cos_name_to_prop(
560 nvpair_name(elem))) == ZPROP_INVAL)
561 return (EINVAL);
562
563 need_sync = B_TRUE;
564 proptype = cos_prop_get_type(prop);
565
566 if (nvpair_type(elem) == DATA_TYPE_STRING) {
567 ASSERT(proptype == PROP_TYPE_STRING);
568 VERIFY(nvpair_value_string(elem, &strval) == 0);
569
570 } else if (nvpair_type(elem) == DATA_TYPE_UINT64) {
571 VERIFY(nvpair_value_uint64(elem, &intval) == 0);
572
573 if (proptype == PROP_TYPE_INDEX) {
574 const char *unused;
575 VERIFY(cos_prop_index_to_string(
576 prop, intval, &unused) == 0);
577 }
578 } else {
579 ASSERT(0); /* not allowed */
580 }
581
582 (void) cos_set_common(cos, strval, intval, prop);
583 }
584 spa_cos_exit(spa);
585
586 if (need_sync)
587 return (cos_sync_task_do(spa, COS_FEATURE_NONE));
588 return (0);
589 }
590
591 int
592 spa_cos_prop_get(spa_t *spa, const char *cosname, nvlist_t **nvp)
593 {
594 cos_t *cos;
595 int err;
596 cos_prop_t prop;
597
598 VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
599
600 spa_cos_enter(spa);
601 cos = spa_lookup_cos_by_name(spa, cosname);
602 if (cos == NULL) {
603 spa_cos_exit(spa);
604 return (ENOENT);
605 }
606
607 for (prop = COS_PROP_GUID; prop < COS_NUM_PROPS; prop++) {
608 uint64_t ival;
609 char *strval = NULL;
610 const char *propname = cos_prop_to_name(prop);
611
612 if ((err = cos_get_common(cos, &strval, &ival, prop)) != 0)
613 return (err);
614
615 if (strval != NULL) {
616 VERIFY(nvlist_add_string(*nvp, propname, strval) == 0);
617 spa_strfree(strval);
618 } else {
619 VERIFY(nvlist_add_uint64(*nvp, propname, ival) == 0);
620 }
621 }
622 spa_cos_exit(spa);
623
624 return (0);
625 }
626
627 static uint64_t
628 generate_cos_guid(spa_t *spa)
629 {
630 uint64_t guid = 0;
631
632 do {
633 (void) random_get_pseudo_bytes((uint8_t *)&guid, sizeof (guid));
634 } while (guid != 0 && spa_lookup_cos_by_guid(spa, guid) != NULL);
635
636 return (guid);
637 }
638
639 static int
640 spa_alloc_cos_nosync(spa_t *spa, const char *cosname, uint64_t cosguid)
641 {
642 cos_t *cos;
643
644 ASSERT(MUTEX_HELD(&spa->spa_cos_props_lock));
645
646 cos = kmem_zalloc(sizeof (cos_t), KM_SLEEP);
647
648 if (spa_lookup_cos_by_name(spa, cosname) != NULL)
649 return (EEXIST);
650
651 cos->cos_guid = (cosguid != 0 ? cosguid : generate_cos_guid(spa));
652 if (cos->cos_guid == 0)
653 return (ENOSPC);
654
655 cos->cos_spa = spa;
656 (void) strlcpy(cos->cos_name, cosname, MAXCOSNAMELEN);
657
658 list_insert_tail(&spa->spa_cos_list, cos);
659
660 return (0);
661 }
662
663 /*
664 * Note: CoS objects are allocated and freed explicitly. Allocated CoS objects
665 * are placed on the list in the pool, and once they are freed, they are no
666 * longer on the list. As such, they may or may not be referenced by vdevs while
667 * allocated. The reference counting is desined to make sure that CoS objects
668 * that are referenced by some vdevs are not de-allocated.
669 */
670
671 int
672 spa_alloc_cos(spa_t *spa, const char *cosname, uint64_t cosid)
673 {
674 int err;
675
676 if (!spa_feature_is_enabled(spa, SPA_FEATURE_COS_PROPS))
677 return (ENOTSUP);
678
679 spa_cos_enter(spa);
680 err = spa_alloc_cos_nosync(spa, cosname, cosid);
681 spa_cos_exit(spa);
682 if (err)
683 return (err);
684
685 return (cos_sync_task_do(spa, COS_FEATURE_INCR));
686 }
687
688
689 /* force cos_free() implementation */
690 typedef void (*cos_free_cb_t)(vdev_t *, void *);
691
692 static void
693 cos_free_cb(vdev_t *vd, void *arg)
694 {
695 cos_t *cos = (cos_t *)arg;
696
697 ASSERT(MUTEX_HELD(&cos->cos_spa->spa_cos_props_lock));
698 ASSERT(spa_config_held(cos->cos_spa, SCL_STATE_ALL,
699 RW_WRITER) == SCL_STATE_ALL);
700
701 if (!vd->vdev_ops->vdev_op_leaf)
702 return;
703
704 if (vd->vdev_queue.vq_cos == cos) {
705 cos_rele(cos);
706 vd->vdev_queue.vq_cos = NULL;
707 }
708 }
709
710 static void
711 cos_vdev_walk(vdev_t *vd, cos_free_cb_t cb, void *arg)
712 {
713 for (int i = 0; i < vd->vdev_children; i++)
714 cos_vdev_walk(vd->vdev_child[i], cb, arg);
715 cb(vd, arg);
716 }
717
718 int
719 spa_free_cos(spa_t *spa, const char *cosname, boolean_t b_force)
720 {
721 int err = 0;
722 cos_t *cos;
723
724 if (b_force)
725 spa_vdev_state_enter(spa, SCL_ALL);
726
727 spa_cos_enter(spa);
728
729 if ((cos = spa_lookup_cos_by_name(spa, cosname)) != NULL) {
730 if (cos_refcount(cos) == 0) {
731 list_remove(&spa->spa_cos_list, cos);
732 } else {
733 if (b_force == B_TRUE) {
734 int i;
735 /*
736 * walk the device tree and the aux devices,
737 * check cos and remove as needed
738 */
739 cos_vdev_walk(spa->spa_root_vdev, cos_free_cb,
740 (void *)cos);
741 for (i = 0; i < spa->spa_l2cache.sav_count;
742 i++) {
743 vdev_t *vd =
744 spa->spa_l2cache.sav_vdevs[i];
745 cos_free_cb(vd, (void *)cos);
746 }
747 for (i = 0; i < spa->spa_spares.sav_count;
748 i++) {
749 vdev_t *vd =
750 spa->spa_spares.sav_vdevs[i];
751 cos_free_cb(vd, (void *)cos);
752 }
753 list_remove(&spa->spa_cos_list, cos);
754 } else {
755 err = SET_ERROR(EBUSY);
756 }
757 }
758 } else {
759 err = ENOENT;
760 }
761
762 spa_cos_exit(spa);
763
764 if (b_force)
765 (void) spa_vdev_state_exit(spa, NULL, 0);
766
767 if (err == 0) {
768 kmem_free(cos, sizeof (cos_t));
769 if (b_force)
770 return (spa_vdev_props_sync_task_do(spa) &&
771 cos_sync_task_do(spa, COS_FEATURE_DECR));
772 else
773 return (cos_sync_task_do(spa, COS_FEATURE_DECR));
774 }
775
776 return (err);
777 }
778
779 int
780 spa_list_cos(spa_t *spa, nvlist_t *nvl)
781 {
782 cos_t *cos;
783 int err = 0;
784
785 spa_cos_enter(spa);
786 for (cos = list_head(&spa->spa_cos_list); cos != NULL;
787 cos = list_next(&spa->spa_cos_list, cos)) {
788 VERIFY(nvlist_add_uint64(nvl, cos->cos_name,
789 cos->cos_guid) == 0);
790 }
791 spa_cos_exit(spa);
792
793 return (err);
794 }
795
796 void
797 spa_cos_init(spa_t *spa)
798 {
799 spa_cos_enter(spa);
800 list_create(&spa->spa_cos_list, sizeof (cos_t), offsetof(cos_t,
801 cos_list_node));
802 spa_cos_exit(spa);
803 }
804
805 /* ARGSUSED */
806 static boolean_t
807 cos_remove(cos_t *cos, void *data)
808 {
809 spa_t *spa = cos->cos_spa;
810
811 ASSERT(MUTEX_HELD(&spa->spa_cos_props_lock));
812
813 list_remove(&spa->spa_cos_list, cos);
814 kmem_free(cos, sizeof (cos_t));
815
816 return (B_FALSE);
817 }
818
819 void
820 spa_cos_fini(spa_t *spa)
821 {
822 spa_cos_enter(spa);
823 (void) spa_foreach_cos(spa, cos_remove, NULL);
824 list_destroy(&spa->spa_cos_list);
825 spa_cos_exit(spa);
826 }
827
828 uint64_t
829 cos_get_prop_uint64(cos_t *cos, cos_prop_t p)
830 {
831 uint64_t val = 0;
832 zio_priority_t zprio = 0;
833
834 switch (p) {
835 case COS_PROP_READ_MINACTIVE:
836 case COS_PROP_AREAD_MINACTIVE:
837 case COS_PROP_WRITE_MINACTIVE:
838 case COS_PROP_AWRITE_MINACTIVE:
839 case COS_PROP_SCRUB_MINACTIVE:
840 case COS_PROP_RESILVER_MINACTIVE:
841 zprio = COS_PROP_TO_ZIO_PRIO_MIN(p);
842 ASSERT(ZIO_PRIORITY_QUEUEABLE_VALID(zprio));
843 val = cos->cos_min_active[zprio];
844 break;
845 case COS_PROP_READ_MAXACTIVE:
846 case COS_PROP_AREAD_MAXACTIVE:
847 case COS_PROP_WRITE_MAXACTIVE:
848 case COS_PROP_AWRITE_MAXACTIVE:
849 case COS_PROP_SCRUB_MAXACTIVE:
850 case COS_PROP_RESILVER_MAXACTIVE:
851 zprio = COS_PROP_TO_ZIO_PRIO_MAX(p);
852 ASSERT(ZIO_PRIORITY_QUEUEABLE_VALID(zprio));
853 val = cos->cos_max_active[zprio];
854 break;
855 case COS_PROP_PREFERRED_READ:
856 val = cos->cos_preferred_read;
857 break;
858 default:
859 panic("Non-numeric property requested\n");
860 return (0);
861 }
862
863 return (val);
864 }