83340d9c61
Commit1e48b34c9b
("nvme: split off TLS sysfs attributes into a separate group") introduced the struct attribute array nvme_tls_attrs. However, the array was not null terminated and caused BUG KASAN global- out-of-bounds. To avoid the BUG, null terminate the array. Reported-by: Yi Zhang <yi.zhang@redhat.com> Closes: https://lore.kernel.org/linux-nvme/jhllwfxcedrcxcnbajwl4x2l2ujcqowqcd4ps574zrafrqhjna@f4icvecutekm/ Fixes:1e48b34c9b
("nvme: split off TLS sysfs attributes into a separate group") Signed-off-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com> Tested-by: Yi Zhang <yi.zhang@redhat.com> Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Keith Busch <kbusch@kernel.org>
877 lines
24 KiB
C
877 lines
24 KiB
C
// 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 ssize_t nvme_adm_passthru_err_log_enabled_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
|
|
|
|
return sysfs_emit(buf,
|
|
ctrl->passthru_err_log_enabled ? "on\n" : "off\n");
|
|
}
|
|
|
|
static ssize_t nvme_adm_passthru_err_log_enabled_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
|
{
|
|
struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
|
|
bool passthru_err_log_enabled;
|
|
int err;
|
|
|
|
err = kstrtobool(buf, &passthru_err_log_enabled);
|
|
if (err)
|
|
return -EINVAL;
|
|
|
|
ctrl->passthru_err_log_enabled = passthru_err_log_enabled;
|
|
|
|
return count;
|
|
}
|
|
|
|
static inline struct nvme_ns_head *dev_to_ns_head(struct device *dev)
|
|
{
|
|
struct gendisk *disk = dev_to_disk(dev);
|
|
|
|
if (nvme_disk_is_ns_head(disk))
|
|
return disk->private_data;
|
|
return nvme_get_ns_from_dev(dev)->head;
|
|
}
|
|
|
|
static ssize_t nvme_io_passthru_err_log_enabled_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct nvme_ns_head *head = dev_to_ns_head(dev);
|
|
|
|
return sysfs_emit(buf, head->passthru_err_log_enabled ? "on\n" : "off\n");
|
|
}
|
|
|
|
static ssize_t nvme_io_passthru_err_log_enabled_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
|
{
|
|
struct nvme_ns_head *head = dev_to_ns_head(dev);
|
|
bool passthru_err_log_enabled;
|
|
int err;
|
|
|
|
err = kstrtobool(buf, &passthru_err_log_enabled);
|
|
if (err)
|
|
return -EINVAL;
|
|
head->passthru_err_log_enabled = passthru_err_log_enabled;
|
|
|
|
return count;
|
|
}
|
|
|
|
static struct device_attribute dev_attr_adm_passthru_err_log_enabled = \
|
|
__ATTR(passthru_err_log_enabled, S_IRUGO | S_IWUSR, \
|
|
nvme_adm_passthru_err_log_enabled_show, nvme_adm_passthru_err_log_enabled_store);
|
|
|
|
static struct device_attribute dev_attr_io_passthru_err_log_enabled = \
|
|
__ATTR(passthru_err_log_enabled, S_IRUGO | S_IWUSR, \
|
|
nvme_io_passthru_err_log_enabled_show, nvme_io_passthru_err_log_enabled_store);
|
|
|
|
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_once(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 ssize_t csi_show(struct device *dev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
return sysfs_emit(buf, "%u\n", dev_to_ns_head(dev)->ids.csi);
|
|
}
|
|
static DEVICE_ATTR_RO(csi);
|
|
|
|
static ssize_t metadata_bytes_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return sysfs_emit(buf, "%u\n", dev_to_ns_head(dev)->ms);
|
|
}
|
|
static DEVICE_ATTR_RO(metadata_bytes);
|
|
|
|
static int ns_head_update_nuse(struct nvme_ns_head *head)
|
|
{
|
|
struct nvme_id_ns *id;
|
|
struct nvme_ns *ns;
|
|
int srcu_idx, ret = -EWOULDBLOCK;
|
|
|
|
/* Avoid issuing commands too often by rate limiting the update */
|
|
if (!__ratelimit(&head->rs_nuse))
|
|
return 0;
|
|
|
|
srcu_idx = srcu_read_lock(&head->srcu);
|
|
ns = nvme_find_path(head);
|
|
if (!ns)
|
|
goto out_unlock;
|
|
|
|
ret = nvme_identify_ns(ns->ctrl, head->ns_id, &id);
|
|
if (ret)
|
|
goto out_unlock;
|
|
|
|
head->nuse = le64_to_cpu(id->nuse);
|
|
kfree(id);
|
|
|
|
out_unlock:
|
|
srcu_read_unlock(&head->srcu, srcu_idx);
|
|
return ret;
|
|
}
|
|
|
|
static int ns_update_nuse(struct nvme_ns *ns)
|
|
{
|
|
struct nvme_id_ns *id;
|
|
int ret;
|
|
|
|
/* Avoid issuing commands too often by rate limiting the update. */
|
|
if (!__ratelimit(&ns->head->rs_nuse))
|
|
return 0;
|
|
|
|
ret = nvme_identify_ns(ns->ctrl, ns->head->ns_id, &id);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ns->head->nuse = le64_to_cpu(id->nuse);
|
|
kfree(id);
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t nuse_show(struct device *dev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct nvme_ns_head *head = dev_to_ns_head(dev);
|
|
struct gendisk *disk = dev_to_disk(dev);
|
|
int ret;
|
|
|
|
if (nvme_disk_is_ns_head(disk))
|
|
ret = ns_head_update_nuse(head);
|
|
else
|
|
ret = ns_update_nuse(disk->private_data);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return sysfs_emit(buf, "%llu\n", head->nuse);
|
|
}
|
|
static DEVICE_ATTR_RO(nuse);
|
|
|
|
static struct attribute *nvme_ns_attrs[] = {
|
|
&dev_attr_wwid.attr,
|
|
&dev_attr_uuid.attr,
|
|
&dev_attr_nguid.attr,
|
|
&dev_attr_eui.attr,
|
|
&dev_attr_csi.attr,
|
|
&dev_attr_nsid.attr,
|
|
&dev_attr_metadata_bytes.attr,
|
|
&dev_attr_nuse.attr,
|
|
#ifdef CONFIG_NVME_MULTIPATH
|
|
&dev_attr_ana_grpid.attr,
|
|
&dev_attr_ana_state.attr,
|
|
#endif
|
|
&dev_attr_io_passthru_err_log_enabled.attr,
|
|
NULL,
|
|
};
|
|
|
|
static umode_t nvme_ns_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) {
|
|
/* per-path attr */
|
|
if (nvme_disk_is_ns_head(dev_to_disk(dev)))
|
|
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_attr_group = {
|
|
.attrs = nvme_ns_attrs,
|
|
.is_visible = nvme_ns_attrs_are_visible,
|
|
};
|
|
|
|
const struct attribute_group *nvme_ns_attr_groups[] = {
|
|
&nvme_ns_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 (!test_bit(NVME_CTRL_STARTED_ONCE, &ctrl->flags))
|
|
return -EBUSY;
|
|
|
|
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);
|
|
unsigned state = (unsigned)nvme_ctrl_state(ctrl);
|
|
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 (state < ARRAY_SIZE(state_name) && state_name[state])
|
|
return sysfs_emit(buf, "%s\n", state_name[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_HOST_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_HOST_AUTH
|
|
&dev_attr_dhchap_secret.attr,
|
|
&dev_attr_dhchap_ctrl_secret.attr,
|
|
#endif
|
|
&dev_attr_adm_passthru_err_log_enabled.attr,
|
|
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_HOST_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);
|
|
|
|
#ifdef CONFIG_NVME_TCP_TLS
|
|
static ssize_t tls_key_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
|
|
|
|
if (!ctrl->tls_pskid)
|
|
return 0;
|
|
return sysfs_emit(buf, "%08x\n", ctrl->tls_pskid);
|
|
}
|
|
static DEVICE_ATTR_RO(tls_key);
|
|
|
|
static ssize_t tls_configured_key_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
|
|
struct key *key = ctrl->opts->tls_key;
|
|
|
|
return sysfs_emit(buf, "%08x\n", key_serial(key));
|
|
}
|
|
static DEVICE_ATTR_RO(tls_configured_key);
|
|
|
|
static ssize_t tls_keyring_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
|
|
struct key *keyring = ctrl->opts->keyring;
|
|
|
|
return sysfs_emit(buf, "%s\n", keyring->description);
|
|
}
|
|
static DEVICE_ATTR_RO(tls_keyring);
|
|
|
|
static struct attribute *nvme_tls_attrs[] = {
|
|
&dev_attr_tls_key.attr,
|
|
&dev_attr_tls_configured_key.attr,
|
|
&dev_attr_tls_keyring.attr,
|
|
NULL,
|
|
};
|
|
|
|
static umode_t nvme_tls_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 (!ctrl->opts || strcmp(ctrl->opts->transport, "tcp"))
|
|
return 0;
|
|
|
|
if (a == &dev_attr_tls_key.attr &&
|
|
!ctrl->opts->tls)
|
|
return 0;
|
|
if (a == &dev_attr_tls_configured_key.attr &&
|
|
!ctrl->opts->tls_key)
|
|
return 0;
|
|
if (a == &dev_attr_tls_keyring.attr &&
|
|
!ctrl->opts->keyring)
|
|
return 0;
|
|
|
|
return a->mode;
|
|
}
|
|
|
|
const struct attribute_group nvme_tls_attrs_group = {
|
|
.attrs = nvme_tls_attrs,
|
|
.is_visible = nvme_tls_attrs_are_visible,
|
|
};
|
|
#endif
|
|
|
|
const struct attribute_group *nvme_dev_attr_groups[] = {
|
|
&nvme_dev_attrs_group,
|
|
#ifdef CONFIG_NVME_TCP_TLS
|
|
&nvme_tls_attrs_group,
|
|
#endif
|
|
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,
|
|
};
|