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