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 }