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