1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25 /*
26 * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved.
27 */
28 /*
29 * Fibre channel Transport Library (fctl)
30 *
31 * Function naming conventions:
32 * Functions called from ULPs begin with fc_ulp_
33 * Functions called from FCAs begin with fc_fca_
34 * Internal functions begin with fctl_
35 *
36 * Fibre channel packet layout:
37 * +---------------------+<--------+
38 * | | |
39 * | ULP Packet private | |
40 * | | |
41 * +---------------------+ |
42 * | |---------+
43 * | struct fc_packet |---------+
44 * | | |
45 * +---------------------+<--------+
46 * | |
47 * | FCA Packet private |
48 * | |
49 * +---------------------+
50 *
51 * So you loved the ascii art ? It's strongly desirable to cache
52 * allocate the entire packet in one common place. So we define a set a
53 * of rules. In a contiguous block of memory, the top portion of the
54 * block points to ulp packet private area, next follows the fc_packet
55 * structure used extensively by all the consumers and what follows this
56 * is the FCA packet private. Note that given a packet structure, it is
57 * possible to get to the ULP and FCA Packet private fields using
58 * ulp_private and fca_private fields (which hold pointers) respectively.
59 *
60 * It should be noted with a grain of salt that ULP Packet private size
61 * varies between two different ULP types, So this poses a challenge to
62 * compute the correct size of the whole block on a per port basis. The
63 * transport layer doesn't have a problem in dealing with FCA packet
64 * private sizes as it is the sole manager of ports underneath. Since
65 * it's not a good idea to cache allocate different sizes of memory for
66 * different ULPs and have the ability to choose from one of these caches
67 * based on ULP type during every packet allocation, the transport some
68 * what wisely (?) hands off this job of cache allocation to the ULPs
69 * themselves.
70 *
71 * That means FCAs need to make their packet private size known to the
72 * transport to pass it up to the ULPs. This is done during
73 * fc_fca_attach(). And the transport passes this size up to ULPs during
74 * fc_ulp_port_attach() of each ULP.
75 *
76 * This leaves us with another possible question; How are packets
77 * allocated for ELS's started by the transport itself ? Well, the port
78 * driver during attach time, cache allocates on a per port basis to
79 * handle ELSs too.
80 */
81
82 #include <sys/note.h>
83 #include <sys/types.h>
84 #include <sys/varargs.h>
85 #include <sys/param.h>
86 #include <sys/errno.h>
87 #include <sys/uio.h>
88 #include <sys/buf.h>
89 #include <sys/modctl.h>
90 #include <sys/open.h>
91 #include <sys/kmem.h>
92 #include <sys/poll.h>
93 #include <sys/conf.h>
94 #include <sys/cmn_err.h>
95 #include <sys/stat.h>
96 #include <sys/ddi.h>
97 #include <sys/sunddi.h>
98 #include <sys/promif.h>
99 #include <sys/byteorder.h>
100 #include <sys/fibre-channel/fc.h>
101 #include <sys/fibre-channel/impl/fc_ulpif.h>
102 #include <sys/fibre-channel/impl/fc_fcaif.h>
103 #include <sys/fibre-channel/impl/fctl_private.h>
104 #include <sys/fibre-channel/impl/fc_portif.h>
105
106 /* These are referenced by fp.c! */
107 int did_table_size = D_ID_HASH_TABLE_SIZE;
108 int pwwn_table_size = PWWN_HASH_TABLE_SIZE;
109
110 static fc_ulp_module_t *fctl_ulp_modules;
111 static fc_fca_port_t *fctl_fca_portlist;
112 static fc_ulp_list_t *fctl_ulp_list;
113
114 static char fctl_greeting[] =
115 "fctl: %s ULP same type (0x%x) as existing module.\n";
116
117 static char *fctl_undefined = "Undefined";
118
119 /*
120 * This lock protects the fc_ulp_module_t linked list (i.e. mod_next field)
121 */
122
123 static krwlock_t fctl_ulp_lock;
124
125 /*
126 * The fctl_mod_ports_lock protects the mod_ports element in the
127 * fc_ulp_ports_t structure
128 */
129
130 static krwlock_t fctl_mod_ports_lock;
131
132 /*
133 * fctl_port_lock protects the linked list of local port structures
134 * (fctl_fca_portlist). When walking the list, this lock must be obtained
135 * prior to any local port locks.
136 */
137
138 static kmutex_t fctl_port_lock;
139 static kmutex_t fctl_ulp_list_mutex;
140
141 static fctl_nwwn_list_t *fctl_nwwn_hash_table;
142 static kmutex_t fctl_nwwn_hash_mutex;
143 int fctl_nwwn_table_size = NWWN_HASH_TABLE_SIZE;
144
145 #define FCTL_VERSION "20090729-1.70"
146 #define FCTL_NAME_VERSION "SunFC Transport v" FCTL_VERSION
147
148 char *fctl_version = FCTL_NAME_VERSION;
149
150 extern struct mod_ops mod_miscops;
151
152 static struct modlmisc modlmisc = {
153 &mod_miscops, /* type of module */
154 FCTL_NAME_VERSION /* Module name */
155 };
156
157 static struct modlinkage modlinkage = {
158 MODREV_1, (void *)&modlmisc, NULL
159 };
160
161 static struct bus_ops fctl_fca_busops = {
162 BUSO_REV,
163 nullbusmap, /* bus_map */
164 NULL, /* bus_get_intrspec */
165 NULL, /* bus_add_intrspec */
166 NULL, /* bus_remove_intrspec */
167 i_ddi_map_fault, /* bus_map_fault */
168 NULL, /* bus_dma_map */
169 ddi_dma_allochdl, /* bus_dma_allochdl */
170 ddi_dma_freehdl, /* bus_dma_freehdl */
171 ddi_dma_bindhdl, /* bus_dma_bindhdl */
172 ddi_dma_unbindhdl, /* bus_unbindhdl */
173 ddi_dma_flush, /* bus_dma_flush */
174 ddi_dma_win, /* bus_dma_win */
175 ddi_dma_mctl, /* bus_dma_ctl */
176 fctl_fca_bus_ctl, /* bus_ctl */
177 ddi_bus_prop_op, /* bus_prop_op */
178 NULL, /* bus_get_eventcookie */
179 NULL, /* bus_add_eventcall */
180 NULL, /* bus_remove_event */
181 NULL, /* bus_post_event */
182 NULL, /* bus_intr_ctl */
183 NULL, /* bus_config */
184 NULL, /* bus_unconfig */
185 NULL, /* bus_fm_init */
186 NULL, /* bus_fm_fini */
187 NULL, /* bus_fm_access_enter */
188 NULL, /* bus_fm_access_exit */
189 NULL, /* bus_power */
190 NULL
191 };
192
193 struct kmem_cache *fctl_job_cache;
194
195 static fc_errmap_t fc_errlist [] = {
196 { FC_FAILURE, "Operation failed" },
197 { FC_SUCCESS, "Operation success" },
198 { FC_CAP_ERROR, "Capability error" },
199 { FC_CAP_FOUND, "Capability found" },
200 { FC_CAP_SETTABLE, "Capability settable" },
201 { FC_UNBOUND, "Port not bound" },
202 { FC_NOMEM, "No memory" },
203 { FC_BADPACKET, "Bad packet" },
204 { FC_OFFLINE, "Port offline" },
205 { FC_OLDPORT, "Old Port" },
206 { FC_NO_MAP, "No map available" },
207 { FC_TRANSPORT_ERROR, "Transport error" },
208 { FC_ELS_FREJECT, "ELS Frejected" },
209 { FC_ELS_PREJECT, "ELS PRejected" },
210 { FC_ELS_BAD, "Bad ELS request" },
211 { FC_ELS_MALFORMED, "Malformed ELS request" },
212 { FC_TOOMANY, "Too many commands" },
213 { FC_UB_BADTOKEN, "Bad Unsolicited buffer token" },
214 { FC_UB_ERROR, "Unsolicited buffer error" },
215 { FC_UB_BUSY, "Unsolicited buffer busy" },
216 { FC_BADULP, "Bad ULP" },
217 { FC_BADTYPE, "Bad Type" },
218 { FC_UNCLAIMED, "Not Claimed" },
219 { FC_ULP_SAMEMODULE, "Same ULP Module" },
220 { FC_ULP_SAMETYPE, "Same ULP Type" },
221 { FC_ABORTED, "Command Aborted" },
222 { FC_ABORT_FAILED, "Abort Failed" },
223 { FC_BADEXCHANGE, "Bad Exchange" },
224 { FC_BADWWN, "Bad World Wide Name" },
225 { FC_BADDEV, "Bad Device" },
226 { FC_BADCMD, "Bad Command" },
227 { FC_BADOBJECT, "Bad Object" },
228 { FC_BADPORT, "Bad Port" },
229 { FC_NOTTHISPORT, "Not on this Port" },
230 { FC_PREJECT, "Operation Prejected" },
231 { FC_FREJECT, "Operation Frejected" },
232 { FC_PBUSY, "Operation Pbusyed" },
233 { FC_FBUSY, "Operation Fbusyed" },
234 { FC_ALREADY, "Already done" },
235 { FC_LOGINREQ, "PLOGI Required" },
236 { FC_RESETFAIL, "Reset operation failed" },
237 { FC_INVALID_REQUEST, "Invalid Request" },
238 { FC_OUTOFBOUNDS, "Out of Bounds" },
239 { FC_TRAN_BUSY, "Command transport Busy" },
240 { FC_STATEC_BUSY, "State change Busy" },
241 { FC_DEVICE_BUSY, "Port driver is working on this device" }
242 };
243
244 fc_pkt_reason_t remote_stop_reasons [] = {
245 { FC_REASON_ABTS, "Abort Sequence" },
246 { FC_REASON_ABTX, "Abort Exchange" },
247 { FC_REASON_INVALID, NULL }
248 };
249
250 fc_pkt_reason_t general_reasons [] = {
251 { FC_REASON_HW_ERROR, "Hardware Error" },
252 { FC_REASON_SEQ_TIMEOUT, "Sequence Timeout" },
253 { FC_REASON_ABORTED, "Aborted" },
254 { FC_REASON_ABORT_FAILED, "Abort Failed" },
255 { FC_REASON_NO_CONNECTION, "No Connection" },
256 { FC_REASON_XCHG_DROPPED, "Exchange Dropped" },
257 { FC_REASON_ILLEGAL_FRAME, "Illegal Frame" },
258 { FC_REASON_ILLEGAL_LENGTH, "Illegal Length" },
259 { FC_REASON_UNSUPPORTED, "Unsuported" },
260 { FC_REASON_RX_BUF_TIMEOUT, "Receive Buffer Timeout" },
261 { FC_REASON_FCAL_OPN_FAIL, "FC AL Open Failed" },
262 { FC_REASON_OVERRUN, "Over run" },
263 { FC_REASON_QFULL, "Queue Full" },
264 { FC_REASON_ILLEGAL_REQ, "Illegal Request", },
265 { FC_REASON_PKT_BUSY, "Busy" },
266 { FC_REASON_OFFLINE, "Offline" },
267 { FC_REASON_BAD_XID, "Bad Exchange Id" },
268 { FC_REASON_XCHG_BSY, "Exchange Busy" },
269 { FC_REASON_NOMEM, "No Memory" },
270 { FC_REASON_BAD_SID, "Bad S_ID" },
271 { FC_REASON_NO_SEQ_INIT, "No Sequence Initiative" },
272 { FC_REASON_DIAG_BUSY, "Diagnostic Busy" },
273 { FC_REASON_DMA_ERROR, "DMA Error" },
274 { FC_REASON_CRC_ERROR, "CRC Error" },
275 { FC_REASON_ABORT_TIMEOUT, "Abort Timeout" },
276 { FC_REASON_FCA_UNIQUE, "FCA Unique" },
277 { FC_REASON_INVALID, NULL }
278 };
279
280 fc_pkt_reason_t rjt_reasons [] = {
281 { FC_REASON_INVALID_D_ID, "Invalid D_ID" },
282 { FC_REASON_INVALID_S_ID, "Invalid S_ID" },
283 { FC_REASON_TEMP_UNAVAILABLE, "Temporarily Unavailable" },
284 { FC_REASON_PERM_UNAVAILABLE, "Permamnently Unavailable" },
285 { FC_REASON_CLASS_NOT_SUPP, "Class Not Supported", },
286 { FC_REASON_DELIMTER_USAGE_ERROR,
287 "Delimeter Usage Error" },
288 { FC_REASON_TYPE_NOT_SUPP, "Type Not Supported" },
289 { FC_REASON_INVALID_LINK_CTRL, "Invalid Link Control" },
290 { FC_REASON_INVALID_R_CTL, "Invalid R_CTL" },
291 { FC_REASON_INVALID_F_CTL, "Invalid F_CTL" },
292 { FC_REASON_INVALID_OX_ID, "Invalid OX_ID" },
293 { FC_REASON_INVALID_RX_ID, "Invalid RX_ID" },
294 { FC_REASON_INVALID_SEQ_ID, "Invalid Sequence ID" },
295 { FC_REASON_INVALID_DF_CTL, "Invalid DF_CTL" },
296 { FC_REASON_INVALID_SEQ_CNT, "Invalid Sequence count" },
297 { FC_REASON_INVALID_PARAM, "Invalid Parameter" },
298 { FC_REASON_EXCH_ERROR, "Exchange Error" },
299 { FC_REASON_PROTOCOL_ERROR, "Protocol Error" },
300 { FC_REASON_INCORRECT_LENGTH, "Incorrect Length" },
301 { FC_REASON_UNEXPECTED_ACK, "Unexpected Ack" },
302 { FC_REASON_UNEXPECTED_LR, "Unexpected Link reset" },
303 { FC_REASON_LOGIN_REQUIRED, "Login Required" },
304 { FC_REASON_EXCESSIVE_SEQS, "Excessive Sequences"
305 " Attempted" },
306 { FC_REASON_EXCH_UNABLE, "Exchange incapable" },
307 { FC_REASON_ESH_NOT_SUPP, "Expiration Security Header "
308 "Not Supported" },
309 { FC_REASON_NO_FABRIC_PATH, "No Fabric Path" },
310 { FC_REASON_VENDOR_UNIQUE, "Vendor Unique" },
311 { FC_REASON_INVALID, NULL }
312 };
313
314 fc_pkt_reason_t n_port_busy_reasons [] = {
315 { FC_REASON_PHYSICAL_BUSY, "Physical Busy" },
316 { FC_REASON_N_PORT_RESOURCE_BSY, "Resource Busy" },
317 { FC_REASON_N_PORT_VENDOR_UNIQUE, "Vendor Unique" },
318 { FC_REASON_INVALID, NULL }
319 };
320
321 fc_pkt_reason_t f_busy_reasons [] = {
322 { FC_REASON_FABRIC_BSY, "Fabric Busy" },
323 { FC_REASON_N_PORT_BSY, "N_Port Busy" },
324 { FC_REASON_INVALID, NULL }
325 };
326
327 fc_pkt_reason_t ls_ba_rjt_reasons [] = {
328 { FC_REASON_INVALID_LA_CODE, "Invalid Link Application Code" },
329 { FC_REASON_LOGICAL_ERROR, "Logical Error" },
330 { FC_REASON_LOGICAL_BSY, "Logical Busy" },
331 { FC_REASON_PROTOCOL_ERROR_RJT, "Protocol Error Reject" },
332 { FC_REASON_CMD_UNABLE, "Unable to Perform Command" },
333 { FC_REASON_CMD_UNSUPPORTED, "Unsupported Command" },
334 { FC_REASON_VU_RJT, "Vendor Unique" },
335 { FC_REASON_INVALID, NULL }
336 };
337
338 fc_pkt_reason_t fs_rjt_reasons [] = {
339 { FC_REASON_FS_INVALID_CMD, "Invalid Command" },
340 { FC_REASON_FS_INVALID_VER, "Invalid Version" },
341 { FC_REASON_FS_LOGICAL_ERR, "Logical Error" },
342 { FC_REASON_FS_INVALID_IUSIZE, "Invalid IU Size" },
343 { FC_REASON_FS_LOGICAL_BUSY, "Logical Busy" },
344 { FC_REASON_FS_PROTOCOL_ERR, "Protocol Error" },
345 { FC_REASON_FS_CMD_UNABLE, "Unable to Perform Command" },
346 { FC_REASON_FS_CMD_UNSUPPORTED, "Unsupported Command" },
347 { FC_REASON_FS_VENDOR_UNIQUE, "Vendor Unique" },
348 { FC_REASON_INVALID, NULL }
349 };
350
351 fc_pkt_action_t n_port_busy_actions [] = {
352 { FC_ACTION_SEQ_TERM_RETRY, "Retry terminated Sequence" },
353 { FC_ACTION_SEQ_ACTIVE_RETRY, "Retry Active Sequence" },
354 { FC_REASON_INVALID, NULL }
355 };
356
357 fc_pkt_action_t rjt_timeout_actions [] = {
358 { FC_ACTION_RETRYABLE, "Retryable" },
359 { FC_ACTION_NON_RETRYABLE, "Non Retryable" },
360 { FC_REASON_INVALID, NULL }
361 };
362
363 fc_pkt_expln_t ba_rjt_explns [] = {
364 { FC_EXPLN_NONE, "No Explanation" },
365 { FC_EXPLN_INVALID_OX_RX_ID, "Invalid X_ID" },
366 { FC_EXPLN_SEQ_ABORTED, "Sequence Aborted" },
367 { FC_EXPLN_INVALID, NULL }
368 };
369
370 fc_pkt_error_t fc_pkt_errlist[] = {
371 {
372 FC_PKT_SUCCESS,
373 "Operation Success",
374 NULL,
375 NULL,
376 NULL
377 },
378 { FC_PKT_REMOTE_STOP,
379 "Remote Stop",
380 remote_stop_reasons,
381 NULL,
382 NULL
383 },
384 {
385 FC_PKT_LOCAL_RJT,
386 "Local Reject",
387 general_reasons,
388 rjt_timeout_actions,
389 NULL
390 },
391 {
392 FC_PKT_NPORT_RJT,
393 "N_Port Reject",
394 rjt_reasons,
395 rjt_timeout_actions,
396 NULL
397 },
398 {
399 FC_PKT_FABRIC_RJT,
400 "Fabric Reject",
401 rjt_reasons,
402 rjt_timeout_actions,
403 NULL
404 },
405 {
406 FC_PKT_LOCAL_BSY,
407 "Local Busy",
408 general_reasons,
409 NULL,
410 NULL,
411 },
412 {
413 FC_PKT_TRAN_BSY,
414 "Transport Busy",
415 general_reasons,
416 NULL,
417 NULL,
418 },
419 {
420 FC_PKT_NPORT_BSY,
421 "N_Port Busy",
422 n_port_busy_reasons,
423 n_port_busy_actions,
424 NULL
425 },
426 {
427 FC_PKT_FABRIC_BSY,
428 "Fabric Busy",
429 f_busy_reasons,
430 NULL,
431 NULL,
432 },
433 {
434 FC_PKT_LS_RJT,
435 "Link Service Reject",
436 ls_ba_rjt_reasons,
437 NULL,
438 NULL,
439 },
440 {
441 FC_PKT_BA_RJT,
442 "Basic Reject",
443 ls_ba_rjt_reasons,
444 NULL,
445 ba_rjt_explns,
446 },
447 {
448 FC_PKT_TIMEOUT,
449 "Timeout",
450 general_reasons,
451 rjt_timeout_actions,
452 NULL
453 },
454 {
455 FC_PKT_FS_RJT,
456 "Fabric Switch Reject",
457 fs_rjt_reasons,
458 NULL,
459 NULL
460 },
461 {
462 FC_PKT_TRAN_ERROR,
463 "Packet Transport error",
464 general_reasons,
465 NULL,
466 NULL
467 },
468 {
469 FC_PKT_FAILURE,
470 "Packet Failure",
471 general_reasons,
472 NULL,
473 NULL
474 },
475 {
476 FC_PKT_PORT_OFFLINE,
477 "Port Offline",
478 NULL,
479 NULL,
480 NULL
481 },
482 {
483 FC_PKT_ELS_IN_PROGRESS,
484 "ELS is in Progress",
485 NULL,
486 NULL,
487 NULL
488 }
489 };
490
491 int
492 _init()
493 {
494 int rval;
495
496 rw_init(&fctl_ulp_lock, NULL, RW_DRIVER, NULL);
497 rw_init(&fctl_mod_ports_lock, NULL, RW_DRIVER, NULL);
498 mutex_init(&fctl_port_lock, NULL, MUTEX_DRIVER, NULL);
499 mutex_init(&fctl_nwwn_hash_mutex, NULL, MUTEX_DRIVER, NULL);
500
501 fctl_nwwn_hash_table = kmem_zalloc(sizeof (*fctl_nwwn_hash_table) *
502 fctl_nwwn_table_size, KM_SLEEP);
503
504 fctl_ulp_modules = NULL;
505 fctl_fca_portlist = NULL;
506
507 fctl_job_cache = kmem_cache_create("fctl_cache",
508 sizeof (job_request_t), 8, fctl_cache_constructor,
509 fctl_cache_destructor, NULL, NULL, NULL, 0);
510
511 if (fctl_job_cache == NULL) {
512 kmem_free(fctl_nwwn_hash_table,
513 sizeof (*fctl_nwwn_hash_table) * fctl_nwwn_table_size);
514 mutex_destroy(&fctl_nwwn_hash_mutex);
515 mutex_destroy(&fctl_port_lock);
516 rw_destroy(&fctl_ulp_lock);
517 rw_destroy(&fctl_mod_ports_lock);
518 return (ENOMEM);
519 }
520
521 if ((rval = mod_install(&modlinkage)) != 0) {
522 kmem_cache_destroy(fctl_job_cache);
523 kmem_free(fctl_nwwn_hash_table,
524 sizeof (*fctl_nwwn_hash_table) * fctl_nwwn_table_size);
525 mutex_destroy(&fctl_nwwn_hash_mutex);
526 mutex_destroy(&fctl_port_lock);
527 rw_destroy(&fctl_ulp_lock);
528 rw_destroy(&fctl_mod_ports_lock);
529 }
530
531 return (rval);
532 }
533
534
535 /*
536 * The mod_uninstall code doesn't call _fini when
537 * there is living dependent module on fctl. So
538 * there is no need to be extra careful here ?
539 */
540 int
541 _fini()
542 {
543 int rval;
544
545 if ((rval = mod_remove(&modlinkage)) != 0) {
546 return (rval);
547 }
548
549 kmem_cache_destroy(fctl_job_cache);
550 kmem_free(fctl_nwwn_hash_table,
551 sizeof (*fctl_nwwn_hash_table) * fctl_nwwn_table_size);
552 mutex_destroy(&fctl_nwwn_hash_mutex);
553 mutex_destroy(&fctl_port_lock);
554 rw_destroy(&fctl_ulp_lock);
555 rw_destroy(&fctl_mod_ports_lock);
556
557 return (rval);
558 }
559
560
561 int
562 _info(struct modinfo *modinfo_p)
563 {
564 return (mod_info(&modlinkage, modinfo_p));
565 }
566
567
568 /* ARGSUSED */
569 static int
570 fctl_cache_constructor(void *buf, void *cdarg, int kmflag)
571 {
572 job_request_t *job = (job_request_t *)buf;
573
574 mutex_init(&job->job_mutex, NULL, MUTEX_DRIVER, NULL);
575 sema_init(&job->job_fctl_sema, 0, NULL, SEMA_DEFAULT, NULL);
576 sema_init(&job->job_port_sema, 0, NULL, SEMA_DEFAULT, NULL);
577
578 return (0);
579 }
580
581
582 /* ARGSUSED */
583 static void
584 fctl_cache_destructor(void *buf, void *cdarg)
585 {
586 job_request_t *job = (job_request_t *)buf;
587
588 sema_destroy(&job->job_fctl_sema);
589 sema_destroy(&job->job_port_sema);
590 mutex_destroy(&job->job_mutex);
591 }
592
593
594 /*
595 * fc_ulp_add:
596 * Add a ULP module
597 *
598 * Return Codes:
599 * FC_ULP_SAMEMODULE
600 * FC_SUCCESS
601 * FC_FAILURE
602 *
603 * fc_ulp_add prints a warning message if there is already a
604 * similar ULP type attached and this is unlikely to change as
605 * we trudge along. Further, this function returns a failure
606 * code if the same module attempts to add more than once for
607 * the same FC-4 type.
608 */
609 int
610 fc_ulp_add(fc_ulp_modinfo_t *ulp_info)
611 {
612 fc_ulp_module_t *mod;
613 fc_ulp_module_t *prev;
614 job_request_t *job;
615 fc_ulp_list_t *new;
616 fc_fca_port_t *fca_port;
617 int ntry = 0;
618
619 ASSERT(ulp_info != NULL);
620
621 /*
622 * Make sure ulp_rev matches fctl version.
623 * Whenever non-private data structure or non-static interface changes,
624 * we should use an increased FCTL_ULP_MODREV_# number here and in all
625 * ulps to prevent version mismatch.
626 */
627 if (ulp_info->ulp_rev != FCTL_ULP_MODREV_4) {
628 cmn_err(CE_WARN, "fctl: ULP %s version mismatch;"
629 " ULP %s would not be loaded", ulp_info->ulp_name,
630 ulp_info->ulp_name);
631 return (FC_BADULP);
632 }
633
634 new = kmem_zalloc(sizeof (*new), KM_SLEEP);
635 ASSERT(new != NULL);
636
637 mutex_enter(&fctl_ulp_list_mutex);
638 new->ulp_info = ulp_info;
639 if (fctl_ulp_list != NULL) {
640 new->ulp_next = fctl_ulp_list;
641 }
642 fctl_ulp_list = new;
643 mutex_exit(&fctl_ulp_list_mutex);
644
645 while (rw_tryenter(&fctl_ulp_lock, RW_WRITER) == 0) {
646 delay(drv_usectohz(1000000));
647 if (ntry++ > FC_ULP_ADD_RETRY_COUNT) {
648 fc_ulp_list_t *list;
649 fc_ulp_list_t *last;
650 mutex_enter(&fctl_ulp_list_mutex);
651 for (last = NULL, list = fctl_ulp_list; list != NULL;
652 list = list->ulp_next) {
653 if (list->ulp_info == ulp_info) {
654 break;
655 }
656 last = list;
657 }
658
659 if (list) {
660 if (last) {
661 last->ulp_next = list->ulp_next;
662 } else {
663 fctl_ulp_list = list->ulp_next;
664 }
665 kmem_free(list, sizeof (*list));
666 }
667 mutex_exit(&fctl_ulp_list_mutex);
668 cmn_err(CE_WARN, "fctl: ULP %s unable to load",
669 ulp_info->ulp_name);
670 return (FC_FAILURE);
671 }
672 }
673
674 for (mod = fctl_ulp_modules, prev = NULL; mod; mod = mod->mod_next) {
675 ASSERT(mod->mod_info != NULL);
676
677 if (ulp_info == mod->mod_info &&
678 ulp_info->ulp_type == mod->mod_info->ulp_type) {
679 rw_exit(&fctl_ulp_lock);
680 return (FC_ULP_SAMEMODULE);
681 }
682
683 if (ulp_info->ulp_type == mod->mod_info->ulp_type) {
684 cmn_err(CE_NOTE, fctl_greeting, ulp_info->ulp_name,
685 ulp_info->ulp_type);
686 }
687 prev = mod;
688 }
689
690 mod = kmem_zalloc(sizeof (*mod), KM_SLEEP);
691 mod->mod_info = ulp_info;
692 mod->mod_next = NULL;
693
694 if (prev) {
695 prev->mod_next = mod;
696 } else {
697 fctl_ulp_modules = mod;
698 }
699
700 /*
701 * Schedule a job to each port's job_handler
702 * thread to attach their ports with this ULP.
703 */
704 mutex_enter(&fctl_port_lock);
705 for (fca_port = fctl_fca_portlist; fca_port != NULL;
706 fca_port = fca_port->port_next) {
707 job = fctl_alloc_job(JOB_ATTACH_ULP, JOB_TYPE_FCTL_ASYNC,
708 NULL, NULL, KM_SLEEP);
709
710 fctl_enque_job(fca_port->port_handle, job);
711 }
712 mutex_exit(&fctl_port_lock);
713
714 rw_exit(&fctl_ulp_lock);
715
716 return (FC_SUCCESS);
717 }
718
719
720 /*
721 * fc_ulp_remove
722 * Remove a ULP module
723 *
724 * A misbehaving ULP may call this routine while I/Os are in progress.
725 * Currently there is no mechanism to detect it to fail such a request.
726 *
727 * Return Codes:
728 * FC_SUCCESS
729 * FC_FAILURE
730 */
731 int
732 fc_ulp_remove(fc_ulp_modinfo_t *ulp_info)
733 {
734 fc_ulp_module_t *mod;
735 fc_ulp_list_t *list;
736 fc_ulp_list_t *last;
737 fc_ulp_module_t *prev;
738
739 mutex_enter(&fctl_ulp_list_mutex);
740
741 for (last = NULL, list = fctl_ulp_list; list != NULL;
742 list = list->ulp_next) {
743 if (list->ulp_info == ulp_info) {
744 break;
745 }
746 last = list;
747 }
748
749 if (list) {
750 if (last) {
751 last->ulp_next = list->ulp_next;
752 } else {
753 fctl_ulp_list = list->ulp_next;
754 }
755 kmem_free(list, sizeof (*list));
756 }
757
758 mutex_exit(&fctl_ulp_list_mutex);
759
760 rw_enter(&fctl_ulp_lock, RW_WRITER);
761
762 for (mod = fctl_ulp_modules, prev = NULL; mod != NULL;
763 mod = mod->mod_next) {
764 if (mod->mod_info == ulp_info) {
765 break;
766 }
767 prev = mod;
768 }
769
770 if (mod) {
771 fc_ulp_ports_t *next;
772
773 if (prev) {
774 prev->mod_next = mod->mod_next;
775 } else {
776 fctl_ulp_modules = mod->mod_next;
777 }
778
779 rw_enter(&fctl_mod_ports_lock, RW_WRITER);
780
781 while ((next = mod->mod_ports) != NULL) {
782 mod->mod_ports = next->port_next;
783 fctl_dealloc_ulp_port(next);
784 }
785
786 rw_exit(&fctl_mod_ports_lock);
787 rw_exit(&fctl_ulp_lock);
788
789 kmem_free(mod, sizeof (*mod));
790
791 return (FC_SUCCESS);
792 }
793 rw_exit(&fctl_ulp_lock);
794
795 return (FC_FAILURE);
796 }
797
798
799 /*
800 * The callers typically cache allocate the packet, complete the
801 * DMA setup for pkt_cmd and pkt_resp fields of the packet and
802 * call this function to see if the FCA is interested in doing
803 * its own intialization. For example, socal may like to initialize
804 * the soc_hdr which is pointed to by the pkt_fca_private field
805 * and sitting right below fc_packet_t in memory.
806 *
807 * The caller is required to ensure that pkt_pd is populated with the
808 * handle that it was given when the transport notified it about the
809 * device this packet is associated with. If there is no associated
810 * device, pkt_pd must be set to NULL. A non-NULL pkt_pd will cause an
811 * increment of the reference count for said pd. When the packet is freed,
812 * the reference count will be decremented. This reference count, in
813 * combination with the PD_GIVEN_TO_ULPS flag guarantees that the pd
814 * will not wink out of existence while there is a packet outstanding.
815 *
816 * This function and fca_init_pkt must not perform any operations that
817 * would result in a call back to the ULP, as the ULP may be required
818 * to hold a mutex across this call to ensure that the pd in question
819 * won't go away prior the call to fc_ulp_transport.
820 *
821 * ULPs are responsible for using the handles they are given during state
822 * change callback processing in a manner that ensures consistency. That
823 * is, they must be aware that they could be processing a state change
824 * notification that tells them the device associated with a particular
825 * handle has gone away at the same time they are being asked to
826 * initialize a packet using that handle. ULPs must therefore ensure
827 * that their state change processing and packet initialization code
828 * paths are sufficiently synchronized to avoid the use of an
829 * invalidated handle in any fc_packet_t struct that is passed to the
830 * fc_ulp_init_packet() function.
831 */
832 int
833 fc_ulp_init_packet(opaque_t port_handle, fc_packet_t *pkt, int sleep)
834 {
835 int rval;
836 fc_local_port_t *port = port_handle;
837 fc_remote_port_t *pd;
838
839 ASSERT(pkt != NULL);
840
841 pd = pkt->pkt_pd;
842
843 /* Call the FCA driver's fca_init_pkt entry point function. */
844 rval = port->fp_fca_tran->fca_init_pkt(port->fp_fca_handle, pkt, sleep);
845
846 if ((rval == FC_SUCCESS) && (pd != NULL)) {
847 /*
848 * A !NULL pd here must still be a valid
849 * reference to the fc_remote_port_t.
850 */
851 mutex_enter(&pd->pd_mutex);
852 ASSERT(pd->pd_ref_count >= 0);
853 pd->pd_ref_count++;
854 mutex_exit(&pd->pd_mutex);
855 }
856
857 return (rval);
858 }
859
860
861 /*
862 * This function is called before destroying the cache allocated
863 * fc_packet to free up (and uninitialize) any resource specially
864 * allocated by the FCA driver during tran_init_pkt().
865 *
866 * If the pkt_pd field in the given fc_packet_t struct is not NULL, then
867 * the pd_ref_count reference count is decremented for the indicated
868 * fc_remote_port_t struct.
869 */
870 int
871 fc_ulp_uninit_packet(opaque_t port_handle, fc_packet_t *pkt)
872 {
873 int rval;
874 fc_local_port_t *port = port_handle;
875 fc_remote_port_t *pd;
876
877 ASSERT(pkt != NULL);
878
879 pd = pkt->pkt_pd;
880
881 /* Call the FCA driver's fca_un_init_pkt entry point function */
882 rval = port->fp_fca_tran->fca_un_init_pkt(port->fp_fca_handle, pkt);
883
884 if ((rval == FC_SUCCESS) && (pd != NULL)) {
885 mutex_enter(&pd->pd_mutex);
886
887 ASSERT(pd->pd_ref_count > 0);
888 pd->pd_ref_count--;
889
890 /*
891 * If at this point the state of this fc_remote_port_t
892 * struct is PORT_DEVICE_INVALID, it probably means somebody
893 * is cleaning up old (e.g. retried) packets. If the
894 * pd_ref_count has also dropped to zero, it's time to
895 * deallocate this fc_remote_port_t struct.
896 */
897 if (pd->pd_state == PORT_DEVICE_INVALID &&
898 pd->pd_ref_count == 0) {
899 fc_remote_node_t *node = pd->pd_remote_nodep;
900
901 mutex_exit(&pd->pd_mutex);
902
903 /*
904 * Also deallocate the associated fc_remote_node_t
905 * struct if it has no other associated
906 * fc_remote_port_t structs.
907 */
908 if ((fctl_destroy_remote_port(port, pd) == 0) &&
909 (node != NULL)) {
910 fctl_destroy_remote_node(node);
911 }
912 return (rval);
913 }
914
915 mutex_exit(&pd->pd_mutex);
916 }
917
918 return (rval);
919 }
920
921
922 int
923 fc_ulp_getportmap(opaque_t port_handle, fc_portmap_t **map, uint32_t *len,
924 int flag)
925 {
926 int job_code;
927 fc_local_port_t *port;
928 job_request_t *job;
929 fc_portmap_t *tmp_map;
930 uint32_t tmp_len;
931 fc_portmap_t *change_list = NULL;
932 uint32_t listlen = 0;
933
934 port = port_handle;
935
936 mutex_enter(&port->fp_mutex);
937 if (port->fp_statec_busy) {
938 mutex_exit(&port->fp_mutex);
939 return (FC_STATEC_BUSY);
940 }
941
942 if (FC_PORT_STATE_MASK(port->fp_state) == FC_STATE_OFFLINE) {
943 mutex_exit(&port->fp_mutex);
944 return (FC_OFFLINE);
945 }
946
947 if (port->fp_dev_count && (port->fp_dev_count ==
948 port->fp_total_devices)) {
949 mutex_exit(&port->fp_mutex);
950 fctl_fillout_map(port, &change_list, &listlen, 1, 1, 0);
951 if (listlen > *len) {
952 tmp_map = (fc_portmap_t *)kmem_zalloc(
953 listlen * sizeof (fc_portmap_t), KM_NOSLEEP);
954 if (tmp_map == NULL) {
955 return (FC_NOMEM);
956 }
957 if (*map) {
958 kmem_free(*map, (*len) * sizeof (fc_portmap_t));
959 }
960 *map = tmp_map;
961 }
962 if (change_list) {
963 bcopy(change_list, *map,
964 listlen * sizeof (fc_portmap_t));
965 kmem_free(change_list, listlen * sizeof (fc_portmap_t));
966 }
967 *len = listlen;
968 } else {
969 mutex_exit(&port->fp_mutex);
970
971 switch (flag) {
972 case FC_ULP_PLOGI_DONTCARE:
973 job_code = JOB_PORT_GETMAP;
974 break;
975
976 case FC_ULP_PLOGI_PRESERVE:
977 job_code = JOB_PORT_GETMAP_PLOGI_ALL;
978 break;
979
980 default:
981 return (FC_INVALID_REQUEST);
982 }
983 /*
984 * Submit a job request to the job handler
985 * thread to get the map and wait
986 */
987 job = fctl_alloc_job(job_code, 0, NULL, NULL, KM_SLEEP);
988 job->job_private = (opaque_t)map;
989 job->job_arg = (opaque_t)len;
990 fctl_enque_job(port, job);
991
992 fctl_jobwait(job);
993 /*
994 * The result of the last I/O operation is
995 * in job_code. We don't care to look at it
996 * Rather we look at the number of devices
997 * that are found to fill out the map for
998 * ULPs.
999 */
1000 fctl_dealloc_job(job);
1001 }
1002
1003 /*
1004 * If we're here, we're returning a map to the caller, which means
1005 * we'd better make sure every pd in that map has the
1006 * PD_GIVEN_TO_ULPS flag set.
1007 */
1008
1009 tmp_len = *len;
1010 tmp_map = *map;
1011
1012 while (tmp_len-- != 0) {
1013 if (tmp_map->map_state != PORT_DEVICE_INVALID) {
1014 fc_remote_port_t *pd =
1015 (fc_remote_port_t *)tmp_map->map_pd;
1016 mutex_enter(&pd->pd_mutex);
1017 pd->pd_aux_flags |= PD_GIVEN_TO_ULPS;
1018 mutex_exit(&pd->pd_mutex);
1019 }
1020 tmp_map++;
1021 }
1022
1023 return (FC_SUCCESS);
1024 }
1025
1026
1027 int
1028 fc_ulp_login(opaque_t port_handle, fc_packet_t **ulp_pkt, uint32_t listlen)
1029 {
1030 int rval = FC_SUCCESS;
1031 int job_flags;
1032 uint32_t count;
1033 fc_packet_t **tmp_array;
1034 job_request_t *job;
1035 fc_local_port_t *port = port_handle;
1036 fc_ulp_rscn_info_t *rscnp =
1037 (fc_ulp_rscn_info_t *)(ulp_pkt[0])->pkt_ulp_rscn_infop;
1038
1039 /*
1040 * If the port is OFFLINE, or if the port driver is
1041 * being SUSPENDED/PM_SUSPENDED/DETACHED, block all
1042 * PLOGI operations
1043 */
1044 mutex_enter(&port->fp_mutex);
1045 if (port->fp_statec_busy) {
1046 mutex_exit(&port->fp_mutex);
1047 return (FC_STATEC_BUSY);
1048 }
1049
1050 if ((FC_PORT_STATE_MASK(port->fp_state) == FC_STATE_OFFLINE) ||
1051 (port->fp_soft_state &
1052 (FP_SOFT_IN_DETACH | FP_SOFT_SUSPEND | FP_SOFT_POWER_DOWN))) {
1053 mutex_exit(&port->fp_mutex);
1054 return (FC_OFFLINE);
1055 }
1056
1057 /*
1058 * If the rscn count in the packet is not the same as the rscn count
1059 * in the fc_local_port_t, then one or more new RSCNs has occurred.
1060 */
1061 if ((rscnp != NULL) &&
1062 (rscnp->ulp_rscn_count != FC_INVALID_RSCN_COUNT) &&
1063 (rscnp->ulp_rscn_count != port->fp_rscn_count)) {
1064 mutex_exit(&port->fp_mutex);
1065 return (FC_DEVICE_BUSY_NEW_RSCN);
1066 }
1067
1068 mutex_exit(&port->fp_mutex);
1069
1070 tmp_array = kmem_zalloc(sizeof (*tmp_array) * listlen, KM_SLEEP);
1071 for (count = 0; count < listlen; count++) {
1072 tmp_array[count] = ulp_pkt[count];
1073 }
1074
1075 job_flags = ((ulp_pkt[0]->pkt_tran_flags) & FC_TRAN_NO_INTR)
1076 ? 0 : JOB_TYPE_FCTL_ASYNC;
1077
1078 #ifdef DEBUG
1079 {
1080 int next;
1081 int count;
1082 int polled;
1083
1084 polled = ((ulp_pkt[0]->pkt_tran_flags) &
1085 FC_TRAN_NO_INTR) ? 0 : JOB_TYPE_FCTL_ASYNC;
1086
1087 for (count = 0; count < listlen; count++) {
1088 next = ((ulp_pkt[count]->pkt_tran_flags)
1089 & FC_TRAN_NO_INTR) ? 0 : JOB_TYPE_FCTL_ASYNC;
1090 ASSERT(next == polled);
1091 }
1092 }
1093 #endif
1094
1095 job = fctl_alloc_job(JOB_PLOGI_GROUP, job_flags, NULL, NULL, KM_SLEEP);
1096 job->job_ulp_pkts = tmp_array;
1097 job->job_ulp_listlen = listlen;
1098
1099 while (listlen--) {
1100 fc_packet_t *pkt;
1101
1102 pkt = tmp_array[listlen];
1103 if (pkt->pkt_pd == NULL) {
1104 pkt->pkt_state = FC_PKT_SUCCESS;
1105 continue;
1106 }
1107
1108 mutex_enter(&pkt->pkt_pd->pd_mutex);
1109 if (pkt->pkt_pd->pd_flags == PD_ELS_IN_PROGRESS ||
1110 pkt->pkt_pd->pd_flags == PD_ELS_MARK) {
1111 /*
1112 * Set the packet state and let the port
1113 * driver call the completion routine
1114 * from its thread
1115 */
1116 mutex_exit(&pkt->pkt_pd->pd_mutex);
1117 pkt->pkt_state = FC_PKT_ELS_IN_PROGRESS;
1118 continue;
1119 }
1120
1121 if (pkt->pkt_pd->pd_state == PORT_DEVICE_INVALID ||
1122 pkt->pkt_pd->pd_type == PORT_DEVICE_OLD) {
1123 mutex_exit(&pkt->pkt_pd->pd_mutex);
1124 pkt->pkt_state = FC_PKT_LOCAL_RJT;
1125 continue;
1126 }
1127 mutex_exit(&pkt->pkt_pd->pd_mutex);
1128 pkt->pkt_state = FC_PKT_SUCCESS;
1129 }
1130
1131 fctl_enque_job(port, job);
1132
1133 if (!(job_flags & JOB_TYPE_FCTL_ASYNC)) {
1134 fctl_jobwait(job);
1135 rval = job->job_result;
1136 fctl_dealloc_job(job);
1137 }
1138
1139 return (rval);
1140 }
1141
1142
1143 opaque_t
1144 fc_ulp_get_remote_port(opaque_t port_handle, la_wwn_t *pwwn, int *error,
1145 int create)
1146 {
1147 fc_local_port_t *port;
1148 job_request_t *job;
1149 fc_remote_port_t *pd;
1150
1151 port = port_handle;
1152 pd = fctl_get_remote_port_by_pwwn(port, pwwn);
1153
1154 if (pd != NULL) {
1155 *error = FC_SUCCESS;
1156 /*
1157 * A ULP now knows about this pd, so mark it
1158 */
1159 mutex_enter(&pd->pd_mutex);
1160 pd->pd_aux_flags |= PD_GIVEN_TO_ULPS;
1161 mutex_exit(&pd->pd_mutex);
1162 return (pd);
1163 }
1164
1165 mutex_enter(&port->fp_mutex);
1166 if (FC_IS_TOP_SWITCH(port->fp_topology) && create) {
1167 uint32_t d_id;
1168 fctl_ns_req_t *ns_cmd;
1169
1170 mutex_exit(&port->fp_mutex);
1171
1172 job = fctl_alloc_job(JOB_NS_CMD, 0, NULL, NULL, KM_SLEEP);
1173
1174 if (job == NULL) {
1175 *error = FC_NOMEM;
1176 return (pd);
1177 }
1178
1179 ns_cmd = fctl_alloc_ns_cmd(sizeof (ns_req_gid_pn_t),
1180 sizeof (ns_resp_gid_pn_t), sizeof (ns_resp_gid_pn_t),
1181 0, KM_SLEEP);
1182
1183 if (ns_cmd == NULL) {
1184 fctl_dealloc_job(job);
1185 *error = FC_NOMEM;
1186 return (pd);
1187 }
1188 ns_cmd->ns_cmd_code = NS_GID_PN;
1189 ((ns_req_gid_pn_t *)(ns_cmd->ns_cmd_buf))->pwwn = *pwwn;
1190
1191 job->job_result = FC_SUCCESS;
1192 job->job_private = (void *)ns_cmd;
1193 job->job_counter = 1;
1194 fctl_enque_job(port, job);
1195 fctl_jobwait(job);
1196
1197 if (job->job_result != FC_SUCCESS) {
1198 *error = job->job_result;
1199 fctl_free_ns_cmd(ns_cmd);
1200 fctl_dealloc_job(job);
1201 return (pd);
1202 }
1203 d_id = ((ns_resp_gid_pn_t *)ns_cmd->ns_data_buf)->pid.port_id;
1204 fctl_free_ns_cmd(ns_cmd);
1205
1206 ns_cmd = fctl_alloc_ns_cmd(sizeof (ns_req_gan_t),
1207 sizeof (ns_resp_gan_t), 0, FCTL_NS_CREATE_DEVICE,
1208 KM_SLEEP);
1209 ASSERT(ns_cmd != NULL);
1210
1211 ns_cmd->ns_gan_max = 1;
1212 ns_cmd->ns_cmd_code = NS_GA_NXT;
1213 ns_cmd->ns_gan_sid = FCTL_GAN_START_ID;
1214 ((ns_req_gan_t *)(ns_cmd->ns_cmd_buf))->pid.port_id = d_id - 1;
1215 ((ns_req_gan_t *)(ns_cmd->ns_cmd_buf))->pid.priv_lilp_posit = 0;
1216
1217 job->job_result = FC_SUCCESS;
1218 job->job_private = (void *)ns_cmd;
1219 job->job_counter = 1;
1220 fctl_enque_job(port, job);
1221 fctl_jobwait(job);
1222
1223 fctl_free_ns_cmd(ns_cmd);
1224 if (job->job_result != FC_SUCCESS) {
1225 *error = job->job_result;
1226 fctl_dealloc_job(job);
1227 return (pd);
1228 }
1229 fctl_dealloc_job(job);
1230
1231 /*
1232 * Check if the port device is created now.
1233 */
1234 pd = fctl_get_remote_port_by_pwwn(port, pwwn);
1235
1236 if (pd == NULL) {
1237 *error = FC_FAILURE;
1238 } else {
1239 *error = FC_SUCCESS;
1240
1241 /*
1242 * A ULP now knows about this pd, so mark it
1243 */
1244 mutex_enter(&pd->pd_mutex);
1245 pd->pd_aux_flags |= PD_GIVEN_TO_ULPS;
1246 mutex_exit(&pd->pd_mutex);
1247 }
1248 } else {
1249 mutex_exit(&port->fp_mutex);
1250 *error = FC_FAILURE;
1251 }
1252
1253 return (pd);
1254 }
1255
1256
1257 /*
1258 * If a NS object exists in the host and query is performed
1259 * on that object, we should retrieve it from our basket
1260 * and return it right here, there by saving a request going
1261 * all the up to the Name Server.
1262 */
1263 int
1264 fc_ulp_port_ns(opaque_t port_handle, opaque_t pd, fc_ns_cmd_t *ns_req)
1265 {
1266 int rval;
1267 int fabric;
1268 job_request_t *job;
1269 fctl_ns_req_t *ns_cmd;
1270 fc_local_port_t *port = port_handle;
1271
1272 mutex_enter(&port->fp_mutex);
1273 fabric = FC_IS_TOP_SWITCH(port->fp_topology) ? 1 : 0;
1274 mutex_exit(&port->fp_mutex);
1275
1276 /*
1277 * Name server query can't be performed for devices not in Fabric
1278 */
1279 if (!fabric && pd) {
1280 return (FC_BADOBJECT);
1281 }
1282
1283 if (FC_IS_CMD_A_REG(ns_req->ns_cmd)) {
1284 if (pd == NULL) {
1285 rval = fctl_update_host_ns_values(port, ns_req);
1286 if (rval != FC_SUCCESS) {
1287 return (rval);
1288 }
1289 } else {
1290 /*
1291 * Guess what, FC-GS-2 currently prohibits (not
1292 * in the strongest language though) setting of
1293 * NS object values by other ports. But we might
1294 * get that changed to at least accommodate setting
1295 * symbolic node/port names - But if disks/tapes
1296 * were going to provide a method to set these
1297 * values directly (which in turn might register
1298 * with the NS when they come up; yep, for that
1299 * to happen the disks will have to be very well
1300 * behaved Fabric citizen) we won't need to
1301 * register the symbolic port/node names for
1302 * other ports too (rather send down SCSI commands
1303 * to the devices to set the names)
1304 *
1305 * Be that as it may, let's continue to fail
1306 * registration requests for other ports. period.
1307 */
1308 return (FC_BADOBJECT);
1309 }
1310
1311 if (!fabric) {
1312 return (FC_SUCCESS);
1313 }
1314 } else if (!fabric) {
1315 return (fctl_retrieve_host_ns_values(port, ns_req));
1316 }
1317
1318 job = fctl_alloc_job(JOB_NS_CMD, 0, NULL, NULL, KM_SLEEP);
1319 ASSERT(job != NULL);
1320
1321 ns_cmd = fctl_alloc_ns_cmd(ns_req->ns_req_len,
1322 ns_req->ns_resp_len, ns_req->ns_resp_len, 0, KM_SLEEP);
1323 ASSERT(ns_cmd != NULL);
1324 ns_cmd->ns_cmd_code = ns_req->ns_cmd;
1325 bcopy(ns_req->ns_req_payload, ns_cmd->ns_cmd_buf,
1326 ns_req->ns_req_len);
1327
1328 job->job_private = (void *)ns_cmd;
1329 fctl_enque_job(port, job);
1330 fctl_jobwait(job);
1331 rval = job->job_result;
1332
1333 if (ns_req->ns_resp_len >= ns_cmd->ns_data_len) {
1334 bcopy(ns_cmd->ns_data_buf, ns_req->ns_resp_payload,
1335 ns_cmd->ns_data_len);
1336 }
1337 bcopy(&ns_cmd->ns_resp_hdr, &ns_req->ns_resp_hdr,
1338 sizeof (fc_ct_header_t));
1339
1340 fctl_free_ns_cmd(ns_cmd);
1341 fctl_dealloc_job(job);
1342
1343 return (rval);
1344 }
1345
1346
1347 int
1348 fc_ulp_transport(opaque_t port_handle, fc_packet_t *pkt)
1349 {
1350 int rval;
1351 fc_local_port_t *port;
1352 fc_remote_port_t *pd, *newpd;
1353 fc_ulp_rscn_info_t *rscnp =
1354 (fc_ulp_rscn_info_t *)pkt->pkt_ulp_rscn_infop;
1355
1356 port = port_handle;
1357
1358 if (pkt->pkt_tran_flags & FC_TRAN_DUMPING) {
1359 return (port->fp_fca_tran->fca_transport(
1360 port->fp_fca_handle, pkt));
1361 }
1362
1363 mutex_enter(&port->fp_mutex);
1364 if (port->fp_statec_busy) {
1365 mutex_exit(&port->fp_mutex);
1366 return (FC_STATEC_BUSY);
1367 }
1368
1369 /* A locus of race conditions */
1370 if (((FC_PORT_STATE_MASK(port->fp_state)) == FC_STATE_OFFLINE) ||
1371 (port->fp_soft_state &
1372 (FP_SOFT_IN_DETACH | FP_SOFT_SUSPEND | FP_SOFT_POWER_DOWN))) {
1373 mutex_exit(&port->fp_mutex);
1374 return (FC_OFFLINE);
1375 }
1376
1377 /*
1378 * If the rscn count in the packet is not the same as the rscn count
1379 * in the fc_local_port_t, then one or more new RSCNs has occurred.
1380 */
1381 if ((rscnp != NULL) &&
1382 (rscnp->ulp_rscn_count != FC_INVALID_RSCN_COUNT) &&
1383 (rscnp->ulp_rscn_count != port->fp_rscn_count)) {
1384 mutex_exit(&port->fp_mutex);
1385 return (FC_DEVICE_BUSY_NEW_RSCN);
1386 }
1387
1388 pd = pkt->pkt_pd;
1389 if (pd) {
1390 if (pd->pd_type == PORT_DEVICE_OLD ||
1391 pd->pd_state == PORT_DEVICE_INVALID) {
1392
1393 newpd = fctl_get_remote_port_by_pwwn_mutex_held(port,
1394 &pd->pd_port_name);
1395
1396 /*
1397 * The remote port (pd) in the packet is no longer
1398 * usable, as the old pd still exists we can use the
1399 * WWN to check if we have a current pd for the device
1400 * we want. Either way we continue with the old logic
1401 * whether we have a new pd or not, as the new pd
1402 * could be bad, or have become unusable.
1403 */
1404 if ((newpd) && (newpd != pd)) {
1405
1406 /*
1407 * There is a better remote port (pd) to try,
1408 * so we need to fix the reference counts, etc.
1409 */
1410 mutex_enter(&newpd->pd_mutex);
1411 newpd->pd_ref_count++;
1412 pkt->pkt_pd = newpd;
1413 mutex_exit(&newpd->pd_mutex);
1414
1415 mutex_enter(&pd->pd_mutex);
1416 pd->pd_ref_count--;
1417 if ((pd->pd_state == PORT_DEVICE_INVALID) &&
1418 (pd->pd_ref_count == 0)) {
1419 fc_remote_node_t *node =
1420 pd->pd_remote_nodep;
1421
1422 mutex_exit(&pd->pd_mutex);
1423 mutex_exit(&port->fp_mutex);
1424
1425 /*
1426 * This will create another PD hole
1427 * where we have a reference to a pd,
1428 * but someone else could remove it.
1429 */
1430 if ((fctl_destroy_remote_port(port, pd)
1431 == 0) && (node != NULL)) {
1432 fctl_destroy_remote_node(node);
1433 }
1434 mutex_enter(&port->fp_mutex);
1435 } else {
1436 mutex_exit(&pd->pd_mutex);
1437 }
1438 pd = newpd;
1439 }
1440 }
1441
1442 if (pd->pd_state != PORT_DEVICE_LOGGED_IN) {
1443 rval = (pd->pd_state == PORT_DEVICE_VALID) ?
1444 FC_LOGINREQ : FC_BADDEV;
1445 mutex_exit(&port->fp_mutex);
1446 return (rval);
1447 }
1448
1449 if (pd->pd_flags != PD_IDLE) {
1450 mutex_exit(&port->fp_mutex);
1451 return (FC_DEVICE_BUSY);
1452 }
1453
1454 if (pd->pd_type == PORT_DEVICE_OLD ||
1455 pd->pd_state == PORT_DEVICE_INVALID) {
1456 mutex_exit(&port->fp_mutex);
1457 return (FC_BADDEV);
1458 }
1459
1460 } else if (FC_IS_REAL_DEVICE(pkt->pkt_cmd_fhdr.d_id)) {
1461 mutex_exit(&port->fp_mutex);
1462 return (FC_BADPACKET);
1463 }
1464 mutex_exit(&port->fp_mutex);
1465
1466 return (port->fp_fca_tran->fca_transport(port->fp_fca_handle, pkt));
1467 }
1468
1469
1470 int
1471 fc_ulp_issue_els(opaque_t port_handle, fc_packet_t *pkt)
1472 {
1473 int rval;
1474 fc_local_port_t *port = port_handle;
1475 fc_remote_port_t *pd;
1476 fc_ulp_rscn_info_t *rscnp =
1477 (fc_ulp_rscn_info_t *)pkt->pkt_ulp_rscn_infop;
1478
1479 /*
1480 * If the port is OFFLINE, or if the port driver is
1481 * being SUSPENDED/PM_SUSPENDED/DETACHED, block all
1482 * ELS operations
1483 */
1484 mutex_enter(&port->fp_mutex);
1485 if ((FC_PORT_STATE_MASK(port->fp_state) == FC_STATE_OFFLINE) ||
1486 (port->fp_soft_state &
1487 (FP_SOFT_IN_DETACH | FP_SOFT_SUSPEND | FP_SOFT_POWER_DOWN))) {
1488 mutex_exit(&port->fp_mutex);
1489 return (FC_OFFLINE);
1490 }
1491
1492 if (port->fp_statec_busy) {
1493 mutex_exit(&port->fp_mutex);
1494 return (FC_STATEC_BUSY);
1495 }
1496
1497 /*
1498 * If the rscn count in the packet is not the same as the rscn count
1499 * in the fc_local_port_t, then one or more new RSCNs has occurred.
1500 */
1501 if ((rscnp != NULL) &&
1502 (rscnp->ulp_rscn_count != FC_INVALID_RSCN_COUNT) &&
1503 (rscnp->ulp_rscn_count != port->fp_rscn_count)) {
1504 mutex_exit(&port->fp_mutex);
1505 return (FC_DEVICE_BUSY_NEW_RSCN);
1506 }
1507
1508 mutex_exit(&port->fp_mutex);
1509
1510 if ((pd = pkt->pkt_pd) != NULL) {
1511 mutex_enter(&pd->pd_mutex);
1512 if (pd->pd_state != PORT_DEVICE_LOGGED_IN) {
1513 rval = (pd->pd_state == PORT_DEVICE_VALID) ?
1514 FC_LOGINREQ : FC_BADDEV;
1515 mutex_exit(&pd->pd_mutex);
1516 return (rval);
1517 }
1518
1519 if (pd->pd_flags != PD_IDLE) {
1520 mutex_exit(&pd->pd_mutex);
1521 return (FC_DEVICE_BUSY);
1522 }
1523 if (pd->pd_type == PORT_DEVICE_OLD ||
1524 pd->pd_state == PORT_DEVICE_INVALID) {
1525 mutex_exit(&pd->pd_mutex);
1526 return (FC_BADDEV);
1527 }
1528 mutex_exit(&pd->pd_mutex);
1529 }
1530
1531 return (port->fp_fca_tran->fca_els_send(port->fp_fca_handle, pkt));
1532 }
1533
1534
1535 int
1536 fc_ulp_uballoc(opaque_t port_handle, uint32_t *count, uint32_t size,
1537 uint32_t type, uint64_t *tokens)
1538 {
1539 fc_local_port_t *port = port_handle;
1540
1541 return (port->fp_fca_tran->fca_ub_alloc(port->fp_fca_handle,
1542 tokens, size, count, type));
1543 }
1544
1545
1546 int
1547 fc_ulp_ubfree(opaque_t port_handle, uint32_t count, uint64_t *tokens)
1548 {
1549 fc_local_port_t *port = port_handle;
1550
1551 return (port->fp_fca_tran->fca_ub_free(port->fp_fca_handle,
1552 count, tokens));
1553 }
1554
1555
1556 int
1557 fc_ulp_ubrelease(opaque_t port_handle, uint32_t count, uint64_t *tokens)
1558 {
1559 fc_local_port_t *port = port_handle;
1560
1561 return (port->fp_fca_tran->fca_ub_release(port->fp_fca_handle,
1562 count, tokens));
1563 }
1564
1565
1566 int
1567 fc_ulp_abort(opaque_t port_handle, fc_packet_t *pkt, int flags)
1568 {
1569 fc_local_port_t *port = port_handle;
1570
1571 return (port->fp_fca_tran->fca_abort(port->fp_fca_handle, pkt, flags));
1572 }
1573
1574
1575 /*
1576 * Submit an asynchronous request to the job handler if the sleep
1577 * flag is set to KM_NOSLEEP, as such calls could have been made
1578 * in interrupt contexts, and the goal is to avoid busy waiting,
1579 * blocking on a conditional variable, a semaphore or any of the
1580 * synchronization primitives. A noticeable draw back with this
1581 * asynchronous request is that an FC_SUCCESS is returned long
1582 * before the reset is complete (successful or not).
1583 */
1584 int
1585 fc_ulp_linkreset(opaque_t port_handle, la_wwn_t *pwwn, int sleep)
1586 {
1587 int rval;
1588 fc_local_port_t *port;
1589 job_request_t *job;
1590
1591 port = port_handle;
1592 /*
1593 * Many a times, this function is called from interrupt
1594 * contexts and there have been several dead locks and
1595 * hangs - One of the simplest work arounds is to fib
1596 * if a RESET is in progress.
1597 */
1598 mutex_enter(&port->fp_mutex);
1599 if (port->fp_soft_state & FP_SOFT_IN_LINK_RESET) {
1600 mutex_exit(&port->fp_mutex);
1601 return (FC_SUCCESS);
1602 }
1603
1604 /*
1605 * Ward off this reset if a state change is in progress.
1606 */
1607 if (port->fp_statec_busy) {
1608 mutex_exit(&port->fp_mutex);
1609 return (FC_STATEC_BUSY);
1610 }
1611 port->fp_soft_state |= FP_SOFT_IN_LINK_RESET;
1612 mutex_exit(&port->fp_mutex);
1613
1614 if (fctl_busy_port(port) != 0) {
1615 mutex_enter(&port->fp_mutex);
1616 port->fp_soft_state &= ~FP_SOFT_IN_LINK_RESET;
1617 mutex_exit(&port->fp_mutex);
1618 return (FC_FAILURE);
1619 }
1620
1621 if (sleep == KM_SLEEP) {
1622 job = fctl_alloc_job(JOB_LINK_RESET, 0, NULL, NULL, sleep);
1623 ASSERT(job != NULL);
1624
1625 job->job_private = (void *)pwwn;
1626 job->job_counter = 1;
1627 fctl_enque_job(port, job);
1628 fctl_jobwait(job);
1629
1630 mutex_enter(&port->fp_mutex);
1631 port->fp_soft_state &= ~FP_SOFT_IN_LINK_RESET;
1632 mutex_exit(&port->fp_mutex);
1633
1634 fctl_idle_port(port);
1635
1636 rval = job->job_result;
1637 fctl_dealloc_job(job);
1638 } else {
1639 job = fctl_alloc_job(JOB_LINK_RESET, JOB_TYPE_FCTL_ASYNC,
1640 fctl_link_reset_done, port, sleep);
1641 if (job == NULL) {
1642 mutex_enter(&port->fp_mutex);
1643 port->fp_soft_state &= ~FP_SOFT_IN_LINK_RESET;
1644 mutex_exit(&port->fp_mutex);
1645 fctl_idle_port(port);
1646 return (FC_NOMEM);
1647 }
1648 job->job_private = (void *)pwwn;
1649 job->job_counter = 1;
1650 fctl_priority_enque_job(port, job);
1651 rval = FC_SUCCESS;
1652 }
1653
1654 return (rval);
1655 }
1656
1657
1658 int
1659 fc_ulp_port_reset(opaque_t port_handle, uint32_t cmd)
1660 {
1661 int rval = FC_SUCCESS;
1662 fc_local_port_t *port = port_handle;
1663
1664 switch (cmd) {
1665 case FC_RESET_PORT:
1666 rval = port->fp_fca_tran->fca_reset(
1667 port->fp_fca_handle, FC_FCA_LINK_RESET);
1668 break;
1669
1670 case FC_RESET_ADAPTER:
1671 rval = port->fp_fca_tran->fca_reset(
1672 port->fp_fca_handle, FC_FCA_RESET);
1673 break;
1674
1675 case FC_RESET_DUMP:
1676 rval = port->fp_fca_tran->fca_reset(
1677 port->fp_fca_handle, FC_FCA_CORE);
1678 break;
1679
1680 case FC_RESET_CRASH:
1681 rval = port->fp_fca_tran->fca_reset(
1682 port->fp_fca_handle, FC_FCA_RESET_CORE);
1683 break;
1684
1685 default:
1686 rval = FC_FAILURE;
1687 }
1688
1689 return (rval);
1690 }
1691
1692
1693 int
1694 fc_ulp_get_port_login_params(opaque_t port_handle, la_els_logi_t *login_params)
1695 {
1696 fc_local_port_t *port = port_handle;
1697
1698 /* Copy the login parameters */
1699 *login_params = port->fp_service_params;
1700 return (FC_SUCCESS);
1701 }
1702
1703
1704 int
1705 fc_ulp_get_port_instance(opaque_t port_handle)
1706 {
1707 fc_local_port_t *port = port_handle;
1708
1709 return (port->fp_instance);
1710 }
1711
1712
1713 opaque_t
1714 fc_ulp_get_port_handle(int port_instance)
1715 {
1716 opaque_t port_handle = NULL;
1717 fc_fca_port_t *cur;
1718
1719 mutex_enter(&fctl_port_lock);
1720 for (cur = fctl_fca_portlist; cur; cur = cur->port_next) {
1721 if (cur->port_handle->fp_instance == port_instance) {
1722 port_handle = (opaque_t)cur->port_handle;
1723 break;
1724 }
1725 }
1726 mutex_exit(&fctl_port_lock);
1727
1728 return (port_handle);
1729 }
1730
1731
1732 int
1733 fc_ulp_error(int fc_errno, char **errmsg)
1734 {
1735 return (fctl_error(fc_errno, errmsg));
1736 }
1737
1738
1739 int
1740 fc_ulp_pkt_error(fc_packet_t *pkt, char **state, char **reason,
1741 char **action, char **expln)
1742 {
1743 return (fctl_pkt_error(pkt, state, reason, action, expln));
1744 }
1745
1746
1747 /*
1748 * If an ULP by the specified name exists, return FC_SUCCESS, else FC_FAILURE
1749 */
1750 int
1751 fc_ulp_is_name_present(caddr_t ulp_name)
1752 {
1753 int rval = FC_FAILURE;
1754 fc_ulp_list_t *list;
1755
1756 mutex_enter(&fctl_ulp_list_mutex);
1757 for (list = fctl_ulp_list; list != NULL; list = list->ulp_next) {
1758 if (strcmp(list->ulp_info->ulp_name, ulp_name) == 0) {
1759 rval = FC_SUCCESS;
1760 break;
1761 }
1762 }
1763 mutex_exit(&fctl_ulp_list_mutex);
1764
1765 return (rval);
1766 }
1767
1768
1769 /*
1770 * Return port WWN for a port Identifier
1771 */
1772 int
1773 fc_ulp_get_pwwn_by_did(opaque_t port_handle, fc_portid_t d_id, la_wwn_t *pwwn)
1774 {
1775 int rval = FC_FAILURE;
1776 fc_remote_port_t *pd;
1777 fc_local_port_t *port = port_handle;
1778
1779 pd = fctl_get_remote_port_by_did(port, d_id.port_id);
1780 if (pd != NULL) {
1781 mutex_enter(&pd->pd_mutex);
1782 *pwwn = pd->pd_port_name;
1783 mutex_exit(&pd->pd_mutex);
1784 rval = FC_SUCCESS;
1785 }
1786
1787 return (rval);
1788 }
1789
1790
1791 /*
1792 * Return a port map for a port WWN
1793 */
1794 int
1795 fc_ulp_pwwn_to_portmap(opaque_t port_handle, la_wwn_t *bytes, fc_portmap_t *map)
1796 {
1797 fc_local_port_t *port = port_handle;
1798 fc_remote_node_t *node;
1799 fc_remote_port_t *pd;
1800
1801 pd = fctl_get_remote_port_by_pwwn(port, bytes);
1802 if (pd == NULL) {
1803 return (FC_FAILURE);
1804 }
1805
1806 mutex_enter(&pd->pd_mutex);
1807 map->map_pwwn = pd->pd_port_name;
1808 map->map_did = pd->pd_port_id;
1809 map->map_hard_addr = pd->pd_hard_addr;
1810 map->map_state = pd->pd_state;
1811 map->map_type = pd->pd_type;
1812 map->map_flags = 0;
1813
1814 ASSERT(map->map_type <= PORT_DEVICE_DELETE);
1815
1816 bcopy(pd->pd_fc4types, map->map_fc4_types, sizeof (pd->pd_fc4types));
1817
1818 node = pd->pd_remote_nodep;
1819 mutex_exit(&pd->pd_mutex);
1820
1821 if (node) {
1822 mutex_enter(&node->fd_mutex);
1823 map->map_nwwn = node->fd_node_name;
1824 mutex_exit(&node->fd_mutex);
1825 }
1826 map->map_pd = pd;
1827
1828 return (FC_SUCCESS);
1829 }
1830
1831
1832 opaque_t
1833 fc_ulp_get_fca_device(opaque_t port_handle, fc_portid_t d_id)
1834 {
1835 fc_local_port_t *port = port_handle;
1836
1837 if (port->fp_fca_tran->fca_get_device == NULL) {
1838 return (NULL);
1839 }
1840
1841 return (port->fp_fca_tran->fca_get_device(port->fp_fca_handle, d_id));
1842 }
1843
1844
1845 int
1846 fc_ulp_port_notify(opaque_t port_handle, uint32_t cmd)
1847 {
1848 int rval = FC_SUCCESS;
1849 fc_local_port_t *port = port_handle;
1850
1851 if (port->fp_fca_tran->fca_notify) {
1852 mutex_enter(&port->fp_mutex);
1853 switch (cmd) {
1854 case FC_NOTIFY_TARGET_MODE:
1855 port->fp_options |= FP_TARGET_MODE;
1856 break;
1857 case FC_NOTIFY_NO_TARGET_MODE:
1858 port->fp_options &= ~FP_TARGET_MODE;
1859 break;
1860 }
1861 mutex_exit(&port->fp_mutex);
1862 rval = port->fp_fca_tran->fca_notify(port->fp_fca_handle, cmd);
1863 }
1864
1865 return (rval);
1866 }
1867
1868
1869 void
1870 fc_ulp_disable_relogin(opaque_t *fc_port, la_wwn_t *pwwn)
1871 {
1872 fc_remote_port_t *pd =
1873 fctl_get_remote_port_by_pwwn((fc_local_port_t *)fc_port, pwwn);
1874
1875 if (pd) {
1876 mutex_enter(&pd->pd_mutex);
1877 pd->pd_aux_flags |= PD_DISABLE_RELOGIN;
1878 mutex_exit(&pd->pd_mutex);
1879 }
1880 }
1881
1882
1883 void
1884 fc_ulp_enable_relogin(opaque_t *fc_port, la_wwn_t *pwwn)
1885 {
1886 fc_remote_port_t *pd =
1887 fctl_get_remote_port_by_pwwn((fc_local_port_t *)fc_port, pwwn);
1888
1889 if (pd) {
1890 mutex_enter(&pd->pd_mutex);
1891 pd->pd_aux_flags &= ~PD_DISABLE_RELOGIN;
1892 mutex_exit(&pd->pd_mutex);
1893 }
1894 }
1895
1896
1897 /*
1898 * fc_fca_init
1899 * Overload the FCA bus_ops vector in its dev_ops with
1900 * fctl_fca_busops to handle all the INITchilds for "sf"
1901 * in one common place.
1902 *
1903 * Should be called from FCA _init routine.
1904 */
1905 void
1906 fc_fca_init(struct dev_ops *fca_devops_p)
1907 {
1908 fca_devops_p->devo_bus_ops = &fctl_fca_busops;
1909 }
1910
1911
1912 /*
1913 * fc_fca_attach
1914 */
1915 int
1916 fc_fca_attach(dev_info_t *fca_dip, fc_fca_tran_t *tran)
1917 {
1918 /*
1919 * When we are in a position to offer downward compatibility
1920 * we should change the following check to allow lower revision
1921 * of FCAs; But we aren't there right now.
1922 */
1923 if (tran->fca_version != FCTL_FCA_MODREV_5) {
1924 const char *name = ddi_driver_name(fca_dip);
1925
1926 ASSERT(name != NULL);
1927
1928 cmn_err(CE_WARN, "fctl: FCA %s version mismatch"
1929 " please upgrade %s", name, name);
1930 return (DDI_FAILURE);
1931 }
1932
1933 ddi_set_driver_private(fca_dip, (caddr_t)tran);
1934 return (DDI_SUCCESS);
1935 }
1936
1937
1938 /*
1939 * fc_fca_detach
1940 */
1941 int
1942 fc_fca_detach(dev_info_t *fca_dip)
1943 {
1944 ddi_set_driver_private(fca_dip, NULL);
1945 return (DDI_SUCCESS);
1946 }
1947
1948
1949 /*
1950 * Check if the frame is a Link response Frame; Handle all cases (P_RJT,
1951 * F_RJT, P_BSY, F_BSY fall into this category). Check also for some Basic
1952 * Link Service responses such as BA_RJT and Extended Link Service response
1953 * such as LS_RJT. If the response is a Link_Data Frame or something that
1954 * this function doesn't understand return FC_FAILURE; Otherwise, fill out
1955 * various fields (state, action, reason, expln) from the response gotten
1956 * in the packet and return FC_SUCCESS.
1957 */
1958 int
1959 fc_fca_update_errors(fc_packet_t *pkt)
1960 {
1961 int ret = FC_SUCCESS;
1962
1963 switch (pkt->pkt_resp_fhdr.r_ctl) {
1964 case R_CTL_P_RJT: {
1965 uint32_t prjt;
1966
1967 prjt = pkt->pkt_resp_fhdr.ro;
1968 pkt->pkt_state = FC_PKT_NPORT_RJT;
1969 pkt->pkt_action = (prjt & 0xFF000000) >> 24;
1970 pkt->pkt_reason = (prjt & 0xFF0000) >> 16;
1971 break;
1972 }
1973
1974 case R_CTL_F_RJT: {
1975 uint32_t frjt;
1976
1977 frjt = pkt->pkt_resp_fhdr.ro;
1978 pkt->pkt_state = FC_PKT_FABRIC_RJT;
1979 pkt->pkt_action = (frjt & 0xFF000000) >> 24;
1980 pkt->pkt_reason = (frjt & 0xFF0000) >> 16;
1981 break;
1982 }
1983
1984 case R_CTL_P_BSY: {
1985 uint32_t pbsy;
1986
1987 pbsy = pkt->pkt_resp_fhdr.ro;
1988 pkt->pkt_state = FC_PKT_NPORT_BSY;
1989 pkt->pkt_action = (pbsy & 0xFF000000) >> 24;
1990 pkt->pkt_reason = (pbsy & 0xFF0000) >> 16;
1991 break;
1992 }
1993
1994 case R_CTL_F_BSY_LC:
1995 case R_CTL_F_BSY_DF: {
1996 uchar_t fbsy;
1997
1998 fbsy = pkt->pkt_resp_fhdr.type;
1999 pkt->pkt_state = FC_PKT_FABRIC_BSY;
2000 pkt->pkt_reason = (fbsy & 0xF0) >> 4;
2001 break;
2002 }
2003
2004 case R_CTL_LS_BA_RJT: {
2005 uint32_t brjt;
2006
2007 brjt = *(uint32_t *)pkt->pkt_resp;
2008 pkt->pkt_state = FC_PKT_BA_RJT;
2009 pkt->pkt_reason = (brjt & 0xFF0000) >> 16;
2010 pkt->pkt_expln = (brjt & 0xFF00) >> 8;
2011 break;
2012 }
2013
2014 case R_CTL_ELS_RSP: {
2015 la_els_rjt_t *lsrjt;
2016
2017 lsrjt = (la_els_rjt_t *)pkt->pkt_resp;
2018 if (lsrjt->ls_code.ls_code == LA_ELS_RJT) {
2019 pkt->pkt_state = FC_PKT_LS_RJT;
2020 pkt->pkt_reason = lsrjt->reason;
2021 pkt->pkt_action = lsrjt->action;
2022 break;
2023 }
2024 /* FALLTHROUGH */
2025 }
2026
2027 default:
2028 ret = FC_FAILURE;
2029 break;
2030 }
2031
2032 return (ret);
2033 }
2034
2035
2036 int
2037 fc_fca_error(int fc_errno, char **errmsg)
2038 {
2039 return (fctl_error(fc_errno, errmsg));
2040 }
2041
2042
2043 int
2044 fc_fca_pkt_error(fc_packet_t *pkt, char **state, char **reason,
2045 char **action, char **expln)
2046 {
2047 return (fctl_pkt_error(pkt, state, reason, action, expln));
2048 }
2049
2050
2051 /*
2052 * WWN to string goodie. Unpredictable results will happen
2053 * if enough memory isn't supplied in str argument. If you
2054 * are wondering how much does this routine need, it is just
2055 * (2 * WWN size + 1). So for a WWN size of 8 bytes the str
2056 * argument should have atleast 17 bytes allocated.
2057 */
2058 void
2059 fc_wwn_to_str(la_wwn_t *wwn, caddr_t str)
2060 {
2061 int count;
2062
2063 for (count = 0; count < FCTL_WWN_SIZE(wwn); count++, str += 2) {
2064 (void) sprintf(str, "%02x", wwn->raw_wwn[count]);
2065 }
2066 *str = '\0';
2067 }
2068
2069 #define FC_ATOB(x) (((x) >= '0' && (x) <= '9') ? ((x) - '0') : \
2070 ((x) >= 'a' && (x) <= 'f') ? \
2071 ((x) - 'a' + 10) : ((x) - 'A' + 10))
2072
2073 void
2074 fc_str_to_wwn(caddr_t str, la_wwn_t *wwn)
2075 {
2076 int count = 0;
2077 uchar_t byte;
2078
2079 while (*str) {
2080 byte = FC_ATOB(*str);
2081 str++;
2082 byte = byte << 4 | FC_ATOB(*str);
2083 str++;
2084 wwn->raw_wwn[count++] = byte;
2085 }
2086 }
2087
2088 /*
2089 * FCA driver's intercepted bus control operations.
2090 */
2091 static int
2092 fctl_fca_bus_ctl(dev_info_t *fca_dip, dev_info_t *rip,
2093 ddi_ctl_enum_t op, void *arg, void *result)
2094 {
2095 switch (op) {
2096 case DDI_CTLOPS_REPORTDEV:
2097 break;
2098
2099 case DDI_CTLOPS_IOMIN:
2100 break;
2101
2102 case DDI_CTLOPS_INITCHILD:
2103 return (fctl_initchild(fca_dip, (dev_info_t *)arg));
2104
2105 case DDI_CTLOPS_UNINITCHILD:
2106 return (fctl_uninitchild(fca_dip, (dev_info_t *)arg));
2107
2108 default:
2109 return (ddi_ctlops(fca_dip, rip, op, arg, result));
2110 }
2111
2112 return (DDI_SUCCESS);
2113 }
2114
2115
2116 /*
2117 * FCAs indicate the maximum number of ports supported in their
2118 * tran structure. Fail the INITCHILD if the child port number
2119 * is any greater than the maximum number of ports supported
2120 * by the FCA.
2121 */
2122 static int
2123 fctl_initchild(dev_info_t *fca_dip, dev_info_t *port_dip)
2124 {
2125 int rval;
2126 int port_no;
2127 int port_len;
2128 char name[20];
2129 fc_fca_tran_t *tran;
2130 dev_info_t *dip;
2131 int portprop;
2132
2133 port_len = sizeof (port_no);
2134
2135 /* physical port do not has this property */
2136 portprop = ddi_prop_get_int(DDI_DEV_T_ANY, port_dip,
2137 DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
2138 "phyport-instance", -1);
2139
2140 if ((portprop == -1) && ndi_dev_is_persistent_node(port_dip)) {
2141 /*
2142 * Clear any addr bindings created by fcode interpreter
2143 * in devi_last_addr so that a ndi_devi_find should never
2144 * return this fcode node.
2145 */
2146 ddi_set_name_addr(port_dip, NULL);
2147 return (DDI_FAILURE);
2148 }
2149
2150 rval = ddi_prop_op(DDI_DEV_T_ANY, port_dip, PROP_LEN_AND_VAL_BUF,
2151 DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "port",
2152 (caddr_t)&port_no, &port_len);
2153
2154 if (rval != DDI_SUCCESS) {
2155 return (DDI_FAILURE);
2156 }
2157
2158 tran = (fc_fca_tran_t *)ddi_get_driver_private(fca_dip);
2159 ASSERT(tran != NULL);
2160
2161 (void) sprintf((char *)name, "%x,0", port_no);
2162 ddi_set_name_addr(port_dip, name);
2163
2164 dip = ndi_devi_find(fca_dip, ddi_binding_name(port_dip), name);
2165
2166 /*
2167 * Even though we never initialize FCode nodes of fp, such a node
2168 * could still be there after a DR operation. There will only be
2169 * one FCode node, so if this is the one, clear it and issue a
2170 * ndi_devi_find again.
2171 */
2172 if ((portprop == -1) && dip && ndi_dev_is_persistent_node(dip)) {
2173 ddi_set_name_addr(dip, NULL);
2174 dip = ndi_devi_find(fca_dip, ddi_binding_name(port_dip), name);
2175 }
2176
2177 if ((portprop == -1) && dip && (dip != port_dip)) {
2178 /*
2179 * Here we have a duplicate .conf entry. Clear the addr
2180 * set previously and return failure.
2181 */
2182 ddi_set_name_addr(port_dip, NULL);
2183 return (DDI_FAILURE);
2184 }
2185
2186 return (DDI_SUCCESS);
2187 }
2188
2189
2190 /* ARGSUSED */
2191 static int
2192 fctl_uninitchild(dev_info_t *fca_dip, dev_info_t *port_dip)
2193 {
2194 ddi_set_name_addr(port_dip, NULL);
2195 return (DDI_SUCCESS);
2196 }
2197
2198
2199 static dev_info_t *
2200 fctl_findchild(dev_info_t *pdip, char *cname, char *caddr)
2201 {
2202 dev_info_t *dip;
2203 char *addr;
2204
2205 ASSERT(cname != NULL && caddr != NULL);
2206 /* ASSERT(DEVI_BUSY_OWNED(pdip)); */
2207
2208 for (dip = ddi_get_child(pdip); dip != NULL;
2209 dip = ddi_get_next_sibling(dip)) {
2210 if (strcmp(cname, ddi_node_name(dip)) != 0) {
2211 continue;
2212 }
2213
2214 if ((addr = ddi_get_name_addr(dip)) == NULL) {
2215 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
2216 DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
2217 "bus-addr", &addr) == DDI_PROP_SUCCESS) {
2218 if (strcmp(caddr, addr) == 0) {
2219 ddi_prop_free(addr);
2220 return (dip);
2221 }
2222 ddi_prop_free(addr);
2223 }
2224 } else {
2225 if (strcmp(caddr, addr) == 0) {
2226 return (dip);
2227 }
2228 }
2229 }
2230
2231 return (NULL);
2232 }
2233
2234 int
2235 fctl_check_npiv_portindex(dev_info_t *dip, int vindex)
2236 {
2237 int i, instance;
2238 fc_local_port_t *port;
2239
2240 instance = ddi_get_instance(dip);
2241 port = (fc_local_port_t *)fc_ulp_get_port_handle(instance);
2242 if ((!port) || (vindex <= 0) || (vindex >= FC_NPIV_MAX_PORT)) {
2243 return (0);
2244 }
2245
2246 i = vindex-1;
2247 mutex_enter(&port->fp_mutex);
2248 if (port->fp_npiv_portindex[i] == 0) {
2249 mutex_exit(&port->fp_mutex);
2250 return (vindex);
2251 }
2252 mutex_exit(&port->fp_mutex);
2253 return (0);
2254 }
2255
2256 int
2257 fctl_get_npiv_portindex(dev_info_t *dip)
2258 {
2259 int i, instance;
2260 fc_local_port_t *port;
2261
2262 instance = ddi_get_instance(dip);
2263 port = (fc_local_port_t *)fc_ulp_get_port_handle(instance);
2264 if (!port) {
2265 return (0);
2266 }
2267
2268 mutex_enter(&port->fp_mutex);
2269 for (i = 0; i < FC_NPIV_MAX_PORT; i++) {
2270 if (port->fp_npiv_portindex[i] == 0) {
2271 mutex_exit(&port->fp_mutex);
2272 return (i+1);
2273 }
2274 }
2275 mutex_exit(&port->fp_mutex);
2276 return (0);
2277 }
2278
2279
2280 void
2281 fctl_set_npiv_portindex(dev_info_t *dip, int index)
2282 {
2283 int instance;
2284 fc_local_port_t *port;
2285
2286 instance = ddi_get_instance(dip);
2287 port = (fc_local_port_t *)fc_ulp_get_port_handle(instance);
2288 if (!port) {
2289 return;
2290 }
2291 mutex_enter(&port->fp_mutex);
2292 port->fp_npiv_portindex[index - 1] = 1;
2293 mutex_exit(&port->fp_mutex);
2294 }
2295
2296
2297 int
2298 fctl_fca_create_npivport(dev_info_t *parent,
2299 dev_info_t *phydip, char *nname, char *pname, uint32_t *vindex)
2300 {
2301 int rval = 0, devstrlen;
2302 char *devname, *cname, *caddr, *devstr;
2303 dev_info_t *child = NULL;
2304 int portnum;
2305
2306 if (*vindex == 0) {
2307 portnum = fctl_get_npiv_portindex(phydip);
2308 *vindex = portnum;
2309 } else {
2310 portnum = fctl_check_npiv_portindex(phydip, *vindex);
2311 }
2312
2313 if (portnum == 0) {
2314 cmn_err(CE_WARN,
2315 "Cann't find valid port index, fail to create devnode");
2316 return (NDI_FAILURE);
2317 }
2318
2319 devname = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
2320 (void) sprintf(devname, "fp@%x,0", portnum);
2321 devstrlen = strlen(devname) + 1;
2322 devstr = i_ddi_strdup(devname, KM_SLEEP);
2323 i_ddi_parse_name(devstr, &cname, &caddr, NULL);
2324
2325 if (fctl_findchild(parent, cname, caddr) != NULL) {
2326 rval = NDI_FAILURE;
2327 goto freememory;
2328 }
2329
2330 ndi_devi_alloc_sleep(parent, cname, DEVI_PSEUDO_NODEID, &child);
2331 if (child == NULL) {
2332 cmn_err(CE_WARN,
2333 "fctl_create_npiv_port fail to create new devinfo");
2334 rval = NDI_FAILURE;
2335 goto freememory;
2336 }
2337
2338 if (ndi_prop_update_string(DDI_DEV_T_NONE, child,
2339 "bus-addr", caddr) != DDI_PROP_SUCCESS) {
2340 cmn_err(CE_WARN, "fctl%d: prop update bus-addr %s@%s failed",
2341 ddi_get_instance(parent), cname, caddr);
2342 (void) ndi_devi_free(child);
2343 rval = NDI_FAILURE;
2344 goto freememory;
2345 }
2346
2347 if (strlen(nname) != 0) {
2348 if (ndi_prop_update_string(DDI_DEV_T_NONE, child,
2349 "node-name", nname) != DDI_PROP_SUCCESS) {
2350 (void) ndi_devi_free(child);
2351 rval = NDI_FAILURE;
2352 goto freememory;
2353 }
2354 }
2355
2356 if (strlen(pname) != 0) {
2357 if (ndi_prop_update_string(DDI_DEV_T_NONE, child,
2358 "port-name", pname) != DDI_PROP_SUCCESS) {
2359 (void) ndi_devi_free(child);
2360 rval = NDI_FAILURE;
2361 goto freememory;
2362 }
2363 }
2364
2365 if (ddi_prop_update_int(DDI_DEV_T_NONE, child,
2366 "port", portnum) != DDI_PROP_SUCCESS) {
2367 cmn_err(CE_WARN, "fp%d: prop_update port %s@%s failed",
2368 ddi_get_instance(parent), cname, caddr);
2369 (void) ndi_devi_free(child);
2370 rval = NDI_FAILURE;
2371 goto freememory;
2372 }
2373
2374 if (ddi_prop_update_int(DDI_DEV_T_NONE, child,
2375 "phyport-instance", ddi_get_instance(phydip)) != DDI_PROP_SUCCESS) {
2376 cmn_err(CE_WARN,
2377 "fp%d: prop_update phyport-instance %s@%s failed",
2378 ddi_get_instance(parent), cname, caddr);
2379 (void) ndi_devi_free(child);
2380 rval = NDI_FAILURE;
2381 goto freememory;
2382 }
2383
2384 rval = ndi_devi_online(child, NDI_ONLINE_ATTACH);
2385 if (rval != NDI_SUCCESS) {
2386 cmn_err(CE_WARN, "fp%d: online_driver %s failed",
2387 ddi_get_instance(parent), cname);
2388 rval = NDI_FAILURE;
2389 goto freememory;
2390 }
2391
2392 fctl_set_npiv_portindex(phydip, portnum);
2393 freememory:
2394 kmem_free(devstr, devstrlen);
2395 kmem_free(devname, MAXNAMELEN);
2396
2397 return (rval);
2398 }
2399
2400
2401 void
2402 fctl_add_port(fc_local_port_t *port)
2403 {
2404 fc_fca_port_t *new;
2405
2406 new = kmem_zalloc(sizeof (*new), KM_SLEEP);
2407
2408 mutex_enter(&fctl_port_lock);
2409 new->port_handle = port;
2410 new->port_next = fctl_fca_portlist;
2411 fctl_fca_portlist = new;
2412 mutex_exit(&fctl_port_lock);
2413 }
2414
2415
2416 void
2417 fctl_remove_port(fc_local_port_t *port)
2418 {
2419 fc_ulp_module_t *mod;
2420 fc_fca_port_t *prev;
2421 fc_fca_port_t *list;
2422 fc_ulp_ports_t *ulp_port;
2423
2424 rw_enter(&fctl_ulp_lock, RW_WRITER);
2425 rw_enter(&fctl_mod_ports_lock, RW_WRITER);
2426
2427 for (mod = fctl_ulp_modules; mod; mod = mod->mod_next) {
2428 ulp_port = fctl_get_ulp_port(mod, port);
2429 if (ulp_port == NULL) {
2430 continue;
2431 }
2432
2433 ASSERT((ulp_port->port_dstate & ULP_PORT_ATTACH) == 0);
2434
2435 (void) fctl_remove_ulp_port(mod, port);
2436 }
2437
2438 rw_exit(&fctl_mod_ports_lock);
2439 rw_exit(&fctl_ulp_lock);
2440
2441 mutex_enter(&fctl_port_lock);
2442
2443 list = fctl_fca_portlist;
2444 prev = NULL;
2445 while (list != NULL) {
2446 if (list->port_handle == port) {
2447 if (prev == NULL) {
2448 fctl_fca_portlist = list->port_next;
2449 } else {
2450 prev->port_next = list->port_next;
2451 }
2452 kmem_free(list, sizeof (*list));
2453 break;
2454 }
2455 prev = list;
2456 list = list->port_next;
2457 }
2458 mutex_exit(&fctl_port_lock);
2459 }
2460
2461
2462 void
2463 fctl_attach_ulps(fc_local_port_t *port, fc_attach_cmd_t cmd,
2464 struct modlinkage *linkage)
2465 {
2466 int rval;
2467 uint32_t s_id;
2468 uint32_t state;
2469 fc_ulp_module_t *mod;
2470 fc_ulp_port_info_t info;
2471 fc_ulp_ports_t *ulp_port;
2472
2473 ASSERT(!MUTEX_HELD(&port->fp_mutex));
2474
2475 info.port_linkage = linkage;
2476 info.port_dip = port->fp_port_dip;
2477 info.port_handle = (opaque_t)port;
2478 info.port_dma_behavior = port->fp_dma_behavior;
2479 info.port_fcp_dma = port->fp_fcp_dma;
2480 info.port_acc_attr = port->fp_fca_tran->fca_acc_attr;
2481 info.port_fca_pkt_size = port->fp_fca_tran->fca_pkt_size;
2482 info.port_reset_action = port->fp_reset_action;
2483
2484 mutex_enter(&port->fp_mutex);
2485
2486 /*
2487 * It is still possible that another thread could have gotten
2488 * into the detach process before we got here.
2489 */
2490 if (port->fp_soft_state & FP_SOFT_IN_DETACH) {
2491 mutex_exit(&port->fp_mutex);
2492 return;
2493 }
2494
2495 s_id = port->fp_port_id.port_id;
2496 if (port->fp_statec_busy) {
2497 info.port_state = port->fp_bind_state;
2498 } else {
2499 info.port_state = port->fp_state;
2500 }
2501
2502 switch (state = FC_PORT_STATE_MASK(info.port_state)) {
2503 case FC_STATE_LOOP:
2504 case FC_STATE_NAMESERVICE:
2505 info.port_state &= ~state;
2506 info.port_state |= FC_STATE_ONLINE;
2507 break;
2508
2509 default:
2510 break;
2511 }
2512 ASSERT((info.port_state & FC_STATE_LOOP) == 0);
2513
2514 info.port_flags = port->fp_topology;
2515 info.port_pwwn = port->fp_service_params.nport_ww_name;
2516 info.port_nwwn = port->fp_service_params.node_ww_name;
2517 mutex_exit(&port->fp_mutex);
2518
2519 rw_enter(&fctl_ulp_lock, RW_READER);
2520 rw_enter(&fctl_mod_ports_lock, RW_WRITER);
2521
2522 for (mod = fctl_ulp_modules; mod; mod = mod->mod_next) {
2523 if ((port->fp_soft_state & FP_SOFT_FCA_IS_NODMA) &&
2524 (mod->mod_info->ulp_type == FC_TYPE_IS8802_SNAP)) {
2525 /*
2526 * We don't support IP over FC on FCOE HBA
2527 */
2528 continue;
2529 }
2530
2531 if ((ulp_port = fctl_get_ulp_port(mod, port)) == NULL) {
2532 ulp_port = fctl_add_ulp_port(mod, port, KM_SLEEP);
2533 ASSERT(ulp_port != NULL);
2534
2535 mutex_enter(&ulp_port->port_mutex);
2536 ulp_port->port_statec = ((info.port_state &
2537 FC_STATE_ONLINE) ? FC_ULP_STATEC_ONLINE :
2538 FC_ULP_STATEC_OFFLINE);
2539 mutex_exit(&ulp_port->port_mutex);
2540 }
2541 }
2542
2543 rw_downgrade(&fctl_mod_ports_lock);
2544
2545 for (mod = fctl_ulp_modules; mod; mod = mod->mod_next) {
2546 if ((port->fp_soft_state & FP_SOFT_FCA_IS_NODMA) &&
2547 (mod->mod_info->ulp_type == FC_TYPE_IS8802_SNAP)) {
2548 /*
2549 * We don't support IP over FC on FCOE HBA
2550 */
2551 continue;
2552 }
2553
2554 ulp_port = fctl_get_ulp_port(mod, port);
2555 ASSERT(ulp_port != NULL);
2556
2557 if (fctl_pre_attach(ulp_port, cmd) == FC_FAILURE) {
2558 continue;
2559 }
2560
2561 fctl_init_dma_attr(port, mod, &info);
2562
2563 rval = mod->mod_info->ulp_port_attach(
2564 mod->mod_info->ulp_handle, &info, cmd, s_id);
2565
2566 fctl_post_attach(mod, ulp_port, cmd, rval);
2567
2568 if (rval == FC_SUCCESS && cmd == FC_CMD_ATTACH &&
2569 strcmp(mod->mod_info->ulp_name, "fcp") == 0) {
2570 ASSERT(ddi_get_driver_private(info.port_dip) != NULL);
2571 }
2572 }
2573
2574 rw_exit(&fctl_mod_ports_lock);
2575 rw_exit(&fctl_ulp_lock);
2576 }
2577
2578
2579 static int
2580 fctl_pre_attach(fc_ulp_ports_t *ulp_port, fc_attach_cmd_t cmd)
2581 {
2582 int rval = FC_SUCCESS;
2583
2584 mutex_enter(&ulp_port->port_mutex);
2585
2586 switch (cmd) {
2587 case FC_CMD_ATTACH:
2588 if (ulp_port->port_dstate & ULP_PORT_ATTACH) {
2589 rval = FC_FAILURE;
2590 }
2591 break;
2592
2593 case FC_CMD_RESUME:
2594 ASSERT((ulp_port->port_dstate & ULP_PORT_POWER_DOWN) == 0);
2595 if (!(ulp_port->port_dstate & ULP_PORT_ATTACH) ||
2596 !(ulp_port->port_dstate & ULP_PORT_SUSPEND)) {
2597 rval = FC_FAILURE;
2598 }
2599 break;
2600
2601 case FC_CMD_POWER_UP:
2602 if (!(ulp_port->port_dstate & ULP_PORT_ATTACH) ||
2603 !(ulp_port->port_dstate & ULP_PORT_POWER_DOWN)) {
2604 rval = FC_FAILURE;
2605 }
2606 break;
2607 }
2608
2609 if (rval == FC_SUCCESS) {
2610 ulp_port->port_dstate |= ULP_PORT_BUSY;
2611 }
2612 mutex_exit(&ulp_port->port_mutex);
2613
2614 return (rval);
2615 }
2616
2617
2618 static void
2619 fctl_post_attach(fc_ulp_module_t *mod, fc_ulp_ports_t *ulp_port,
2620 fc_attach_cmd_t cmd, int rval)
2621 {
2622 int be_chatty;
2623
2624 ASSERT(cmd == FC_CMD_ATTACH || cmd == FC_CMD_RESUME ||
2625 cmd == FC_CMD_POWER_UP);
2626
2627 mutex_enter(&ulp_port->port_mutex);
2628 ulp_port->port_dstate &= ~ULP_PORT_BUSY;
2629
2630 be_chatty = (rval == FC_FAILURE_SILENT) ? 0 : 1;
2631
2632 if (rval != FC_SUCCESS) {
2633 caddr_t op;
2634 fc_local_port_t *port = ulp_port->port_handle;
2635
2636 mutex_exit(&ulp_port->port_mutex);
2637
2638 switch (cmd) {
2639 case FC_CMD_ATTACH:
2640 op = "attach";
2641 break;
2642
2643 case FC_CMD_RESUME:
2644 op = "resume";
2645 break;
2646
2647 case FC_CMD_POWER_UP:
2648 op = "power up";
2649 break;
2650 }
2651
2652 if (be_chatty) {
2653 cmn_err(CE_WARN, "!fctl(%d): %s failed for %s",
2654 port->fp_instance, op, mod->mod_info->ulp_name);
2655 }
2656
2657 return;
2658 }
2659
2660 switch (cmd) {
2661 case FC_CMD_ATTACH:
2662 ulp_port->port_dstate |= ULP_PORT_ATTACH;
2663 break;
2664
2665 case FC_CMD_RESUME:
2666 ulp_port->port_dstate &= ~ULP_PORT_SUSPEND;
2667 break;
2668
2669 case FC_CMD_POWER_UP:
2670 ulp_port->port_dstate &= ~ULP_PORT_POWER_DOWN;
2671 break;
2672 }
2673 mutex_exit(&ulp_port->port_mutex);
2674 }
2675
2676
2677 int
2678 fctl_detach_ulps(fc_local_port_t *port, fc_detach_cmd_t cmd,
2679 struct modlinkage *linkage)
2680 {
2681 int rval = FC_SUCCESS;
2682 fc_ulp_module_t *mod;
2683 fc_ulp_port_info_t info;
2684 fc_ulp_ports_t *ulp_port;
2685
2686 ASSERT(!MUTEX_HELD(&port->fp_mutex));
2687
2688 info.port_linkage = linkage;
2689 info.port_dip = port->fp_port_dip;
2690 info.port_handle = (opaque_t)port;
2691 info.port_acc_attr = port->fp_fca_tran->fca_acc_attr;
2692 info.port_fca_pkt_size = port->fp_fca_tran->fca_pkt_size;
2693
2694 rw_enter(&fctl_ulp_lock, RW_READER);
2695 rw_enter(&fctl_mod_ports_lock, RW_READER);
2696
2697 for (mod = fctl_ulp_modules; mod; mod = mod->mod_next) {
2698 if ((ulp_port = fctl_get_ulp_port(mod, port)) == NULL) {
2699 continue;
2700 }
2701
2702 if (fctl_pre_detach(ulp_port, cmd) != FC_SUCCESS) {
2703 continue;
2704 }
2705
2706 fctl_init_dma_attr(port, mod, &info);
2707
2708 rval = mod->mod_info->ulp_port_detach(
2709 mod->mod_info->ulp_handle, &info, cmd);
2710
2711 fctl_post_detach(mod, ulp_port, cmd, rval);
2712
2713 if (rval != FC_SUCCESS) {
2714 break;
2715 }
2716
2717 if (cmd == FC_CMD_DETACH && strcmp(mod->mod_info->ulp_name,
2718 "fcp") == 0) {
2719 ASSERT(ddi_get_driver_private(info.port_dip) == NULL);
2720 }
2721
2722 mutex_enter(&ulp_port->port_mutex);
2723 ulp_port->port_statec = FC_ULP_STATEC_DONT_CARE;
2724 mutex_exit(&ulp_port->port_mutex);
2725 }
2726
2727 rw_exit(&fctl_mod_ports_lock);
2728 rw_exit(&fctl_ulp_lock);
2729
2730 return (rval);
2731 }
2732
2733 static void
2734 fctl_init_dma_attr(fc_local_port_t *port, fc_ulp_module_t *mod,
2735 fc_ulp_port_info_t *info)
2736 {
2737
2738 if ((strcmp(mod->mod_info->ulp_name, "fcp") == 0) ||
2739 (strcmp(mod->mod_info->ulp_name, "ltct") == 0)) {
2740 info->port_cmd_dma_attr =
2741 port->fp_fca_tran->fca_dma_fcp_cmd_attr;
2742 info->port_data_dma_attr =
2743 port->fp_fca_tran->fca_dma_fcp_data_attr;
2744 info->port_resp_dma_attr =
2745 port->fp_fca_tran->fca_dma_fcp_rsp_attr;
2746 } else if (strcmp(mod->mod_info->ulp_name, "fcsm") == 0) {
2747 info->port_cmd_dma_attr =
2748 port->fp_fca_tran->fca_dma_fcsm_cmd_attr;
2749 info->port_data_dma_attr =
2750 port->fp_fca_tran->fca_dma_attr;
2751 info->port_resp_dma_attr =
2752 port->fp_fca_tran->fca_dma_fcsm_rsp_attr;
2753 } else if (strcmp(mod->mod_info->ulp_name, "fcip") == 0) {
2754 info->port_cmd_dma_attr =
2755 port->fp_fca_tran->fca_dma_fcip_cmd_attr;
2756 info->port_data_dma_attr =
2757 port->fp_fca_tran->fca_dma_attr;
2758 info->port_resp_dma_attr =
2759 port->fp_fca_tran->fca_dma_fcip_rsp_attr;
2760 } else {
2761 info->port_cmd_dma_attr = info->port_data_dma_attr =
2762 info->port_resp_dma_attr =
2763 port->fp_fca_tran->fca_dma_attr; /* default */
2764 }
2765 }
2766
2767 static int
2768 fctl_pre_detach(fc_ulp_ports_t *ulp_port, fc_detach_cmd_t cmd)
2769 {
2770 int rval = FC_SUCCESS;
2771
2772 mutex_enter(&ulp_port->port_mutex);
2773
2774 switch (cmd) {
2775 case FC_CMD_DETACH:
2776 if ((ulp_port->port_dstate & ULP_PORT_ATTACH) == 0) {
2777 rval = FC_FAILURE;
2778 }
2779 break;
2780
2781 case FC_CMD_SUSPEND:
2782 if (!(ulp_port->port_dstate & ULP_PORT_ATTACH) ||
2783 ulp_port->port_dstate & ULP_PORT_SUSPEND) {
2784 rval = FC_FAILURE;
2785 }
2786 break;
2787
2788 case FC_CMD_POWER_DOWN:
2789 if (!(ulp_port->port_dstate & ULP_PORT_ATTACH) ||
2790 ulp_port->port_dstate & ULP_PORT_POWER_DOWN) {
2791 rval = FC_FAILURE;
2792 }
2793 break;
2794 }
2795
2796 if (rval == FC_SUCCESS) {
2797 ulp_port->port_dstate |= ULP_PORT_BUSY;
2798 }
2799 mutex_exit(&ulp_port->port_mutex);
2800
2801 return (rval);
2802 }
2803
2804
2805 static void
2806 fctl_post_detach(fc_ulp_module_t *mod, fc_ulp_ports_t *ulp_port,
2807 fc_detach_cmd_t cmd, int rval)
2808 {
2809 ASSERT(cmd == FC_CMD_DETACH || cmd == FC_CMD_SUSPEND ||
2810 cmd == FC_CMD_POWER_DOWN);
2811
2812 mutex_enter(&ulp_port->port_mutex);
2813 ulp_port->port_dstate &= ~ULP_PORT_BUSY;
2814
2815 if (rval != FC_SUCCESS) {
2816 caddr_t op;
2817 fc_local_port_t *port = ulp_port->port_handle;
2818
2819 mutex_exit(&ulp_port->port_mutex);
2820
2821 switch (cmd) {
2822 case FC_CMD_DETACH:
2823 op = "detach";
2824 break;
2825
2826 case FC_CMD_SUSPEND:
2827 op = "suspend";
2828 break;
2829
2830 case FC_CMD_POWER_DOWN:
2831 op = "power down";
2832 break;
2833 }
2834
2835 cmn_err(CE_WARN, "!fctl(%d): %s failed for %s",
2836 port->fp_instance, op, mod->mod_info->ulp_name);
2837
2838 return;
2839 }
2840
2841 switch (cmd) {
2842 case FC_CMD_DETACH:
2843 ulp_port->port_dstate &= ~ULP_PORT_ATTACH;
2844 break;
2845
2846 case FC_CMD_SUSPEND:
2847 ulp_port->port_dstate |= ULP_PORT_SUSPEND;
2848 break;
2849
2850 case FC_CMD_POWER_DOWN:
2851 ulp_port->port_dstate |= ULP_PORT_POWER_DOWN;
2852 break;
2853 }
2854 mutex_exit(&ulp_port->port_mutex);
2855 }
2856
2857
2858 static fc_ulp_ports_t *
2859 fctl_add_ulp_port(fc_ulp_module_t *ulp_module, fc_local_port_t *port_handle,
2860 int sleep)
2861 {
2862 fc_ulp_ports_t *last;
2863 fc_ulp_ports_t *next;
2864 fc_ulp_ports_t *new;
2865
2866 ASSERT(RW_READ_HELD(&fctl_ulp_lock));
2867 ASSERT(RW_WRITE_HELD(&fctl_mod_ports_lock));
2868
2869 last = NULL;
2870 next = ulp_module->mod_ports;
2871
2872 while (next != NULL) {
2873 last = next;
2874 next = next->port_next;
2875 }
2876
2877 new = fctl_alloc_ulp_port(sleep);
2878 if (new == NULL) {
2879 return (new);
2880 }
2881
2882 new->port_handle = port_handle;
2883 if (last == NULL) {
2884 ulp_module->mod_ports = new;
2885 } else {
2886 last->port_next = new;
2887 }
2888
2889 return (new);
2890 }
2891
2892
2893 static fc_ulp_ports_t *
2894 fctl_alloc_ulp_port(int sleep)
2895 {
2896 fc_ulp_ports_t *new;
2897
2898 new = kmem_zalloc(sizeof (*new), sleep);
2899 if (new == NULL) {
2900 return (new);
2901 }
2902 mutex_init(&new->port_mutex, NULL, MUTEX_DRIVER, NULL);
2903
2904 return (new);
2905 }
2906
2907
2908 static int
2909 fctl_remove_ulp_port(struct ulp_module *ulp_module,
2910 fc_local_port_t *port_handle)
2911 {
2912 fc_ulp_ports_t *last;
2913 fc_ulp_ports_t *next;
2914
2915 ASSERT(RW_WRITE_HELD(&fctl_ulp_lock));
2916 ASSERT(RW_WRITE_HELD(&fctl_mod_ports_lock));
2917
2918 last = NULL;
2919 next = ulp_module->mod_ports;
2920
2921 while (next != NULL) {
2922 if (next->port_handle == port_handle) {
2923 if (next->port_dstate & ULP_PORT_ATTACH) {
2924 return (FC_FAILURE);
2925 }
2926 break;
2927 }
2928 last = next;
2929 next = next->port_next;
2930 }
2931
2932 if (next != NULL) {
2933 ASSERT((next->port_dstate & ULP_PORT_ATTACH) == 0);
2934
2935 if (last == NULL) {
2936 ulp_module->mod_ports = next->port_next;
2937 } else {
2938 last->port_next = next->port_next;
2939 }
2940 fctl_dealloc_ulp_port(next);
2941
2942 return (FC_SUCCESS);
2943 } else {
2944 return (FC_FAILURE);
2945 }
2946 }
2947
2948
2949 static void
2950 fctl_dealloc_ulp_port(fc_ulp_ports_t *next)
2951 {
2952 mutex_destroy(&next->port_mutex);
2953 kmem_free(next, sizeof (*next));
2954 }
2955
2956
2957 static fc_ulp_ports_t *
2958 fctl_get_ulp_port(struct ulp_module *ulp_module, fc_local_port_t *port_handle)
2959 {
2960 fc_ulp_ports_t *next;
2961
2962 ASSERT(RW_LOCK_HELD(&fctl_ulp_lock));
2963 ASSERT(RW_LOCK_HELD(&fctl_mod_ports_lock));
2964
2965 for (next = ulp_module->mod_ports; next != NULL;
2966 next = next->port_next) {
2967 if (next->port_handle == port_handle) {
2968 return (next);
2969 }
2970 }
2971
2972 return (NULL);
2973 }
2974
2975
2976 /*
2977 * Pass state change notfications on to registered ULPs.
2978 *
2979 * Can issue wakeups to client callers who might be waiting for completions
2980 * on other threads.
2981 *
2982 * Caution: will silently deallocate any fc_remote_port_t and/or
2983 * fc_remote_node_t structs it finds that are not in use.
2984 */
2985 void
2986 fctl_ulp_statec_cb(void *arg)
2987 {
2988 uint32_t s_id;
2989 uint32_t new_state;
2990 fc_local_port_t *port;
2991 fc_ulp_ports_t *ulp_port;
2992 fc_ulp_module_t *mod;
2993 fc_port_clist_t *clist = (fc_port_clist_t *)arg;
2994
2995 ASSERT(clist != NULL);
2996
2997 port = clist->clist_port;
2998
2999 mutex_enter(&port->fp_mutex);
3000 s_id = port->fp_port_id.port_id;
3001 mutex_exit(&port->fp_mutex);
3002
3003 switch (clist->clist_state) {
3004 case FC_STATE_ONLINE:
3005 new_state = FC_ULP_STATEC_ONLINE;
3006 break;
3007
3008 case FC_STATE_OFFLINE:
3009 if (clist->clist_len) {
3010 new_state = FC_ULP_STATEC_OFFLINE_TIMEOUT;
3011 } else {
3012 new_state = FC_ULP_STATEC_OFFLINE;
3013 }
3014 break;
3015
3016 default:
3017 new_state = FC_ULP_STATEC_DONT_CARE;
3018 break;
3019 }
3020
3021 #ifdef DEBUG
3022 /*
3023 * sanity check for presence of OLD devices in the hash lists
3024 */
3025 if (clist->clist_size) {
3026 int count;
3027 fc_remote_port_t *pd;
3028
3029 ASSERT(clist->clist_map != NULL);
3030 for (count = 0; count < clist->clist_len; count++) {
3031 if (clist->clist_map[count].map_state ==
3032 PORT_DEVICE_INVALID) {
3033 la_wwn_t pwwn;
3034 fc_portid_t d_id;
3035
3036 pd = clist->clist_map[count].map_pd;
3037 if (pd != NULL) {
3038 mutex_enter(&pd->pd_mutex);
3039 pwwn = pd->pd_port_name;
3040 d_id = pd->pd_port_id;
3041 mutex_exit(&pd->pd_mutex);
3042
3043 pd = fctl_get_remote_port_by_pwwn(port,
3044 &pwwn);
3045
3046 ASSERT(pd != clist->clist_map[count].
3047 map_pd);
3048
3049 pd = fctl_get_remote_port_by_did(port,
3050 d_id.port_id);
3051 ASSERT(pd != clist->clist_map[count].
3052 map_pd);
3053 }
3054 }
3055 }
3056 }
3057 #endif
3058
3059 /*
3060 * Check for duplicate map entries
3061 */
3062 if (clist->clist_size) {
3063 int count;
3064 fc_remote_port_t *pd1, *pd2;
3065
3066 ASSERT(clist->clist_map != NULL);
3067 for (count = 0; count < clist->clist_len-1; count++) {
3068 int count2;
3069
3070 pd1 = clist->clist_map[count].map_pd;
3071 if (pd1 == NULL) {
3072 continue;
3073 }
3074
3075 for (count2 = count+1;
3076 count2 < clist->clist_len;
3077 count2++) {
3078
3079 pd2 = clist->clist_map[count2].map_pd;
3080 if (pd2 == NULL) {
3081 continue;
3082 }
3083
3084 if (pd1 == pd2) {
3085 clist->clist_map[count].map_flags |=
3086 PORT_DEVICE_DUPLICATE_MAP_ENTRY;
3087 break;
3088 }
3089 }
3090 }
3091 }
3092
3093
3094 rw_enter(&fctl_ulp_lock, RW_READER);
3095 for (mod = fctl_ulp_modules; mod; mod = mod->mod_next) {
3096 rw_enter(&fctl_mod_ports_lock, RW_READER);
3097 ulp_port = fctl_get_ulp_port(mod, port);
3098 rw_exit(&fctl_mod_ports_lock);
3099
3100 if (ulp_port == NULL) {
3101 continue;
3102 }
3103
3104 mutex_enter(&ulp_port->port_mutex);
3105 if (FCTL_DISALLOW_CALLBACKS(ulp_port->port_dstate)) {
3106 mutex_exit(&ulp_port->port_mutex);
3107 continue;
3108 }
3109
3110 switch (ulp_port->port_statec) {
3111 case FC_ULP_STATEC_DONT_CARE:
3112 if (ulp_port->port_statec != new_state) {
3113 ulp_port->port_statec = new_state;
3114 }
3115 break;
3116
3117 case FC_ULP_STATEC_ONLINE:
3118 case FC_ULP_STATEC_OFFLINE:
3119 if (ulp_port->port_statec == new_state) {
3120 mutex_exit(&ulp_port->port_mutex);
3121 continue;
3122 }
3123 ulp_port->port_statec = new_state;
3124 break;
3125
3126 case FC_ULP_STATEC_OFFLINE_TIMEOUT:
3127 if (ulp_port->port_statec == new_state ||
3128 new_state == FC_ULP_STATEC_OFFLINE) {
3129 mutex_exit(&ulp_port->port_mutex);
3130 continue;
3131 }
3132 ulp_port->port_statec = new_state;
3133 break;
3134
3135 default:
3136 ASSERT(0);
3137 break;
3138 }
3139
3140 mod->mod_info->ulp_statec_callback(
3141 mod->mod_info->ulp_handle, (opaque_t)port,
3142 clist->clist_state, clist->clist_flags,
3143 clist->clist_map, clist->clist_len, s_id);
3144
3145 mutex_exit(&ulp_port->port_mutex);
3146 }
3147 rw_exit(&fctl_ulp_lock);
3148
3149 if (clist->clist_size) {
3150 int count;
3151 fc_remote_node_t *node;
3152 fc_remote_port_t *pd;
3153
3154 ASSERT(clist->clist_map != NULL);
3155 for (count = 0; count < clist->clist_len; count++) {
3156
3157 if ((pd = clist->clist_map[count].map_pd) == NULL) {
3158 continue;
3159 }
3160
3161 mutex_enter(&pd->pd_mutex);
3162
3163 pd->pd_ref_count--;
3164 ASSERT(pd->pd_ref_count >= 0);
3165
3166 if (clist->clist_map[count].map_state !=
3167 PORT_DEVICE_INVALID) {
3168 mutex_exit(&pd->pd_mutex);
3169 continue;
3170 }
3171
3172 node = pd->pd_remote_nodep;
3173 pd->pd_aux_flags &= ~PD_GIVEN_TO_ULPS;
3174
3175 mutex_exit(&pd->pd_mutex);
3176
3177 /*
3178 * This fc_remote_port_t is no longer referenced
3179 * by any ULPs. Deallocate it if its pd_ref_count
3180 * has reached zero.
3181 */
3182 if ((fctl_destroy_remote_port(port, pd) == 0) &&
3183 (node != NULL)) {
3184 fctl_destroy_remote_node(node);
3185 }
3186 }
3187
3188 kmem_free(clist->clist_map,
3189 sizeof (*(clist->clist_map)) * clist->clist_size);
3190 }
3191
3192 if (clist->clist_wait) {
3193 mutex_enter(&clist->clist_mutex);
3194 clist->clist_wait = 0;
3195 cv_signal(&clist->clist_cv);
3196 mutex_exit(&clist->clist_mutex);
3197 } else {
3198 kmem_free(clist, sizeof (*clist));
3199 }
3200 }
3201
3202
3203 /*
3204 * Allocate an fc_remote_node_t struct to represent a remote node for the
3205 * given nwwn. This will also add the nwwn to the global nwwn table.
3206 *
3207 * Returns a pointer to the newly-allocated struct. Returns NULL if
3208 * the kmem_zalloc fails or if the enlist_wwn attempt fails.
3209 */
3210 fc_remote_node_t *
3211 fctl_create_remote_node(la_wwn_t *nwwn, int sleep)
3212 {
3213 fc_remote_node_t *rnodep;
3214
3215 if ((rnodep = kmem_zalloc(sizeof (*rnodep), sleep)) == NULL) {
3216 return (NULL);
3217 }
3218
3219 mutex_init(&rnodep->fd_mutex, NULL, MUTEX_DRIVER, NULL);
3220
3221 rnodep->fd_node_name = *nwwn;
3222 rnodep->fd_flags = FC_REMOTE_NODE_VALID;
3223 rnodep->fd_numports = 1;
3224
3225 if (fctl_enlist_nwwn_table(rnodep, sleep) != FC_SUCCESS) {
3226 mutex_destroy(&rnodep->fd_mutex);
3227 kmem_free(rnodep, sizeof (*rnodep));
3228 return (NULL);
3229 }
3230
3231 return (rnodep);
3232 }
3233
3234 /*
3235 * Deconstruct and free the given fc_remote_node_t struct (remote node struct).
3236 * Silently skips the deconstruct/free if there are any fc_remote_port_t
3237 * (remote port device) structs still referenced by the given
3238 * fc_remote_node_t struct.
3239 */
3240 void
3241 fctl_destroy_remote_node(fc_remote_node_t *rnodep)
3242 {
3243 mutex_enter(&rnodep->fd_mutex);
3244
3245 /*
3246 * Look at the count and linked list of of remote ports
3247 * (fc_remote_port_t structs); bail if these indicate that
3248 * given fc_remote_node_t may be in use.
3249 */
3250 if (rnodep->fd_numports != 0 || rnodep->fd_portlistp) {
3251 mutex_exit(&rnodep->fd_mutex);
3252 return;
3253 }
3254
3255 mutex_exit(&rnodep->fd_mutex);
3256
3257 mutex_destroy(&rnodep->fd_mutex);
3258 kmem_free(rnodep, sizeof (*rnodep));
3259 }
3260
3261
3262 /*
3263 * Add the given fc_remote_node_t to the global fctl_nwwn_hash_table[]. This
3264 * uses the nwwn in the fd_node_name.raw_wwn of the given struct.
3265 * This only fails if the kmem_zalloc fails. This does not check for a
3266 * unique or pre-existing nwwn in the fctl_nwwn_hash_table[].
3267 * This is only called from fctl_create_remote_node().
3268 */
3269 int
3270 fctl_enlist_nwwn_table(fc_remote_node_t *rnodep, int sleep)
3271 {
3272 int index;
3273 fctl_nwwn_elem_t *new;
3274 fctl_nwwn_list_t *head;
3275
3276 ASSERT(!MUTEX_HELD(&rnodep->fd_mutex));
3277
3278 if ((new = kmem_zalloc(sizeof (*new), sleep)) == NULL) {
3279 return (FC_FAILURE);
3280 }
3281
3282 mutex_enter(&fctl_nwwn_hash_mutex);
3283 new->fne_nodep = rnodep;
3284
3285 mutex_enter(&rnodep->fd_mutex);
3286 ASSERT(fctl_is_wwn_zero(&rnodep->fd_node_name) == FC_FAILURE);
3287 index = HASH_FUNC(WWN_HASH_KEY(rnodep->fd_node_name.raw_wwn),
3288 fctl_nwwn_table_size);
3289 mutex_exit(&rnodep->fd_mutex);
3290
3291 head = &fctl_nwwn_hash_table[index];
3292
3293 /* Link it in at the head of the hash list */
3294 new->fne_nextp = head->fnl_headp;
3295 head->fnl_headp = new;
3296
3297 mutex_exit(&fctl_nwwn_hash_mutex);
3298
3299 return (FC_SUCCESS);
3300 }
3301
3302
3303 /*
3304 * Remove the given fc_remote_node_t from the global fctl_nwwn_hash_table[].
3305 * This uses the nwwn in the fd_node_name.raw_wwn of the given struct.
3306 */
3307 void
3308 fctl_delist_nwwn_table(fc_remote_node_t *rnodep)
3309 {
3310 int index;
3311 fctl_nwwn_list_t *head;
3312 fctl_nwwn_elem_t *elem;
3313 fctl_nwwn_elem_t *prev;
3314
3315 ASSERT(MUTEX_HELD(&fctl_nwwn_hash_mutex));
3316 ASSERT(MUTEX_HELD(&rnodep->fd_mutex));
3317
3318 index = HASH_FUNC(WWN_HASH_KEY(rnodep->fd_node_name.raw_wwn),
3319 fctl_nwwn_table_size);
3320
3321 head = &fctl_nwwn_hash_table[index];
3322 elem = head->fnl_headp;
3323 prev = NULL;
3324
3325 while (elem != NULL) {
3326 if (elem->fne_nodep == rnodep) {
3327 /*
3328 * Found it -- unlink it from the list & decrement
3329 * the count for the hash chain.
3330 */
3331 if (prev == NULL) {
3332 head->fnl_headp = elem->fne_nextp;
3333 } else {
3334 prev->fne_nextp = elem->fne_nextp;
3335 }
3336 break;
3337 }
3338 prev = elem;
3339 elem = elem->fne_nextp;
3340 }
3341
3342 if (elem != NULL) {
3343 kmem_free(elem, sizeof (*elem));
3344 }
3345 }
3346
3347
3348 /*
3349 * Returns a reference to an fc_remote_node_t struct for the given node_wwn.
3350 * Looks in the global fctl_nwwn_hash_table[]. Identical to the
3351 * fctl_lock_remote_node_by_nwwn() function, except that this does NOT increment
3352 * the fc_count reference count in the f_device_t before returning.
3353 *
3354 * This function is called by: fctl_create_remote_port_t().
3355 *
3356 * OLD COMMENT:
3357 * Note: The calling thread needs to make sure it isn't holding any device
3358 * mutex (more so the fc_remote_node_t that could potentially have this wwn).
3359 */
3360 fc_remote_node_t *
3361 fctl_get_remote_node_by_nwwn(la_wwn_t *node_wwn)
3362 {
3363 int index;
3364 fctl_nwwn_elem_t *elem;
3365 fc_remote_node_t *next;
3366 fc_remote_node_t *rnodep = NULL;
3367
3368 index = HASH_FUNC(WWN_HASH_KEY(node_wwn->raw_wwn),
3369 fctl_nwwn_table_size);
3370 ASSERT(index >= 0 && index < fctl_nwwn_table_size);
3371
3372 mutex_enter(&fctl_nwwn_hash_mutex);
3373 elem = fctl_nwwn_hash_table[index].fnl_headp;
3374 while (elem != NULL) {
3375 next = elem->fne_nodep;
3376 if (next != NULL) {
3377 mutex_enter(&next->fd_mutex);
3378 if (fctl_wwn_cmp(node_wwn, &next->fd_node_name) == 0) {
3379 rnodep = next;
3380 mutex_exit(&next->fd_mutex);
3381 break;
3382 }
3383 mutex_exit(&next->fd_mutex);
3384 }
3385 elem = elem->fne_nextp;
3386 }
3387 mutex_exit(&fctl_nwwn_hash_mutex);
3388
3389 return (rnodep);
3390 }
3391
3392
3393 /*
3394 * Returns a reference to an fc_remote_node_t struct for the given node_wwn.
3395 * Looks in the global fctl_nwwn_hash_table[]. Increments the fd_numports
3396 * reference count in the f_device_t before returning.
3397 *
3398 * This function is only called by fctl_create_remote_port_t().
3399 */
3400 fc_remote_node_t *
3401 fctl_lock_remote_node_by_nwwn(la_wwn_t *node_wwn)
3402 {
3403 int index;
3404 fctl_nwwn_elem_t *elem;
3405 fc_remote_node_t *next;
3406 fc_remote_node_t *rnodep = NULL;
3407
3408 index = HASH_FUNC(WWN_HASH_KEY(node_wwn->raw_wwn),
3409 fctl_nwwn_table_size);
3410 ASSERT(index >= 0 && index < fctl_nwwn_table_size);
3411
3412 mutex_enter(&fctl_nwwn_hash_mutex);
3413 elem = fctl_nwwn_hash_table[index].fnl_headp;
3414 while (elem != NULL) {
3415 next = elem->fne_nodep;
3416 if (next != NULL) {
3417 mutex_enter(&next->fd_mutex);
3418 if (fctl_wwn_cmp(node_wwn, &next->fd_node_name) == 0) {
3419 rnodep = next;
3420 rnodep->fd_numports++;
3421 mutex_exit(&next->fd_mutex);
3422 break;
3423 }
3424 mutex_exit(&next->fd_mutex);
3425 }
3426 elem = elem->fne_nextp;
3427 }
3428 mutex_exit(&fctl_nwwn_hash_mutex);
3429
3430 return (rnodep);
3431 }
3432
3433
3434 /*
3435 * Allocate and initialize an fc_remote_port_t struct & returns a pointer to
3436 * the newly allocated struct. Only fails if the kmem_zalloc() fails.
3437 */
3438 fc_remote_port_t *
3439 fctl_alloc_remote_port(fc_local_port_t *port, la_wwn_t *port_wwn,
3440 uint32_t d_id, uchar_t recepient, int sleep)
3441 {
3442 fc_remote_port_t *pd;
3443
3444 ASSERT(MUTEX_HELD(&port->fp_mutex));
3445 ASSERT(FC_IS_REAL_DEVICE(d_id));
3446
3447 if ((pd = kmem_zalloc(sizeof (*pd), sleep)) == NULL) {
3448 return (NULL);
3449 }
3450 fctl_tc_constructor(&pd->pd_logo_tc, FC_LOGO_TOLERANCE_LIMIT,
3451 FC_LOGO_TOLERANCE_TIME_LIMIT);
3452
3453 mutex_init(&pd->pd_mutex, NULL, MUTEX_DRIVER, NULL);
3454
3455 pd->pd_port_id.port_id = d_id;
3456 pd->pd_port_name = *port_wwn;
3457 pd->pd_port = port;
3458 pd->pd_state = PORT_DEVICE_VALID;
3459 pd->pd_type = PORT_DEVICE_NEW;
3460 pd->pd_recepient = recepient;
3461
3462 return (pd);
3463 }
3464
3465
3466 /*
3467 * Deconstruct and free the given fc_remote_port_t struct (unconditionally).
3468 */
3469 void
3470 fctl_dealloc_remote_port(fc_remote_port_t *pd)
3471 {
3472 ASSERT(!MUTEX_HELD(&pd->pd_mutex));
3473
3474 fctl_tc_destructor(&pd->pd_logo_tc);
3475 mutex_destroy(&pd->pd_mutex);
3476 kmem_free(pd, sizeof (*pd));
3477 }
3478
3479 /*
3480 * Add the given fc_remote_port_t onto the linked list of remote port
3481 * devices associated with the given fc_remote_node_t. Does NOT add the
3482 * fc_remote_port_t to the list if already exists on the list.
3483 */
3484 void
3485 fctl_link_remote_port_to_remote_node(fc_remote_node_t *rnodep,
3486 fc_remote_port_t *pd)
3487 {
3488 fc_remote_port_t *last;
3489 fc_remote_port_t *ports;
3490
3491 mutex_enter(&rnodep->fd_mutex);
3492
3493 last = NULL;
3494 for (ports = rnodep->fd_portlistp; ports != NULL;
3495 ports = ports->pd_port_next) {
3496 if (ports == pd) {
3497 /*
3498 * The given fc_remote_port_t is already on the linked
3499 * list chain for the given remote node, so bail now.
3500 */
3501 mutex_exit(&rnodep->fd_mutex);
3502 return;
3503 }
3504 last = ports;
3505 }
3506
3507 /* Add the fc_remote_port_t to the tail of the linked list */
3508 if (last != NULL) {
3509 last->pd_port_next = pd;
3510 } else {
3511 rnodep->fd_portlistp = pd;
3512 }
3513 pd->pd_port_next = NULL;
3514
3515 /*
3516 * Link the fc_remote_port_t back to the associated fc_remote_node_t.
3517 */
3518 mutex_enter(&pd->pd_mutex);
3519 pd->pd_remote_nodep = rnodep;
3520 mutex_exit(&pd->pd_mutex);
3521
3522 mutex_exit(&rnodep->fd_mutex);
3523 }
3524
3525
3526 /*
3527 * Remove the specified fc_remote_port_t from the linked list of remote ports
3528 * for the given fc_remote_node_t.
3529 *
3530 * Returns a count of the _remaining_ fc_remote_port_t structs on the linked
3531 * list of the fc_remote_node_t.
3532 *
3533 * The fd_numports on the given fc_remote_node_t is decremented, and if
3534 * it hits zero then this function also removes the fc_remote_node_t from the
3535 * global fctl_nwwn_hash_table[]. This appears to be the ONLY WAY that entries
3536 * are removed from the fctl_nwwn_hash_table[].
3537 */
3538 int
3539 fctl_unlink_remote_port_from_remote_node(fc_remote_node_t *rnodep,
3540 fc_remote_port_t *pd)
3541 {
3542 int rcount = 0;
3543 fc_remote_port_t *last;
3544 fc_remote_port_t *ports;
3545
3546 ASSERT(!MUTEX_HELD(&rnodep->fd_mutex));
3547 ASSERT(!MUTEX_HELD(&pd->pd_mutex));
3548
3549 last = NULL;
3550
3551 mutex_enter(&fctl_nwwn_hash_mutex);
3552
3553 mutex_enter(&rnodep->fd_mutex);
3554
3555 /*
3556 * Go thru the linked list of fc_remote_port_t structs for the given
3557 * fc_remote_node_t; try to find the specified fc_remote_port_t (pd).
3558 */
3559 ports = rnodep->fd_portlistp;
3560 while (ports != NULL) {
3561 if (ports == pd) {
3562 break; /* Found the requested fc_remote_port_t */
3563 }
3564 last = ports;
3565 ports = ports->pd_port_next;
3566 }
3567
3568 if (ports) {
3569 rcount = --rnodep->fd_numports;
3570 if (rcount == 0) {
3571 /* Note: this is only ever called from here */
3572 fctl_delist_nwwn_table(rnodep);
3573 }
3574 if (last) {
3575 last->pd_port_next = pd->pd_port_next;
3576 } else {
3577 rnodep->fd_portlistp = pd->pd_port_next;
3578 }
3579 mutex_enter(&pd->pd_mutex);
3580 pd->pd_remote_nodep = NULL;
3581 mutex_exit(&pd->pd_mutex);
3582 }
3583
3584 pd->pd_port_next = NULL;
3585
3586 mutex_exit(&rnodep->fd_mutex);
3587 mutex_exit(&fctl_nwwn_hash_mutex);
3588
3589 return (rcount);
3590 }
3591
3592
3593 /*
3594 * Add the given fc_remote_port_t struct to the d_id table in the given
3595 * fc_local_port_t struct. Hashes based upon the pd->pd_port_id.port_id in the
3596 * fc_remote_port_t.
3597 *
3598 * No memory allocs are required, so this never fails, but it does use the
3599 * (pd->pd_aux_flags & PD_IN_DID_QUEUE) to keep duplicates off the list.
3600 * (There does not seem to be a way to tell the caller that a duplicate
3601 * exists.)
3602 */
3603 void
3604 fctl_enlist_did_table(fc_local_port_t *port, fc_remote_port_t *pd)
3605 {
3606 struct d_id_hash *head;
3607
3608 ASSERT(MUTEX_HELD(&port->fp_mutex));
3609 ASSERT(MUTEX_HELD(&pd->pd_mutex));
3610
3611 if (pd->pd_aux_flags & PD_IN_DID_QUEUE) {
3612 return;
3613 }
3614
3615 head = &port->fp_did_table[D_ID_HASH_FUNC(pd->pd_port_id.port_id,
3616 did_table_size)];
3617
3618 #ifdef DEBUG
3619 {
3620 int index;
3621 fc_remote_port_t *tmp_pd;
3622 struct d_id_hash *tmp_head;
3623
3624 /*
3625 * Search down in each bucket for a duplicate pd
3626 * Also search for duplicate D_IDs
3627 * This DEBUG code will force an ASSERT if a duplicate
3628 * is ever found.
3629 */
3630 for (index = 0; index < did_table_size; index++) {
3631 tmp_head = &port->fp_did_table[index];
3632
3633 tmp_pd = tmp_head->d_id_head;
3634 while (tmp_pd != NULL) {
3635 ASSERT(tmp_pd != pd);
3636
3637 if (tmp_pd->pd_state != PORT_DEVICE_INVALID &&
3638 tmp_pd->pd_type != PORT_DEVICE_OLD) {
3639 ASSERT(tmp_pd->pd_port_id.port_id !=
3640 pd->pd_port_id.port_id);
3641 }
3642
3643 tmp_pd = tmp_pd->pd_did_hnext;
3644 }
3645 }
3646 }
3647
3648 bzero(pd->pd_d_stack, sizeof (pd->pd_d_stack));
3649 pd->pd_d_depth = getpcstack(pd->pd_d_stack, FC_STACK_DEPTH);
3650 #endif
3651
3652 pd->pd_did_hnext = head->d_id_head;
3653 head->d_id_head = pd;
3654
3655 pd->pd_aux_flags |= PD_IN_DID_QUEUE;
3656 head->d_id_count++;
3657 }
3658
3659
3660 /*
3661 * Remove the given fc_remote_port_t struct from the d_id table in the given
3662 * fc_local_port_t struct. Hashes based upon the pd->pd_port_id.port_id in the
3663 * fc_remote_port_t.
3664 *
3665 * Does nothing if the requested fc_remote_port_t was not found.
3666 */
3667 void
3668 fctl_delist_did_table(fc_local_port_t *port, fc_remote_port_t *pd)
3669 {
3670 uint32_t d_id;
3671 struct d_id_hash *head;
3672 fc_remote_port_t *pd_next;
3673 fc_remote_port_t *last;
3674
3675 ASSERT(MUTEX_HELD(&port->fp_mutex));
3676 ASSERT(MUTEX_HELD(&pd->pd_mutex));
3677
3678 d_id = pd->pd_port_id.port_id;
3679 head = &port->fp_did_table[D_ID_HASH_FUNC(d_id, did_table_size)];
3680
3681 pd_next = head->d_id_head;
3682 last = NULL;
3683 while (pd_next != NULL) {
3684 if (pd == pd_next) {
3685 break; /* Found the given fc_remote_port_t */
3686 }
3687 last = pd_next;
3688 pd_next = pd_next->pd_did_hnext;
3689 }
3690
3691 if (pd_next) {
3692 /*
3693 * Found the given fc_remote_port_t; now remove it from the
3694 * d_id list.
3695 */
3696 head->d_id_count--;
3697 if (last == NULL) {
3698 head->d_id_head = pd->pd_did_hnext;
3699 } else {
3700 last->pd_did_hnext = pd->pd_did_hnext;
3701 }
3702 pd->pd_aux_flags &= ~PD_IN_DID_QUEUE;
3703 pd->pd_did_hnext = NULL;
3704 }
3705 }
3706
3707
3708 /*
3709 * Add the given fc_remote_port_t struct to the pwwn table in the given
3710 * fc_local_port_t struct. Hashes based upon the pd->pd_port_name.raw_wwn
3711 * in the fc_remote_port_t.
3712 *
3713 * No memory allocs are required, so this never fails.
3714 */
3715 void
3716 fctl_enlist_pwwn_table(fc_local_port_t *port, fc_remote_port_t *pd)
3717 {
3718 int index;
3719 struct pwwn_hash *head;
3720
3721 ASSERT(MUTEX_HELD(&port->fp_mutex));
3722 ASSERT(MUTEX_HELD(&pd->pd_mutex));
3723
3724 ASSERT(fctl_is_wwn_zero(&pd->pd_port_name) == FC_FAILURE);
3725
3726 index = HASH_FUNC(WWN_HASH_KEY(pd->pd_port_name.raw_wwn),
3727 pwwn_table_size);
3728
3729 head = &port->fp_pwwn_table[index];
3730
3731 #ifdef DEBUG
3732 {
3733 int index;
3734 fc_remote_port_t *tmp_pd;
3735 struct pwwn_hash *tmp_head;
3736
3737 /*
3738 * Search down in each bucket for a duplicate pd
3739 * Search also for a duplicate WWN
3740 * Throw an ASSERT if any duplicate is found.
3741 */
3742 for (index = 0; index < pwwn_table_size; index++) {
3743 tmp_head = &port->fp_pwwn_table[index];
3744
3745 tmp_pd = tmp_head->pwwn_head;
3746 while (tmp_pd != NULL) {
3747 ASSERT(tmp_pd != pd);
3748
3749 if (tmp_pd->pd_state != PORT_DEVICE_INVALID &&
3750 tmp_pd->pd_type != PORT_DEVICE_OLD) {
3751 ASSERT(fctl_wwn_cmp(
3752 &tmp_pd->pd_port_name,
3753 &pd->pd_port_name) != 0);
3754 }
3755
3756 tmp_pd = tmp_pd->pd_wwn_hnext;
3757 }
3758 }
3759 }
3760
3761 bzero(pd->pd_w_stack, sizeof (pd->pd_w_stack));
3762 pd->pd_w_depth = getpcstack(pd->pd_w_stack, FC_STACK_DEPTH);
3763 #endif /* DEBUG */
3764
3765 pd->pd_wwn_hnext = head->pwwn_head;
3766 head->pwwn_head = pd;
3767
3768 head->pwwn_count++;
3769 /*
3770 * Make sure we tie fp_dev_count to the size of the
3771 * pwwn_table
3772 */
3773 port->fp_dev_count++;
3774 }
3775
3776
3777 /*
3778 * Remove the given fc_remote_port_t struct from the pwwn table in the given
3779 * fc_local_port_t struct. Hashes based upon the pd->pd_port_name.raw_wwn
3780 * in the fc_remote_port_t.
3781 *
3782 * Does nothing if the requested fc_remote_port_t was not found.
3783 */
3784 void
3785 fctl_delist_pwwn_table(fc_local_port_t *port, fc_remote_port_t *pd)
3786 {
3787 int index;
3788 la_wwn_t pwwn;
3789 struct pwwn_hash *head;
3790 fc_remote_port_t *pd_next;
3791 fc_remote_port_t *last;
3792
3793 ASSERT(MUTEX_HELD(&port->fp_mutex));
3794 ASSERT(MUTEX_HELD(&pd->pd_mutex));
3795
3796 pwwn = pd->pd_port_name;
3797 index = HASH_FUNC(WWN_HASH_KEY(pwwn.raw_wwn), pwwn_table_size);
3798
3799 head = &port->fp_pwwn_table[index];
3800
3801 last = NULL;
3802 pd_next = head->pwwn_head;
3803 while (pd_next != NULL) {
3804 if (pd_next == pd) {
3805 break; /* Found the given fc_remote_port_t */
3806 }
3807 last = pd_next;
3808 pd_next = pd_next->pd_wwn_hnext;
3809 }
3810
3811 if (pd_next) {
3812 /*
3813 * Found the given fc_remote_port_t; now remove it from the
3814 * pwwn list.
3815 */
3816 head->pwwn_count--;
3817 /*
3818 * Make sure we tie fp_dev_count to the size of the
3819 * pwwn_table
3820 */
3821 port->fp_dev_count--;
3822 if (last == NULL) {
3823 head->pwwn_head = pd->pd_wwn_hnext;
3824 } else {
3825 last->pd_wwn_hnext = pd->pd_wwn_hnext;
3826 }
3827 pd->pd_wwn_hnext = NULL;
3828 }
3829 }
3830
3831
3832 /*
3833 * Looks in the d_id table of the specified fc_local_port_t for the
3834 * fc_remote_port_t that matches the given d_id. Hashes based upon
3835 * the given d_id.
3836 * Returns a pointer to the fc_remote_port_t struct, but does not update any
3837 * reference counts or otherwise indicate that the fc_remote_port_t is in
3838 * use.
3839 */
3840 fc_remote_port_t *
3841 fctl_get_remote_port_by_did(fc_local_port_t *port, uint32_t d_id)
3842 {
3843 struct d_id_hash *head;
3844 fc_remote_port_t *pd;
3845
3846 ASSERT(!MUTEX_HELD(&port->fp_mutex));
3847
3848 mutex_enter(&port->fp_mutex);
3849
3850 head = &port->fp_did_table[D_ID_HASH_FUNC(d_id, did_table_size)];
3851
3852 pd = head->d_id_head;
3853 while (pd != NULL) {
3854 mutex_enter(&pd->pd_mutex);
3855 if (pd->pd_port_id.port_id == d_id) {
3856 /* Match found -- break out of the loop */
3857 mutex_exit(&pd->pd_mutex);
3858 break;
3859 }
3860 mutex_exit(&pd->pd_mutex);
3861 pd = pd->pd_did_hnext;
3862 }
3863
3864 mutex_exit(&port->fp_mutex);
3865
3866 return (pd);
3867 }
3868
3869
3870 void
3871 fc_ulp_hold_remote_port(opaque_t port_handle)
3872 {
3873 fc_remote_port_t *pd = port_handle;
3874
3875 mutex_enter(&pd->pd_mutex);
3876 pd->pd_ref_count++;
3877 mutex_exit(&pd->pd_mutex);
3878 }
3879
3880 /*
3881 * Looks in the d_id table of the specified fc_local_port_t for the
3882 * fc_remote_port_t that matches the given d_id. Hashes based upon
3883 * the given d_id. Returns a pointer to the fc_remote_port_t struct.
3884 *
3885 * Increments pd_ref_count in the fc_remote_port_t if the
3886 * fc_remote_port_t is found at the given d_id.
3887 *
3888 * The fc_remote_port_t is ignored (treated as non-existent) if either
3889 * its pd_state == PORT_DEVICE_INVALID _OR_ its pd_type == PORT_DEVICE_OLD.
3890 */
3891 fc_remote_port_t *
3892 fctl_hold_remote_port_by_did(fc_local_port_t *port, uint32_t d_id)
3893 {
3894 struct d_id_hash *head;
3895 fc_remote_port_t *pd;
3896
3897 ASSERT(!MUTEX_HELD(&port->fp_mutex));
3898
3899 mutex_enter(&port->fp_mutex);
3900
3901 head = &port->fp_did_table[D_ID_HASH_FUNC(d_id, did_table_size)];
3902
3903 pd = head->d_id_head;
3904 while (pd != NULL) {
3905 mutex_enter(&pd->pd_mutex);
3906 if (pd->pd_port_id.port_id == d_id && pd->pd_state !=
3907 PORT_DEVICE_INVALID && pd->pd_type != PORT_DEVICE_OLD) {
3908 ASSERT(pd->pd_ref_count >= 0);
3909 pd->pd_ref_count++;
3910 mutex_exit(&pd->pd_mutex);
3911 break;
3912 }
3913 mutex_exit(&pd->pd_mutex);
3914 pd = pd->pd_did_hnext;
3915 }
3916
3917 mutex_exit(&port->fp_mutex);
3918
3919 return (pd);
3920 }
3921
3922 /*
3923 * Looks in the pwwn table of the specified fc_local_port_t for the
3924 * fc_remote_port_t that matches the given pwwn. Hashes based upon the
3925 * given pwwn->raw_wwn. Returns a pointer to the fc_remote_port_t struct,
3926 * but does not update any reference counts or otherwise indicate that
3927 * the fc_remote_port_t is in use.
3928 */
3929 fc_remote_port_t *
3930 fctl_get_remote_port_by_pwwn(fc_local_port_t *port, la_wwn_t *pwwn)
3931 {
3932 int index;
3933 struct pwwn_hash *head;
3934 fc_remote_port_t *pd;
3935
3936 ASSERT(!MUTEX_HELD(&port->fp_mutex));
3937
3938 mutex_enter(&port->fp_mutex);
3939
3940 index = HASH_FUNC(WWN_HASH_KEY(pwwn->raw_wwn), pwwn_table_size);
3941 head = &port->fp_pwwn_table[index];
3942
3943 pd = head->pwwn_head;
3944 while (pd != NULL) {
3945 mutex_enter(&pd->pd_mutex);
3946 if (fctl_wwn_cmp(&pd->pd_port_name, pwwn) == 0) {
3947 mutex_exit(&pd->pd_mutex);
3948 break;
3949 }
3950 mutex_exit(&pd->pd_mutex);
3951 pd = pd->pd_wwn_hnext;
3952 }
3953
3954 mutex_exit(&port->fp_mutex);
3955
3956 return (pd);
3957 }
3958
3959
3960 /*
3961 * Basically the same as fctl_get_remote_port_by_pwwn(), but requires that
3962 * the caller already hold the fp_mutex in the fc_local_port_t struct.
3963 */
3964 fc_remote_port_t *
3965 fctl_get_remote_port_by_pwwn_mutex_held(fc_local_port_t *port, la_wwn_t *pwwn)
3966 {
3967 int index;
3968 struct pwwn_hash *head;
3969 fc_remote_port_t *pd;
3970
3971 ASSERT(MUTEX_HELD(&port->fp_mutex));
3972
3973 index = HASH_FUNC(WWN_HASH_KEY(pwwn->raw_wwn), pwwn_table_size);
3974 head = &port->fp_pwwn_table[index];
3975
3976 pd = head->pwwn_head;
3977 while (pd != NULL) {
3978 mutex_enter(&pd->pd_mutex);
3979 if (fctl_wwn_cmp(&pd->pd_port_name, pwwn) == 0) {
3980 mutex_exit(&pd->pd_mutex);
3981 break;
3982 }
3983 mutex_exit(&pd->pd_mutex);
3984 pd = pd->pd_wwn_hnext;
3985 }
3986
3987 return (pd);
3988 }
3989
3990
3991 /*
3992 * Looks in the pwwn table of the specified fc_local_port_t for the
3993 * fc_remote_port_t that matches the given d_id. Hashes based upon the
3994 * given pwwn->raw_wwn. Returns a pointer to the fc_remote_port_t struct.
3995 *
3996 * Increments pd_ref_count in the fc_remote_port_t if the
3997 * fc_remote_port_t is found at the given pwwn.
3998 *
3999 * The fc_remote_port_t is ignored (treated as non-existent) if either
4000 * its pd_state == PORT_DEVICE_INVALID _OR_ its pd_type == PORT_DEVICE_OLD.
4001 */
4002 fc_remote_port_t *
4003 fctl_hold_remote_port_by_pwwn(fc_local_port_t *port, la_wwn_t *pwwn)
4004 {
4005 int index;
4006 struct pwwn_hash *head;
4007 fc_remote_port_t *pd;
4008
4009 ASSERT(!MUTEX_HELD(&port->fp_mutex));
4010
4011 mutex_enter(&port->fp_mutex);
4012
4013 index = HASH_FUNC(WWN_HASH_KEY(pwwn->raw_wwn), pwwn_table_size);
4014 head = &port->fp_pwwn_table[index];
4015
4016 pd = head->pwwn_head;
4017 while (pd != NULL) {
4018 mutex_enter(&pd->pd_mutex);
4019 if (fctl_wwn_cmp(&pd->pd_port_name, pwwn) == 0 &&
4020 pd->pd_state != PORT_DEVICE_INVALID &&
4021 pd->pd_type != PORT_DEVICE_OLD) {
4022 ASSERT(pd->pd_ref_count >= 0);
4023 pd->pd_ref_count++;
4024 mutex_exit(&pd->pd_mutex);
4025 break;
4026 }
4027 mutex_exit(&pd->pd_mutex);
4028 pd = pd->pd_wwn_hnext;
4029 }
4030
4031 mutex_exit(&port->fp_mutex);
4032
4033 return (pd);
4034 }
4035
4036
4037 /*
4038 * Unconditionally decrement pd_ref_count in the given fc_remote_port_t
4039 * struct.
4040 *
4041 * If pd_ref_count reaches zero, then this function will see if the
4042 * fc_remote_port_t has been marked for deallocation. If so (and also if there
4043 * are no other potential operations in progress, as indicated by the
4044 * PD_ELS_IN_PROGRESS & PD_ELS_MARK settings in the pd_flags), then
4045 * fctl_destroy_remote_port_t() is called to deconstruct/free the given
4046 * fc_remote_port_t (which will also remove it from the d_id and pwwn tables
4047 * on the associated fc_local_port_t). If the associated fc_remote_node_t is no
4048 * longer in use, then it too is deconstructed/freed.
4049 */
4050 void
4051 fctl_release_remote_port(fc_remote_port_t *pd)
4052 {
4053 int remove = 0;
4054 fc_remote_node_t *node;
4055 fc_local_port_t *port;
4056
4057 mutex_enter(&pd->pd_mutex);
4058 port = pd->pd_port;
4059
4060 ASSERT(pd->pd_ref_count > 0);
4061 pd->pd_ref_count--;
4062 if (pd->pd_ref_count == 0 &&
4063 (pd->pd_aux_flags & PD_NEEDS_REMOVAL) &&
4064 (pd->pd_flags != PD_ELS_IN_PROGRESS) &&
4065 (pd->pd_flags != PD_ELS_MARK)) {
4066 remove = 1;
4067 pd->pd_aux_flags &= ~PD_NEEDS_REMOVAL;
4068 }
4069 node = pd->pd_remote_nodep;
4070 ASSERT(node != NULL);
4071
4072 mutex_exit(&pd->pd_mutex);
4073
4074 if (remove) {
4075 /*
4076 * The fc_remote_port_t struct has to go away now, so call the
4077 * cleanup function to get it off the various lists and remove
4078 * references to it in any other associated structs.
4079 */
4080 if (fctl_destroy_remote_port(port, pd) == 0) {
4081 /*
4082 * No more fc_remote_port_t references found in the
4083 * associated fc_remote_node_t, so deallocate the
4084 * fc_remote_node_t (if it even exists).
4085 */
4086 if (node) {
4087 fctl_destroy_remote_node(node);
4088 }
4089 }
4090 }
4091 }
4092
4093
4094 void
4095 fctl_fillout_map(fc_local_port_t *port, fc_portmap_t **map, uint32_t *len,
4096 int whole_map, int justcopy, int orphan)
4097 {
4098 int index;
4099 int listlen;
4100 int full_list;
4101 int initiator;
4102 uint32_t topology;
4103 struct pwwn_hash *head;
4104 fc_remote_port_t *pd;
4105 fc_remote_port_t *old_pd;
4106 fc_remote_port_t *last_pd;
4107 fc_portmap_t *listptr;
4108
4109 ASSERT(!MUTEX_HELD(&port->fp_mutex));
4110
4111 mutex_enter(&port->fp_mutex);
4112
4113 topology = port->fp_topology;
4114
4115 if (orphan) {
4116 ASSERT(!FC_IS_TOP_SWITCH(topology));
4117 }
4118
4119 for (full_list = listlen = index = 0;
4120 index < pwwn_table_size; index++) {
4121 head = &port->fp_pwwn_table[index];
4122 pd = head->pwwn_head;
4123 while (pd != NULL) {
4124 full_list++;
4125 mutex_enter(&pd->pd_mutex);
4126 if (pd->pd_type != PORT_DEVICE_NOCHANGE) {
4127 listlen++;
4128 }
4129 mutex_exit(&pd->pd_mutex);
4130 pd = pd->pd_wwn_hnext;
4131 }
4132 }
4133
4134 if (whole_map == 0) {
4135 if (listlen == 0 && *len == 0) {
4136 *map = NULL;
4137 *len = listlen;
4138 mutex_exit(&port->fp_mutex);
4139 return;
4140 }
4141 } else {
4142 if (full_list == 0 && *len == 0) {
4143 *map = NULL;
4144 *len = full_list;
4145 mutex_exit(&port->fp_mutex);
4146 return;
4147 }
4148 }
4149
4150 if (*len == 0) {
4151 ASSERT(*map == NULL);
4152 if (whole_map == 0) {
4153 listptr = *map = kmem_zalloc(
4154 sizeof (*listptr) * listlen, KM_SLEEP);
4155 *len = listlen;
4156 } else {
4157 listptr = *map = kmem_zalloc(
4158 sizeof (*listptr) * full_list, KM_SLEEP);
4159 *len = full_list;
4160 }
4161 } else {
4162 /*
4163 * By design this routine mandates the callers to
4164 * ask for a whole map when they specify the length
4165 * and the listptr.
4166 */
4167 ASSERT(whole_map == 1);
4168 if (*len < full_list) {
4169 *len = full_list;
4170 mutex_exit(&port->fp_mutex);
4171 return;
4172 }
4173 listptr = *map;
4174 *len = full_list;
4175 }
4176
4177 for (index = 0; index < pwwn_table_size; index++) {
4178 head = &port->fp_pwwn_table[index];
4179 last_pd = NULL;
4180 pd = head->pwwn_head;
4181 while (pd != NULL) {
4182 mutex_enter(&pd->pd_mutex);
4183 if ((whole_map == 0 &&
4184 pd->pd_type == PORT_DEVICE_NOCHANGE) ||
4185 pd->pd_state == PORT_DEVICE_INVALID) {
4186 mutex_exit(&pd->pd_mutex);
4187 last_pd = pd;
4188 pd = pd->pd_wwn_hnext;
4189 continue;
4190 }
4191 mutex_exit(&pd->pd_mutex);
4192
4193 fctl_copy_portmap(listptr, pd);
4194
4195 if (justcopy) {
4196 last_pd = pd;
4197 pd = pd->pd_wwn_hnext;
4198 listptr++;
4199 continue;
4200 }
4201
4202 mutex_enter(&pd->pd_mutex);
4203 ASSERT(pd->pd_state != PORT_DEVICE_INVALID);
4204 if (pd->pd_type == PORT_DEVICE_OLD) {
4205 listptr->map_pd = pd;
4206 listptr->map_state = pd->pd_state =
4207 PORT_DEVICE_INVALID;
4208 /*
4209 * Remove this from the PWWN hash table.
4210 */
4211 old_pd = pd;
4212 pd = old_pd->pd_wwn_hnext;
4213
4214 if (last_pd == NULL) {
4215 ASSERT(old_pd == head->pwwn_head);
4216
4217 head->pwwn_head = pd;
4218 } else {
4219 last_pd->pd_wwn_hnext = pd;
4220 }
4221 head->pwwn_count--;
4222 /*
4223 * Make sure we tie fp_dev_count to the size
4224 * of the pwwn_table
4225 */
4226 port->fp_dev_count--;
4227 old_pd->pd_wwn_hnext = NULL;
4228
4229 if (port->fp_topology == FC_TOP_PRIVATE_LOOP &&
4230 port->fp_statec_busy && !orphan) {
4231 fctl_check_alpa_list(port, old_pd);
4232 }
4233
4234 /*
4235 * Remove if the port device has stealthily
4236 * present in the D_ID hash table
4237 */
4238 fctl_delist_did_table(port, old_pd);
4239
4240 ASSERT(old_pd->pd_remote_nodep != NULL);
4241
4242 initiator = (old_pd->pd_recepient ==
4243 PD_PLOGI_INITIATOR) ? 1 : 0;
4244
4245 mutex_exit(&old_pd->pd_mutex);
4246 mutex_exit(&port->fp_mutex);
4247
4248 if (orphan) {
4249 fctl_print_if_not_orphan(port, old_pd);
4250
4251 (void) fctl_add_orphan(port, old_pd,
4252 KM_NOSLEEP);
4253 }
4254
4255 if (FC_IS_TOP_SWITCH(topology) && initiator) {
4256 (void) fctl_add_orphan(port, old_pd,
4257 KM_NOSLEEP);
4258 }
4259 mutex_enter(&port->fp_mutex);
4260 } else {
4261 listptr->map_pd = pd;
4262 pd->pd_type = PORT_DEVICE_NOCHANGE;
4263 mutex_exit(&pd->pd_mutex);
4264 last_pd = pd;
4265 pd = pd->pd_wwn_hnext;
4266 }
4267 listptr++;
4268 }
4269 }
4270 mutex_exit(&port->fp_mutex);
4271 }
4272
4273
4274 job_request_t *
4275 fctl_alloc_job(int job_code, int job_flags, void (*comp) (opaque_t, uchar_t),
4276 opaque_t arg, int sleep)
4277 {
4278 job_request_t *job;
4279
4280 job = (job_request_t *)kmem_cache_alloc(fctl_job_cache, sleep);
4281 if (job != NULL) {
4282 job->job_result = FC_SUCCESS;
4283 job->job_code = job_code;
4284 job->job_flags = job_flags;
4285 job->job_cb_arg = arg;
4286 job->job_comp = comp;
4287 job->job_private = NULL;
4288 job->job_ulp_pkts = NULL;
4289 job->job_ulp_listlen = 0;
4290 job->job_counter = 0;
4291 job->job_next = NULL;
4292 }
4293
4294 return (job);
4295 }
4296
4297
4298 void
4299 fctl_dealloc_job(job_request_t *job)
4300 {
4301 kmem_cache_free(fctl_job_cache, (void *)job);
4302 }
4303
4304
4305 void
4306 fctl_enque_job(fc_local_port_t *port, job_request_t *job)
4307 {
4308 ASSERT(!MUTEX_HELD(&port->fp_mutex));
4309
4310 mutex_enter(&port->fp_mutex);
4311
4312 if (port->fp_job_tail == NULL) {
4313 ASSERT(port->fp_job_head == NULL);
4314 port->fp_job_head = port->fp_job_tail = job;
4315 } else {
4316 port->fp_job_tail->job_next = job;
4317 port->fp_job_tail = job;
4318 }
4319 job->job_next = NULL;
4320
4321 cv_signal(&port->fp_cv);
4322 mutex_exit(&port->fp_mutex);
4323 }
4324
4325
4326 job_request_t *
4327 fctl_deque_job(fc_local_port_t *port)
4328 {
4329 job_request_t *job;
4330
4331 ASSERT(MUTEX_HELD(&port->fp_mutex));
4332
4333 if (port->fp_job_head == NULL) {
4334 ASSERT(port->fp_job_tail == NULL);
4335 job = NULL;
4336 } else {
4337 job = port->fp_job_head;
4338 if (job->job_next == NULL) {
4339 ASSERT(job == port->fp_job_tail);
4340 port->fp_job_tail = NULL;
4341 }
4342 port->fp_job_head = job->job_next;
4343 }
4344
4345 return (job);
4346 }
4347
4348
4349 void
4350 fctl_priority_enque_job(fc_local_port_t *port, job_request_t *job)
4351 {
4352 ASSERT(!MUTEX_HELD(&port->fp_mutex));
4353
4354 mutex_enter(&port->fp_mutex);
4355 if (port->fp_job_tail == NULL) {
4356 ASSERT(port->fp_job_head == NULL);
4357 port->fp_job_head = port->fp_job_tail = job;
4358 job->job_next = NULL;
4359 } else {
4360 job->job_next = port->fp_job_head;
4361 port->fp_job_head = job;
4362 }
4363 cv_signal(&port->fp_cv);
4364 mutex_exit(&port->fp_mutex);
4365 }
4366
4367
4368 void
4369 fctl_jobwait(job_request_t *job)
4370 {
4371 ASSERT(!(job->job_flags & JOB_TYPE_FCTL_ASYNC));
4372 sema_p(&job->job_fctl_sema);
4373 ASSERT(!MUTEX_HELD(&job->job_mutex));
4374 }
4375
4376
4377 void
4378 fctl_jobdone(job_request_t *job)
4379 {
4380 if (job->job_flags & JOB_TYPE_FCTL_ASYNC) {
4381 if (job->job_comp) {
4382 job->job_comp(job->job_cb_arg, job->job_result);
4383 }
4384 fctl_dealloc_job(job);
4385 } else {
4386 sema_v(&job->job_fctl_sema);
4387 }
4388 }
4389
4390
4391 /*
4392 * Compare two WWNs.
4393 * The NAA can't be omitted for comparison.
4394 *
4395 * Return Values:
4396 * if src == dst return 0
4397 * if src > dst return 1
4398 * if src < dst return -1
4399 */
4400 int
4401 fctl_wwn_cmp(la_wwn_t *src, la_wwn_t *dst)
4402 {
4403 uint8_t *l, *r;
4404 int i;
4405 uint64_t wl, wr;
4406
4407 l = (uint8_t *)src;
4408 r = (uint8_t *)dst;
4409
4410 for (i = 0, wl = 0; i < 8; i++) {
4411 wl <<= 8;
4412 wl |= l[i];
4413 }
4414 for (i = 0, wr = 0; i < 8; i++) {
4415 wr <<= 8;
4416 wr |= r[i];
4417 }
4418
4419 if (wl > wr) {
4420 return (1);
4421 } else if (wl == wr) {
4422 return (0);
4423 } else {
4424 return (-1);
4425 }
4426 }
4427
4428
4429 /*
4430 * ASCII to Integer goodie with support for base 16, 10, 2 and 8
4431 */
4432 int
4433 fctl_atoi(char *s, int base)
4434 {
4435 int val;
4436 int ch;
4437
4438 for (val = 0; *s != '\0'; s++) {
4439 switch (base) {
4440 case 16:
4441 if (*s >= '0' && *s <= '9') {
4442 ch = *s - '0';
4443 } else if (*s >= 'a' && *s <= 'f') {
4444 ch = *s - 'a' + 10;
4445 } else if (*s >= 'A' && *s <= 'F') {
4446 ch = *s - 'A' + 10;
4447 } else {
4448 return (-1);
4449 }
4450 break;
4451
4452 case 10:
4453 if (*s < '0' || *s > '9') {
4454 return (-1);
4455 }
4456 ch = *s - '0';
4457 break;
4458
4459 case 2:
4460 if (*s < '0' || *s > '1') {
4461 return (-1);
4462 }
4463 ch = *s - '0';
4464 break;
4465
4466 case 8:
4467 if (*s < '0' || *s > '7') {
4468 return (-1);
4469 }
4470 ch = *s - '0';
4471 break;
4472
4473 default:
4474 return (-1);
4475 }
4476 val = (val * base) + ch;
4477 }
4478 return (val);
4479 }
4480
4481
4482 /*
4483 * Create the fc_remote_port_t struct for the given port_wwn and d_id.
4484 *
4485 * If the struct already exists (and is "valid"), then use it. Before using
4486 * it, the code below also checks: (a) if the d_id has changed, and (b) if
4487 * the device is maked as PORT_DEVICE_OLD.
4488 *
4489 * If no fc_remote_node_t struct exists for the given node_wwn, then that
4490 * struct is also created (and linked with the fc_remote_port_t).
4491 *
4492 * The given fc_local_port_t struct is updated with the info on the new
4493 * struct(s). The d_id and pwwn hash tables in the port_wwn are updated.
4494 * The global node_hash_table[] is updated (if necessary).
4495 */
4496 fc_remote_port_t *
4497 fctl_create_remote_port(fc_local_port_t *port, la_wwn_t *node_wwn,
4498 la_wwn_t *port_wwn, uint32_t d_id, uchar_t recepient, int sleep)
4499 {
4500 int invalid = 0;
4501 fc_remote_node_t *rnodep;
4502 fc_remote_port_t *pd;
4503
4504 rnodep = fctl_get_remote_node_by_nwwn(node_wwn);
4505 if (rnodep) {
4506 /*
4507 * We found an fc_remote_node_t for the remote node -- see if
4508 * anyone has marked it as going away or gone.
4509 */
4510 mutex_enter(&rnodep->fd_mutex);
4511 invalid = (rnodep->fd_flags == FC_REMOTE_NODE_INVALID) ? 1 : 0;
4512 mutex_exit(&rnodep->fd_mutex);
4513 }
4514 if (rnodep == NULL || invalid) {
4515 /*
4516 * No valid remote node struct found -- create it.
4517 * Note: this is the only place that this func is called.
4518 */
4519 rnodep = fctl_create_remote_node(node_wwn, sleep);
4520 if (rnodep == NULL) {
4521 return (NULL);
4522 }
4523 }
4524
4525 mutex_enter(&port->fp_mutex);
4526
4527 /*
4528 * See if there already is an fc_remote_port_t struct in existence
4529 * on the specified fc_local_port_t for the given pwwn. If so, then
4530 * grab a reference to it. The 'held' here just means that fp_mutex
4531 * is held by the caller -- no reference counts are updated.
4532 */
4533 pd = fctl_get_remote_port_by_pwwn_mutex_held(port, port_wwn);
4534 if (pd) {
4535 /*
4536 * An fc_remote_port_t struct was found -- see if anyone has
4537 * marked it as "invalid", which means that it is in the
4538 * process of going away & we don't want to use it.
4539 */
4540 mutex_enter(&pd->pd_mutex);
4541 invalid = (pd->pd_state == PORT_DEVICE_INVALID) ? 1 : 0;
4542 mutex_exit(&pd->pd_mutex);
4543 }
4544
4545 if (pd == NULL || invalid) {
4546 /*
4547 * No fc_remote_port_t was found (or the existing one is
4548 * marked as "invalid".) Allocate a new one and use that.
4549 * This call will also update the d_id and pwwn hash tables
4550 * in the given fc_local_port_t struct with the newly allocated
4551 * fc_remote_port_t.
4552 */
4553 if ((pd = fctl_alloc_remote_port(port, port_wwn, d_id,
4554 recepient, sleep)) == NULL) {
4555 /* Just give up if the allocation fails. */
4556 mutex_exit(&port->fp_mutex);
4557 fctl_destroy_remote_node(rnodep);
4558 return (pd);
4559 }
4560
4561 /*
4562 * Add the new fc_remote_port_t struct to the d_id and pwwn
4563 * hash tables on the associated fc_local_port_t struct.
4564 */
4565 mutex_enter(&pd->pd_mutex);
4566 pd->pd_remote_nodep = rnodep;
4567 fctl_enlist_did_table(port, pd);
4568 fctl_enlist_pwwn_table(port, pd);
4569 mutex_exit(&pd->pd_mutex);
4570 mutex_exit(&port->fp_mutex);
4571
4572 /*
4573 * Retrieve a pointer to the fc_remote_node_t (i.e., remote
4574 * node) specified by the given node_wwn. This looks in the
4575 * global fctl_nwwn_hash_table[]. The fd_numports reference
4576 * count in the fc_remote_node_t struct is incremented.
4577 */
4578 rnodep = fctl_lock_remote_node_by_nwwn(node_wwn);
4579
4580 } else {
4581 /*
4582 * An existing and valid fc_remote_port_t struct already
4583 * exists on the fc_local_port_t for the given pwwn.
4584 */
4585
4586 mutex_enter(&pd->pd_mutex);
4587 ASSERT(pd->pd_remote_nodep != NULL);
4588
4589 if (pd->pd_port_id.port_id != d_id) {
4590 /*
4591 * A very unlikely occurance in a well
4592 * behaved environment.
4593 */
4594
4595 /*
4596 * The existing fc_remote_port_t has a different
4597 * d_id than what we were given. This code will
4598 * update the existing one with the one that was
4599 * just given.
4600 */
4601 char string[(FCTL_WWN_SIZE(port_wwn) << 1) + 1];
4602 uint32_t old_id;
4603
4604 fc_wwn_to_str(port_wwn, string);
4605
4606 old_id = pd->pd_port_id.port_id;
4607
4608 fctl_delist_did_table(port, pd);
4609
4610 cmn_err(CE_NOTE, "!fctl(%d): D_ID of a device"
4611 " with PWWN %s changed. New D_ID = %x,"
4612 " OLD D_ID = %x", port->fp_instance, string,
4613 d_id, old_id);
4614
4615 pd->pd_port_id.port_id = d_id;
4616
4617 /*
4618 * Looks like we have to presume here that the
4619 * remote port could be something entirely different
4620 * from what was previously existing & valid at this
4621 * pwwn.
4622 */
4623 pd->pd_type = PORT_DEVICE_CHANGED;
4624
4625 /* Record (update) the new d_id for the remote port */
4626 fctl_enlist_did_table(port, pd);
4627
4628 } else if (pd->pd_type == PORT_DEVICE_OLD) {
4629 /*
4630 * OK at least the old & new d_id's match. So for
4631 * PORT_DEVICE_OLD, this assumes that the remote
4632 * port had disappeared but now has come back.
4633 * Update the pd_type and pd_state to put the
4634 * remote port back into service.
4635 */
4636 pd->pd_type = PORT_DEVICE_NOCHANGE;
4637 pd->pd_state = PORT_DEVICE_VALID;
4638
4639 fctl_enlist_did_table(port, pd);
4640
4641 } else {
4642 /*
4643 * OK the old & new d_id's match, and the remote
4644 * port struct is not marked as PORT_DEVICE_OLD, so
4645 * presume that it's still the same device and is
4646 * still in good shape. Also this presumes that we
4647 * do not need to update d_id or pwwn hash tables.
4648 */
4649 /* sanitize device values */
4650 pd->pd_type = PORT_DEVICE_NOCHANGE;
4651 pd->pd_state = PORT_DEVICE_VALID;
4652 }
4653
4654 mutex_exit(&pd->pd_mutex);
4655 mutex_exit(&port->fp_mutex);
4656
4657 if (rnodep != pd->pd_remote_nodep) {
4658 if ((rnodep != NULL) &&
4659 (fctl_wwn_cmp(&pd->pd_remote_nodep->fd_node_name,
4660 node_wwn) != 0)) {
4661 /*
4662 * Rut-roh, there is an fc_remote_node_t remote
4663 * node struct for the given node_wwn, but the
4664 * fc_remote_port_t remote port struct doesn't
4665 * know about it. This just prints a warning
4666 * message & fails the fc_remote_port_t
4667 * allocation (possible leak here?).
4668 */
4669 char ww1_name[17];
4670 char ww2_name[17];
4671
4672 fc_wwn_to_str(
4673 &pd->pd_remote_nodep->fd_node_name,
4674 ww1_name);
4675 fc_wwn_to_str(node_wwn, ww2_name);
4676
4677 cmn_err(CE_WARN, "fctl(%d) NWWN Mismatch: "
4678 "Expected %s Got %s", port->fp_instance,
4679 ww1_name, ww2_name);
4680 }
4681
4682 return (NULL);
4683 }
4684 }
4685
4686 /*
4687 * Add the fc_remote_port_t onto the linked list of remote port
4688 * devices associated with the given fc_remote_node_t (remote node).
4689 */
4690 fctl_link_remote_port_to_remote_node(rnodep, pd);
4691
4692 return (pd);
4693 }
4694
4695
4696 /*
4697 * Disassociate the given fc_local_port_t and fc_remote_port_t structs. Removes
4698 * the fc_remote_port_t from the associated fc_remote_node_t. Also removes any
4699 * references to the fc_remote_port_t from the d_id and pwwn tables in the
4700 * given fc_local_port_t. Deallocates the given fc_remote_port_t.
4701 *
4702 * Returns a count of the number of remaining fc_remote_port_t structs
4703 * associated with the fc_remote_node_t struct.
4704 *
4705 * If pd_ref_count in the given fc_remote_port_t is nonzero, then this
4706 * function just sets the pd->pd_aux_flags |= PD_NEEDS_REMOVAL and the
4707 * pd->pd_type = PORT_DEVICE_OLD and lets some other function(s) worry about
4708 * the cleanup. The function then also returns '1'
4709 * instead of the actual number of remaining fc_remote_port_t structs
4710 *
4711 * If there are no more remote ports on the remote node, return 0.
4712 * Otherwise, return non-zero.
4713 */
4714 int
4715 fctl_destroy_remote_port(fc_local_port_t *port, fc_remote_port_t *pd)
4716 {
4717 fc_remote_node_t *rnodep;
4718 int rcount = 0;
4719
4720 mutex_enter(&pd->pd_mutex);
4721
4722 /*
4723 * If pd_ref_count > 0, we can't pull the rug out from any
4724 * current users of this fc_remote_port_t. We'll mark it as old
4725 * and in need of removal. The same goes for any fc_remote_port_t
4726 * that has a reference handle(s) in a ULP(s) but for which the ULP(s)
4727 * have not yet been notified that the handle is no longer valid
4728 * (i.e., PD_GIVEN_TO_ULPS is set).
4729 */
4730 if ((pd->pd_ref_count > 0) ||
4731 (pd->pd_aux_flags & PD_GIVEN_TO_ULPS)) {
4732 pd->pd_aux_flags |= PD_NEEDS_REMOVAL;
4733 pd->pd_type = PORT_DEVICE_OLD;
4734 mutex_exit(&pd->pd_mutex);
4735 return (1);
4736 }
4737
4738 pd->pd_type = PORT_DEVICE_OLD;
4739
4740 rnodep = pd->pd_remote_nodep;
4741
4742 mutex_exit(&pd->pd_mutex);
4743
4744 if (rnodep != NULL) {
4745 /*
4746 * Remove the fc_remote_port_t from the linked list of remote
4747 * ports for the given fc_remote_node_t. This is only called
4748 * here and in fctl_destroy_all_remote_ports().
4749 */
4750 rcount = fctl_unlink_remote_port_from_remote_node(rnodep, pd);
4751 }
4752
4753 mutex_enter(&port->fp_mutex);
4754 mutex_enter(&pd->pd_mutex);
4755
4756 fctl_delist_did_table(port, pd);
4757 fctl_delist_pwwn_table(port, pd);
4758
4759 mutex_exit(&pd->pd_mutex);
4760
4761 /*
4762 * Deconstruct & free the fc_remote_port_t. This is only called
4763 * here and in fctl_destroy_all_remote_ports().
4764 */
4765 fctl_dealloc_remote_port(pd);
4766
4767 mutex_exit(&port->fp_mutex);
4768
4769 return (rcount);
4770 }
4771
4772
4773 /*
4774 * This goes thru the d_id table on the given fc_local_port_t.
4775 * For each fc_remote_port_t found, this will:
4776 *
4777 * - Remove the fc_remote_port_t from the linked list of remote ports for
4778 * the associated fc_remote_node_t. If the linked list goes empty, then this
4779 * tries to deconstruct & free the fc_remote_node_t (that also removes the
4780 * fc_remote_node_t from the global fctl_nwwn_hash_table[]).
4781 *
4782 * - Remove the fc_remote_port_t from the pwwn list on the given
4783 * fc_local_port_t.
4784 *
4785 * - Deconstruct and free the fc_remote_port_t.
4786 *
4787 * - Removes the link to the fc_remote_port_t in the d_id table. Note, this
4788 * does not appear to correctle decrement the d_id_count tho.
4789 */
4790 void
4791 fctl_destroy_all_remote_ports(fc_local_port_t *port)
4792 {
4793 int index;
4794 fc_remote_port_t *pd;
4795 fc_remote_node_t *rnodep;
4796 struct d_id_hash *head;
4797
4798 mutex_enter(&port->fp_mutex);
4799
4800 for (index = 0; index < did_table_size; index++) {
4801
4802 head = &port->fp_did_table[index];
4803
4804 while (head->d_id_head != NULL) {
4805 pd = head->d_id_head;
4806
4807 /*
4808 * See if this remote port (fc_remote_port_t) has a
4809 * reference to a remote node (fc_remote_node_t) in its
4810 * pd->pd_remote_nodep pointer.
4811 */
4812 mutex_enter(&pd->pd_mutex);
4813 rnodep = pd->pd_remote_nodep;
4814 mutex_exit(&pd->pd_mutex);
4815
4816 if (rnodep != NULL) {
4817 /*
4818 * An fc_remote_node_t reference exists. Remove
4819 * the fc_remote_port_t from the linked list of
4820 * remote ports for fc_remote_node_t.
4821 */
4822 if (fctl_unlink_remote_port_from_remote_node(
4823 rnodep, pd) == 0) {
4824 /*
4825 * The fd_numports reference count
4826 * in the fc_remote_node_t has come
4827 * back as zero, so we can free the
4828 * fc_remote_node_t. This also means
4829 * that the fc_remote_node_t was
4830 * removed from the
4831 * fctl_nwwn_hash_table[].
4832 *
4833 * This will silently skip the
4834 * kmem_free() if either the
4835 * fd_numports is nonzero or
4836 * the fd_port is not NULL in
4837 * the fc_remote_node_t.
4838 */
4839 fctl_destroy_remote_node(rnodep);
4840 }
4841 }
4842
4843 /*
4844 * Clean up the entry in the fc_local_port_t's pwwn
4845 * table for the given fc_remote_port_t (i.e., the pd).
4846 */
4847 mutex_enter(&pd->pd_mutex);
4848 fctl_delist_pwwn_table(port, pd);
4849 pd->pd_aux_flags &= ~PD_IN_DID_QUEUE;
4850 mutex_exit(&pd->pd_mutex);
4851
4852 /*
4853 * Remove the current entry from the d_id list.
4854 */
4855 head->d_id_head = pd->pd_did_hnext;
4856
4857 /*
4858 * Deconstruct & free the fc_remote_port_t (pd)
4859 * Note: this is only called here and in
4860 * fctl_destroy_remote_port_t().
4861 */
4862 fctl_dealloc_remote_port(pd);
4863 }
4864 }
4865
4866 mutex_exit(&port->fp_mutex);
4867 }
4868
4869
4870 int
4871 fctl_is_wwn_zero(la_wwn_t *wwn)
4872 {
4873 int count;
4874
4875 for (count = 0; count < sizeof (la_wwn_t); count++) {
4876 if (wwn->raw_wwn[count] != 0) {
4877 return (FC_FAILURE);
4878 }
4879 }
4880
4881 return (FC_SUCCESS);
4882 }
4883
4884
4885 void
4886 fctl_ulp_unsol_cb(fc_local_port_t *port, fc_unsol_buf_t *buf, uchar_t type)
4887 {
4888 int data_cb;
4889 int check_type;
4890 int rval;
4891 uint32_t claimed;
4892 fc_ulp_module_t *mod;
4893 fc_ulp_ports_t *ulp_port;
4894
4895 claimed = 0;
4896 check_type = 1;
4897
4898 switch ((buf->ub_frame.r_ctl) & R_CTL_ROUTING) {
4899 case R_CTL_DEVICE_DATA:
4900 data_cb = 1;
4901 break;
4902
4903 case R_CTL_EXTENDED_SVC:
4904 check_type = 0;
4905 /* FALLTHROUGH */
4906
4907 case R_CTL_FC4_SVC:
4908 data_cb = 0;
4909 break;
4910
4911 default:
4912 mutex_enter(&port->fp_mutex);
4913 ASSERT(port->fp_active_ubs > 0);
4914 if (--(port->fp_active_ubs) == 0) {
4915 port->fp_soft_state &= ~FP_SOFT_IN_UNSOL_CB;
4916 }
4917 mutex_exit(&port->fp_mutex);
4918 port->fp_fca_tran->fca_ub_release(port->fp_fca_handle,
4919 1, &buf->ub_token);
4920 return;
4921 }
4922
4923 rw_enter(&fctl_ulp_lock, RW_READER);
4924 for (mod = fctl_ulp_modules; mod; mod = mod->mod_next) {
4925 if (check_type && mod->mod_info->ulp_type != type) {
4926 continue;
4927 }
4928
4929 rw_enter(&fctl_mod_ports_lock, RW_READER);
4930 ulp_port = fctl_get_ulp_port(mod, port);
4931 rw_exit(&fctl_mod_ports_lock);
4932
4933 if (ulp_port == NULL) {
4934 continue;
4935 }
4936
4937 mutex_enter(&ulp_port->port_mutex);
4938 if (FCTL_DISALLOW_CALLBACKS(ulp_port->port_dstate)) {
4939 mutex_exit(&ulp_port->port_mutex);
4940 continue;
4941 }
4942 mutex_exit(&ulp_port->port_mutex);
4943
4944 if (data_cb == 1) {
4945 rval = mod->mod_info->ulp_data_callback(
4946 mod->mod_info->ulp_handle,
4947 (opaque_t)port, buf, claimed);
4948 } else {
4949 rval = mod->mod_info->ulp_els_callback(
4950 mod->mod_info->ulp_handle,
4951 (opaque_t)port, buf, claimed);
4952 }
4953
4954 if (rval == FC_SUCCESS && claimed == 0) {
4955 claimed = 1;
4956 }
4957 }
4958 rw_exit(&fctl_ulp_lock);
4959
4960 if (claimed == 0) {
4961 /*
4962 * We should actually RJT since nobody claimed it.
4963 */
4964 mutex_enter(&port->fp_mutex);
4965 ASSERT(port->fp_active_ubs > 0);
4966 if (--(port->fp_active_ubs) == 0) {
4967 port->fp_soft_state &= ~FP_SOFT_IN_UNSOL_CB;
4968 }
4969 mutex_exit(&port->fp_mutex);
4970 port->fp_fca_tran->fca_ub_release(port->fp_fca_handle,
4971 1, &buf->ub_token);
4972
4973 } else {
4974 mutex_enter(&port->fp_mutex);
4975 if (--port->fp_active_ubs == 0) {
4976 port->fp_soft_state &= ~FP_SOFT_IN_UNSOL_CB;
4977 }
4978 mutex_exit(&port->fp_mutex);
4979 }
4980 }
4981
4982
4983 /*
4984 * Both fd_mutex and pd_mutex are held (in that order) coming in to this func
4985 *
4986 * With all these mutexes held, we should make sure this function does not eat
4987 * up much time.
4988 */
4989 void
4990 fctl_copy_portmap_held(fc_portmap_t *map, fc_remote_port_t *pd)
4991 {
4992 fc_remote_node_t *node;
4993
4994 ASSERT(MUTEX_HELD(&pd->pd_mutex));
4995
4996 map->map_pwwn = pd->pd_port_name;
4997 map->map_did = pd->pd_port_id;
4998 map->map_hard_addr = pd->pd_hard_addr;
4999 map->map_state = pd->pd_state;
5000 map->map_type = pd->pd_type;
5001 map->map_flags = 0;
5002
5003 ASSERT(map->map_type <= PORT_DEVICE_DELETE);
5004
5005 bcopy(pd->pd_fc4types, map->map_fc4_types, sizeof (pd->pd_fc4types));
5006
5007 node = pd->pd_remote_nodep;
5008
5009 ASSERT(MUTEX_HELD(&node->fd_mutex));
5010
5011 if (node) {
5012 map->map_nwwn = node->fd_node_name;
5013 }
5014 map->map_pd = pd;
5015 }
5016
5017 void
5018 fctl_copy_portmap(fc_portmap_t *map, fc_remote_port_t *pd)
5019 {
5020 fc_remote_node_t *node;
5021
5022 ASSERT(!MUTEX_HELD(&pd->pd_mutex));
5023
5024 mutex_enter(&pd->pd_mutex);
5025 map->map_pwwn = pd->pd_port_name;
5026 map->map_did = pd->pd_port_id;
5027 map->map_hard_addr = pd->pd_hard_addr;
5028 map->map_state = pd->pd_state;
5029 map->map_type = pd->pd_type;
5030 map->map_flags = 0;
5031
5032 ASSERT(map->map_type <= PORT_DEVICE_DELETE);
5033
5034 bcopy(pd->pd_fc4types, map->map_fc4_types, sizeof (pd->pd_fc4types));
5035
5036 node = pd->pd_remote_nodep;
5037 mutex_exit(&pd->pd_mutex);
5038
5039 if (node) {
5040 mutex_enter(&node->fd_mutex);
5041 map->map_nwwn = node->fd_node_name;
5042 mutex_exit(&node->fd_mutex);
5043 }
5044 map->map_pd = pd;
5045 }
5046
5047
5048 static int
5049 fctl_update_host_ns_values(fc_local_port_t *port, fc_ns_cmd_t *ns_req)
5050 {
5051 int rval = FC_SUCCESS;
5052
5053 switch (ns_req->ns_cmd) {
5054 case NS_RFT_ID: {
5055 int count;
5056 uint32_t *src;
5057 uint32_t *dst;
5058 ns_rfc_type_t *rfc;
5059
5060 rfc = (ns_rfc_type_t *)ns_req->ns_req_payload;
5061
5062 mutex_enter(&port->fp_mutex);
5063 src = (uint32_t *)port->fp_fc4_types;
5064 dst = (uint32_t *)rfc->rfc_types;
5065
5066 for (count = 0; count < 8; count++) {
5067 *src++ |= *dst++;
5068 }
5069 mutex_exit(&port->fp_mutex);
5070
5071 break;
5072 }
5073
5074 case NS_RSPN_ID: {
5075 ns_spn_t *spn;
5076
5077 spn = (ns_spn_t *)ns_req->ns_req_payload;
5078
5079 mutex_enter(&port->fp_mutex);
5080 port->fp_sym_port_namelen = spn->spn_len;
5081 if (spn->spn_len) {
5082 bcopy((caddr_t)spn + sizeof (ns_spn_t),
5083 port->fp_sym_port_name, spn->spn_len);
5084 }
5085 mutex_exit(&port->fp_mutex);
5086
5087 break;
5088 }
5089
5090 case NS_RSNN_NN: {
5091 ns_snn_t *snn;
5092
5093 snn = (ns_snn_t *)ns_req->ns_req_payload;
5094
5095 mutex_enter(&port->fp_mutex);
5096 port->fp_sym_node_namelen = snn->snn_len;
5097 if (snn->snn_len) {
5098 bcopy((caddr_t)snn + sizeof (ns_snn_t),
5099 port->fp_sym_node_name, snn->snn_len);
5100 }
5101 mutex_exit(&port->fp_mutex);
5102
5103 break;
5104 }
5105
5106 case NS_RIP_NN: {
5107 ns_rip_t *rip;
5108
5109 rip = (ns_rip_t *)ns_req->ns_req_payload;
5110
5111 mutex_enter(&port->fp_mutex);
5112 bcopy(rip->rip_ip_addr, port->fp_ip_addr,
5113 sizeof (rip->rip_ip_addr));
5114 mutex_exit(&port->fp_mutex);
5115
5116 break;
5117 }
5118
5119 case NS_RIPA_NN: {
5120 ns_ipa_t *ipa;
5121
5122 ipa = (ns_ipa_t *)ns_req->ns_req_payload;
5123
5124 mutex_enter(&port->fp_mutex);
5125 bcopy(ipa->ipa_value, port->fp_ipa, sizeof (ipa->ipa_value));
5126 mutex_exit(&port->fp_mutex);
5127
5128 break;
5129 }
5130
5131 default:
5132 rval = FC_BADOBJECT;
5133 break;
5134 }
5135
5136 return (rval);
5137 }
5138
5139
5140 static int
5141 fctl_retrieve_host_ns_values(fc_local_port_t *port, fc_ns_cmd_t *ns_req)
5142 {
5143 int rval = FC_SUCCESS;
5144
5145 switch (ns_req->ns_cmd) {
5146 case NS_GFT_ID: {
5147 ns_rfc_type_t *rfc;
5148
5149 rfc = (ns_rfc_type_t *)ns_req->ns_resp_payload;
5150
5151 mutex_enter(&port->fp_mutex);
5152 bcopy(port->fp_fc4_types, rfc->rfc_types,
5153 sizeof (rfc->rfc_types));
5154 mutex_exit(&port->fp_mutex);
5155 break;
5156 }
5157
5158 case NS_GSPN_ID: {
5159 ns_spn_t *spn;
5160
5161 spn = (ns_spn_t *)ns_req->ns_resp_payload;
5162
5163 mutex_enter(&port->fp_mutex);
5164 spn->spn_len = port->fp_sym_port_namelen;
5165 if (spn->spn_len) {
5166 bcopy(port->fp_sym_port_name, (caddr_t)spn +
5167 sizeof (ns_spn_t), spn->spn_len);
5168 }
5169 mutex_exit(&port->fp_mutex);
5170
5171 break;
5172 }
5173
5174 case NS_GSNN_NN: {
5175 ns_snn_t *snn;
5176
5177 snn = (ns_snn_t *)ns_req->ns_resp_payload;
5178
5179 mutex_enter(&port->fp_mutex);
5180 snn->snn_len = port->fp_sym_node_namelen;
5181 if (snn->snn_len) {
5182 bcopy(port->fp_sym_node_name, (caddr_t)snn +
5183 sizeof (ns_snn_t), snn->snn_len);
5184 }
5185 mutex_exit(&port->fp_mutex);
5186
5187 break;
5188 }
5189
5190 case NS_GIP_NN: {
5191 ns_rip_t *rip;
5192
5193 rip = (ns_rip_t *)ns_req->ns_resp_payload;
5194
5195 mutex_enter(&port->fp_mutex);
5196 bcopy(port->fp_ip_addr, rip->rip_ip_addr,
5197 sizeof (rip->rip_ip_addr));
5198 mutex_exit(&port->fp_mutex);
5199
5200 break;
5201 }
5202
5203 case NS_GIPA_NN: {
5204 ns_ipa_t *ipa;
5205
5206 ipa = (ns_ipa_t *)ns_req->ns_resp_payload;
5207
5208 mutex_enter(&port->fp_mutex);
5209 bcopy(port->fp_ipa, ipa->ipa_value, sizeof (ipa->ipa_value));
5210 mutex_exit(&port->fp_mutex);
5211
5212 break;
5213 }
5214
5215 default:
5216 rval = FC_BADOBJECT;
5217 break;
5218 }
5219
5220 return (rval);
5221 }
5222
5223
5224 fctl_ns_req_t *
5225 fctl_alloc_ns_cmd(uint32_t cmd_len, uint32_t resp_len, uint32_t data_len,
5226 uint32_t ns_flags, int sleep)
5227 {
5228 fctl_ns_req_t *ns_cmd;
5229
5230 ns_cmd = kmem_zalloc(sizeof (*ns_cmd), sleep);
5231 if (ns_cmd == NULL) {
5232 return (NULL);
5233 }
5234
5235 if (cmd_len) {
5236 ns_cmd->ns_cmd_buf = kmem_zalloc(cmd_len, sleep);
5237 if (ns_cmd->ns_cmd_buf == NULL) {
5238 kmem_free(ns_cmd, sizeof (*ns_cmd));
5239 return (NULL);
5240 }
5241 ns_cmd->ns_cmd_size = cmd_len;
5242 }
5243
5244 ns_cmd->ns_resp_size = resp_len;
5245
5246 if (data_len) {
5247 ns_cmd->ns_data_buf = kmem_zalloc(data_len, sleep);
5248 if (ns_cmd->ns_data_buf == NULL) {
5249 if (ns_cmd->ns_cmd_buf && cmd_len) {
5250 kmem_free(ns_cmd->ns_cmd_buf, cmd_len);
5251 }
5252 kmem_free(ns_cmd, sizeof (*ns_cmd));
5253 return (NULL);
5254 }
5255 ns_cmd->ns_data_len = data_len;
5256 }
5257 ns_cmd->ns_flags = ns_flags;
5258
5259 return (ns_cmd);
5260 }
5261
5262
5263 void
5264 fctl_free_ns_cmd(fctl_ns_req_t *ns_cmd)
5265 {
5266 if (ns_cmd->ns_cmd_size && ns_cmd->ns_cmd_buf) {
5267 kmem_free(ns_cmd->ns_cmd_buf, ns_cmd->ns_cmd_size);
5268 }
5269 if (ns_cmd->ns_data_len && ns_cmd->ns_data_buf) {
5270 kmem_free(ns_cmd->ns_data_buf, ns_cmd->ns_data_len);
5271 }
5272 kmem_free(ns_cmd, sizeof (*ns_cmd));
5273 }
5274
5275
5276 int
5277 fctl_ulp_port_ioctl(fc_local_port_t *port, dev_t dev, int cmd,
5278 intptr_t data, int mode, cred_t *credp, int *rval)
5279 {
5280 int ret;
5281 int save;
5282 uint32_t claimed;
5283 fc_ulp_module_t *mod;
5284 fc_ulp_ports_t *ulp_port;
5285
5286 save = *rval;
5287 *rval = ENOTTY;
5288
5289 rw_enter(&fctl_ulp_lock, RW_READER);
5290 for (claimed = 0, mod = fctl_ulp_modules; mod; mod = mod->mod_next) {
5291 rw_enter(&fctl_mod_ports_lock, RW_READER);
5292 ulp_port = fctl_get_ulp_port(mod, port);
5293 rw_exit(&fctl_mod_ports_lock);
5294
5295 if (ulp_port == NULL) {
5296 continue;
5297 }
5298
5299 mutex_enter(&ulp_port->port_mutex);
5300 if (FCTL_DISALLOW_CALLBACKS(ulp_port->port_dstate) ||
5301 mod->mod_info->ulp_port_ioctl == NULL) {
5302 mutex_exit(&ulp_port->port_mutex);
5303 continue;
5304 }
5305 mutex_exit(&ulp_port->port_mutex);
5306
5307 ret = mod->mod_info->ulp_port_ioctl(
5308 mod->mod_info->ulp_handle, (opaque_t)port,
5309 dev, cmd, data, mode, credp, rval, claimed);
5310
5311 if (ret == FC_SUCCESS && claimed == 0) {
5312 claimed = 1;
5313 }
5314 }
5315 rw_exit(&fctl_ulp_lock);
5316
5317 ret = *rval;
5318 *rval = save;
5319
5320 return (ret);
5321 }
5322
5323 /*
5324 * raise power if necessary, and set the port busy
5325 *
5326 * this may cause power to be raised, so no power related locks should
5327 * be held
5328 */
5329 int
5330 fc_ulp_busy_port(opaque_t port_handle)
5331 {
5332 fc_local_port_t *port = port_handle;
5333
5334 return (fctl_busy_port(port));
5335 }
5336
5337 void
5338 fc_ulp_idle_port(opaque_t port_handle)
5339 {
5340 fc_local_port_t *port = port_handle;
5341 fctl_idle_port(port);
5342 }
5343
5344 void
5345 fc_ulp_copy_portmap(fc_portmap_t *map, opaque_t pd)
5346 {
5347 fctl_copy_portmap(map, (fc_remote_port_t *)pd);
5348 }
5349
5350
5351 int
5352 fc_ulp_get_npiv_port_num(opaque_t port_handle)
5353 {
5354 int portsnum = 0;
5355 fc_local_port_t *port = port_handle;
5356 fc_local_port_t *tmpport;
5357
5358 mutex_enter(&port->fp_mutex);
5359 tmpport = port->fp_port_next;
5360 if (!tmpport) {
5361 mutex_exit(&port->fp_mutex);
5362 return (portsnum);
5363 }
5364 while (tmpport != port) {
5365 portsnum ++;
5366 tmpport = tmpport->fp_port_next;
5367 }
5368 mutex_exit(&port->fp_mutex);
5369 return (portsnum);
5370 }
5371
5372 fc_local_port_t *
5373 fc_get_npiv_port(fc_local_port_t *phyport, la_wwn_t *pwwn)
5374 {
5375 fc_fca_port_t *fca_port;
5376 fc_local_port_t *tmpPort = phyport;
5377
5378 mutex_enter(&fctl_port_lock);
5379
5380 for (fca_port = fctl_fca_portlist; fca_port != NULL;
5381 fca_port = fca_port->port_next) {
5382 tmpPort = fca_port->port_handle;
5383 if (tmpPort == NULL) {
5384 continue;
5385 }
5386 mutex_enter(&tmpPort->fp_mutex);
5387 if (bcmp(tmpPort->fp_service_params.nport_ww_name.raw_wwn,
5388 pwwn->raw_wwn, sizeof (la_wwn_t)) == 0) {
5389 mutex_exit(&tmpPort->fp_mutex);
5390 mutex_exit(&fctl_port_lock);
5391 return (tmpPort);
5392 }
5393 mutex_exit(&tmpPort->fp_mutex);
5394 }
5395
5396 mutex_exit(&fctl_port_lock);
5397
5398 return (NULL);
5399 }
5400
5401 int
5402 fc_ulp_get_npiv_port_list(opaque_t port_handle, char *pathList)
5403 {
5404 int portsnum = 0;
5405 fc_local_port_t *port = port_handle;
5406 fc_local_port_t *tmpport;
5407
5408 mutex_enter(&port->fp_mutex);
5409 tmpport = port->fp_port_next;
5410 if (!tmpport || (port->fp_npiv_type == FC_NPIV_PORT)) {
5411 mutex_exit(&port->fp_mutex);
5412 return (portsnum);
5413 }
5414
5415 while (tmpport != port) {
5416 (void) ddi_pathname(tmpport->fp_port_dip,
5417 &pathList[MAXPATHLEN * portsnum]);
5418 portsnum ++;
5419 tmpport = tmpport->fp_port_next;
5420 }
5421 mutex_exit(&port->fp_mutex);
5422
5423 return (portsnum);
5424 }
5425
5426
5427 fc_local_port_t *
5428 fc_delete_npiv_port(fc_local_port_t *port, la_wwn_t *pwwn)
5429 {
5430 fc_local_port_t *tmpport;
5431
5432 mutex_enter(&port->fp_mutex);
5433 tmpport = port->fp_port_next;
5434 if (!tmpport || (port->fp_npiv_type == FC_NPIV_PORT)) {
5435 mutex_exit(&port->fp_mutex);
5436 return (NULL);
5437 }
5438
5439 while (tmpport != port) {
5440 if ((bcmp(tmpport->fp_service_params.nport_ww_name.raw_wwn,
5441 pwwn->raw_wwn, sizeof (la_wwn_t)) == 0) &&
5442 (tmpport->fp_npiv_state == 0)) {
5443 tmpport->fp_npiv_state = FC_NPIV_DELETING;
5444 mutex_exit(&port->fp_mutex);
5445 return (tmpport);
5446 }
5447 tmpport = tmpport->fp_port_next;
5448 }
5449
5450 mutex_exit(&port->fp_mutex);
5451 return (NULL);
5452 }
5453
5454 /*
5455 * Get the list of Adapters. On multi-ported adapters,
5456 * only ONE port on the adapter will be returned.
5457 * pathList should be (count * MAXPATHLEN) long.
5458 * The return value will be set to the number of
5459 * HBAs that were found on the system. If the value
5460 * is greater than count, the routine should be retried
5461 * with a larger buffer.
5462 */
5463 int
5464 fc_ulp_get_adapter_paths(char *pathList, int count)
5465 {
5466 fc_fca_port_t *fca_port;
5467 int in = 0, out = 0, check, skip, maxPorts = 0;
5468 fc_local_port_t **portList;
5469 fc_local_port_t *new_port, *stored_port;
5470 fca_hba_fru_details_t *new_fru, *stored_fru;
5471
5472 ASSERT(pathList != NULL);
5473
5474 /* First figure out how many ports we have */
5475 mutex_enter(&fctl_port_lock);
5476
5477 for (fca_port = fctl_fca_portlist; fca_port != NULL;
5478 fca_port = fca_port->port_next) {
5479 maxPorts ++;
5480 }
5481
5482 /* Now allocate a buffer to store all the pointers for comparisons */
5483 portList = kmem_zalloc(sizeof (fc_local_port_t *) * maxPorts, KM_SLEEP);
5484
5485 for (fca_port = fctl_fca_portlist; fca_port != NULL;
5486 fca_port = fca_port->port_next) {
5487 skip = 0;
5488
5489 /* Lock the new port for subsequent comparisons */
5490 new_port = fca_port->port_handle;
5491 mutex_enter(&new_port->fp_mutex);
5492 new_fru = &new_port->fp_hba_port_attrs.hba_fru_details;
5493
5494 /* Filter out secondary ports from the list */
5495 for (check = 0; check < out; check++) {
5496 if (portList[check] == NULL) {
5497 continue;
5498 }
5499 /* Guard against duplicates (should never happen) */
5500 if (portList[check] == fca_port->port_handle) {
5501 /* Same port */
5502 skip = 1;
5503 break;
5504 }
5505
5506 /* Lock the already stored port for comparison */
5507 stored_port = portList[check];
5508 mutex_enter(&stored_port->fp_mutex);
5509 stored_fru =
5510 &stored_port->fp_hba_port_attrs.hba_fru_details;
5511
5512 /* Are these ports on the same HBA? */
5513 if (new_fru->high == stored_fru->high &&
5514 new_fru->low == stored_fru->low) {
5515 /* Now double check driver */
5516 if (strncmp(
5517 new_port->fp_hba_port_attrs.driver_name,
5518 stored_port->fp_hba_port_attrs.driver_name,
5519 FCHBA_DRIVER_NAME_LEN) == 0) {
5520 /* we don't need to grow the list */
5521 skip = 1;
5522 /* looking at a lower port index? */
5523 if (new_fru->port_index <
5524 stored_fru->port_index) {
5525 /* Replace the port in list */
5526 mutex_exit(
5527 &stored_port->fp_mutex);
5528 if (new_port->fp_npiv_type ==
5529 FC_NPIV_PORT) {
5530 break;
5531 }
5532 portList[check] = new_port;
5533 break;
5534 } /* Else, just skip this port */
5535 }
5536 }
5537
5538 mutex_exit(&stored_port->fp_mutex);
5539 }
5540 mutex_exit(&new_port->fp_mutex);
5541
5542 if (!skip) {
5543 /*
5544 * Either this is the first port for this HBA, or
5545 * it's a secondary port and we haven't stored the
5546 * primary/first port for that HBA. In the latter case,
5547 * will just filter it out as we proceed to loop.
5548 */
5549 if (fca_port->port_handle->fp_npiv_type ==
5550 FC_NPIV_PORT) {
5551 continue;
5552 } else {
5553 portList[out++] = fca_port->port_handle;
5554 }
5555 }
5556 }
5557
5558 if (out <= count) {
5559 for (in = 0; in < out; in++) {
5560 (void) ddi_pathname(portList[in]->fp_port_dip,
5561 &pathList[MAXPATHLEN * in]);
5562 }
5563 }
5564 mutex_exit(&fctl_port_lock);
5565 kmem_free(portList, sizeof (*portList) * maxPorts);
5566 return (out);
5567 }
5568
5569 uint32_t
5570 fc_ulp_get_rscn_count(opaque_t port_handle)
5571 {
5572 uint32_t count;
5573 fc_local_port_t *port;
5574
5575 port = (fc_local_port_t *)port_handle;
5576 mutex_enter(&port->fp_mutex);
5577 count = port->fp_rscn_count;
5578 mutex_exit(&port->fp_mutex);
5579
5580 return (count);
5581 }
5582
5583
5584 /*
5585 * This function is a very similar to fctl_add_orphan except that it expects
5586 * that the fp_mutex and pd_mutex of the pd passed in are held coming in.
5587 *
5588 * Note that there is a lock hierarchy here (fp_mutex should be held first) but
5589 * since this function could be called with a different pd's pd_mutex held, we
5590 * should take care not to release fp_mutex in this function.
5591 */
5592 int
5593 fctl_add_orphan_held(fc_local_port_t *port, fc_remote_port_t *pd)
5594 {
5595 int rval = FC_FAILURE;
5596 la_wwn_t pwwn;
5597 fc_orphan_t *orp;
5598 fc_orphan_t *orphan;
5599
5600 ASSERT(MUTEX_HELD(&port->fp_mutex));
5601 ASSERT(MUTEX_HELD(&pd->pd_mutex));
5602
5603 pwwn = pd->pd_port_name;
5604
5605 for (orp = port->fp_orphan_list; orp != NULL; orp = orp->orp_next) {
5606 if (fctl_wwn_cmp(&orp->orp_pwwn, &pwwn) == 0) {
5607 return (FC_SUCCESS);
5608 }
5609 }
5610
5611 orphan = kmem_zalloc(sizeof (*orphan), KM_NOSLEEP);
5612 if (orphan) {
5613 orphan->orp_pwwn = pwwn;
5614 orphan->orp_tstamp = ddi_get_lbolt();
5615
5616 if (port->fp_orphan_list) {
5617 ASSERT(port->fp_orphan_count > 0);
5618 orphan->orp_next = port->fp_orphan_list;
5619 }
5620 port->fp_orphan_list = orphan;
5621 port->fp_orphan_count++;
5622
5623 rval = FC_SUCCESS;
5624 }
5625
5626 return (rval);
5627 }
5628
5629 int
5630 fctl_add_orphan(fc_local_port_t *port, fc_remote_port_t *pd, int sleep)
5631 {
5632 int rval = FC_FAILURE;
5633 la_wwn_t pwwn;
5634 fc_orphan_t *orp;
5635 fc_orphan_t *orphan;
5636
5637 mutex_enter(&port->fp_mutex);
5638
5639 mutex_enter(&pd->pd_mutex);
5640 pwwn = pd->pd_port_name;
5641 mutex_exit(&pd->pd_mutex);
5642
5643 for (orp = port->fp_orphan_list; orp != NULL; orp = orp->orp_next) {
5644 if (fctl_wwn_cmp(&orp->orp_pwwn, &pwwn) == 0) {
5645 mutex_exit(&port->fp_mutex);
5646 return (FC_SUCCESS);
5647 }
5648 }
5649 mutex_exit(&port->fp_mutex);
5650
5651 orphan = kmem_zalloc(sizeof (*orphan), sleep);
5652 if (orphan != NULL) {
5653 mutex_enter(&port->fp_mutex);
5654
5655 orphan->orp_pwwn = pwwn;
5656 orphan->orp_tstamp = ddi_get_lbolt();
5657
5658 if (port->fp_orphan_list) {
5659 ASSERT(port->fp_orphan_count > 0);
5660 orphan->orp_next = port->fp_orphan_list;
5661 }
5662 port->fp_orphan_list = orphan;
5663 port->fp_orphan_count++;
5664 mutex_exit(&port->fp_mutex);
5665
5666 rval = FC_SUCCESS;
5667 }
5668
5669 return (rval);
5670 }
5671
5672
5673 int
5674 fctl_remove_if_orphan(fc_local_port_t *port, la_wwn_t *pwwn)
5675 {
5676 int rval = FC_FAILURE;
5677 fc_orphan_t *prev = NULL;
5678 fc_orphan_t *orp;
5679
5680 mutex_enter(&port->fp_mutex);
5681 for (orp = port->fp_orphan_list; orp != NULL; orp = orp->orp_next) {
5682 if (fctl_wwn_cmp(&orp->orp_pwwn, pwwn) == 0) {
5683 if (prev) {
5684 prev->orp_next = orp->orp_next;
5685 } else {
5686 ASSERT(port->fp_orphan_list == orp);
5687 port->fp_orphan_list = orp->orp_next;
5688 }
5689 port->fp_orphan_count--;
5690 rval = FC_SUCCESS;
5691 break;
5692 }
5693 prev = orp;
5694 }
5695 mutex_exit(&port->fp_mutex);
5696
5697 if (rval == FC_SUCCESS) {
5698 kmem_free(orp, sizeof (*orp));
5699 }
5700
5701 return (rval);
5702 }
5703
5704
5705 static void
5706 fctl_print_if_not_orphan(fc_local_port_t *port, fc_remote_port_t *pd)
5707 {
5708 char ww_name[17];
5709 la_wwn_t pwwn;
5710 fc_orphan_t *orp;
5711
5712 mutex_enter(&port->fp_mutex);
5713
5714 mutex_enter(&pd->pd_mutex);
5715 pwwn = pd->pd_port_name;
5716 mutex_exit(&pd->pd_mutex);
5717
5718 for (orp = port->fp_orphan_list; orp != NULL; orp = orp->orp_next) {
5719 if (fctl_wwn_cmp(&orp->orp_pwwn, &pwwn) == 0) {
5720 mutex_exit(&port->fp_mutex);
5721 return;
5722 }
5723 }
5724 mutex_exit(&port->fp_mutex);
5725
5726 fc_wwn_to_str(&pwwn, ww_name);
5727
5728 cmn_err(CE_WARN, "!fctl(%d): N_x Port with D_ID=%x, PWWN=%s"
5729 " disappeared from fabric", port->fp_instance,
5730 pd->pd_port_id.port_id, ww_name);
5731 }
5732
5733
5734 /* ARGSUSED */
5735 static void
5736 fctl_link_reset_done(opaque_t port_handle, uchar_t result)
5737 {
5738 fc_local_port_t *port = port_handle;
5739
5740 mutex_enter(&port->fp_mutex);
5741 port->fp_soft_state &= ~FP_SOFT_IN_LINK_RESET;
5742 mutex_exit(&port->fp_mutex);
5743
5744 fctl_idle_port(port);
5745 }
5746
5747
5748 static int
5749 fctl_error(int fc_errno, char **errmsg)
5750 {
5751 int count;
5752
5753 for (count = 0; count < sizeof (fc_errlist) /
5754 sizeof (fc_errlist[0]); count++) {
5755 if (fc_errlist[count].fc_errno == fc_errno) {
5756 *errmsg = fc_errlist[count].fc_errname;
5757 return (FC_SUCCESS);
5758 }
5759 }
5760 *errmsg = fctl_undefined;
5761
5762 return (FC_FAILURE);
5763 }
5764
5765
5766 /*
5767 * Return number of successful translations.
5768 * Anybody with some userland programming experience would have
5769 * figured it by now that the return value exactly resembles that
5770 * of scanf(3c). This function returns a count of successful
5771 * translations. It could range from 0 (no match for state, reason,
5772 * action, expln) to 4 (successful matches for all state, reason,
5773 * action, expln) and where translation isn't successful into a
5774 * friendlier message the relevent field is set to "Undefined"
5775 */
5776 static int
5777 fctl_pkt_error(fc_packet_t *pkt, char **state, char **reason,
5778 char **action, char **expln)
5779 {
5780 int ret;
5781 int len;
5782 int index;
5783 fc_pkt_error_t *error;
5784 fc_pkt_reason_t *reason_b; /* Base pointer */
5785 fc_pkt_action_t *action_b; /* Base pointer */
5786 fc_pkt_expln_t *expln_b; /* Base pointer */
5787
5788 ret = 0;
5789 *state = *reason = *action = *expln = fctl_undefined;
5790
5791 len = sizeof (fc_pkt_errlist) / sizeof fc_pkt_errlist[0];
5792 for (index = 0; index < len; index++) {
5793 error = fc_pkt_errlist + index;
5794 if (pkt->pkt_state == error->pkt_state) {
5795 *state = error->pkt_msg;
5796 ret++;
5797
5798 reason_b = error->pkt_reason;
5799 action_b = error->pkt_action;
5800 expln_b = error->pkt_expln;
5801
5802 while (reason_b != NULL &&
5803 reason_b->reason_val != FC_REASON_INVALID) {
5804 if (reason_b->reason_val == pkt->pkt_reason) {
5805 *reason = reason_b->reason_msg;
5806 ret++;
5807 break;
5808 }
5809 reason_b++;
5810 }
5811
5812 while (action_b != NULL &&
5813 action_b->action_val != FC_ACTION_INVALID) {
5814 if (action_b->action_val == pkt->pkt_action) {
5815 *action = action_b->action_msg;
5816 ret++;
5817 break;
5818 }
5819 action_b++;
5820 }
5821
5822 while (expln_b != NULL &&
5823 expln_b->expln_val != FC_EXPLN_INVALID) {
5824 if (expln_b->expln_val == pkt->pkt_expln) {
5825 *expln = expln_b->expln_msg;
5826 ret++;
5827 break;
5828 }
5829 expln_b++;
5830 }
5831 break;
5832 }
5833 }
5834
5835 return (ret);
5836 }
5837
5838
5839 /*
5840 * Remove all port devices that are marked OLD, remove
5841 * corresponding node devices (fc_remote_node_t)
5842 */
5843 void
5844 fctl_remove_oldies(fc_local_port_t *port)
5845 {
5846 int index;
5847 int initiator;
5848 fc_remote_node_t *node;
5849 struct pwwn_hash *head;
5850 fc_remote_port_t *pd;
5851 fc_remote_port_t *old_pd;
5852 fc_remote_port_t *last_pd;
5853
5854 /*
5855 * Nuke all OLD devices
5856 */
5857 mutex_enter(&port->fp_mutex);
5858
5859 for (index = 0; index < pwwn_table_size; index++) {
5860 head = &port->fp_pwwn_table[index];
5861 last_pd = NULL;
5862 pd = head->pwwn_head;
5863
5864 while (pd != NULL) {
5865 mutex_enter(&pd->pd_mutex);
5866 if (pd->pd_type != PORT_DEVICE_OLD) {
5867 mutex_exit(&pd->pd_mutex);
5868 last_pd = pd;
5869 pd = pd->pd_wwn_hnext;
5870 continue;
5871 }
5872
5873 /*
5874 * Remove this from the PWWN hash table
5875 */
5876 old_pd = pd;
5877 pd = old_pd->pd_wwn_hnext;
5878
5879 if (last_pd == NULL) {
5880 ASSERT(old_pd == head->pwwn_head);
5881 head->pwwn_head = pd;
5882 } else {
5883 last_pd->pd_wwn_hnext = pd;
5884 }
5885 head->pwwn_count--;
5886 /*
5887 * Make sure we tie fp_dev_count to the size of the
5888 * pwwn_table
5889 */
5890 port->fp_dev_count--;
5891 old_pd->pd_wwn_hnext = NULL;
5892
5893 fctl_delist_did_table(port, old_pd);
5894 node = old_pd->pd_remote_nodep;
5895 ASSERT(node != NULL);
5896
5897 initiator = (old_pd->pd_recepient ==
5898 PD_PLOGI_INITIATOR) ? 1 : 0;
5899
5900 mutex_exit(&old_pd->pd_mutex);
5901
5902 if (FC_IS_TOP_SWITCH(port->fp_topology) && initiator) {
5903 mutex_exit(&port->fp_mutex);
5904
5905 (void) fctl_add_orphan(port, old_pd,
5906 KM_NOSLEEP);
5907 } else {
5908 mutex_exit(&port->fp_mutex);
5909 }
5910
5911 if (fctl_destroy_remote_port(port, old_pd) == 0) {
5912 if (node) {
5913 fctl_destroy_remote_node(node);
5914 }
5915 }
5916
5917 mutex_enter(&port->fp_mutex);
5918 }
5919 }
5920
5921 mutex_exit(&port->fp_mutex);
5922 }
5923
5924
5925 static void
5926 fctl_check_alpa_list(fc_local_port_t *port, fc_remote_port_t *pd)
5927 {
5928 ASSERT(MUTEX_HELD(&port->fp_mutex));
5929 ASSERT(port->fp_topology == FC_TOP_PRIVATE_LOOP);
5930
5931 if (fctl_is_alpa_present(port, pd->pd_port_id.port_id) == FC_SUCCESS) {
5932 return;
5933 }
5934
5935 cmn_err(CE_WARN, "!fctl(%d): AL_PA=0x%x doesn't exist in LILP map",
5936 port->fp_instance, pd->pd_port_id.port_id);
5937 }
5938
5939
5940 static int
5941 fctl_is_alpa_present(fc_local_port_t *port, uchar_t alpa)
5942 {
5943 int index;
5944
5945 ASSERT(MUTEX_HELD(&port->fp_mutex));
5946 ASSERT(port->fp_topology == FC_TOP_PRIVATE_LOOP);
5947
5948 for (index = 0; index < port->fp_lilp_map.lilp_length; index++) {
5949 if (port->fp_lilp_map.lilp_alpalist[index] == alpa) {
5950 return (FC_SUCCESS);
5951 }
5952 }
5953
5954 return (FC_FAILURE);
5955 }
5956
5957
5958 fc_remote_port_t *
5959 fctl_lookup_pd_by_did(fc_local_port_t *port, uint32_t d_id)
5960 {
5961 int index;
5962 struct pwwn_hash *head;
5963 fc_remote_port_t *pd;
5964
5965 ASSERT(MUTEX_HELD(&port->fp_mutex));
5966
5967 for (index = 0; index < pwwn_table_size; index++) {
5968 head = &port->fp_pwwn_table[index];
5969 pd = head->pwwn_head;
5970
5971 while (pd != NULL) {
5972 mutex_enter(&pd->pd_mutex);
5973 if (pd->pd_port_id.port_id == d_id) {
5974 mutex_exit(&pd->pd_mutex);
5975 return (pd);
5976 }
5977 mutex_exit(&pd->pd_mutex);
5978 pd = pd->pd_wwn_hnext;
5979 }
5980 }
5981
5982 return (pd);
5983 }
5984
5985
5986 /*
5987 * trace debugging
5988 */
5989 void
5990 fc_trace_debug(fc_trace_logq_t *logq, caddr_t name, int dflag, int dlevel,
5991 int errno, const char *fmt, ...)
5992 {
5993 char buf[FC_MAX_TRACE_BUF_LEN + 3]; /* 3 is for "\n" */
5994 char *bufptr = buf;
5995 va_list ap;
5996 int cnt = 0;
5997
5998 if ((dlevel & dflag) == 0) {
5999 return;
6000 }
6001
6002 if (name) {
6003 cnt = snprintf(buf, FC_MAX_TRACE_BUF_LEN + 1, "%d=>%s::",
6004 logq->il_id++, name);
6005 } else {
6006 cnt = snprintf(buf, FC_MAX_TRACE_BUF_LEN + 1, "%d=>trace::",
6007 logq->il_id++);
6008 }
6009
6010 if (cnt < FC_MAX_TRACE_BUF_LEN) {
6011 va_start(ap, fmt);
6012 cnt += vsnprintf(buf + cnt, FC_MAX_TRACE_BUF_LEN + 1 - cnt,
6013 fmt, ap);
6014 va_end(ap);
6015 }
6016
6017 if (cnt > FC_MAX_TRACE_BUF_LEN) {
6018 cnt = FC_MAX_TRACE_BUF_LEN;
6019 }
6020 if (errno && (cnt < FC_MAX_TRACE_BUF_LEN)) {
6021 cnt += snprintf(buf + cnt, FC_MAX_TRACE_BUF_LEN + 1 - cnt,
6022 "error=0x%x\n", errno);
6023 }
6024 (void) snprintf(buf + cnt, FC_MAX_TRACE_BUF_LEN + 3 - cnt, "\n");
6025
6026 if (logq && (dlevel & FC_TRACE_LOG_BUF) != 0) {
6027 fc_trace_logmsg(logq, buf, dlevel);
6028 }
6029
6030 /*
6031 * We do not want to print the log numbers that appear as
6032 * random numbers at the console and messages files, to
6033 * the user.
6034 */
6035 if ((bufptr = strchr(buf, '>')) == NULL) {
6036 /*
6037 * We would have added the a string with "=>" above and so,
6038 * ideally, we should not get here at all. But, if we do,
6039 * we'll just use the full buf.
6040 */
6041 bufptr = buf;
6042 } else {
6043 bufptr++;
6044 }
6045
6046 switch (dlevel & FC_TRACE_LOG_MASK) {
6047 case FC_TRACE_LOG_CONSOLE:
6048 cmn_err(CE_WARN, "%s", bufptr);
6049 break;
6050
6051 case FC_TRACE_LOG_CONSOLE_MSG:
6052 cmn_err(CE_WARN, "%s", bufptr);
6053 break;
6054
6055 case FC_TRACE_LOG_MSG:
6056 cmn_err(CE_WARN, "!%s", bufptr);
6057 break;
6058
6059 default:
6060 break;
6061 }
6062 }
6063
6064
6065 /*
6066 * This function can block
6067 */
6068 fc_trace_logq_t *
6069 fc_trace_alloc_logq(int maxsize)
6070 {
6071 fc_trace_logq_t *logq;
6072
6073 logq = kmem_zalloc(sizeof (*logq), KM_SLEEP);
6074
6075 mutex_init(&logq->il_lock, NULL, MUTEX_DRIVER, NULL);
6076 logq->il_hiwat = maxsize;
6077 logq->il_flags |= FC_TRACE_LOGQ_V2;
6078
6079 return (logq);
6080 }
6081
6082
6083 void
6084 fc_trace_free_logq(fc_trace_logq_t *logq)
6085 {
6086 mutex_enter(&logq->il_lock);
6087 while (logq->il_msgh) {
6088 fc_trace_freemsg(logq);
6089 }
6090 mutex_exit(&logq->il_lock);
6091
6092 mutex_destroy(&logq->il_lock);
6093 kmem_free(logq, sizeof (*logq));
6094 }
6095
6096
6097 /* ARGSUSED */
6098 void
6099 fc_trace_logmsg(fc_trace_logq_t *logq, caddr_t buf, int level)
6100 {
6101 int qfull = 0;
6102 fc_trace_dmsg_t *dmsg;
6103
6104 dmsg = kmem_alloc(sizeof (*dmsg), KM_NOSLEEP);
6105 if (dmsg == NULL) {
6106 mutex_enter(&logq->il_lock);
6107 logq->il_afail++;
6108 mutex_exit(&logq->il_lock);
6109
6110 return;
6111 }
6112
6113 gethrestime(&dmsg->id_time);
6114
6115 dmsg->id_size = strlen(buf) + 1;
6116 dmsg->id_buf = kmem_alloc(dmsg->id_size, KM_NOSLEEP);
6117 if (dmsg->id_buf == NULL) {
6118 kmem_free(dmsg, sizeof (*dmsg));
6119
6120 mutex_enter(&logq->il_lock);
6121 logq->il_afail++;
6122 mutex_exit(&logq->il_lock);
6123
6124 return;
6125 }
6126 bcopy(buf, dmsg->id_buf, strlen(buf));
6127 dmsg->id_buf[strlen(buf)] = '\0';
6128
6129 mutex_enter(&logq->il_lock);
6130
6131 logq->il_size += dmsg->id_size;
6132 if (logq->il_size >= logq->il_hiwat) {
6133 qfull = 1;
6134 }
6135
6136 if (qfull) {
6137 fc_trace_freemsg(logq);
6138 }
6139
6140 dmsg->id_next = NULL;
6141 if (logq->il_msgt) {
6142 logq->il_msgt->id_next = dmsg;
6143 } else {
6144 ASSERT(logq->il_msgh == NULL);
6145 logq->il_msgh = dmsg;
6146 }
6147 logq->il_msgt = dmsg;
6148
6149 mutex_exit(&logq->il_lock);
6150 }
6151
6152
6153 static void
6154 fc_trace_freemsg(fc_trace_logq_t *logq)
6155 {
6156 fc_trace_dmsg_t *dmsg;
6157
6158 ASSERT(MUTEX_HELD(&logq->il_lock));
6159
6160 if ((dmsg = logq->il_msgh) != NULL) {
6161 logq->il_msgh = dmsg->id_next;
6162 if (logq->il_msgh == NULL) {
6163 logq->il_msgt = NULL;
6164 }
6165
6166 logq->il_size -= dmsg->id_size;
6167 kmem_free(dmsg->id_buf, dmsg->id_size);
6168 kmem_free(dmsg, sizeof (*dmsg));
6169 } else {
6170 ASSERT(logq->il_msgt == NULL);
6171 }
6172 }
6173
6174 /*
6175 * Used by T11 FC-HBA to fetch discovered ports by index.
6176 * Returns NULL if the index isn't valid.
6177 */
6178 fc_remote_port_t *
6179 fctl_lookup_pd_by_index(fc_local_port_t *port, uint32_t index)
6180 {
6181 int outer;
6182 int match = 0;
6183 struct pwwn_hash *head;
6184 fc_remote_port_t *pd;
6185
6186 ASSERT(MUTEX_HELD(&port->fp_mutex));
6187
6188 for (outer = 0;
6189 outer < pwwn_table_size && match <= index;
6190 outer++) {
6191 head = &port->fp_pwwn_table[outer];
6192 pd = head->pwwn_head;
6193 if (pd != NULL) match ++;
6194
6195 while (pd != NULL && match <= index) {
6196 pd = pd->pd_wwn_hnext;
6197 if (pd != NULL) match ++;
6198 }
6199 }
6200
6201 return (pd);
6202 }
6203
6204 /*
6205 * Search for a matching Node or Port WWN in the discovered port list
6206 */
6207 fc_remote_port_t *
6208 fctl_lookup_pd_by_wwn(fc_local_port_t *port, la_wwn_t wwn)
6209 {
6210 int index;
6211 struct pwwn_hash *head;
6212 fc_remote_port_t *pd;
6213
6214 ASSERT(MUTEX_HELD(&port->fp_mutex));
6215
6216 for (index = 0; index < pwwn_table_size; index++) {
6217 head = &port->fp_pwwn_table[index];
6218 pd = head->pwwn_head;
6219
6220 while (pd != NULL) {
6221 mutex_enter(&pd->pd_mutex);
6222 if (bcmp(pd->pd_port_name.raw_wwn, wwn.raw_wwn,
6223 sizeof (la_wwn_t)) == 0) {
6224 mutex_exit(&pd->pd_mutex);
6225 return (pd);
6226 }
6227 if (bcmp(pd->pd_remote_nodep->fd_node_name.raw_wwn,
6228 wwn.raw_wwn, sizeof (la_wwn_t)) == 0) {
6229 mutex_exit(&pd->pd_mutex);
6230 return (pd);
6231 }
6232 mutex_exit(&pd->pd_mutex);
6233 pd = pd->pd_wwn_hnext;
6234 }
6235 }
6236 /* No match */
6237 return (NULL);
6238 }
6239
6240
6241 /*
6242 * Count the number of ports on this adapter.
6243 * This routine will walk the port list and count up the number of adapters
6244 * with matching fp_hba_port_attrs.hba_fru_details.high and
6245 * fp_hba_port_attrs.hba_fru_details.low.
6246 *
6247 * port->fp_mutex must not be held.
6248 */
6249 int
6250 fctl_count_fru_ports(fc_local_port_t *port, int npivflag)
6251 {
6252 fca_hba_fru_details_t *fru;
6253 fc_fca_port_t *fca_port;
6254 fc_local_port_t *tmpPort = NULL;
6255 uint32_t count = 1;
6256
6257 mutex_enter(&fctl_port_lock);
6258
6259 mutex_enter(&port->fp_mutex);
6260 fru = &port->fp_hba_port_attrs.hba_fru_details;
6261
6262 /* Detect FCA drivers that don't support linking HBA ports */
6263 if (fru->high == 0 && fru->low == 0 && fru->port_index == 0) {
6264 mutex_exit(&port->fp_mutex);
6265 mutex_exit(&fctl_port_lock);
6266 return (1);
6267 }
6268
6269 for (fca_port = fctl_fca_portlist; fca_port != NULL;
6270 fca_port = fca_port->port_next) {
6271 tmpPort = fca_port->port_handle;
6272 if (tmpPort == port) {
6273 continue;
6274 }
6275 mutex_enter(&tmpPort->fp_mutex);
6276
6277 /*
6278 * If an FCA driver returns unique fru->high and fru->low for
6279 * ports on the same card, there is no way for the transport
6280 * layer to determine that the two ports on the same FRU. So,
6281 * the discovery of the ports on a same FRU is limited to what
6282 * the FCA driver can report back.
6283 */
6284 if (tmpPort->fp_hba_port_attrs.hba_fru_details.high ==
6285 fru->high &&
6286 tmpPort->fp_hba_port_attrs.hba_fru_details.low ==
6287 fru->low) {
6288 /* Now double check driver */
6289 if (strncmp(port->fp_hba_port_attrs.driver_name,
6290 tmpPort->fp_hba_port_attrs.driver_name,
6291 FCHBA_DRIVER_NAME_LEN) == 0) {
6292 if (!npivflag ||
6293 (tmpPort->fp_npiv_type != FC_NPIV_PORT)) {
6294 count++;
6295 }
6296 } /* Else, different FCA driver */
6297 } /* Else not the same HBA FRU */
6298 mutex_exit(&tmpPort->fp_mutex);
6299 }
6300
6301 mutex_exit(&port->fp_mutex);
6302 mutex_exit(&fctl_port_lock);
6303
6304 return (count);
6305 }
6306
6307 fc_fca_port_t *
6308 fctl_local_port_list_add(fc_fca_port_t *list, fc_local_port_t *port)
6309 {
6310 fc_fca_port_t *tmp = list, *newentry = NULL;
6311
6312 newentry = kmem_zalloc(sizeof (fc_fca_port_t), KM_NOSLEEP);
6313 if (newentry == NULL) {
6314 return (list);
6315 }
6316 newentry->port_handle = port;
6317
6318 if (tmp == NULL) {
6319 return (newentry);
6320 }
6321 while (tmp->port_next != NULL) tmp = tmp->port_next;
6322 tmp->port_next = newentry;
6323
6324 return (list);
6325 }
6326
6327 void
6328 fctl_local_port_list_free(fc_fca_port_t *list)
6329 {
6330 fc_fca_port_t *tmp = list, *nextentry;
6331
6332 if (tmp == NULL) {
6333 return;
6334 }
6335
6336 while (tmp != NULL) {
6337 nextentry = tmp->port_next;
6338 kmem_free(tmp, sizeof (*tmp));
6339 tmp = nextentry;
6340 }
6341 }
6342
6343 /*
6344 * Fetch another port on the HBA FRU based on index.
6345 * Returns NULL if index not found.
6346 *
6347 * port->fp_mutex must not be held.
6348 */
6349 fc_local_port_t *
6350 fctl_get_adapter_port_by_index(fc_local_port_t *port, uint32_t port_index)
6351 {
6352 fca_hba_fru_details_t *fru;
6353 fc_fca_port_t *fca_port;
6354 fc_local_port_t *tmpPort = NULL;
6355 fc_fca_port_t *list = NULL, *tmpEntry;
6356 fc_local_port_t *phyPort, *virPort = NULL;
6357 int index, phyPortNum = 0;
6358
6359 mutex_enter(&fctl_port_lock);
6360
6361 mutex_enter(&port->fp_mutex);
6362 fru = &port->fp_hba_port_attrs.hba_fru_details;
6363
6364 /* Are we looking for this port? */
6365 if (fru->port_index == port_index) {
6366 mutex_exit(&port->fp_mutex);
6367 mutex_exit(&fctl_port_lock);
6368 return (port);
6369 }
6370
6371 /* Detect FCA drivers that don't support linking HBA ports */
6372 if (fru->high == 0 && fru->low == 0 && fru->port_index == 0) {
6373 mutex_exit(&port->fp_mutex);
6374 mutex_exit(&fctl_port_lock);
6375 return (NULL);
6376 }
6377
6378 list = fctl_local_port_list_add(list, port);
6379 phyPortNum++;
6380 /* Loop through all known ports */
6381 for (fca_port = fctl_fca_portlist; fca_port != NULL;
6382 fca_port = fca_port->port_next) {
6383 tmpPort = fca_port->port_handle;
6384 if (tmpPort == port) {
6385 /* Skip the port that was passed in as the argument */
6386 continue;
6387 }
6388 mutex_enter(&tmpPort->fp_mutex);
6389
6390 /* See if this port is on the same HBA FRU (fast check) */
6391 if (tmpPort->fp_hba_port_attrs.hba_fru_details.high ==
6392 fru->high &&
6393 tmpPort->fp_hba_port_attrs.hba_fru_details.low ==
6394 fru->low) {
6395 /* Now double check driver (slower check) */
6396 if (strncmp(port->fp_hba_port_attrs.driver_name,
6397 tmpPort->fp_hba_port_attrs.driver_name,
6398 FCHBA_DRIVER_NAME_LEN) == 0) {
6399
6400 fru =
6401 &tmpPort->fp_hba_port_attrs.hba_fru_details;
6402 /* Check for the matching port_index */
6403 if ((tmpPort->fp_npiv_type != FC_NPIV_PORT) &&
6404 (fru->port_index == port_index)) {
6405 /* Found it! */
6406 mutex_exit(&tmpPort->fp_mutex);
6407 mutex_exit(&port->fp_mutex);
6408 mutex_exit(&fctl_port_lock);
6409 fctl_local_port_list_free(list);
6410 return (tmpPort);
6411 }
6412 if (tmpPort->fp_npiv_type != FC_NPIV_PORT) {
6413 (void) fctl_local_port_list_add(list,
6414 tmpPort);
6415 phyPortNum++;
6416 }
6417 } /* Else, different FCA driver */
6418 } /* Else not the same HBA FRU */
6419 mutex_exit(&tmpPort->fp_mutex);
6420
6421 }
6422
6423 /* scan all physical port on same chip to find virtual port */
6424 tmpEntry = list;
6425 index = phyPortNum - 1;
6426 virPort = NULL;
6427 while (index < port_index) {
6428 if (tmpEntry == NULL) {
6429 break;
6430 }
6431 if (virPort == NULL) {
6432 phyPort = tmpEntry->port_handle;
6433 virPort = phyPort->fp_port_next;
6434 if (virPort == NULL) {
6435 tmpEntry = tmpEntry->port_next;
6436 continue;
6437 }
6438 } else {
6439 virPort = virPort->fp_port_next;
6440 }
6441 if (virPort == phyPort) {
6442 tmpEntry = tmpEntry->port_next;
6443 virPort = NULL;
6444 } else {
6445 index++;
6446 }
6447 }
6448 mutex_exit(&port->fp_mutex);
6449 mutex_exit(&fctl_port_lock);
6450
6451 fctl_local_port_list_free(list);
6452 if (virPort) {
6453 return (virPort);
6454 }
6455 return (NULL);
6456 }
6457
6458 int
6459 fctl_busy_port(fc_local_port_t *port)
6460 {
6461 ASSERT(!MUTEX_HELD(&port->fp_mutex));
6462
6463 mutex_enter(&port->fp_mutex);
6464 if (port->fp_soft_state & FP_SOFT_NO_PMCOMP) {
6465 /*
6466 * If fctl_busy_port() is called before we've registered our
6467 * PM components, we return success. We need to be aware of
6468 * this because the caller will eventually call fctl_idle_port.
6469 * This wouldn't be a problem except that if we have
6470 * registered our PM components in the meantime, we will
6471 * then be idling a component that was never busied. PM
6472 * will be very unhappy if we do this. Thus, we keep
6473 * track of this with port->fp_pm_busy_nocomp.
6474 */
6475 port->fp_pm_busy_nocomp++;
6476 mutex_exit(&port->fp_mutex);
6477 return (0);
6478 }
6479 port->fp_pm_busy++;
6480 mutex_exit(&port->fp_mutex);
6481
6482 if (pm_busy_component(port->fp_port_dip,
6483 FP_PM_COMPONENT) != DDI_SUCCESS) {
6484 mutex_enter(&port->fp_mutex);
6485 port->fp_pm_busy--;
6486 mutex_exit(&port->fp_mutex);
6487 return (ENXIO);
6488 }
6489
6490 mutex_enter(&port->fp_mutex);
6491 if (port->fp_pm_level == FP_PM_PORT_DOWN) {
6492 mutex_exit(&port->fp_mutex);
6493 if (pm_raise_power(port->fp_port_dip, FP_PM_COMPONENT,
6494 FP_PM_PORT_UP) != DDI_SUCCESS) {
6495
6496 mutex_enter(&port->fp_mutex);
6497 port->fp_pm_busy--;
6498 mutex_exit(&port->fp_mutex);
6499
6500 (void) pm_idle_component(port->fp_port_dip,
6501 FP_PM_COMPONENT);
6502 return (EIO);
6503 }
6504 return (0);
6505 }
6506 mutex_exit(&port->fp_mutex);
6507 return (0);
6508 }
6509
6510 void
6511 fctl_idle_port(fc_local_port_t *port)
6512 {
6513 ASSERT(!MUTEX_HELD(&port->fp_mutex));
6514
6515 mutex_enter(&port->fp_mutex);
6516
6517 /*
6518 * If port->fp_pm_busy_nocomp is > 0, that means somebody had
6519 * called fctl_busy_port prior to us registering our PM components.
6520 * In that case, we just decrement fp_pm_busy_nocomp and return.
6521 */
6522
6523 if (port->fp_pm_busy_nocomp > 0) {
6524 port->fp_pm_busy_nocomp--;
6525 mutex_exit(&port->fp_mutex);
6526 return;
6527 }
6528
6529 port->fp_pm_busy--;
6530 mutex_exit(&port->fp_mutex);
6531
6532 (void) pm_idle_component(port->fp_port_dip, FP_PM_COMPONENT);
6533 }
6534
6535 /*
6536 * Function: fctl_tc_timer
6537 *
6538 * Description: Resets the value of the timed counter.
6539 *
6540 * Arguments: *tc Timed counter
6541 *
6542 * Return Value: Nothing
6543 *
6544 * Context: Kernel context.
6545 */
6546 static void
6547 fctl_tc_timer(void *arg)
6548 {
6549 timed_counter_t *tc = (timed_counter_t *)arg;
6550
6551 ASSERT(tc != NULL);
6552 ASSERT(tc->sig == tc);
6553
6554 mutex_enter(&tc->mutex);
6555 if (tc->active) {
6556 tc->active = B_FALSE;
6557 tc->counter = 0;
6558 }
6559 mutex_exit(&tc->mutex);
6560 }
6561
6562 /*
6563 * Function: fctl_tc_constructor
6564 *
6565 * Description: Constructs a timed counter.
6566 *
6567 * Arguments: *tc Address where the timed counter will reside.
6568 * max_value Maximum value the counter is allowed to take.
6569 * timer Number of microseconds after which the counter
6570 * will be reset. The timer is started when the
6571 * value of the counter goes from 0 to 1.
6572 *
6573 * Return Value: Nothing
6574 *
6575 * Context: Kernel context.
6576 */
6577 void
6578 fctl_tc_constructor(timed_counter_t *tc, uint32_t max_value, clock_t timer)
6579 {
6580 ASSERT(tc != NULL);
6581 ASSERT(tc->sig != tc);
6582
6583 bzero(tc, sizeof (*tc));
6584 mutex_init(&tc->mutex, NULL, MUTEX_DRIVER, NULL);
6585 tc->timer = drv_usectohz(timer);
6586 tc->active = B_FALSE;
6587 tc->maxed_out = B_FALSE;
6588 tc->max_value = max_value;
6589 tc->sig = tc;
6590 }
6591
6592 /*
6593 * Function: fctl_tc_destructor
6594 *
6595 * Description: Destroyes a timed counter.
6596 *
6597 * Arguments: *tc Timed counter to destroy.
6598 *
6599 * Return Value: Nothing
6600 *
6601 * Context: Kernel context.
6602 */
6603 void
6604 fctl_tc_destructor(timed_counter_t *tc)
6605 {
6606 ASSERT(tc != NULL);
6607 ASSERT(tc->sig == tc);
6608 ASSERT(!mutex_owned(&tc->mutex));
6609
6610 mutex_enter(&tc->mutex);
6611 if (tc->active) {
6612 tc->active = B_FALSE;
6613 mutex_exit(&tc->mutex);
6614 (void) untimeout(tc->tid);
6615 mutex_enter(&tc->mutex);
6616 tc->sig = NULL;
6617 }
6618 mutex_exit(&tc->mutex);
6619 mutex_destroy(&tc->mutex);
6620 }
6621
6622 /*
6623 * Function: fctl_tc_increment
6624 *
6625 * Description: Increments a timed counter
6626 *
6627 * Arguments: *tc Timed counter to increment.
6628 *
6629 * Return Value: B_TRUE Counter reached the max value.
6630 * B_FALSE Counter hasn't reached the max value.
6631 *
6632 * Context: Kernel or interrupt context.
6633 */
6634 boolean_t
6635 fctl_tc_increment(timed_counter_t *tc)
6636 {
6637 ASSERT(tc != NULL);
6638 ASSERT(tc->sig == tc);
6639
6640 mutex_enter(&tc->mutex);
6641 if (!tc->maxed_out) {
6642 /* Hasn't maxed out yet. */
6643 ++tc->counter;
6644 if (tc->counter >= tc->max_value) {
6645 /* Just maxed out. */
6646 tc->maxed_out = B_TRUE;
6647 }
6648 if (!tc->active) {
6649 tc->tid = timeout(fctl_tc_timer, tc, tc->timer);
6650 tc->active = B_TRUE;
6651 }
6652 }
6653 mutex_exit(&tc->mutex);
6654
6655 return (tc->maxed_out);
6656 }
6657
6658 /*
6659 * Function: fctl_tc_reset
6660 *
6661 * Description: Resets a timed counter. The caller of this function has to
6662 * to make sure that while in fctl_tc_reset() fctl_tc_increment()
6663 * is not called.
6664 *
6665 * Arguments: *tc Timed counter to reset.
6666 *
6667 * Return Value: 0 Counter reached the max value.
6668 * Not 0 Counter hasn't reached the max value.
6669 *
6670 * Context: Kernel or interrupt context.
6671 */
6672 void
6673 fctl_tc_reset(timed_counter_t *tc)
6674 {
6675 ASSERT(tc != NULL);
6676 ASSERT(tc->sig == tc);
6677
6678 mutex_enter(&tc->mutex);
6679 tc->counter = 0;
6680 tc->maxed_out = B_FALSE;
6681 if (tc->active) {
6682 tc->active = B_FALSE;
6683 (void) untimeout(tc->tid);
6684 }
6685 mutex_exit(&tc->mutex);
6686 }
6687
6688 void
6689 fc_ulp_log_device_event(opaque_t port_handle, int type)
6690 {
6691 fc_local_port_t *port = port_handle;
6692 nvlist_t *attr_list;
6693
6694 if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE,
6695 KM_SLEEP) != DDI_SUCCESS) {
6696 return;
6697 }
6698
6699 if (nvlist_add_uint32(attr_list, "instance",
6700 port->fp_instance) != DDI_SUCCESS) {
6701 goto error;
6702 }
6703
6704 if (nvlist_add_byte_array(attr_list, "port-wwn",
6705 port->fp_service_params.nport_ww_name.raw_wwn,
6706 sizeof (la_wwn_t)) != DDI_SUCCESS) {
6707 goto error;
6708 }
6709
6710 (void) ddi_log_sysevent(port->fp_port_dip, DDI_VENDOR_SUNW, EC_SUNFC,
6711 (type == FC_ULP_DEVICE_ONLINE) ?
6712 ESC_SUNFC_DEVICE_ONLINE : ESC_SUNFC_DEVICE_OFFLINE,
6713 attr_list, NULL, DDI_SLEEP);
6714 nvlist_free(attr_list);
6715 return;
6716
6717 error:
6718 nvlist_free(attr_list);
6719 }