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 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/debug.h> 27 #include <sys/types.h> 28 #include <sys/errno.h> 29 #include <sys/cred.h> 30 #include <sys/dditypes.h> 31 #include <sys/sunddi.h> 32 #include <sys/sunndi.h> 33 #include <sys/ddi.h> 34 #include <sys/ddi_impldefs.h> 35 #include <sys/ndi_impldefs.h> 36 #include <sys/kmem.h> 37 #include <sys/note.h> 38 39 #include <sys/sbdpriv.h> 40 #include <sys/sbd_io.h> 41 #include <sys/machsystm.h> 42 43 44 extern void sbd_errno_decode(int err, sbderror_t *ep, dev_info_t *dip); 45 extern sbd_state_t ostate_cvt(sbd_istate_t); 46 47 /* 48 * Given a dev_info_t of a branch root, walk down the 49 * branch to attach drivers 50 */ 51 /*ARGSUSED*/ 52 void 53 sbd_attach_io(sbd_handle_t *hp, sbderror_t *ep, dev_info_t *dip, int unit) 54 { 55 sbd_board_t *sbp = SBDH2BD(hp->h_sbd); 56 57 ASSERT(e_ddi_branch_held(dip)); 58 59 (void) e_ddi_branch_configure(dip, NULL, 0); 60 61 ASSERT(sbp->sb_iopath[unit] != NULL); 62 63 (void) ddi_pathname(dip, sbp->sb_iopath[unit]); 64 } 65 66 /* 67 * remove device nodes for the branch indicated by dip 68 * Hold the status lock so that status can safely do ddi_pathname(). 69 */ 70 /*ARGSUSED*/ 71 void 72 sbd_detach_io(sbd_handle_t *hp, sbderror_t *ep, dev_info_t *dip, int unit) 73 { 74 int rv; 75 dev_info_t *fdip = NULL; 76 sbd_board_t *sbp = SBDH2BD(hp->h_sbd); 77 78 ASSERT(e_ddi_branch_held(dip)); 79 mutex_enter(&sbp->sb_slock); 80 rv = e_ddi_branch_unconfigure(dip, &fdip, DEVI_BRANCH_EVENT); 81 mutex_exit(&sbp->sb_slock); 82 if (rv) { 83 /* 84 * If non-NULL, fdip is returned held and must be released. 85 */ 86 if (fdip != NULL) { 87 sbd_errno_decode(rv, ep, fdip); 88 ddi_release_devi(fdip); 89 } else { 90 sbd_errno_decode(rv, ep, dip); 91 } 92 } 93 } 94 95 /*ARGSUSED*/ 96 void 97 sbd_init_io_unit(sbd_board_t *sbp, int unit) 98 { 99 sbd_istate_t new_state; 100 sbd_io_unit_t *ip; 101 dev_info_t *dip; 102 103 ip = SBD_GET_BOARD_IOUNIT(sbp, unit); 104 105 if (SBD_DEV_IS_ATTACHED(sbp, SBD_COMP_IO, unit)) { 106 new_state = SBD_STATE_CONFIGURED; 107 } else if (SBD_DEV_IS_PRESENT(sbp, SBD_COMP_IO, unit)) { 108 new_state = SBD_STATE_CONNECTED; 109 } else { 110 new_state = SBD_STATE_EMPTY; 111 } 112 dip = sbp->sb_devlist[NIX(SBD_COMP_IO)][unit]; 113 ip->sbi_cm.sbdev_cond = sbd_get_comp_cond(dip); 114 115 /* 116 * Any changes to this io component should be performed above 117 * this call to ensure the component is fully initialized 118 * before transitioning to the new state. 119 */ 120 SBD_DEVICE_TRANSITION(sbp, SBD_COMP_IO, unit, new_state); 121 } 122 123 /*ARGSUSED*/ 124 int 125 sbd_disconnect_io(sbd_handle_t *hp, int unit) 126 { 127 return (0); 128 } 129 130 int 131 sbd_pre_attach_io(sbd_handle_t *hp, sbd_devlist_t *devlist, int devnum) 132 { 133 _NOTE(ARGUNUSED(hp)) 134 _NOTE(ARGUNUSED(devlist)) 135 _NOTE(ARGUNUSED(devnum)) 136 137 return (0); 138 } 139 140 /*ARGSUSED*/ 141 int 142 sbd_pre_detach_io(sbd_handle_t *hp, sbd_devlist_t *devlist, int devnum) 143 { 144 fn_t f = "sbd_pre_detach_io"; 145 146 PR_IO("%s...\n", f); 147 148 if (devnum <= 0) 149 return (-1); 150 151 /* fail if any I/O devices are referenced */ 152 if (sbd_check_io_refs(hp, devlist, devnum) > 0) { 153 PR_IO("%s: failed - I/O devices ref'd\n", f); 154 return (-1); 155 } 156 157 return (0); 158 } 159 160 /*ARGSUSED*/ 161 int 162 sbd_post_attach_io(sbd_handle_t *hp, sbd_devlist_t *devlist, int devnum) 163 { 164 _NOTE(ARGUNUSED(hp)) 165 _NOTE(ARGUNUSED(devlist)) 166 _NOTE(ARGUNUSED(devnum)) 167 168 return (0); 169 } 170 171 /*ARGSUSED*/ 172 int 173 sbd_post_detach_io(sbd_handle_t *hp, sbd_devlist_t *devlist, int devnum) 174 { 175 return (0); 176 } 177 178 /*ARGSUSED*/ 179 int 180 sbd_io_status(sbd_handle_t *hp, sbd_devset_t devset, sbd_dev_stat_t *dsp) 181 { 182 int i, ix; 183 sbd_board_t *sbp; 184 sbd_io_stat_t *isp; 185 sbd_io_unit_t *ip; 186 sbd_istate_t dstate; 187 sbdp_handle_t *hdp; 188 sbderror_t *ep; 189 sbd_error_t *sep; 190 191 /* 192 * Only look for requested devices that are actually present. 193 */ 194 sbp = SBDH2BD(hp->h_sbd); 195 196 ep = HD2MACHERR(hp); 197 sep = kmem_zalloc(sizeof (sbd_error_t), KM_SLEEP); 198 hdp = sbd_get_sbdp_handle(sbp, hp); 199 200 /* 201 * Concurrent status and unconfigure, disconnect are allowed. 202 * To prevent DR code from accessing stale dips, check the 203 * present devset and access the dips with status lock held. 204 * Disconnect and unconfigure code change dip state with 205 * status lock (sb_slock) held. 206 */ 207 mutex_enter(&sbp->sb_slock); 208 209 devset &= SBD_DEVS_PRESENT(sbp); 210 211 for (i = ix = 0; i < MAX_IO_UNITS_PER_BOARD; i++) { 212 dev_info_t *dip; 213 int unit; 214 int namelen; 215 int refcount = 0; 216 217 if (DEVSET_IN_SET(devset, SBD_COMP_IO, i) == 0) 218 continue; 219 /* 220 * Check to make sure the io component is in a state 221 * where its fully initialized. 222 */ 223 if (SBD_DEVICE_STATE(sbp, SBD_COMP_IO, i) == SBD_STATE_EMPTY) 224 continue; 225 226 dip = sbp->sb_devlist[NIX(SBD_COMP_IO)][i]; 227 if (dip == NULL) 228 continue; 229 230 isp = &dsp->d_io; 231 232 bzero((caddr_t)isp, sizeof (*isp)); 233 namelen = sizeof (isp->is_name); 234 (void) ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 235 DDI_PROP_DONTPASS, OBP_DEVICETYPE, 236 (caddr_t)isp->is_name, &namelen); 237 238 isp->is_unit = sbdp_get_unit_num(hdp, dip); 239 if (isp->is_unit < 0) { 240 if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) 241 continue; 242 else { 243 SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp)); 244 break; 245 } 246 } 247 unit = isp->is_unit; 248 249 dstate = SBD_DEVICE_STATE(sbp, SBD_COMP_IO, unit); 250 isp->is_ostate = ostate_cvt(dstate); 251 isp->is_type = SBD_COMP_IO; 252 ip = SBD_GET_BOARD_IOUNIT(sbp, unit); 253 ip->sbi_cm.sbdev_cond = sbd_get_comp_cond(dip); 254 isp->is_cm.c_cond = ip->sbi_cm.sbdev_cond; 255 isp->is_cm.c_busy = ip->sbi_cm.sbdev_busy; 256 isp->is_cm.c_time = ip->sbi_cm.sbdev_time; 257 258 259 /* 260 * This is safe to do as unconfigure and disconnect 261 * hold the status lock while changing dip state. 262 */ 263 (void) ddi_pathname(dip, isp->is_pathname); 264 265 /* 266 * We use a dummy handle in which to collect 267 * the major numbers of unsafe devices. 268 */ 269 sbdp_check_devices(dip, &refcount, sep, NULL); 270 271 isp->is_referenced = (refcount == 0) ? 0 : 1; 272 273 isp->is_unsafe_count = 0; 274 275 /* 276 * Reset error field since we don't care about 277 * errors at this level. The unsafe devices 278 * will be reported in the structure. 279 */ 280 SBD_SET_ERR(ep, ESBD_NOERROR); 281 ep->e_rsc[0] = '\0'; 282 283 ix++; 284 dsp++; 285 } 286 287 mutex_exit(&sbp->sb_slock); 288 289 kmem_free(sep, sizeof (sbd_error_t)); 290 sbd_release_sbdp_handle(hdp); 291 292 return (ix); 293 } 294 295 /*ARGSUSED*/ 296 int 297 sbd_io_cnt(sbd_handle_t *hp, sbd_devset_t devset) 298 { 299 int i, ix; 300 sbd_board_t *sbp; 301 302 sbp = SBDH2BD(hp->h_sbd); 303 304 /* 305 * Only look for requested devices that are actually present. 306 */ 307 devset &= SBD_DEVS_PRESENT(sbp); 308 309 for (i = ix = 0; i < MAX_IO_UNITS_PER_BOARD; i++) { 310 dev_info_t *dip; 311 312 if (DEVSET_IN_SET(devset, SBD_COMP_IO, i) == 0) 313 continue; 314 315 dip = sbp->sb_devlist[NIX(SBD_COMP_IO)][i]; 316 if (dip == NULL) 317 continue; 318 319 ix++; 320 } 321 322 return (ix); 323 } 324 325 int 326 sbd_check_io_refs(sbd_handle_t *hp, sbd_devlist_t devlist[], int devnum) 327 { 328 register int i, reftotal = 0; 329 fn_t f = "sbd_check_io_refs"; 330 sbd_error_t *sep; 331 sbderror_t *ep; 332 333 sep = kmem_zalloc(sizeof (sbd_error_t), KM_SLEEP); 334 ep = HD2MACHERR(hp); 335 336 for (i = 0; i < devnum; i++) { 337 dev_info_t *dip; 338 int ref; 339 int refcount_non_gldv3; 340 341 dip = devlist[i].dv_dip; 342 ref = 0; 343 refcount_non_gldv3 = 0; 344 sbdp_check_devices(dip, &ref, sep, &refcount_non_gldv3); 345 ASSERT(refcount_non_gldv3 >= 0); 346 ASSERT(ref >= refcount_non_gldv3); 347 /* 348 * Ignore reference counts of non-gldv3 network devices 349 * as Crossbow creates reference counts for non-active 350 * (unplumbed) instances. Reference count check in 351 * detach() known to prevent device from detaching 352 * as necessary. 353 */ 354 ref -= refcount_non_gldv3; 355 if (ref) { 356 if (SBD_GET_ERR(ep) == 0) { 357 SBD_GET_PERR(sep, ep); 358 } 359 SBD_GET_PERR(sep, &devlist[i].dv_error); 360 } 361 PR_IO("%s: dip(%s) ref = %d\n", f, ddi_get_name(dip), ref); 362 reftotal += ref; 363 } 364 365 kmem_free(sep, sizeof (sbd_error_t)); 366 367 return (reftotal); 368 } 369 370 int 371 sbd_check_io_attached(dev_info_t *dip, void *arg) 372 { 373 dev_info_t **tdip; 374 375 tdip = (dev_info_t **)arg; 376 377 if (dip == *tdip) { 378 int state; 379 380 state = ddi_get_devstate(dip); 381 if (i_ddi_devi_attached(dip) || (state == DDI_DEVSTATE_UP)) { 382 *tdip = NULL; 383 return (DDI_WALK_TERMINATE); 384 } 385 } 386 return (DDI_WALK_CONTINUE); 387 } 388 389 int 390 sbd_pre_release_io(sbd_handle_t *hp, 391 sbd_devlist_t *devlist, int devnum) 392 { 393 fn_t f = "sbd_pre_release_io"; 394 int rv = 0; 395 int i; 396 397 ASSERT(devnum > 0); 398 399 /* fail if any I/O devices are referenced */ 400 if ((rv = sbd_check_io_refs(hp, devlist, devnum)) > 0) { 401 /* 402 * One of the devices may have failed check to see which 403 * and set in the main handle 404 */ 405 for (i = 0; i < devnum; i++) { 406 if (SBD_GET_ERR(&devlist[i].dv_error) != 0) { 407 (void) sbd_set_err_in_hdl(hp, 408 &devlist[i].dv_error); 409 break; 410 } 411 } 412 PR_IO("%s: failed - I/O devices ref'd\n", f); 413 } 414 415 return (rv); 416 } 417