13c6ffbabSRob Johnston /*
23c6ffbabSRob Johnston * This file and its contents are supplied under the terms of the
33c6ffbabSRob Johnston * Common Development and Distribution License ("CDDL"), version 1.0.
43c6ffbabSRob Johnston * You may only use this file in accordance with the terms of version
53c6ffbabSRob Johnston * 1.0 of the CDDL.
63c6ffbabSRob Johnston *
73c6ffbabSRob Johnston * A full copy of the text of the CDDL should have accompanied this
83c6ffbabSRob Johnston * source. A copy of the CDDL is also available via the Internet at
93c6ffbabSRob Johnston * http://www.illumos.org/license/CDDL.
103c6ffbabSRob Johnston */
113c6ffbabSRob Johnston
123c6ffbabSRob Johnston /*
133c6ffbabSRob Johnston * Copyright 2020 Joyent, Inc.
14153f3212SHans Rosenfeld * Copyright 2022 Tintri by DDN, Inc. All rights reserved.
15*533affcbSRobert Mustacchi * Copyright 2024 Oxide Computer Company
163c6ffbabSRob Johnston */
173c6ffbabSRob Johnston
183c6ffbabSRob Johnston /*
193c6ffbabSRob Johnston * This file drives topo node enumeration of NVMe controllers. A single "nvme"
203c6ffbabSRob Johnston * node is enumerated for each NVMe controller. Child "disk" nodes are then
21744642a2SRobert Mustacchi * enumerated for each active or attached NVMe namespace.
223c6ffbabSRob Johnston *
233c6ffbabSRob Johnston * nvme nodes are expected to be enumerated under either a "bay" node (for U.2
243c6ffbabSRob Johnston * devices) or a "slot" node (for M.2 devices) or a "pciexfn" node (for AIC
253c6ffbabSRob Johnston * devices).
263c6ffbabSRob Johnston *
273c6ffbabSRob Johnston * Enumeration of NVMe controllers on PCIe add-in cards is automatically driven
283c6ffbabSRob Johnston * by the pcibus topo module.
293c6ffbabSRob Johnston *
303c6ffbabSRob Johnston * In order to allow for associating a given NVMe controller with a physical
313c6ffbabSRob Johnston * location, enumeration of U.2 and M.2 devices should be driven by a
323c6ffbabSRob Johnston * platform-specific topo map which statically sets the following two
333c6ffbabSRob Johnston * properties on the parent "bay" or "slot" node:
343c6ffbabSRob Johnston *
353c6ffbabSRob Johnston * propgroup property description
363c6ffbabSRob Johnston * --------- -------- ------------
373c6ffbabSRob Johnston * binding driver "nvme"
383c6ffbabSRob Johnston * binding parent-device devpath of parent PCIe device
393c6ffbabSRob Johnston *
403c6ffbabSRob Johnston * for example:
413c6ffbabSRob Johnston *
423c6ffbabSRob Johnston * <propgroup name="binding" version="1" name-stability="Private"
433c6ffbabSRob Johnston * data-stability="Private">
443c6ffbabSRob Johnston * <propval name="driver" type="string" value="nvme"/>
453c6ffbabSRob Johnston * <propval name="parent-device" type="string"
463c6ffbabSRob Johnston * value="/pci@0,0/pci8086,6f09@3,1"/>
473c6ffbabSRob Johnston * </propgroup>
483c6ffbabSRob Johnston * <dependents grouping="children">
493c6ffbabSRob Johnston * <range name="nvme" min="0" max="0">
503c6ffbabSRob Johnston * <enum-method name="disk" version="1"/>
513c6ffbabSRob Johnston * </range>
523c6ffbabSRob Johnston * </dependents>
533c6ffbabSRob Johnston */
543c6ffbabSRob Johnston #include <stdlib.h>
553c6ffbabSRob Johnston #include <sys/types.h>
563c6ffbabSRob Johnston #include <sys/stat.h>
573c6ffbabSRob Johnston #include <fcntl.h>
583c6ffbabSRob Johnston #include <unistd.h>
593c6ffbabSRob Johnston #include <string.h>
603c6ffbabSRob Johnston #include <strings.h>
61744642a2SRobert Mustacchi #include <stdbool.h>
623c6ffbabSRob Johnston
633c6ffbabSRob Johnston #include <sys/fm/protocol.h>
643c6ffbabSRob Johnston #include <fm/topo_hc.h>
653c6ffbabSRob Johnston #include <fm/topo_mod.h>
66744642a2SRobert Mustacchi #include <topo_ufm.h>
673c6ffbabSRob Johnston
683c6ffbabSRob Johnston #include <sys/dkio.h>
693c6ffbabSRob Johnston #include <sys/scsi/generic/inquiry.h>
703c6ffbabSRob Johnston
71*533affcbSRobert Mustacchi #include <libnvme.h>
723c6ffbabSRob Johnston #include "disk.h"
733c6ffbabSRob Johnston #include "disk_drivers.h"
743c6ffbabSRob Johnston
753c6ffbabSRob Johnston typedef struct nvme_enum_info {
763c6ffbabSRob Johnston topo_mod_t *nei_mod;
773c6ffbabSRob Johnston di_node_t nei_dinode;
78*533affcbSRobert Mustacchi nvme_t *nei_libnvme;
79*533affcbSRobert Mustacchi nvme_ctrl_t *nei_ctrl;
80*533affcbSRobert Mustacchi nvme_ctrl_info_t *nei_ctrl_info;
81*533affcbSRobert Mustacchi const nvme_version_t *nei_vers;
823c6ffbabSRob Johnston tnode_t *nei_parent;
833c6ffbabSRob Johnston tnode_t *nei_nvme;
843c6ffbabSRob Johnston nvlist_t *nei_nvme_fmri;
853c6ffbabSRob Johnston int nei_fd;
863c6ffbabSRob Johnston } nvme_enum_info_t;
873c6ffbabSRob Johnston
883c6ffbabSRob Johnston typedef struct devlink_arg {
893c6ffbabSRob Johnston topo_mod_t *dla_mod;
903c6ffbabSRob Johnston char *dla_logical_disk;
913c6ffbabSRob Johnston uint_t dla_strsz;
923c6ffbabSRob Johnston } devlink_arg_t;
933c6ffbabSRob Johnston
943c6ffbabSRob Johnston static int
devlink_cb(di_devlink_t dl,void * arg)953c6ffbabSRob Johnston devlink_cb(di_devlink_t dl, void *arg)
963c6ffbabSRob Johnston {
973c6ffbabSRob Johnston devlink_arg_t *dlarg = (devlink_arg_t *)arg;
983c6ffbabSRob Johnston topo_mod_t *mod = dlarg->dla_mod;
993c6ffbabSRob Johnston const char *devpath;
1003c6ffbabSRob Johnston char *slice, *ctds;
1013c6ffbabSRob Johnston
1023c6ffbabSRob Johnston if ((devpath = di_devlink_path(dl)) == NULL ||
1033c6ffbabSRob Johnston (dlarg->dla_logical_disk = topo_mod_strdup(mod, devpath)) ==
1043c6ffbabSRob Johnston NULL) {
1053c6ffbabSRob Johnston return (DI_WALK_TERMINATE);
1063c6ffbabSRob Johnston }
1073c6ffbabSRob Johnston
1083c6ffbabSRob Johnston /*
1093c6ffbabSRob Johnston * We need to keep track of the original string size before we
1103c6ffbabSRob Johnston * truncate it with a NUL, so that we can free the right number of
1113c6ffbabSRob Johnston * bytes when we're done, otherwise libumem will complain.
1123c6ffbabSRob Johnston */
1133c6ffbabSRob Johnston dlarg->dla_strsz = strlen(dlarg->dla_logical_disk) + 1;
1143c6ffbabSRob Johnston
1153c6ffbabSRob Johnston /* trim the slice off the public name */
1163c6ffbabSRob Johnston if (((ctds = strrchr(dlarg->dla_logical_disk, '/')) != NULL) &&
1173c6ffbabSRob Johnston ((slice = strchr(ctds, 's')) != NULL))
1183c6ffbabSRob Johnston *slice = '\0';
1193c6ffbabSRob Johnston
1203c6ffbabSRob Johnston return (DI_WALK_TERMINATE);
1213c6ffbabSRob Johnston }
1223c6ffbabSRob Johnston
1233c6ffbabSRob Johnston static char *
get_logical_disk(topo_mod_t * mod,const char * devpath,uint_t * bufsz)1243c6ffbabSRob Johnston get_logical_disk(topo_mod_t *mod, const char *devpath, uint_t *bufsz)
1253c6ffbabSRob Johnston {
1263c6ffbabSRob Johnston di_devlink_handle_t devhdl;
1273c6ffbabSRob Johnston devlink_arg_t dlarg = { 0 };
1283c6ffbabSRob Johnston char *minorpath = NULL;
1293c6ffbabSRob Johnston
1303c6ffbabSRob Johnston if (asprintf(&minorpath, "%s:a", devpath) < 0) {
1313c6ffbabSRob Johnston return (NULL);
1323c6ffbabSRob Johnston }
1333c6ffbabSRob Johnston
1343c6ffbabSRob Johnston if ((devhdl = di_devlink_init(NULL, 0)) == DI_NODE_NIL) {
1353c6ffbabSRob Johnston topo_mod_dprintf(mod, "%s: di_devlink_init failed", __func__);
1363c6ffbabSRob Johnston free(minorpath);
1373c6ffbabSRob Johnston return (NULL);
1383c6ffbabSRob Johnston }
1393c6ffbabSRob Johnston
1403c6ffbabSRob Johnston dlarg.dla_mod = mod;
1413c6ffbabSRob Johnston
1423c6ffbabSRob Johnston (void) di_devlink_walk(devhdl, "^dsk/", minorpath, DI_PRIMARY_LINK,
1433c6ffbabSRob Johnston &dlarg, devlink_cb);
1443c6ffbabSRob Johnston
1453c6ffbabSRob Johnston (void) di_devlink_fini(&devhdl);
1463c6ffbabSRob Johnston free(minorpath);
1473c6ffbabSRob Johnston
1483c6ffbabSRob Johnston *bufsz = dlarg.dla_strsz;
1493c6ffbabSRob Johnston return (dlarg.dla_logical_disk);
1503c6ffbabSRob Johnston }
1513c6ffbabSRob Johnston
152744642a2SRobert Mustacchi static bool
disk_nvme_make_ns_serial(topo_mod_t * mod,nvme_ns_info_t * ns_info,char * buf,size_t buflen)153*533affcbSRobert Mustacchi disk_nvme_make_ns_serial(topo_mod_t *mod, nvme_ns_info_t *ns_info, char *buf,
154*533affcbSRobert Mustacchi size_t buflen)
1553c6ffbabSRob Johnston {
156*533affcbSRobert Mustacchi uint8_t nguid[16], eui64[8];
157744642a2SRobert Mustacchi int ret;
1583c6ffbabSRob Johnston
159*533affcbSRobert Mustacchi if (nvme_ns_info_nguid(ns_info, nguid)) {
160744642a2SRobert Mustacchi ret = snprintf(buf, buflen, "%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X"
161744642a2SRobert Mustacchi "%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X",
162*533affcbSRobert Mustacchi nguid[0], nguid[1], nguid[2], nguid[3], nguid[4],
163*533affcbSRobert Mustacchi nguid[5], nguid[6], nguid[7], nguid[8], nguid[9],
164*533affcbSRobert Mustacchi nguid[10], nguid[11], nguid[12], nguid[13], nguid[14],
165*533affcbSRobert Mustacchi nguid[15]);
166*533affcbSRobert Mustacchi } else if (nvme_ns_info_eui64(ns_info, eui64)) {
167744642a2SRobert Mustacchi ret = snprintf(buf, buflen,
168744642a2SRobert Mustacchi "%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X",
169*533affcbSRobert Mustacchi eui64[0], eui64[1], eui64[2], eui64[3], eui64[4],
170*533affcbSRobert Mustacchi eui64[5], eui64[6], eui64[7]);
1713c6ffbabSRob Johnston } else {
172*533affcbSRobert Mustacchi ret = snprintf(buf, buflen, "%u", nvme_ns_info_nsid(ns_info));
173744642a2SRobert Mustacchi }
174744642a2SRobert Mustacchi
175744642a2SRobert Mustacchi if ((size_t)ret >= buflen) {
176744642a2SRobert Mustacchi topo_mod_dprintf(mod, "overflowed serial number for nsid %u: "
177*533affcbSRobert Mustacchi "needed %zu bytes, got %d", nvme_ns_info_nsid(ns_info),
178*533affcbSRobert Mustacchi buflen, ret);
179744642a2SRobert Mustacchi return (false);
180744642a2SRobert Mustacchi }
181744642a2SRobert Mustacchi
182744642a2SRobert Mustacchi return (true);
1833c6ffbabSRob Johnston }
1843c6ffbabSRob Johnston
1853c6ffbabSRob Johnston /*
186744642a2SRobert Mustacchi * Create the common I/O property group properties that are shared between
187744642a2SRobert Mustacchi * controllers and namespaces. We assume the property group was already created.
1883c6ffbabSRob Johnston */
189744642a2SRobert Mustacchi static bool
disk_nvme_common_io(topo_mod_t * mod,tnode_t * tn,di_node_t di)190744642a2SRobert Mustacchi disk_nvme_common_io(topo_mod_t *mod, tnode_t *tn, di_node_t di)
191744642a2SRobert Mustacchi {
192744642a2SRobert Mustacchi int err;
193744642a2SRobert Mustacchi int inst = di_instance(di);
194744642a2SRobert Mustacchi const char *drv = di_driver_name(di);
195744642a2SRobert Mustacchi char *path;
196744642a2SRobert Mustacchi const char *ppaths[1];
1973c6ffbabSRob Johnston
198744642a2SRobert Mustacchi if (inst != -1 && topo_prop_set_uint32(tn, TOPO_PGROUP_IO,
199744642a2SRobert Mustacchi TOPO_IO_INSTANCE, TOPO_PROP_IMMUTABLE, (uint32_t)inst, &err) != 0) {
200744642a2SRobert Mustacchi topo_mod_dprintf(mod, "failed to set %s:%s on %s[%" PRIu64 "]: "
201744642a2SRobert Mustacchi "%s", TOPO_PGROUP_IO, TOPO_IO_INSTANCE, topo_node_name(tn),
202744642a2SRobert Mustacchi topo_node_instance(tn), topo_strerror(err));
203744642a2SRobert Mustacchi return (false);
2043c6ffbabSRob Johnston }
2053c6ffbabSRob Johnston
206744642a2SRobert Mustacchi if (drv != NULL && topo_prop_set_string(tn, TOPO_PGROUP_IO,
207744642a2SRobert Mustacchi TOPO_IO_DRIVER, TOPO_PROP_IMMUTABLE, drv, &err) != 0) {
208744642a2SRobert Mustacchi topo_mod_dprintf(mod, "failed to set %s:%s on %s[%" PRIu64 "]: "
209744642a2SRobert Mustacchi "%s", TOPO_PGROUP_IO, TOPO_IO_DRIVER, topo_node_name(tn),
210744642a2SRobert Mustacchi topo_node_instance(tn), topo_strerror(err));
211744642a2SRobert Mustacchi return (false);
212744642a2SRobert Mustacchi }
2133c6ffbabSRob Johnston
214744642a2SRobert Mustacchi if (drv != NULL) {
215744642a2SRobert Mustacchi nvlist_t *fmri = topo_mod_modfmri(mod, FM_MOD_SCHEME_VERSION,
216744642a2SRobert Mustacchi drv);
217744642a2SRobert Mustacchi if (mod != NULL && topo_prop_set_fmri(tn, TOPO_PGROUP_IO,
218744642a2SRobert Mustacchi TOPO_IO_MODULE, TOPO_PROP_IMMUTABLE, fmri, &err) != 0) {
219744642a2SRobert Mustacchi topo_mod_dprintf(mod, "failed to set %s:%s on %s[%"
220744642a2SRobert Mustacchi PRIu64 "]: %s", TOPO_PGROUP_IO, TOPO_IO_MODULE,
221744642a2SRobert Mustacchi topo_node_name(tn), topo_node_instance(tn),
2223c6ffbabSRob Johnston topo_strerror(err));
223744642a2SRobert Mustacchi nvlist_free(fmri);
224744642a2SRobert Mustacchi return (false);
225744642a2SRobert Mustacchi }
226744642a2SRobert Mustacchi nvlist_free(fmri);
2273c6ffbabSRob Johnston }
2283c6ffbabSRob Johnston
229744642a2SRobert Mustacchi path = di_devfs_path(di);
2303c6ffbabSRob Johnston ppaths[0] = path;
231744642a2SRobert Mustacchi if (path != NULL && topo_prop_set_string(tn, TOPO_PGROUP_IO,
232744642a2SRobert Mustacchi TOPO_IO_DEV_PATH, TOPO_PROP_IMMUTABLE, path, &err) != 0) {
233744642a2SRobert Mustacchi topo_mod_dprintf(mod, "failed to set %s:%s on %s[%" PRIu64 "]: "
234744642a2SRobert Mustacchi "%s", TOPO_PGROUP_IO, TOPO_IO_DRIVER, topo_node_name(tn),
235744642a2SRobert Mustacchi topo_node_instance(tn), topo_strerror(err));
236744642a2SRobert Mustacchi di_devfs_path_free(path);
237744642a2SRobert Mustacchi return (false);
238744642a2SRobert Mustacchi }
239744642a2SRobert Mustacchi
240744642a2SRobert Mustacchi if (path != NULL && topo_prop_set_string_array(tn, TOPO_PGROUP_IO,
241744642a2SRobert Mustacchi TOPO_IO_PHYS_PATH, TOPO_PROP_IMMUTABLE, ppaths, 1, &err) != 0) {
242744642a2SRobert Mustacchi topo_mod_dprintf(mod, "failed to set %s:%s on %s[%" PRIu64 "]: "
243744642a2SRobert Mustacchi "%s", TOPO_PGROUP_IO, TOPO_IO_PHYS_PATH, topo_node_name(tn),
244744642a2SRobert Mustacchi topo_node_instance(tn), topo_strerror(err));
245744642a2SRobert Mustacchi di_devfs_path_free(path);
246744642a2SRobert Mustacchi return (false);
247744642a2SRobert Mustacchi }
248744642a2SRobert Mustacchi di_devfs_path_free(path);
249744642a2SRobert Mustacchi
250744642a2SRobert Mustacchi return (true);
251744642a2SRobert Mustacchi }
2523c6ffbabSRob Johnston
2533c6ffbabSRob Johnston /*
254744642a2SRobert Mustacchi * Add the various storage and I/O property group items that are appropriate
255744642a2SRobert Mustacchi * given that we have a devinfo node. The storage property group has already
256744642a2SRobert Mustacchi * been created, but the I/O property group has not.
2573c6ffbabSRob Johnston */
258744642a2SRobert Mustacchi static void
disk_nvme_make_ns_di_props(topo_mod_t * mod,tnode_t * tn,di_node_t di)259744642a2SRobert Mustacchi disk_nvme_make_ns_di_props(topo_mod_t *mod, tnode_t *tn, di_node_t di)
260744642a2SRobert Mustacchi {
261744642a2SRobert Mustacchi int err;
262744642a2SRobert Mustacchi char *devid, *mfg, *model, *rev, *serial, *log, *path;
263744642a2SRobert Mustacchi uint_t buflen;
264744642a2SRobert Mustacchi
265744642a2SRobert Mustacchi if (di_prop_lookup_strings(DDI_DEV_T_ANY, di, DEVID_PROP_NAME,
266744642a2SRobert Mustacchi &devid) != 1 ||
267744642a2SRobert Mustacchi di_prop_lookup_strings(DDI_DEV_T_ANY, di, INQUIRY_VENDOR_ID,
268744642a2SRobert Mustacchi &mfg) != 1 ||
269744642a2SRobert Mustacchi di_prop_lookup_strings(DDI_DEV_T_ANY, di, INQUIRY_PRODUCT_ID,
270744642a2SRobert Mustacchi &model) != 1 ||
271744642a2SRobert Mustacchi di_prop_lookup_strings(DDI_DEV_T_ANY, di, INQUIRY_REVISION_ID,
272744642a2SRobert Mustacchi &rev) != 1 ||
273744642a2SRobert Mustacchi di_prop_lookup_strings(DDI_DEV_T_ANY, di, INQUIRY_SERIAL_NO,
274744642a2SRobert Mustacchi &serial) != 1) {
275744642a2SRobert Mustacchi topo_mod_dprintf(mod, "failed to get devinfo props for %s[%"
276744642a2SRobert Mustacchi PRIu64 "]", topo_node_name(tn), topo_node_instance(tn));
277744642a2SRobert Mustacchi return;
2783c6ffbabSRob Johnston }
2793c6ffbabSRob Johnston
280744642a2SRobert Mustacchi /*
281744642a2SRobert Mustacchi * Set the basic storage manufacturer information. Yes, this is
282744642a2SRobert Mustacchi * information really about the NVMe controller and not the namespace.
283744642a2SRobert Mustacchi * That's how the storage property group basically works here.
284744642a2SRobert Mustacchi */
285744642a2SRobert Mustacchi if (topo_prop_set_string(tn, TOPO_PGROUP_STORAGE,
286744642a2SRobert Mustacchi TOPO_STORAGE_MANUFACTURER, TOPO_PROP_IMMUTABLE, mfg, &err) != 0 ||
287744642a2SRobert Mustacchi topo_prop_set_string(tn, TOPO_PGROUP_STORAGE,
288744642a2SRobert Mustacchi TOPO_STORAGE_SERIAL_NUM, TOPO_PROP_IMMUTABLE, serial, &err) != 0 ||
289744642a2SRobert Mustacchi topo_prop_set_string(tn, TOPO_PGROUP_STORAGE,
290744642a2SRobert Mustacchi TOPO_STORAGE_FIRMWARE_REV, TOPO_PROP_IMMUTABLE, rev, &err) != 0 ||
291744642a2SRobert Mustacchi topo_prop_set_string(tn, TOPO_PGROUP_STORAGE,
292744642a2SRobert Mustacchi TOPO_STORAGE_MODEL, TOPO_PROP_IMMUTABLE, model, &err) != 0) {
293744642a2SRobert Mustacchi topo_mod_dprintf(mod, "failed to set storage properties on "
294744642a2SRobert Mustacchi "%s[%" PRIu64 "]: %s", topo_node_name(tn),
295744642a2SRobert Mustacchi topo_node_instance(tn), topo_strerror(err));
296744642a2SRobert Mustacchi return;
2973c6ffbabSRob Johnston }
2983c6ffbabSRob Johnston
299744642a2SRobert Mustacchi if (topo_pgroup_create(tn, &io_pgroup, &err) != 0) {
300744642a2SRobert Mustacchi topo_mod_dprintf(mod, "failed to create I/O property "
301744642a2SRobert Mustacchi "group on %s[%" PRIu64 "]: %s", topo_node_name(tn),
302744642a2SRobert Mustacchi topo_node_instance(tn), topo_strerror(err));
303744642a2SRobert Mustacchi }
3043c6ffbabSRob Johnston
305744642a2SRobert Mustacchi if (!disk_nvme_common_io(mod, tn, di)) {
306744642a2SRobert Mustacchi return;
307744642a2SRobert Mustacchi }
308744642a2SRobert Mustacchi
309744642a2SRobert Mustacchi /*
310744642a2SRobert Mustacchi * The last property that we'd like to attempt to create for a namespace
311744642a2SRobert Mustacchi * is a mapping back to its corresponding logical disk entry in /dev.
312744642a2SRobert Mustacchi * The logical disk will be everything past the trailing /, i.e. a
313744642a2SRobert Mustacchi * cXtXdX value.
314744642a2SRobert Mustacchi */
315744642a2SRobert Mustacchi path = di_devfs_path(di);
316744642a2SRobert Mustacchi if (path == NULL) {
317744642a2SRobert Mustacchi return;
318744642a2SRobert Mustacchi }
319744642a2SRobert Mustacchi log = get_logical_disk(mod, path, &buflen);
3203c6ffbabSRob Johnston di_devfs_path_free(path);
321744642a2SRobert Mustacchi if (log == NULL) {
322744642a2SRobert Mustacchi return;
323744642a2SRobert Mustacchi }
324744642a2SRobert Mustacchi path = strrchr(log, '/');
325744642a2SRobert Mustacchi if (path != NULL && path[1] != '\0' &&
326744642a2SRobert Mustacchi topo_prop_set_string(tn, TOPO_PGROUP_STORAGE,
327744642a2SRobert Mustacchi TOPO_STORAGE_LOGICAL_DISK_NAME, TOPO_PROP_IMMUTABLE, path + 1,
328744642a2SRobert Mustacchi &err) != 0) {
329744642a2SRobert Mustacchi topo_mod_dprintf(mod, "failed to set %s:%s on %s[%"
330744642a2SRobert Mustacchi PRIu64 "]: %s", TOPO_PGROUP_STORAGE,
331744642a2SRobert Mustacchi TOPO_STORAGE_LOGICAL_DISK_NAME, topo_node_name(tn),
332744642a2SRobert Mustacchi topo_node_instance(tn), topo_strerror(err));
333744642a2SRobert Mustacchi }
334744642a2SRobert Mustacchi topo_mod_free(mod, log, buflen);
335744642a2SRobert Mustacchi }
336744642a2SRobert Mustacchi
337744642a2SRobert Mustacchi static void
disk_nvme_make_ns(nvme_enum_info_t * nei,nvme_ns_info_t * ns_info)338*533affcbSRobert Mustacchi disk_nvme_make_ns(nvme_enum_info_t *nei, nvme_ns_info_t *ns_info)
339744642a2SRobert Mustacchi {
340744642a2SRobert Mustacchi topo_mod_t *mod = nei->nei_mod;
341744642a2SRobert Mustacchi nvlist_t *auth = NULL, *fmri = NULL;
342*533affcbSRobert Mustacchi const uint32_t nsid = nvme_ns_info_nsid(ns_info);
343744642a2SRobert Mustacchi const topo_instance_t inst = nsid - 1;
344744642a2SRobert Mustacchi char serial[64], capstr[64];
345*533affcbSRobert Mustacchi const nvme_nvm_lba_fmt_t *fmt;
346*533affcbSRobert Mustacchi const char *bd_addr;
347*533affcbSRobert Mustacchi uint64_t cap, blksz, capblks;
348744642a2SRobert Mustacchi tnode_t *tn;
349744642a2SRobert Mustacchi int err;
350744642a2SRobert Mustacchi
351744642a2SRobert Mustacchi auth = topo_mod_auth(mod, nei->nei_nvme);
352744642a2SRobert Mustacchi if (auth == NULL) {
353744642a2SRobert Mustacchi topo_mod_dprintf(mod, "failed to get auth for nsid %u from "
354744642a2SRobert Mustacchi "parent %s[%" PRIu64 "]: %s", nsid,
355744642a2SRobert Mustacchi topo_node_name(nei->nei_nvme),
356744642a2SRobert Mustacchi topo_node_instance(nei->nei_nvme), topo_mod_errmsg(mod));
357744642a2SRobert Mustacchi goto done;
358744642a2SRobert Mustacchi }
359744642a2SRobert Mustacchi
360744642a2SRobert Mustacchi /*
361744642a2SRobert Mustacchi * We want to construct the FMRI for the namespace. The namespace is a
362744642a2SRobert Mustacchi * little awkward in terms of things like the model, revision, and
363744642a2SRobert Mustacchi * serial. While blkdev sets up standard inquiry properties to map these
364744642a2SRobert Mustacchi * to the parent device which makes sense in the context of trying to
365744642a2SRobert Mustacchi * use this as a normal block device, it's not really appropriate here.
366744642a2SRobert Mustacchi * The namespace is not the NVMe controller. We construct the namespace
367744642a2SRobert Mustacchi * serial number from the preferential ordering of information that
368744642a2SRobert Mustacchi * we're given of the NGUID, EUI64, and then fall back to the namespace
369744642a2SRobert Mustacchi * number.
370744642a2SRobert Mustacchi */
371*533affcbSRobert Mustacchi if (!disk_nvme_make_ns_serial(mod, ns_info, serial, sizeof (serial))) {
372744642a2SRobert Mustacchi goto done;
373744642a2SRobert Mustacchi }
374744642a2SRobert Mustacchi fmri = topo_mod_hcfmri(mod, nei->nei_nvme, FM_HC_SCHEME_VERSION,
375744642a2SRobert Mustacchi DISK, inst, NULL, auth, NULL, NULL, serial);
376744642a2SRobert Mustacchi if (fmri == NULL) {
377744642a2SRobert Mustacchi topo_mod_dprintf(mod, "failed to make fmri for %s[%" PRIu64
378744642a2SRobert Mustacchi "] on nsid %u: %s", DISK, inst, nsid, topo_mod_errmsg(mod));
379744642a2SRobert Mustacchi goto done;
380744642a2SRobert Mustacchi }
381744642a2SRobert Mustacchi
382744642a2SRobert Mustacchi tn = topo_node_bind(mod, nei->nei_nvme, DISK, inst, fmri);
383744642a2SRobert Mustacchi if (tn == NULL) {
384744642a2SRobert Mustacchi topo_mod_dprintf(mod, "failed to bind fmri for %s[%" PRIu64
385744642a2SRobert Mustacchi "] on nsid %u: %s", DISK, inst, nsid, topo_mod_errmsg(mod));
386744642a2SRobert Mustacchi goto done;
387744642a2SRobert Mustacchi }
388744642a2SRobert Mustacchi
389744642a2SRobert Mustacchi /*
390744642a2SRobert Mustacchi * Always inherit our parent's FRU. The namespace is just a part of the
391744642a2SRobert Mustacchi * device in reality.
392744642a2SRobert Mustacchi */
393744642a2SRobert Mustacchi if (topo_node_fru_set(tn, NULL, 0, &err) != 0) {
394744642a2SRobert Mustacchi topo_mod_dprintf(mod, "failed to set FRU for %s[%" PRIu64
395744642a2SRobert Mustacchi "] on nsid %u: %s", DISK, inst, nsid, topo_strerror(err));
396744642a2SRobert Mustacchi goto done;
397744642a2SRobert Mustacchi
398744642a2SRobert Mustacchi }
399744642a2SRobert Mustacchi
400744642a2SRobert Mustacchi /*
401744642a2SRobert Mustacchi * Our namespace may or may not be attached. From the namespace we will
402744642a2SRobert Mustacchi * always get the capacity and block information. The rest of it will
403744642a2SRobert Mustacchi * end up being filled in if we find a devinfo node.
404744642a2SRobert Mustacchi */
405744642a2SRobert Mustacchi if (topo_pgroup_create(tn, &storage_pgroup, &err) != 0) {
406744642a2SRobert Mustacchi topo_mod_dprintf(mod, "failed to create storage property "
407744642a2SRobert Mustacchi "group on %s[%" PRIu64 "]: %s", DISK, inst,
408744642a2SRobert Mustacchi topo_strerror(err));
409744642a2SRobert Mustacchi }
410744642a2SRobert Mustacchi
411*533affcbSRobert Mustacchi if (!nvme_ns_info_curformat(ns_info, &fmt)) {
412*533affcbSRobert Mustacchi topo_mod_dprintf(mod, "failed to get current namespace "
413*533affcbSRobert Mustacchi "format: %s", nvme_ns_info_errmsg(ns_info));
414*533affcbSRobert Mustacchi goto done;
415*533affcbSRobert Mustacchi }
416*533affcbSRobert Mustacchi
417*533affcbSRobert Mustacchi blksz = nvme_nvm_lba_fmt_data_size(fmt);
418*533affcbSRobert Mustacchi if (topo_prop_set_uint64(tn, TOPO_PGROUP_STORAGE,
419744642a2SRobert Mustacchi TOPO_STORAGE_LOG_BLOCK_SIZE, TOPO_PROP_IMMUTABLE, blksz, &err) !=
420744642a2SRobert Mustacchi 0) {
421744642a2SRobert Mustacchi topo_mod_dprintf(mod, "failed to create property %s:%s on %s[%"
422744642a2SRobert Mustacchi PRIu64 "]: %s", TOPO_PGROUP_STORAGE,
423744642a2SRobert Mustacchi TOPO_STORAGE_LOG_BLOCK_SIZE, DISK, inst,
424744642a2SRobert Mustacchi topo_strerror(err));
425744642a2SRobert Mustacchi goto done;
426744642a2SRobert Mustacchi }
427744642a2SRobert Mustacchi
428*533affcbSRobert Mustacchi if (!nvme_ns_info_cap(ns_info, &capblks)) {
429*533affcbSRobert Mustacchi topo_mod_dprintf(mod, "failed to get namespace capacity: %s",
430*533affcbSRobert Mustacchi nvme_ns_info_errmsg(ns_info));
431*533affcbSRobert Mustacchi goto done;
432*533affcbSRobert Mustacchi }
433*533affcbSRobert Mustacchi
434*533affcbSRobert Mustacchi cap = blksz * capblks;
435744642a2SRobert Mustacchi if (snprintf(capstr, sizeof (capstr), "%" PRIu64, cap) >=
436744642a2SRobert Mustacchi sizeof (capstr)) {
437744642a2SRobert Mustacchi topo_mod_dprintf(mod, "overflowed capacity calculation on "
438744642a2SRobert Mustacchi "nsid %u", nsid);
439744642a2SRobert Mustacchi goto done;
440744642a2SRobert Mustacchi }
441744642a2SRobert Mustacchi
442744642a2SRobert Mustacchi /*
443744642a2SRobert Mustacchi * Finally attempt to find a child node that has a matching name and go
444744642a2SRobert Mustacchi * from there. Sorry, this does result in node creation being O(n^2),
445*533affcbSRobert Mustacchi * but at least n is usually small today. Note, we may not have a blkdev
446*533affcbSRobert Mustacchi * address because the disk may not be attached.
447744642a2SRobert Mustacchi */
448*533affcbSRobert Mustacchi if (!nvme_ns_info_bd_addr(ns_info, &bd_addr)) {
449*533affcbSRobert Mustacchi if (nvme_ns_info_err(ns_info) != NVME_INFO_ERR_NS_NO_BLKDEV) {
450*533affcbSRobert Mustacchi topo_mod_dprintf(mod, "failed to get namespace blkdev "
451*533affcbSRobert Mustacchi "address: %s", nvme_ns_info_errmsg(ns_info));
452*533affcbSRobert Mustacchi }
453*533affcbSRobert Mustacchi goto done;
454*533affcbSRobert Mustacchi }
455*533affcbSRobert Mustacchi
456744642a2SRobert Mustacchi for (di_node_t di = di_child_node(nei->nei_dinode); di != DI_NODE_NIL;
457744642a2SRobert Mustacchi di = di_sibling_node(di)) {
458744642a2SRobert Mustacchi const char *addr = di_bus_addr(di);
459*533affcbSRobert Mustacchi if (addr != NULL && strcmp(addr, bd_addr) == 0) {
460744642a2SRobert Mustacchi disk_nvme_make_ns_di_props(mod, tn, di);
461744642a2SRobert Mustacchi }
462744642a2SRobert Mustacchi }
463744642a2SRobert Mustacchi
464744642a2SRobert Mustacchi done:
4653c6ffbabSRob Johnston nvlist_free(auth);
4663c6ffbabSRob Johnston nvlist_free(fmri);
467744642a2SRobert Mustacchi }
468744642a2SRobert Mustacchi
469744642a2SRobert Mustacchi /*
470744642a2SRobert Mustacchi * Attempt to make a ufm node, but swallow the error so we can try to get as
471744642a2SRobert Mustacchi * much of the disk information as possible.
472744642a2SRobert Mustacchi */
473744642a2SRobert Mustacchi static void
disk_nvme_make_ufm(topo_mod_t * mod,nvme_enum_info_t * nei)474744642a2SRobert Mustacchi disk_nvme_make_ufm(topo_mod_t *mod, nvme_enum_info_t *nei)
475744642a2SRobert Mustacchi {
476744642a2SRobert Mustacchi topo_ufm_devinfo_t tud;
477744642a2SRobert Mustacchi char *path = di_devfs_path(nei->nei_dinode);
478744642a2SRobert Mustacchi if (path == NULL) {
479744642a2SRobert Mustacchi return;
480744642a2SRobert Mustacchi }
481744642a2SRobert Mustacchi
482744642a2SRobert Mustacchi tud.tud_method = TOPO_UFM_M_DEVINFO;
483744642a2SRobert Mustacchi tud.tud_path = path;
484744642a2SRobert Mustacchi if (topo_mod_load(mod, TOPO_MOD_UFM, TOPO_VERSION) == NULL) {
485744642a2SRobert Mustacchi topo_mod_dprintf(mod, "disk enum could not load ufm module");
486744642a2SRobert Mustacchi di_devfs_path_free(path);
487744642a2SRobert Mustacchi return;
488744642a2SRobert Mustacchi }
489744642a2SRobert Mustacchi
490744642a2SRobert Mustacchi (void) topo_mod_enumerate(mod, nei->nei_nvme, TOPO_MOD_UFM, UFM, 0, 0,
491744642a2SRobert Mustacchi &tud);
492744642a2SRobert Mustacchi di_devfs_path_free(path);
4933c6ffbabSRob Johnston }
4943c6ffbabSRob Johnston
4953c6ffbabSRob Johnston static const topo_pgroup_info_t nvme_pgroup = {
4963c6ffbabSRob Johnston TOPO_PGROUP_NVME,
4973c6ffbabSRob Johnston TOPO_STABILITY_PRIVATE,
4983c6ffbabSRob Johnston TOPO_STABILITY_PRIVATE,
4993c6ffbabSRob Johnston 1
5003c6ffbabSRob Johnston };
5013c6ffbabSRob Johnston
5023c6ffbabSRob Johnston static int
make_nvme_node(nvme_enum_info_t * nvme_info)5033c6ffbabSRob Johnston make_nvme_node(nvme_enum_info_t *nvme_info)
5043c6ffbabSRob Johnston {
5053c6ffbabSRob Johnston topo_mod_t *mod = nvme_info->nei_mod;
506*533affcbSRobert Mustacchi nvme_ctrl_info_t *info = nvme_info->nei_ctrl_info;
507*533affcbSRobert Mustacchi nvme_ns_iter_t *iter = NULL;
508*533affcbSRobert Mustacchi nvme_iter_t nret;
509*533affcbSRobert Mustacchi const nvme_ns_disc_t *disc;
5103c6ffbabSRob Johnston nvlist_t *auth = NULL, *fmri = NULL, *fru;
5113c6ffbabSRob Johnston tnode_t *nvme;
512*533affcbSRobert Mustacchi char *model = NULL, *serial = NULL, *vers = NULL;
5133c6ffbabSRob Johnston char *pname = topo_node_name(nvme_info->nei_parent);
5143c6ffbabSRob Johnston char *label = NULL;
5153c6ffbabSRob Johnston topo_instance_t pinst = topo_node_instance(nvme_info->nei_parent);
5163c6ffbabSRob Johnston int err = 0, ret = -1;
5173c6ffbabSRob Johnston
5183c6ffbabSRob Johnston /*
519*533affcbSRobert Mustacchi * Pass the model and serial strings through a function that sanitizes
520*533affcbSRobert Mustacchi * them of any characters that can't be used in an FMRI string. Note, we
521*533affcbSRobert Mustacchi * do not use the firmware revision here because that's not really a
522*533affcbSRobert Mustacchi * device property that should be part of the FMRI (it can be changed at
523*533affcbSRobert Mustacchi * runtime).
5243c6ffbabSRob Johnston */
525*533affcbSRobert Mustacchi model = topo_mod_clean_str(mod, nvme_ctrl_info_model(info));
526*533affcbSRobert Mustacchi serial = topo_mod_clean_str(mod, nvme_ctrl_info_serial(info));
5273c6ffbabSRob Johnston
5283c6ffbabSRob Johnston auth = topo_mod_auth(mod, nvme_info->nei_parent);
5293c6ffbabSRob Johnston fmri = topo_mod_hcfmri(mod, nvme_info->nei_parent, FM_HC_SCHEME_VERSION,
530*533affcbSRobert Mustacchi NVME, 0, NULL, auth, model, NULL, serial);
5313c6ffbabSRob Johnston
5323c6ffbabSRob Johnston if (fmri == NULL) {
5333c6ffbabSRob Johnston /* errno set */
5346597d6fcSRobert Mustacchi topo_mod_dprintf(mod, "%s: hcfmri failed for %s=%" PRIu64
5356597d6fcSRobert Mustacchi "/%s=0", __func__, pname, pinst, NVME);
5363c6ffbabSRob Johnston goto error;
5373c6ffbabSRob Johnston }
5383c6ffbabSRob Johnston
5393c6ffbabSRob Johnston /*
5403c6ffbabSRob Johnston * If our parent is a pciexfn node, then we need to create a nvme range
541744642a2SRobert Mustacchi * underneath it to hold the nvme hierarchy. For other cases, where
5423c6ffbabSRob Johnston * enumeration is being driven by a topo map file, this range will have
5433c6ffbabSRob Johnston * already been statically defined in the XML.
5443c6ffbabSRob Johnston */
5453c6ffbabSRob Johnston if (strcmp(pname, PCIEX_FUNCTION) == 0) {
5463c6ffbabSRob Johnston if (topo_node_range_create(mod, nvme_info->nei_parent, NVME, 0,
5473c6ffbabSRob Johnston 0) < 0) {
5483c6ffbabSRob Johnston /* errno set */
5493c6ffbabSRob Johnston topo_mod_dprintf(mod, "%s: error creating %s range",
5503c6ffbabSRob Johnston __func__, NVME);
5513c6ffbabSRob Johnston goto error;
5523c6ffbabSRob Johnston }
5533c6ffbabSRob Johnston }
5543c6ffbabSRob Johnston
5553c6ffbabSRob Johnston /*
5563c6ffbabSRob Johnston * Create a new topo node to represent the NVMe controller and bind it
5573c6ffbabSRob Johnston * to the parent node.
5583c6ffbabSRob Johnston */
5593c6ffbabSRob Johnston if ((nvme = topo_node_bind(mod, nvme_info->nei_parent, NVME, 0,
5603c6ffbabSRob Johnston fmri)) == NULL) {
5613c6ffbabSRob Johnston /* errno set */
5626597d6fcSRobert Mustacchi topo_mod_dprintf(mod, "%s: bind failed for %s=%" PRIu64
5636597d6fcSRobert Mustacchi "/%s=0", __func__, pname, pinst, NVME);
5643c6ffbabSRob Johnston goto error;
5653c6ffbabSRob Johnston }
5663c6ffbabSRob Johnston nvme_info->nei_nvme = nvme;
5673c6ffbabSRob Johnston nvme_info->nei_nvme_fmri = fmri;
5683c6ffbabSRob Johnston
5693c6ffbabSRob Johnston /*
5703c6ffbabSRob Johnston * If our parent node is a "pciexfn" node then this is a NVMe device on
5713c6ffbabSRob Johnston * a PCIe AIC, so we inherit our parent's FRU. Otherwise, we set the
5723c6ffbabSRob Johnston * FRU to ourself.
5733c6ffbabSRob Johnston */
5743c6ffbabSRob Johnston if (strcmp(topo_node_name(nvme_info->nei_parent), PCIEX_FUNCTION) == 0)
5753c6ffbabSRob Johnston fru = NULL;
5763c6ffbabSRob Johnston else
5773c6ffbabSRob Johnston fru = fmri;
5783c6ffbabSRob Johnston
5793c6ffbabSRob Johnston if (topo_node_fru_set(nvme, fru, 0, &err) != 0) {
5803c6ffbabSRob Johnston topo_mod_dprintf(mod, "%s: failed to set FRU: %s", __func__,
5813c6ffbabSRob Johnston topo_strerror(err));
5823c6ffbabSRob Johnston (void) topo_mod_seterrno(mod, err);
5833c6ffbabSRob Johnston goto error;
5843c6ffbabSRob Johnston }
5853c6ffbabSRob Johnston
5863c6ffbabSRob Johnston /*
5873c6ffbabSRob Johnston * Clone the label from our parent node. We can't inherit the property
5883c6ffbabSRob Johnston * because the label prop is mutable on bay nodes and only immutable
5893c6ffbabSRob Johnston * properties can be inherited.
5903c6ffbabSRob Johnston */
5913c6ffbabSRob Johnston if ((topo_node_label(nvme_info->nei_parent, &label, &err) != 0 &&
5923c6ffbabSRob Johnston err != ETOPO_PROP_NOENT) ||
5933c6ffbabSRob Johnston topo_node_label_set(nvme, label, &err) != 0) {
5943c6ffbabSRob Johnston topo_mod_dprintf(mod, "%s: failed to set label: %s",
5953c6ffbabSRob Johnston __func__, topo_strerror(err));
5963c6ffbabSRob Johnston (void) topo_mod_seterrno(mod, err);
5973c6ffbabSRob Johnston goto error;
5983c6ffbabSRob Johnston }
5993c6ffbabSRob Johnston
600744642a2SRobert Mustacchi /*
601744642a2SRobert Mustacchi * Ensure that we have a UFM property set based on our devinfo path.
602744642a2SRobert Mustacchi * This is a little repetitive if our parent actually did so as well,
603744642a2SRobert Mustacchi * but given that the majority of such nodes are under bays and slots
604744642a2SRobert Mustacchi * right now, it's a worthwhile tradeoff.
605744642a2SRobert Mustacchi */
606744642a2SRobert Mustacchi disk_nvme_make_ufm(mod, nvme_info);
607744642a2SRobert Mustacchi
6083c6ffbabSRob Johnston if (topo_pgroup_create(nvme, &nvme_pgroup, &err) != 0) {
6093c6ffbabSRob Johnston topo_mod_dprintf(mod, "%s: failed to create %s pgroup: %s",
6103c6ffbabSRob Johnston __func__, TOPO_PGROUP_NVME, topo_strerror(err));
6113c6ffbabSRob Johnston (void) topo_mod_seterrno(mod, err);
6123c6ffbabSRob Johnston goto error;
6133c6ffbabSRob Johnston }
6143c6ffbabSRob Johnston
615*533affcbSRobert Mustacchi if (asprintf(&vers, "%u.%u", nvme_info->nei_vers->v_major,
616*533affcbSRobert Mustacchi nvme_info->nei_vers->v_minor) < 0) {
6173c6ffbabSRob Johnston topo_mod_dprintf(mod, "%s: failed to alloc string", __func__);
6183c6ffbabSRob Johnston (void) topo_mod_seterrno(mod, EMOD_NOMEM);
6193c6ffbabSRob Johnston goto error;
6203c6ffbabSRob Johnston }
6213c6ffbabSRob Johnston if (topo_prop_set_string(nvme, TOPO_PGROUP_NVME, TOPO_PROP_NVME_VER,
6223c6ffbabSRob Johnston TOPO_PROP_IMMUTABLE, vers, &err) != 0) {
6233c6ffbabSRob Johnston topo_mod_dprintf(mod, "%s: failed to set %s/%s property",
6243c6ffbabSRob Johnston __func__, TOPO_PGROUP_NVME, TOPO_PROP_NVME_VER);
6253c6ffbabSRob Johnston (void) topo_mod_seterrno(mod, err);
6263c6ffbabSRob Johnston goto error;
6273c6ffbabSRob Johnston }
6283c6ffbabSRob Johnston
6293c6ffbabSRob Johnston if (topo_pgroup_create(nvme, &io_pgroup, &err) != 0) {
6303c6ffbabSRob Johnston topo_mod_dprintf(mod, "%s: failed to create %s pgroup: %s",
6313c6ffbabSRob Johnston __func__, TOPO_PGROUP_IO, topo_strerror(err));
6323c6ffbabSRob Johnston (void) topo_mod_seterrno(mod, err);
6333c6ffbabSRob Johnston goto error;
6343c6ffbabSRob Johnston }
635744642a2SRobert Mustacchi
636744642a2SRobert Mustacchi if (!disk_nvme_common_io(mod, nvme, nvme_info->nei_dinode)) {
6373c6ffbabSRob Johnston goto error;
6383c6ffbabSRob Johnston }
6393c6ffbabSRob Johnston
6403c6ffbabSRob Johnston /*
6413c6ffbabSRob Johnston * Create a child disk node for each namespace.
6423c6ffbabSRob Johnston */
6433c6ffbabSRob Johnston if (topo_node_range_create(mod, nvme, DISK, 0,
644*533affcbSRobert Mustacchi nvme_ctrl_info_nns(info) - 1) < 0) {
6453c6ffbabSRob Johnston /* errno set */
6463c6ffbabSRob Johnston topo_mod_dprintf(mod, "%s: error creating %s range", __func__,
6473c6ffbabSRob Johnston DISK);
6483c6ffbabSRob Johnston goto error;
6493c6ffbabSRob Johnston }
6503c6ffbabSRob Johnston
6513c6ffbabSRob Johnston /*
652744642a2SRobert Mustacchi * Iterate over each namespace to see if it's a candidate for inclusion.
653744642a2SRobert Mustacchi * Namespaces start at index 1 and not every namespace will be included.
654744642a2SRobert Mustacchi * We map things such that a disk instance is always namespace - 1 to
655744642a2SRobert Mustacchi * fit into the above mapping.
6563c6ffbabSRob Johnston */
657*533affcbSRobert Mustacchi if (!nvme_ns_discover_init(nvme_info->nei_ctrl,
658*533affcbSRobert Mustacchi NVME_NS_DISC_F_NOT_IGNORED, &iter)) {
659*533affcbSRobert Mustacchi topo_mod_dprintf(mod, "failed to initialize namespace "
660*533affcbSRobert Mustacchi "discovery: %s", nvme_errmsg(nvme_info->nei_libnvme));
661*533affcbSRobert Mustacchi ret = topo_mod_seterrno(mod, EMOD_UNKNOWN);
662*533affcbSRobert Mustacchi goto error;
663*533affcbSRobert Mustacchi }
664*533affcbSRobert Mustacchi
665*533affcbSRobert Mustacchi for (nret = nvme_ns_discover_step(iter, &disc); nret == NVME_ITER_VALID;
666*533affcbSRobert Mustacchi nret = nvme_ns_discover_step(iter, &disc)) {
667*533affcbSRobert Mustacchi nvme_ns_info_t *ns_info;
668*533affcbSRobert Mustacchi uint32_t nsid = nvme_ns_disc_nsid(disc);
669*533affcbSRobert Mustacchi
670*533affcbSRobert Mustacchi if (!nvme_ctrl_ns_info_snap(nvme_info->nei_ctrl, nsid,
671*533affcbSRobert Mustacchi &ns_info)) {
672*533affcbSRobert Mustacchi topo_mod_dprintf(mod, "failed to get namespace "
673*533affcbSRobert Mustacchi "information for ns %u: %s", nsid,
674*533affcbSRobert Mustacchi nvme_errmsg(nvme_info->nei_libnvme));
675*533affcbSRobert Mustacchi ret = topo_mod_seterrno(mod, EMOD_UNKNOWN);
676*533affcbSRobert Mustacchi goto error;
677*533affcbSRobert Mustacchi }
678*533affcbSRobert Mustacchi
679*533affcbSRobert Mustacchi disk_nvme_make_ns(nvme_info, ns_info);
680*533affcbSRobert Mustacchi nvme_ns_info_free(ns_info);
681*533affcbSRobert Mustacchi }
682*533affcbSRobert Mustacchi
683*533affcbSRobert Mustacchi if (nret == NVME_ITER_ERROR) {
684*533affcbSRobert Mustacchi topo_mod_dprintf(mod, "namespace discovery failed: %s",
685*533affcbSRobert Mustacchi nvme_errmsg(nvme_info->nei_libnvme));
686*533affcbSRobert Mustacchi ret = topo_mod_seterrno(mod, EMOD_UNKNOWN);
6873c6ffbabSRob Johnston }
6883c6ffbabSRob Johnston ret = 0;
6893c6ffbabSRob Johnston
6903c6ffbabSRob Johnston error:
691*533affcbSRobert Mustacchi nvme_ns_discover_fini(iter);
6923c6ffbabSRob Johnston free(vers);
6933c6ffbabSRob Johnston nvlist_free(auth);
6943c6ffbabSRob Johnston nvlist_free(fmri);
6953c6ffbabSRob Johnston topo_mod_strfree(mod, model);
6963c6ffbabSRob Johnston topo_mod_strfree(mod, serial);
6973c6ffbabSRob Johnston topo_mod_strfree(mod, label);
6983c6ffbabSRob Johnston return (ret);
6993c6ffbabSRob Johnston }
7003c6ffbabSRob Johnston
7013c6ffbabSRob Johnston /*
7023c6ffbabSRob Johnston * This function gathers identity information from the NVMe controller and
7033c6ffbabSRob Johnston * stores it in a struct. This struct is passed to make_nvme_node(), which
7043c6ffbabSRob Johnston * does the actual topo node creation.
7053c6ffbabSRob Johnston */
7063c6ffbabSRob Johnston static int
discover_nvme_ctl(topo_mod_t * mod,tnode_t * pnode,di_node_t dinode)707*533affcbSRobert Mustacchi discover_nvme_ctl(topo_mod_t *mod, tnode_t *pnode, di_node_t dinode)
7083c6ffbabSRob Johnston {
709*533affcbSRobert Mustacchi topo_disk_t *disk = topo_mod_getspecific(mod);
7103c6ffbabSRob Johnston nvme_enum_info_t nvme_info = { 0 };
711*533affcbSRobert Mustacchi int ret;
7123c6ffbabSRob Johnston
7133c6ffbabSRob Johnston nvme_info.nei_mod = mod;
714*533affcbSRobert Mustacchi nvme_info.nei_dinode = dinode;
715*533affcbSRobert Mustacchi nvme_info.nei_parent = pnode;
716*533affcbSRobert Mustacchi nvme_info.nei_libnvme = disk->td_nvme;
7173c6ffbabSRob Johnston
718*533affcbSRobert Mustacchi if (!nvme_ctrl_init(disk->td_nvme, dinode, &nvme_info.nei_ctrl)) {
719*533affcbSRobert Mustacchi topo_mod_dprintf(mod, "failed to initialize nvme_ctrl_t: %s",
720*533affcbSRobert Mustacchi nvme_errmsg(disk->td_nvme));
721*533affcbSRobert Mustacchi return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
722*533affcbSRobert Mustacchi }
723*533affcbSRobert Mustacchi
724*533affcbSRobert Mustacchi if (!nvme_ctrl_info_snap(nvme_info.nei_ctrl,
725*533affcbSRobert Mustacchi &nvme_info.nei_ctrl_info)) {
726*533affcbSRobert Mustacchi topo_mod_dprintf(mod, "failed to initialize nvme_ctrl_t: %s",
727*533affcbSRobert Mustacchi nvme_errmsg(disk->td_nvme));
728*533affcbSRobert Mustacchi ret = topo_mod_seterrno(mod, EMOD_UNKNOWN);
7293c6ffbabSRob Johnston goto error;
7303c6ffbabSRob Johnston }
7313c6ffbabSRob Johnston
732*533affcbSRobert Mustacchi nvme_info.nei_vers = nvme_ctrl_info_version(nvme_info.nei_ctrl_info);
733*533affcbSRobert Mustacchi
734*533affcbSRobert Mustacchi if ((ret = make_nvme_node(&nvme_info)) != 0) {
735*533affcbSRobert Mustacchi goto error;
736*533affcbSRobert Mustacchi }
7373c6ffbabSRob Johnston
7383c6ffbabSRob Johnston error:
739*533affcbSRobert Mustacchi if (nvme_info.nei_ctrl_info != NULL)
740*533affcbSRobert Mustacchi nvme_ctrl_info_free(nvme_info.nei_ctrl_info);
741*533affcbSRobert Mustacchi if (nvme_info.nei_ctrl != NULL)
742*533affcbSRobert Mustacchi nvme_ctrl_fini(nvme_info.nei_ctrl);
7433c6ffbabSRob Johnston return (ret);
7443c6ffbabSRob Johnston }
7453c6ffbabSRob Johnston
7463c6ffbabSRob Johnston int
disk_nvme_enum_disk(topo_mod_t * mod,tnode_t * pnode)7473c6ffbabSRob Johnston disk_nvme_enum_disk(topo_mod_t *mod, tnode_t *pnode)
7483c6ffbabSRob Johnston {
7493c6ffbabSRob Johnston char *parent = NULL;
7503c6ffbabSRob Johnston int err;
7513c6ffbabSRob Johnston di_node_t devtree;
7523c6ffbabSRob Johnston di_node_t dnode;
7533c6ffbabSRob Johnston int ret = -1;
7543c6ffbabSRob Johnston
7553c6ffbabSRob Johnston /*
7563c6ffbabSRob Johnston * Lookup a property containing the devfs path of the parent PCIe
7573c6ffbabSRob Johnston * device of the NVMe device we're attempting to enumerate. This
7583c6ffbabSRob Johnston * property is hard-coded in per-platform topo XML maps that are
7593c6ffbabSRob Johnston * delivered with the OS. This hard-coded path allows topo to map a
7603c6ffbabSRob Johnston * given NVMe controller to a physical location (bay or slot) on the
7613c6ffbabSRob Johnston * platform, when generating the topo snapshot.
7623c6ffbabSRob Johnston */
7633c6ffbabSRob Johnston if (topo_prop_get_string(pnode, TOPO_PGROUP_BINDING,
7643c6ffbabSRob Johnston TOPO_BINDING_PARENT_DEV, &parent, &err) != 0) {
7653c6ffbabSRob Johnston topo_mod_dprintf(mod, "parent node was missing nvme binding "
7663c6ffbabSRob Johnston "properties\n");
7673c6ffbabSRob Johnston (void) topo_mod_seterrno(mod, err);
7683c6ffbabSRob Johnston goto out;
7693c6ffbabSRob Johnston }
7703c6ffbabSRob Johnston if ((devtree = topo_mod_devinfo(mod)) == DI_NODE_NIL) {
7713c6ffbabSRob Johnston topo_mod_dprintf(mod, "failed to get devinfo snapshot");
7723c6ffbabSRob Johnston (void) topo_mod_seterrno(mod, EMOD_UNKNOWN);
7733c6ffbabSRob Johnston goto out;
7743c6ffbabSRob Johnston }
7753c6ffbabSRob Johnston
7763c6ffbabSRob Johnston /*
7773c6ffbabSRob Johnston * Walk the devinfo tree looking NVMe devices. For each NVMe device,
7783c6ffbabSRob Johnston * check if the devfs path of the parent matches the one specified in
7793c6ffbabSRob Johnston * TOPO_BINDING_PARENT_DEV.
7803c6ffbabSRob Johnston */
7813c6ffbabSRob Johnston dnode = di_drv_first_node(NVME_DRV, devtree);
7823c6ffbabSRob Johnston while (dnode != DI_NODE_NIL) {
7833c6ffbabSRob Johnston char *path;
7843c6ffbabSRob Johnston
7853c6ffbabSRob Johnston if ((path = di_devfs_path(di_parent_node(dnode))) == NULL) {
7863c6ffbabSRob Johnston topo_mod_dprintf(mod, "failed to get dev path");
7873c6ffbabSRob Johnston (void) topo_mod_seterrno(mod, EMOD_UNKNOWN);
7883c6ffbabSRob Johnston goto out;
7893c6ffbabSRob Johnston }
7903c6ffbabSRob Johnston if (strcmp(parent, path) == 0) {
791*533affcbSRobert Mustacchi ret = discover_nvme_ctl(mod, pnode, dnode);
7923c6ffbabSRob Johnston di_devfs_path_free(path);
7933c6ffbabSRob Johnston goto out;
7943c6ffbabSRob Johnston }
7953c6ffbabSRob Johnston di_devfs_path_free(path);
7963c6ffbabSRob Johnston dnode = di_drv_next_node(dnode);
7973c6ffbabSRob Johnston }
7983c6ffbabSRob Johnston ret = 0;
7993c6ffbabSRob Johnston
8003c6ffbabSRob Johnston out:
8013c6ffbabSRob Johnston topo_mod_strfree(mod, parent);
8023c6ffbabSRob Johnston return (ret);
8033c6ffbabSRob Johnston }
804