linux/drivers/scsi/iscsi_boot_sysfs.c
<<
>>
Prefs
   1/*
   2 * Export the iSCSI boot info to userland via sysfs.
   3 *
   4 * Copyright (C) 2010 Red Hat, Inc.  All rights reserved.
   5 * Copyright (C) 2010 Mike Christie
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License v2.0 as published by
   9 * the Free Software Foundation
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 */
  16
  17#include <linux/module.h>
  18#include <linux/string.h>
  19#include <linux/slab.h>
  20#include <linux/sysfs.h>
  21#include <linux/capability.h>
  22#include <linux/iscsi_boot_sysfs.h>
  23
  24
  25MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>");
  26MODULE_DESCRIPTION("sysfs interface and helpers to export iSCSI boot information");
  27MODULE_LICENSE("GPL");
  28/*
  29 * The kobject and attribute structures.
  30 */
  31struct iscsi_boot_attr {
  32        struct attribute attr;
  33        int type;
  34        ssize_t (*show) (void *data, int type, char *buf);
  35};
  36
  37/*
  38 * The routine called for all sysfs attributes.
  39 */
  40static ssize_t iscsi_boot_show_attribute(struct kobject *kobj,
  41                                         struct attribute *attr, char *buf)
  42{
  43        struct iscsi_boot_kobj *boot_kobj =
  44                        container_of(kobj, struct iscsi_boot_kobj, kobj);
  45        struct iscsi_boot_attr *boot_attr =
  46                        container_of(attr, struct iscsi_boot_attr, attr);
  47        ssize_t ret = -EIO;
  48        char *str = buf;
  49
  50        if (!capable(CAP_SYS_ADMIN))
  51                return -EACCES;
  52
  53        if (boot_kobj->show)
  54                ret = boot_kobj->show(boot_kobj->data, boot_attr->type, str);
  55        return ret;
  56}
  57
  58static const struct sysfs_ops iscsi_boot_attr_ops = {
  59        .show = iscsi_boot_show_attribute,
  60};
  61
  62static void iscsi_boot_kobj_release(struct kobject *kobj)
  63{
  64        struct iscsi_boot_kobj *boot_kobj =
  65                        container_of(kobj, struct iscsi_boot_kobj, kobj);
  66
  67        if (boot_kobj->release)
  68                boot_kobj->release(boot_kobj->data);
  69        kfree(boot_kobj);
  70}
  71
  72static struct kobj_type iscsi_boot_ktype = {
  73        .release = iscsi_boot_kobj_release,
  74        .sysfs_ops = &iscsi_boot_attr_ops,
  75};
  76
  77#define iscsi_boot_rd_attr(fnname, sysfs_name, attr_type)               \
  78static struct iscsi_boot_attr iscsi_boot_attr_##fnname = {      \
  79        .attr   = { .name = __stringify(sysfs_name), .mode = 0444 },    \
  80        .type   = attr_type,                                            \
  81}
  82
  83/* Target attrs */
  84iscsi_boot_rd_attr(tgt_index, index, ISCSI_BOOT_TGT_INDEX);
  85iscsi_boot_rd_attr(tgt_flags, flags, ISCSI_BOOT_TGT_FLAGS);
  86iscsi_boot_rd_attr(tgt_ip, ip-addr, ISCSI_BOOT_TGT_IP_ADDR);
  87iscsi_boot_rd_attr(tgt_port, port, ISCSI_BOOT_TGT_PORT);
  88iscsi_boot_rd_attr(tgt_lun, lun, ISCSI_BOOT_TGT_LUN);
  89iscsi_boot_rd_attr(tgt_chap, chap-type, ISCSI_BOOT_TGT_CHAP_TYPE);
  90iscsi_boot_rd_attr(tgt_nic, nic-assoc, ISCSI_BOOT_TGT_NIC_ASSOC);
  91iscsi_boot_rd_attr(tgt_name, target-name, ISCSI_BOOT_TGT_NAME);
  92iscsi_boot_rd_attr(tgt_chap_name, chap-name, ISCSI_BOOT_TGT_CHAP_NAME);
  93iscsi_boot_rd_attr(tgt_chap_secret, chap-secret, ISCSI_BOOT_TGT_CHAP_SECRET);
  94iscsi_boot_rd_attr(tgt_chap_rev_name, rev-chap-name,
  95                   ISCSI_BOOT_TGT_REV_CHAP_NAME);
  96iscsi_boot_rd_attr(tgt_chap_rev_secret, rev-chap-name-secret,
  97                   ISCSI_BOOT_TGT_REV_CHAP_SECRET);
  98
  99static struct attribute *target_attrs[] = {
 100        &iscsi_boot_attr_tgt_index.attr,
 101        &iscsi_boot_attr_tgt_flags.attr,
 102        &iscsi_boot_attr_tgt_ip.attr,
 103        &iscsi_boot_attr_tgt_port.attr,
 104        &iscsi_boot_attr_tgt_lun.attr,
 105        &iscsi_boot_attr_tgt_chap.attr,
 106        &iscsi_boot_attr_tgt_nic.attr,
 107        &iscsi_boot_attr_tgt_name.attr,
 108        &iscsi_boot_attr_tgt_chap_name.attr,
 109        &iscsi_boot_attr_tgt_chap_secret.attr,
 110        &iscsi_boot_attr_tgt_chap_rev_name.attr,
 111        &iscsi_boot_attr_tgt_chap_rev_secret.attr,
 112        NULL
 113};
 114
 115static umode_t iscsi_boot_tgt_attr_is_visible(struct kobject *kobj,
 116                                             struct attribute *attr, int i)
 117{
 118        struct iscsi_boot_kobj *boot_kobj =
 119                        container_of(kobj, struct iscsi_boot_kobj, kobj);
 120
 121        if (attr ==  &iscsi_boot_attr_tgt_index.attr)
 122                return boot_kobj->is_visible(boot_kobj->data,
 123                                             ISCSI_BOOT_TGT_INDEX);
 124        else if (attr == &iscsi_boot_attr_tgt_flags.attr)
 125                return boot_kobj->is_visible(boot_kobj->data,
 126                                             ISCSI_BOOT_TGT_FLAGS);
 127        else if (attr == &iscsi_boot_attr_tgt_ip.attr)
 128                return boot_kobj->is_visible(boot_kobj->data,
 129                                              ISCSI_BOOT_TGT_IP_ADDR);
 130        else if (attr == &iscsi_boot_attr_tgt_port.attr)
 131                return boot_kobj->is_visible(boot_kobj->data,
 132                                              ISCSI_BOOT_TGT_PORT);
 133        else if (attr == &iscsi_boot_attr_tgt_lun.attr)
 134                return boot_kobj->is_visible(boot_kobj->data,
 135                                              ISCSI_BOOT_TGT_LUN);
 136        else if (attr == &iscsi_boot_attr_tgt_chap.attr)
 137                return boot_kobj->is_visible(boot_kobj->data,
 138                                             ISCSI_BOOT_TGT_CHAP_TYPE);
 139        else if (attr == &iscsi_boot_attr_tgt_nic.attr)
 140                return boot_kobj->is_visible(boot_kobj->data,
 141                                             ISCSI_BOOT_TGT_NIC_ASSOC);
 142        else if (attr == &iscsi_boot_attr_tgt_name.attr)
 143                return boot_kobj->is_visible(boot_kobj->data,
 144                                             ISCSI_BOOT_TGT_NAME);
 145        else if (attr == &iscsi_boot_attr_tgt_chap_name.attr)
 146                return boot_kobj->is_visible(boot_kobj->data,
 147                                             ISCSI_BOOT_TGT_CHAP_NAME);
 148        else if (attr == &iscsi_boot_attr_tgt_chap_secret.attr)
 149                return boot_kobj->is_visible(boot_kobj->data,
 150                                             ISCSI_BOOT_TGT_CHAP_SECRET);
 151        else if (attr == &iscsi_boot_attr_tgt_chap_rev_name.attr)
 152                return boot_kobj->is_visible(boot_kobj->data,
 153                                             ISCSI_BOOT_TGT_REV_CHAP_NAME);
 154        else if (attr == &iscsi_boot_attr_tgt_chap_rev_secret.attr)
 155                return boot_kobj->is_visible(boot_kobj->data,
 156                                             ISCSI_BOOT_TGT_REV_CHAP_SECRET);
 157        return 0;
 158}
 159
 160static struct attribute_group iscsi_boot_target_attr_group = {
 161        .attrs = target_attrs,
 162        .is_visible = iscsi_boot_tgt_attr_is_visible,
 163};
 164
 165/* Ethernet attrs */
 166iscsi_boot_rd_attr(eth_index, index, ISCSI_BOOT_ETH_INDEX);
 167iscsi_boot_rd_attr(eth_flags, flags, ISCSI_BOOT_ETH_FLAGS);
 168iscsi_boot_rd_attr(eth_ip, ip-addr, ISCSI_BOOT_ETH_IP_ADDR);
 169iscsi_boot_rd_attr(eth_subnet, subnet-mask, ISCSI_BOOT_ETH_SUBNET_MASK);
 170iscsi_boot_rd_attr(eth_origin, origin, ISCSI_BOOT_ETH_ORIGIN);
 171iscsi_boot_rd_attr(eth_gateway, gateway, ISCSI_BOOT_ETH_GATEWAY);
 172iscsi_boot_rd_attr(eth_primary_dns, primary-dns, ISCSI_BOOT_ETH_PRIMARY_DNS);
 173iscsi_boot_rd_attr(eth_secondary_dns, secondary-dns,
 174                   ISCSI_BOOT_ETH_SECONDARY_DNS);
 175iscsi_boot_rd_attr(eth_dhcp, dhcp, ISCSI_BOOT_ETH_DHCP);
 176iscsi_boot_rd_attr(eth_vlan, vlan, ISCSI_BOOT_ETH_VLAN);
 177iscsi_boot_rd_attr(eth_mac, mac, ISCSI_BOOT_ETH_MAC);
 178iscsi_boot_rd_attr(eth_hostname, hostname, ISCSI_BOOT_ETH_HOSTNAME);
 179
 180static struct attribute *ethernet_attrs[] = {
 181        &iscsi_boot_attr_eth_index.attr,
 182        &iscsi_boot_attr_eth_flags.attr,
 183        &iscsi_boot_attr_eth_ip.attr,
 184        &iscsi_boot_attr_eth_subnet.attr,
 185        &iscsi_boot_attr_eth_origin.attr,
 186        &iscsi_boot_attr_eth_gateway.attr,
 187        &iscsi_boot_attr_eth_primary_dns.attr,
 188        &iscsi_boot_attr_eth_secondary_dns.attr,
 189        &iscsi_boot_attr_eth_dhcp.attr,
 190        &iscsi_boot_attr_eth_vlan.attr,
 191        &iscsi_boot_attr_eth_mac.attr,
 192        &iscsi_boot_attr_eth_hostname.attr,
 193        NULL
 194};
 195
 196static umode_t iscsi_boot_eth_attr_is_visible(struct kobject *kobj,
 197                                             struct attribute *attr, int i)
 198{
 199        struct iscsi_boot_kobj *boot_kobj =
 200                        container_of(kobj, struct iscsi_boot_kobj, kobj);
 201
 202        if (attr ==  &iscsi_boot_attr_eth_index.attr)
 203                return boot_kobj->is_visible(boot_kobj->data,
 204                                             ISCSI_BOOT_ETH_INDEX);
 205        else if (attr ==  &iscsi_boot_attr_eth_flags.attr)
 206                return boot_kobj->is_visible(boot_kobj->data,
 207                                             ISCSI_BOOT_ETH_FLAGS);
 208        else if (attr ==  &iscsi_boot_attr_eth_ip.attr)
 209                return boot_kobj->is_visible(boot_kobj->data,
 210                                             ISCSI_BOOT_ETH_IP_ADDR);
 211        else if (attr ==  &iscsi_boot_attr_eth_subnet.attr)
 212                return boot_kobj->is_visible(boot_kobj->data,
 213                                             ISCSI_BOOT_ETH_SUBNET_MASK);
 214        else if (attr ==  &iscsi_boot_attr_eth_origin.attr)
 215                return boot_kobj->is_visible(boot_kobj->data,
 216                                             ISCSI_BOOT_ETH_ORIGIN);
 217        else if (attr ==  &iscsi_boot_attr_eth_gateway.attr)
 218                return boot_kobj->is_visible(boot_kobj->data,
 219                                             ISCSI_BOOT_ETH_GATEWAY);
 220        else if (attr ==  &iscsi_boot_attr_eth_primary_dns.attr)
 221                return boot_kobj->is_visible(boot_kobj->data,
 222                                             ISCSI_BOOT_ETH_PRIMARY_DNS);
 223        else if (attr ==  &iscsi_boot_attr_eth_secondary_dns.attr)
 224                return boot_kobj->is_visible(boot_kobj->data,
 225                                             ISCSI_BOOT_ETH_SECONDARY_DNS);
 226        else if (attr ==  &iscsi_boot_attr_eth_dhcp.attr)
 227                return boot_kobj->is_visible(boot_kobj->data,
 228                                             ISCSI_BOOT_ETH_DHCP);
 229        else if (attr ==  &iscsi_boot_attr_eth_vlan.attr)
 230                return boot_kobj->is_visible(boot_kobj->data,
 231                                             ISCSI_BOOT_ETH_VLAN);
 232        else if (attr ==  &iscsi_boot_attr_eth_mac.attr)
 233                return boot_kobj->is_visible(boot_kobj->data,
 234                                             ISCSI_BOOT_ETH_MAC);
 235        else if (attr ==  &iscsi_boot_attr_eth_hostname.attr)
 236                return boot_kobj->is_visible(boot_kobj->data,
 237                                             ISCSI_BOOT_ETH_HOSTNAME);
 238        return 0;
 239}
 240
 241static struct attribute_group iscsi_boot_ethernet_attr_group = {
 242        .attrs = ethernet_attrs,
 243        .is_visible = iscsi_boot_eth_attr_is_visible,
 244};
 245
 246/* Initiator attrs */
 247iscsi_boot_rd_attr(ini_index, index, ISCSI_BOOT_INI_INDEX);
 248iscsi_boot_rd_attr(ini_flags, flags, ISCSI_BOOT_INI_FLAGS);
 249iscsi_boot_rd_attr(ini_isns, isns-server, ISCSI_BOOT_INI_ISNS_SERVER);
 250iscsi_boot_rd_attr(ini_slp, slp-server, ISCSI_BOOT_INI_SLP_SERVER);
 251iscsi_boot_rd_attr(ini_primary_radius, pri-radius-server,
 252                   ISCSI_BOOT_INI_PRI_RADIUS_SERVER);
 253iscsi_boot_rd_attr(ini_secondary_radius, sec-radius-server,
 254                   ISCSI_BOOT_INI_SEC_RADIUS_SERVER);
 255iscsi_boot_rd_attr(ini_name, initiator-name, ISCSI_BOOT_INI_INITIATOR_NAME);
 256
 257static struct attribute *initiator_attrs[] = {
 258        &iscsi_boot_attr_ini_index.attr,
 259        &iscsi_boot_attr_ini_flags.attr,
 260        &iscsi_boot_attr_ini_isns.attr,
 261        &iscsi_boot_attr_ini_slp.attr,
 262        &iscsi_boot_attr_ini_primary_radius.attr,
 263        &iscsi_boot_attr_ini_secondary_radius.attr,
 264        &iscsi_boot_attr_ini_name.attr,
 265        NULL
 266};
 267
 268static umode_t iscsi_boot_ini_attr_is_visible(struct kobject *kobj,
 269                                             struct attribute *attr, int i)
 270{
 271        struct iscsi_boot_kobj *boot_kobj =
 272                        container_of(kobj, struct iscsi_boot_kobj, kobj);
 273
 274        if (attr ==  &iscsi_boot_attr_ini_index.attr)
 275                return boot_kobj->is_visible(boot_kobj->data,
 276                                             ISCSI_BOOT_INI_INDEX);
 277        if (attr ==  &iscsi_boot_attr_ini_flags.attr)
 278                return boot_kobj->is_visible(boot_kobj->data,
 279                                             ISCSI_BOOT_INI_FLAGS);
 280        if (attr ==  &iscsi_boot_attr_ini_isns.attr)
 281                return boot_kobj->is_visible(boot_kobj->data,
 282                                             ISCSI_BOOT_INI_ISNS_SERVER);
 283        if (attr ==  &iscsi_boot_attr_ini_slp.attr)
 284                return boot_kobj->is_visible(boot_kobj->data,
 285                                             ISCSI_BOOT_INI_SLP_SERVER);
 286        if (attr ==  &iscsi_boot_attr_ini_primary_radius.attr)
 287                return boot_kobj->is_visible(boot_kobj->data,
 288                                             ISCSI_BOOT_INI_PRI_RADIUS_SERVER);
 289        if (attr ==  &iscsi_boot_attr_ini_secondary_radius.attr)
 290                return boot_kobj->is_visible(boot_kobj->data,
 291                                             ISCSI_BOOT_INI_SEC_RADIUS_SERVER);
 292        if (attr ==  &iscsi_boot_attr_ini_name.attr)
 293                return boot_kobj->is_visible(boot_kobj->data,
 294                                             ISCSI_BOOT_INI_INITIATOR_NAME);
 295
 296        return 0;
 297}
 298
 299static struct attribute_group iscsi_boot_initiator_attr_group = {
 300        .attrs = initiator_attrs,
 301        .is_visible = iscsi_boot_ini_attr_is_visible,
 302};
 303
 304static struct iscsi_boot_kobj *
 305iscsi_boot_create_kobj(struct iscsi_boot_kset *boot_kset,
 306                       struct attribute_group *attr_group,
 307                       const char *name, int index, void *data,
 308                       ssize_t (*show) (void *data, int type, char *buf),
 309                       umode_t (*is_visible) (void *data, int type),
 310                       void (*release) (void *data))
 311{
 312        struct iscsi_boot_kobj *boot_kobj;
 313
 314        boot_kobj = kzalloc(sizeof(*boot_kobj), GFP_KERNEL);
 315        if (!boot_kobj)
 316                return NULL;
 317        INIT_LIST_HEAD(&boot_kobj->list);
 318
 319        boot_kobj->kobj.kset = boot_kset->kset;
 320        if (kobject_init_and_add(&boot_kobj->kobj, &iscsi_boot_ktype,
 321                                 NULL, name, index)) {
 322                kfree(boot_kobj);
 323                return NULL;
 324        }
 325        boot_kobj->data = data;
 326        boot_kobj->show = show;
 327        boot_kobj->is_visible = is_visible;
 328        boot_kobj->release = release;
 329
 330        if (sysfs_create_group(&boot_kobj->kobj, attr_group)) {
 331                /*
 332                 * We do not want to free this because the caller
 333                 * will assume that since the creation call failed
 334                 * the boot kobj was not setup and the normal release
 335                 * path is not being run.
 336                 */
 337                boot_kobj->release = NULL;
 338                kobject_put(&boot_kobj->kobj);
 339                return NULL;
 340        }
 341        boot_kobj->attr_group = attr_group;
 342
 343        kobject_uevent(&boot_kobj->kobj, KOBJ_ADD);
 344        /* Nothing broke so lets add it to the list. */
 345        list_add_tail(&boot_kobj->list, &boot_kset->kobj_list);
 346        return boot_kobj;
 347}
 348
 349static void iscsi_boot_remove_kobj(struct iscsi_boot_kobj *boot_kobj)
 350{
 351        list_del(&boot_kobj->list);
 352        sysfs_remove_group(&boot_kobj->kobj, boot_kobj->attr_group);
 353        kobject_put(&boot_kobj->kobj);
 354}
 355
 356/**
 357 * iscsi_boot_create_target() - create boot target sysfs dir
 358 * @boot_kset: boot kset
 359 * @index: the target id
 360 * @data: driver specific data for target
 361 * @show: attr show function
 362 * @is_visible: attr visibility function
 363 * @release: release function
 364 *
 365 * Note: The boot sysfs lib will free the data passed in for the caller
 366 * when all refs to the target kobject have been released.
 367 */
 368struct iscsi_boot_kobj *
 369iscsi_boot_create_target(struct iscsi_boot_kset *boot_kset, int index,
 370                         void *data,
 371                         ssize_t (*show) (void *data, int type, char *buf),
 372                         umode_t (*is_visible) (void *data, int type),
 373                         void (*release) (void *data))
 374{
 375        return iscsi_boot_create_kobj(boot_kset, &iscsi_boot_target_attr_group,
 376                                      "target%d", index, data, show, is_visible,
 377                                      release);
 378}
 379EXPORT_SYMBOL_GPL(iscsi_boot_create_target);
 380
 381/**
 382 * iscsi_boot_create_initiator() - create boot initiator sysfs dir
 383 * @boot_kset: boot kset
 384 * @index: the initiator id
 385 * @data: driver specific data
 386 * @show: attr show function
 387 * @is_visible: attr visibility function
 388 * @release: release function
 389 *
 390 * Note: The boot sysfs lib will free the data passed in for the caller
 391 * when all refs to the initiator kobject have been released.
 392 */
 393struct iscsi_boot_kobj *
 394iscsi_boot_create_initiator(struct iscsi_boot_kset *boot_kset, int index,
 395                            void *data,
 396                            ssize_t (*show) (void *data, int type, char *buf),
 397                            umode_t (*is_visible) (void *data, int type),
 398                            void (*release) (void *data))
 399{
 400        return iscsi_boot_create_kobj(boot_kset,
 401                                      &iscsi_boot_initiator_attr_group,
 402                                      "initiator", index, data, show,
 403                                      is_visible, release);
 404}
 405EXPORT_SYMBOL_GPL(iscsi_boot_create_initiator);
 406
 407/**
 408 * iscsi_boot_create_ethernet() - create boot ethernet sysfs dir
 409 * @boot_kset: boot kset
 410 * @index: the ethernet device id
 411 * @data: driver specific data
 412 * @show: attr show function
 413 * @is_visible: attr visibility function
 414 * @release: release function
 415 *
 416 * Note: The boot sysfs lib will free the data passed in for the caller
 417 * when all refs to the ethernet kobject have been released.
 418 */
 419struct iscsi_boot_kobj *
 420iscsi_boot_create_ethernet(struct iscsi_boot_kset *boot_kset, int index,
 421                           void *data,
 422                           ssize_t (*show) (void *data, int type, char *buf),
 423                           umode_t (*is_visible) (void *data, int type),
 424                           void (*release) (void *data))
 425{
 426        return iscsi_boot_create_kobj(boot_kset,
 427                                      &iscsi_boot_ethernet_attr_group,
 428                                      "ethernet%d", index, data, show,
 429                                      is_visible, release);
 430}
 431EXPORT_SYMBOL_GPL(iscsi_boot_create_ethernet);
 432
 433/**
 434 * iscsi_boot_create_kset() - creates root sysfs tree
 435 * @set_name: name of root dir
 436 */
 437struct iscsi_boot_kset *iscsi_boot_create_kset(const char *set_name)
 438{
 439        struct iscsi_boot_kset *boot_kset;
 440
 441        boot_kset = kzalloc(sizeof(*boot_kset), GFP_KERNEL);
 442        if (!boot_kset)
 443                return NULL;
 444
 445        boot_kset->kset = kset_create_and_add(set_name, NULL, firmware_kobj);
 446        if (!boot_kset->kset) {
 447                kfree(boot_kset);
 448                return NULL;
 449        }
 450
 451        INIT_LIST_HEAD(&boot_kset->kobj_list);
 452        return boot_kset;
 453}
 454EXPORT_SYMBOL_GPL(iscsi_boot_create_kset);
 455
 456/**
 457 * iscsi_boot_create_host_kset() - creates root sysfs tree for a scsi host
 458 * @hostno: host number of scsi host
 459 */
 460struct iscsi_boot_kset *iscsi_boot_create_host_kset(unsigned int hostno)
 461{
 462        struct iscsi_boot_kset *boot_kset;
 463        char *set_name;
 464
 465        set_name = kasprintf(GFP_KERNEL, "iscsi_boot%u", hostno);
 466        if (!set_name)
 467                return NULL;
 468
 469        boot_kset = iscsi_boot_create_kset(set_name);
 470        kfree(set_name);
 471        return boot_kset;
 472}
 473EXPORT_SYMBOL_GPL(iscsi_boot_create_host_kset);
 474
 475/**
 476 * iscsi_boot_destroy_kset() - destroy kset and kobjects under it
 477 * @boot_kset: boot kset
 478 *
 479 * This will remove the kset and kobjects and attrs under it.
 480 */
 481void iscsi_boot_destroy_kset(struct iscsi_boot_kset *boot_kset)
 482{
 483        struct iscsi_boot_kobj *boot_kobj, *tmp_kobj;
 484
 485        if (!boot_kset)
 486                return;
 487
 488        list_for_each_entry_safe(boot_kobj, tmp_kobj,
 489                                 &boot_kset->kobj_list, list)
 490                iscsi_boot_remove_kobj(boot_kobj);
 491
 492        kset_unregister(boot_kset->kset);
 493}
 494EXPORT_SYMBOL_GPL(iscsi_boot_destroy_kset);
 495