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