Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tree: read all attributes from sysfs when available #754

Merged
merged 2 commits into from
Dec 19, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 146 additions & 51 deletions src/nvme/tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -2322,60 +2322,164 @@ int nvme_ns_flush(nvme_ns_t n)
return nvme_flush(nvme_ns_get_fd(n), nvme_ns_get_nsid(n));
}

static void nvme_ns_parse_descriptors(struct nvme_ns *n,
struct nvme_ns_id_desc *descs)
static int nvme_strtou64(const char *str, void *res)
{
void *d = descs;
int i, len;
char *endptr;
__u64 v;

for (i = 0; i < NVME_IDENTIFY_DATA_SIZE; i += len) {
struct nvme_ns_id_desc *desc = d + i;
errno = 0;
v = strtoull(str, &endptr, 0);

if (!desc->nidl)
break;
len = desc->nidl + sizeof(*desc);
if (errno != 0)
return -errno;

switch (desc->nidt) {
case NVME_NIDT_EUI64:
memcpy(n->eui64, desc->nid, sizeof(n->eui64));
break;
case NVME_NIDT_NGUID:
memcpy(n->nguid, desc->nid, sizeof(n->nguid));
break;
case NVME_NIDT_UUID:
memcpy(n->uuid, desc->nid, sizeof(n->uuid));
break;
case NVME_NIDT_CSI:
memcpy(&n->csi, desc->nid, sizeof(n->csi));
break;
if (endptr == str) {
/* no digits found */
return -EINVAL;
}

*(__u64 *)res = v;
return 0;
}

static int nvme_strtou32(const char *str, void *res)
{
char *endptr;
__u32 v;

errno = 0;
v = strtol(str, &endptr, 0);

if (errno != 0)
return -errno;

if (endptr == str) {
/* no digits found */
return -EINVAL;
}

*(__u32 *)res = v;
return 0;
}

static int nvme_strtoi(const char *str, void *res)
{
char *endptr;
int v;

errno = 0;
v = strtol(str, &endptr, 0);

if (errno != 0)
return -errno;

if (endptr == str) {
/* no digits found */
return -EINVAL;
}

*(int *)res = v;
return 0;
}

static int nvme_strtoeuid(const char *str, void *res)
{
memcpy(res, str, 8);
return 0;
}

static int nvme_strtouuid(const char *str, void *res)
{
memcpy(res, str, NVME_UUID_LEN);
return 0;
}

struct sysfs_attr_table {
void *var;
int (*parse)(const char *str, void *res);
bool mandatory;
const char *name;
};

#define GETSHIFT(x) (__builtin_ffsll(x) - 1)
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

static int parse_attrs(const char *path, struct sysfs_attr_table *tbl, int size)
{
char *str;
int ret, i;

for (i = 0; i < size; i++) {
struct sysfs_attr_table *e = &tbl[i];

str = nvme_get_attr(path, e->name);
if (!str) {
if (!e->mandatory)
continue;
return -ENOENT;
}
ret = e->parse(str, e->var);
free(str);
if (ret)
return ret;
}

return 0;
}

static int nvme_ns_init(struct nvme_ns *n)
static int nvme_ns_init(const char *path, struct nvme_ns *ns)
{
_cleanup_free_ struct nvme_id_ns *ns;
_cleanup_free_ struct nvme_ns_id_desc *descs = NULL;
uint8_t flbas;
_cleanup_free_ char *attr = NULL;
struct stat sb;
int ret;

ns = __nvme_alloc(sizeof(*ns));
if (!ns)
return 0;
ret = nvme_ns_identify(n, ns);
struct sysfs_attr_table base[] = {
{ &ns->nsid, nvme_strtou32, true, "nsid" },
{ &ns->lba_count, nvme_strtou64, true, "size" },
{ &ns->lba_size, nvme_strtou64, true, "queue/physical_block_size" },
{ ns->eui64, nvme_strtoeuid, false, "eui" },
{ ns->nguid, nvme_strtouuid, false, "nguid" },
{ ns->uuid, nvme_strtouuid, false, "uuid" }
};

ret = parse_attrs(path, base, ARRAY_SIZE(base));
if (ret)
return ret;

nvme_id_ns_flbas_to_lbaf_inuse(ns->flbas, &flbas);
n->lba_shift = ns->lbaf[flbas].ds;
n->lba_size = 1 << n->lba_shift;
n->lba_count = le64_to_cpu(ns->nsze);
n->lba_util = le64_to_cpu(ns->nuse);
n->meta_size = le16_to_cpu(ns->lbaf[flbas].ms);
ns->lba_shift = GETSHIFT(ns->lba_size);

if (asprintf(&attr, "%s/csi", path) < 0)
return -errno;
ret = stat(attr, &sb);
if (ret == 0) {
/* only available on kernels >= 6.8 */
struct sysfs_attr_table ext[] = {
{ &ns->csi, nvme_strtoi, true, "csi" },
{ &ns->lba_util, nvme_strtou64, true, "nuse" },
{ &ns->meta_size, nvme_strtoi, true, "metadata_bytes"},

};

ret = parse_attrs(path, ext, ARRAY_SIZE(ext));
if (ret)
return ret;
} else {
struct nvme_id_ns *id;
uint8_t flbas;

id = __nvme_alloc(sizeof(*ns));
if (!id)
return -ENOMEM;

descs = __nvme_alloc(NVME_IDENTIFY_DATA_SIZE);
if (descs && !nvme_ns_identify_descs(n, descs))
nvme_ns_parse_descriptors(n, descs);
ret = nvme_ns_identify(ns, id);
if (ret)
free(ns);

nvme_id_ns_flbas_to_lbaf_inuse(id->flbas, &flbas);
ns->lba_count = le64_to_cpu(id->nsze);
ns->lba_util = le64_to_cpu(id->nuse);
ns->meta_size = le16_to_cpu(id->lbaf[flbas].ms);
}

return 0;
}
Expand All @@ -2394,10 +2498,9 @@ static void nvme_ns_set_generic_name(struct nvme_ns *n, const char *name)
n->generic_name = strdup(generic_name);
}

static nvme_ns_t nvme_ns_open(const char *name)
static nvme_ns_t nvme_ns_open(const char *sys_path, const char *name)
{
struct nvme_ns *n;
int fd;

n = calloc(1, sizeof(*n));
if (!n) {
Expand All @@ -2408,16 +2511,9 @@ static nvme_ns_t nvme_ns_open(const char *name)
n->fd = -1;
n->name = strdup(name);

fd = nvme_ns_get_fd(n);
if (fd < 0)
goto free_ns;

nvme_ns_set_generic_name(n, name);

if (nvme_get_nsid(fd, &n->nsid) < 0)
goto free_ns;

if (nvme_ns_init(n) != 0)
if (nvme_ns_init(sys_path, n) != 0)
goto free_ns;

list_head_init(&n->paths);
Expand All @@ -2427,7 +2523,6 @@ static nvme_ns_t nvme_ns_open(const char *name)
return n;

free_ns:
nvme_ns_release_fd(n);
free(n->generic_name);
free(n->name);
free(n);
Expand Down Expand Up @@ -2477,7 +2572,7 @@ static struct nvme_ns *__nvme_scan_namespace(const char *sysfs_dir, const char *
return NULL;
}

n = nvme_ns_open(blkdev);
n = nvme_ns_open(path, blkdev);
if (!n)
return NULL;

Expand Down