obj-$(CONFIG_NVME_TCP)                 += nvme-tcp.o
 obj-$(CONFIG_NVME_APPLE)               += nvme-apple.o
 
-nvme-core-y                            += core.o ioctl.o
+nvme-core-y                            += core.o ioctl.o sysfs.o
 nvme-core-$(CONFIG_NVME_VERBOSE_ERRORS)        += constants.o
 nvme-core-$(CONFIG_TRACING)            += trace.o
 nvme-core-$(CONFIG_NVME_MULTIPATH)     += multipath.o
 
 }
 EXPORT_SYMBOL_GPL(nvme_delete_ctrl);
 
-static void nvme_delete_ctrl_sync(struct nvme_ctrl *ctrl)
+void nvme_delete_ctrl_sync(struct nvme_ctrl *ctrl)
 {
        /*
         * Keep a reference until nvme_do_delete_ctrl() complete,
 #define nvme_report_zones      NULL
 #endif /* CONFIG_BLK_DEV_ZONED */
 
-static const struct block_device_operations nvme_bdev_ops = {
+const struct block_device_operations nvme_bdev_ops = {
        .owner          = THIS_MODULE,
        .ioctl          = nvme_ioctl,
        .compat_ioctl   = blkdev_compat_ptr_ioctl,
        return NULL;
 }
 
-#define SUBSYS_ATTR_RO(_name, _mode, _show)                    \
-       struct device_attribute subsys_attr_##_name = \
-               __ATTR(_name, _mode, _show, NULL)
-
-static ssize_t nvme_subsys_show_nqn(struct device *dev,
-                                   struct device_attribute *attr,
-                                   char *buf)
-{
-       struct nvme_subsystem *subsys =
-               container_of(dev, struct nvme_subsystem, dev);
-
-       return sysfs_emit(buf, "%s\n", subsys->subnqn);
-}
-static SUBSYS_ATTR_RO(subsysnqn, S_IRUGO, nvme_subsys_show_nqn);
-
-static ssize_t nvme_subsys_show_type(struct device *dev,
-                                   struct device_attribute *attr,
-                                   char *buf)
-{
-       struct nvme_subsystem *subsys =
-               container_of(dev, struct nvme_subsystem, dev);
-
-       switch (subsys->subtype) {
-       case NVME_NQN_DISC:
-               return sysfs_emit(buf, "discovery\n");
-       case NVME_NQN_NVME:
-               return sysfs_emit(buf, "nvm\n");
-       default:
-               return sysfs_emit(buf, "reserved\n");
-       }
-}
-static SUBSYS_ATTR_RO(subsystype, S_IRUGO, nvme_subsys_show_type);
-
-#define nvme_subsys_show_str_function(field)                           \
-static ssize_t subsys_##field##_show(struct device *dev,               \
-                           struct device_attribute *attr, char *buf)   \
-{                                                                      \
-       struct nvme_subsystem *subsys =                                 \
-               container_of(dev, struct nvme_subsystem, dev);          \
-       return sysfs_emit(buf, "%.*s\n",                                \
-                          (int)sizeof(subsys->field), subsys->field);  \
-}                                                                      \
-static SUBSYS_ATTR_RO(field, S_IRUGO, subsys_##field##_show);
-
-nvme_subsys_show_str_function(model);
-nvme_subsys_show_str_function(serial);
-nvme_subsys_show_str_function(firmware_rev);
-
-static struct attribute *nvme_subsys_attrs[] = {
-       &subsys_attr_model.attr,
-       &subsys_attr_serial.attr,
-       &subsys_attr_firmware_rev.attr,
-       &subsys_attr_subsysnqn.attr,
-       &subsys_attr_subsystype.attr,
-#ifdef CONFIG_NVME_MULTIPATH
-       &subsys_attr_iopolicy.attr,
-#endif
-       NULL,
-};
-
-static const struct attribute_group nvme_subsys_attrs_group = {
-       .attrs = nvme_subsys_attrs,
-};
-
-static const struct attribute_group *nvme_subsys_attrs_groups[] = {
-       &nvme_subsys_attrs_group,
-       NULL,
-};
-
 static inline bool nvme_discovery_ctrl(struct nvme_ctrl *ctrl)
 {
        return ctrl->opts && ctrl->opts->discovery_nqn;
        .uring_cmd      = nvme_dev_uring_cmd,
 };
 
-static ssize_t nvme_sysfs_reset(struct device *dev,
-                               struct device_attribute *attr, const char *buf,
-                               size_t count)
-{
-       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
-       int ret;
-
-       ret = nvme_reset_ctrl_sync(ctrl);
-       if (ret < 0)
-               return ret;
-       return count;
-}
-static DEVICE_ATTR(reset_controller, S_IWUSR, NULL, nvme_sysfs_reset);
-
-static ssize_t nvme_sysfs_rescan(struct device *dev,
-                               struct device_attribute *attr, const char *buf,
-                               size_t count)
-{
-       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
-
-       nvme_queue_scan(ctrl);
-       return count;
-}
-static DEVICE_ATTR(rescan_controller, S_IWUSR, NULL, nvme_sysfs_rescan);
-
-static inline struct nvme_ns_head *dev_to_ns_head(struct device *dev)
-{
-       struct gendisk *disk = dev_to_disk(dev);
-
-       if (disk->fops == &nvme_bdev_ops)
-               return nvme_get_ns_from_dev(dev)->head;
-       else
-               return disk->private_data;
-}
-
-static ssize_t wwid_show(struct device *dev, struct device_attribute *attr,
-               char *buf)
-{
-       struct nvme_ns_head *head = dev_to_ns_head(dev);
-       struct nvme_ns_ids *ids = &head->ids;
-       struct nvme_subsystem *subsys = head->subsys;
-       int serial_len = sizeof(subsys->serial);
-       int model_len = sizeof(subsys->model);
-
-       if (!uuid_is_null(&ids->uuid))
-               return sysfs_emit(buf, "uuid.%pU\n", &ids->uuid);
-
-       if (memchr_inv(ids->nguid, 0, sizeof(ids->nguid)))
-               return sysfs_emit(buf, "eui.%16phN\n", ids->nguid);
-
-       if (memchr_inv(ids->eui64, 0, sizeof(ids->eui64)))
-               return sysfs_emit(buf, "eui.%8phN\n", ids->eui64);
-
-       while (serial_len > 0 && (subsys->serial[serial_len - 1] == ' ' ||
-                                 subsys->serial[serial_len - 1] == '\0'))
-               serial_len--;
-       while (model_len > 0 && (subsys->model[model_len - 1] == ' ' ||
-                                subsys->model[model_len - 1] == '\0'))
-               model_len--;
-
-       return sysfs_emit(buf, "nvme.%04x-%*phN-%*phN-%08x\n", subsys->vendor_id,
-               serial_len, subsys->serial, model_len, subsys->model,
-               head->ns_id);
-}
-static DEVICE_ATTR_RO(wwid);
-
-static ssize_t nguid_show(struct device *dev, struct device_attribute *attr,
-               char *buf)
-{
-       return sysfs_emit(buf, "%pU\n", dev_to_ns_head(dev)->ids.nguid);
-}
-static DEVICE_ATTR_RO(nguid);
-
-static ssize_t uuid_show(struct device *dev, struct device_attribute *attr,
-               char *buf)
-{
-       struct nvme_ns_ids *ids = &dev_to_ns_head(dev)->ids;
-
-       /* For backward compatibility expose the NGUID to userspace if
-        * we have no UUID set
-        */
-       if (uuid_is_null(&ids->uuid)) {
-               dev_warn_ratelimited(dev,
-                       "No UUID available providing old NGUID\n");
-               return sysfs_emit(buf, "%pU\n", ids->nguid);
-       }
-       return sysfs_emit(buf, "%pU\n", &ids->uuid);
-}
-static DEVICE_ATTR_RO(uuid);
-
-static ssize_t eui_show(struct device *dev, struct device_attribute *attr,
-               char *buf)
-{
-       return sysfs_emit(buf, "%8ph\n", dev_to_ns_head(dev)->ids.eui64);
-}
-static DEVICE_ATTR_RO(eui);
-
-static ssize_t nsid_show(struct device *dev, struct device_attribute *attr,
-               char *buf)
-{
-       return sysfs_emit(buf, "%d\n", dev_to_ns_head(dev)->ns_id);
-}
-static DEVICE_ATTR_RO(nsid);
-
-static struct attribute *nvme_ns_id_attrs[] = {
-       &dev_attr_wwid.attr,
-       &dev_attr_uuid.attr,
-       &dev_attr_nguid.attr,
-       &dev_attr_eui.attr,
-       &dev_attr_nsid.attr,
-#ifdef CONFIG_NVME_MULTIPATH
-       &dev_attr_ana_grpid.attr,
-       &dev_attr_ana_state.attr,
-#endif
-       NULL,
-};
-
-static umode_t nvme_ns_id_attrs_are_visible(struct kobject *kobj,
-               struct attribute *a, int n)
-{
-       struct device *dev = container_of(kobj, struct device, kobj);
-       struct nvme_ns_ids *ids = &dev_to_ns_head(dev)->ids;
-
-       if (a == &dev_attr_uuid.attr) {
-               if (uuid_is_null(&ids->uuid) &&
-                   !memchr_inv(ids->nguid, 0, sizeof(ids->nguid)))
-                       return 0;
-       }
-       if (a == &dev_attr_nguid.attr) {
-               if (!memchr_inv(ids->nguid, 0, sizeof(ids->nguid)))
-                       return 0;
-       }
-       if (a == &dev_attr_eui.attr) {
-               if (!memchr_inv(ids->eui64, 0, sizeof(ids->eui64)))
-                       return 0;
-       }
-#ifdef CONFIG_NVME_MULTIPATH
-       if (a == &dev_attr_ana_grpid.attr || a == &dev_attr_ana_state.attr) {
-               if (dev_to_disk(dev)->fops != &nvme_bdev_ops) /* per-path attr */
-                       return 0;
-               if (!nvme_ctrl_use_ana(nvme_get_ns_from_dev(dev)->ctrl))
-                       return 0;
-       }
-#endif
-       return a->mode;
-}
-
-static const struct attribute_group nvme_ns_id_attr_group = {
-       .attrs          = nvme_ns_id_attrs,
-       .is_visible     = nvme_ns_id_attrs_are_visible,
-};
-
-const struct attribute_group *nvme_ns_id_attr_groups[] = {
-       &nvme_ns_id_attr_group,
-       NULL,
-};
-
-#define nvme_show_str_function(field)                                          \
-static ssize_t  field##_show(struct device *dev,                               \
-                           struct device_attribute *attr, char *buf)           \
-{                                                                              \
-        struct nvme_ctrl *ctrl = dev_get_drvdata(dev);                         \
-        return sysfs_emit(buf, "%.*s\n",                                       \
-               (int)sizeof(ctrl->subsys->field), ctrl->subsys->field);         \
-}                                                                              \
-static DEVICE_ATTR(field, S_IRUGO, field##_show, NULL);
-
-nvme_show_str_function(model);
-nvme_show_str_function(serial);
-nvme_show_str_function(firmware_rev);
-
-#define nvme_show_int_function(field)                                          \
-static ssize_t  field##_show(struct device *dev,                               \
-                           struct device_attribute *attr, char *buf)           \
-{                                                                              \
-        struct nvme_ctrl *ctrl = dev_get_drvdata(dev);                         \
-        return sysfs_emit(buf, "%d\n", ctrl->field);                           \
-}                                                                              \
-static DEVICE_ATTR(field, S_IRUGO, field##_show, NULL);
-
-nvme_show_int_function(cntlid);
-nvme_show_int_function(numa_node);
-nvme_show_int_function(queue_count);
-nvme_show_int_function(sqsize);
-nvme_show_int_function(kato);
-
-static ssize_t nvme_sysfs_delete(struct device *dev,
-                               struct device_attribute *attr, const char *buf,
-                               size_t count)
-{
-       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
-
-       if (device_remove_file_self(dev, attr))
-               nvme_delete_ctrl_sync(ctrl);
-       return count;
-}
-static DEVICE_ATTR(delete_controller, S_IWUSR, NULL, nvme_sysfs_delete);
-
-static ssize_t nvme_sysfs_show_transport(struct device *dev,
-                                        struct device_attribute *attr,
-                                        char *buf)
-{
-       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
-
-       return sysfs_emit(buf, "%s\n", ctrl->ops->name);
-}
-static DEVICE_ATTR(transport, S_IRUGO, nvme_sysfs_show_transport, NULL);
-
-static ssize_t nvme_sysfs_show_state(struct device *dev,
-                                    struct device_attribute *attr,
-                                    char *buf)
-{
-       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
-       static const char *const state_name[] = {
-               [NVME_CTRL_NEW]         = "new",
-               [NVME_CTRL_LIVE]        = "live",
-               [NVME_CTRL_RESETTING]   = "resetting",
-               [NVME_CTRL_CONNECTING]  = "connecting",
-               [NVME_CTRL_DELETING]    = "deleting",
-               [NVME_CTRL_DELETING_NOIO]= "deleting (no IO)",
-               [NVME_CTRL_DEAD]        = "dead",
-       };
-
-       if ((unsigned)ctrl->state < ARRAY_SIZE(state_name) &&
-           state_name[ctrl->state])
-               return sysfs_emit(buf, "%s\n", state_name[ctrl->state]);
-
-       return sysfs_emit(buf, "unknown state\n");
-}
-
-static DEVICE_ATTR(state, S_IRUGO, nvme_sysfs_show_state, NULL);
-
-static ssize_t nvme_sysfs_show_subsysnqn(struct device *dev,
-                                        struct device_attribute *attr,
-                                        char *buf)
-{
-       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
-
-       return sysfs_emit(buf, "%s\n", ctrl->subsys->subnqn);
-}
-static DEVICE_ATTR(subsysnqn, S_IRUGO, nvme_sysfs_show_subsysnqn, NULL);
-
-static ssize_t nvme_sysfs_show_hostnqn(struct device *dev,
-                                       struct device_attribute *attr,
-                                       char *buf)
-{
-       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
-
-       return sysfs_emit(buf, "%s\n", ctrl->opts->host->nqn);
-}
-static DEVICE_ATTR(hostnqn, S_IRUGO, nvme_sysfs_show_hostnqn, NULL);
-
-static ssize_t nvme_sysfs_show_hostid(struct device *dev,
-                                       struct device_attribute *attr,
-                                       char *buf)
-{
-       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
-
-       return sysfs_emit(buf, "%pU\n", &ctrl->opts->host->id);
-}
-static DEVICE_ATTR(hostid, S_IRUGO, nvme_sysfs_show_hostid, NULL);
-
-static ssize_t nvme_sysfs_show_address(struct device *dev,
-                                        struct device_attribute *attr,
-                                        char *buf)
-{
-       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
-
-       return ctrl->ops->get_address(ctrl, buf, PAGE_SIZE);
-}
-static DEVICE_ATTR(address, S_IRUGO, nvme_sysfs_show_address, NULL);
-
-static ssize_t nvme_ctrl_loss_tmo_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
-       struct nvmf_ctrl_options *opts = ctrl->opts;
-
-       if (ctrl->opts->max_reconnects == -1)
-               return sysfs_emit(buf, "off\n");
-       return sysfs_emit(buf, "%d\n",
-                         opts->max_reconnects * opts->reconnect_delay);
-}
-
-static ssize_t nvme_ctrl_loss_tmo_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
-{
-       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
-       struct nvmf_ctrl_options *opts = ctrl->opts;
-       int ctrl_loss_tmo, err;
-
-       err = kstrtoint(buf, 10, &ctrl_loss_tmo);
-       if (err)
-               return -EINVAL;
-
-       if (ctrl_loss_tmo < 0)
-               opts->max_reconnects = -1;
-       else
-               opts->max_reconnects = DIV_ROUND_UP(ctrl_loss_tmo,
-                                               opts->reconnect_delay);
-       return count;
-}
-static DEVICE_ATTR(ctrl_loss_tmo, S_IRUGO | S_IWUSR,
-       nvme_ctrl_loss_tmo_show, nvme_ctrl_loss_tmo_store);
-
-static ssize_t nvme_ctrl_reconnect_delay_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
-
-       if (ctrl->opts->reconnect_delay == -1)
-               return sysfs_emit(buf, "off\n");
-       return sysfs_emit(buf, "%d\n", ctrl->opts->reconnect_delay);
-}
-
-static ssize_t nvme_ctrl_reconnect_delay_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
-{
-       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
-       unsigned int v;
-       int err;
-
-       err = kstrtou32(buf, 10, &v);
-       if (err)
-               return err;
-
-       ctrl->opts->reconnect_delay = v;
-       return count;
-}
-static DEVICE_ATTR(reconnect_delay, S_IRUGO | S_IWUSR,
-       nvme_ctrl_reconnect_delay_show, nvme_ctrl_reconnect_delay_store);
-
-static ssize_t nvme_ctrl_fast_io_fail_tmo_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
-
-       if (ctrl->opts->fast_io_fail_tmo == -1)
-               return sysfs_emit(buf, "off\n");
-       return sysfs_emit(buf, "%d\n", ctrl->opts->fast_io_fail_tmo);
-}
-
-static ssize_t nvme_ctrl_fast_io_fail_tmo_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
-{
-       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
-       struct nvmf_ctrl_options *opts = ctrl->opts;
-       int fast_io_fail_tmo, err;
-
-       err = kstrtoint(buf, 10, &fast_io_fail_tmo);
-       if (err)
-               return -EINVAL;
-
-       if (fast_io_fail_tmo < 0)
-               opts->fast_io_fail_tmo = -1;
-       else
-               opts->fast_io_fail_tmo = fast_io_fail_tmo;
-       return count;
-}
-static DEVICE_ATTR(fast_io_fail_tmo, S_IRUGO | S_IWUSR,
-       nvme_ctrl_fast_io_fail_tmo_show, nvme_ctrl_fast_io_fail_tmo_store);
-
-static ssize_t cntrltype_show(struct device *dev,
-                             struct device_attribute *attr, char *buf)
-{
-       static const char * const type[] = {
-               [NVME_CTRL_IO] = "io\n",
-               [NVME_CTRL_DISC] = "discovery\n",
-               [NVME_CTRL_ADMIN] = "admin\n",
-       };
-       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
-
-       if (ctrl->cntrltype > NVME_CTRL_ADMIN || !type[ctrl->cntrltype])
-               return sysfs_emit(buf, "reserved\n");
-
-       return sysfs_emit(buf, type[ctrl->cntrltype]);
-}
-static DEVICE_ATTR_RO(cntrltype);
-
-static ssize_t dctype_show(struct device *dev,
-                          struct device_attribute *attr, char *buf)
-{
-       static const char * const type[] = {
-               [NVME_DCTYPE_NOT_REPORTED] = "none\n",
-               [NVME_DCTYPE_DDC] = "ddc\n",
-               [NVME_DCTYPE_CDC] = "cdc\n",
-       };
-       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
-
-       if (ctrl->dctype > NVME_DCTYPE_CDC || !type[ctrl->dctype])
-               return sysfs_emit(buf, "reserved\n");
-
-       return sysfs_emit(buf, type[ctrl->dctype]);
-}
-static DEVICE_ATTR_RO(dctype);
-
-#ifdef CONFIG_NVME_AUTH
-static ssize_t nvme_ctrl_dhchap_secret_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
-       struct nvmf_ctrl_options *opts = ctrl->opts;
-
-       if (!opts->dhchap_secret)
-               return sysfs_emit(buf, "none\n");
-       return sysfs_emit(buf, "%s\n", opts->dhchap_secret);
-}
-
-static ssize_t nvme_ctrl_dhchap_secret_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
-{
-       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
-       struct nvmf_ctrl_options *opts = ctrl->opts;
-       char *dhchap_secret;
-
-       if (!ctrl->opts->dhchap_secret)
-               return -EINVAL;
-       if (count < 7)
-               return -EINVAL;
-       if (memcmp(buf, "DHHC-1:", 7))
-               return -EINVAL;
-
-       dhchap_secret = kzalloc(count + 1, GFP_KERNEL);
-       if (!dhchap_secret)
-               return -ENOMEM;
-       memcpy(dhchap_secret, buf, count);
-       nvme_auth_stop(ctrl);
-       if (strcmp(dhchap_secret, opts->dhchap_secret)) {
-               struct nvme_dhchap_key *key, *host_key;
-               int ret;
-
-               ret = nvme_auth_generate_key(dhchap_secret, &key);
-               if (ret) {
-                       kfree(dhchap_secret);
-                       return ret;
-               }
-               kfree(opts->dhchap_secret);
-               opts->dhchap_secret = dhchap_secret;
-               host_key = ctrl->host_key;
-               mutex_lock(&ctrl->dhchap_auth_mutex);
-               ctrl->host_key = key;
-               mutex_unlock(&ctrl->dhchap_auth_mutex);
-               nvme_auth_free_key(host_key);
-       } else
-               kfree(dhchap_secret);
-       /* Start re-authentication */
-       dev_info(ctrl->device, "re-authenticating controller\n");
-       queue_work(nvme_wq, &ctrl->dhchap_auth_work);
-
-       return count;
-}
-static DEVICE_ATTR(dhchap_secret, S_IRUGO | S_IWUSR,
-       nvme_ctrl_dhchap_secret_show, nvme_ctrl_dhchap_secret_store);
-
-static ssize_t nvme_ctrl_dhchap_ctrl_secret_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
-       struct nvmf_ctrl_options *opts = ctrl->opts;
-
-       if (!opts->dhchap_ctrl_secret)
-               return sysfs_emit(buf, "none\n");
-       return sysfs_emit(buf, "%s\n", opts->dhchap_ctrl_secret);
-}
-
-static ssize_t nvme_ctrl_dhchap_ctrl_secret_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
-{
-       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
-       struct nvmf_ctrl_options *opts = ctrl->opts;
-       char *dhchap_secret;
-
-       if (!ctrl->opts->dhchap_ctrl_secret)
-               return -EINVAL;
-       if (count < 7)
-               return -EINVAL;
-       if (memcmp(buf, "DHHC-1:", 7))
-               return -EINVAL;
-
-       dhchap_secret = kzalloc(count + 1, GFP_KERNEL);
-       if (!dhchap_secret)
-               return -ENOMEM;
-       memcpy(dhchap_secret, buf, count);
-       nvme_auth_stop(ctrl);
-       if (strcmp(dhchap_secret, opts->dhchap_ctrl_secret)) {
-               struct nvme_dhchap_key *key, *ctrl_key;
-               int ret;
-
-               ret = nvme_auth_generate_key(dhchap_secret, &key);
-               if (ret) {
-                       kfree(dhchap_secret);
-                       return ret;
-               }
-               kfree(opts->dhchap_ctrl_secret);
-               opts->dhchap_ctrl_secret = dhchap_secret;
-               ctrl_key = ctrl->ctrl_key;
-               mutex_lock(&ctrl->dhchap_auth_mutex);
-               ctrl->ctrl_key = key;
-               mutex_unlock(&ctrl->dhchap_auth_mutex);
-               nvme_auth_free_key(ctrl_key);
-       } else
-               kfree(dhchap_secret);
-       /* Start re-authentication */
-       dev_info(ctrl->device, "re-authenticating controller\n");
-       queue_work(nvme_wq, &ctrl->dhchap_auth_work);
-
-       return count;
-}
-static DEVICE_ATTR(dhchap_ctrl_secret, S_IRUGO | S_IWUSR,
-       nvme_ctrl_dhchap_ctrl_secret_show, nvme_ctrl_dhchap_ctrl_secret_store);
-#endif
-
-static struct attribute *nvme_dev_attrs[] = {
-       &dev_attr_reset_controller.attr,
-       &dev_attr_rescan_controller.attr,
-       &dev_attr_model.attr,
-       &dev_attr_serial.attr,
-       &dev_attr_firmware_rev.attr,
-       &dev_attr_cntlid.attr,
-       &dev_attr_delete_controller.attr,
-       &dev_attr_transport.attr,
-       &dev_attr_subsysnqn.attr,
-       &dev_attr_address.attr,
-       &dev_attr_state.attr,
-       &dev_attr_numa_node.attr,
-       &dev_attr_queue_count.attr,
-       &dev_attr_sqsize.attr,
-       &dev_attr_hostnqn.attr,
-       &dev_attr_hostid.attr,
-       &dev_attr_ctrl_loss_tmo.attr,
-       &dev_attr_reconnect_delay.attr,
-       &dev_attr_fast_io_fail_tmo.attr,
-       &dev_attr_kato.attr,
-       &dev_attr_cntrltype.attr,
-       &dev_attr_dctype.attr,
-#ifdef CONFIG_NVME_AUTH
-       &dev_attr_dhchap_secret.attr,
-       &dev_attr_dhchap_ctrl_secret.attr,
-#endif
-       NULL
-};
-
-static umode_t nvme_dev_attrs_are_visible(struct kobject *kobj,
-               struct attribute *a, int n)
-{
-       struct device *dev = container_of(kobj, struct device, kobj);
-       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
-
-       if (a == &dev_attr_delete_controller.attr && !ctrl->ops->delete_ctrl)
-               return 0;
-       if (a == &dev_attr_address.attr && !ctrl->ops->get_address)
-               return 0;
-       if (a == &dev_attr_hostnqn.attr && !ctrl->opts)
-               return 0;
-       if (a == &dev_attr_hostid.attr && !ctrl->opts)
-               return 0;
-       if (a == &dev_attr_ctrl_loss_tmo.attr && !ctrl->opts)
-               return 0;
-       if (a == &dev_attr_reconnect_delay.attr && !ctrl->opts)
-               return 0;
-       if (a == &dev_attr_fast_io_fail_tmo.attr && !ctrl->opts)
-               return 0;
-#ifdef CONFIG_NVME_AUTH
-       if (a == &dev_attr_dhchap_secret.attr && !ctrl->opts)
-               return 0;
-       if (a == &dev_attr_dhchap_ctrl_secret.attr && !ctrl->opts)
-               return 0;
-#endif
-
-       return a->mode;
-}
-
-const struct attribute_group nvme_dev_attrs_group = {
-       .attrs          = nvme_dev_attrs,
-       .is_visible     = nvme_dev_attrs_are_visible,
-};
-EXPORT_SYMBOL_GPL(nvme_dev_attrs_group);
-
-static const struct attribute_group *nvme_dev_attr_groups[] = {
-       &nvme_dev_attrs_group,
-       NULL,
-};
-
 static struct nvme_ns_head *nvme_find_ns_head(struct nvme_ctrl *ctrl,
                unsigned nsid)
 {
 
 extern const struct pr_ops nvme_pr_ops;
 extern const struct block_device_operations nvme_ns_head_ops;
 extern const struct attribute_group nvme_dev_attrs_group;
+extern const struct attribute_group *nvme_subsys_attrs_groups[];
+extern const struct attribute_group *nvme_dev_attr_groups[];
+extern const struct block_device_operations nvme_bdev_ops;
 
+void nvme_delete_ctrl_sync(struct nvme_ctrl *ctrl);
 struct nvme_ns *nvme_find_path(struct nvme_ns_head *head);
 #ifdef CONFIG_NVME_MULTIPATH
 static inline bool nvme_ctrl_use_ana(struct nvme_ctrl *ctrl)
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Sysfs interface for the NVMe core driver.
+ *
+ * Copyright (c) 2011-2014, Intel Corporation.
+ */
+
+#include <linux/nvme-auth.h>
+
+#include "nvme.h"
+#include "fabrics.h"
+
+static ssize_t nvme_sysfs_reset(struct device *dev,
+                               struct device_attribute *attr, const char *buf,
+                               size_t count)
+{
+       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+       int ret;
+
+       ret = nvme_reset_ctrl_sync(ctrl);
+       if (ret < 0)
+               return ret;
+       return count;
+}
+static DEVICE_ATTR(reset_controller, S_IWUSR, NULL, nvme_sysfs_reset);
+
+static ssize_t nvme_sysfs_rescan(struct device *dev,
+                               struct device_attribute *attr, const char *buf,
+                               size_t count)
+{
+       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+
+       nvme_queue_scan(ctrl);
+       return count;
+}
+static DEVICE_ATTR(rescan_controller, S_IWUSR, NULL, nvme_sysfs_rescan);
+
+static inline struct nvme_ns_head *dev_to_ns_head(struct device *dev)
+{
+       struct gendisk *disk = dev_to_disk(dev);
+
+       if (disk->fops == &nvme_bdev_ops)
+               return nvme_get_ns_from_dev(dev)->head;
+       else
+               return disk->private_data;
+}
+
+static ssize_t wwid_show(struct device *dev, struct device_attribute *attr,
+               char *buf)
+{
+       struct nvme_ns_head *head = dev_to_ns_head(dev);
+       struct nvme_ns_ids *ids = &head->ids;
+       struct nvme_subsystem *subsys = head->subsys;
+       int serial_len = sizeof(subsys->serial);
+       int model_len = sizeof(subsys->model);
+
+       if (!uuid_is_null(&ids->uuid))
+               return sysfs_emit(buf, "uuid.%pU\n", &ids->uuid);
+
+       if (memchr_inv(ids->nguid, 0, sizeof(ids->nguid)))
+               return sysfs_emit(buf, "eui.%16phN\n", ids->nguid);
+
+       if (memchr_inv(ids->eui64, 0, sizeof(ids->eui64)))
+               return sysfs_emit(buf, "eui.%8phN\n", ids->eui64);
+
+       while (serial_len > 0 && (subsys->serial[serial_len - 1] == ' ' ||
+                                 subsys->serial[serial_len - 1] == '\0'))
+               serial_len--;
+       while (model_len > 0 && (subsys->model[model_len - 1] == ' ' ||
+                                subsys->model[model_len - 1] == '\0'))
+               model_len--;
+
+       return sysfs_emit(buf, "nvme.%04x-%*phN-%*phN-%08x\n", subsys->vendor_id,
+               serial_len, subsys->serial, model_len, subsys->model,
+               head->ns_id);
+}
+static DEVICE_ATTR_RO(wwid);
+
+static ssize_t nguid_show(struct device *dev, struct device_attribute *attr,
+               char *buf)
+{
+       return sysfs_emit(buf, "%pU\n", dev_to_ns_head(dev)->ids.nguid);
+}
+static DEVICE_ATTR_RO(nguid);
+
+static ssize_t uuid_show(struct device *dev, struct device_attribute *attr,
+               char *buf)
+{
+       struct nvme_ns_ids *ids = &dev_to_ns_head(dev)->ids;
+
+       /* For backward compatibility expose the NGUID to userspace if
+        * we have no UUID set
+        */
+       if (uuid_is_null(&ids->uuid)) {
+               dev_warn_ratelimited(dev,
+                       "No UUID available providing old NGUID\n");
+               return sysfs_emit(buf, "%pU\n", ids->nguid);
+       }
+       return sysfs_emit(buf, "%pU\n", &ids->uuid);
+}
+static DEVICE_ATTR_RO(uuid);
+
+static ssize_t eui_show(struct device *dev, struct device_attribute *attr,
+               char *buf)
+{
+       return sysfs_emit(buf, "%8ph\n", dev_to_ns_head(dev)->ids.eui64);
+}
+static DEVICE_ATTR_RO(eui);
+
+static ssize_t nsid_show(struct device *dev, struct device_attribute *attr,
+               char *buf)
+{
+       return sysfs_emit(buf, "%d\n", dev_to_ns_head(dev)->ns_id);
+}
+static DEVICE_ATTR_RO(nsid);
+
+static struct attribute *nvme_ns_id_attrs[] = {
+       &dev_attr_wwid.attr,
+       &dev_attr_uuid.attr,
+       &dev_attr_nguid.attr,
+       &dev_attr_eui.attr,
+       &dev_attr_nsid.attr,
+#ifdef CONFIG_NVME_MULTIPATH
+       &dev_attr_ana_grpid.attr,
+       &dev_attr_ana_state.attr,
+#endif
+       NULL,
+};
+
+static umode_t nvme_ns_id_attrs_are_visible(struct kobject *kobj,
+               struct attribute *a, int n)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct nvme_ns_ids *ids = &dev_to_ns_head(dev)->ids;
+
+       if (a == &dev_attr_uuid.attr) {
+               if (uuid_is_null(&ids->uuid) &&
+                   !memchr_inv(ids->nguid, 0, sizeof(ids->nguid)))
+                       return 0;
+       }
+       if (a == &dev_attr_nguid.attr) {
+               if (!memchr_inv(ids->nguid, 0, sizeof(ids->nguid)))
+                       return 0;
+       }
+       if (a == &dev_attr_eui.attr) {
+               if (!memchr_inv(ids->eui64, 0, sizeof(ids->eui64)))
+                       return 0;
+       }
+#ifdef CONFIG_NVME_MULTIPATH
+       if (a == &dev_attr_ana_grpid.attr || a == &dev_attr_ana_state.attr) {
+               if (dev_to_disk(dev)->fops != &nvme_bdev_ops) /* per-path attr */
+                       return 0;
+               if (!nvme_ctrl_use_ana(nvme_get_ns_from_dev(dev)->ctrl))
+                       return 0;
+       }
+#endif
+       return a->mode;
+}
+
+static const struct attribute_group nvme_ns_id_attr_group = {
+       .attrs          = nvme_ns_id_attrs,
+       .is_visible     = nvme_ns_id_attrs_are_visible,
+};
+
+const struct attribute_group *nvme_ns_id_attr_groups[] = {
+       &nvme_ns_id_attr_group,
+       NULL,
+};
+
+#define nvme_show_str_function(field)                                          \
+static ssize_t  field##_show(struct device *dev,                               \
+                           struct device_attribute *attr, char *buf)           \
+{                                                                              \
+        struct nvme_ctrl *ctrl = dev_get_drvdata(dev);                         \
+        return sysfs_emit(buf, "%.*s\n",                                       \
+               (int)sizeof(ctrl->subsys->field), ctrl->subsys->field);         \
+}                                                                              \
+static DEVICE_ATTR(field, S_IRUGO, field##_show, NULL);
+
+nvme_show_str_function(model);
+nvme_show_str_function(serial);
+nvme_show_str_function(firmware_rev);
+
+#define nvme_show_int_function(field)                                          \
+static ssize_t  field##_show(struct device *dev,                               \
+                           struct device_attribute *attr, char *buf)           \
+{                                                                              \
+        struct nvme_ctrl *ctrl = dev_get_drvdata(dev);                         \
+        return sysfs_emit(buf, "%d\n", ctrl->field);                           \
+}                                                                              \
+static DEVICE_ATTR(field, S_IRUGO, field##_show, NULL);
+
+nvme_show_int_function(cntlid);
+nvme_show_int_function(numa_node);
+nvme_show_int_function(queue_count);
+nvme_show_int_function(sqsize);
+nvme_show_int_function(kato);
+
+static ssize_t nvme_sysfs_delete(struct device *dev,
+                               struct device_attribute *attr, const char *buf,
+                               size_t count)
+{
+       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+
+       if (device_remove_file_self(dev, attr))
+               nvme_delete_ctrl_sync(ctrl);
+       return count;
+}
+static DEVICE_ATTR(delete_controller, S_IWUSR, NULL, nvme_sysfs_delete);
+
+static ssize_t nvme_sysfs_show_transport(struct device *dev,
+                                        struct device_attribute *attr,
+                                        char *buf)
+{
+       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+
+       return sysfs_emit(buf, "%s\n", ctrl->ops->name);
+}
+static DEVICE_ATTR(transport, S_IRUGO, nvme_sysfs_show_transport, NULL);
+
+static ssize_t nvme_sysfs_show_state(struct device *dev,
+                                    struct device_attribute *attr,
+                                    char *buf)
+{
+       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+       static const char *const state_name[] = {
+               [NVME_CTRL_NEW]         = "new",
+               [NVME_CTRL_LIVE]        = "live",
+               [NVME_CTRL_RESETTING]   = "resetting",
+               [NVME_CTRL_CONNECTING]  = "connecting",
+               [NVME_CTRL_DELETING]    = "deleting",
+               [NVME_CTRL_DELETING_NOIO]= "deleting (no IO)",
+               [NVME_CTRL_DEAD]        = "dead",
+       };
+
+       if ((unsigned)ctrl->state < ARRAY_SIZE(state_name) &&
+           state_name[ctrl->state])
+               return sysfs_emit(buf, "%s\n", state_name[ctrl->state]);
+
+       return sysfs_emit(buf, "unknown state\n");
+}
+
+static DEVICE_ATTR(state, S_IRUGO, nvme_sysfs_show_state, NULL);
+
+static ssize_t nvme_sysfs_show_subsysnqn(struct device *dev,
+                                        struct device_attribute *attr,
+                                        char *buf)
+{
+       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+
+       return sysfs_emit(buf, "%s\n", ctrl->subsys->subnqn);
+}
+static DEVICE_ATTR(subsysnqn, S_IRUGO, nvme_sysfs_show_subsysnqn, NULL);
+
+static ssize_t nvme_sysfs_show_hostnqn(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+
+       return sysfs_emit(buf, "%s\n", ctrl->opts->host->nqn);
+}
+static DEVICE_ATTR(hostnqn, S_IRUGO, nvme_sysfs_show_hostnqn, NULL);
+
+static ssize_t nvme_sysfs_show_hostid(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+
+       return sysfs_emit(buf, "%pU\n", &ctrl->opts->host->id);
+}
+static DEVICE_ATTR(hostid, S_IRUGO, nvme_sysfs_show_hostid, NULL);
+
+static ssize_t nvme_sysfs_show_address(struct device *dev,
+                                        struct device_attribute *attr,
+                                        char *buf)
+{
+       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+
+       return ctrl->ops->get_address(ctrl, buf, PAGE_SIZE);
+}
+static DEVICE_ATTR(address, S_IRUGO, nvme_sysfs_show_address, NULL);
+
+static ssize_t nvme_ctrl_loss_tmo_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+       struct nvmf_ctrl_options *opts = ctrl->opts;
+
+       if (ctrl->opts->max_reconnects == -1)
+               return sysfs_emit(buf, "off\n");
+       return sysfs_emit(buf, "%d\n",
+                         opts->max_reconnects * opts->reconnect_delay);
+}
+
+static ssize_t nvme_ctrl_loss_tmo_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+       struct nvmf_ctrl_options *opts = ctrl->opts;
+       int ctrl_loss_tmo, err;
+
+       err = kstrtoint(buf, 10, &ctrl_loss_tmo);
+       if (err)
+               return -EINVAL;
+
+       if (ctrl_loss_tmo < 0)
+               opts->max_reconnects = -1;
+       else
+               opts->max_reconnects = DIV_ROUND_UP(ctrl_loss_tmo,
+                                               opts->reconnect_delay);
+       return count;
+}
+static DEVICE_ATTR(ctrl_loss_tmo, S_IRUGO | S_IWUSR,
+       nvme_ctrl_loss_tmo_show, nvme_ctrl_loss_tmo_store);
+
+static ssize_t nvme_ctrl_reconnect_delay_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+
+       if (ctrl->opts->reconnect_delay == -1)
+               return sysfs_emit(buf, "off\n");
+       return sysfs_emit(buf, "%d\n", ctrl->opts->reconnect_delay);
+}
+
+static ssize_t nvme_ctrl_reconnect_delay_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+       unsigned int v;
+       int err;
+
+       err = kstrtou32(buf, 10, &v);
+       if (err)
+               return err;
+
+       ctrl->opts->reconnect_delay = v;
+       return count;
+}
+static DEVICE_ATTR(reconnect_delay, S_IRUGO | S_IWUSR,
+       nvme_ctrl_reconnect_delay_show, nvme_ctrl_reconnect_delay_store);
+
+static ssize_t nvme_ctrl_fast_io_fail_tmo_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+
+       if (ctrl->opts->fast_io_fail_tmo == -1)
+               return sysfs_emit(buf, "off\n");
+       return sysfs_emit(buf, "%d\n", ctrl->opts->fast_io_fail_tmo);
+}
+
+static ssize_t nvme_ctrl_fast_io_fail_tmo_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+       struct nvmf_ctrl_options *opts = ctrl->opts;
+       int fast_io_fail_tmo, err;
+
+       err = kstrtoint(buf, 10, &fast_io_fail_tmo);
+       if (err)
+               return -EINVAL;
+
+       if (fast_io_fail_tmo < 0)
+               opts->fast_io_fail_tmo = -1;
+       else
+               opts->fast_io_fail_tmo = fast_io_fail_tmo;
+       return count;
+}
+static DEVICE_ATTR(fast_io_fail_tmo, S_IRUGO | S_IWUSR,
+       nvme_ctrl_fast_io_fail_tmo_show, nvme_ctrl_fast_io_fail_tmo_store);
+
+static ssize_t cntrltype_show(struct device *dev,
+                             struct device_attribute *attr, char *buf)
+{
+       static const char * const type[] = {
+               [NVME_CTRL_IO] = "io\n",
+               [NVME_CTRL_DISC] = "discovery\n",
+               [NVME_CTRL_ADMIN] = "admin\n",
+       };
+       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+
+       if (ctrl->cntrltype > NVME_CTRL_ADMIN || !type[ctrl->cntrltype])
+               return sysfs_emit(buf, "reserved\n");
+
+       return sysfs_emit(buf, type[ctrl->cntrltype]);
+}
+static DEVICE_ATTR_RO(cntrltype);
+
+static ssize_t dctype_show(struct device *dev,
+                          struct device_attribute *attr, char *buf)
+{
+       static const char * const type[] = {
+               [NVME_DCTYPE_NOT_REPORTED] = "none\n",
+               [NVME_DCTYPE_DDC] = "ddc\n",
+               [NVME_DCTYPE_CDC] = "cdc\n",
+       };
+       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+
+       if (ctrl->dctype > NVME_DCTYPE_CDC || !type[ctrl->dctype])
+               return sysfs_emit(buf, "reserved\n");
+
+       return sysfs_emit(buf, type[ctrl->dctype]);
+}
+static DEVICE_ATTR_RO(dctype);
+
+#ifdef CONFIG_NVME_AUTH
+static ssize_t nvme_ctrl_dhchap_secret_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+       struct nvmf_ctrl_options *opts = ctrl->opts;
+
+       if (!opts->dhchap_secret)
+               return sysfs_emit(buf, "none\n");
+       return sysfs_emit(buf, "%s\n", opts->dhchap_secret);
+}
+
+static ssize_t nvme_ctrl_dhchap_secret_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+       struct nvmf_ctrl_options *opts = ctrl->opts;
+       char *dhchap_secret;
+
+       if (!ctrl->opts->dhchap_secret)
+               return -EINVAL;
+       if (count < 7)
+               return -EINVAL;
+       if (memcmp(buf, "DHHC-1:", 7))
+               return -EINVAL;
+
+       dhchap_secret = kzalloc(count + 1, GFP_KERNEL);
+       if (!dhchap_secret)
+               return -ENOMEM;
+       memcpy(dhchap_secret, buf, count);
+       nvme_auth_stop(ctrl);
+       if (strcmp(dhchap_secret, opts->dhchap_secret)) {
+               struct nvme_dhchap_key *key, *host_key;
+               int ret;
+
+               ret = nvme_auth_generate_key(dhchap_secret, &key);
+               if (ret) {
+                       kfree(dhchap_secret);
+                       return ret;
+               }
+               kfree(opts->dhchap_secret);
+               opts->dhchap_secret = dhchap_secret;
+               host_key = ctrl->host_key;
+               mutex_lock(&ctrl->dhchap_auth_mutex);
+               ctrl->host_key = key;
+               mutex_unlock(&ctrl->dhchap_auth_mutex);
+               nvme_auth_free_key(host_key);
+       } else
+               kfree(dhchap_secret);
+       /* Start re-authentication */
+       dev_info(ctrl->device, "re-authenticating controller\n");
+       queue_work(nvme_wq, &ctrl->dhchap_auth_work);
+
+       return count;
+}
+
+static DEVICE_ATTR(dhchap_secret, S_IRUGO | S_IWUSR,
+       nvme_ctrl_dhchap_secret_show, nvme_ctrl_dhchap_secret_store);
+
+static ssize_t nvme_ctrl_dhchap_ctrl_secret_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+       struct nvmf_ctrl_options *opts = ctrl->opts;
+
+       if (!opts->dhchap_ctrl_secret)
+               return sysfs_emit(buf, "none\n");
+       return sysfs_emit(buf, "%s\n", opts->dhchap_ctrl_secret);
+}
+
+static ssize_t nvme_ctrl_dhchap_ctrl_secret_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+       struct nvmf_ctrl_options *opts = ctrl->opts;
+       char *dhchap_secret;
+
+       if (!ctrl->opts->dhchap_ctrl_secret)
+               return -EINVAL;
+       if (count < 7)
+               return -EINVAL;
+       if (memcmp(buf, "DHHC-1:", 7))
+               return -EINVAL;
+
+       dhchap_secret = kzalloc(count + 1, GFP_KERNEL);
+       if (!dhchap_secret)
+               return -ENOMEM;
+       memcpy(dhchap_secret, buf, count);
+       nvme_auth_stop(ctrl);
+       if (strcmp(dhchap_secret, opts->dhchap_ctrl_secret)) {
+               struct nvme_dhchap_key *key, *ctrl_key;
+               int ret;
+
+               ret = nvme_auth_generate_key(dhchap_secret, &key);
+               if (ret) {
+                       kfree(dhchap_secret);
+                       return ret;
+               }
+               kfree(opts->dhchap_ctrl_secret);
+               opts->dhchap_ctrl_secret = dhchap_secret;
+               ctrl_key = ctrl->ctrl_key;
+               mutex_lock(&ctrl->dhchap_auth_mutex);
+               ctrl->ctrl_key = key;
+               mutex_unlock(&ctrl->dhchap_auth_mutex);
+               nvme_auth_free_key(ctrl_key);
+       } else
+               kfree(dhchap_secret);
+       /* Start re-authentication */
+       dev_info(ctrl->device, "re-authenticating controller\n");
+       queue_work(nvme_wq, &ctrl->dhchap_auth_work);
+
+       return count;
+}
+
+static DEVICE_ATTR(dhchap_ctrl_secret, S_IRUGO | S_IWUSR,
+       nvme_ctrl_dhchap_ctrl_secret_show, nvme_ctrl_dhchap_ctrl_secret_store);
+#endif
+
+static struct attribute *nvme_dev_attrs[] = {
+       &dev_attr_reset_controller.attr,
+       &dev_attr_rescan_controller.attr,
+       &dev_attr_model.attr,
+       &dev_attr_serial.attr,
+       &dev_attr_firmware_rev.attr,
+       &dev_attr_cntlid.attr,
+       &dev_attr_delete_controller.attr,
+       &dev_attr_transport.attr,
+       &dev_attr_subsysnqn.attr,
+       &dev_attr_address.attr,
+       &dev_attr_state.attr,
+       &dev_attr_numa_node.attr,
+       &dev_attr_queue_count.attr,
+       &dev_attr_sqsize.attr,
+       &dev_attr_hostnqn.attr,
+       &dev_attr_hostid.attr,
+       &dev_attr_ctrl_loss_tmo.attr,
+       &dev_attr_reconnect_delay.attr,
+       &dev_attr_fast_io_fail_tmo.attr,
+       &dev_attr_kato.attr,
+       &dev_attr_cntrltype.attr,
+       &dev_attr_dctype.attr,
+#ifdef CONFIG_NVME_AUTH
+       &dev_attr_dhchap_secret.attr,
+       &dev_attr_dhchap_ctrl_secret.attr,
+#endif
+       NULL
+};
+
+static umode_t nvme_dev_attrs_are_visible(struct kobject *kobj,
+               struct attribute *a, int n)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+
+       if (a == &dev_attr_delete_controller.attr && !ctrl->ops->delete_ctrl)
+               return 0;
+       if (a == &dev_attr_address.attr && !ctrl->ops->get_address)
+               return 0;
+       if (a == &dev_attr_hostnqn.attr && !ctrl->opts)
+               return 0;
+       if (a == &dev_attr_hostid.attr && !ctrl->opts)
+               return 0;
+       if (a == &dev_attr_ctrl_loss_tmo.attr && !ctrl->opts)
+               return 0;
+       if (a == &dev_attr_reconnect_delay.attr && !ctrl->opts)
+               return 0;
+       if (a == &dev_attr_fast_io_fail_tmo.attr && !ctrl->opts)
+               return 0;
+#ifdef CONFIG_NVME_AUTH
+       if (a == &dev_attr_dhchap_secret.attr && !ctrl->opts)
+               return 0;
+       if (a == &dev_attr_dhchap_ctrl_secret.attr && !ctrl->opts)
+               return 0;
+#endif
+
+       return a->mode;
+}
+
+const struct attribute_group nvme_dev_attrs_group = {
+       .attrs          = nvme_dev_attrs,
+       .is_visible     = nvme_dev_attrs_are_visible,
+};
+EXPORT_SYMBOL_GPL(nvme_dev_attrs_group);
+
+const struct attribute_group *nvme_dev_attr_groups[] = {
+       &nvme_dev_attrs_group,
+       NULL,
+};
+
+#define SUBSYS_ATTR_RO(_name, _mode, _show)                    \
+       struct device_attribute subsys_attr_##_name = \
+               __ATTR(_name, _mode, _show, NULL)
+
+static ssize_t nvme_subsys_show_nqn(struct device *dev,
+                                   struct device_attribute *attr,
+                                   char *buf)
+{
+       struct nvme_subsystem *subsys =
+               container_of(dev, struct nvme_subsystem, dev);
+
+       return sysfs_emit(buf, "%s\n", subsys->subnqn);
+}
+static SUBSYS_ATTR_RO(subsysnqn, S_IRUGO, nvme_subsys_show_nqn);
+
+static ssize_t nvme_subsys_show_type(struct device *dev,
+                                   struct device_attribute *attr,
+                                   char *buf)
+{
+       struct nvme_subsystem *subsys =
+               container_of(dev, struct nvme_subsystem, dev);
+
+       switch (subsys->subtype) {
+       case NVME_NQN_DISC:
+               return sysfs_emit(buf, "discovery\n");
+       case NVME_NQN_NVME:
+               return sysfs_emit(buf, "nvm\n");
+       default:
+               return sysfs_emit(buf, "reserved\n");
+       }
+}
+static SUBSYS_ATTR_RO(subsystype, S_IRUGO, nvme_subsys_show_type);
+
+#define nvme_subsys_show_str_function(field)                           \
+static ssize_t subsys_##field##_show(struct device *dev,               \
+                           struct device_attribute *attr, char *buf)   \
+{                                                                      \
+       struct nvme_subsystem *subsys =                                 \
+               container_of(dev, struct nvme_subsystem, dev);          \
+       return sysfs_emit(buf, "%.*s\n",                                \
+                          (int)sizeof(subsys->field), subsys->field);  \
+}                                                                      \
+static SUBSYS_ATTR_RO(field, S_IRUGO, subsys_##field##_show);
+
+nvme_subsys_show_str_function(model);
+nvme_subsys_show_str_function(serial);
+nvme_subsys_show_str_function(firmware_rev);
+
+static struct attribute *nvme_subsys_attrs[] = {
+       &subsys_attr_model.attr,
+       &subsys_attr_serial.attr,
+       &subsys_attr_firmware_rev.attr,
+       &subsys_attr_subsysnqn.attr,
+       &subsys_attr_subsystype.attr,
+#ifdef CONFIG_NVME_MULTIPATH
+       &subsys_attr_iopolicy.attr,
+#endif
+       NULL,
+};
+
+static const struct attribute_group nvme_subsys_attrs_group = {
+       .attrs = nvme_subsys_attrs,
+};
+
+const struct attribute_group *nvme_subsys_attrs_groups[] = {
+       &nvme_subsys_attrs_group,
+       NULL,
+};