Skip to content

Commit

Permalink
tree: read all attributes from sysfs when available
Browse files Browse the repository at this point in the history
The kernel already exposes parts or all attributes we are looking up
with the ns id command. By reading these from the sysfs we can remove
the last command we are issuing during the topology scan.

Signed-off-by: Daniel Wagner <dwagner@suse.de>
  • Loading branch information
igaw committed Dec 7, 2023
1 parent 6b61dd4 commit 44adf49
Showing 1 changed file with 126 additions and 48 deletions.
174 changes: 126 additions & 48 deletions src/nvme/tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -2322,60 +2322,141 @@ 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, __u64 *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 = strtoul(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;
}

*res = v;
return 0;
}

static int nvme_ns_init(struct nvme_ns *n)
static int nvme_strtou32(const char *str, __u32 *res)
{
_cleanup_free_ struct nvme_id_ns *ns;
_cleanup_free_ struct nvme_ns_id_desc *descs = NULL;
uint8_t flbas;
int ret;
char *endptr;
__u32 v;

ns = __nvme_alloc(sizeof(*ns));
if (!ns)
return 0;
ret = nvme_ns_identify(n, ns);
if (ret)
errno = 0;
v = strtol(str, &endptr, 0);

if (errno != 0)
return -errno;

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

*res = v;
return 0;
}

static int nvme_strtoi(const char *str, int *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;
}

*res = v;
return 0;
}

#define parse_attr_u32(path, name, var) \
str = nvme_get_attr(path, name); \
if (!str) \
return -ENOENT; \
ret = nvme_strtou32(str, (__u32 *)&var); \
free(str); \
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);
#define parse_attr_u64(path, name, var) \
str = nvme_get_attr(path, name); \
if (!str) \
return -ENOENT; \
ret = nvme_strtou64(str, (__u64 *)&var); \
free(str); \
if (ret) \
return ret;

#define parse_attr_int(path, name, var) \
str = nvme_get_attr(path, name); \
if (!str) \
return -ENOENT; \
ret = nvme_strtoi(str, (int *)&var); \
free(str); \
if (ret) \
return ret;

#define parse_attr_ids(path, name, var) \
str = nvme_get_attr(path, name); \
if (!str) \
return -ENOENT; \
memcpy(&var, str, sizeof(var)); \
free(str);

#define GETSHIFT(x) (__builtin_ffsll(x) - 1)

descs = __nvme_alloc(NVME_IDENTIFY_DATA_SIZE);
if (descs && !nvme_ns_identify_descs(n, descs))
nvme_ns_parse_descriptors(n, descs);
static int nvme_ns_init(const char *path, struct nvme_ns *ns)
{
_cleanup_free_ char *str, *attr = NULL
struct stat sb;
int ret;

parse_attr_u32(path, "nsid", ns->nsid);
parse_attr_u64(path, "size", ns->lba_count);
parse_attr_u64(path, "queue/physical_block_size", ns->lba_size);
ns->lba_shift = GETSHIFT(ns->lba_size);
parse_attr_ids(path, "eui", ns->eui64);
parse_attr_ids(path, "nguid", ns->nguid);
parse_attr_ids(path, "uuid", ns->uuid);

if (asprintf(&attr, "%s/csi", path) < 0)
return -errno;
ret = stat(attr, &sb);
if (ret == 0) {
/* only available on kernels >= 6.x */
parse_attr_ids(path, "csi", ns->csi);
parse_attr_u64(path, "nuse", ns->lba_util);
parse_attr_int(path, "metadata_bytes", ns->meta_size);
} else {
struct nvme_id_ns *id;
uint8_t flbas;

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

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,7 +2475,7 @@ 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;
Expand All @@ -2414,10 +2495,7 @@ static nvme_ns_t nvme_ns_open(const char *name)

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 Down Expand Up @@ -2477,7 +2555,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

0 comments on commit 44adf49

Please sign in to comment.