xref: /illumos-gate/usr/src/lib/fm/topo/modules/common/disk/disk.c (revision da7fc762b82ced1a0ec19a51e04cdf823187ec77)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 /*
25  * Copyright 2020 Joyent, Inc.
26  * Copyright 2024 Oxide Computer Company
27  */
28 
29 #include <strings.h>
30 #include <devid.h>
31 #include <pthread.h>
32 #include <inttypes.h>
33 #include <sys/dkio.h>
34 #include <sys/scsi/scsi_types.h>
35 #include <fm/topo_mod.h>
36 #include <fm/topo_list.h>
37 #include <fm/libdiskstatus.h>
38 #include <sys/fm/protocol.h>
39 #include "disk.h"
40 #include "disk_drivers.h"
41 
42 static int disk_enum(topo_mod_t *, tnode_t *, const char *,
43 	topo_instance_t, topo_instance_t, void *, void *);
44 
45 static const topo_modops_t disk_ops =
46 	{ disk_enum, NULL };
47 
48 static const topo_modinfo_t disk_info =
49 	{DISK, FM_FMRI_SCHEME_HC, DISK_VERSION, &disk_ops};
50 
51 static int
52 disk_declare_driver(topo_mod_t *mod, tnode_t *baynode, topo_list_t *dlistp,
53     char *driver)
54 {
55 	int err;
56 
57 	if (strcmp(MPTSAS_DRV, driver) == 0) {
58 		char *sas_address = NULL;
59 		tnode_t *child = NULL;
60 
61 		if ((err = disk_mptsas_find_disk(mod, baynode,
62 		    &sas_address)) != 0)
63 			return (err);
64 
65 		err = disk_declare_addr(mod, baynode, dlistp,
66 		    sas_address, &child);
67 		topo_mod_strfree(mod, sas_address);
68 
69 		return (err);
70 	} else if (strcmp(NVME_DRV, driver) == 0) {
71 		if (disk_nvme_enum_disk(mod, baynode) != 0)
72 			return (-1);
73 
74 		return (0);
75 	}
76 
77 	topo_mod_dprintf(mod, "unknown disk driver '%s'\n", driver);
78 	return (-1);
79 }
80 
81 /*ARGSUSED*/
82 static int
83 disk_enum(topo_mod_t *mod, tnode_t *baynode,
84     const char *name, topo_instance_t min, topo_instance_t max,
85     void *arg, void *notused)
86 {
87 	char		*device, *driver, *pname;
88 	int		err;
89 	topo_disk_t	*disk = topo_mod_getspecific(mod);
90 	topo_list_t	*dlistp = &disk->td_dlist;
91 
92 	if (strcmp(name, DISK) != 0 && strcmp(name, NVME) != 0) {
93 		topo_mod_dprintf(mod, "disk_enum: can't enumerate %s nodes - "
94 		    "only know how to enumerate %s and %s nodes.", name,
95 		    DISK, NVME);
96 		return (-1);
97 	}
98 
99 	/*
100 	 * Historically we've always set the parent FRU on nodes; however, it's
101 	 * not clear why. Certain node types like USB don't want this, so we
102 	 * only do this if the parent is actually a bay.
103 	 */
104 	pname = topo_node_name(baynode);
105 	if (strcmp(pname, BAY) == 0) {
106 		nvlist_t	*fmri;
107 		if (topo_node_resource(baynode, &fmri, &err) != 0) {
108 			topo_mod_dprintf(mod, "disk_enum: "
109 			    "topo_node_resource error %s\n",
110 			    topo_strerror(err));
111 			return (-1);
112 		}
113 		/*
114 		 * If the disk enumerator module has been run from an XML map
115 		 * and the parent bay node was already created by an enumerator
116 		 * module (e.g. ses), then the FRU will already be set.
117 		 */
118 		if (topo_node_fru_set(baynode, fmri, 0, &err) != 0 &&
119 		    err != ETOPO_PROP_DEFD) {
120 			topo_mod_dprintf(mod, "disk_enum: "
121 			    "topo_node_fru error %s\n", topo_strerror(err));
122 			nvlist_free(fmri);
123 			return (-1);
124 		}
125 		nvlist_free(fmri);
126 	}
127 
128 	/*
129 	 * For internal storage, first check to see if we need to
130 	 * request more detail from an HBA driver.
131 	 */
132 	if (topo_prop_get_string(baynode, TOPO_PGROUP_BINDING,
133 	    TOPO_BINDING_DRIVER, &driver, &err) == 0) {
134 		err = disk_declare_driver(mod, baynode, dlistp, driver);
135 
136 		topo_mod_strfree(mod, driver);
137 		return (err);
138 	} else if (err != ETOPO_PROP_NOENT) {
139 		topo_mod_dprintf(mod, "disk_enum: "
140 		    "binding error %s\n", topo_strerror(err));
141 		return (-1);
142 	}
143 
144 	/*
145 	 * For internal storage, get the path to the occupant from the
146 	 * binding group of the bay node
147 	 */
148 	if (topo_prop_get_string(baynode, TOPO_PGROUP_BINDING,
149 	    TOPO_BINDING_OCCUPANT, &device, &err) != 0) {
150 		topo_mod_dprintf(mod, "disk_enum: "
151 		    "failed to lookup prop %s/%s: %s\n", TOPO_PGROUP_BINDING,
152 		    TOPO_BINDING_OCCUPANT, topo_strerror(err));
153 		return (-1);
154 	}
155 
156 
157 	/* locate and topo enumerate the disk with that path */
158 	err = disk_declare_path(mod, baynode, dlistp, device);
159 
160 	topo_mod_strfree(mod, device);
161 	return (err);
162 }
163 
164 /*ARGSUSED*/
165 int
166 _topo_init(topo_mod_t *mod, topo_version_t version)
167 {
168 	topo_disk_t *disk;
169 
170 	/*
171 	 * Turn on module debugging output
172 	 */
173 	if (getenv("TOPODISKDEBUG") != NULL)
174 		topo_mod_setdebug(mod);
175 	topo_mod_dprintf(mod, "_topo_init: "
176 	    "initializing %s enumerator\n", DISK);
177 
178 	if (topo_mod_register(mod, &disk_info, TOPO_VERSION) != 0) {
179 		topo_mod_dprintf(mod, "_topo_init: "
180 		    "%s registration failed: %s\n", DISK, topo_mod_errmsg(mod));
181 		return (-1);		/* mod errno already set */
182 	}
183 
184 	if ((disk = topo_mod_zalloc(mod, sizeof (topo_disk_t))) == NULL) {
185 		topo_mod_dprintf(mod, "_topo_init: failed to allocate "
186 		    "module data");
187 		return (-1);
188 	}
189 
190 	if ((disk->td_nvme = nvme_init()) == NULL) {
191 		topo_mod_dprintf(mod, "_topo_init: failed to create libnvme "
192 		    "handle: %s", strerror(errno));
193 		topo_mod_free(mod, disk, sizeof (topo_disk_t));
194 		topo_mod_unregister(mod);
195 		return (-1);
196 	}
197 
198 	if (dev_list_gather(mod, &disk->td_dlist) != 0) {
199 		nvme_fini(disk->td_nvme);
200 		topo_mod_free(mod, disk, sizeof (topo_disk_t));
201 		topo_mod_unregister(mod);
202 		topo_mod_dprintf(mod, "_topo_init: "
203 		    "failed to locate disks");
204 		return (-1);
205 	}
206 
207 	topo_mod_dprintf(mod, "_topo_init: "
208 	    "%s enumerator initialized\n", DISK);
209 
210 	topo_mod_setspecific(mod, disk);
211 
212 	return (0);
213 }
214 
215 void
216 _topo_fini(topo_mod_t *mod)
217 {
218 	topo_disk_t *disk = topo_mod_getspecific(mod);
219 	dev_list_free(mod, &disk->td_dlist);
220 	nvme_fini(disk->td_nvme);
221 	topo_mod_free(mod, disk, sizeof (topo_disk_t));
222 	topo_mod_unregister(mod);
223 	topo_mod_dprintf(mod, "_topo_fini: "
224 	    "%s enumerator uninitialized\n", DISK);
225 }
226