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