Print this page
8368 remove warlock leftovers from usr/src/uts
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/uts/common/io/fibre-channel/ulp/fcsm.c
+++ new/usr/src/uts/common/io/fibre-channel/ulp/fcsm.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 23 * Use is subject to license terms.
24 24 */
25 25
26 26 /*
27 27 * fcsm - ULP Module for Fibre Channel SAN Management
28 28 */
29 29
30 30 #include <sys/types.h>
31 31 #include <sys/file.h>
32 32 #include <sys/kmem.h>
33 33 #include <sys/scsi/scsi.h>
34 34 #include <sys/var.h>
35 35 #include <sys/byteorder.h>
36 36 #include <sys/fibre-channel/fc.h>
37 37 #include <sys/fibre-channel/impl/fc_ulpif.h>
38 38 #include <sys/fibre-channel/ulp/fcsm.h>
39 39
40 40 /* Definitions */
41 41 #define FCSM_VERSION "20090729-1.28"
42 42 #define FCSM_NAME_VERSION "SunFC FCSM v" FCSM_VERSION
43 43
44 44 /* Global Variables */
45 45 static char fcsm_name[] = "FCSM";
46 46 static void *fcsm_state = NULL;
47 47 static kmutex_t fcsm_global_mutex;
48 48 static uint32_t fcsm_flag = FCSM_IDLE;
49 49 static dev_info_t *fcsm_dip = NULL;
50 50 static fcsm_t *fcsm_port_head = NULL;
51 51 static kmem_cache_t *fcsm_job_cache = NULL;
52 52 static int fcsm_num_attaching = 0;
53 53 static int fcsm_num_detaching = 0;
54 54 static int fcsm_detached = 0;
55 55
56 56 static int fcsm_max_cmd_retries = FCSM_MAX_CMD_RETRIES;
57 57 static int fcsm_retry_interval = FCSM_RETRY_INTERVAL;
58 58 static int fcsm_retry_ticker = FCSM_RETRY_TICKER;
59 59 static int fcsm_offline_ticker = FCSM_OFFLINE_TICKER;
60 60 static int fcsm_max_job_retries = FCSM_MAX_JOB_RETRIES;
61 61 static clock_t fcsm_retry_ticks;
62 62 static clock_t fcsm_offline_ticks;
63 63
64 64
65 65
66 66 #ifdef DEBUG
67 67 uint32_t fcsm_debug = 0;
68 68 #endif
69 69
70 70
71 71 /* Character/Block entry points */
72 72 struct cb_ops fcsm_cb_ops = {
73 73 fcsm_open, /* open */
74 74 fcsm_close, /* close */
75 75 nodev, /* strategy */
76 76 nodev, /* print */
77 77 nodev, /* dump */
78 78 nodev, /* read */
79 79 nodev, /* write */
80 80 fcsm_ioctl, /* ioctl */
81 81 nodev, /* devmap */
82 82 nodev, /* mmap */
83 83 nodev, /* segmap */
84 84 nochpoll, /* poll */
85 85 ddi_prop_op,
86 86 NULL, /* streams info */
87 87 D_NEW | D_MP,
88 88 CB_REV,
89 89 nodev, /* aread */
90 90 nodev /* awrite */
91 91 };
92 92
93 93 struct dev_ops fcsm_ops = {
94 94 DEVO_REV,
95 95 0, /* refcnt */
96 96 fcsm_getinfo, /* get info */
97 97 nulldev, /* identify (obsolete) */
98 98 nulldev, /* probe (not required for self-identifying devices) */
99 99 fcsm_attach, /* attach */
100 100 fcsm_detach, /* detach */
101 101 nodev, /* reset */
102 102 &fcsm_cb_ops, /* char/block entry points structure for leaf drivers */
103 103 NULL, /* bus operations for nexus driver */
104 104 NULL /* power management */
105 105 };
106 106
107 107
108 108 struct modldrv modldrv = {
109 109 &mod_driverops,
110 110 FCSM_NAME_VERSION,
111 111 &fcsm_ops
112 112 };
113 113
114 114 struct modlinkage modlinkage = {
115 115 MODREV_1,
116 116 &modldrv,
117 117 NULL
118 118 };
119 119
120 120 static fc_ulp_modinfo_t fcsm_modinfo = {
121 121 &fcsm_modinfo, /* ulp_handle */
122 122 FCTL_ULP_MODREV_4, /* ulp_rev */
123 123 FC_TYPE_FC_SERVICES, /* ulp_type */
124 124 fcsm_name, /* ulp_name */
125 125 0, /* ulp_statec_mask: get all statec callbacks */
126 126 fcsm_port_attach, /* ulp_port_attach */
127 127 fcsm_port_detach, /* ulp_port_detach */
128 128 fcsm_port_ioctl, /* ulp_port_ioctl */
129 129 fcsm_els_cb, /* ulp_els_callback */
130 130 fcsm_data_cb, /* ulp_data_callback */
131 131 fcsm_statec_cb /* ulp_statec_callback */
132 132 };
133 133
134 134 struct fcsm_xlat_pkt_state {
135 135 uchar_t xlat_state;
136 136 int xlat_rval;
137 137 } fcsm_xlat_pkt_state [] = {
138 138 { FC_PKT_SUCCESS, FC_SUCCESS },
139 139 { FC_PKT_REMOTE_STOP, FC_FAILURE },
140 140 { FC_PKT_LOCAL_RJT, FC_TRANSPORT_ERROR },
141 141 { FC_PKT_NPORT_RJT, FC_PREJECT },
142 142 { FC_PKT_FABRIC_RJT, FC_FREJECT },
143 143 { FC_PKT_LOCAL_BSY, FC_TRAN_BUSY },
144 144 { FC_PKT_TRAN_BSY, FC_TRAN_BUSY },
145 145 { FC_PKT_NPORT_BSY, FC_PBUSY },
146 146 { FC_PKT_FABRIC_BSY, FC_FBUSY },
147 147 { FC_PKT_LS_RJT, FC_PREJECT },
148 148 { FC_PKT_BA_RJT, FC_PREJECT },
149 149 { FC_PKT_TIMEOUT, FC_FAILURE },
150 150 { FC_PKT_FS_RJT, FC_FAILURE },
151 151 { FC_PKT_TRAN_ERROR, FC_TRANSPORT_ERROR },
152 152 { FC_PKT_FAILURE, FC_FAILURE },
153 153 { FC_PKT_PORT_OFFLINE, FC_OFFLINE },
154 154 { FC_PKT_ELS_IN_PROGRESS, FC_FAILURE }
155 155 };
156 156
157 157 struct fcsm_xlat_port_state {
158 158 uint32_t xlat_pstate;
159 159 caddr_t xlat_state_str;
160 160 } fcsm_xlat_port_state [] = {
161 161 { FC_STATE_OFFLINE, "OFFLINE" },
162 162 { FC_STATE_ONLINE, "ONLINE" },
163 163 { FC_STATE_LOOP, "LOOP" },
164 164 { FC_STATE_NAMESERVICE, "NAMESERVICE" },
165 165 { FC_STATE_RESET, "RESET" },
166 166 { FC_STATE_RESET_REQUESTED, "RESET_REQUESTED" },
167 167 { FC_STATE_LIP, "LIP" },
168 168 { FC_STATE_LIP_LBIT_SET, "LIP_LBIT_SET" },
169 169 { FC_STATE_DEVICE_CHANGE, "DEVICE_CHANGE" },
170 170 { FC_STATE_TARGET_PORT_RESET, "TARGET_PORT_RESET" }
171 171 };
172 172
173 173 struct fcsm_xlat_topology {
174 174 uint32_t xlat_top;
175 175 caddr_t xlat_top_str;
176 176 } fcsm_xlat_topology [] = {
177 177 { FC_TOP_UNKNOWN, "UNKNOWN" },
178 178 { FC_TOP_PRIVATE_LOOP, "Private Loop" },
179 179 { FC_TOP_PUBLIC_LOOP, "Public Loop" },
180 180 { FC_TOP_FABRIC, "Fabric" },
181 181 { FC_TOP_PT_PT, "Point-to-Point" },
182 182 { FC_TOP_NO_NS, "NO_NS" }
183 183 };
184 184
185 185 struct fcsm_xlat_dev_type {
186 186 uint32_t xlat_type;
187 187 caddr_t xlat_str;
188 188 } fcsm_xlat_dev_type [] = {
189 189 { PORT_DEVICE_NOCHANGE, "No Change" },
190 190 { PORT_DEVICE_NEW, "New" },
191 191 { PORT_DEVICE_OLD, "Old" },
192 192 { PORT_DEVICE_CHANGED, "Changed" },
193 193 { PORT_DEVICE_DELETE, "Delete" },
194 194 { PORT_DEVICE_USER_LOGIN, "User Login" },
195 195 { PORT_DEVICE_USER_LOGOUT, "User Logout" },
196 196 { PORT_DEVICE_USER_CREATE, "User Create" },
197 197 { PORT_DEVICE_USER_DELETE, "User Delete" }
198 198 };
199 199
200 200 int
201 201 _init(void)
202 202 {
203 203 int rval;
204 204
205 205 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "_init"));
206 206
207 207 fcsm_retry_ticks = drv_usectohz(fcsm_retry_ticker * 1000 * 1000);
208 208 fcsm_offline_ticks = drv_usectohz(fcsm_offline_ticker * 1000 * 1000);
209 209
210 210 if (rval = ddi_soft_state_init(&fcsm_state, sizeof (fcsm_t),
211 211 FCSM_INIT_INSTANCES)) {
212 212 fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
213 213 "_init: ddi_soft_state_init failed");
214 214 return (ENOMEM);
215 215 }
216 216
217 217 mutex_init(&fcsm_global_mutex, NULL, MUTEX_DRIVER, NULL);
218 218
219 219 fcsm_job_cache = kmem_cache_create("fcsm_job_cache",
220 220 sizeof (fcsm_job_t), 8, fcsm_job_cache_constructor,
221 221 fcsm_job_cache_destructor, NULL, NULL, NULL, 0);
222 222
223 223 if (fcsm_job_cache == NULL) {
224 224 mutex_destroy(&fcsm_global_mutex);
225 225 ddi_soft_state_fini(&fcsm_state);
226 226 return (ENOMEM);
227 227 }
228 228
229 229 /*
230 230 * Now call fc_ulp_add to add this ULP in the transport layer
231 231 * database. This will cause 'ulp_port_attach' callback function
232 232 * to be called.
233 233 */
234 234 rval = fc_ulp_add(&fcsm_modinfo);
235 235 if (rval != 0) {
236 236 switch (rval) {
237 237 case FC_ULP_SAMEMODULE:
238 238 fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
239 239 "_init: FC SAN Management module is already "
240 240 "registered with transport layer");
241 241 rval = EEXIST;
242 242 break;
243 243
244 244 case FC_ULP_SAMETYPE:
245 245 fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
246 246 "_init: Another module with same type 0x%x is "
247 247 "already registered with transport layer",
248 248 fcsm_modinfo.ulp_type);
249 249 rval = EEXIST;
250 250 break;
251 251
252 252 case FC_BADULP:
253 253 fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
254 254 "_init: Please upgrade this module. Current "
255 255 "version 0x%x is not the most recent version",
256 256 fcsm_modinfo.ulp_rev);
257 257 rval = EIO;
258 258 break;
259 259 default:
260 260 fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
261 261 "_init: fc_ulp_add failed with status 0x%x", rval);
262 262 rval = EIO;
263 263 break;
264 264 }
265 265 kmem_cache_destroy(fcsm_job_cache);
266 266 mutex_destroy(&fcsm_global_mutex);
267 267 ddi_soft_state_fini(&fcsm_state);
268 268 return (rval);
269 269 }
270 270
271 271 if ((rval = mod_install(&modlinkage)) != 0) {
272 272 FCSM_DEBUG(SMDL_ERR, (CE_WARN, SM_LOG, NULL, NULL,
273 273 "_init: mod_install failed with status 0x%x", rval));
274 274 (void) fc_ulp_remove(&fcsm_modinfo);
275 275 kmem_cache_destroy(fcsm_job_cache);
276 276 mutex_destroy(&fcsm_global_mutex);
277 277 ddi_soft_state_fini(&fcsm_state);
278 278 return (rval);
279 279 }
280 280
281 281 return (rval);
282 282 }
283 283
284 284 int
285 285 _fini(void)
286 286 {
287 287 int rval;
288 288 #ifdef DEBUG
289 289 int status;
290 290 #endif /* DEBUG */
291 291
292 292 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "_fini"));
293 293
294 294 /*
295 295 * don't start cleaning up until we know that the module remove
296 296 * has worked -- if this works, then we know that each instance
297 297 * has successfully been DDI_DETACHed
298 298 */
299 299 if ((rval = mod_remove(&modlinkage)) != 0) {
300 300 return (rval);
301 301 }
302 302
303 303 #ifdef DEBUG
304 304 status = fc_ulp_remove(&fcsm_modinfo);
305 305 if (status != 0) {
306 306 FCSM_DEBUG(SMDL_ERR, (CE_WARN, SM_LOG, NULL, NULL,
307 307 "_fini: fc_ulp_remove failed with status 0x%x", status));
308 308 }
309 309 #else
310 310 (void) fc_ulp_remove(&fcsm_modinfo);
311 311 #endif /* DEBUG */
312 312
313 313 fcsm_detached = 0;
314 314
315 315 /*
316 316 * It is possible to modunload fcsm manually, which will cause
317 317 * a bypass of all the port_detach functionality. We may need
318 318 * to force that code path to be executed to properly clean up
319 319 * in that case.
320 320 */
321 321 fcsm_force_port_detach_all();
322 322
323 323 kmem_cache_destroy(fcsm_job_cache);
324 324 mutex_destroy(&fcsm_global_mutex);
325 325 ddi_soft_state_fini(&fcsm_state);
326 326
327 327 return (rval);
328 328 }
329 329
330 330
331 331 int
332 332 _info(struct modinfo *modinfop)
333 333 {
334 334 return (mod_info(&modlinkage, modinfop));
335 335 }
336 336
337 337 /* ARGSUSED */
338 338 static int
339 339 fcsm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
340 340 {
341 341 int rval = DDI_FAILURE;
342 342
343 343 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
344 344 "attach: cmd 0x%x", cmd));
345 345
346 346 switch (cmd) {
347 347 case DDI_ATTACH:
348 348 mutex_enter(&fcsm_global_mutex);
349 349 if (fcsm_dip != NULL) {
350 350 mutex_exit(&fcsm_global_mutex);
351 351 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
352 352 "attach: duplicate attach of fcsm!!"));
353 353 break;
354 354 }
355 355
356 356 fcsm_dip = dip;
357 357
358 358 /*
359 359 * The detach routine cleans up all the port instances
360 360 * i.e. it detaches all ports.
361 361 * If _fini never got called after detach, then
362 362 * perform an fc_ulp_remove() followed by fc_ulp_add()
363 363 * to ensure that port_attach callbacks are called
364 364 * again.
365 365 */
366 366 if (fcsm_detached) {
367 367 int status;
368 368
369 369 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
370 370 "attach: rebinding to transport driver"));
371 371
372 372 mutex_exit(&fcsm_global_mutex);
373 373
374 374 (void) fc_ulp_remove(&fcsm_modinfo);
375 375
376 376 /*
377 377 * Reset the detached flag, so that ports can attach
378 378 */
379 379 mutex_enter(&fcsm_global_mutex);
380 380 fcsm_detached = 0;
381 381 mutex_exit(&fcsm_global_mutex);
382 382
383 383 status = fc_ulp_add(&fcsm_modinfo);
384 384
385 385 if (status != 0) {
386 386 /*
387 387 * ULP add failed. So set the
388 388 * detached flag again
389 389 */
390 390 mutex_enter(&fcsm_global_mutex);
391 391 fcsm_detached = 1;
392 392 mutex_exit(&fcsm_global_mutex);
393 393
394 394 switch (status) {
395 395 case FC_ULP_SAMEMODULE:
396 396 fcsm_display(CE_WARN, SM_LOG, NULL,
397 397 NULL, "attach: FC SAN Management "
398 398 "module is already "
399 399 "registered with transport layer");
400 400 break;
401 401
402 402 case FC_ULP_SAMETYPE:
403 403 fcsm_display(CE_WARN, SM_LOG, NULL,
404 404 NULL, "attach: Another module with "
405 405 "same type 0x%x is already "
406 406 "registered with transport layer",
407 407 fcsm_modinfo.ulp_type);
408 408 break;
409 409
410 410 case FC_BADULP:
411 411 fcsm_display(CE_WARN, SM_LOG, NULL,
412 412 NULL, "attach: Please upgrade this "
413 413 "module. Current version 0x%x is "
414 414 "not the most recent version",
415 415 fcsm_modinfo.ulp_rev);
416 416 break;
417 417 default:
418 418 fcsm_display(CE_WARN, SM_LOG, NULL,
419 419 NULL, "attach: fc_ulp_add failed "
420 420 "with status 0x%x", status);
421 421 break;
422 422 }
423 423
424 424 /* Return failure */
425 425 break;
426 426 }
427 427
428 428 mutex_enter(&fcsm_global_mutex);
429 429 }
430 430
431 431 /* Create a minor node */
432 432 if (ddi_create_minor_node(fcsm_dip, "fcsm", S_IFCHR,
433 433 NULL, DDI_PSEUDO, 0) == DDI_SUCCESS) {
434 434 /* Announce presence of the device */
435 435 mutex_exit(&fcsm_global_mutex);
436 436 ddi_report_dev(dip);
437 437 rval = DDI_SUCCESS;
438 438 } else {
439 439 fcsm_dip = NULL;
440 440 mutex_exit(&fcsm_global_mutex);
441 441 fcsm_display(CE_WARN, SM_LOG_AND_CONSOLE,
442 442 NULL, NULL, "attach: create minor node failed");
443 443 }
444 444 break;
445 445
446 446 case DDI_RESUME:
447 447 rval = DDI_SUCCESS;
448 448 break;
449 449
450 450 default:
451 451 FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, NULL, NULL,
452 452 "attach: unknown cmd 0x%x dip 0x%p", cmd, dip));
453 453 break;
454 454 }
455 455
456 456 return (rval);
457 457 }
458 458
459 459 /* ARGSUSED */
460 460 static int
461 461 fcsm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
462 462 {
463 463 int instance;
464 464 int rval = DDI_SUCCESS;
465 465
466 466 instance = getminor((dev_t)arg);
467 467
468 468 switch (cmd) {
469 469 case DDI_INFO_DEVT2INSTANCE:
470 470 *result = (void *)(long)instance; /* minor number is instance */
471 471 break;
472 472
473 473 case DDI_INFO_DEVT2DEVINFO:
474 474 mutex_enter(&fcsm_global_mutex);
475 475 *result = (void *)fcsm_dip;
476 476 mutex_exit(&fcsm_global_mutex);
477 477 break;
478 478
479 479 default:
480 480 rval = DDI_FAILURE;
481 481 break;
482 482 }
483 483
484 484 return (rval);
485 485 }
486 486
487 487
488 488 /* ARGSUSED */
489 489 static int
490 490 fcsm_port_attach(opaque_t ulph, fc_ulp_port_info_t *pinfo,
491 491 fc_attach_cmd_t cmd, uint32_t s_id)
492 492 {
493 493 int instance;
494 494 int rval = FC_FAILURE;
495 495
496 496 instance = ddi_get_instance(pinfo->port_dip);
497 497
498 498 /*
499 499 * Set the attaching flag, so that fcsm_detach will fail, if
500 500 * port attach is in progress.
501 501 */
502 502 mutex_enter(&fcsm_global_mutex);
503 503 if (fcsm_detached) {
504 504 mutex_exit(&fcsm_global_mutex);
505 505
506 506 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
507 507 "port_attach: end. detach in progress. failing attach "
508 508 "instance 0x%x", instance));
509 509 return (((cmd == FC_CMD_POWER_UP) || (cmd == FC_CMD_RESUME)) ?
510 510 FC_FAILURE_SILENT : FC_FAILURE);
511 511 }
512 512
513 513 fcsm_num_attaching++;
514 514 mutex_exit(&fcsm_global_mutex);
515 515
516 516 switch (cmd) {
517 517 case FC_CMD_ATTACH:
518 518 if (fcsm_handle_port_attach(pinfo, s_id, instance)
519 519 != DDI_SUCCESS) {
520 520 ASSERT(ddi_get_soft_state(fcsm_state,
521 521 instance) == NULL);
522 522 break;
523 523 }
524 524 rval = FC_SUCCESS;
525 525 break;
526 526
527 527 case FC_CMD_RESUME:
528 528 case FC_CMD_POWER_UP: {
529 529 fcsm_t *fcsm;
530 530 char fcsm_pathname[MAXPATHLEN];
531 531
532 532 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
533 533 "port_attach: cmd 0x%x instance 0x%x", cmd, instance));
534 534
535 535 /* Get the soft state structure */
536 536 if ((fcsm = ddi_get_soft_state(fcsm_state, instance)) == NULL) {
537 537 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
538 538 "port_attach: instance 0x%x, cmd 0x%x "
539 539 "get softstate failed", instance, cmd));
540 540 break;
541 541 }
542 542
543 543 ASSERT(fcsm->sm_instance == instance);
544 544
545 545 /* If this instance is not attached, then return failure */
546 546 mutex_enter(&fcsm->sm_mutex);
547 547 if ((fcsm->sm_flags & FCSM_ATTACHED) == 0) {
548 548 mutex_exit(&fcsm->sm_mutex);
549 549 fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
550 550 "port_detach: port is not attached");
551 551 break;
552 552 }
553 553 mutex_exit(&fcsm->sm_mutex);
554 554
555 555 if (fcsm_handle_port_resume(ulph, pinfo, cmd, s_id, fcsm) !=
556 556 DDI_SUCCESS) {
557 557 break;
558 558 }
559 559
560 560 (void) ddi_pathname(fcsm->sm_port_info.port_dip, fcsm_pathname);
561 561 fcsm_display(CE_NOTE, SM_LOG, fcsm, NULL,
562 562 "attached to path %s", fcsm_pathname);
563 563 rval = FC_SUCCESS;
564 564 break;
565 565 }
566 566
567 567 default:
568 568 FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, NULL, NULL,
569 569 "port_attach: unknown cmd 0x%x for port 0x%x",
570 570 cmd, instance));
571 571 break;
572 572 }
573 573
574 574 mutex_enter(&fcsm_global_mutex);
575 575 fcsm_num_attaching--;
576 576 mutex_exit(&fcsm_global_mutex);
577 577 return (rval);
578 578 }
579 579
580 580
581 581 static int
582 582 fcsm_handle_port_attach(fc_ulp_port_info_t *pinfo, uint32_t s_id, int instance)
583 583 {
584 584 fcsm_t *fcsm;
585 585 kthread_t *thread;
586 586 char name[32];
587 587 char fcsm_pathname[MAXPATHLEN];
588 588
589 589 /* Allocate a soft state structure for the port */
590 590 if (ddi_soft_state_zalloc(fcsm_state, instance) != DDI_SUCCESS) {
591 591 fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
592 592 "port_attach: instance 0x%x, soft state alloc failed",
593 593 instance);
594 594 return (DDI_FAILURE);
595 595 }
596 596
597 597 if ((fcsm = ddi_get_soft_state(fcsm_state, instance)) == NULL) {
598 598 fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
599 599 "port_attach: instance 0x%x, get soft state failed",
600 600 instance);
601 601 ddi_soft_state_free(fcsm_state, instance);
602 602 return (DDI_FAILURE);
603 603 }
604 604
605 605
606 606 /* Initialize the mutex */
607 607 mutex_init(&fcsm->sm_mutex, NULL, MUTEX_DRIVER, NULL);
608 608 cv_init(&fcsm->sm_job_cv, NULL, CV_DRIVER, NULL);
609 609
610 610 mutex_enter(&fcsm->sm_mutex);
611 611 fcsm->sm_flags |= FCSM_ATTACHING;
612 612 fcsm->sm_sid = s_id;
613 613 fcsm->sm_instance = instance;
614 614 fcsm->sm_port_state = pinfo->port_state;
615 615
616 616 /*
617 617 * Make a copy of the port_information structure, since fctl
618 618 * uses a temporary structure.
619 619 */
620 620 fcsm->sm_port_info = *pinfo; /* Structure copy !!! */
621 621 mutex_exit(&fcsm->sm_mutex);
622 622
623 623
624 624 (void) sprintf(name, "fcsm%d_cmd_cache", fcsm->sm_instance);
625 625 fcsm->sm_cmd_cache = kmem_cache_create(name,
626 626 sizeof (fcsm_cmd_t) + pinfo->port_fca_pkt_size, 8,
627 627 fcsm_cmd_cache_constructor, fcsm_cmd_cache_destructor,
628 628 NULL, (void *)fcsm, NULL, 0);
629 629 if (fcsm->sm_cmd_cache == NULL) {
630 630 fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
631 631 "port_attach: pkt cache create failed");
632 632 cv_destroy(&fcsm->sm_job_cv);
633 633 mutex_destroy(&fcsm->sm_mutex);
634 634 ddi_soft_state_free(fcsm_state, instance);
635 635 return (DDI_FAILURE);
636 636 }
637 637
638 638 thread = thread_create((caddr_t)NULL, 0, fcsm_job_thread,
639 639 (caddr_t)fcsm, 0, &p0, TS_RUN, v.v_maxsyspri-2);
640 640 if (thread == NULL) {
641 641 fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
642 642 "port_attach: job thread create failed");
643 643 kmem_cache_destroy(fcsm->sm_cmd_cache);
644 644 cv_destroy(&fcsm->sm_job_cv);
645 645 mutex_destroy(&fcsm->sm_mutex);
646 646 ddi_soft_state_free(fcsm_state, instance);
647 647 return (DDI_FAILURE);
648 648 }
649 649
650 650 fcsm->sm_thread = thread;
651 651
652 652 /* Add this structure to fcsm global linked list */
653 653 mutex_enter(&fcsm_global_mutex);
654 654 if (fcsm_port_head == NULL) {
655 655 fcsm_port_head = fcsm;
656 656 } else {
657 657 fcsm->sm_next = fcsm_port_head;
658 658 fcsm_port_head = fcsm;
659 659 }
660 660 mutex_exit(&fcsm_global_mutex);
661 661
662 662 mutex_enter(&fcsm->sm_mutex);
663 663 fcsm->sm_flags &= ~FCSM_ATTACHING;
664 664 fcsm->sm_flags |= FCSM_ATTACHED;
665 665 fcsm->sm_port_top = pinfo->port_flags;
666 666 fcsm->sm_port_state = pinfo->port_state;
667 667 if (pinfo->port_acc_attr == NULL) {
668 668 /*
669 669 * The corresponding FCA doesn't support DMA at all
670 670 */
671 671 fcsm->sm_flags |= FCSM_USING_NODMA_FCA;
672 672 }
673 673 mutex_exit(&fcsm->sm_mutex);
674 674
675 675 (void) ddi_pathname(fcsm->sm_port_info.port_dip, fcsm_pathname);
676 676 fcsm_display(CE_NOTE, SM_LOG, fcsm, NULL,
677 677 "attached to path %s", fcsm_pathname);
678 678
679 679 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
680 680 "port_attach: state <%s>(0x%x) topology <%s>(0x%x)",
681 681 fcsm_port_state_to_str(FC_PORT_STATE_MASK(pinfo->port_state)),
682 682 pinfo->port_state,
683 683 fcsm_topology_to_str(pinfo->port_flags), pinfo->port_flags));
684 684
685 685 return (DDI_SUCCESS);
686 686 }
687 687
688 688 static int
689 689 fcsm_handle_port_resume(opaque_t ulph, fc_ulp_port_info_t *pinfo,
690 690 fc_attach_cmd_t cmd, uint32_t s_id, fcsm_t *fcsm)
691 691 {
692 692 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
693 693 "port_resume: cmd 0x%x", cmd));
694 694
695 695 mutex_enter(&fcsm->sm_mutex);
696 696
697 697 switch (cmd) {
698 698 case FC_CMD_RESUME:
699 699 ASSERT(!(fcsm->sm_flags & FCSM_POWER_DOWN));
700 700 fcsm->sm_flags &= ~FCSM_SUSPENDED;
701 701 break;
702 702
703 703 case FC_CMD_POWER_UP:
704 704 /* If port is suspended, then no need to resume */
705 705 fcsm->sm_flags &= ~FCSM_POWER_DOWN;
706 706 if (fcsm->sm_flags & FCSM_SUSPENDED) {
707 707 mutex_exit(&fcsm->sm_mutex);
708 708 return (DDI_SUCCESS);
709 709 }
710 710 break;
711 711 default:
712 712 mutex_exit(&fcsm->sm_mutex);
713 713 return (DDI_FAILURE);
714 714 }
715 715
716 716 fcsm->sm_sid = s_id;
717 717
718 718 /*
719 719 * Make a copy of the new port_information structure
720 720 */
721 721 fcsm->sm_port_info = *pinfo; /* Structure copy !!! */
722 722 mutex_exit(&fcsm->sm_mutex);
723 723
724 724 fcsm_resume_port(fcsm);
725 725
726 726 /*
727 727 * Invoke state change processing.
728 728 * This will ensure that
729 729 * - offline timer is started if new port state changed to offline.
730 730 * - MGMT_SERVER_LOGIN flag is reset.
731 731 * - Port topology is updated.
732 732 */
733 733 fcsm_statec_cb(ulph, (opaque_t)pinfo->port_handle, pinfo->port_state,
734 734 pinfo->port_flags, NULL, 0, s_id);
735 735
736 736 return (DDI_SUCCESS);
737 737 }
738 738
739 739
740 740 /* ARGSUSED */
741 741 static int
742 742 fcsm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
743 743 {
744 744 int rval = DDI_SUCCESS;
745 745
746 746 switch (cmd) {
747 747 case DDI_DETACH: {
748 748 fcsm_t *fcsm;
749 749
750 750 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
751 751 "detach: start. cmd <DETACH>", cmd));
752 752
753 753 mutex_enter(&fcsm_global_mutex);
754 754
755 755 /*
756 756 * If port attach/detach in progress, then wait for 5 seconds
757 757 * for them to complete.
758 758 */
759 759 if (fcsm_num_attaching || fcsm_num_detaching) {
760 760 int count;
761 761
762 762 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
763 763 "detach: wait for port attach/detach to complete"));
764 764
765 765 count = 0;
766 766 while ((count++ <= 30) &&
767 767 (fcsm_num_attaching || fcsm_num_detaching)) {
768 768 mutex_exit(&fcsm_global_mutex);
769 769 delay(drv_usectohz(1000000));
770 770 mutex_enter(&fcsm_global_mutex);
771 771 }
772 772
773 773 /* Port attach/detach still in prog, so fail detach */
774 774 if (fcsm_num_attaching || fcsm_num_detaching) {
775 775 mutex_exit(&fcsm_global_mutex);
776 776 FCSM_DEBUG(SMDL_ERR, (CE_WARN, SM_LOG, NULL,
777 777 NULL, "detach: Failing detach. port "
778 778 "attach/detach in progress"));
779 779 rval = DDI_FAILURE;
780 780 break;
781 781 }
782 782 }
783 783
784 784 if (fcsm_port_head == NULL) {
785 785 /* Not much do, Succeed to detach. */
786 786 ddi_remove_minor_node(fcsm_dip, NULL);
787 787 fcsm_dip = NULL;
788 788 fcsm_detached = 0;
789 789 mutex_exit(&fcsm_global_mutex);
790 790 break;
791 791 }
792 792
793 793 /*
794 794 * Check to see, if any ports are active.
795 795 * If not, then set the DETACHING flag to indicate
796 796 * that they are being detached.
797 797 */
798 798 fcsm = fcsm_port_head;
799 799 while (fcsm != NULL) {
800 800
801 801 mutex_enter(&fcsm->sm_mutex);
802 802 if (!(fcsm->sm_flags & FCSM_ATTACHED) ||
803 803 fcsm->sm_ncmds || fcsm->sm_cb_count) {
804 804 /* port is busy. We can't detach */
805 805 mutex_exit(&fcsm->sm_mutex);
806 806 break;
807 807 }
808 808
809 809 fcsm->sm_flags |= FCSM_DETACHING;
810 810 mutex_exit(&fcsm->sm_mutex);
811 811
812 812 fcsm = fcsm->sm_next;
813 813 }
814 814
815 815 /*
816 816 * If all ports could not be marked for detaching,
817 817 * then clear the flags and fail the detach.
818 818 * Also if a port attach is currently in progress
819 819 * then fail the detach.
820 820 */
821 821 if (fcsm != NULL || fcsm_num_attaching || fcsm_num_detaching) {
822 822 /*
823 823 * Some ports were busy, so can't detach.
824 824 * Clear the DETACHING flag and return failure
825 825 */
826 826 fcsm = fcsm_port_head;
827 827 while (fcsm != NULL) {
828 828 mutex_enter(&fcsm->sm_mutex);
829 829 if (fcsm->sm_flags & FCSM_DETACHING) {
830 830 fcsm->sm_flags &= ~FCSM_DETACHING;
831 831 }
832 832 mutex_exit(&fcsm->sm_mutex);
833 833
834 834 fcsm = fcsm->sm_next;
835 835 }
836 836 mutex_exit(&fcsm_global_mutex);
837 837 return (DDI_FAILURE);
838 838 } else {
839 839 fcsm_detached = 1;
840 840 /*
841 841 * Mark all the detaching ports as detached, as we
842 842 * will be detaching them
843 843 */
844 844 fcsm = fcsm_port_head;
845 845 while (fcsm != NULL) {
846 846 mutex_enter(&fcsm->sm_mutex);
847 847 fcsm->sm_flags &= ~FCSM_DETACHING;
848 848 fcsm->sm_flags |= FCSM_DETACHED;
849 849 mutex_exit(&fcsm->sm_mutex);
850 850
851 851 fcsm = fcsm->sm_next;
852 852 }
853 853 }
854 854 mutex_exit(&fcsm_global_mutex);
855 855
856 856
857 857 /*
858 858 * Go ahead and detach the ports
859 859 */
860 860 mutex_enter(&fcsm_global_mutex);
861 861 while (fcsm_port_head != NULL) {
862 862 fcsm = fcsm_port_head;
863 863 mutex_exit(&fcsm_global_mutex);
864 864
865 865 /*
866 866 * Call fcsm_cleanup_port(). This cleansup and
867 867 * removes the fcsm structure from global linked list
868 868 */
869 869 fcsm_cleanup_port(fcsm);
870 870
871 871 /*
872 872 * Soft state cleanup done.
873 873 * Remember that fcsm struct doesn't exist anymore.
874 874 */
875 875
876 876 mutex_enter(&fcsm_global_mutex);
877 877 }
878 878
879 879 ddi_remove_minor_node(fcsm_dip, NULL);
880 880 fcsm_dip = NULL;
881 881 mutex_exit(&fcsm_global_mutex);
882 882 break;
883 883 }
884 884
885 885 case DDI_SUSPEND:
886 886 rval = DDI_SUCCESS;
887 887 break;
888 888
889 889 default:
890 890 FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, NULL, NULL,
891 891 "detach: unknown cmd 0x%x", cmd));
892 892 rval = DDI_FAILURE;
893 893 break;
894 894 }
895 895
896 896 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
897 897 "detach: end. cmd 0x%x, rval 0x%x", cmd, rval));
898 898
899 899 return (rval);
900 900 }
901 901
902 902
903 903 /* ARGSUSED */
904 904 static void
905 905 fcsm_force_port_detach_all(void)
906 906 {
907 907 fcsm_t *fcsm;
908 908
909 909 fcsm = fcsm_port_head;
910 910
911 911 while (fcsm) {
912 912 fcsm_cleanup_port(fcsm);
913 913 /*
914 914 * fcsm_cleanup_port will remove the current fcsm structure
915 915 * from the list, which will cause fcsm_port_head to point
916 916 * to what would have been the next structure on the list.
917 917 */
918 918 fcsm = fcsm_port_head;
919 919 }
920 920 }
921 921
922 922
923 923 /* ARGSUSED */
924 924 static int
925 925 fcsm_port_detach(opaque_t ulph, fc_ulp_port_info_t *pinfo, fc_detach_cmd_t cmd)
926 926 {
927 927 int instance;
928 928 int rval = FC_FAILURE;
929 929 fcsm_t *fcsm;
930 930
931 931 instance = ddi_get_instance(pinfo->port_dip);
932 932
933 933 mutex_enter(&fcsm_global_mutex);
934 934 if (fcsm_detached) {
935 935 mutex_exit(&fcsm_global_mutex);
936 936
937 937 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
938 938 "port_detach: end. instance 0x%x, fcsm is detached",
939 939 instance));
940 940 return (FC_SUCCESS);
941 941 }
942 942 fcsm_num_detaching++; /* Set the flag */
943 943 mutex_exit(&fcsm_global_mutex);
944 944
945 945 /* Get the soft state structure */
946 946 if ((fcsm = ddi_get_soft_state(fcsm_state, instance)) == NULL) {
947 947 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
948 948 "port_detach: instance 0x%x, cmd 0x%x get softstate failed",
949 949 instance, cmd));
950 950 mutex_enter(&fcsm_global_mutex);
951 951 fcsm_num_detaching--;
952 952 mutex_exit(&fcsm_global_mutex);
953 953 return (rval);
954 954 }
955 955
956 956 ASSERT(fcsm->sm_instance == instance);
957 957
958 958 /* If this instance is not attached, then fail the detach */
959 959 mutex_enter(&fcsm->sm_mutex);
960 960 if ((fcsm->sm_flags & FCSM_ATTACHED) == 0) {
961 961 mutex_exit(&fcsm->sm_mutex);
962 962 fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
963 963 "port_detach: port is not attached");
964 964 mutex_enter(&fcsm_global_mutex);
965 965 fcsm_num_detaching--;
966 966 mutex_exit(&fcsm_global_mutex);
967 967 return (rval);
968 968 }
969 969 mutex_exit(&fcsm->sm_mutex);
970 970
971 971 /*
972 972 * If fcsm has been detached, then all instance has already been
973 973 * detached or are being detached. So succeed this detach.
974 974 */
975 975
976 976 switch (cmd) {
977 977 case FC_CMD_DETACH:
978 978 case FC_CMD_SUSPEND:
979 979 case FC_CMD_POWER_DOWN:
980 980 break;
981 981
982 982 default:
983 983 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
984 984 "port_detach: port unknown cmd 0x%x", cmd));
985 985 mutex_enter(&fcsm_global_mutex);
986 986 fcsm_num_detaching--;
987 987 mutex_exit(&fcsm_global_mutex);
988 988 return (rval);
989 989 };
990 990
991 991 if (fcsm_handle_port_detach(pinfo, fcsm, cmd) == DDI_SUCCESS) {
992 992 rval = FC_SUCCESS;
993 993 }
994 994
995 995 mutex_enter(&fcsm_global_mutex);
996 996 fcsm_num_detaching--;
997 997 mutex_exit(&fcsm_global_mutex);
998 998
999 999 /* If it was a detach, then fcsm state structure no longer exists */
1000 1000 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1001 1001 "port_detach: end. cmd 0x%x rval 0x%x", cmd, rval));
1002 1002 return (rval);
1003 1003 }
1004 1004
1005 1005
1006 1006 static int
1007 1007 fcsm_handle_port_detach(fc_ulp_port_info_t *pinfo, fcsm_t *fcsm,
1008 1008 fc_detach_cmd_t cmd)
1009 1009 {
1010 1010 uint32_t flag;
1011 1011 int count;
1012 1012 #ifdef DEBUG
1013 1013 char pathname[MAXPATHLEN];
1014 1014 #endif /* DEBUG */
1015 1015
1016 1016 /*
1017 1017 * If port is already powered down OR suspended and there is nothing
1018 1018 * else to do then just return.
1019 1019 * Otherwise, set the flag, so that no more new activity will be
1020 1020 * initiated on this port.
1021 1021 */
1022 1022 mutex_enter(&fcsm->sm_mutex);
1023 1023
1024 1024 switch (cmd) {
1025 1025 case FC_CMD_DETACH:
1026 1026 flag = FCSM_DETACHING;
1027 1027 break;
1028 1028
1029 1029 case FC_CMD_SUSPEND:
1030 1030 case FC_CMD_POWER_DOWN:
1031 1031 ((cmd == FC_CMD_SUSPEND) ? (flag = FCSM_SUSPENDED) :
1032 1032 (flag = FCSM_POWER_DOWN));
1033 1033 if (fcsm->sm_flags &
1034 1034 (FCSM_POWER_DOWN | FCSM_SUSPENDED)) {
1035 1035 fcsm->sm_flags |= flag;
1036 1036 mutex_exit(&fcsm->sm_mutex);
1037 1037 return (DDI_SUCCESS);
1038 1038 }
1039 1039 break;
1040 1040
1041 1041 default:
1042 1042 mutex_exit(&fcsm->sm_mutex);
1043 1043 return (DDI_FAILURE);
1044 1044 };
1045 1045
1046 1046 fcsm->sm_flags |= flag;
1047 1047
1048 1048 /*
1049 1049 * If some commands are pending OR callback in progress, then
1050 1050 * wait for some finite amount of time for their completion.
1051 1051 * TODO: add more checks here to check for cmd timeout, offline
1052 1052 * timeout and other (??) threads.
1053 1053 */
1054 1054 count = 0;
1055 1055 while ((count++ <= 30) && (fcsm->sm_ncmds || fcsm->sm_cb_count)) {
1056 1056 mutex_exit(&fcsm->sm_mutex);
1057 1057 delay(drv_usectohz(1000000));
1058 1058 mutex_enter(&fcsm->sm_mutex);
1059 1059 }
1060 1060 if (fcsm->sm_ncmds || fcsm->sm_cb_count) {
1061 1061 fcsm->sm_flags &= ~flag;
1062 1062 mutex_exit(&fcsm->sm_mutex);
1063 1063 fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
1064 1064 "port_detach: Failing suspend, port is busy");
1065 1065 return (DDI_FAILURE);
1066 1066 }
1067 1067 if (flag == FCSM_DETACHING) {
1068 1068 fcsm->sm_flags &= ~FCSM_DETACHING;
1069 1069 fcsm->sm_flags |= FCSM_DETACHED;
1070 1070 }
1071 1071
1072 1072 mutex_exit(&fcsm->sm_mutex);
1073 1073
1074 1074 FCSM_DEBUG(SMDL_INFO, (CE_CONT, SM_LOG, fcsm, NULL,
1075 1075 "port_detach: cmd 0x%x pathname <%s>",
1076 1076 cmd, ddi_pathname(pinfo->port_dip, pathname)));
1077 1077
1078 1078 if (cmd == FC_CMD_DETACH) {
1079 1079 fcsm_cleanup_port(fcsm);
1080 1080 /*
1081 1081 * Soft state cleanup done.
1082 1082 * Always remember that fcsm struct doesn't exist anymore.
1083 1083 */
1084 1084 } else {
1085 1085 fcsm_suspend_port(fcsm);
1086 1086 }
1087 1087
1088 1088 return (DDI_SUCCESS);
1089 1089 }
1090 1090
1091 1091 static void
1092 1092 fcsm_suspend_port(fcsm_t *fcsm)
1093 1093 {
1094 1094 mutex_enter(&fcsm->sm_mutex);
1095 1095
1096 1096 if (fcsm->sm_offline_tid != NULL) {
1097 1097 timeout_id_t tid;
1098 1098
1099 1099 tid = fcsm->sm_offline_tid;
1100 1100 fcsm->sm_offline_tid = (timeout_id_t)NULL;
1101 1101 mutex_exit(&fcsm->sm_mutex);
1102 1102 (void) untimeout(tid);
1103 1103 mutex_enter(&fcsm->sm_mutex);
1104 1104 fcsm->sm_flags |= FCSM_RESTORE_OFFLINE_TIMEOUT;
1105 1105 }
1106 1106
1107 1107 if (fcsm->sm_retry_tid != NULL) {
1108 1108 timeout_id_t tid;
1109 1109
1110 1110 tid = fcsm->sm_retry_tid;
1111 1111 fcsm->sm_retry_tid = (timeout_id_t)NULL;
1112 1112 mutex_exit(&fcsm->sm_mutex);
1113 1113 (void) untimeout(tid);
1114 1114 mutex_enter(&fcsm->sm_mutex);
1115 1115 fcsm->sm_flags |= FCSM_RESTORE_RETRY_TIMEOUT;
1116 1116 }
1117 1117
1118 1118 mutex_exit(&fcsm->sm_mutex);
1119 1119 }
1120 1120
1121 1121 static void
1122 1122 fcsm_resume_port(fcsm_t *fcsm)
1123 1123 {
1124 1124 mutex_enter(&fcsm->sm_mutex);
1125 1125
1126 1126 if (fcsm->sm_flags & FCSM_RESTORE_OFFLINE_TIMEOUT) {
1127 1127 fcsm->sm_flags &= ~FCSM_RESTORE_OFFLINE_TIMEOUT;
1128 1128
1129 1129 /*
1130 1130 * If port if offline, link is not marked down and offline
1131 1131 * timer is not already running, then restart offline timer.
1132 1132 */
1133 1133 if (!(fcsm->sm_flags & FCSM_LINK_DOWN) &&
1134 1134 fcsm->sm_offline_tid == NULL &&
1135 1135 (fcsm->sm_flags & FCSM_PORT_OFFLINE)) {
1136 1136 fcsm->sm_offline_tid = timeout(fcsm_offline_timeout,
1137 1137 (caddr_t)fcsm, fcsm_offline_ticks);
1138 1138 }
1139 1139 }
1140 1140
1141 1141 if (fcsm->sm_flags & FCSM_RESTORE_RETRY_TIMEOUT) {
1142 1142 fcsm->sm_flags &= ~FCSM_RESTORE_RETRY_TIMEOUT;
1143 1143
1144 1144 /*
1145 1145 * If retry queue is not suspended and some cmds are waiting
1146 1146 * to be retried, then restart the retry timer
1147 1147 */
1148 1148 if (fcsm->sm_retry_head && fcsm->sm_retry_tid == NULL) {
1149 1149 fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
1150 1150 (caddr_t)fcsm, fcsm_retry_ticks);
1151 1151 }
1152 1152 }
1153 1153 mutex_exit(&fcsm->sm_mutex);
1154 1154 }
1155 1155
1156 1156 static void
1157 1157 fcsm_cleanup_port(fcsm_t *fcsm)
1158 1158 {
1159 1159 fcsm_t *curr, *prev;
1160 1160 int status;
1161 1161 fcsm_job_t *job;
1162 1162
1163 1163 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1164 1164 "fcsm_cleanup_port: entered"));
1165 1165
1166 1166 /*
1167 1167 * Kill the job thread
1168 1168 */
1169 1169 job = fcsm_alloc_job(KM_SLEEP);
1170 1170 ASSERT(job != NULL);
1171 1171 fcsm_init_job(job, fcsm->sm_instance, FCSM_JOB_THREAD_SHUTDOWN,
1172 1172 FCSM_JOBFLAG_SYNC, NULL, NULL, NULL, NULL);
1173 1173
1174 1174 status = fcsm_process_job(job, 0);
1175 1175 ASSERT(status == FC_SUCCESS);
1176 1176
1177 1177 ASSERT(job->job_result == FC_SUCCESS);
1178 1178 fcsm_dealloc_job(job);
1179 1179
1180 1180 /*
1181 1181 * We got here after ensuring the no commands are pending or active.
1182 1182 * Therefore retry timeout thread should NOT be running.
1183 1183 * Kill the offline timeout thread if currently running.
1184 1184 */
1185 1185 mutex_enter(&fcsm->sm_mutex);
1186 1186
1187 1187 ASSERT(fcsm->sm_retry_tid == NULL);
1188 1188
1189 1189 if (fcsm->sm_offline_tid != NULL) {
1190 1190 timeout_id_t tid;
1191 1191
1192 1192 tid = fcsm->sm_offline_tid;
1193 1193 fcsm->sm_offline_tid = (timeout_id_t)NULL;
1194 1194 mutex_exit(&fcsm->sm_mutex);
1195 1195 (void) untimeout(tid);
1196 1196 } else {
1197 1197 mutex_exit(&fcsm->sm_mutex);
1198 1198 }
1199 1199
1200 1200 /* Remove from the fcsm state structure from global linked list */
1201 1201 mutex_enter(&fcsm_global_mutex);
1202 1202 curr = fcsm_port_head;
1203 1203 prev = NULL;
1204 1204 while (curr != fcsm && curr != NULL) {
1205 1205 prev = curr;
1206 1206 curr = curr->sm_next;
1207 1207 }
1208 1208 ASSERT(curr != NULL);
1209 1209
1210 1210 if (prev == NULL) {
1211 1211 fcsm_port_head = curr->sm_next;
1212 1212 } else {
1213 1213 prev->sm_next = curr->sm_next;
1214 1214 }
1215 1215 mutex_exit(&fcsm_global_mutex);
1216 1216
1217 1217 if (fcsm->sm_cmd_cache != NULL) {
1218 1218 kmem_cache_destroy(fcsm->sm_cmd_cache);
1219 1219 }
1220 1220 cv_destroy(&fcsm->sm_job_cv);
1221 1221 mutex_destroy(&fcsm->sm_mutex);
1222 1222
1223 1223 /* Free the fcsm state structure */
1224 1224 ddi_soft_state_free(fcsm_state, fcsm->sm_instance);
1225 1225 }
1226 1226
1227 1227
1228 1228 /* ARGSUSED */
1229 1229 static void
1230 1230 fcsm_statec_cb(opaque_t ulph, opaque_t port_handle, uint32_t port_state,
1231 1231 uint32_t port_top, fc_portmap_t *devlist, uint32_t dev_cnt,
1232 1232 uint32_t port_sid)
1233 1233 {
1234 1234 fcsm_t *fcsm;
1235 1235 timeout_id_t offline_tid, retry_tid;
1236 1236
1237 1237 mutex_enter(&fcsm_global_mutex);
1238 1238 if (fcsm_detached) {
1239 1239 mutex_exit(&fcsm_global_mutex);
1240 1240 return;
1241 1241 }
1242 1242
1243 1243 fcsm = ddi_get_soft_state(fcsm_state,
1244 1244 fc_ulp_get_port_instance(port_handle));
1245 1245 if (fcsm == NULL) {
1246 1246 mutex_exit(&fcsm_global_mutex);
1247 1247 FCSM_DEBUG(SMDL_TRACE, (CE_NOTE, SM_LOG, NULL, NULL,
1248 1248 "statec_cb: instance 0x%x not found",
1249 1249 fc_ulp_get_port_instance(port_handle)));
1250 1250 return;
1251 1251 }
1252 1252 mutex_enter(&fcsm->sm_mutex);
1253 1253 ASSERT(fcsm->sm_instance == fc_ulp_get_port_instance(port_handle));
1254 1254 if ((fcsm->sm_flags & FCSM_ATTACHED) == 0) {
1255 1255 mutex_exit(&fcsm->sm_mutex);
1256 1256 mutex_exit(&fcsm_global_mutex);
1257 1257 FCSM_DEBUG(SMDL_TRACE, (CE_NOTE, SM_LOG, fcsm, NULL,
1258 1258 "statec_cb: port not attached"));
1259 1259 return;
1260 1260 }
1261 1261
1262 1262 ASSERT(fcsm->sm_cb_count >= 0);
1263 1263
1264 1264 fcsm->sm_cb_count++;
1265 1265 mutex_exit(&fcsm->sm_mutex);
1266 1266 mutex_exit(&fcsm_global_mutex);
1267 1267
1268 1268 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1269 1269 "statec_cb: state <%s>(0x%x) topology <%s>(0x%x) dev_cnt %d",
1270 1270 fcsm_port_state_to_str(FC_PORT_STATE_MASK(port_state)), port_state,
1271 1271 fcsm_topology_to_str(port_top), port_top, dev_cnt));
1272 1272
1273 1273 fcsm_disp_devlist(fcsm, devlist, dev_cnt);
1274 1274
1275 1275 mutex_enter(&fcsm->sm_mutex);
1276 1276
1277 1277 /*
1278 1278 * Reset the Mgmt server Login flag, so that login is performed again.
1279 1279 */
1280 1280 fcsm->sm_flags &= ~FCSM_MGMT_SERVER_LOGGED_IN;
1281 1281
1282 1282 fcsm->sm_sid = port_sid;
1283 1283 fcsm->sm_port_top = port_top;
1284 1284 fcsm->sm_port_state = port_state;
1285 1285
1286 1286 switch (port_state) {
1287 1287 case FC_STATE_OFFLINE:
1288 1288 case FC_STATE_RESET:
1289 1289 case FC_STATE_RESET_REQUESTED:
1290 1290 fcsm->sm_flags |= FCSM_PORT_OFFLINE;
1291 1291 break;
1292 1292
1293 1293 case FC_STATE_ONLINE:
1294 1294 case FC_STATE_LOOP:
1295 1295 case FC_STATE_LIP:
1296 1296 case FC_STATE_LIP_LBIT_SET:
1297 1297 fcsm->sm_flags &= ~FCSM_PORT_OFFLINE;
1298 1298 fcsm->sm_flags &= ~FCSM_LINK_DOWN;
1299 1299 break;
1300 1300
1301 1301 case FC_STATE_NAMESERVICE:
1302 1302 case FC_STATE_DEVICE_CHANGE:
1303 1303 case FC_STATE_TARGET_PORT_RESET:
1304 1304 default:
1305 1305 /* Do nothing */
1306 1306 break;
1307 1307 }
1308 1308
1309 1309 offline_tid = retry_tid = NULL;
1310 1310 if (fcsm->sm_flags & FCSM_PORT_OFFLINE) {
1311 1311 /*
1312 1312 * Port is offline.
1313 1313 * Suspend cmd processing and start offline timeout thread.
1314 1314 */
1315 1315 if (fcsm->sm_offline_tid == NULL) {
1316 1316 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1317 1317 "statec_cb: schedule offline timeout thread"));
1318 1318 fcsm->sm_flags |= FCSM_CMD_RETRY_Q_SUSPENDED;
1319 1319 /* Stop the cmd retry thread */
1320 1320 retry_tid = fcsm->sm_retry_tid;
1321 1321 fcsm->sm_retry_tid = (timeout_id_t)NULL;
1322 1322
1323 1323 fcsm->sm_offline_tid = timeout(fcsm_offline_timeout,
1324 1324 (caddr_t)fcsm, fcsm_offline_ticks);
1325 1325 }
1326 1326
1327 1327 } else {
1328 1328 /*
1329 1329 * Port is online.
1330 1330 * Cancel offline timeout thread and resume command processing.
1331 1331 */
1332 1332 if (fcsm->sm_offline_tid) {
1333 1333 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1334 1334 "statec_cb: cancel offline timeout thread"));
1335 1335 offline_tid = fcsm->sm_offline_tid;
1336 1336 fcsm->sm_offline_tid = (timeout_id_t)NULL;
1337 1337 }
1338 1338
1339 1339 fcsm->sm_flags &= ~FCSM_CMD_RETRY_Q_SUSPENDED;
1340 1340 /* Start retry thread if needed */
1341 1341 if (fcsm->sm_retry_head && fcsm->sm_retry_tid == NULL) {
1342 1342 fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
1343 1343 (caddr_t)fcsm, fcsm_retry_ticks);
1344 1344 }
1345 1345 }
1346 1346
1347 1347 mutex_exit(&fcsm->sm_mutex);
1348 1348
1349 1349 if (offline_tid != NULL) {
1350 1350 (void) untimeout(offline_tid);
1351 1351 }
1352 1352
1353 1353 if (retry_tid != NULL) {
1354 1354 (void) untimeout(retry_tid);
1355 1355 }
1356 1356
1357 1357 mutex_enter(&fcsm->sm_mutex);
1358 1358 fcsm->sm_cb_count--;
1359 1359 ASSERT(fcsm->sm_cb_count >= 0);
1360 1360 mutex_exit(&fcsm->sm_mutex);
1361 1361 }
1362 1362
1363 1363
1364 1364 static void
1365 1365 fcsm_offline_timeout(void *handle)
1366 1366 {
1367 1367 fcsm_t *fcsm = (fcsm_t *)handle;
1368 1368
1369 1369 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1370 1370 "offline_timeout"));
1371 1371
1372 1372 mutex_enter(&fcsm->sm_mutex);
1373 1373 if (fcsm->sm_flags & FCSM_PORT_OFFLINE) {
1374 1374 fcsm->sm_flags |= FCSM_LINK_DOWN;
1375 1375 }
1376 1376 fcsm->sm_offline_tid = (timeout_id_t)NULL;
1377 1377 fcsm->sm_flags &= ~FCSM_CMD_RETRY_Q_SUSPENDED;
1378 1378
1379 1379 /* Start the retry thread if needed */
1380 1380 if (fcsm->sm_retry_head && fcsm->sm_retry_tid == NULL) {
1381 1381 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1382 1382 "offline_timeout: reschedule cmd retry thread"));
1383 1383 ASSERT(fcsm->sm_retry_tid == NULL);
1384 1384 fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
1385 1385 (caddr_t)fcsm, fcsm_retry_ticks);
1386 1386 }
1387 1387 mutex_exit(&fcsm->sm_mutex);
1388 1388 }
1389 1389
1390 1390 /* ARGSUSED */
1391 1391 static int
1392 1392 fcsm_els_cb(opaque_t ulph, opaque_t port_handle, fc_unsol_buf_t *buf,
1393 1393 uint32_t claimed)
1394 1394 {
1395 1395 return (FC_UNCLAIMED);
1396 1396 }
1397 1397
1398 1398
1399 1399 /* ARGSUSED */
1400 1400 static int
1401 1401 fcsm_data_cb(opaque_t ulph, opaque_t port_handle, fc_unsol_buf_t *buf,
1402 1402 uint32_t claimed)
1403 1403 {
1404 1404 return (FC_UNCLAIMED);
1405 1405 }
1406 1406
1407 1407
1408 1408 /* ARGSUSED */
1409 1409 static int
1410 1410 fcsm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
1411 1411 int *rval_p)
1412 1412 {
1413 1413 int retval = 0;
1414 1414
1415 1415 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "ioctl: start"));
1416 1416
1417 1417 mutex_enter(&fcsm_global_mutex);
1418 1418 if (!(fcsm_flag & FCSM_OPEN)) {
1419 1419 mutex_exit(&fcsm_global_mutex);
1420 1420 return (ENXIO);
1421 1421 }
1422 1422 mutex_exit(&fcsm_global_mutex);
1423 1423
1424 1424 /* Allow only root to talk */
1425 1425 if (drv_priv(credp)) {
1426 1426 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1427 1427 "ioctl: end (disallowing underprivileged user)"));
1428 1428 return (EPERM);
1429 1429 }
1430 1430
1431 1431 switch (cmd) {
1432 1432
1433 1433 case FCSMIO_CMD: {
1434 1434 fcio_t fcio;
1435 1435 int status;
1436 1436 #ifdef _MULTI_DATAMODEL
1437 1437 switch (ddi_model_convert_from(mode & FMODELS)) {
1438 1438 case DDI_MODEL_ILP32: {
1439 1439 struct fcio32 fcio32;
1440 1440
1441 1441 if (status = ddi_copyin((void *)arg, (void *)&fcio32,
1442 1442 sizeof (struct fcio32), mode)) {
1443 1443 retval = EFAULT;
1444 1444 break;
1445 1445 }
1446 1446 fcio.fcio_xfer = fcio32.fcio_xfer;
1447 1447 fcio.fcio_cmd = fcio32.fcio_cmd;
1448 1448 fcio.fcio_flags = fcio32.fcio_flags;
1449 1449 fcio.fcio_cmd_flags = fcio32.fcio_cmd_flags;
1450 1450 fcio.fcio_ilen = (size_t)fcio32.fcio_ilen;
1451 1451 fcio.fcio_ibuf = (caddr_t)(long)fcio32.fcio_ibuf;
1452 1452 fcio.fcio_olen = (size_t)fcio32.fcio_olen;
1453 1453 fcio.fcio_obuf = (caddr_t)(long)fcio32.fcio_obuf;
1454 1454 fcio.fcio_alen = (size_t)fcio32.fcio_alen;
1455 1455 fcio.fcio_abuf = (caddr_t)(long)fcio32.fcio_abuf;
1456 1456 fcio.fcio_errno = fcio32.fcio_errno;
1457 1457 break;
1458 1458 }
1459 1459
1460 1460 case DDI_MODEL_NONE:
1461 1461 if (status = ddi_copyin((void *)arg, (void *)&fcio,
1462 1462 sizeof (fcio_t), mode)) {
1463 1463 retval = EFAULT;
1464 1464 }
1465 1465 break;
1466 1466 }
1467 1467 #else /* _MULTI_DATAMODEL */
1468 1468 if (status = ddi_copyin((void *)arg, (void *)&fcio,
1469 1469 sizeof (fcio_t), mode)) {
1470 1470 retval = EFAULT;
1471 1471 break;
1472 1472 }
1473 1473 #endif /* _MULTI_DATAMODEL */
1474 1474 if (!status) {
1475 1475 retval = fcsm_fciocmd(arg, mode, credp, &fcio);
1476 1476 }
1477 1477 break;
1478 1478 }
1479 1479
1480 1480 default:
1481 1481 retval = ENOTTY;
1482 1482 break;
1483 1483 }
1484 1484
1485 1485 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "ioctl: end"));
1486 1486 return (retval);
1487 1487 }
1488 1488
1489 1489 /* ARGSUSED */
1490 1490 static int
1491 1491 fcsm_port_ioctl(opaque_t ulph, opaque_t port_handle, dev_t dev, int cmd,
1492 1492 intptr_t arg, int mode, cred_t *credp, int *rval, uint32_t claimed)
1493 1493 {
1494 1494 return (FC_UNCLAIMED);
1495 1495 }
1496 1496
1497 1497
1498 1498 /* ARGSUSED */
1499 1499 static int
1500 1500 fcsm_fciocmd(intptr_t arg, int mode, cred_t *credp, fcio_t *fcio)
1501 1501 {
1502 1502 int retval = 0;
1503 1503
1504 1504 switch (fcio->fcio_cmd) {
1505 1505 case FCSMIO_CT_CMD: {
1506 1506 fcsm_t *fcsm;
1507 1507 caddr_t user_ibuf, user_obuf;
1508 1508 caddr_t req_iu, rsp_iu, abuf;
1509 1509 int status, instance, count;
1510 1510
1511 1511 if ((fcio->fcio_xfer != FCIO_XFER_RW) ||
1512 1512 (fcio->fcio_ilen == 0) || (fcio->fcio_ibuf == 0) ||
1513 1513 (fcio->fcio_olen == 0) || (fcio->fcio_obuf == 0) ||
1514 1514 (fcio->fcio_alen == 0) || (fcio->fcio_abuf == 0) ||
1515 1515 (fcio->fcio_flags != 0) || (fcio->fcio_cmd_flags != 0) ||
1516 1516 (fcio->fcio_ilen > FCSM_MAX_CT_SIZE) ||
1517 1517 (fcio->fcio_olen > FCSM_MAX_CT_SIZE) ||
1518 1518 (fcio->fcio_alen > MAXPATHLEN)) {
1519 1519 retval = EINVAL;
1520 1520 break;
1521 1521 }
1522 1522
1523 1523 /*
1524 1524 * Get the destination port for which this ioctl
1525 1525 * is targeted. The abuf will have the fp_minor
1526 1526 * number.
1527 1527 */
1528 1528 abuf = kmem_zalloc(fcio->fcio_alen, KM_SLEEP);
1529 1529 ASSERT(abuf != NULL);
1530 1530 if (ddi_copyin(fcio->fcio_abuf, abuf, fcio->fcio_alen, mode)) {
1531 1531 retval = EFAULT;
1532 1532 kmem_free(abuf, fcio->fcio_alen);
1533 1533 break;
1534 1534 }
1535 1535
1536 1536 instance = *((int *)abuf);
1537 1537 kmem_free(abuf, fcio->fcio_alen);
1538 1538
1539 1539 if (instance < 0) {
1540 1540 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
1541 1541 "fciocmd: instance 0x%x, invalid instance",
1542 1542 instance));
1543 1543 retval = ENXIO;
1544 1544 break;
1545 1545 }
1546 1546
1547 1547 /*
1548 1548 * We confirmed that path corresponds to our port driver
1549 1549 * and a valid instance.
1550 1550 * If this port instance is not yet attached, then wait
1551 1551 * for a finite time for attach to complete
1552 1552 */
1553 1553 fcsm = ddi_get_soft_state(fcsm_state, instance);
1554 1554 count = 0;
1555 1555 while (count++ <= 30) {
1556 1556 if (fcsm != NULL) {
1557 1557 mutex_enter(&fcsm->sm_mutex);
1558 1558 if (fcsm->sm_flags & FCSM_ATTACHED) {
1559 1559 mutex_exit(&fcsm->sm_mutex);
1560 1560 break;
1561 1561 }
1562 1562 mutex_exit(&fcsm->sm_mutex);
1563 1563 }
1564 1564 if (count == 1) {
1565 1565 FCSM_DEBUG(SMDL_TRACE,
1566 1566 (CE_WARN, SM_LOG, NULL, NULL,
1567 1567 "fciocmd: instance 0x%x, "
1568 1568 "wait for port attach", instance));
1569 1569 }
1570 1570 delay(drv_usectohz(1000000));
1571 1571 fcsm = ddi_get_soft_state(fcsm_state, instance);
1572 1572 }
1573 1573 if (count > 30) {
1574 1574 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
1575 1575 "fciocmd: instance 0x%x, port not attached",
1576 1576 instance));
1577 1577 retval = ENXIO;
1578 1578 break;
1579 1579 }
1580 1580
1581 1581 req_iu = kmem_zalloc(fcio->fcio_ilen, KM_SLEEP);
1582 1582 rsp_iu = kmem_zalloc(fcio->fcio_olen, KM_SLEEP);
1583 1583 ASSERT((req_iu != NULL) && (rsp_iu != NULL));
1584 1584
1585 1585 if (ddi_copyin(fcio->fcio_ibuf, req_iu,
1586 1586 fcio->fcio_ilen, mode)) {
1587 1587 retval = EFAULT;
1588 1588 kmem_free(req_iu, fcio->fcio_ilen);
1589 1589 kmem_free(rsp_iu, fcio->fcio_olen);
1590 1590 break;
1591 1591 }
1592 1592
1593 1593 user_ibuf = fcio->fcio_ibuf;
1594 1594 user_obuf = fcio->fcio_obuf;
1595 1595 fcio->fcio_ibuf = req_iu;
1596 1596 fcio->fcio_obuf = rsp_iu;
1597 1597
1598 1598 status = fcsm_ct_passthru(fcsm->sm_instance, fcio, KM_SLEEP,
1599 1599 FCSM_JOBFLAG_SYNC, NULL);
1600 1600 if (status != FC_SUCCESS) {
1601 1601 retval = EIO;
1602 1602 }
1603 1603
1604 1604 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1605 1605 "fciocmd: cmd 0x%x completion status 0x%x",
1606 1606 fcio->fcio_cmd, status));
1607 1607 fcio->fcio_errno = status;
1608 1608 fcio->fcio_ibuf = user_ibuf;
1609 1609 fcio->fcio_obuf = user_obuf;
1610 1610
1611 1611 if (ddi_copyout(rsp_iu, fcio->fcio_obuf,
1612 1612 fcio->fcio_olen, mode)) {
1613 1613 retval = EFAULT;
1614 1614 kmem_free(req_iu, fcio->fcio_ilen);
1615 1615 kmem_free(rsp_iu, fcio->fcio_olen);
1616 1616 break;
1617 1617 }
1618 1618
1619 1619 kmem_free(req_iu, fcio->fcio_ilen);
1620 1620 kmem_free(rsp_iu, fcio->fcio_olen);
1621 1621
1622 1622 if (fcsm_fcio_copyout(fcio, arg, mode)) {
1623 1623 retval = EFAULT;
1624 1624 }
1625 1625 break;
1626 1626 }
1627 1627
1628 1628 case FCSMIO_ADAPTER_LIST: {
1629 1629 fc_hba_list_t *list;
1630 1630 int count;
1631 1631
1632 1632 if ((fcio->fcio_xfer != FCIO_XFER_RW) ||
1633 1633 (fcio->fcio_olen == 0) || (fcio->fcio_obuf == 0)) {
1634 1634 retval = EINVAL;
1635 1635 break;
1636 1636 }
1637 1637
1638 1638 list = kmem_zalloc(fcio->fcio_olen, KM_SLEEP);
1639 1639
1640 1640 if (ddi_copyin(fcio->fcio_obuf, list, fcio->fcio_olen, mode)) {
1641 1641 retval = EFAULT;
1642 1642 break;
1643 1643 }
1644 1644 list->version = FC_HBA_LIST_VERSION;
1645 1645
1646 1646 if (fcio->fcio_olen < MAXPATHLEN * list->numAdapters) {
1647 1647 retval = EFAULT;
1648 1648 break;
1649 1649 }
1650 1650
1651 1651 count = fc_ulp_get_adapter_paths((char *)list->hbaPaths,
1652 1652 list->numAdapters);
1653 1653 if (count < 0) {
1654 1654 /* Did something go wrong? */
1655 1655 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1656 1656 "Error fetching adapter list."));
1657 1657 retval = ENXIO;
1658 1658 kmem_free(list, fcio->fcio_olen);
1659 1659 break;
1660 1660 }
1661 1661 /* Sucess (or short buffer) */
1662 1662 list->numAdapters = count;
1663 1663 if (ddi_copyout(list, fcio->fcio_obuf,
1664 1664 fcio->fcio_olen, mode)) {
1665 1665 retval = EFAULT;
1666 1666 }
1667 1667 kmem_free(list, fcio->fcio_olen);
1668 1668 break;
1669 1669 }
1670 1670
1671 1671 default:
1672 1672 FCSM_DEBUG(SMDL_TRACE, (CE_NOTE, SM_LOG, NULL, NULL,
1673 1673 "fciocmd: unknown cmd <0x%x>", fcio->fcio_cmd));
1674 1674 retval = ENOTTY;
1675 1675 break;
1676 1676 }
1677 1677
1678 1678 return (retval);
1679 1679 }
1680 1680
1681 1681 static int
1682 1682 fcsm_fcio_copyout(fcio_t *fcio, intptr_t arg, int mode)
1683 1683 {
1684 1684 int status;
1685 1685
1686 1686 #ifdef _MULTI_DATAMODEL
1687 1687 switch (ddi_model_convert_from(mode & FMODELS)) {
1688 1688 case DDI_MODEL_ILP32: {
1689 1689 struct fcio32 fcio32;
1690 1690
1691 1691 fcio32.fcio_xfer = fcio->fcio_xfer;
1692 1692 fcio32.fcio_cmd = fcio->fcio_cmd;
1693 1693 fcio32.fcio_flags = fcio->fcio_flags;
1694 1694 fcio32.fcio_cmd_flags = fcio->fcio_cmd_flags;
1695 1695 fcio32.fcio_ilen = fcio->fcio_ilen;
1696 1696 fcio32.fcio_ibuf = (caddr32_t)(long)fcio->fcio_ibuf;
1697 1697 fcio32.fcio_olen = fcio->fcio_olen;
1698 1698 fcio32.fcio_obuf = (caddr32_t)(long)fcio->fcio_obuf;
1699 1699 fcio32.fcio_alen = fcio->fcio_alen;
1700 1700 fcio32.fcio_abuf = (caddr32_t)(long)fcio->fcio_abuf;
1701 1701 fcio32.fcio_errno = fcio->fcio_errno;
1702 1702
1703 1703 status = ddi_copyout((void *)&fcio32, (void *)arg,
1704 1704 sizeof (struct fcio32), mode);
1705 1705 break;
1706 1706 }
1707 1707 case DDI_MODEL_NONE:
1708 1708 status = ddi_copyout((void *)fcio, (void *)arg,
1709 1709 sizeof (fcio_t), mode);
1710 1710 break;
1711 1711 }
1712 1712 #else /* _MULTI_DATAMODEL */
1713 1713 status = ddi_copyout((void *)fcio, (void *)arg, sizeof (fcio_t), mode);
1714 1714 #endif /* _MULTI_DATAMODEL */
1715 1715
1716 1716 return (status);
1717 1717 }
1718 1718
1719 1719
1720 1720 /* ARGSUSED */
1721 1721 static int
1722 1722 fcsm_open(dev_t *devp, int flags, int otyp, cred_t *credp)
1723 1723 {
1724 1724 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "open"));
1725 1725
1726 1726 if (otyp != OTYP_CHR) {
1727 1727 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1728 1728 "fcsm_open: failed. open type 0x%x for minor 0x%x is not "
1729 1729 "OTYP_CHR", otyp, getminor(*devp)));
1730 1730 return (EINVAL);
1731 1731 }
1732 1732
1733 1733 /*
1734 1734 * Allow anybody to open (both root and non-root users).
1735 1735 * Previlege level checks are made on the per ioctl basis.
1736 1736 */
1737 1737 mutex_enter(&fcsm_global_mutex);
1738 1738 if (flags & FEXCL) {
1739 1739 if (fcsm_flag & FCSM_OPEN) {
1740 1740 mutex_exit(&fcsm_global_mutex);
1741 1741 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1742 1742 "fcsm_open: exclusive open of 0x%x failed",
1743 1743 getminor(*devp)));
1744 1744 return (EBUSY);
1745 1745 } else {
1746 1746 ASSERT(fcsm_flag == FCSM_IDLE);
1747 1747 fcsm_flag |= FCSM_EXCL;
1748 1748 }
1749 1749 } else {
1750 1750 if (fcsm_flag & FCSM_EXCL) {
1751 1751 mutex_exit(&fcsm_global_mutex);
1752 1752 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1753 1753 "fcsm_open: failed. Device minor 0x%x is in "
1754 1754 "exclusive open mode", getminor(*devp)));
1755 1755 return (EBUSY);
1756 1756 }
1757 1757
1758 1758 }
1759 1759 fcsm_flag |= FCSM_OPEN;
1760 1760 mutex_exit(&fcsm_global_mutex);
1761 1761 return (0);
1762 1762 }
1763 1763
1764 1764
1765 1765 /* ARGSUSED */
1766 1766 static int
1767 1767 fcsm_close(dev_t dev, int flag, int otyp, cred_t *credp)
1768 1768 {
1769 1769 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "close"));
1770 1770
1771 1771 if (otyp != OTYP_CHR) {
1772 1772 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1773 1773 "fcsm_close: failed. close type 0x%x for minor 0x%x is not "
1774 1774 "OTYP_CHR", otyp, getminor(dev)));
1775 1775 return (EINVAL);
1776 1776 }
1777 1777
1778 1778 mutex_enter(&fcsm_global_mutex);
1779 1779 if ((fcsm_flag & FCSM_OPEN) == 0) {
1780 1780 mutex_exit(&fcsm_global_mutex);
1781 1781 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1782 1782 "fcsm_close: failed. minor 0x%x is already closed",
1783 1783 getminor(dev)));
1784 1784 return (ENODEV);
1785 1785 }
1786 1786 fcsm_flag = FCSM_IDLE;
1787 1787 mutex_exit(&fcsm_global_mutex);
1788 1788 return (0);
1789 1789 }
1790 1790
1791 1791
1792 1792 /* ARGSUSED */
1793 1793 static void
1794 1794 fcsm_disp_devlist(fcsm_t *fcsm, fc_portmap_t *devlist, uint32_t dev_cnt)
1795 1795 {
1796 1796 fc_portmap_t *map;
1797 1797 uint32_t i;
1798 1798
1799 1799 if (dev_cnt == 0) {
1800 1800 return;
1801 1801 }
1802 1802
1803 1803 ASSERT(devlist != NULL);
1804 1804 for (i = 0; i < dev_cnt; i++) {
1805 1805 map = &devlist[i];
1806 1806 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1807 1807 "list[%d]: ID 0x%x WWN %x:%x:%x:%x:%x:%x:%x:%x "
1808 1808 "state (0x%x) "
1809 1809 "type <%s>(0x%x) "
1810 1810 "flags (0x%x)",
1811 1811 i, map->map_did.port_id,
1812 1812 map->map_pwwn.raw_wwn[0], map->map_pwwn.raw_wwn[1],
1813 1813 map->map_pwwn.raw_wwn[2], map->map_pwwn.raw_wwn[3],
1814 1814 map->map_pwwn.raw_wwn[4], map->map_pwwn.raw_wwn[5],
1815 1815 map->map_pwwn.raw_wwn[6], map->map_pwwn.raw_wwn[7],
1816 1816 map->map_state,
1817 1817 fcsm_dev_type_to_str(map->map_type), map->map_type,
1818 1818 map->map_flags));
1819 1819 }
1820 1820 }
1821 1821
1822 1822 /* ARGSUSED */
1823 1823 static void
1824 1824 fcsm_display(int level, int flags, fcsm_t *fcsm, fc_packet_t *pkt,
1825 1825 const char *fmt, ...)
1826 1826 {
1827 1827 caddr_t buf;
1828 1828 va_list ap;
1829 1829
1830 1830 buf = kmem_zalloc(256, KM_NOSLEEP);
1831 1831 if (buf == NULL) {
1832 1832 return;
1833 1833 }
1834 1834
1835 1835 if (fcsm) {
1836 1836 (void) sprintf(buf + strlen(buf), "fcsm(%d): ",
1837 1837 ddi_get_instance(fcsm->sm_port_info.port_dip));
1838 1838 } else {
1839 1839 (void) sprintf(buf, "fcsm: ");
1840 1840 }
1841 1841
1842 1842 va_start(ap, fmt);
1843 1843 (void) vsprintf(buf + strlen(buf), fmt, ap);
1844 1844 va_end(ap);
1845 1845
1846 1846 if (pkt) {
1847 1847 caddr_t state, reason, action, expln;
1848 1848
1849 1849 (void) fc_ulp_pkt_error(pkt, &state, &reason, &action, &expln);
1850 1850
1851 1851 (void) sprintf(buf + strlen(buf),
1852 1852 " state: %s(0x%x); reason: %s(0x%x)",
1853 1853 state, pkt->pkt_state, reason, pkt->pkt_reason);
1854 1854 }
1855 1855
1856 1856 switch (flags) {
1857 1857 case SM_LOG:
1858 1858 cmn_err(level, "!%s", buf);
1859 1859 break;
1860 1860
1861 1861 case SM_CONSOLE:
1862 1862 cmn_err(level, "^%s", buf);
1863 1863 break;
1864 1864
1865 1865 default:
1866 1866 cmn_err(level, "%s", buf);
1867 1867 break;
1868 1868 }
1869 1869
1870 1870 kmem_free(buf, 256);
1871 1871 }
1872 1872
1873 1873
1874 1874 /*
1875 1875 * Convert FC packet state to FC errno
1876 1876 */
1877 1877 int
1878 1878 fcsm_pkt_state_to_rval(uchar_t state, uint32_t reason)
1879 1879 {
1880 1880 int count;
1881 1881
1882 1882 if (state == FC_PKT_LOCAL_RJT && (reason == FC_REASON_NO_CONNECTION ||
1883 1883 reason == FC_REASON_LOGIN_REQUIRED)) {
1884 1884 return (FC_LOGINREQ);
1885 1885 } else if (state == FC_PKT_PORT_OFFLINE &&
1886 1886 reason == FC_REASON_LOGIN_REQUIRED) {
1887 1887 return (FC_LOGINREQ);
1888 1888 }
1889 1889
1890 1890 for (count = 0; count < sizeof (fcsm_xlat_pkt_state) /
1891 1891 sizeof (fcsm_xlat_pkt_state[0]); count++) {
1892 1892 if (fcsm_xlat_pkt_state[count].xlat_state == state) {
1893 1893 return (fcsm_xlat_pkt_state[count].xlat_rval);
1894 1894 }
1895 1895 }
1896 1896
1897 1897 return (FC_FAILURE);
1898 1898 }
1899 1899
1900 1900
1901 1901 /*
1902 1902 * Convert port state state to descriptive string
1903 1903 */
1904 1904 caddr_t
1905 1905 fcsm_port_state_to_str(uint32_t port_state)
1906 1906 {
1907 1907 int count;
1908 1908
1909 1909 for (count = 0; count < sizeof (fcsm_xlat_port_state) /
1910 1910 sizeof (fcsm_xlat_port_state[0]); count++) {
1911 1911 if (fcsm_xlat_port_state[count].xlat_pstate == port_state) {
1912 1912 return (fcsm_xlat_port_state[count].xlat_state_str);
1913 1913 }
1914 1914 }
1915 1915
1916 1916 return (NULL);
1917 1917 }
1918 1918
1919 1919
1920 1920 /*
1921 1921 * Convert port topology state to descriptive string
1922 1922 */
1923 1923 caddr_t
1924 1924 fcsm_topology_to_str(uint32_t topology)
1925 1925 {
1926 1926 int count;
1927 1927
1928 1928 for (count = 0; count < sizeof (fcsm_xlat_topology) /
1929 1929 sizeof (fcsm_xlat_topology[0]); count++) {
1930 1930 if (fcsm_xlat_topology[count].xlat_top == topology) {
1931 1931 return (fcsm_xlat_topology[count].xlat_top_str);
1932 1932 }
1933 1933 }
1934 1934
1935 1935 return (NULL);
1936 1936 }
1937 1937
1938 1938
1939 1939 /*
1940 1940 * Convert port topology state to descriptive string
1941 1941 */
1942 1942 static caddr_t
1943 1943 fcsm_dev_type_to_str(uint32_t type)
1944 1944 {
1945 1945 int count;
1946 1946
1947 1947 for (count = 0; count < sizeof (fcsm_xlat_dev_type) /
1948 1948 sizeof (fcsm_xlat_dev_type[0]); count++) {
1949 1949 if (fcsm_xlat_dev_type[count].xlat_type == type) {
1950 1950 return (fcsm_xlat_dev_type[count].xlat_str);
1951 1951 }
1952 1952 }
1953 1953
1954 1954 return (NULL);
1955 1955 }
1956 1956
1957 1957 static int
1958 1958 fcsm_cmd_cache_constructor(void *buf, void *cdarg, int kmflags)
1959 1959 {
1960 1960 fcsm_cmd_t *cmd = (fcsm_cmd_t *)buf;
1961 1961 fcsm_t *fcsm = (fcsm_t *)cdarg;
1962 1962 int (*callback)(caddr_t);
1963 1963 fc_packet_t *pkt;
1964 1964 fc_ulp_port_info_t *pinfo;
1965 1965
1966 1966 ASSERT(fcsm != NULL && buf != NULL);
1967 1967 callback = (kmflags == KM_SLEEP) ? DDI_DMA_SLEEP: DDI_DMA_DONTWAIT;
1968 1968
1969 1969 cmd->cmd_fp_pkt = &cmd->cmd_fc_packet;
1970 1970 cmd->cmd_job = NULL;
1971 1971 cmd->cmd_fcsm = fcsm;
1972 1972 cmd->cmd_dma_flags = 0;
1973 1973
1974 1974 pkt = &cmd->cmd_fc_packet;
1975 1975
1976 1976 pkt->pkt_ulp_rscn_infop = NULL;
1977 1977 pkt->pkt_fca_private = (opaque_t)((caddr_t)cmd + sizeof (fcsm_cmd_t));
1978 1978 pkt->pkt_ulp_private = (opaque_t)cmd;
1979 1979
1980 1980 if (!(fcsm->sm_flags & FCSM_USING_NODMA_FCA)) {
1981 1981 pinfo = &fcsm->sm_port_info;
1982 1982 if (ddi_dma_alloc_handle(pinfo->port_dip,
1983 1983 pinfo->port_cmd_dma_attr,
1984 1984 callback, NULL, &pkt->pkt_cmd_dma) != DDI_SUCCESS) {
1985 1985 return (1);
1986 1986 }
1987 1987
1988 1988 if (ddi_dma_alloc_handle(pinfo->port_dip,
1989 1989 pinfo->port_resp_dma_attr,
1990 1990 callback, NULL, &pkt->pkt_resp_dma) != DDI_SUCCESS) {
1991 1991 ddi_dma_free_handle(&pkt->pkt_cmd_dma);
1992 1992 return (1);
1993 1993 }
1994 1994 } else {
1995 1995 pkt->pkt_cmd_dma = NULL;
1996 1996 pkt->pkt_cmd = NULL;
1997 1997 pkt->pkt_resp_dma = NULL;
1998 1998 pkt->pkt_resp = NULL;
1999 1999 }
2000 2000
2001 2001 pkt->pkt_cmd_acc = pkt->pkt_resp_acc = NULL;
2002 2002 pkt->pkt_cmd_cookie_cnt = pkt->pkt_resp_cookie_cnt =
2003 2003 pkt->pkt_data_cookie_cnt = 0;
2004 2004 pkt->pkt_cmd_cookie = pkt->pkt_resp_cookie =
2005 2005 pkt->pkt_data_cookie = NULL;
2006 2006
2007 2007 return (0);
2008 2008 }
2009 2009
2010 2010
2011 2011 /* ARGSUSED */
2012 2012 static void
2013 2013 fcsm_cmd_cache_destructor(void *buf, void *cdarg)
2014 2014 {
2015 2015 fcsm_cmd_t *cmd = (fcsm_cmd_t *)buf;
2016 2016 fcsm_t *fcsm = (fcsm_t *)cdarg;
2017 2017 fc_packet_t *pkt;
2018 2018
2019 2019 ASSERT(fcsm == cmd->cmd_fcsm);
2020 2020
2021 2021 pkt = cmd->cmd_fp_pkt;
2022 2022
2023 2023 if (pkt->pkt_cmd_dma != NULL) {
2024 2024 ddi_dma_free_handle(&pkt->pkt_cmd_dma);
2025 2025 }
2026 2026
2027 2027 if (pkt->pkt_resp_dma != NULL) {
2028 2028 ddi_dma_free_handle(&pkt->pkt_resp_dma);
2029 2029 }
2030 2030 }
2031 2031
2032 2032
2033 2033 static fcsm_cmd_t *
2034 2034 fcsm_alloc_cmd(fcsm_t *fcsm, uint32_t cmd_len, uint32_t resp_len, int sleep)
2035 2035 {
2036 2036 fcsm_cmd_t *cmd;
2037 2037 fc_packet_t *pkt;
2038 2038 int rval;
2039 2039 ulong_t real_len;
2040 2040 int (*callback)(caddr_t);
2041 2041 ddi_dma_cookie_t pkt_cookie;
2042 2042 ddi_dma_cookie_t *cp;
2043 2043 uint32_t cnt;
2044 2044 fc_ulp_port_info_t *pinfo;
2045 2045
2046 2046 ASSERT(fcsm != NULL);
2047 2047 pinfo = &fcsm->sm_port_info;
2048 2048
2049 2049 callback = (sleep == KM_SLEEP) ? DDI_DMA_SLEEP: DDI_DMA_DONTWAIT;
2050 2050
2051 2051 cmd = (fcsm_cmd_t *)kmem_cache_alloc(fcsm->sm_cmd_cache, sleep);
2052 2052 if (cmd == NULL) {
2053 2053 FCSM_DEBUG(SMDL_ERR, (CE_WARN, SM_LOG, fcsm, NULL,
2054 2054 "alloc_cmd: kmem_cache_alloc failed"));
2055 2055 return (NULL);
2056 2056 }
2057 2057
2058 2058 cmd->cmd_retry_count = 0;
2059 2059 cmd->cmd_max_retries = 0;
2060 2060 cmd->cmd_retry_interval = 0;
2061 2061 cmd->cmd_transport = NULL;
2062 2062
2063 2063 ASSERT(cmd->cmd_dma_flags == 0);
2064 2064 ASSERT(cmd->cmd_fp_pkt == &cmd->cmd_fc_packet);
2065 2065 pkt = cmd->cmd_fp_pkt;
2066 2066
2067 2067 /* Zero out the important fc_packet fields */
2068 2068 pkt->pkt_pd = NULL;
2069 2069 pkt->pkt_datalen = 0;
2070 2070 pkt->pkt_data = NULL;
2071 2071 pkt->pkt_state = 0;
2072 2072 pkt->pkt_action = 0;
2073 2073 pkt->pkt_reason = 0;
2074 2074 pkt->pkt_expln = 0;
2075 2075
2076 2076 /*
2077 2077 * Now that pkt_pd is initialized, we can call fc_ulp_init_packet
2078 2078 */
2079 2079
2080 2080 if (fc_ulp_init_packet((opaque_t)pinfo->port_handle, pkt, sleep)
2081 2081 != FC_SUCCESS) {
2082 2082 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2083 2083 return (NULL);
2084 2084 }
2085 2085
2086 2086 if ((cmd_len) && !(fcsm->sm_flags & FCSM_USING_NODMA_FCA)) {
2087 2087 ASSERT(pkt->pkt_cmd_dma != NULL);
2088 2088
2089 2089 rval = ddi_dma_mem_alloc(pkt->pkt_cmd_dma, cmd_len,
2090 2090 fcsm->sm_port_info.port_acc_attr, DDI_DMA_CONSISTENT,
2091 2091 callback, NULL, (caddr_t *)&pkt->pkt_cmd, &real_len,
2092 2092 &pkt->pkt_cmd_acc);
2093 2093
2094 2094 if (rval != DDI_SUCCESS) {
2095 2095 (void) fc_ulp_uninit_packet(
2096 2096 (opaque_t)pinfo->port_handle, pkt);
2097 2097 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2098 2098 fcsm_free_cmd_dma(cmd);
2099 2099 return (NULL);
2100 2100 }
2101 2101
2102 2102 cmd->cmd_dma_flags |= FCSM_CF_CMD_VALID_DMA_MEM;
2103 2103
2104 2104 if (real_len < cmd_len) {
2105 2105 (void) fc_ulp_uninit_packet(
2106 2106 (opaque_t)pinfo->port_handle, pkt);
2107 2107 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2108 2108 fcsm_free_cmd_dma(cmd);
2109 2109 return (NULL);
2110 2110 }
2111 2111
2112 2112 rval = ddi_dma_addr_bind_handle(pkt->pkt_cmd_dma, NULL,
2113 2113 pkt->pkt_cmd, real_len, DDI_DMA_WRITE | DDI_DMA_CONSISTENT,
2114 2114 callback, NULL, &pkt_cookie, &pkt->pkt_cmd_cookie_cnt);
2115 2115
2116 2116 if (rval != DDI_DMA_MAPPED) {
2117 2117 (void) fc_ulp_uninit_packet(
2118 2118 (opaque_t)pinfo->port_handle, pkt);
2119 2119 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2120 2120 fcsm_free_cmd_dma(cmd);
2121 2121 return (NULL);
2122 2122 }
2123 2123
2124 2124 cmd->cmd_dma_flags |= FCSM_CF_CMD_VALID_DMA_BIND;
2125 2125
2126 2126 if (pkt->pkt_cmd_cookie_cnt >
2127 2127 pinfo->port_cmd_dma_attr->dma_attr_sgllen) {
2128 2128 (void) fc_ulp_uninit_packet(
2129 2129 (opaque_t)pinfo->port_handle, pkt);
2130 2130 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2131 2131 fcsm_free_cmd_dma(cmd);
2132 2132 return (NULL);
2133 2133 }
2134 2134
2135 2135 ASSERT(pkt->pkt_cmd_cookie_cnt != 0);
2136 2136
2137 2137 cp = pkt->pkt_cmd_cookie = (ddi_dma_cookie_t *)kmem_alloc(
2138 2138 pkt->pkt_cmd_cookie_cnt * sizeof (pkt_cookie),
2139 2139 KM_NOSLEEP);
2140 2140
2141 2141 if (cp == NULL) {
2142 2142 (void) fc_ulp_uninit_packet(
2143 2143 (opaque_t)pinfo->port_handle, pkt);
2144 2144 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2145 2145 fcsm_free_cmd_dma(cmd);
2146 2146 return (NULL);
2147 2147 }
2148 2148
2149 2149 *cp = pkt_cookie;
2150 2150 cp++;
2151 2151 for (cnt = 1; cnt < pkt->pkt_cmd_cookie_cnt; cnt++, cp++) {
2152 2152 ddi_dma_nextcookie(pkt->pkt_cmd_dma, &pkt_cookie);
2153 2153 *cp = pkt_cookie;
2154 2154 }
2155 2155 } else if (cmd_len != 0) {
2156 2156 pkt->pkt_cmd = kmem_zalloc(cmd_len, KM_SLEEP);
2157 2157 }
2158 2158
2159 2159 if ((resp_len) && !(fcsm->sm_flags & FCSM_USING_NODMA_FCA)) {
2160 2160 ASSERT(pkt->pkt_resp_dma != NULL);
2161 2161
2162 2162 rval = ddi_dma_mem_alloc(pkt->pkt_resp_dma, resp_len,
2163 2163 fcsm->sm_port_info.port_acc_attr, DDI_DMA_CONSISTENT,
2164 2164 callback, NULL, (caddr_t *)&pkt->pkt_resp, &real_len,
2165 2165 &pkt->pkt_resp_acc);
2166 2166
2167 2167 if (rval != DDI_SUCCESS) {
2168 2168 (void) fc_ulp_uninit_packet(
2169 2169 (opaque_t)pinfo->port_handle, pkt);
2170 2170 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2171 2171 fcsm_free_cmd_dma(cmd);
2172 2172 return (NULL);
2173 2173 }
2174 2174
2175 2175 cmd->cmd_dma_flags |= FCSM_CF_RESP_VALID_DMA_MEM;
2176 2176
2177 2177 if (real_len < resp_len) {
2178 2178 (void) fc_ulp_uninit_packet(
2179 2179 (opaque_t)pinfo->port_handle, pkt);
2180 2180 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2181 2181 fcsm_free_cmd_dma(cmd);
2182 2182 return (NULL);
2183 2183 }
2184 2184
2185 2185 rval = ddi_dma_addr_bind_handle(pkt->pkt_resp_dma, NULL,
2186 2186 pkt->pkt_resp, real_len, DDI_DMA_READ | DDI_DMA_CONSISTENT,
2187 2187 callback, NULL, &pkt_cookie, &pkt->pkt_resp_cookie_cnt);
2188 2188
2189 2189 if (rval != DDI_DMA_MAPPED) {
2190 2190 (void) fc_ulp_uninit_packet(
2191 2191 (opaque_t)pinfo->port_handle, pkt);
2192 2192 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2193 2193 fcsm_free_cmd_dma(cmd);
2194 2194 return (NULL);
2195 2195 }
2196 2196
2197 2197 cmd->cmd_dma_flags |= FCSM_CF_RESP_VALID_DMA_BIND;
2198 2198
2199 2199 if (pkt->pkt_resp_cookie_cnt >
2200 2200 pinfo->port_resp_dma_attr->dma_attr_sgllen) {
2201 2201 (void) fc_ulp_uninit_packet(
2202 2202 (opaque_t)pinfo->port_handle, pkt);
2203 2203 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2204 2204 fcsm_free_cmd_dma(cmd);
2205 2205 return (NULL);
2206 2206 }
2207 2207
2208 2208 ASSERT(pkt->pkt_resp_cookie_cnt != 0);
2209 2209
2210 2210 cp = pkt->pkt_resp_cookie = (ddi_dma_cookie_t *)kmem_alloc(
2211 2211 pkt->pkt_resp_cookie_cnt * sizeof (pkt_cookie),
2212 2212 KM_NOSLEEP);
2213 2213
2214 2214 if (cp == NULL) {
2215 2215 (void) fc_ulp_uninit_packet(
2216 2216 (opaque_t)pinfo->port_handle, pkt);
2217 2217 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2218 2218 fcsm_free_cmd_dma(cmd);
2219 2219 return (NULL);
2220 2220 }
2221 2221
2222 2222 *cp = pkt_cookie;
2223 2223 cp++;
2224 2224 for (cnt = 1; cnt < pkt->pkt_resp_cookie_cnt; cnt++, cp++) {
2225 2225 ddi_dma_nextcookie(pkt->pkt_resp_dma, &pkt_cookie);
2226 2226 *cp = pkt_cookie;
2227 2227 }
2228 2228 } else if (resp_len != 0) {
2229 2229 pkt->pkt_resp = kmem_zalloc(resp_len, KM_SLEEP);
2230 2230 }
2231 2231
2232 2232 pkt->pkt_cmdlen = cmd_len;
2233 2233 pkt->pkt_rsplen = resp_len;
2234 2234
2235 2235 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2236 2236 "alloc_cmd: cmd 0x%p", (void *)cmd));
2237 2237 return (cmd);
2238 2238 }
2239 2239
2240 2240 static void
2241 2241 fcsm_free_cmd(fcsm_cmd_t *cmd)
2242 2242 {
2243 2243 fcsm_t *fcsm;
2244 2244
2245 2245 fcsm = cmd->cmd_fcsm;
2246 2246 ASSERT(fcsm != NULL);
2247 2247
2248 2248 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2249 2249 "free_cmd: cmd 0x%p", (void *)cmd));
2250 2250
2251 2251 fcsm_free_cmd_dma(cmd);
2252 2252
2253 2253 (void) fc_ulp_uninit_packet((opaque_t)fcsm->sm_port_info.port_handle,
2254 2254 cmd->cmd_fp_pkt);
2255 2255 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2256 2256 }
2257 2257
2258 2258 static void
2259 2259 fcsm_free_cmd_dma(fcsm_cmd_t *cmd)
2260 2260 {
2261 2261 fc_packet_t *pkt;
2262 2262
2263 2263 pkt = cmd->cmd_fp_pkt;
2264 2264 ASSERT(pkt != NULL);
2265 2265
2266 2266 if (cmd->cmd_fcsm->sm_flags & FCSM_USING_NODMA_FCA) {
2267 2267 if (pkt->pkt_cmd) {
2268 2268 kmem_free(pkt->pkt_cmd, pkt->pkt_cmdlen);
2269 2269 pkt->pkt_cmd = NULL;
2270 2270 }
2271 2271
2272 2272 if (pkt->pkt_resp) {
2273 2273 kmem_free(pkt->pkt_resp, pkt->pkt_rsplen);
2274 2274 pkt->pkt_resp = NULL;
2275 2275 }
2276 2276 }
2277 2277
2278 2278 pkt->pkt_cmdlen = 0;
2279 2279 pkt->pkt_rsplen = 0;
2280 2280 pkt->pkt_tran_type = 0;
2281 2281 pkt->pkt_tran_flags = 0;
2282 2282
2283 2283 if (pkt->pkt_cmd_cookie != NULL) {
2284 2284 kmem_free(pkt->pkt_cmd_cookie, pkt->pkt_cmd_cookie_cnt *
2285 2285 sizeof (ddi_dma_cookie_t));
2286 2286 pkt->pkt_cmd_cookie = NULL;
2287 2287 }
2288 2288
2289 2289 if (pkt->pkt_resp_cookie != NULL) {
2290 2290 kmem_free(pkt->pkt_resp_cookie, pkt->pkt_resp_cookie_cnt *
2291 2291 sizeof (ddi_dma_cookie_t));
2292 2292 pkt->pkt_resp_cookie = NULL;
2293 2293 }
2294 2294
2295 2295 if (cmd->cmd_dma_flags & FCSM_CF_CMD_VALID_DMA_BIND) {
2296 2296 (void) ddi_dma_unbind_handle(pkt->pkt_cmd_dma);
2297 2297 }
2298 2298
2299 2299 if (cmd->cmd_dma_flags & FCSM_CF_CMD_VALID_DMA_MEM) {
2300 2300 if (pkt->pkt_cmd_acc) {
2301 2301 ddi_dma_mem_free(&pkt->pkt_cmd_acc);
2302 2302 }
2303 2303 }
2304 2304
2305 2305 if (cmd->cmd_dma_flags & FCSM_CF_RESP_VALID_DMA_BIND) {
2306 2306 (void) ddi_dma_unbind_handle(pkt->pkt_resp_dma);
2307 2307 }
2308 2308
2309 2309 if (cmd->cmd_dma_flags & FCSM_CF_RESP_VALID_DMA_MEM) {
2310 2310 if (pkt->pkt_resp_acc) {
2311 2311 ddi_dma_mem_free(&pkt->pkt_resp_acc);
2312 2312 }
2313 2313 }
2314 2314
2315 2315 cmd->cmd_dma_flags = 0;
2316 2316 }
2317 2317
2318 2318 /* ARGSUSED */
2319 2319 static int
2320 2320 fcsm_job_cache_constructor(void *buf, void *cdarg, int kmflag)
2321 2321 {
2322 2322 fcsm_job_t *job = (fcsm_job_t *)buf;
2323 2323
2324 2324 mutex_init(&job->job_mutex, NULL, MUTEX_DRIVER, NULL);
2325 2325 sema_init(&job->job_sema, 0, NULL, SEMA_DEFAULT, NULL);
2326 2326
2327 2327 return (0);
2328 2328 }
2329 2329
2330 2330 /* ARGSUSED */
2331 2331 static void
2332 2332 fcsm_job_cache_destructor(void *buf, void *cdarg)
2333 2333 {
2334 2334 fcsm_job_t *job = (fcsm_job_t *)buf;
2335 2335
2336 2336 sema_destroy(&job->job_sema);
2337 2337 mutex_destroy(&job->job_mutex);
2338 2338 }
2339 2339
2340 2340
2341 2341 static fcsm_job_t *
2342 2342 fcsm_alloc_job(int sleep)
2343 2343 {
2344 2344 fcsm_job_t *job;
2345 2345
2346 2346 job = (fcsm_job_t *)kmem_cache_alloc(fcsm_job_cache, sleep);
2347 2347 if (job != NULL) {
2348 2348 job->job_code = FCSM_JOB_NONE;
2349 2349 job->job_flags = 0;
2350 2350 job->job_port_instance = -1;
2351 2351 job->job_result = -1;
2352 2352 job->job_arg = (opaque_t)0;
2353 2353 job->job_caller_priv = (opaque_t)0;
2354 2354 job->job_comp = NULL;
2355 2355 job->job_comp_arg = (opaque_t)0;
2356 2356 job->job_priv = (void *)0;
2357 2357 job->job_priv_flags = 0;
2358 2358 job->job_next = 0;
2359 2359 }
2360 2360
2361 2361 return (job);
2362 2362 }
2363 2363
2364 2364 static void
2365 2365 fcsm_dealloc_job(fcsm_job_t *job)
2366 2366 {
2367 2367 kmem_cache_free(fcsm_job_cache, (void *)job);
2368 2368 }
2369 2369
2370 2370
2371 2371 static void
2372 2372 fcsm_init_job(fcsm_job_t *job, int instance, uint32_t command, uint32_t flags,
2373 2373 opaque_t arg, opaque_t caller_priv,
2374 2374 void (*comp)(opaque_t, fcsm_job_t *, int), opaque_t comp_arg)
2375 2375 {
2376 2376 ASSERT(job != NULL);
2377 2377 job->job_port_instance = instance;
2378 2378 job->job_code = command;
2379 2379 job->job_flags = flags;
2380 2380 job->job_arg = arg;
2381 2381 job->job_caller_priv = caller_priv;
2382 2382 job->job_comp = comp;
2383 2383 job->job_comp_arg = comp_arg;
2384 2384 job->job_retry_count = 0;
2385 2385 }
2386 2386
2387 2387 static int
2388 2388 fcsm_process_job(fcsm_job_t *job, int priority_flag)
2389 2389 {
2390 2390 fcsm_t *fcsm;
2391 2391 int sync;
2392 2392
2393 2393 ASSERT(job != NULL);
2394 2394 ASSERT(!MUTEX_HELD(&job->job_mutex));
2395 2395
2396 2396 fcsm = ddi_get_soft_state(fcsm_state, job->job_port_instance);
2397 2397
2398 2398 if (fcsm == NULL) {
2399 2399 FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, NULL, NULL,
2400 2400 "process_job: port instance 0x%x not found",
2401 2401 job->job_port_instance));
2402 2402 return (FC_BADDEV);
2403 2403 }
2404 2404
2405 2405 mutex_enter(&job->job_mutex);
2406 2406 /* Both SYNC and ASYNC flags should not be set */
2407 2407 ASSERT(((job->job_flags & (FCSM_JOBFLAG_SYNC | FCSM_JOBFLAG_ASYNC)) ==
2408 2408 FCSM_JOBFLAG_SYNC) || ((job->job_flags &
2409 2409 (FCSM_JOBFLAG_SYNC | FCSM_JOBFLAG_ASYNC)) == FCSM_JOBFLAG_ASYNC));
2410 2410 /*
2411 2411 * Check if job is a synchronous job. We might not be able to
2412 2412 * check it reliably after enque_job(), if job is an ASYNC job.
2413 2413 */
2414 2414 sync = job->job_flags & FCSM_JOBFLAG_SYNC;
2415 2415 mutex_exit(&job->job_mutex);
2416 2416
2417 2417 /* Queue the job for processing by job thread */
2418 2418 fcsm_enque_job(fcsm, job, priority_flag);
2419 2419
2420 2420 /* Wait for job completion, if it is a synchronous job */
2421 2421 if (sync) {
2422 2422 /*
2423 2423 * This is a Synchronous Job. So job structure is available.
2424 2424 * Caller is responsible for freeing it.
2425 2425 */
2426 2426 FCSM_DEBUG(SMDL_ERR, (CE_CONT, SM_LOG, fcsm, NULL,
2427 2427 "process_job: Waiting for sync job <%p> completion",
2428 2428 (void *)job));
2429 2429 sema_p(&job->job_sema);
2430 2430 }
2431 2431
2432 2432 return (FC_SUCCESS);
2433 2433 }
2434 2434
2435 2435 static void
2436 2436 fcsm_enque_job(fcsm_t *fcsm, fcsm_job_t *job, int priority_flag)
2437 2437 {
2438 2438 ASSERT(!MUTEX_HELD(&fcsm->sm_mutex));
2439 2439
2440 2440 mutex_enter(&fcsm->sm_mutex);
2441 2441 /* Queue the job at the head or tail depending on the job priority */
2442 2442 if (priority_flag) {
2443 2443 FCSM_DEBUG(SMDL_INFO, (CE_CONT, SM_LOG, fcsm, NULL,
2444 2444 "enque_job: job 0x%p is high priority", job));
2445 2445 /* Queue at the head */
2446 2446 if (fcsm->sm_job_tail == NULL) {
2447 2447 ASSERT(fcsm->sm_job_head == NULL);
2448 2448 fcsm->sm_job_head = fcsm->sm_job_tail = job;
2449 2449 } else {
2450 2450 ASSERT(fcsm->sm_job_head != NULL);
2451 2451 job->job_next = fcsm->sm_job_head;
2452 2452 fcsm->sm_job_head = job;
2453 2453 }
2454 2454 } else {
2455 2455 FCSM_DEBUG(SMDL_INFO, (CE_CONT, SM_LOG, fcsm, NULL,
2456 2456 "enque_job: job 0x%p is normal", job));
2457 2457 /* Queue at the tail */
2458 2458 if (fcsm->sm_job_tail == NULL) {
2459 2459 ASSERT(fcsm->sm_job_head == NULL);
2460 2460 fcsm->sm_job_head = fcsm->sm_job_tail = job;
2461 2461 } else {
2462 2462 ASSERT(fcsm->sm_job_head != NULL);
2463 2463 fcsm->sm_job_tail->job_next = job;
2464 2464 fcsm->sm_job_tail = job;
2465 2465 }
2466 2466 job->job_next = NULL;
2467 2467 }
2468 2468
2469 2469 /* Signal the job thread to process the job */
2470 2470 cv_signal(&fcsm->sm_job_cv);
2471 2471 mutex_exit(&fcsm->sm_mutex);
2472 2472 }
2473 2473
2474 2474 static int
2475 2475 fcsm_retry_job(fcsm_t *fcsm, fcsm_job_t *job)
2476 2476 {
2477 2477 /*
2478 2478 * If it is a CT passthru job and status is login required, then
2479 2479 * retry the job so that login can be performed again.
2480 2480 * Ensure that this retry is performed a finite number of times,
2481 2481 * so that a faulty fabric does not cause us to retry forever.
2482 2482 */
2483 2483
2484 2484 switch (job->job_code) {
2485 2485 case FCSM_JOB_CT_PASSTHRU: {
2486 2486 uint32_t jobflag;
2487 2487 fc_ct_header_t *ct_header;
2488 2488
2489 2489 if (job->job_result != FC_LOGINREQ) {
2490 2490 break;
2491 2491 }
2492 2492
2493 2493 /*
2494 2494 * If it is a management server command
2495 2495 * then Reset the Management server login flag, so that login
2496 2496 * gets re-established.
2497 2497 * If it is a Name server command,
2498 2498 * then it is 'fp' responsibility to perform the login.
2499 2499 */
2500 2500 ASSERT(job->job_arg != NULL);
2501 2501 ct_header =
2502 2502 (fc_ct_header_t *)((fcio_t *)job->job_arg)->fcio_ibuf;
2503 2503 if (ct_header->ct_fcstype == FCSTYPE_MGMTSERVICE) {
2504 2504 mutex_enter(&fcsm->sm_mutex);
2505 2505 fcsm->sm_flags &= ~FCSM_MGMT_SERVER_LOGGED_IN;
2506 2506 mutex_exit(&fcsm->sm_mutex);
2507 2507 }
2508 2508
2509 2509 if (job->job_retry_count >= fcsm_max_job_retries) {
2510 2510 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2511 2511 "retry_job: job 0x%p max retries (%d) reached",
2512 2512 (void *)job, job->job_retry_count));
2513 2513 break;
2514 2514 }
2515 2515
2516 2516 /*
2517 2517 * Login is required again. Retry the command, so that
2518 2518 * login will get performed again.
2519 2519 */
2520 2520 mutex_enter(&job->job_mutex);
2521 2521 job->job_retry_count++;
2522 2522 jobflag = job->job_flags;
2523 2523 mutex_exit(&job->job_mutex);
2524 2524
2525 2525 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2526 2526 "retry_job: retry(%d) job 0x%p",
2527 2527 job->job_retry_count, (void *)job));
2528 2528 /*
2529 2529 * This job should get picked up before the
2530 2530 * other jobs sitting in the queue.
2531 2531 * Requeue the command at the head and then
2532 2532 * reset the SERIALIZE flag.
2533 2533 */
2534 2534 fcsm_enque_job(fcsm, job, 1);
2535 2535 if (jobflag & FCSM_JOBFLAG_SERIALIZE) {
2536 2536 mutex_enter(&fcsm->sm_mutex);
2537 2537 ASSERT(fcsm->sm_flags & FCSM_SERIALIZE_JOBTHREAD);
2538 2538 fcsm->sm_flags &= ~FCSM_SERIALIZE_JOBTHREAD;
2539 2539
2540 2540 /* Signal the job thread to process the job */
2541 2541 cv_signal(&fcsm->sm_job_cv);
2542 2542 mutex_exit(&fcsm->sm_mutex);
2543 2543 }
2544 2544
2545 2545 /* Command is queued for retrying */
2546 2546 return (0);
2547 2547 }
2548 2548
2549 2549 default:
2550 2550 break;
2551 2551 }
2552 2552 return (1);
2553 2553 }
2554 2554
2555 2555 static void
2556 2556 fcsm_jobdone(fcsm_job_t *job)
2557 2557 {
2558 2558 fcsm_t *fcsm;
2559 2559
2560 2560 fcsm = ddi_get_soft_state(fcsm_state, job->job_port_instance);
2561 2561 ASSERT(fcsm != NULL);
2562 2562
2563 2563 if (job->job_result != FC_SUCCESS) {
2564 2564 if (fcsm_retry_job(fcsm, job) == 0) {
2565 2565 /* Job retried. so just return from here */
2566 2566 return;
2567 2567 }
2568 2568 }
2569 2569
2570 2570 if (job->job_comp) {
2571 2571 job->job_comp(job->job_comp_arg, job, job->job_result);
2572 2572 }
2573 2573
2574 2574 mutex_enter(&job->job_mutex);
2575 2575 if (job->job_flags & FCSM_JOBFLAG_SERIALIZE) {
2576 2576 mutex_exit(&job->job_mutex);
2577 2577 mutex_enter(&fcsm->sm_mutex);
2578 2578 ASSERT(fcsm->sm_flags & FCSM_SERIALIZE_JOBTHREAD);
2579 2579 fcsm->sm_flags &= ~FCSM_SERIALIZE_JOBTHREAD;
2580 2580
2581 2581 /* Signal the job thread to process the job */
2582 2582 cv_signal(&fcsm->sm_job_cv);
2583 2583 mutex_exit(&fcsm->sm_mutex);
2584 2584 mutex_enter(&job->job_mutex);
2585 2585 }
2586 2586
2587 2587 if (job->job_flags & FCSM_JOBFLAG_SYNC) {
2588 2588 mutex_exit(&job->job_mutex);
2589 2589 sema_v(&job->job_sema);
2590 2590 } else {
2591 2591 mutex_exit(&job->job_mutex);
2592 2592 /* Async job, free the job structure */
2593 2593 fcsm_dealloc_job(job);
2594 2594 }
2595 2595 }
2596 2596
2597 2597 fcsm_job_t *
2598 2598 fcsm_deque_job(fcsm_t *fcsm)
2599 2599 {
2600 2600 fcsm_job_t *job;
2601 2601
2602 2602 ASSERT(MUTEX_HELD(&fcsm->sm_mutex));
2603 2603
2604 2604 if (fcsm->sm_job_head == NULL) {
2605 2605 ASSERT(fcsm->sm_job_tail == NULL);
2606 2606 job = NULL;
2607 2607 } else {
2608 2608 ASSERT(fcsm->sm_job_tail != NULL);
2609 2609 job = fcsm->sm_job_head;
2610 2610 if (job->job_next == NULL) {
2611 2611 ASSERT(fcsm->sm_job_tail == job);
2612 2612 fcsm->sm_job_tail = NULL;
2613 2613 }
2614 2614 fcsm->sm_job_head = job->job_next;
2615 2615 job->job_next = NULL;
2616 2616 }
2617 2617
2618 2618 return (job);
↓ open down ↓ |
2618 lines elided |
↑ open up ↑ |
2619 2619 }
2620 2620
2621 2621
2622 2622 /* Dedicated per port thread to process various commands */
2623 2623 static void
2624 2624 fcsm_job_thread(fcsm_t *fcsm)
2625 2625 {
2626 2626 fcsm_job_t *job;
2627 2627
2628 2628 ASSERT(fcsm != NULL);
2629 -#ifndef __lock_lint
2630 2629 CALLB_CPR_INIT(&fcsm->sm_cpr_info, &fcsm->sm_mutex,
2631 2630 callb_generic_cpr, "fcsm_job_thread");
2632 -#endif /* __lock_lint */
2633 2631
2634 2632 for (;;) {
2635 2633 mutex_enter(&fcsm->sm_mutex);
2636 2634
2637 2635 while (fcsm->sm_job_head == NULL ||
2638 2636 fcsm->sm_flags & FCSM_SERIALIZE_JOBTHREAD) {
2639 2637 CALLB_CPR_SAFE_BEGIN(&fcsm->sm_cpr_info);
2640 2638 cv_wait(&fcsm->sm_job_cv, &fcsm->sm_mutex);
2641 2639 CALLB_CPR_SAFE_END(&fcsm->sm_cpr_info, &fcsm->sm_mutex);
2642 2640 }
2643 2641
2644 2642 job = fcsm_deque_job(fcsm);
2645 2643
2646 2644 mutex_exit(&fcsm->sm_mutex);
2647 2645
2648 2646 mutex_enter(&job->job_mutex);
2649 2647 if (job->job_flags & FCSM_JOBFLAG_SERIALIZE) {
2650 2648 mutex_exit(&job->job_mutex);
2651 2649
2652 2650 mutex_enter(&fcsm->sm_mutex);
2653 2651 ASSERT(!(fcsm->sm_flags & FCSM_SERIALIZE_JOBTHREAD));
2654 2652 fcsm->sm_flags |= FCSM_SERIALIZE_JOBTHREAD;
2655 2653 mutex_exit(&fcsm->sm_mutex);
2656 2654 } else {
2657 2655 mutex_exit(&job->job_mutex);
2658 2656 }
2659 2657
2660 2658 ASSERT(fcsm->sm_instance == job->job_port_instance);
2661 2659
2662 2660 switch (job->job_code) {
2663 2661 case FCSM_JOB_NONE:
2664 2662 fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
2665 2663 "job_thread: uninitialized job code");
2666 2664 job->job_result = FC_FAILURE;
2667 2665 fcsm_jobdone(job);
2668 2666 break;
2669 2667
2670 2668 case FCSM_JOB_THREAD_SHUTDOWN:
2671 2669 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2672 2670 "job_thread: job code <JOB PORT SHUTDOWN>"));
2673 2671
↓ open down ↓ |
31 lines elided |
↑ open up ↑ |
2674 2672 /*
2675 2673 * There should not be any pending jobs, when this
2676 2674 * is being called.
2677 2675 */
2678 2676 mutex_enter(&fcsm->sm_mutex);
2679 2677 ASSERT(fcsm->sm_job_head == NULL);
2680 2678 ASSERT(fcsm->sm_job_tail == NULL);
2681 2679 ASSERT(fcsm->sm_retry_head == NULL);
2682 2680 ASSERT(fcsm->sm_retry_tail == NULL);
2683 2681 job->job_result = FC_SUCCESS;
2684 -#ifndef __lock_lint
2685 2682 CALLB_CPR_EXIT(&fcsm->sm_cpr_info);
2686 -#endif
2687 2683 /* CPR_EXIT has also dropped the fcsm->sm_mutex */
2688 2684
2689 2685 fcsm_jobdone(job);
2690 2686 thread_exit();
2691 2687 /* NOTREACHED */
2692 2688 break;
2693 2689
2694 2690 case FCSM_JOB_LOGIN_NAME_SERVER:
2695 2691 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2696 2692 "job_thread: job code <LOGIN_NAME_SERVER>"));
2697 2693 job->job_result = FC_SUCCESS;
2698 2694 fcsm_jobdone(job);
2699 2695 break;
2700 2696
2701 2697 case FCSM_JOB_LOGIN_MGMT_SERVER:
2702 2698 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2703 2699 "job_thread: job code <LOGIN_MGMT_SERVER>"));
2704 2700 fcsm_job_login_mgmt_server(job);
2705 2701 break;
2706 2702
2707 2703 case FCSM_JOB_CT_PASSTHRU:
2708 2704 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2709 2705 "job_thread: job code <CT_PASSTHRU>"));
2710 2706 fcsm_job_ct_passthru(job);
2711 2707 break;
2712 2708
2713 2709 default:
2714 2710 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2715 2711 "job_thread: job code <UNKNOWN>"));
2716 2712 job->job_result = FC_FAILURE;
2717 2713 fcsm_jobdone(job);
2718 2714 break;
2719 2715 }
2720 2716 }
2721 2717
2722 2718 /* NOTREACHED */
2723 2719 }
2724 2720
2725 2721
2726 2722 static void
2727 2723 fcsm_ct_init(fcsm_t *fcsm, fcsm_cmd_t *cmd, fc_ct_aiu_t *req_iu, size_t req_len,
2728 2724 void (*comp_func)())
2729 2725 {
2730 2726 fc_packet_t *pkt;
2731 2727
2732 2728 pkt = cmd->cmd_fp_pkt;
2733 2729 ASSERT(pkt != NULL);
2734 2730
2735 2731 ASSERT(req_iu->aiu_header.ct_fcstype == FCSTYPE_MGMTSERVICE ||
2736 2732 (req_iu->aiu_header.ct_fcstype == FCSTYPE_DIRECTORY &&
2737 2733 req_iu->aiu_header.ct_fcssubtype == FCSSUB_DS_NAME_SERVER));
2738 2734
2739 2735
2740 2736 /* Set the pkt d_id properly */
2741 2737 if (req_iu->aiu_header.ct_fcstype == FCSTYPE_MGMTSERVICE) {
2742 2738 pkt->pkt_cmd_fhdr.d_id = FS_MANAGEMENT_SERVER;
2743 2739 } else {
2744 2740 pkt->pkt_cmd_fhdr.d_id = FS_NAME_SERVER;
2745 2741 }
2746 2742
2747 2743 pkt->pkt_cmd_fhdr.r_ctl = R_CTL_UNSOL_CONTROL;
2748 2744 pkt->pkt_cmd_fhdr.rsvd = 0;
2749 2745 pkt->pkt_cmd_fhdr.s_id = fcsm->sm_sid;
2750 2746 pkt->pkt_cmd_fhdr.type = FC_TYPE_FC_SERVICES;
2751 2747 pkt->pkt_cmd_fhdr.f_ctl = F_CTL_SEQ_INITIATIVE |
2752 2748 F_CTL_FIRST_SEQ | F_CTL_END_SEQ;
2753 2749 pkt->pkt_cmd_fhdr.seq_id = 0;
2754 2750 pkt->pkt_cmd_fhdr.df_ctl = 0;
2755 2751 pkt->pkt_cmd_fhdr.seq_cnt = 0;
2756 2752 pkt->pkt_cmd_fhdr.ox_id = 0xffff;
2757 2753 pkt->pkt_cmd_fhdr.rx_id = 0xffff;
2758 2754 pkt->pkt_cmd_fhdr.ro = 0;
2759 2755
2760 2756 pkt->pkt_timeout = FCSM_MS_TIMEOUT;
2761 2757 pkt->pkt_comp = comp_func;
2762 2758
2763 2759 FCSM_REP_WR(pkt->pkt_cmd_acc, req_iu, pkt->pkt_cmd, req_len);
2764 2760
2765 2761 cmd->cmd_transport = fc_ulp_transport;
2766 2762 }
2767 2763
2768 2764 static void
2769 2765 fcsm_ct_intr(fcsm_cmd_t *cmd)
2770 2766 {
2771 2767 fc_packet_t *pkt;
2772 2768 fcsm_job_t *job;
2773 2769 fcio_t *fcio;
2774 2770 fcsm_t *fcsm;
2775 2771
2776 2772 pkt = cmd->cmd_fp_pkt;
2777 2773 job = cmd->cmd_job;
2778 2774 ASSERT(job != NULL);
2779 2775
2780 2776 fcio = job->job_arg;
2781 2777 ASSERT(fcio != NULL);
2782 2778
2783 2779 if (pkt->pkt_state != FC_PKT_SUCCESS) {
2784 2780 FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, cmd->cmd_fcsm, pkt,
2785 2781 "ct_intr: CT command <0x%x> to did 0x%x failed",
2786 2782 ((fc_ct_aiu_t *)fcio->fcio_ibuf)->aiu_header.ct_cmdrsp,
2787 2783 pkt->pkt_cmd_fhdr.d_id));
2788 2784 } else {
2789 2785 /* Get the CT response payload */
2790 2786 fcsm = cmd->cmd_fcsm;
2791 2787 FCSM_REP_RD(pkt->pkt_resp_acc, fcio->fcio_obuf,
2792 2788 pkt->pkt_resp, fcio->fcio_olen);
2793 2789 }
2794 2790
2795 2791 job->job_result =
2796 2792 fcsm_pkt_state_to_rval(pkt->pkt_state, pkt->pkt_reason);
2797 2793
2798 2794 fcsm_free_cmd(cmd);
2799 2795
2800 2796 fcsm_jobdone(job);
2801 2797 }
2802 2798
2803 2799
2804 2800 static void
2805 2801 fcsm_job_ct_passthru(fcsm_job_t *job)
2806 2802 {
2807 2803 fcsm_t *fcsm;
2808 2804 fcio_t *fcio;
2809 2805 fcsm_cmd_t *cmd;
2810 2806 int status;
2811 2807 fc_ct_header_t *ct_header;
2812 2808
2813 2809 ASSERT(job != NULL);
2814 2810 ASSERT(job->job_port_instance != -1);
2815 2811
2816 2812 job->job_result = FC_FAILURE;
2817 2813 fcsm = ddi_get_soft_state(fcsm_state, job->job_port_instance);
2818 2814 if (fcsm == NULL) {
2819 2815 fcsm_jobdone(job);
2820 2816 return;
2821 2817 }
2822 2818
2823 2819 /*
2824 2820 * Process the CT Passthru job only if port is attached
2825 2821 * to a FABRIC.
2826 2822 */
2827 2823 if (!FC_TOP_EXTERNAL(fcsm->sm_port_top)) {
2828 2824 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2829 2825 "job_ct_passthru: end (non-fabric port)"));
2830 2826 job->job_result = FC_BADDEV;
2831 2827 fcsm_jobdone(job);
2832 2828 return;
2833 2829 }
2834 2830
2835 2831 fcio = job->job_arg;
2836 2832 ASSERT(fcio != NULL);
2837 2833
2838 2834 /*
2839 2835 * If it is NOT a Management Seriver (MS) or Name Server (NS) command
2840 2836 * then complete the command with failure.
2841 2837 */
2842 2838 ct_header = (fc_ct_header_t *)fcio->fcio_ibuf;
2843 2839
2844 2840 /*
2845 2841 * According to libHBAAPI spec, CT header from libHBAAPI would always
2846 2842 * be big endian, so we must swap CT header before continue in little
2847 2843 * endian platforms.
2848 2844 */
2849 2845 mutex_enter(&job->job_mutex);
2850 2846 if (!(job->job_flags & FCSM_JOBFLAG_CTHEADER_BE)) {
2851 2847 job->job_flags |= FCSM_JOBFLAG_CTHEADER_BE;
2852 2848 *((uint32_t *)((uint32_t *)ct_header + 0)) =
2853 2849 BE_32(*((uint32_t *)((uint32_t *)ct_header + 0)));
2854 2850 *((uint32_t *)((uint32_t *)ct_header + 1)) =
2855 2851 BE_32(*((uint32_t *)((uint32_t *)ct_header + 1)));
2856 2852 *((uint32_t *)((uint32_t *)ct_header + 2)) =
2857 2853 BE_32(*((uint32_t *)((uint32_t *)ct_header + 2)));
2858 2854 *((uint32_t *)((uint32_t *)ct_header + 3)) =
2859 2855 BE_32(*((uint32_t *)((uint32_t *)ct_header + 3)));
2860 2856 }
2861 2857 mutex_exit(&job->job_mutex);
2862 2858
2863 2859 if (ct_header->ct_fcstype == FCSTYPE_MGMTSERVICE) {
2864 2860 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2865 2861 "job_ct_passthru: Management Server Cmd"));
2866 2862 } else if (ct_header->ct_fcstype == FCSTYPE_DIRECTORY) {
2867 2863 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2868 2864 "job_ct_passthru: Name Server Cmd"));
2869 2865 } else {
2870 2866 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2871 2867 "job_ct_passthru: Unsupported Destination "
2872 2868 "gs_type <0x%x> gs_subtype <0x%x>",
2873 2869 ct_header->ct_fcstype, ct_header->ct_fcssubtype));
2874 2870 }
2875 2871
2876 2872 if (ct_header->ct_fcstype != FCSTYPE_MGMTSERVICE &&
2877 2873 (ct_header->ct_fcstype != FCSTYPE_DIRECTORY ||
2878 2874 ct_header->ct_fcssubtype != FCSSUB_DS_NAME_SERVER)) {
2879 2875 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2880 2876 "job_ct_passthru: end (Not a Name Server OR "
2881 2877 "Mgmt Server Cmd)"));
2882 2878 job->job_result = FC_BADCMD;
2883 2879 fcsm_jobdone(job);
2884 2880 return;
2885 2881 }
2886 2882
2887 2883 /*
2888 2884 * If it is an MS command and we are not logged in to the management
2889 2885 * server, then start the login and requeue the command.
2890 2886 * If login to management server is in progress, then reque the
2891 2887 * command to wait for login to complete.
2892 2888 */
2893 2889 mutex_enter(&fcsm->sm_mutex);
2894 2890 if ((ct_header->ct_fcstype == FCSTYPE_MGMTSERVICE) &&
2895 2891 !(fcsm->sm_flags & FCSM_MGMT_SERVER_LOGGED_IN)) {
2896 2892 mutex_exit(&fcsm->sm_mutex);
2897 2893 if (fcsm_login_and_process_job(fcsm, job) != FC_SUCCESS) {
2898 2894 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2899 2895 "job_ct_passthru: perform login failed"));
2900 2896 job->job_result = FC_FAILURE;
2901 2897 fcsm_jobdone(job);
2902 2898 }
2903 2899 return;
2904 2900 }
2905 2901 mutex_exit(&fcsm->sm_mutex);
2906 2902
2907 2903 /*
2908 2904 * We are already logged in to the management server.
2909 2905 * Issue the CT Passthru command
2910 2906 */
2911 2907 cmd = fcsm_alloc_cmd(fcsm, fcio->fcio_ilen, fcio->fcio_olen, KM_SLEEP);
2912 2908 if (cmd == NULL) {
2913 2909 job->job_result = FC_NOMEM;
2914 2910 fcsm_jobdone(job);
2915 2911 return;
2916 2912 }
2917 2913
2918 2914 FCSM_INIT_CMD(cmd, job, FC_TRAN_INTR | FC_TRAN_CLASS3, FC_PKT_EXCHANGE,
2919 2915 fcsm_max_cmd_retries, fcsm_ct_intr);
2920 2916
2921 2917 fcsm_ct_init(fcsm, cmd, (fc_ct_aiu_t *)fcio->fcio_ibuf, fcio->fcio_ilen,
2922 2918 fcsm_pkt_common_intr);
2923 2919
2924 2920 if ((status = fcsm_issue_cmd(cmd)) != FC_SUCCESS) {
2925 2921 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, cmd->cmd_fcsm, NULL,
2926 2922 "job_ct_passthru: issue CT Passthru failed, status 0x%x",
2927 2923 status));
2928 2924 job->job_result = status;
2929 2925 fcsm_free_cmd(cmd);
2930 2926 fcsm_jobdone(job);
2931 2927 return;
2932 2928 }
2933 2929 }
2934 2930
2935 2931 static int
2936 2932 fcsm_login_and_process_job(fcsm_t *fcsm, fcsm_job_t *orig_job)
2937 2933 {
2938 2934 fcsm_job_t *login_job;
2939 2935 #ifdef DEBUG
2940 2936 int status;
2941 2937 #endif /* DEBUG */
2942 2938
2943 2939 if (orig_job->job_code != FCSM_JOB_CT_PASSTHRU) {
2944 2940 return (FC_FAILURE);
2945 2941 }
2946 2942
2947 2943 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2948 2944 "login_and_process_job: start login."));
2949 2945
2950 2946 mutex_enter(&fcsm->sm_mutex);
2951 2947 if (fcsm->sm_flags & FCSM_MGMT_SERVER_LOGGED_IN) {
2952 2948 /*
2953 2949 * Directory server login completed just now, while the
2954 2950 * mutex was dropped. Just queue the command again for
2955 2951 * processing.
2956 2952 */
2957 2953 mutex_exit(&fcsm->sm_mutex);
2958 2954 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2959 2955 "login_and_process_job: got job 0x%p. login just "
2960 2956 "completed", (void *)orig_job));
2961 2957 fcsm_enque_job(fcsm, orig_job, 0);
2962 2958 return (FC_SUCCESS);
2963 2959 }
2964 2960
2965 2961 if (fcsm->sm_flags & FCSM_MGMT_SERVER_LOGIN_IN_PROG) {
2966 2962 /*
2967 2963 * Ideally we shouldn't have come here, since login
2968 2964 * job has the serialize flag set.
2969 2965 * Anyway, put the command back on the queue.
2970 2966 */
2971 2967 mutex_exit(&fcsm->sm_mutex);
2972 2968 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2973 2969 "login_and_process_job: got job 0x%p while login to "
2974 2970 "management server in progress", (void *)orig_job));
2975 2971 fcsm_enque_job(fcsm, orig_job, 0);
2976 2972 return (FC_SUCCESS);
2977 2973 }
2978 2974
2979 2975 fcsm->sm_flags |= FCSM_MGMT_SERVER_LOGIN_IN_PROG;
2980 2976 mutex_exit(&fcsm->sm_mutex);
2981 2977
2982 2978 login_job = fcsm_alloc_job(KM_SLEEP);
2983 2979 ASSERT(login_job != NULL);
2984 2980
2985 2981 /*
2986 2982 * Mark the login job as SERIALIZE, so that all other jobs will
2987 2983 * be processed after completing the login.
2988 2984 * Save the original job (CT Passthru job) in the caller private
2989 2985 * field in the job structure, so that CT command can be issued
2990 2986 * after login has completed.
2991 2987 */
2992 2988 fcsm_init_job(login_job, fcsm->sm_instance, FCSM_JOB_LOGIN_MGMT_SERVER,
2993 2989 FCSM_JOBFLAG_ASYNC | FCSM_JOBFLAG_SERIALIZE,
2994 2990 (opaque_t)NULL, (opaque_t)orig_job, fcsm_login_ms_comp, NULL);
2995 2991 orig_job->job_priv = (void *)login_job;
2996 2992
2997 2993 #ifdef DEBUG
2998 2994 status = fcsm_process_job(login_job, 1);
2999 2995 ASSERT(status == FC_SUCCESS);
3000 2996 #else /* DEBUG */
3001 2997 (void) fcsm_process_job(login_job, 1);
3002 2998 #endif /* DEBUG */
3003 2999 return (FC_SUCCESS);
3004 3000 }
3005 3001
3006 3002
3007 3003 /* ARGSUSED */
3008 3004 static void
3009 3005 fcsm_login_ms_comp(opaque_t comp_arg, fcsm_job_t *login_job, int result)
3010 3006 {
3011 3007 fcsm_t *fcsm;
3012 3008 fcsm_job_t *orig_job;
3013 3009
3014 3010 ASSERT(login_job != NULL);
3015 3011
3016 3012 orig_job = (fcsm_job_t *)login_job->job_caller_priv;
3017 3013
3018 3014 ASSERT(orig_job != NULL);
3019 3015 ASSERT(orig_job->job_priv == (void *)login_job);
3020 3016 orig_job->job_priv = NULL;
3021 3017
3022 3018 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
3023 3019 "login_ms_comp: result 0x%x", login_job->job_result));
3024 3020
3025 3021 /* Set the login flag in the per port fcsm structure */
3026 3022 ASSERT(login_job->job_port_instance == orig_job->job_port_instance);
3027 3023 fcsm = ddi_get_soft_state(fcsm_state, login_job->job_port_instance);
3028 3024 ASSERT(fcsm != NULL);
3029 3025
3030 3026 mutex_enter(&fcsm->sm_mutex);
3031 3027 ASSERT((fcsm->sm_flags & FCSM_MGMT_SERVER_LOGGED_IN) == 0);
3032 3028 ASSERT(fcsm->sm_flags & FCSM_MGMT_SERVER_LOGIN_IN_PROG);
3033 3029 fcsm->sm_flags &= ~FCSM_MGMT_SERVER_LOGIN_IN_PROG;
3034 3030 if (login_job->job_result != FC_SUCCESS) {
3035 3031 caddr_t msg;
3036 3032
3037 3033 /*
3038 3034 * Login failed. Complete the original job with FC_LOGINREQ
3039 3035 * status. Retry of that job will cause login to be
3040 3036 * retried.
3041 3037 */
3042 3038 mutex_exit(&fcsm->sm_mutex);
3043 3039 orig_job->job_result = FC_LOGINREQ;
3044 3040 fcsm_jobdone(orig_job);
3045 3041
3046 3042 (void) fc_ulp_error(login_job->job_result, &msg);
3047 3043 fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
3048 3044 "login_ms_comp: Management server login failed: <%s>", msg);
3049 3045 return;
3050 3046 }
3051 3047 fcsm->sm_flags |= FCSM_MGMT_SERVER_LOGGED_IN;
3052 3048 mutex_exit(&fcsm->sm_mutex);
3053 3049
3054 3050 /*
3055 3051 * Queue the original job at the head of the queue for processing.
3056 3052 */
3057 3053 fcsm_enque_job(fcsm, orig_job, 1);
3058 3054 }
3059 3055
3060 3056
3061 3057 static void
3062 3058 fcsm_els_init(fcsm_cmd_t *cmd, uint32_t d_id)
3063 3059 {
3064 3060 fc_packet_t *pkt;
3065 3061 fcsm_t *fcsm;
3066 3062
3067 3063 fcsm = cmd->cmd_fcsm;
3068 3064 pkt = cmd->cmd_fp_pkt;
3069 3065 ASSERT(fcsm != NULL && pkt != NULL);
3070 3066
3071 3067 pkt->pkt_cmd_fhdr.r_ctl = R_CTL_ELS_REQ;
3072 3068 pkt->pkt_cmd_fhdr.d_id = d_id;
3073 3069 pkt->pkt_cmd_fhdr.rsvd = 0;
3074 3070 pkt->pkt_cmd_fhdr.s_id = fcsm->sm_sid;
3075 3071 pkt->pkt_cmd_fhdr.type = FC_TYPE_EXTENDED_LS;
3076 3072 pkt->pkt_cmd_fhdr.f_ctl = F_CTL_SEQ_INITIATIVE | F_CTL_FIRST_SEQ;
3077 3073 pkt->pkt_cmd_fhdr.seq_id = 0;
3078 3074 pkt->pkt_cmd_fhdr.df_ctl = 0;
3079 3075 pkt->pkt_cmd_fhdr.seq_cnt = 0;
3080 3076 pkt->pkt_cmd_fhdr.ox_id = 0xffff;
3081 3077 pkt->pkt_cmd_fhdr.rx_id = 0xffff;
3082 3078 pkt->pkt_cmd_fhdr.ro = 0;
3083 3079
3084 3080 pkt->pkt_timeout = FCSM_ELS_TIMEOUT;
3085 3081 }
3086 3082
3087 3083
3088 3084 static int
3089 3085 fcsm_xlogi_init(fcsm_t *fcsm, fcsm_cmd_t *cmd, uint32_t d_id,
3090 3086 void (*comp_func)(), uchar_t ls_code)
3091 3087 {
3092 3088 ls_code_t payload;
3093 3089 fc_packet_t *pkt;
3094 3090 la_els_logi_t *login_params;
3095 3091 int status;
3096 3092
3097 3093 login_params = (la_els_logi_t *)
3098 3094 kmem_zalloc(sizeof (la_els_logi_t), KM_SLEEP);
3099 3095 if (login_params == NULL) {
3100 3096 return (FC_NOMEM);
3101 3097 }
3102 3098
3103 3099 status = fc_ulp_get_port_login_params(fcsm->sm_port_info.port_handle,
3104 3100 login_params);
3105 3101 if (status != FC_SUCCESS) {
3106 3102 kmem_free(login_params, sizeof (la_els_logi_t));
3107 3103 return (status);
3108 3104 }
3109 3105
3110 3106 pkt = cmd->cmd_fp_pkt;
3111 3107
3112 3108 fcsm_els_init(cmd, d_id);
3113 3109 pkt->pkt_comp = comp_func;
3114 3110
3115 3111 payload.ls_code = ls_code;
3116 3112 payload.mbz = 0;
3117 3113
3118 3114 FCSM_REP_WR(pkt->pkt_cmd_acc, login_params,
3119 3115 pkt->pkt_cmd, sizeof (la_els_logi_t));
3120 3116 FCSM_REP_WR(pkt->pkt_cmd_acc, &payload,
3121 3117 pkt->pkt_cmd, sizeof (payload));
3122 3118
3123 3119 cmd->cmd_transport = fc_ulp_issue_els;
3124 3120
3125 3121 kmem_free(login_params, sizeof (la_els_logi_t));
3126 3122
3127 3123 return (FC_SUCCESS);
3128 3124 }
3129 3125
3130 3126 static void
3131 3127 fcsm_xlogi_intr(fcsm_cmd_t *cmd)
3132 3128 {
3133 3129 fc_packet_t *pkt;
3134 3130 fcsm_job_t *job;
3135 3131 fcsm_t *fcsm;
3136 3132
3137 3133 pkt = cmd->cmd_fp_pkt;
3138 3134 job = cmd->cmd_job;
3139 3135 ASSERT(job != NULL);
3140 3136
3141 3137 fcsm = cmd->cmd_fcsm;
3142 3138 ASSERT(fcsm != NULL);
3143 3139
3144 3140 if (pkt->pkt_state != FC_PKT_SUCCESS) {
3145 3141 fcsm_display(CE_WARN, SM_LOG, fcsm, pkt,
3146 3142 "xlogi_intr: login to DID 0x%x failed",
3147 3143 pkt->pkt_cmd_fhdr.d_id);
3148 3144 } else {
3149 3145 /* Get the Login parameters of the Management Server */
3150 3146 FCSM_REP_RD(pkt->pkt_resp_acc, &fcsm->sm_ms_service_params,
3151 3147 pkt->pkt_resp, sizeof (la_els_logi_t));
3152 3148 }
3153 3149
3154 3150 job->job_result =
3155 3151 fcsm_pkt_state_to_rval(pkt->pkt_state, pkt->pkt_reason);
3156 3152
3157 3153 fcsm_free_cmd(cmd);
3158 3154
3159 3155 fcsm_jobdone(job);
3160 3156 }
3161 3157
3162 3158 static void
3163 3159 fcsm_job_login_mgmt_server(fcsm_job_t *job)
3164 3160 {
3165 3161 fcsm_t *fcsm;
3166 3162 fcsm_cmd_t *cmd;
3167 3163 int status;
3168 3164
3169 3165 ASSERT(job != NULL);
3170 3166 ASSERT(job->job_port_instance != -1);
3171 3167
3172 3168 fcsm = ddi_get_soft_state(fcsm_state, job->job_port_instance);
3173 3169 if (fcsm == NULL) {
3174 3170 job->job_result = FC_NOMEM;
3175 3171 fcsm_jobdone(job);
3176 3172 return;
3177 3173 }
3178 3174
3179 3175 /*
3180 3176 * Issue the Login command to the management server.
3181 3177 */
3182 3178 cmd = fcsm_alloc_cmd(fcsm, sizeof (la_els_logi_t),
3183 3179 sizeof (la_els_logi_t), KM_SLEEP);
3184 3180 if (cmd == NULL) {
3185 3181 job->job_result = FC_NOMEM;
3186 3182 fcsm_jobdone(job);
3187 3183 return;
3188 3184 }
3189 3185
3190 3186 FCSM_INIT_CMD(cmd, job, FC_TRAN_INTR | FC_TRAN_CLASS3, FC_PKT_EXCHANGE,
3191 3187 fcsm_max_cmd_retries, fcsm_xlogi_intr);
3192 3188
3193 3189 status = fcsm_xlogi_init(fcsm, cmd, FS_MANAGEMENT_SERVER,
3194 3190 fcsm_pkt_common_intr, LA_ELS_PLOGI);
3195 3191
3196 3192 if (status != FC_SUCCESS) {
3197 3193 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
3198 3194 "job_login_mgmt_server: plogi init failed. status 0x%x",
3199 3195 status));
3200 3196 job->job_result = status;
3201 3197 fcsm_free_cmd(cmd);
3202 3198 fcsm_jobdone(job);
3203 3199 return;
3204 3200 }
3205 3201
3206 3202 if ((status = fcsm_issue_cmd(cmd)) != FC_SUCCESS) {
3207 3203 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, cmd->cmd_fcsm, NULL,
3208 3204 "job_ct_passthru: issue login cmd failed, status 0x%x",
3209 3205 status));
3210 3206 job->job_result = status;
3211 3207 fcsm_free_cmd(cmd);
3212 3208 fcsm_jobdone(job);
3213 3209 return;
3214 3210 }
3215 3211 }
3216 3212
3217 3213
3218 3214 int
3219 3215 fcsm_ct_passthru(int instance, fcio_t *fcio, int sleep, int job_flags,
3220 3216 void (*func)(fcio_t *))
3221 3217 {
3222 3218 fcsm_job_t *job;
3223 3219 int status;
3224 3220
3225 3221 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
3226 3222 "ct_passthru: instance 0x%x fcio 0x%p", instance, fcio));
3227 3223 job = fcsm_alloc_job(sleep);
3228 3224 ASSERT(sleep == KM_NOSLEEP || job != NULL);
3229 3225
3230 3226 fcsm_init_job(job, instance, FCSM_JOB_CT_PASSTHRU, job_flags,
3231 3227 (opaque_t)fcio, (opaque_t)func, fcsm_ct_passthru_comp, NULL);
3232 3228 status = fcsm_process_job(job, 0);
3233 3229 if (status != FC_SUCCESS) {
3234 3230 /* Job could not be issued. So free the job and return */
3235 3231 fcsm_dealloc_job(job);
3236 3232 return (status);
3237 3233 }
3238 3234
3239 3235 if (job_flags & FCSM_JOBFLAG_SYNC) {
3240 3236 status = job->job_result;
3241 3237 fcsm_dealloc_job(job);
3242 3238 }
3243 3239
3244 3240 return (status);
3245 3241 }
3246 3242
3247 3243
3248 3244 /* ARGSUSED */
3249 3245 static void
3250 3246 fcsm_ct_passthru_comp(opaque_t comp_arg, fcsm_job_t *job, int result)
3251 3247 {
3252 3248 ASSERT(job != NULL);
3253 3249 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
3254 3250 "ct_passthru_comp: result 0x%x port 0x%x",
3255 3251 job->job_result, job->job_port_instance));
3256 3252 }
3257 3253
3258 3254
3259 3255 static void
3260 3256 fcsm_pkt_common_intr(fc_packet_t *pkt)
3261 3257 {
3262 3258 fcsm_cmd_t *cmd;
3263 3259 int jobstatus;
3264 3260 fcsm_t *fcsm;
3265 3261
3266 3262 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
3267 3263 "pkt_common_intr"));
3268 3264
3269 3265 cmd = (fcsm_cmd_t *)pkt->pkt_ulp_private;
3270 3266 ASSERT(cmd != NULL);
3271 3267
3272 3268 if (pkt->pkt_state == FC_PKT_SUCCESS) {
3273 3269 /* Command completed successfully. Just complete the command */
3274 3270 cmd->cmd_comp(cmd);
3275 3271 return;
3276 3272 }
3277 3273
3278 3274 fcsm = cmd->cmd_fcsm;
3279 3275 ASSERT(fcsm != NULL);
3280 3276
3281 3277 FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, cmd->cmd_fcsm, pkt,
3282 3278 "fc packet to DID 0x%x failed for pkt 0x%p",
3283 3279 pkt->pkt_cmd_fhdr.d_id, pkt));
3284 3280
3285 3281 mutex_enter(&fcsm->sm_mutex);
3286 3282 if (fcsm->sm_flags & FCSM_LINK_DOWN) {
3287 3283 /*
3288 3284 * No need to retry the command. The link previously
3289 3285 * suffered an offline timeout.
3290 3286 */
3291 3287 mutex_exit(&fcsm->sm_mutex);
3292 3288 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, cmd->cmd_fcsm, NULL,
3293 3289 "pkt_common_intr: end. Link is down"));
3294 3290 cmd->cmd_comp(cmd);
3295 3291 return;
3296 3292 }
3297 3293 mutex_exit(&fcsm->sm_mutex);
3298 3294
3299 3295 jobstatus = fcsm_pkt_state_to_rval(pkt->pkt_state, pkt->pkt_reason);
3300 3296 if (jobstatus == FC_LOGINREQ) {
3301 3297 /*
3302 3298 * Login to the destination is required. No need to
3303 3299 * retry this cmd again.
3304 3300 */
3305 3301 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, cmd->cmd_fcsm, NULL,
3306 3302 "pkt_common_intr: end. LOGIN required"));
3307 3303 cmd->cmd_comp(cmd);
3308 3304 return;
3309 3305 }
3310 3306
3311 3307 switch (pkt->pkt_state) {
3312 3308 case FC_PKT_PORT_OFFLINE:
3313 3309 case FC_PKT_LOCAL_RJT:
3314 3310 case FC_PKT_TIMEOUT: {
3315 3311 uchar_t pkt_state;
3316 3312
3317 3313 pkt_state = pkt->pkt_state;
3318 3314 cmd->cmd_retry_interval = fcsm_retry_interval;
3319 3315 if (fcsm_retry_cmd(cmd) != 0) {
3320 3316 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG,
3321 3317 cmd->cmd_fcsm, NULL,
3322 3318 "common_intr: max retries(%d) reached, status 0x%x",
3323 3319 cmd->cmd_retry_count));
3324 3320
3325 3321 /*
3326 3322 * Restore the pkt_state to the actual failure status
3327 3323 * received at the time of pkt completion.
3328 3324 */
3329 3325 pkt->pkt_state = pkt_state;
3330 3326 pkt->pkt_reason = 0;
3331 3327 cmd->cmd_comp(cmd);
3332 3328 } else {
3333 3329 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG,
3334 3330 cmd->cmd_fcsm, NULL,
3335 3331 "pkt_common_intr: retry(%d) on pkt state (0x%x)",
3336 3332 cmd->cmd_retry_count, pkt_state));
3337 3333 }
3338 3334 break;
3339 3335 }
3340 3336 default:
3341 3337 cmd->cmd_comp(cmd);
3342 3338 break;
3343 3339 }
3344 3340 }
3345 3341
3346 3342 static int
3347 3343 fcsm_issue_cmd(fcsm_cmd_t *cmd)
3348 3344 {
3349 3345 fc_packet_t *pkt;
3350 3346 fcsm_t *fcsm;
3351 3347 int status;
3352 3348
3353 3349 pkt = cmd->cmd_fp_pkt;
3354 3350 fcsm = cmd->cmd_fcsm;
3355 3351
3356 3352 /* Explicitly invalidate this field till fcsm decides to use it */
3357 3353 pkt->pkt_ulp_rscn_infop = NULL;
3358 3354
3359 3355 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3360 3356 "issue_cmd: entry"));
3361 3357
3362 3358 ASSERT(!MUTEX_HELD(&fcsm->sm_mutex));
3363 3359 mutex_enter(&fcsm->sm_mutex);
3364 3360 if (fcsm->sm_flags & FCSM_LINK_DOWN) {
3365 3361 /*
3366 3362 * Update the pkt_state/pkt_reason appropriately.
3367 3363 * Caller of this function can decide whether to call
3368 3364 * 'pkt->pkt_comp' or use the 'status' returned by this func.
3369 3365 */
3370 3366 mutex_exit(&fcsm->sm_mutex);
3371 3367 pkt->pkt_state = FC_PKT_PORT_OFFLINE;
3372 3368 pkt->pkt_reason = FC_REASON_OFFLINE;
3373 3369 return (FC_OFFLINE);
3374 3370 }
3375 3371 mutex_exit(&fcsm->sm_mutex);
3376 3372
3377 3373 ASSERT(cmd->cmd_transport != NULL);
3378 3374 status = cmd->cmd_transport(fcsm->sm_port_info.port_handle, pkt);
3379 3375 if (status != FC_SUCCESS) {
3380 3376 switch (status) {
3381 3377 case FC_LOGINREQ:
3382 3378 /*
3383 3379 * No need to retry. Return the cause of failure.
3384 3380 * Also update the pkt_state/pkt_reason. Caller of
3385 3381 * this function can decide, whether to call
3386 3382 * 'pkt->pkt_comp' or use the 'status' code returned
3387 3383 * by this function.
3388 3384 */
3389 3385 pkt->pkt_state = FC_PKT_LOCAL_RJT;
3390 3386 pkt->pkt_reason = FC_REASON_LOGIN_REQUIRED;
3391 3387 break;
3392 3388
3393 3389 case FC_DEVICE_BUSY_NEW_RSCN:
3394 3390 /*
3395 3391 * There was a newer RSCN than what fcsm knows about.
3396 3392 * So, just retry again
3397 3393 */
3398 3394 cmd->cmd_retry_count = 0;
3399 3395 /*FALLTHROUGH*/
3400 3396 case FC_OFFLINE:
3401 3397 case FC_STATEC_BUSY:
3402 3398 /*
3403 3399 * TODO: set flag, so that command is retried after
3404 3400 * port is back online.
3405 3401 * FALL Through for now.
3406 3402 */
3407 3403
3408 3404 case FC_TRAN_BUSY:
3409 3405 case FC_NOMEM:
3410 3406 case FC_DEVICE_BUSY:
3411 3407 cmd->cmd_retry_interval = fcsm_retry_interval;
3412 3408 if (fcsm_retry_cmd(cmd) != 0) {
3413 3409 FCSM_DEBUG(SMDL_TRACE,
3414 3410 (CE_WARN, SM_LOG, fcsm, NULL,
3415 3411 "issue_cmd: max retries (%d) reached",
3416 3412 cmd->cmd_retry_count));
3417 3413
3418 3414 /*
3419 3415 * status variable is not changed here.
3420 3416 * Return the cause of the original
3421 3417 * cmd_transport failure.
3422 3418 * Update the pkt_state/pkt_reason. Caller
3423 3419 * of this function can decide whether to
3424 3420 * call 'pkt->pkt_comp' or use the 'status'
3425 3421 * code returned by this function.
3426 3422 */
3427 3423 pkt->pkt_state = FC_PKT_TRAN_BSY;
3428 3424 pkt->pkt_reason = 0;
3429 3425 } else {
3430 3426 FCSM_DEBUG(SMDL_TRACE,
3431 3427 (CE_WARN, SM_LOG, fcsm, NULL,
3432 3428 "issue_cmd: retry (%d) on fc status (0x%x)",
3433 3429 cmd->cmd_retry_count, status));
3434 3430
3435 3431 status = FC_SUCCESS;
3436 3432 }
3437 3433 break;
3438 3434
3439 3435 default:
3440 3436 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
3441 3437 "issue_cmd: failure status 0x%x", status));
3442 3438
3443 3439 pkt->pkt_state = FC_PKT_TRAN_ERROR;
3444 3440 pkt->pkt_reason = 0;
3445 3441 break;
3446 3442
3447 3443
3448 3444 }
3449 3445 }
3450 3446
3451 3447 return (status);
3452 3448 }
3453 3449
3454 3450
3455 3451 static int
3456 3452 fcsm_retry_cmd(fcsm_cmd_t *cmd)
3457 3453 {
3458 3454 if (cmd->cmd_retry_count < cmd->cmd_max_retries) {
3459 3455 cmd->cmd_retry_count++;
3460 3456 fcsm_enque_cmd(cmd->cmd_fcsm, cmd);
3461 3457 return (0);
3462 3458 }
3463 3459
3464 3460 return (1);
3465 3461 }
3466 3462
3467 3463 static void
3468 3464 fcsm_enque_cmd(fcsm_t *fcsm, fcsm_cmd_t *cmd)
3469 3465 {
3470 3466 ASSERT(!MUTEX_HELD(&fcsm->sm_mutex));
3471 3467
3472 3468 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL, "enque_cmd"));
3473 3469
3474 3470 cmd->cmd_next = NULL;
3475 3471 mutex_enter(&fcsm->sm_mutex);
3476 3472 if (fcsm->sm_retry_tail) {
3477 3473 ASSERT(fcsm->sm_retry_head != NULL);
3478 3474 fcsm->sm_retry_tail->cmd_next = cmd;
3479 3475 fcsm->sm_retry_tail = cmd;
3480 3476 } else {
3481 3477 ASSERT(fcsm->sm_retry_tail == NULL);
3482 3478 fcsm->sm_retry_head = fcsm->sm_retry_tail = cmd;
3483 3479
3484 3480 /* Schedule retry thread, if not already running */
3485 3481 if (fcsm->sm_retry_tid == NULL) {
3486 3482 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3487 3483 "enque_cmd: schedule retry thread"));
3488 3484 fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
3489 3485 (caddr_t)fcsm, fcsm_retry_ticks);
3490 3486 }
3491 3487 }
3492 3488 mutex_exit(&fcsm->sm_mutex);
3493 3489 }
3494 3490
3495 3491
3496 3492 static fcsm_cmd_t *
3497 3493 fcsm_deque_cmd(fcsm_t *fcsm)
3498 3494 {
3499 3495 fcsm_cmd_t *cmd;
3500 3496
3501 3497 ASSERT(!MUTEX_HELD(&fcsm->sm_mutex));
3502 3498
3503 3499 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL, "deque_cmd"));
3504 3500
3505 3501 mutex_enter(&fcsm->sm_mutex);
3506 3502 if (fcsm->sm_retry_head == NULL) {
3507 3503 ASSERT(fcsm->sm_retry_tail == NULL);
3508 3504 cmd = NULL;
3509 3505 } else {
3510 3506 cmd = fcsm->sm_retry_head;
3511 3507 fcsm->sm_retry_head = cmd->cmd_next;
3512 3508 if (fcsm->sm_retry_head == NULL) {
3513 3509 fcsm->sm_retry_tail = NULL;
3514 3510 }
3515 3511 cmd->cmd_next = NULL;
3516 3512 }
3517 3513 mutex_exit(&fcsm->sm_mutex);
3518 3514
3519 3515 return (cmd);
3520 3516 }
3521 3517
3522 3518 static void
3523 3519 fcsm_retry_timeout(void *handle)
3524 3520 {
3525 3521 fcsm_t *fcsm;
3526 3522 fcsm_cmd_t *curr_tail;
3527 3523 fcsm_cmd_t *cmd;
3528 3524 int done = 0;
3529 3525 int linkdown;
3530 3526
3531 3527 fcsm = (fcsm_t *)handle;
3532 3528
3533 3529 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL, "retry_timeout"));
3534 3530
3535 3531 /*
3536 3532 * If retry cmd queue is suspended, then go away.
3537 3533 * This retry thread will be restarted, when cmd queue resumes.
3538 3534 */
3539 3535 mutex_enter(&fcsm->sm_mutex);
3540 3536 if (fcsm->sm_flags & FCSM_CMD_RETRY_Q_SUSPENDED) {
3541 3537 /*
3542 3538 * Clear the retry_tid, to indicate that this routine is not
3543 3539 * currently being rescheduled.
3544 3540 */
3545 3541 fcsm->sm_retry_tid = (timeout_id_t)NULL;
3546 3542 mutex_exit(&fcsm->sm_mutex);
3547 3543 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3548 3544 "retry_timeout: end. No processing. "
3549 3545 "Queue is currently suspended for this instance"));
3550 3546 return;
3551 3547 }
3552 3548
3553 3549 linkdown = (fcsm->sm_flags & FCSM_LINK_DOWN) ? 1 : 0;
3554 3550
3555 3551 /*
3556 3552 * Save the curr_tail, so that we only process the commands
3557 3553 * which are in the queue at this time.
3558 3554 */
3559 3555 curr_tail = fcsm->sm_retry_tail;
3560 3556 mutex_exit(&fcsm->sm_mutex);
3561 3557
3562 3558 /*
3563 3559 * Check for done flag before dequeing the command.
3564 3560 * Dequeing before checking the done flag will cause a command
3565 3561 * to be lost.
3566 3562 */
3567 3563 while ((!done) && ((cmd = fcsm_deque_cmd(fcsm)) != NULL)) {
3568 3564
3569 3565 if (cmd == curr_tail) {
3570 3566 done = 1;
3571 3567 }
3572 3568
3573 3569 cmd->cmd_retry_interval -= fcsm_retry_ticker;
3574 3570
3575 3571 if (linkdown) {
3576 3572 fc_packet_t *pkt;
3577 3573
3578 3574 /*
3579 3575 * No need to retry the command. The link has
3580 3576 * suffered an offline timeout.
3581 3577 */
3582 3578 pkt = cmd->cmd_fp_pkt;
3583 3579 pkt->pkt_state = FC_PKT_PORT_OFFLINE;
3584 3580 pkt->pkt_reason = FC_REASON_OFFLINE;
3585 3581 pkt->pkt_comp(pkt);
3586 3582 continue;
3587 3583 }
3588 3584
3589 3585 if (cmd->cmd_retry_interval <= 0) {
3590 3586 /* Retry the command */
3591 3587 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3592 3588 "retry_timeout: issue cmd 0x%p", (void *)cmd));
3593 3589 if (fcsm_issue_cmd(cmd) != FC_SUCCESS) {
3594 3590 cmd->cmd_fp_pkt->pkt_comp(cmd->cmd_fp_pkt);
3595 3591 }
3596 3592 } else {
3597 3593 /*
3598 3594 * Put the command back on the queue. Retry time
3599 3595 * has not yet reached.
3600 3596 */
3601 3597 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3602 3598 "retry_timeout: queue cmd 0x%p", (void *)cmd));
3603 3599 fcsm_enque_cmd(fcsm, cmd);
3604 3600 }
3605 3601 }
3606 3602
3607 3603 mutex_enter(&fcsm->sm_mutex);
3608 3604 if (fcsm->sm_retry_head) {
3609 3605 /* Activate timer */
3610 3606 fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
3611 3607 (caddr_t)fcsm, fcsm_retry_ticks);
3612 3608 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3613 3609 "retry_timeout: retry thread rescheduled"));
3614 3610 } else {
3615 3611 /*
3616 3612 * Reset the tid variable. The first thread which queues the
3617 3613 * command, will restart the timer.
3618 3614 */
3619 3615 fcsm->sm_retry_tid = (timeout_id_t)NULL;
3620 3616 }
3621 3617 mutex_exit(&fcsm->sm_mutex);
3622 3618 }
↓ open down ↓ |
926 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX