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