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 /* 23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * I/O support routines for DR 29 */ 30 31 #include <sys/debug.h> 32 #include <sys/types.h> 33 #include <sys/errno.h> 34 #include <sys/cred.h> 35 #include <sys/dditypes.h> 36 #include <sys/devops.h> 37 #include <sys/modctl.h> 38 #include <sys/poll.h> 39 #include <sys/conf.h> 40 #include <sys/ddi.h> 41 #include <sys/sunddi.h> 42 #include <sys/sunndi.h> 43 #include <sys/ndi_impldefs.h> 44 #include <sys/stat.h> 45 #include <sys/kmem.h> 46 #include <sys/processor.h> 47 #include <sys/cpuvar.h> 48 #include <sys/mem_config.h> 49 #include <sys/promif.h> 50 #include <sys/x_call.h> 51 #include <sys/cpu_sgnblk_defs.h> 52 #include <sys/membar.h> 53 #include <sys/stack.h> 54 #include <sys/sysmacros.h> 55 #include <sys/machsystm.h> 56 #include <sys/spitregs.h> 57 #include <sys/cpupart.h> 58 59 #include <sys/archsystm.h> 60 #include <vm/hat_sfmmu.h> 61 #include <sys/pte.h> 62 #include <sys/mmu.h> 63 #include <sys/x_call.h> 64 #include <sys/cpu_module.h> 65 66 #include <sys/cmn_err.h> 67 68 #include <sys/dr.h> 69 #include <sys/dr_util.h> 70 #include <sys/drmach.h> 71 72 void 73 dr_init_io_unit(dr_io_unit_t *ip) 74 { 75 dr_state_t new_state; 76 77 if (DR_DEV_IS_ATTACHED(&ip->sbi_cm)) { 78 new_state = DR_STATE_CONFIGURED; 79 ip->sbi_cm.sbdev_cond = SBD_COND_OK; 80 } else if (DR_DEV_IS_PRESENT(&ip->sbi_cm)) { 81 new_state = DR_STATE_CONNECTED; 82 ip->sbi_cm.sbdev_cond = SBD_COND_OK; 83 } else { 84 new_state = DR_STATE_EMPTY; 85 } 86 dr_device_transition(&ip->sbi_cm, new_state); 87 } 88 89 /*ARGSUSED*/ 90 void 91 dr_attach_io(dr_handle_t *hp, dr_common_unit_t *cp) 92 { 93 sbd_error_t *err; 94 95 dr_lock_status(hp->h_bd); 96 err = drmach_configure(cp->sbdev_id, 0); 97 dr_unlock_status(hp->h_bd); 98 99 if (!err) 100 err = drmach_io_post_attach(cp->sbdev_id); 101 102 if (err) 103 DRERR_SET_C(&cp->sbdev_error, &err); 104 } 105 106 /* 107 * remove device nodes for the branch indicated by cp 108 */ 109 /*ARGSUSED*/ 110 void 111 dr_detach_io(dr_handle_t *hp, dr_common_unit_t *cp) 112 { 113 sbd_error_t *err; 114 115 err = drmach_unconfigure(cp->sbdev_id, 0); 116 117 if (!err) 118 err = drmach_unconfigure(cp->sbdev_id, DEVI_BRANCH_DESTROY); 119 120 if (!err) 121 err = drmach_io_post_release(cp->sbdev_id); 122 123 if (err) { 124 dr_device_transition(cp, DR_STATE_CONFIGURED); 125 DRERR_SET_C(&cp->sbdev_error, &err); 126 } 127 } 128 129 /*ARGSUSED*/ 130 int 131 dr_disconnect_io(dr_io_unit_t *ip) 132 { 133 return (0); 134 } 135 136 /*ARGSUSED*/ 137 int 138 dr_pre_attach_io(dr_handle_t *hp, 139 dr_common_unit_t **devlist, int devnum) 140 { 141 int d; 142 143 for (d = 0; d < devnum; d++) { 144 dr_common_unit_t *cp = devlist[d]; 145 146 cmn_err(CE_CONT, "OS configure %s", cp->sbdev_path); 147 } 148 149 return (0); 150 } 151 152 /*ARGSUSED*/ 153 int 154 dr_post_attach_io(dr_handle_t *hp, 155 dr_common_unit_t **devlist, int devnum) 156 { 157 return (0); 158 } 159 160 static int 161 dr_check_io_refs(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum) 162 { 163 register int i, reftotal = 0; 164 static fn_t f = "dr_check_io_refs"; 165 166 for (i = 0; i < devnum; i++) { 167 dr_io_unit_t *ip = (dr_io_unit_t *)devlist[i]; 168 dev_info_t *dip; 169 int ref; 170 int refcount_non_gldv3; 171 sbd_error_t *err; 172 173 err = drmach_get_dip(ip->sbi_cm.sbdev_id, &dip); 174 if (err) 175 DRERR_SET_C(&ip->sbi_cm.sbdev_error, &err); 176 else if (dip != NULL) { 177 ref = 0; 178 refcount_non_gldv3 = 0; 179 ASSERT(e_ddi_branch_held(dip)); 180 dr_check_devices(dip, &ref, hp, NULL, NULL, 181 0, &refcount_non_gldv3); 182 ASSERT(refcount_non_gldv3 >= 0); 183 ASSERT(ref >= refcount_non_gldv3); 184 /* 185 * Ignore reference counts of non-gldv3 network devices 186 * as Crossbow creates reference counts for non-active 187 * (unplumbed) instances. Reference count check in 188 * detach() known to prevent device from detaching 189 * as necessary. 190 */ 191 ref -= refcount_non_gldv3; 192 hp->h_err = NULL; 193 if (ref) { 194 dr_dev_err(CE_WARN, &ip->sbi_cm, ESBD_BUSY); 195 } 196 PR_IO("%s: dip(%s) ref = %d\n", 197 f, ddi_get_name(dip), ref); 198 reftotal += ref; 199 } else { 200 PR_IO("%s: NO dip for id (0x%x)\n", 201 f, (uint_t)(uintptr_t)ip->sbi_cm.sbdev_id); 202 } 203 } 204 205 return (reftotal); 206 } 207 208 int 209 dr_pre_release_io(dr_handle_t *hp, 210 dr_common_unit_t **devlist, int devnum) 211 { 212 static fn_t f = "dr_pre_release_io"; 213 int d; 214 215 ASSERT(devnum > 0); 216 217 /* fail if any I/O device pre-release fails */ 218 for (d = 0; d < devnum; d++) { 219 dr_io_unit_t *ip = (dr_io_unit_t *)devlist[d]; 220 221 if ((hp->h_err = drmach_io_pre_release( 222 ip->sbi_cm.sbdev_id)) != 0) { 223 return (-1); 224 } 225 } 226 227 for (d = 0; d < devnum; d++) { 228 dr_io_unit_t *ip = (dr_io_unit_t *)devlist[d]; 229 sbd_error_t *err; 230 231 err = drmach_release(ip->sbi_cm.sbdev_id); 232 if (err) { 233 DRERR_SET_C(&ip->sbi_cm.sbdev_error, 234 &err); 235 return (-1); 236 } 237 } 238 239 /* fail if any I/O devices are still referenced */ 240 if (dr_check_io_refs(hp, devlist, devnum) > 0) { 241 PR_IO("%s: failed - I/O devices ref'd\n", f); 242 243 /* recover before return error */ 244 for (d = 0; d < devnum; d++) { 245 dr_io_unit_t *ip = (dr_io_unit_t *)devlist[d]; 246 sbd_error_t *err; 247 err = drmach_io_unrelease(ip->sbi_cm.sbdev_id); 248 if (err) { 249 DRERR_SET_C(&ip->sbi_cm.sbdev_error, &err); 250 return (-1); 251 } 252 } 253 return (-1); 254 } 255 return (0); 256 } 257 258 /*ARGSUSED*/ 259 int 260 dr_pre_detach_io(dr_handle_t *hp, 261 dr_common_unit_t **devlist, int devnum) 262 { 263 int d; 264 265 ASSERT(devnum > 0); 266 267 for (d = 0; d < devnum; d++) { 268 dr_common_unit_t *cp = devlist[d]; 269 270 cmn_err(CE_CONT, "OS unconfigure %s", cp->sbdev_path); 271 } 272 273 return (0); 274 } 275 276 /*ARGSUSED*/ 277 int 278 dr_post_detach_io(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum) 279 { 280 register int i; 281 int rv = 0; 282 static fn_t f = "dr_post_detach_io"; 283 284 ASSERT(devnum > 0); 285 for (i = 0; i < devnum; i++) { 286 dr_common_unit_t *cp = devlist[i]; 287 if (cp->sbdev_error != NULL) { 288 PR_IO("%s: Failed\n", f); 289 rv = -1; 290 break; 291 } 292 } 293 return (rv); 294 } 295 296 static void 297 dr_get_comp_cond(dr_io_unit_t *ip, dev_info_t *dip) 298 { 299 if (dip == NULL) { 300 ip->sbi_cm.sbdev_cond = SBD_COND_UNKNOWN; 301 return; 302 } 303 304 if (DEVI(dip)->devi_flags & DEVI_RETIRED) { 305 ip->sbi_cm.sbdev_cond = SBD_COND_FAILED; 306 return; 307 } 308 309 if (DR_DEV_IS_ATTACHED(&ip->sbi_cm)) { 310 ip->sbi_cm.sbdev_cond = SBD_COND_OK; 311 } else if (DR_DEV_IS_PRESENT(&ip->sbi_cm)) { 312 ip->sbi_cm.sbdev_cond = SBD_COND_OK; 313 } 314 } 315 316 int 317 dr_io_status(dr_handle_t *hp, dr_devset_t devset, sbd_dev_stat_t *dsp) 318 { 319 int i, ix; 320 dr_board_t *bp; 321 sbd_io_stat_t *isp; 322 dr_io_unit_t *ip; 323 324 bp = hp->h_bd; 325 326 /* 327 * Only look for requested devices that are actually present. 328 */ 329 devset &= DR_DEVS_PRESENT(bp); 330 331 for (i = ix = 0; i < MAX_IO_UNITS_PER_BOARD; i++) { 332 drmachid_t id; 333 dev_info_t *dip; 334 sbd_error_t *err; 335 drmach_status_t pstat; 336 337 if (DEVSET_IN_SET(devset, SBD_COMP_IO, i) == 0) 338 continue; 339 340 ip = dr_get_io_unit(bp, i); 341 342 if (ip->sbi_cm.sbdev_state == DR_STATE_EMPTY) { 343 /* present, but not fully initialized */ 344 continue; 345 } 346 347 id = ip->sbi_cm.sbdev_id; 348 if (id == (drmachid_t)0) 349 continue; 350 351 err = drmach_status(ip->sbi_cm.sbdev_id, &pstat); 352 if (err) { 353 DRERR_SET_C(&ip->sbi_cm.sbdev_error, &err); 354 return (-1); 355 } 356 357 dip = NULL; 358 err = drmach_get_dip(id, &dip); 359 if (err) { 360 /* catch this in debug kernels */ 361 ASSERT(0); 362 363 sbd_err_clear(&err); 364 continue; 365 } 366 367 isp = &dsp->d_io; 368 bzero((caddr_t)isp, sizeof (*isp)); 369 370 isp->is_cm.c_id.c_type = ip->sbi_cm.sbdev_type; 371 isp->is_cm.c_id.c_unit = ip->sbi_cm.sbdev_unum; 372 (void) strncpy(isp->is_cm.c_id.c_name, pstat.type, 373 sizeof (isp->is_cm.c_id.c_name)); 374 375 dr_get_comp_cond(ip, dip); 376 isp->is_cm.c_cond = ip->sbi_cm.sbdev_cond; 377 isp->is_cm.c_busy = ip->sbi_cm.sbdev_busy | pstat.busy; 378 isp->is_cm.c_time = ip->sbi_cm.sbdev_time; 379 isp->is_cm.c_ostate = ip->sbi_cm.sbdev_ostate; 380 isp->is_cm.c_sflags = 0; 381 382 if (dip == NULL) { 383 isp->is_pathname[0] = '\0'; 384 isp->is_referenced = 0; 385 isp->is_unsafe_count = 0; 386 } else { 387 int refcount = 0, idx = 0; 388 uint64_t unsafe_devs[SBD_MAX_UNSAFE]; 389 390 ASSERT(e_ddi_branch_held(dip)); 391 (void) ddi_pathname(dip, isp->is_pathname); 392 393 /* check reference and unsafe counts on devices */ 394 isp->is_unsafe_count = 0; 395 dr_check_devices(dip, &refcount, hp, unsafe_devs, 396 &idx, SBD_MAX_UNSAFE, NULL); 397 while (idx > 0) { 398 isp->is_unsafe_list[idx-1] = unsafe_devs[idx-1]; 399 --idx; 400 } 401 402 isp->is_referenced = (refcount == 0) ? 0 : 1; 403 404 hp->h_err = NULL; 405 } 406 ix++; 407 dsp++; 408 } 409 410 return (ix); 411 } 412