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) 2018, 2019 by Delphix. All rights reserved. 23 */ 24 25 #include <sys/types.h> 26 #include <sys/param.h> 27 #include <sys/zfeature.h> 28 #include <sys/zfs_ioctl.h> 29 #include <sys/zfs_sysfs.h> 30 #include <sys/kmem.h> 31 #include <sys/fs/zfs.h> 32 #include <linux/kobject.h> 33 34 #include "zfs_prop.h" 35 36 #if !defined(_KERNEL) 37 #error kernel builds only 38 #endif 39 40 /* 41 * ZFS Module sysfs support 42 * 43 * This extends our sysfs '/sys/module/zfs' entry to include feature 44 * and property attributes. The primary consumer of this information 45 * is user processes, like the zfs CLI, that need to know what the 46 * current loaded ZFS module supports. The libzfs binary will consult 47 * this information when instantiating the zfs|zpool property tables 48 * and the pool features table. 49 * 50 * The added top-level directories are: 51 * /sys/module/zfs 52 * ├── features.kernel 53 * ├── features.pool 54 * ├── properties.dataset 55 * └── properties.pool 56 * 57 * The local interface for the zfs kobjects includes: 58 * zfs_kobj_init() 59 * zfs_kobj_add() 60 * zfs_kobj_release() 61 * zfs_kobj_add_attr() 62 * zfs_kobj_fini() 63 */ 64 65 /* 66 * A zfs_mod_kobj_t represents a zfs kobject under '/sys/module/zfs' 67 */ 68 struct zfs_mod_kobj; 69 typedef struct zfs_mod_kobj zfs_mod_kobj_t; 70 71 struct zfs_mod_kobj { 72 struct kobject zko_kobj; 73 struct kobj_type zko_kobj_type; 74 struct sysfs_ops zko_sysfs_ops; 75 size_t zko_attr_count; 76 struct attribute *zko_attr_list; /* allocated */ 77 struct attribute **zko_default_attrs; /* allocated */ 78 size_t zko_child_count; 79 zfs_mod_kobj_t *zko_children; /* allocated */ 80 }; 81 82 #define ATTR_TABLE_SIZE(cnt) (sizeof (struct attribute) * (cnt)) 83 /* Note +1 for NULL terminator slot */ 84 #define DEFAULT_ATTR_SIZE(cnt) (sizeof (struct attribute *) * (cnt + 1)) 85 #define CHILD_TABLE_SIZE(cnt) (sizeof (zfs_mod_kobj_t) * (cnt)) 86 87 /* 88 * These are the top-level kobjects under '/sys/module/zfs/' 89 */ 90 static zfs_mod_kobj_t kernel_features_kobj; 91 static zfs_mod_kobj_t pool_features_kobj; 92 static zfs_mod_kobj_t dataset_props_kobj; 93 static zfs_mod_kobj_t vdev_props_kobj; 94 static zfs_mod_kobj_t pool_props_kobj; 95 96 /* 97 * The show function is used to provide the content 98 * of an attribute into a PAGE_SIZE buffer. 99 */ 100 typedef ssize_t (*sysfs_show_func)(struct kobject *, struct attribute *, 101 char *); 102 103 static void 104 zfs_kobj_fini(zfs_mod_kobj_t *zkobj) 105 { 106 /* finalize any child kobjects */ 107 if (zkobj->zko_child_count != 0) { 108 ASSERT(zkobj->zko_children); 109 for (int i = 0; i < zkobj->zko_child_count; i++) 110 zfs_kobj_fini(&zkobj->zko_children[i]); 111 } 112 113 /* kobject_put() will call zfs_kobj_release() to release memory */ 114 kobject_del(&zkobj->zko_kobj); 115 kobject_put(&zkobj->zko_kobj); 116 } 117 118 static void 119 zfs_kobj_release(struct kobject *kobj) 120 { 121 zfs_mod_kobj_t *zkobj = container_of(kobj, zfs_mod_kobj_t, zko_kobj); 122 123 if (zkobj->zko_attr_list != NULL) { 124 ASSERT3S(zkobj->zko_attr_count, !=, 0); 125 kmem_free(zkobj->zko_attr_list, 126 ATTR_TABLE_SIZE(zkobj->zko_attr_count)); 127 zkobj->zko_attr_list = NULL; 128 } 129 130 if (zkobj->zko_default_attrs != NULL) { 131 kmem_free(zkobj->zko_default_attrs, 132 DEFAULT_ATTR_SIZE(zkobj->zko_attr_count)); 133 zkobj->zko_default_attrs = NULL; 134 } 135 136 if (zkobj->zko_child_count != 0) { 137 ASSERT(zkobj->zko_children); 138 139 kmem_free(zkobj->zko_children, 140 CHILD_TABLE_SIZE(zkobj->zko_child_count)); 141 zkobj->zko_child_count = 0; 142 zkobj->zko_children = NULL; 143 } 144 145 zkobj->zko_attr_count = 0; 146 } 147 148 #ifndef sysfs_attr_init 149 #define sysfs_attr_init(attr) do {} while (0) 150 #endif 151 152 static void 153 zfs_kobj_add_attr(zfs_mod_kobj_t *zkobj, int attr_num, const char *attr_name) 154 { 155 VERIFY3U(attr_num, <, zkobj->zko_attr_count); 156 ASSERT(zkobj->zko_attr_list); 157 ASSERT(zkobj->zko_default_attrs); 158 159 zkobj->zko_attr_list[attr_num].name = attr_name; 160 zkobj->zko_attr_list[attr_num].mode = 0444; 161 zkobj->zko_default_attrs[attr_num] = &zkobj->zko_attr_list[attr_num]; 162 sysfs_attr_init(&zkobj->zko_attr_list[attr_num]); 163 } 164 165 static int 166 zfs_kobj_init(zfs_mod_kobj_t *zkobj, int attr_cnt, int child_cnt, 167 sysfs_show_func show_func) 168 { 169 /* 170 * Initialize object's attributes. Count can be zero. 171 */ 172 if (attr_cnt > 0) { 173 zkobj->zko_attr_list = kmem_zalloc(ATTR_TABLE_SIZE(attr_cnt), 174 KM_SLEEP); 175 if (zkobj->zko_attr_list == NULL) 176 return (ENOMEM); 177 } 178 /* this will always have at least one slot for NULL termination */ 179 zkobj->zko_default_attrs = kmem_zalloc(DEFAULT_ATTR_SIZE(attr_cnt), 180 KM_SLEEP); 181 if (zkobj->zko_default_attrs == NULL) { 182 if (zkobj->zko_attr_list != NULL) { 183 kmem_free(zkobj->zko_attr_list, 184 ATTR_TABLE_SIZE(attr_cnt)); 185 } 186 return (ENOMEM); 187 } 188 zkobj->zko_attr_count = attr_cnt; 189 zkobj->zko_kobj_type.default_attrs = zkobj->zko_default_attrs; 190 191 if (child_cnt > 0) { 192 zkobj->zko_children = kmem_zalloc(CHILD_TABLE_SIZE(child_cnt), 193 KM_SLEEP); 194 if (zkobj->zko_children == NULL) { 195 if (zkobj->zko_default_attrs != NULL) { 196 kmem_free(zkobj->zko_default_attrs, 197 DEFAULT_ATTR_SIZE(attr_cnt)); 198 } 199 if (zkobj->zko_attr_list != NULL) { 200 kmem_free(zkobj->zko_attr_list, 201 ATTR_TABLE_SIZE(attr_cnt)); 202 } 203 return (ENOMEM); 204 } 205 zkobj->zko_child_count = child_cnt; 206 } 207 208 zkobj->zko_sysfs_ops.show = show_func; 209 zkobj->zko_kobj_type.sysfs_ops = &zkobj->zko_sysfs_ops; 210 zkobj->zko_kobj_type.release = zfs_kobj_release; 211 212 return (0); 213 } 214 215 static int 216 zfs_kobj_add(zfs_mod_kobj_t *zkobj, struct kobject *parent, const char *name) 217 { 218 /* zko_default_attrs must be NULL terminated */ 219 ASSERT(zkobj->zko_default_attrs != NULL); 220 ASSERT(zkobj->zko_default_attrs[zkobj->zko_attr_count] == NULL); 221 222 kobject_init(&zkobj->zko_kobj, &zkobj->zko_kobj_type); 223 return (kobject_add(&zkobj->zko_kobj, parent, name)); 224 } 225 226 /* 227 * Each zfs property has these common attributes 228 */ 229 static const char *zprop_attrs[] = { 230 "type", 231 "readonly", 232 "setonce", 233 "visible", 234 "values", 235 "default", 236 "datasets" /* zfs properties only */ 237 }; 238 239 #define ZFS_PROP_ATTR_COUNT ARRAY_SIZE(zprop_attrs) 240 #define ZPOOL_PROP_ATTR_COUNT (ZFS_PROP_ATTR_COUNT - 1) 241 242 static const char *zprop_types[] = { 243 "number", 244 "string", 245 "index", 246 }; 247 248 typedef struct zfs_type_map { 249 zfs_type_t ztm_type; 250 const char *ztm_name; 251 } zfs_type_map_t; 252 253 static zfs_type_map_t type_map[] = { 254 {ZFS_TYPE_FILESYSTEM, "filesystem"}, 255 {ZFS_TYPE_SNAPSHOT, "snapshot"}, 256 {ZFS_TYPE_VOLUME, "volume"}, 257 {ZFS_TYPE_BOOKMARK, "bookmark"} 258 }; 259 260 /* 261 * Show the content for a zfs property attribute 262 */ 263 static ssize_t 264 zprop_sysfs_show(const char *attr_name, const zprop_desc_t *property, 265 char *buf, size_t buflen) 266 { 267 const char *show_str; 268 char number[32]; 269 270 /* For dataset properties list the dataset types that apply */ 271 if (strcmp(attr_name, "datasets") == 0 && 272 property->pd_types != ZFS_TYPE_POOL) { 273 int len = 0; 274 275 for (int i = 0; i < ARRAY_SIZE(type_map); i++) { 276 if (type_map[i].ztm_type & property->pd_types) { 277 len += snprintf(buf + len, buflen - len, "%s ", 278 type_map[i].ztm_name); 279 } 280 } 281 len += snprintf(buf + len, buflen - len, "\n"); 282 return (len); 283 } 284 285 if (strcmp(attr_name, "type") == 0) { 286 show_str = zprop_types[property->pd_proptype]; 287 } else if (strcmp(attr_name, "readonly") == 0) { 288 show_str = property->pd_attr == PROP_READONLY ? "1" : "0"; 289 } else if (strcmp(attr_name, "setonce") == 0) { 290 show_str = property->pd_attr == PROP_ONETIME ? "1" : "0"; 291 } else if (strcmp(attr_name, "visible") == 0) { 292 show_str = property->pd_visible ? "1" : "0"; 293 } else if (strcmp(attr_name, "values") == 0) { 294 show_str = property->pd_values ? property->pd_values : ""; 295 } else if (strcmp(attr_name, "default") == 0) { 296 switch (property->pd_proptype) { 297 case PROP_TYPE_NUMBER: 298 (void) snprintf(number, sizeof (number), "%llu", 299 (u_longlong_t)property->pd_numdefault); 300 show_str = number; 301 break; 302 case PROP_TYPE_STRING: 303 show_str = property->pd_strdefault ? 304 property->pd_strdefault : ""; 305 break; 306 case PROP_TYPE_INDEX: 307 if (zprop_index_to_string(property->pd_propnum, 308 property->pd_numdefault, &show_str, 309 property->pd_types) != 0) { 310 show_str = ""; 311 } 312 break; 313 default: 314 return (0); 315 } 316 } else { 317 return (0); 318 } 319 320 return (snprintf(buf, buflen, "%s\n", show_str)); 321 } 322 323 static ssize_t 324 dataset_property_show(struct kobject *kobj, struct attribute *attr, char *buf) 325 { 326 zfs_prop_t prop = zfs_name_to_prop(kobject_name(kobj)); 327 zprop_desc_t *prop_tbl = zfs_prop_get_table(); 328 ssize_t len; 329 330 ASSERT3U(prop, <, ZFS_NUM_PROPS); 331 332 len = zprop_sysfs_show(attr->name, &prop_tbl[prop], buf, PAGE_SIZE); 333 334 return (len); 335 } 336 337 static ssize_t 338 vdev_property_show(struct kobject *kobj, struct attribute *attr, char *buf) 339 { 340 vdev_prop_t prop = vdev_name_to_prop(kobject_name(kobj)); 341 zprop_desc_t *prop_tbl = vdev_prop_get_table(); 342 ssize_t len; 343 344 ASSERT3U(prop, <, VDEV_NUM_PROPS); 345 346 len = zprop_sysfs_show(attr->name, &prop_tbl[prop], buf, PAGE_SIZE); 347 348 return (len); 349 } 350 351 static ssize_t 352 pool_property_show(struct kobject *kobj, struct attribute *attr, char *buf) 353 { 354 zpool_prop_t prop = zpool_name_to_prop(kobject_name(kobj)); 355 zprop_desc_t *prop_tbl = zpool_prop_get_table(); 356 ssize_t len; 357 358 ASSERT3U(prop, <, ZPOOL_NUM_PROPS); 359 360 len = zprop_sysfs_show(attr->name, &prop_tbl[prop], buf, PAGE_SIZE); 361 362 return (len); 363 } 364 365 /* 366 * ZFS kernel feature attributes for '/sys/module/zfs/features.kernel' 367 * 368 * This list is intended for kernel features that don't have a pool feature 369 * association or that extend existing user kernel interfaces. 370 * 371 * A user process can easily check if the running zfs kernel module 372 * supports the new feature. 373 */ 374 static const char *zfs_kernel_features[] = { 375 /* --> Add new kernel features here */ 376 "com.delphix:vdev_initialize", 377 "org.zfsonlinux:vdev_trim", 378 "org.openzfs:l2arc_persistent", 379 }; 380 381 #define KERNEL_FEATURE_COUNT ARRAY_SIZE(zfs_kernel_features) 382 383 static ssize_t 384 kernel_feature_show(struct kobject *kobj, struct attribute *attr, char *buf) 385 { 386 if (strcmp(attr->name, "supported") == 0) 387 return (snprintf(buf, PAGE_SIZE, "yes\n")); 388 return (0); 389 } 390 391 static void 392 kernel_feature_to_kobj(zfs_mod_kobj_t *parent, int slot, const char *name) 393 { 394 zfs_mod_kobj_t *zfs_kobj = &parent->zko_children[slot]; 395 396 ASSERT3U(slot, <, KERNEL_FEATURE_COUNT); 397 ASSERT(name); 398 399 int err = zfs_kobj_init(zfs_kobj, 1, 0, kernel_feature_show); 400 if (err) 401 return; 402 403 zfs_kobj_add_attr(zfs_kobj, 0, "supported"); 404 405 err = zfs_kobj_add(zfs_kobj, &parent->zko_kobj, name); 406 if (err) 407 zfs_kobj_release(&zfs_kobj->zko_kobj); 408 } 409 410 static int 411 zfs_kernel_features_init(zfs_mod_kobj_t *zfs_kobj, struct kobject *parent) 412 { 413 /* 414 * Create a parent kobject to host kernel features. 415 * 416 * '/sys/module/zfs/features.kernel' 417 */ 418 int err = zfs_kobj_init(zfs_kobj, 0, KERNEL_FEATURE_COUNT, 419 kernel_feature_show); 420 if (err) 421 return (err); 422 err = zfs_kobj_add(zfs_kobj, parent, ZFS_SYSFS_KERNEL_FEATURES); 423 if (err) { 424 zfs_kobj_release(&zfs_kobj->zko_kobj); 425 return (err); 426 } 427 428 /* 429 * Now create a kobject for each feature. 430 * 431 * '/sys/module/zfs/features.kernel/<feature>' 432 */ 433 for (int f = 0; f < KERNEL_FEATURE_COUNT; f++) 434 kernel_feature_to_kobj(zfs_kobj, f, zfs_kernel_features[f]); 435 436 return (0); 437 } 438 439 /* 440 * Each pool feature has these common attributes 441 */ 442 static const char *pool_feature_attrs[] = { 443 "description", 444 "guid", 445 "uname", 446 "readonly_compatible", 447 "required_for_mos", 448 "activate_on_enable", 449 "per_dataset" 450 }; 451 452 #define ZPOOL_FEATURE_ATTR_COUNT ARRAY_SIZE(pool_feature_attrs) 453 454 /* 455 * Show the content for the given zfs pool feature attribute 456 */ 457 static ssize_t 458 pool_feature_show(struct kobject *kobj, struct attribute *attr, char *buf) 459 { 460 spa_feature_t fid; 461 462 if (zfeature_lookup_guid(kobject_name(kobj), &fid) != 0) 463 return (0); 464 465 ASSERT3U(fid, <, SPA_FEATURES); 466 467 zfeature_flags_t flags = spa_feature_table[fid].fi_flags; 468 const char *show_str = NULL; 469 470 if (strcmp(attr->name, "description") == 0) { 471 show_str = spa_feature_table[fid].fi_desc; 472 } else if (strcmp(attr->name, "guid") == 0) { 473 show_str = spa_feature_table[fid].fi_guid; 474 } else if (strcmp(attr->name, "uname") == 0) { 475 show_str = spa_feature_table[fid].fi_uname; 476 } else if (strcmp(attr->name, "readonly_compatible") == 0) { 477 show_str = flags & ZFEATURE_FLAG_READONLY_COMPAT ? "1" : "0"; 478 } else if (strcmp(attr->name, "required_for_mos") == 0) { 479 show_str = flags & ZFEATURE_FLAG_MOS ? "1" : "0"; 480 } else if (strcmp(attr->name, "activate_on_enable") == 0) { 481 show_str = flags & ZFEATURE_FLAG_ACTIVATE_ON_ENABLE ? "1" : "0"; 482 } else if (strcmp(attr->name, "per_dataset") == 0) { 483 show_str = flags & ZFEATURE_FLAG_PER_DATASET ? "1" : "0"; 484 } 485 if (show_str == NULL) 486 return (0); 487 488 return (snprintf(buf, PAGE_SIZE, "%s\n", show_str)); 489 } 490 491 static void 492 pool_feature_to_kobj(zfs_mod_kobj_t *parent, spa_feature_t fid, 493 const char *name) 494 { 495 zfs_mod_kobj_t *zfs_kobj = &parent->zko_children[fid]; 496 497 ASSERT3U(fid, <, SPA_FEATURES); 498 ASSERT(name); 499 500 int err = zfs_kobj_init(zfs_kobj, ZPOOL_FEATURE_ATTR_COUNT, 0, 501 pool_feature_show); 502 if (err) 503 return; 504 505 for (int i = 0; i < ZPOOL_FEATURE_ATTR_COUNT; i++) 506 zfs_kobj_add_attr(zfs_kobj, i, pool_feature_attrs[i]); 507 508 err = zfs_kobj_add(zfs_kobj, &parent->zko_kobj, name); 509 if (err) 510 zfs_kobj_release(&zfs_kobj->zko_kobj); 511 } 512 513 static int 514 zfs_pool_features_init(zfs_mod_kobj_t *zfs_kobj, struct kobject *parent) 515 { 516 /* 517 * Create a parent kobject to host pool features. 518 * 519 * '/sys/module/zfs/features.pool' 520 */ 521 int err = zfs_kobj_init(zfs_kobj, 0, SPA_FEATURES, pool_feature_show); 522 if (err) 523 return (err); 524 err = zfs_kobj_add(zfs_kobj, parent, ZFS_SYSFS_POOL_FEATURES); 525 if (err) { 526 zfs_kobj_release(&zfs_kobj->zko_kobj); 527 return (err); 528 } 529 530 /* 531 * Now create a kobject for each feature. 532 * 533 * '/sys/module/zfs/features.pool/<feature>' 534 */ 535 for (spa_feature_t i = 0; i < SPA_FEATURES; i++) 536 pool_feature_to_kobj(zfs_kobj, i, spa_feature_table[i].fi_guid); 537 538 return (0); 539 } 540 541 typedef struct prop_to_kobj_arg { 542 zprop_desc_t *p2k_table; 543 zfs_mod_kobj_t *p2k_parent; 544 sysfs_show_func p2k_show_func; 545 int p2k_attr_count; 546 } prop_to_kobj_arg_t; 547 548 static int 549 zprop_to_kobj(int prop, void *args) 550 { 551 prop_to_kobj_arg_t *data = args; 552 zfs_mod_kobj_t *parent = data->p2k_parent; 553 zfs_mod_kobj_t *zfs_kobj = &parent->zko_children[prop]; 554 const char *name = data->p2k_table[prop].pd_name; 555 int err; 556 557 ASSERT(name); 558 559 err = zfs_kobj_init(zfs_kobj, data->p2k_attr_count, 0, 560 data->p2k_show_func); 561 if (err) 562 return (ZPROP_CONT); 563 564 for (int i = 0; i < data->p2k_attr_count; i++) 565 zfs_kobj_add_attr(zfs_kobj, i, zprop_attrs[i]); 566 567 err = zfs_kobj_add(zfs_kobj, &parent->zko_kobj, name); 568 if (err) 569 zfs_kobj_release(&zfs_kobj->zko_kobj); 570 571 return (ZPROP_CONT); 572 } 573 574 static int 575 zfs_sysfs_properties_init(zfs_mod_kobj_t *zfs_kobj, struct kobject *parent, 576 zfs_type_t type) 577 { 578 prop_to_kobj_arg_t context; 579 const char *name; 580 int err; 581 582 /* 583 * Create a parent kobject to host properties. 584 * 585 * '/sys/module/zfs/properties.<type>' 586 */ 587 if (type == ZFS_TYPE_POOL) { 588 name = ZFS_SYSFS_POOL_PROPERTIES; 589 context.p2k_table = zpool_prop_get_table(); 590 context.p2k_attr_count = ZPOOL_PROP_ATTR_COUNT; 591 context.p2k_parent = zfs_kobj; 592 context.p2k_show_func = pool_property_show; 593 err = zfs_kobj_init(zfs_kobj, 0, ZPOOL_NUM_PROPS, 594 pool_property_show); 595 } else if (type == ZFS_TYPE_VDEV) { 596 name = ZFS_SYSFS_VDEV_PROPERTIES; 597 context.p2k_table = vdev_prop_get_table(); 598 context.p2k_attr_count = ZPOOL_PROP_ATTR_COUNT; 599 context.p2k_parent = zfs_kobj; 600 context.p2k_show_func = vdev_property_show; 601 err = zfs_kobj_init(zfs_kobj, 0, VDEV_NUM_PROPS, 602 vdev_property_show); 603 } else { 604 name = ZFS_SYSFS_DATASET_PROPERTIES; 605 context.p2k_table = zfs_prop_get_table(); 606 context.p2k_attr_count = ZFS_PROP_ATTR_COUNT; 607 context.p2k_parent = zfs_kobj; 608 context.p2k_show_func = dataset_property_show; 609 err = zfs_kobj_init(zfs_kobj, 0, ZFS_NUM_PROPS, 610 dataset_property_show); 611 } 612 613 if (err) 614 return (err); 615 616 err = zfs_kobj_add(zfs_kobj, parent, name); 617 if (err) { 618 zfs_kobj_release(&zfs_kobj->zko_kobj); 619 return (err); 620 } 621 622 /* 623 * Create a kobject for each property. 624 * 625 * '/sys/module/zfs/properties.<type>/<property>' 626 */ 627 (void) zprop_iter_common(zprop_to_kobj, &context, B_TRUE, 628 B_FALSE, type); 629 630 return (err); 631 } 632 633 void 634 zfs_sysfs_init(void) 635 { 636 struct kobject *parent; 637 #if defined(CONFIG_ZFS) && !defined(CONFIG_ZFS_MODULE) 638 parent = kobject_create_and_add("zfs", fs_kobj); 639 #else 640 parent = &(((struct module *)(THIS_MODULE))->mkobj).kobj; 641 #endif 642 int err; 643 644 if (parent == NULL) 645 return; 646 647 err = zfs_kernel_features_init(&kernel_features_kobj, parent); 648 if (err) 649 return; 650 651 err = zfs_pool_features_init(&pool_features_kobj, parent); 652 if (err) { 653 zfs_kobj_fini(&kernel_features_kobj); 654 return; 655 } 656 657 err = zfs_sysfs_properties_init(&pool_props_kobj, parent, 658 ZFS_TYPE_POOL); 659 if (err) { 660 zfs_kobj_fini(&kernel_features_kobj); 661 zfs_kobj_fini(&pool_features_kobj); 662 return; 663 } 664 665 err = zfs_sysfs_properties_init(&vdev_props_kobj, parent, 666 ZFS_TYPE_VDEV); 667 if (err) { 668 zfs_kobj_fini(&kernel_features_kobj); 669 zfs_kobj_fini(&pool_features_kobj); 670 zfs_kobj_fini(&pool_props_kobj); 671 return; 672 } 673 674 err = zfs_sysfs_properties_init(&dataset_props_kobj, parent, 675 ZFS_TYPE_FILESYSTEM); 676 if (err) { 677 zfs_kobj_fini(&kernel_features_kobj); 678 zfs_kobj_fini(&pool_features_kobj); 679 zfs_kobj_fini(&pool_props_kobj); 680 zfs_kobj_fini(&vdev_props_kobj); 681 return; 682 } 683 } 684 685 void 686 zfs_sysfs_fini(void) 687 { 688 /* 689 * Remove top-level kobjects; each will remove any children kobjects 690 */ 691 zfs_kobj_fini(&kernel_features_kobj); 692 zfs_kobj_fini(&pool_features_kobj); 693 zfs_kobj_fini(&pool_props_kobj); 694 zfs_kobj_fini(&vdev_props_kobj); 695 zfs_kobj_fini(&dataset_props_kobj); 696 } 697