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