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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
24 */
25
26 #include <sys/conf.h>
27 #include <sys/file.h>
28 #include <sys/ddi.h>
29 #include <sys/sunddi.h>
30 #include <sys/modctl.h>
31 #include <sys/scsi/scsi.h>
32 #include <sys/scsi/impl/scsi_reset_notify.h>
33 #include <sys/disp.h>
34 #include <sys/byteorder.h>
35 #include <sys/varargs.h>
36 #include <sys/atomic.h>
37 #include <sys/sdt.h>
38
39 #include <sys/stmf.h>
40 #include <sys/stmf_ioctl.h>
41 #include <sys/portif.h>
42 #include <sys/fct.h>
43 #include <sys/fctio.h>
44
45 #include "fct_impl.h"
46 #include "discovery.h"
47
48 static int fct_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
49 static int fct_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
50 static int fct_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
51 void **result);
52 static int fct_open(dev_t *devp, int flag, int otype, cred_t *credp);
53 static int fct_close(dev_t dev, int flag, int otype, cred_t *credp);
54 static int fct_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
55 cred_t *credp, int *rval);
56 static int fct_fctiocmd(intptr_t data, int mode);
57 void fct_init_kstats(fct_i_local_port_t *iport);
58
59 static dev_info_t *fct_dip;
60 static struct cb_ops fct_cb_ops = {
61 fct_open, /* open */
62 fct_close, /* close */
63 nodev, /* strategy */
64 nodev, /* print */
65 nodev, /* dump */
66 nodev, /* read */
67 nodev, /* write */
68 fct_ioctl, /* ioctl */
69 nodev, /* devmap */
70 nodev, /* mmap */
71 nodev, /* segmap */
72 nochpoll, /* chpoll */
73 ddi_prop_op, /* cb_prop_op */
74 0, /* streamtab */
75 D_NEW | D_MP, /* cb_flag */
76 CB_REV, /* rev */
77 nodev, /* aread */
78 nodev /* awrite */
79 };
80
81 static struct dev_ops fct_ops = {
82 DEVO_REV,
83 0,
84 fct_getinfo,
85 nulldev, /* identify */
86 nulldev, /* probe */
87 fct_attach,
88 fct_detach,
89 nodev, /* reset */
90 &fct_cb_ops,
91 NULL, /* bus_ops */
92 NULL /* power */
93 };
94
95 #define FCT_NAME "COMSTAR FCT"
96 #define FCT_MODULE_NAME "fct"
97
98 extern struct mod_ops mod_driverops;
99 static struct modldrv modldrv = {
100 &mod_driverops,
101 FCT_NAME,
102 &fct_ops
103 };
104
105 static struct modlinkage modlinkage = {
106 MODREV_1,
107 &modldrv,
108 NULL
109 };
110
111 static uint32_t rportid_table_size = FCT_HASH_TABLE_SIZE;
112 static int max_cached_ncmds = FCT_MAX_CACHED_CMDS;
113 static fct_i_local_port_t *fct_iport_list = NULL;
114 static kmutex_t fct_global_mutex;
115 uint32_t fct_rscn_options = RSCN_OPTION_VERIFY;
116 /*
117 * This is to keep fibre channel from hanging if syseventd is
118 * not working correctly and the queue fills. It is a tunable
119 * to allow the user to force event logging to always happen
120 * which is the default.
121 */
122 static uint8_t fct_force_log = 0; /* use DDI_SLEEP on ddi_log_sysevent */
123
124 /*
125 * For use during core examination. These counts are normally really low
126 * since they're bumped during port operations. If a customer core shows
127 * really high values without having an uptime of a year something is most
128 * likely wrong with their environment.
129 */
130 int fct_els_cnt = 0;
131 int fct_abort_cnt = 0;
132
133 int
134 _init(void)
135 {
136 int ret;
137
138 ret = mod_install(&modlinkage);
139 if (ret)
140 return (ret);
141 /* XXX */
142 mutex_init(&fct_global_mutex, NULL, MUTEX_DRIVER, NULL);
143 return (ret);
144 }
145
146 int
147 _fini(void)
148 {
149 int ret;
150
151 ret = mod_remove(&modlinkage);
152 if (ret)
153 return (ret);
154 /* XXX */
155 mutex_destroy(&fct_global_mutex);
156 return (ret);
157 }
158
159 int
160 _info(struct modinfo *modinfop)
161 {
162 return (mod_info(&modlinkage, modinfop));
163 }
164
165 /* ARGSUSED */
166 static int
167 fct_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
168 {
169 switch (cmd) {
170 case DDI_INFO_DEVT2DEVINFO:
171 *result = fct_dip;
172 break;
173 case DDI_INFO_DEVT2INSTANCE:
174 *result = (void *)(uintptr_t)ddi_get_instance(fct_dip);
175 break;
176 default:
177 return (DDI_FAILURE);
178 }
179
180 return (DDI_SUCCESS);
181 }
182
183 static int
184 fct_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
185 {
186 switch (cmd) {
187 case DDI_ATTACH:
188 fct_dip = dip;
189
190 if (ddi_create_minor_node(dip, "admin", S_IFCHR, 0,
191 DDI_NT_STMF_PP, 0) != DDI_SUCCESS) {
192 break;
193 }
194 ddi_report_dev(dip);
195 return (DDI_SUCCESS);
196 }
197
198 return (DDI_FAILURE);
199 }
200
201 static int
202 fct_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
203 {
204 switch (cmd) {
205 case DDI_DETACH:
206 ddi_remove_minor_node(dip, 0);
207 return (DDI_SUCCESS);
208 }
209
210 return (DDI_FAILURE);
211 }
212
213 /* ARGSUSED */
214 static int
215 fct_open(dev_t *devp, int flag, int otype, cred_t *credp)
216 {
217 if (otype != OTYP_CHR)
218 return (EINVAL);
219 return (0);
220 }
221
222 /* ARGSUSED */
223 static int
224 fct_close(dev_t dev, int flag, int otype, cred_t *credp)
225 {
226 return (0);
227 }
228
229 /* ARGSUSED */
230 static int
231 fct_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
232 cred_t *credp, int *rval)
233 {
234 int ret = 0;
235
236 if ((cmd & 0xff000000) != FCT_IOCTL) {
237 return (ENOTTY);
238 }
239
240 if (drv_priv(credp) != 0) {
241 return (EPERM);
242 }
243
244 switch (cmd) {
245 case FCTIO_CMD:
246 ret = fct_fctiocmd(data, mode);
247 break;
248 default:
249 ret = ENOTTY;
250 break;
251 }
252
253 return (ret);
254 }
255
256 int
257 fct_copyin_iocdata(intptr_t data, int mode, fctio_t **fctio,
258 void **ibuf, void **abuf, void **obuf)
259 {
260 int ret = 0;
261
262 *ibuf = NULL;
263 *abuf = NULL;
264 *obuf = NULL;
265 *fctio = kmem_zalloc(sizeof (fctio_t), KM_SLEEP);
266 if (ddi_copyin((void *)data, *fctio, sizeof (fctio_t), mode)) {
267 ret = EFAULT;
268 goto copyin_iocdata_done;
269 }
270
271 if ((*fctio)->fctio_ilen) {
272 *ibuf = kmem_zalloc((*fctio)->fctio_ilen, KM_SLEEP);
273 if (ddi_copyin((void *)(unsigned long)(*fctio)->fctio_ibuf,
274 *ibuf, (*fctio)->fctio_ilen, mode)) {
275 ret = EFAULT;
276 goto copyin_iocdata_done;
277 }
278 }
279 if ((*fctio)->fctio_alen) {
280 *abuf = kmem_zalloc((*fctio)->fctio_alen, KM_SLEEP);
281 if (ddi_copyin((void *)(unsigned long)(*fctio)->fctio_abuf,
282 *abuf, (*fctio)->fctio_alen, mode)) {
283 ret = EFAULT;
284 goto copyin_iocdata_done;
285 }
286 }
287 if ((*fctio)->fctio_olen)
288 *obuf = kmem_zalloc((*fctio)->fctio_olen, KM_SLEEP);
289 if (ret == 0)
290 return (0);
291 ret = EFAULT;
292 copyin_iocdata_done:
293 if (*obuf) {
294 kmem_free(*obuf, (*fctio)->fctio_olen);
295 *obuf = NULL;
296 }
297 if (*abuf) {
298 kmem_free(*abuf, (*fctio)->fctio_alen);
299 *abuf = NULL;
300 }
301 if (*ibuf) {
302 kmem_free(*ibuf, (*fctio)->fctio_ilen);
303 *ibuf = NULL;
304 }
305 kmem_free(*fctio, sizeof (fctio_t));
306 return (ret);
307 }
308
309 int
310 fct_copyout_iocdata(intptr_t data, int mode, fctio_t *fctio, void *obuf)
311 {
312 int ret = 0;
313
314 if (fctio->fctio_olen) {
315 ret = ddi_copyout(obuf,
316 (void *)(unsigned long)fctio->fctio_obuf, fctio->fctio_olen,
317 mode);
318 if (ret) {
319 return (EFAULT);
320 }
321 }
322 ret = ddi_copyout(fctio, (void *)data, sizeof (fctio_t), mode);
323 if (ret) {
324 return (EFAULT);
325 }
326 return (0);
327 }
328
329 int
330 fct_get_port_list(char *pathList, int count)
331 {
332 fct_i_local_port_t *iport;
333 int i = 0, maxPorts = 0;
334
335 ASSERT(pathList != NULL);
336
337 mutex_enter(&fct_global_mutex);
338 for (iport = fct_iport_list; iport; iport = iport->iport_next) {
339 if (i < count)
340 bcopy(iport->iport_port->port_pwwn,
341 pathList + 8 * i, 8);
342 maxPorts ++;
343 i++;
344 }
345 mutex_exit(&fct_global_mutex);
346 return (maxPorts);
347 }
348
349 /* invoked with fct_global_mutex locked */
350 fct_i_local_port_t *
351 fct_get_iport_per_wwn(uint8_t *pwwn)
352 {
353 fct_i_local_port_t *iport;
354
355 ASSERT(mutex_owned(&fct_global_mutex));
356 for (iport = fct_iport_list; iport; iport = iport->iport_next) {
357 if (bcmp(iport->iport_port->port_pwwn, pwwn, 8) == 0)
358 return (iport);
359 }
360 return (NULL);
361 }
362
363 int
364 fct_get_adapter_attr(uint8_t *pwwn, fc_tgt_hba_adapter_attributes_t *hba_attr,
365 uint32_t *err_detail)
366 {
367 fct_i_local_port_t *iport;
368 fct_port_attrs_t *attr;
369
370 hba_attr->version = FCT_HBA_ADAPTER_ATTRIBUTES_VERSION;
371 iport = fct_get_iport_per_wwn(pwwn);
372 if (!iport) {
373 *err_detail = FCTIO_BADWWN;
374 return (ENXIO);
375 }
376
377 attr = (fct_port_attrs_t *)kmem_zalloc(sizeof (fct_port_attrs_t),
378 KM_SLEEP);
379 mutex_exit(&fct_global_mutex);
380 iport->iport_port->port_populate_hba_details(iport->iport_port, attr);
381 mutex_enter(&fct_global_mutex);
382
383 bcopy(attr->manufacturer, hba_attr->Manufacturer,
384 sizeof (hba_attr->Manufacturer));
385 bcopy(attr->serial_number, hba_attr->SerialNumber,
386 sizeof (hba_attr->SerialNumber));
387 bcopy(attr->model, hba_attr->Model, sizeof (hba_attr->Model));
388 bcopy(attr->model_description, hba_attr->ModelDescription,
389 sizeof (hba_attr->ModelDescription));
390 if (iport->iport_port->port_sym_node_name)
391 bcopy(iport->iport_port->port_sym_node_name,
392 hba_attr->NodeSymbolicName,
393 strlen(iport->iport_port->port_sym_node_name));
394 else
395 bcopy(utsname.nodename, hba_attr->NodeSymbolicName,
396 strlen(utsname.nodename));
397 bcopy(attr->hardware_version, hba_attr->HardwareVersion,
398 sizeof (hba_attr->HardwareVersion));
399 bcopy(attr->option_rom_version, hba_attr->OptionROMVersion,
400 sizeof (hba_attr->OptionROMVersion));
401 bcopy(attr->firmware_version, hba_attr->FirmwareVersion,
402 sizeof (hba_attr->FirmwareVersion));
403 hba_attr->VendorSpecificID = attr->vendor_specific_id;
404 bcopy(iport->iport_port->port_nwwn, hba_attr->NodeWWN,
405 sizeof (hba_attr->NodeWWN));
406
407 bcopy(attr->driver_name, hba_attr->DriverName,
408 sizeof (hba_attr->DriverName));
409 bcopy(attr->driver_version, hba_attr->DriverVersion,
410 sizeof (hba_attr->DriverVersion));
411
412
413 /* hba_attr->NumberOfPorts = fct_count_fru_ports(iport); */
414 hba_attr->NumberOfPorts = 1;
415
416 kmem_free(attr, sizeof (fct_port_attrs_t));
417 return (0);
418 }
419
420 int
421 fct_get_adapter_port_attr(fct_i_local_port_t *ilport, uint8_t *pwwn,
422 fc_tgt_hba_port_attributes_t *port_attr, uint32_t *err_detail)
423 {
424 fct_i_local_port_t *iport = ilport;
425 fct_i_remote_port_t *irp = NULL;
426 fct_port_attrs_t *attr;
427 int i = 0;
428
429 port_attr->version = FCT_HBA_PORT_ATTRIBUTES_VERSION;
430
431 if (!ilport) {
432 iport = fct_get_iport_per_wwn(pwwn);
433 if (!iport) {
434 *err_detail = FCTIO_BADWWN;
435 return (ENXIO);
436 }
437 }
438
439 attr = (fct_port_attrs_t *)kmem_zalloc(sizeof (fct_port_attrs_t),
440 KM_SLEEP);
441 mutex_exit(&fct_global_mutex);
442 iport->iport_port->port_populate_hba_details(iport->iport_port, attr);
443 mutex_enter(&fct_global_mutex);
444
445 port_attr->lastChange = iport->iport_last_change;
446 bcopy(iport->iport_port->port_nwwn, port_attr->NodeWWN,
447 sizeof (port_attr->NodeWWN));
448 bcopy(iport->iport_port->port_pwwn, port_attr->PortWWN,
449 sizeof (port_attr->PortWWN));
450 bzero(port_attr->FabricName, sizeof (port_attr->FabricName));
451 port_attr->PortFcId = iport->iport_link_info.portid;
452 if ((iport->iport_link_state & S_LINK_ONLINE) ||
453 (iport->iport_link_state & S_RCVD_LINK_UP)) {
454 port_attr->PortState = FC_HBA_PORTSTATE_ONLINE;
455 } else {
456 port_attr->PortState = FC_HBA_PORTSTATE_OFFLINE;
457 }
458 switch (iport->iport_link_info.port_topology) {
459 case PORT_TOPOLOGY_PT_TO_PT:
460 port_attr->PortType = FC_HBA_PORTTYPE_PTP;
461 break;
462 case PORT_TOPOLOGY_PRIVATE_LOOP:
463 port_attr->PortType = FC_HBA_PORTTYPE_LPORT;
464 break;
465 case PORT_TOPOLOGY_PUBLIC_LOOP:
466 port_attr->PortType = FC_HBA_PORTTYPE_NLPORT;
467 break;
468 case PORT_TOPOLOGY_FABRIC_PT_TO_PT:
469 port_attr->PortType = FC_HBA_PORTTYPE_FPORT;
470 break;
471 default:
472 port_attr->PortType = FC_HBA_PORTTYPE_UNKNOWN;
473 break;
474 }
475 port_attr->PortSupportedClassofService = attr->supported_cos;
476 port_attr->PortSupportedFc4Types[0] = 0;
477 port_attr->PortActiveFc4Types[2] = 1;
478 if (iport->iport_port->port_sym_port_name)
479 bcopy(iport->iport_port->port_sym_port_name,
480 port_attr->PortSymbolicName,
481 strlen(iport->iport_port->port_sym_port_name));
482 else if (iport->iport_port->port_default_alias)
483 bcopy(iport->iport_port->port_default_alias,
484 port_attr->PortSymbolicName,
485 strlen(iport->iport_port->port_default_alias));
486 else
487 port_attr->PortSymbolicName[0] = 0;
488 /* the definition is different so need to translate */
489 if (attr->supported_speed & PORT_SPEED_1G)
490 port_attr->PortSupportedSpeed |= FC_HBA_PORTSPEED_1GBIT;
491 if (attr->supported_speed & PORT_SPEED_2G)
492 port_attr->PortSupportedSpeed |= FC_HBA_PORTSPEED_2GBIT;
493 if (attr->supported_speed & PORT_SPEED_4G)
494 port_attr->PortSupportedSpeed |= FC_HBA_PORTSPEED_4GBIT;
495 if (attr->supported_speed & PORT_SPEED_8G)
496 port_attr->PortSupportedSpeed |= FC_HBA_PORTSPEED_8GBIT;
497 if (attr->supported_speed & PORT_SPEED_10G)
498 port_attr->PortSupportedSpeed |= FC_HBA_PORTSPEED_10GBIT;
499 if (attr->supported_speed & PORT_SPEED_16G)
500 port_attr->PortSupportedSpeed |= FC_HBA_PORTSPEED_16GBIT;
501 switch (iport->iport_link_info.port_speed) {
502 case PORT_SPEED_1G:
503 port_attr->PortSpeed = FC_HBA_PORTSPEED_1GBIT;
504 break;
505 case PORT_SPEED_2G:
506 port_attr->PortSpeed = FC_HBA_PORTSPEED_2GBIT;
507 break;
508 case PORT_SPEED_4G:
509 port_attr->PortSpeed = FC_HBA_PORTSPEED_4GBIT;
510 break;
511 case PORT_SPEED_8G:
512 port_attr->PortSpeed = FC_HBA_PORTSPEED_8GBIT;
513 break;
514 case PORT_SPEED_10G:
515 port_attr->PortSpeed = FC_HBA_PORTSPEED_10GBIT;
516 break;
517 case PORT_SPEED_16G:
518 port_attr->PortSpeed = FC_HBA_PORTSPEED_16GBIT;
519 break;
520 default:
521 port_attr->PortSpeed = FC_HBA_PORTSPEED_UNKNOWN;
522 break;
523 }
524 port_attr->PortMaxFrameSize = attr->max_frame_size;
525 rw_enter(&iport->iport_lock, RW_READER);
526 port_attr->NumberofDiscoveredPorts = iport->iport_nrps_login;
527 for (; i < iport->iport_port->port_max_logins; i++) {
528 irp = iport->iport_rp_slots[i];
529 if (irp && irp->irp_flags & IRP_PLOGI_DONE) {
530 if (FC_WELL_KNOWN_ADDR(irp->irp_portid))
531 port_attr->NumberofDiscoveredPorts --;
532 }
533 }
534 rw_exit(&iport->iport_lock);
535
536 kmem_free(attr, sizeof (fct_port_attrs_t));
537
538 return (0);
539 }
540
541 int
542 fct_get_discovered_port_attr(fct_i_remote_port_t *remote_port,
543 uint8_t *port_wwn, uint32_t index, fc_tgt_hba_port_attributes_t *port_attr,
544 uint32_t *error_detail)
545 {
546 fct_i_local_port_t *iport;
547 fct_i_remote_port_t *irp = remote_port;
548 int count = 0, i = 0;
549
550 port_attr->version = FCT_HBA_PORT_ATTRIBUTES_VERSION;
551 if (!remote_port) {
552 iport = fct_get_iport_per_wwn(port_wwn);
553 if (!iport) {
554 *error_detail = FCTIO_BADWWN;
555 return (ENXIO);
556 }
557
558 rw_enter(&iport->iport_lock, RW_READER);
559
560 if (index >= iport->iport_nrps_login) {
561 rw_exit(&iport->iport_lock);
562 *error_detail = FCTIO_OUTOFBOUNDS;
563 return (EINVAL);
564 }
565 for (; i < iport->iport_port->port_max_logins; i++) {
566 irp = iport->iport_rp_slots[i];
567 if (irp && irp->irp_flags & IRP_PLOGI_DONE &&
568 !FC_WELL_KNOWN_ADDR(irp->irp_portid)) {
569 count ++;
570 if ((index + 1) <= count)
571 break;
572 }
573 }
574 if (i >= iport->iport_port->port_max_logins) {
575 rw_exit(&iport->iport_lock);
576 *error_detail = FCTIO_OUTOFBOUNDS;
577 return (EINVAL);
578 }
579 ASSERT(irp);
580 } else {
581 iport = (fct_i_local_port_t *)
582 irp->irp_rp->rp_port->port_fct_private;
583 }
584 port_attr->lastChange = iport->iport_last_change;
585 rw_enter(&irp->irp_lock, RW_READER);
586 bcopy(irp->irp_rp->rp_pwwn, port_attr->PortWWN,
587 sizeof (port_attr->PortWWN));
588 bcopy(irp->irp_rp->rp_nwwn, port_attr->NodeWWN,
589 sizeof (port_attr->NodeWWN));
590 port_attr->PortFcId = irp->irp_portid;
591 if (irp->irp_spn)
592 (void) strncpy(port_attr->PortSymbolicName, irp->irp_spn,
593 strlen(irp->irp_spn));
594 else
595 port_attr->PortSymbolicName[0] = '\0';
596 port_attr->PortSupportedClassofService = irp->irp_cos;
597 bcopy((caddr_t)irp->irp_fc4types, port_attr->PortActiveFc4Types,
598 sizeof (irp->irp_fc4types));
599 bcopy((caddr_t)irp->irp_fc4types, port_attr->PortSupportedFc4Types,
600 sizeof (irp->irp_fc4types));
601 if (irp->irp_flags & IRP_PLOGI_DONE)
602 port_attr->PortState = FC_HBA_PORTSTATE_ONLINE;
603 else
604 port_attr->PortState = FC_HBA_PORTSTATE_UNKNOWN;
605
606 port_attr->PortType = FC_HBA_PORTTYPE_UNKNOWN;
607 port_attr->PortSupportedSpeed = FC_HBA_PORTSPEED_UNKNOWN;
608 port_attr->PortSpeed = FC_HBA_PORTSPEED_UNKNOWN;
609 port_attr->PortMaxFrameSize = 0;
610 port_attr->NumberofDiscoveredPorts = 0;
611 rw_exit(&irp->irp_lock);
612 if (!remote_port) {
613 rw_exit(&iport->iport_lock);
614 }
615 return (0);
616 }
617
618 int
619 fct_get_port_attr(uint8_t *port_wwn,
620 fc_tgt_hba_port_attributes_t *port_attr, uint32_t *error_detail)
621 {
622 fct_i_local_port_t *iport;
623 fct_i_remote_port_t *irp;
624 int i, ret;
625
626 iport = fct_get_iport_per_wwn(port_wwn);
627 if (iport) {
628 return (fct_get_adapter_port_attr(iport, port_wwn,
629 port_attr, error_detail));
630 }
631 /* else */
632 for (iport = fct_iport_list; iport; iport = iport->iport_next) {
633 rw_enter(&iport->iport_lock, RW_READER);
634 for (i = 0; i < rportid_table_size; i++) {
635 irp = iport->iport_rp_tb[i];
636 while (irp) {
637 if (bcmp(irp->irp_rp->rp_pwwn,
638 port_wwn, 8) == 0 &&
639 irp->irp_flags & IRP_PLOGI_DONE) {
640 ret = fct_get_discovered_port_attr(
641 irp, NULL, 0, port_attr,
642 error_detail);
643 rw_exit(&iport->iport_lock);
644 return (ret);
645 }
646 irp = irp->irp_next;
647 }
648 }
649 rw_exit(&iport->iport_lock);
650 }
651 *error_detail = FCTIO_BADWWN;
652 return (ENXIO);
653 }
654
655 /* ARGSUSED */
656 int
657 fct_get_port_stats(uint8_t *port_wwn,
658 fc_tgt_hba_adapter_port_stats_t *port_stats, uint32_t *error_detail)
659 {
660 int ret;
661 fct_i_local_port_t *iport = fct_get_iport_per_wwn(port_wwn);
662 fct_port_link_status_t stat;
663 uint32_t buf_size = sizeof (fc_tgt_hba_adapter_port_stats_t);
664
665 if (!iport)
666 return (ENXIO);
667 port_stats->version = FCT_HBA_ADAPTER_PORT_STATS_VERSION;
668
669 if (iport->iport_port->port_info == NULL) {
670 *error_detail = FCTIO_FAILURE;
671 return (EIO);
672 }
673 ret = iport->iport_port->port_info(FC_TGT_PORT_RLS,
674 iport->iport_port, NULL, (uint8_t *)&stat, &buf_size);
675 if (ret != STMF_SUCCESS) {
676 *error_detail = FCTIO_FAILURE;
677 return (EIO);
678 }
679
680 port_stats->SecondsSinceLastReset = 0;
681 port_stats->TxFrames = 0;
682 port_stats->TxWords = 0;
683 port_stats->RxFrames = 0;
684 port_stats->RxWords = 0;
685 port_stats->LIPCount = 0;
686 port_stats->NOSCount = 0;
687 port_stats->ErrorFrames = 0;
688 port_stats->DumpedFrames = 0;
689 port_stats->LinkFailureCount = stat.LinkFailureCount;
690 port_stats->LossOfSyncCount = stat.LossOfSyncCount;
691 port_stats->LossOfSignalCount = stat.LossOfSignalsCount;
692 port_stats->PrimitiveSeqProtocolErrCount =
693 stat.PrimitiveSeqProtocolErrorCount;
694 port_stats->InvalidTxWordCount =
695 stat.InvalidTransmissionWordCount;
696 port_stats->InvalidCRCCount = stat.InvalidCRCCount;
697
698 return (ret);
699 }
700
701 int
702 fct_get_link_status(uint8_t *port_wwn, uint64_t *dest_id,
703 fct_port_link_status_t *link_status, uint32_t *error_detail)
704 {
705 fct_i_local_port_t *iport = fct_get_iport_per_wwn(port_wwn);
706 fct_i_remote_port_t *irp = NULL;
707 uint32_t buf_size = sizeof (fct_port_link_status_t);
708 stmf_status_t ret = 0;
709 int i;
710 fct_cmd_t *cmd = NULL;
711
712 if (!iport) {
713 *error_detail = FCTIO_BADWWN;
714 return (ENXIO);
715 }
716
717 /*
718 * If what we are requesting is zero or same as local port,
719 * then we use port_info()
720 */
721 if (dest_id == NULL || *dest_id == iport->iport_link_info.portid) {
722 if (iport->iport_port->port_info == NULL) {
723 *error_detail = FCTIO_FAILURE;
724 return (EIO);
725 }
726 ret = iport->iport_port->port_info(FC_TGT_PORT_RLS,
727 iport->iport_port, NULL,
728 (uint8_t *)link_status, &buf_size);
729 if (ret == STMF_SUCCESS) {
730 return (0);
731 } else {
732 *error_detail = FCTIO_FAILURE;
733 return (EIO);
734 }
735 }
736
737 /*
738 * For remote port, we will send RLS
739 */
740 for (i = 0; i < rportid_table_size; i++) {
741 irp = iport->iport_rp_tb[i];
742 while (irp) {
743 if (irp->irp_rp->rp_id == *dest_id &&
744 irp->irp_flags & IRP_PLOGI_DONE) {
745 goto SEND_RLS_ELS;
746 }
747 irp = irp->irp_next;
748 }
749 }
750 return (ENXIO);
751
752 SEND_RLS_ELS:
753 cmd = fct_create_solels(iport->iport_port,
754 irp->irp_rp, 0, ELS_OP_RLS,
755 0, fct_rls_cb);
756 if (!cmd)
757 return (ENOMEM);
758 iport->iport_rls_cb_data.fct_link_status = link_status;
759 CMD_TO_ICMD(cmd)->icmd_cb_private = &iport->iport_rls_cb_data;
760 fct_post_to_solcmd_queue(iport->iport_port, cmd);
761 sema_p(&iport->iport_rls_sema);
762 if (iport->iport_rls_cb_data.fct_els_res != FCT_SUCCESS)
763 ret = EIO;
764 return (ret);
765 }
766
767 static int
768 fct_forcelip(uint8_t *port_wwn, uint32_t *fctio_errno)
769 {
770 fct_status_t rval;
771 fct_i_local_port_t *iport;
772
773 mutex_enter(&fct_global_mutex);
774 iport = fct_get_iport_per_wwn(port_wwn);
775 mutex_exit(&fct_global_mutex);
776 if (iport == NULL) {
777 return (-1);
778 }
779
780 iport->iport_port->port_ctl(iport->iport_port,
781 FCT_CMD_FORCE_LIP, &rval);
782 if (rval != FCT_SUCCESS) {
783 *fctio_errno = FCTIO_FAILURE;
784 } else {
785 *fctio_errno = 0;
786 }
787
788 return (0);
789 }
790
791 static int
792 fct_fctiocmd(intptr_t data, int mode)
793 {
794 int ret = 0;
795 void *ibuf = NULL;
796 void *obuf = NULL;
797 void *abuf = NULL;
798 fctio_t *fctio;
799 uint32_t attr_length;
800
801 ret = fct_copyin_iocdata(data, mode, &fctio, &ibuf, &abuf, &obuf);
802 if (ret) {
803 return (ret);
804 }
805
806 switch (fctio->fctio_cmd) {
807 case FCTIO_ADAPTER_LIST: {
808 fc_tgt_hba_list_t *list = (fc_tgt_hba_list_t *)obuf;
809 int count;
810
811 if (fctio->fctio_olen < sizeof (fc_tgt_hba_list_t)) {
812 ret = EINVAL;
813 break;
814 }
815 list->numPorts = (fctio->fctio_olen -
816 sizeof (fc_tgt_hba_list_t))/8 + 1;
817
818 list->version = FCT_HBA_LIST_VERSION;
819 count = fct_get_port_list((char *)list->port_wwn,
820 list->numPorts);
821 if (count < 0) {
822 ret = ENXIO;
823 break;
824 }
825 if (count > list->numPorts) {
826 fctio->fctio_errno = FCTIO_MOREDATA;
827 ret = ENOSPC;
828 }
829 list->numPorts = count;
830 break;
831 }
832 case FCTIO_GET_ADAPTER_ATTRIBUTES: {
833 fc_tgt_hba_adapter_attributes_t *hba_attr;
834 uint8_t *port_wwn = (uint8_t *)ibuf;
835
836 attr_length = sizeof (fc_tgt_hba_adapter_attributes_t);
837 if (fctio->fctio_olen < attr_length ||
838 fctio->fctio_xfer != FCTIO_XFER_READ) {
839 ret = EINVAL;
840 break;
841 }
842 hba_attr = (fc_tgt_hba_adapter_attributes_t *)obuf;
843
844 mutex_enter(&fct_global_mutex);
845 ret = fct_get_adapter_attr(port_wwn, hba_attr,
846 &fctio->fctio_errno);
847 mutex_exit(&fct_global_mutex);
848
849 break;
850 }
851 case FCTIO_GET_ADAPTER_PORT_ATTRIBUTES: {
852 fc_tgt_hba_port_attributes_t *port_attr;
853
854 uint8_t *port_wwn = (uint8_t *)ibuf;
855
856 attr_length = sizeof (fc_tgt_hba_port_attributes_t);
857 if (fctio->fctio_olen < attr_length ||
858 fctio->fctio_xfer != FCTIO_XFER_READ) {
859 ret = EINVAL;
860 break;
861 }
862 port_attr = (fc_tgt_hba_port_attributes_t *)obuf;
863
864 mutex_enter(&fct_global_mutex);
865 ret = fct_get_adapter_port_attr(NULL, port_wwn, port_attr,
866 &fctio->fctio_errno);
867 mutex_exit(&fct_global_mutex);
868
869 break;
870 }
871 case FCTIO_GET_DISCOVERED_PORT_ATTRIBUTES: {
872 uint8_t *port_wwn = (uint8_t *)ibuf;
873 uint32_t *port_index = (uint32_t *)abuf;
874 fc_tgt_hba_port_attributes_t *port_attr;
875
876 attr_length = sizeof (fc_tgt_hba_port_attributes_t);
877 if (fctio->fctio_olen < attr_length ||
878 fctio->fctio_xfer != FCTIO_XFER_READ) {
879 ret = EINVAL;
880 break;
881 }
882 port_attr = (fc_tgt_hba_port_attributes_t *)obuf;
883
884 mutex_enter(&fct_global_mutex);
885 ret = fct_get_discovered_port_attr(NULL, port_wwn,
886 *port_index, port_attr, &fctio->fctio_errno);
887 mutex_exit(&fct_global_mutex);
888
889 break;
890 }
891 case FCTIO_GET_PORT_ATTRIBUTES: {
892 uint8_t *port_wwn = (uint8_t *)ibuf;
893 fc_tgt_hba_port_attributes_t *port_attr;
894
895 attr_length = sizeof (fc_tgt_hba_port_attributes_t);
896 if (fctio->fctio_olen < attr_length ||
897 fctio->fctio_xfer != FCTIO_XFER_READ) {
898 ret = EINVAL;
899 break;
900 }
901
902 port_attr = (fc_tgt_hba_port_attributes_t *)obuf;
903
904 mutex_enter(&fct_global_mutex);
905 ret = fct_get_port_attr(port_wwn, port_attr,
906 &fctio->fctio_errno);
907 mutex_exit(&fct_global_mutex);
908
909 break;
910 }
911 case FCTIO_GET_ADAPTER_PORT_STATS: {
912 uint8_t *port_wwn = (uint8_t *)ibuf;
913 fc_tgt_hba_adapter_port_stats_t *port_stats =
914 (fc_tgt_hba_adapter_port_stats_t *)obuf;
915 mutex_enter(&fct_global_mutex);
916 ret = fct_get_port_stats(port_wwn, port_stats,
917 &fctio->fctio_errno);
918 mutex_exit(&fct_global_mutex);
919 break;
920 }
921 case FCTIO_GET_LINK_STATUS: {
922 uint8_t *port_wwn = (uint8_t *)ibuf;
923 fct_port_link_status_t *link_status =
924 (fct_port_link_status_t *)obuf;
925 uint64_t *dest_id = abuf;
926
927 mutex_enter(&fct_global_mutex);
928 ret = fct_get_link_status(port_wwn, dest_id, link_status,
929 &fctio->fctio_errno);
930 mutex_exit(&fct_global_mutex);
931 break;
932 }
933
934 case FCTIO_FORCE_LIP:
935 ret = fct_forcelip((uint8_t *)ibuf, &fctio->fctio_errno);
936 break;
937
938 default:
939 break;
940 }
941 if (ret == 0) {
942 ret = fct_copyout_iocdata(data, mode, fctio, obuf);
943 } else if (fctio->fctio_errno) {
944 (void) fct_copyout_iocdata(data, mode, fctio, obuf);
945 }
946
947 if (obuf) {
948 kmem_free(obuf, fctio->fctio_olen);
949 obuf = NULL;
950 }
951 if (abuf) {
952 kmem_free(abuf, fctio->fctio_alen);
953 abuf = NULL;
954 }
955
956 if (ibuf) {
957 kmem_free(ibuf, fctio->fctio_ilen);
958 ibuf = NULL;
959 }
960 kmem_free(fctio, sizeof (fctio_t));
961 return (ret);
962 }
963
964 typedef struct {
965 void *bp; /* back pointer from internal struct to main struct */
966 int alloc_size;
967 fct_struct_id_t struct_id;
968 } __ifct_t;
969
970 typedef struct {
971 __ifct_t *fp; /* Framework private */
972 void *cp; /* Caller private */
973 void *ss; /* struct specific */
974 } __fct_t;
975
976 static struct {
977 int shared;
978 int fw_private;
979 int struct_specific;
980 } fct_sizes[] = { { 0, 0, 0 },
981 { GET_STRUCT_SIZE(fct_local_port_t),
982 GET_STRUCT_SIZE(fct_i_local_port_t), 0 },
983 { GET_STRUCT_SIZE(fct_remote_port_t),
984 GET_STRUCT_SIZE(fct_i_remote_port_t), 0 },
985 { GET_STRUCT_SIZE(fct_cmd_t),
986 GET_STRUCT_SIZE(fct_i_cmd_t), GET_STRUCT_SIZE(fct_els_t) },
987 { GET_STRUCT_SIZE(fct_cmd_t),
988 GET_STRUCT_SIZE(fct_i_cmd_t), GET_STRUCT_SIZE(fct_els_t) },
989 { GET_STRUCT_SIZE(fct_cmd_t),
990 GET_STRUCT_SIZE(fct_i_cmd_t), GET_STRUCT_SIZE(fct_sol_ct_t) },
991 { GET_STRUCT_SIZE(fct_cmd_t), GET_STRUCT_SIZE(fct_i_cmd_t),
992 GET_STRUCT_SIZE(fct_rcvd_abts_t) },
993 { GET_STRUCT_SIZE(fct_cmd_t), /* FCT_STRUCT_CMD_FCP_XCHG */
994 GET_STRUCT_SIZE(fct_i_cmd_t), 0 },
995 { GET_STRUCT_SIZE(fct_dbuf_store_t),
996 GET_STRUCT_SIZE(__ifct_t), 0 }
997 };
998
999 void *
1000 fct_alloc(fct_struct_id_t struct_id, int additional_size, int flags)
1001 {
1002 int fct_size;
1003 int kmem_flag;
1004 __fct_t *sh;
1005
1006 if ((struct_id == 0) || (struct_id >= FCT_MAX_STRUCT_IDS))
1007 return (NULL);
1008
1009 if ((curthread->t_flag & T_INTR_THREAD) || (flags & AF_FORCE_NOSLEEP)) {
1010 kmem_flag = KM_NOSLEEP;
1011 } else {
1012 kmem_flag = KM_SLEEP;
1013 }
1014
1015 additional_size = (additional_size + 7) & (~7);
1016 fct_size = fct_sizes[struct_id].shared +
1017 fct_sizes[struct_id].fw_private +
1018 fct_sizes[struct_id].struct_specific + additional_size;
1019
1020 if (struct_id == FCT_STRUCT_LOCAL_PORT) {
1021 stmf_local_port_t *lport;
1022
1023 lport = (stmf_local_port_t *)stmf_alloc(
1024 STMF_STRUCT_STMF_LOCAL_PORT, fct_size, flags);
1025 if (lport) {
1026 sh = (__fct_t *)lport->lport_port_private;
1027 sh->ss = lport;
1028 } else {
1029 return (NULL);
1030 }
1031 } else if (struct_id == FCT_STRUCT_DBUF_STORE) {
1032 stmf_dbuf_store_t *ds;
1033
1034 ds = (stmf_dbuf_store_t *)stmf_alloc(STMF_STRUCT_DBUF_STORE,
1035 fct_size, flags);
1036 if (ds) {
1037 sh = (__fct_t *)ds->ds_port_private;
1038 sh->ss = ds;
1039 } else {
1040 return (NULL);
1041 }
1042 } else {
1043 sh = (__fct_t *)kmem_zalloc(fct_size, kmem_flag);
1044 }
1045
1046 if (sh == NULL)
1047 return (NULL);
1048
1049 sh->fp = (__ifct_t *)GET_BYTE_OFFSET(sh, fct_sizes[struct_id].shared);
1050 sh->cp = GET_BYTE_OFFSET(sh->fp, fct_sizes[struct_id].fw_private);
1051 if (fct_sizes[struct_id].struct_specific)
1052 sh->ss = GET_BYTE_OFFSET(sh->cp, additional_size);
1053
1054 sh->fp->bp = sh;
1055 sh->fp->alloc_size = fct_size;
1056 sh->fp->struct_id = struct_id;
1057
1058 if (struct_id == FCT_STRUCT_CMD_FCP_XCHG) {
1059 ((fct_cmd_t *)sh)->cmd_type = FCT_CMD_FCP_XCHG;
1060 } else if (struct_id == FCT_STRUCT_CMD_RCVD_ELS) {
1061 ((fct_cmd_t *)sh)->cmd_type = FCT_CMD_RCVD_ELS;
1062 } else if (struct_id == FCT_STRUCT_CMD_SOL_ELS) {
1063 ((fct_cmd_t *)sh)->cmd_type = FCT_CMD_SOL_ELS;
1064 } else if (struct_id == FCT_STRUCT_CMD_RCVD_ABTS) {
1065 ((fct_cmd_t *)sh)->cmd_type = FCT_CMD_RCVD_ABTS;
1066 } else if (struct_id == FCT_STRUCT_CMD_SOL_CT) {
1067 ((fct_cmd_t *)sh)->cmd_type = FCT_CMD_SOL_CT;
1068 }
1069
1070 return (sh);
1071 }
1072
1073 void
1074 fct_free(void *ptr)
1075 {
1076 __fct_t *sh = (__fct_t *)ptr;
1077 fct_struct_id_t struct_id = sh->fp->struct_id;
1078
1079 if (struct_id == FCT_STRUCT_CMD_SOL_CT) {
1080 fct_sol_ct_t *ct = (fct_sol_ct_t *)
1081 ((fct_cmd_t *)ptr)->cmd_specific;
1082
1083 if (ct->ct_req_alloc_size) {
1084 kmem_free(ct->ct_req_payload, ct->ct_req_alloc_size);
1085 }
1086 if (ct->ct_resp_alloc_size) {
1087 kmem_free(ct->ct_resp_payload, ct->ct_resp_alloc_size);
1088 }
1089 } else if ((struct_id == FCT_STRUCT_CMD_RCVD_ELS) ||
1090 (struct_id == FCT_STRUCT_CMD_SOL_ELS)) {
1091 fct_els_t *els = (fct_els_t *)
1092 ((fct_cmd_t *)ptr)->cmd_specific;
1093 if (els->els_req_alloc_size)
1094 kmem_free(els->els_req_payload,
1095 els->els_req_alloc_size);
1096 if (els->els_resp_alloc_size)
1097 kmem_free(els->els_resp_payload,
1098 els->els_resp_alloc_size);
1099 }
1100
1101 if (struct_id == FCT_STRUCT_LOCAL_PORT) {
1102 stmf_free(((fct_local_port_t *)ptr)->port_lport);
1103 } else if (struct_id == FCT_STRUCT_DBUF_STORE) {
1104 stmf_free(((fct_dbuf_store_t *)ptr)->fds_ds);
1105 } else {
1106 kmem_free(ptr, sh->fp->alloc_size);
1107 }
1108 }
1109
1110 stmf_data_buf_t *
1111 fct_alloc_dbuf(scsi_task_t *task, uint32_t size, uint32_t *pminsize,
1112 uint32_t flags)
1113 {
1114 fct_local_port_t *port = (fct_local_port_t *)
1115 task->task_lport->lport_port_private;
1116
1117 return (port->port_fds->fds_alloc_data_buf(port, size,
1118 pminsize, flags));
1119 }
1120
1121 stmf_status_t
1122 fct_setup_dbuf(scsi_task_t *task, stmf_data_buf_t *dbuf, uint32_t flags)
1123 {
1124 fct_local_port_t *port = (fct_local_port_t *)
1125 task->task_lport->lport_port_private;
1126
1127 ASSERT(port->port_fds->fds_setup_dbuf != NULL);
1128 if (port->port_fds->fds_setup_dbuf == NULL)
1129 return (STMF_FAILURE);
1130
1131 return (port->port_fds->fds_setup_dbuf(port, dbuf, flags));
1132 }
1133
1134 void
1135 fct_teardown_dbuf(stmf_dbuf_store_t *ds, stmf_data_buf_t *dbuf)
1136 {
1137 fct_dbuf_store_t *fds = ds->ds_port_private;
1138
1139 fds->fds_teardown_dbuf(fds, dbuf);
1140 }
1141
1142 void
1143 fct_free_dbuf(stmf_dbuf_store_t *ds, stmf_data_buf_t *dbuf)
1144 {
1145 fct_dbuf_store_t *fds;
1146
1147 fds = (fct_dbuf_store_t *)ds->ds_port_private;
1148
1149 fds->fds_free_data_buf(fds, dbuf);
1150 }
1151
1152 static uint32_t taskq_cntr = 0;
1153
1154 fct_status_t
1155 fct_register_local_port(fct_local_port_t *port)
1156 {
1157 fct_i_local_port_t *iport;
1158 stmf_local_port_t *lport;
1159 fct_cmd_slot_t *slot;
1160 int i;
1161 char taskq_name[FCT_TASKQ_NAME_LEN];
1162
1163 iport = (fct_i_local_port_t *)port->port_fct_private;
1164 if (port->port_fca_version != FCT_FCA_MODREV_1) {
1165 cmn_err(CE_WARN,
1166 "fct: %s driver version mismatch",
1167 port->port_default_alias);
1168 return (FCT_FAILURE);
1169 }
1170 if (port->port_default_alias) {
1171 int l = strlen(port->port_default_alias);
1172
1173 if (l < 16) {
1174 iport->iport_alias = iport->iport_alias_mem;
1175 } else {
1176 iport->iport_alias =
1177 (char *)kmem_zalloc(l+1, KM_SLEEP);
1178 }
1179 (void) strcpy(iport->iport_alias, port->port_default_alias);
1180 } else {
1181 iport->iport_alias = NULL;
1182 }
1183 stmf_wwn_to_devid_desc((scsi_devid_desc_t *)iport->iport_id,
1184 port->port_pwwn, PROTOCOL_FIBRE_CHANNEL);
1185 (void) snprintf(taskq_name, sizeof (taskq_name), "stmf_fct_taskq_%d",
1186 atomic_inc_32_nv(&taskq_cntr));
1187 if ((iport->iport_worker_taskq = ddi_taskq_create(NULL,
1188 taskq_name, 1, TASKQ_DEFAULTPRI, 0)) == NULL) {
1189 return (FCT_FAILURE);
1190 }
1191 mutex_init(&iport->iport_worker_lock, NULL, MUTEX_DRIVER, NULL);
1192 cv_init(&iport->iport_worker_cv, NULL, CV_DRIVER, NULL);
1193 rw_init(&iport->iport_lock, NULL, RW_DRIVER, NULL);
1194 sema_init(&iport->iport_rls_sema, 0, NULL, SEMA_DRIVER, NULL);
1195
1196 /* Remote port mgmt */
1197 iport->iport_rp_slots = (fct_i_remote_port_t **)kmem_zalloc(
1198 port->port_max_logins * sizeof (fct_i_remote_port_t *), KM_SLEEP);
1199 iport->iport_rp_tb = kmem_zalloc(rportid_table_size *
1200 sizeof (fct_i_remote_port_t *), KM_SLEEP);
1201
1202 /* fct_cmds for SCSI traffic */
1203 iport->iport_total_alloced_ncmds = 0;
1204 iport->iport_cached_ncmds = 0;
1205 port->port_fca_fcp_cmd_size =
1206 (port->port_fca_fcp_cmd_size + 7) & ~7;
1207 list_create(&iport->iport_cached_cmdlist, sizeof (fct_i_cmd_t),
1208 offsetof(fct_i_cmd_t, icmd_node));
1209 list_create(&iport->iport_abort_queue, sizeof (fct_i_cmd_t),
1210 offsetof(fct_i_cmd_t, icmd_node));
1211
1212 mutex_init(&iport->iport_cached_cmd_lock, NULL, MUTEX_DRIVER, NULL);
1213
1214 /* Initialize cmd slots */
1215 iport->iport_cmd_slots = (fct_cmd_slot_t *)kmem_zalloc(
1216 port->port_max_xchges * sizeof (fct_cmd_slot_t), KM_SLEEP);
1217 iport->iport_next_free_slot = 0;
1218 for (i = 0; i < port->port_max_xchges; ) {
1219 slot = &iport->iport_cmd_slots[i];
1220 slot->slot_no = (uint16_t)i;
1221 slot->slot_next = (uint16_t)(++i);
1222 }
1223 slot->slot_next = FCT_SLOT_EOL;
1224 iport->iport_nslots_free = port->port_max_xchges;
1225
1226 iport->iport_task_green_limit =
1227 (port->port_max_xchges * FCT_TASK_GREEN_LIMIT) / 100;
1228 iport->iport_task_yellow_limit =
1229 (port->port_max_xchges * FCT_TASK_YELLOW_LIMIT) / 100;
1230 iport->iport_task_red_limit =
1231 (port->port_max_xchges * FCT_TASK_RED_LIMIT) / 100;
1232
1233 /* Start worker thread */
1234 atomic_and_32(&iport->iport_flags, ~IPORT_TERMINATE_WORKER);
1235 (void) ddi_taskq_dispatch(iport->iport_worker_taskq,
1236 fct_port_worker, port, DDI_SLEEP);
1237 /* Wait for taskq to start */
1238 while ((iport->iport_flags & IPORT_WORKER_RUNNING) == 0) {
1239 delay(1);
1240 }
1241
1242 lport = port->port_lport;
1243 lport->lport_id = (scsi_devid_desc_t *)iport->iport_id;
1244 lport->lport_alias = iport->iport_alias;
1245 lport->lport_pp = port->port_pp;
1246 port->port_fds->fds_ds->ds_alloc_data_buf = fct_alloc_dbuf;
1247 port->port_fds->fds_ds->ds_free_data_buf = fct_free_dbuf;
1248 port->port_fds->fds_ds->ds_setup_dbuf = fct_setup_dbuf;
1249 port->port_fds->fds_ds->ds_teardown_dbuf = fct_teardown_dbuf;
1250 lport->lport_ds = port->port_fds->fds_ds;
1251 lport->lport_xfer_data = fct_xfer_scsi_data;
1252 lport->lport_send_status = fct_send_scsi_status;
1253 lport->lport_task_free = fct_scsi_task_free;
1254 lport->lport_abort = fct_scsi_abort;
1255 lport->lport_ctl = fct_ctl;
1256 lport->lport_info = fct_info;
1257 lport->lport_event_handler = fct_event_handler;
1258 /* set up as alua participating port */
1259 stmf_set_port_alua(lport);
1260 if (stmf_register_local_port(port->port_lport) != FCT_SUCCESS) {
1261 goto fct_regport_fail1;
1262 }
1263 (void) stmf_lport_add_event(lport, LPORT_EVENT_INITIAL_LUN_MAPPED);
1264
1265 mutex_enter(&fct_global_mutex);
1266 iport->iport_next = fct_iport_list;
1267 iport->iport_prev = NULL;
1268 if (iport->iport_next)
1269 iport->iport_next->iport_prev = iport;
1270 fct_iport_list = iport;
1271 mutex_exit(&fct_global_mutex);
1272
1273 fct_init_kstats(iport);
1274
1275 fct_log_local_port_event(port, ESC_SUNFC_PORT_ATTACH);
1276
1277 return (FCT_SUCCESS);
1278
1279 fct_regport_fail1:;
1280 /* Stop the taskq 1st */
1281 if (iport->iport_flags & IPORT_WORKER_RUNNING) {
1282 atomic_or_32(&iport->iport_flags, IPORT_TERMINATE_WORKER);
1283 cv_broadcast(&iport->iport_worker_cv);
1284 while (iport->iport_flags & IPORT_WORKER_RUNNING) {
1285 delay(1);
1286 }
1287 }
1288 ddi_taskq_destroy(iport->iport_worker_taskq);
1289 if (iport->iport_rp_tb) {
1290 kmem_free(iport->iport_rp_tb, rportid_table_size *
1291 sizeof (fct_i_remote_port_t *));
1292 }
1293 return (FCT_FAILURE);
1294 }
1295
1296 fct_status_t
1297 fct_deregister_local_port(fct_local_port_t *port)
1298 {
1299 fct_i_local_port_t *iport;
1300 fct_i_cmd_t *icmd;
1301 int ndx;
1302
1303 iport = (fct_i_local_port_t *)port->port_fct_private;
1304
1305 if ((iport->iport_state != FCT_STATE_OFFLINE) ||
1306 iport->iport_state_not_acked) {
1307 return (FCT_FAILURE);
1308 }
1309
1310 /* Stop the taskq 1st */
1311 if (iport->iport_flags & IPORT_WORKER_RUNNING) {
1312 atomic_or_32(&iport->iport_flags, IPORT_TERMINATE_WORKER);
1313 cv_broadcast(&iport->iport_worker_cv);
1314 for (ndx = 0; ndx < 100; ndx++) {
1315 if ((iport->iport_flags & IPORT_WORKER_RUNNING)
1316 == 0) {
1317 break;
1318 }
1319 delay(drv_usectohz(10000));
1320 }
1321 if (ndx == 100) {
1322 atomic_and_32(&iport->iport_flags,
1323 ~IPORT_TERMINATE_WORKER);
1324 return (FCT_WORKER_STUCK);
1325 }
1326 }
1327
1328 if (stmf_deregister_local_port(port->port_lport) != FCT_SUCCESS) {
1329 goto fct_deregport_fail1;
1330 }
1331
1332 mutex_enter(&fct_global_mutex);
1333 if (iport->iport_next)
1334 iport->iport_next->iport_prev = iport->iport_prev;
1335 if (iport->iport_prev)
1336 iport->iport_prev->iport_next = iport->iport_next;
1337 else
1338 fct_iport_list = iport->iport_next;
1339 mutex_exit(&fct_global_mutex);
1340 /*
1341 * At this time, there should be no outstanding and pending
1342 * I/Os, so we can just release resources.
1343 */
1344 ASSERT(iport->iport_total_alloced_ncmds == iport->iport_cached_ncmds);
1345 while (!list_is_empty(&iport->iport_cached_cmdlist)) {
1346 icmd = list_remove_head(&iport->iport_cached_cmdlist);
1347 fct_free(icmd);
1348 }
1349 mutex_destroy(&iport->iport_cached_cmd_lock);
1350 kmem_free(iport->iport_cmd_slots, port->port_max_xchges *
1351 sizeof (fct_cmd_slot_t));
1352 kmem_free(iport->iport_rp_slots, port->port_max_logins *
1353 sizeof (fct_i_remote_port_t *));
1354 rw_destroy(&iport->iport_lock);
1355 cv_destroy(&iport->iport_worker_cv);
1356 sema_destroy(&iport->iport_rls_sema);
1357 mutex_destroy(&iport->iport_worker_lock);
1358 ddi_taskq_destroy(iport->iport_worker_taskq);
1359 if (iport->iport_rp_tb) {
1360 kmem_free(iport->iport_rp_tb, rportid_table_size *
1361 sizeof (fct_i_remote_port_t *));
1362 }
1363
1364 if (iport->iport_kstat_portstat) {
1365 kstat_delete(iport->iport_kstat_portstat);
1366 }
1367
1368 fct_log_local_port_event(port, ESC_SUNFC_PORT_DETACH);
1369 return (FCT_SUCCESS);
1370
1371 fct_deregport_fail1:;
1372 /* Restart the worker */
1373 atomic_and_32(&iport->iport_flags, ~IPORT_TERMINATE_WORKER);
1374 (void) ddi_taskq_dispatch(iport->iport_worker_taskq,
1375 fct_port_worker, port, DDI_SLEEP);
1376 /* Wait for taskq to start */
1377 while ((iport->iport_flags & IPORT_WORKER_RUNNING) == 0) {
1378 delay(1);
1379 }
1380 return (FCT_FAILURE);
1381 }
1382
1383 /* ARGSUSED */
1384 void
1385 fct_handle_event(fct_local_port_t *port, int event_id, uint32_t event_flags,
1386 caddr_t arg)
1387 {
1388 char info[FCT_INFO_LEN];
1389 fct_i_event_t *e;
1390 fct_i_local_port_t *iport = (fct_i_local_port_t *)
1391 port->port_fct_private;
1392
1393 e = kmem_zalloc(sizeof (fct_i_event_t), KM_NOSLEEP);
1394
1395 if (e == NULL) {
1396 /*
1397 * XXX Throw HBA fatal error event
1398 */
1399 (void) snprintf(info, sizeof (info),
1400 "fct_handle_event: iport-%p, allocation "
1401 "of fct_i_event failed", (void *)iport);
1402 (void) fct_port_shutdown(iport->iport_port,
1403 STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
1404 return;
1405 }
1406 /* Just queue the event */
1407 e->event_type = event_id;
1408 mutex_enter(&iport->iport_worker_lock);
1409 if (iport->iport_event_head == NULL) {
1410 iport->iport_event_head = iport->iport_event_tail = e;
1411 } else {
1412 iport->iport_event_tail->event_next = e;
1413 iport->iport_event_tail = e;
1414 }
1415 if (IS_WORKER_SLEEPING(iport))
1416 cv_signal(&iport->iport_worker_cv);
1417 mutex_exit(&iport->iport_worker_lock);
1418 }
1419
1420 /*
1421 * Called with iport_lock held as reader.
1422 */
1423 fct_i_remote_port_t *
1424 fct_portid_to_portptr(fct_i_local_port_t *iport, uint32_t portid)
1425 {
1426 fct_i_remote_port_t *irp;
1427
1428 irp = iport->iport_rp_tb[FCT_PORTID_HASH_FUNC(portid)];
1429 for (; irp != NULL; irp = irp->irp_next) {
1430 if (irp->irp_portid == portid)
1431 return (irp);
1432 }
1433
1434 return (NULL);
1435
1436 }
1437
1438 /*
1439 * Called with irp_lock held as writer.
1440 */
1441 void
1442 fct_queue_rp(fct_i_local_port_t *iport, fct_i_remote_port_t *irp)
1443 {
1444 int hash_key =
1445 FCT_PORTID_HASH_FUNC(irp->irp_portid);
1446
1447 irp->irp_next = iport->iport_rp_tb[hash_key];
1448 iport->iport_rp_tb[hash_key] = irp;
1449 iport->iport_nrps++;
1450 }
1451
1452 /*
1453 * Called with irp_lock and iport_lock held as writer.
1454 */
1455 void
1456 fct_deque_rp(fct_i_local_port_t *iport, fct_i_remote_port_t *irp)
1457 {
1458 fct_i_remote_port_t *irp_next = NULL;
1459 fct_i_remote_port_t *irp_last = NULL;
1460 int hash_key =
1461 FCT_PORTID_HASH_FUNC(irp->irp_portid);
1462
1463 irp_next = iport->iport_rp_tb[hash_key];
1464 irp_last = NULL;
1465 while (irp_next != NULL) {
1466 if (irp == irp_next) {
1467 if (irp->irp_flags & IRP_PLOGI_DONE) {
1468 atomic_dec_32(&iport->iport_nrps_login);
1469 }
1470 atomic_and_32(&irp->irp_flags,
1471 ~(IRP_PLOGI_DONE | IRP_PRLI_DONE));
1472 break;
1473 }
1474 irp_last = irp_next;
1475 irp_next = irp_next->irp_next;
1476 }
1477
1478 if (irp_next) {
1479 if (irp_last == NULL) {
1480 iport->iport_rp_tb[hash_key] =
1481 irp->irp_next;
1482 } else {
1483 irp_last->irp_next = irp->irp_next;
1484 }
1485 irp->irp_next = NULL;
1486 iport->iport_nrps--;
1487 }
1488 }
1489
1490 int
1491 fct_is_irp_logging_out(fct_i_remote_port_t *irp, int force_implicit)
1492 {
1493 int logging_out = 0;
1494
1495 rw_enter(&irp->irp_lock, RW_WRITER);
1496 if ((irp->irp_flags & IRP_IN_DISCOVERY_QUEUE) == 0) {
1497 logging_out = 0;
1498 goto ilo_done;
1499 }
1500 if (list_is_empty(&irp->irp_els_list) && (irp->irp_deregister_timer)) {
1501 if (force_implicit && irp->irp_nonfcp_xchg_count) {
1502 logging_out = 0;
1503 } else {
1504 logging_out = 1;
1505 }
1506 goto ilo_done;
1507 }
1508 if (!list_is_empty(&irp->irp_els_list)) {
1509 fct_i_cmd_t *icmd;
1510 /* Last session affecting ELS should be a LOGO */
1511 for (icmd = list_head(&irp->irp_els_list); icmd;
1512 icmd = list_next(&irp->irp_els_list, icmd)) {
1513 uint8_t op = (ICMD_TO_ELS(icmd))->els_req_payload[0];
1514 if (op == ELS_OP_LOGO) {
1515 if (force_implicit) {
1516 if (icmd->icmd_flags & ICMD_IMPLICIT)
1517 logging_out = 1;
1518 else
1519 logging_out = 0;
1520 } else {
1521 logging_out = 1;
1522 }
1523 } else if ((op == ELS_OP_PLOGI) ||
1524 (op == ELS_OP_PRLI) ||
1525 (op == ELS_OP_PRLO) || (op == ELS_OP_TPRLO)) {
1526 logging_out = 0;
1527 }
1528 }
1529 }
1530 ilo_done:;
1531 rw_exit(&irp->irp_lock);
1532
1533 return (logging_out);
1534 }
1535
1536 /*
1537 * The force_implicit flag enforces the implicit semantics which may be
1538 * needed if a received logout got stuck e.g. a response to a received
1539 * LOGO never came back from the FCA.
1540 */
1541 int
1542 fct_implicitly_logo_all(fct_i_local_port_t *iport, int force_implicit)
1543 {
1544 fct_i_remote_port_t *irp = NULL;
1545 fct_cmd_t *cmd = NULL;
1546 int i = 0;
1547 int nports = 0;
1548
1549 if (!iport->iport_nrps) {
1550 return (nports);
1551 }
1552
1553 rw_enter(&iport->iport_lock, RW_WRITER);
1554 for (i = 0; i < rportid_table_size; i++) {
1555 irp = iport->iport_rp_tb[i];
1556 while (irp) {
1557 if ((!(irp->irp_flags & IRP_PLOGI_DONE)) &&
1558 (fct_is_irp_logging_out(irp, force_implicit))) {
1559 irp = irp->irp_next;
1560 continue;
1561 }
1562
1563 cmd = fct_create_solels(iport->iport_port, irp->irp_rp,
1564 1, ELS_OP_LOGO, 0, fct_logo_cb);
1565 if (cmd == NULL) {
1566 stmf_trace(iport->iport_alias,
1567 "fct_implictly_logo_all: cmd null");
1568 rw_exit(&iport->iport_lock);
1569
1570 return (nports);
1571 }
1572
1573 fct_post_implicit_logo(cmd);
1574 nports++;
1575 irp = irp->irp_next;
1576 }
1577 }
1578 rw_exit(&iport->iport_lock);
1579
1580 return (nports);
1581 }
1582
1583 void
1584 fct_rehash(fct_i_local_port_t *iport)
1585 {
1586 fct_i_remote_port_t **iport_rp_tb_tmp;
1587 fct_i_remote_port_t **iport_rp_tb_new;
1588 fct_i_remote_port_t *irp;
1589 fct_i_remote_port_t *irp_next;
1590 int i;
1591
1592 iport_rp_tb_new = kmem_zalloc(rportid_table_size *
1593 sizeof (fct_i_remote_port_t *), KM_SLEEP);
1594 rw_enter(&iport->iport_lock, RW_WRITER);
1595 /* reconstruct the hash table */
1596 iport_rp_tb_tmp = iport->iport_rp_tb;
1597 iport->iport_rp_tb = iport_rp_tb_new;
1598 iport->iport_nrps = 0;
1599 for (i = 0; i < rportid_table_size; i++) {
1600 irp = iport_rp_tb_tmp[i];
1601 while (irp) {
1602 irp_next = irp->irp_next;
1603 fct_queue_rp(iport, irp);
1604 irp = irp_next;
1605 }
1606 }
1607 rw_exit(&iport->iport_lock);
1608 kmem_free(iport_rp_tb_tmp, rportid_table_size *
1609 sizeof (fct_i_remote_port_t *));
1610
1611 }
1612
1613 uint8_t
1614 fct_local_port_cleanup_done(fct_i_local_port_t *iport)
1615 {
1616 fct_i_remote_port_t *irp;
1617 int i;
1618
1619 if (iport->iport_nrps_login)
1620 return (0);
1621 /* loop all rps to check if the cmd have already been drained */
1622 for (i = 0; i < rportid_table_size; i++) {
1623 irp = iport->iport_rp_tb[i];
1624 while (irp) {
1625 if (irp->irp_fcp_xchg_count ||
1626 irp->irp_nonfcp_xchg_count)
1627 return (0);
1628 irp = irp->irp_next;
1629 }
1630 }
1631 return (1);
1632 }
1633
1634 fct_cmd_t *
1635 fct_scsi_task_alloc(fct_local_port_t *port, uint16_t rp_handle,
1636 uint32_t rportid, uint8_t *lun, uint16_t cdb_length,
1637 uint16_t task_ext)
1638 {
1639 fct_cmd_t *cmd;
1640 fct_i_cmd_t *icmd;
1641 fct_i_local_port_t *iport =
1642 (fct_i_local_port_t *)port->port_fct_private;
1643 fct_i_remote_port_t *irp;
1644 scsi_task_t *task;
1645 fct_remote_port_t *rp;
1646 uint16_t cmd_slot;
1647
1648 rw_enter(&iport->iport_lock, RW_READER);
1649 if ((iport->iport_link_state & S_LINK_ONLINE) == 0) {
1650 rw_exit(&iport->iport_lock);
1651 stmf_trace(iport->iport_alias, "cmd alloc called while the port"
1652 " was offline");
1653 return (NULL);
1654 }
1655
1656 if (rp_handle == FCT_HANDLE_NONE) {
1657 irp = fct_portid_to_portptr(iport, rportid);
1658 if (irp == NULL) {
1659 rw_exit(&iport->iport_lock);
1660 stmf_trace(iport->iport_alias, "cmd received from "
1661 "non existent port %x", rportid);
1662 return (NULL);
1663 }
1664 } else {
1665 if ((rp_handle >= port->port_max_logins) ||
1666 ((irp = iport->iport_rp_slots[rp_handle]) == NULL)) {
1667 rw_exit(&iport->iport_lock);
1668 stmf_trace(iport->iport_alias, "cmd received from "
1669 "invalid port handle %x", rp_handle);
1670 return (NULL);
1671 }
1672 }
1673 rp = irp->irp_rp;
1674
1675 rw_enter(&irp->irp_lock, RW_READER);
1676 if ((irp->irp_flags & IRP_PRLI_DONE) == 0) {
1677 rw_exit(&irp->irp_lock);
1678 rw_exit(&iport->iport_lock);
1679 stmf_trace(iport->iport_alias, "cmd alloc called while fcp "
1680 "login was not done. portid=%x, rp=%p", rp->rp_id, rp);
1681 return (NULL);
1682 }
1683
1684 mutex_enter(&iport->iport_cached_cmd_lock);
1685 if (!list_is_empty(&iport->iport_cached_cmdlist)) {
1686 icmd = list_remove_head(&iport->iport_cached_cmdlist);
1687 iport->iport_cached_ncmds--;
1688 cmd = icmd->icmd_cmd;
1689 } else {
1690 icmd = NULL;
1691 }
1692 mutex_exit(&iport->iport_cached_cmd_lock);
1693 if (icmd == NULL) {
1694 cmd = (fct_cmd_t *)fct_alloc(FCT_STRUCT_CMD_FCP_XCHG,
1695 port->port_fca_fcp_cmd_size, 0);
1696 if (cmd == NULL) {
1697 rw_exit(&irp->irp_lock);
1698 rw_exit(&iport->iport_lock);
1699 stmf_trace(iport->iport_alias, "Ran out of "
1700 "memory, port=%p", port);
1701 return (NULL);
1702 }
1703
1704 icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1705 list_link_init(&icmd->icmd_node);
1706 cmd->cmd_port = port;
1707 atomic_inc_32(&iport->iport_total_alloced_ncmds);
1708 }
1709
1710 /*
1711 * The accuracy of iport_max_active_ncmds is not important
1712 */
1713 if ((iport->iport_total_alloced_ncmds - iport->iport_cached_ncmds) >
1714 iport->iport_max_active_ncmds) {
1715 iport->iport_max_active_ncmds =
1716 iport->iport_total_alloced_ncmds -
1717 iport->iport_cached_ncmds;
1718 }
1719
1720 /* Lets get a slot */
1721 cmd_slot = fct_alloc_cmd_slot(iport, cmd);
1722 if (cmd_slot == FCT_SLOT_EOL) {
1723 rw_exit(&irp->irp_lock);
1724 rw_exit(&iport->iport_lock);
1725 stmf_trace(iport->iport_alias, "Ran out of xchg resources");
1726 cmd->cmd_handle = 0;
1727 fct_cmd_free(cmd);
1728 return (NULL);
1729 }
1730 atomic_inc_16(&irp->irp_fcp_xchg_count);
1731 cmd->cmd_rp = rp;
1732 icmd->icmd_flags |= ICMD_IN_TRANSITION | ICMD_KNOWN_TO_FCA;
1733 rw_exit(&irp->irp_lock);
1734 rw_exit(&iport->iport_lock);
1735
1736 icmd->icmd_start_time = ddi_get_lbolt();
1737
1738 cmd->cmd_specific = stmf_task_alloc(port->port_lport, irp->irp_session,
1739 lun, cdb_length, task_ext);
1740 if ((task = (scsi_task_t *)cmd->cmd_specific) != NULL) {
1741 task->task_port_private = cmd;
1742 return (cmd);
1743 }
1744
1745 fct_cmd_free(cmd);
1746
1747 return (NULL);
1748 }
1749
1750 void
1751 fct_scsi_task_free(scsi_task_t *task)
1752 {
1753 fct_cmd_t *cmd = (fct_cmd_t *)task->task_port_private;
1754
1755 cmd->cmd_comp_status = task->task_completion_status;
1756 fct_cmd_free(cmd);
1757 }
1758
1759 void
1760 fct_post_rcvd_cmd(fct_cmd_t *cmd, stmf_data_buf_t *dbuf)
1761 {
1762 fct_dbuf_store_t *fds;
1763
1764 if (cmd->cmd_type == FCT_CMD_FCP_XCHG) {
1765 fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1766 fct_i_local_port_t *iport =
1767 (fct_i_local_port_t *)cmd->cmd_port->port_fct_private;
1768 fct_i_remote_port_t *irp =
1769 (fct_i_remote_port_t *)cmd->cmd_rp->rp_fct_private;
1770 scsi_task_t *task = (scsi_task_t *)cmd->cmd_specific;
1771
1772 uint16_t irp_task = irp->irp_fcp_xchg_count;
1773 uint32_t load = iport->iport_total_alloced_ncmds -
1774 iport->iport_cached_ncmds;
1775
1776 DTRACE_FC_4(scsi__command,
1777 fct_cmd_t, cmd,
1778 fct_i_local_port_t, iport,
1779 scsi_task_t, task,
1780 fct_i_remote_port_t, irp);
1781
1782 if (load >= iport->iport_task_green_limit) {
1783 if ((load < iport->iport_task_yellow_limit &&
1784 irp_task >= 4) ||
1785 (load >= iport->iport_task_yellow_limit &&
1786 load < iport->iport_task_red_limit &&
1787 irp_task >= 1) ||
1788 (load >= iport->iport_task_red_limit))
1789 task->task_additional_flags |=
1790 TASK_AF_PORT_LOAD_HIGH;
1791 }
1792 /*
1793 * If the target driver accepts sglists, fill in task fields.
1794 */
1795 fds = cmd->cmd_port->port_fds;
1796 if (fds->fds_setup_dbuf != NULL) {
1797 task->task_additional_flags |= TASK_AF_ACCEPT_LU_DBUF;
1798 task->task_copy_threshold = fds->fds_copy_threshold;
1799 task->task_max_xfer_len = fds->fds_max_sgl_xfer_len;
1800 /*
1801 * A single stream load encounters a little extra
1802 * latency if large xfers are done in 1 chunk.
1803 * Give a hint to the LU that starting the xfer
1804 * with a smaller chunk would be better in this case.
1805 * For any other load, use maximum chunk size.
1806 */
1807 if (load == 1) {
1808 /* estimate */
1809 task->task_1st_xfer_len = 128*1024;
1810 } else {
1811 /* zero means no hint */
1812 task->task_1st_xfer_len = 0;
1813 }
1814 }
1815
1816 stmf_post_task((scsi_task_t *)cmd->cmd_specific, dbuf);
1817 atomic_and_32(&icmd->icmd_flags, ~ICMD_IN_TRANSITION);
1818 return;
1819 }
1820 /* We dont need dbuf for other cmds */
1821 if (dbuf) {
1822 cmd->cmd_port->port_fds->fds_free_data_buf(
1823 cmd->cmd_port->port_fds, dbuf);
1824 dbuf = NULL;
1825 }
1826 if (cmd->cmd_type == FCT_CMD_RCVD_ELS) {
1827 fct_handle_els(cmd);
1828 return;
1829 }
1830 if (cmd->cmd_type == FCT_CMD_RCVD_ABTS) {
1831 fct_handle_rcvd_abts(cmd);
1832 return;
1833 }
1834
1835 ASSERT(0);
1836 }
1837
1838 /*
1839 * This function bypasses fct_handle_els()
1840 */
1841 void
1842 fct_post_implicit_logo(fct_cmd_t *cmd)
1843 {
1844 fct_local_port_t *port = cmd->cmd_port;
1845 fct_i_local_port_t *iport =
1846 (fct_i_local_port_t *)port->port_fct_private;
1847 fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1848 fct_remote_port_t *rp = cmd->cmd_rp;
1849 fct_i_remote_port_t *irp = (fct_i_remote_port_t *)rp->rp_fct_private;
1850
1851 icmd->icmd_start_time = ddi_get_lbolt();
1852
1853 rw_enter(&irp->irp_lock, RW_WRITER);
1854 atomic_or_32(&icmd->icmd_flags, ICMD_IMPLICIT_CMD_HAS_RESOURCE);
1855 atomic_inc_16(&irp->irp_nonfcp_xchg_count);
1856 atomic_inc_16(&irp->irp_sa_elses_count);
1857 /*
1858 * An implicit LOGO can also be posted to a irp where a PLOGI might
1859 * be in process. That PLOGI will reset this flag and decrement the
1860 * iport_nrps_login counter.
1861 */
1862 if (irp->irp_flags & IRP_PLOGI_DONE) {
1863 atomic_dec_32(&iport->iport_nrps_login);
1864 }
1865 atomic_and_32(&irp->irp_flags, ~(IRP_PLOGI_DONE | IRP_PRLI_DONE));
1866 atomic_or_32(&icmd->icmd_flags, ICMD_SESSION_AFFECTING);
1867 fct_post_to_discovery_queue(iport, irp, icmd);
1868 rw_exit(&irp->irp_lock);
1869 }
1870
1871 /*
1872 * called with iport_lock held, return the slot number
1873 */
1874 uint16_t
1875 fct_alloc_cmd_slot(fct_i_local_port_t *iport, fct_cmd_t *cmd)
1876 {
1877 uint16_t cmd_slot;
1878 uint32_t old, new;
1879 fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1880
1881 do {
1882 old = iport->iport_next_free_slot;
1883 cmd_slot = old & 0xFFFF;
1884 if (cmd_slot == FCT_SLOT_EOL)
1885 return (cmd_slot);
1886 /*
1887 * We use high order 16 bits as a counter which keeps on
1888 * incrementing to avoid ABA issues with atomic lists.
1889 */
1890 new = ((old + (0x10000)) & 0xFFFF0000);
1891 new |= iport->iport_cmd_slots[cmd_slot].slot_next;
1892 } while (atomic_cas_32(&iport->iport_next_free_slot, old, new) != old);
1893
1894 atomic_dec_16(&iport->iport_nslots_free);
1895 iport->iport_cmd_slots[cmd_slot].slot_cmd = icmd;
1896 cmd->cmd_handle = (uint32_t)cmd_slot | 0x80000000 |
1897 (((uint32_t)(iport->iport_cmd_slots[cmd_slot].slot_uniq_cntr))
1898 << 24);
1899 return (cmd_slot);
1900 }
1901
1902 /*
1903 * If icmd is not NULL, irp_lock must be held
1904 */
1905 void
1906 fct_post_to_discovery_queue(fct_i_local_port_t *iport,
1907 fct_i_remote_port_t *irp, fct_i_cmd_t *icmd)
1908 {
1909 ASSERT(!MUTEX_HELD(&iport->iport_worker_lock));
1910 if (icmd) {
1911 list_insert_tail(&irp->irp_els_list, icmd);
1912 fct_els_cnt++;
1913 atomic_or_32(&icmd->icmd_flags, ICMD_IN_IRP_QUEUE);
1914 }
1915
1916 mutex_enter(&iport->iport_worker_lock);
1917 if ((irp->irp_flags & IRP_IN_DISCOVERY_QUEUE) == 0) {
1918
1919 /*
1920 * CAUTION: do not grab local_port/remote_port locks after
1921 * grabbing the worker lock.
1922 */
1923 irp->irp_discovery_next = NULL;
1924 if (iport->iport_rpwe_tail) {
1925 iport->iport_rpwe_tail->irp_discovery_next = irp;
1926 iport->iport_rpwe_tail = irp;
1927 } else {
1928 iport->iport_rpwe_head = iport->iport_rpwe_tail = irp;
1929 }
1930
1931 atomic_or_32(&irp->irp_flags, IRP_IN_DISCOVERY_QUEUE);
1932 }
1933
1934 /*
1935 * We need always signal the port worker irrespective of the fact that
1936 * irp is already in discovery queue or not.
1937 */
1938 if (IS_WORKER_SLEEPING(iport)) {
1939 cv_signal(&iport->iport_worker_cv);
1940 }
1941 mutex_exit(&iport->iport_worker_lock);
1942 }
1943
1944 stmf_status_t
1945 fct_xfer_scsi_data(scsi_task_t *task, stmf_data_buf_t *dbuf, uint32_t ioflags)
1946 {
1947 fct_cmd_t *cmd = (fct_cmd_t *)task->task_port_private;
1948
1949 DTRACE_FC_5(xfer__start,
1950 fct_cmd_t, cmd,
1951 fct_i_local_port_t, cmd->cmd_port->port_fct_private,
1952 scsi_task_t, task,
1953 fct_i_remote_port_t, cmd->cmd_rp->rp_fct_private,
1954 stmf_data_buf_t, dbuf);
1955
1956 return (cmd->cmd_port->port_xfer_scsi_data(cmd, dbuf, ioflags));
1957 }
1958
1959 void
1960 fct_scsi_data_xfer_done(fct_cmd_t *cmd, stmf_data_buf_t *dbuf, uint32_t ioflags)
1961 {
1962 fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1963 uint32_t old, new;
1964 uint32_t iof = 0;
1965
1966 DTRACE_FC_5(xfer__done,
1967 fct_cmd_t, cmd,
1968 fct_i_local_port_t, cmd->cmd_port->port_fct_private,
1969 scsi_task_t, ((scsi_task_t *)cmd->cmd_specific),
1970 fct_i_remote_port_t, cmd->cmd_rp->rp_fct_private,
1971 stmf_data_buf_t, dbuf);
1972
1973 if (ioflags & FCT_IOF_FCA_DONE) {
1974 do {
1975 old = new = icmd->icmd_flags;
1976 if (old & ICMD_BEING_ABORTED) {
1977 return;
1978 }
1979 new &= ~ICMD_KNOWN_TO_FCA;
1980 } while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
1981 iof = STMF_IOF_LPORT_DONE;
1982 cmd->cmd_comp_status = dbuf->db_xfer_status;
1983 }
1984
1985 if (icmd->icmd_flags & ICMD_BEING_ABORTED)
1986 return;
1987 stmf_data_xfer_done((scsi_task_t *)cmd->cmd_specific, dbuf, iof);
1988 }
1989
1990 stmf_status_t
1991 fct_send_scsi_status(scsi_task_t *task, uint32_t ioflags)
1992 {
1993 fct_cmd_t *cmd = (fct_cmd_t *)task->task_port_private;
1994
1995 DTRACE_FC_4(scsi__response,
1996 fct_cmd_t, cmd,
1997 fct_i_local_port_t,
1998 (fct_i_local_port_t *)cmd->cmd_port->port_fct_private,
1999 scsi_task_t, task,
2000 fct_i_remote_port_t,
2001 (fct_i_remote_port_t *)cmd->cmd_rp->rp_fct_private);
2002
2003 return (cmd->cmd_port->port_send_cmd_response(cmd, ioflags));
2004 }
2005
2006 void
2007 fct_send_response_done(fct_cmd_t *cmd, fct_status_t s, uint32_t ioflags)
2008 {
2009 fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
2010 fct_local_port_t *port = cmd->cmd_port;
2011 fct_i_local_port_t *iport = (fct_i_local_port_t *)
2012 port->port_fct_private;
2013 uint32_t old, new;
2014
2015 if ((ioflags & FCT_IOF_FCA_DONE) == 0) {
2016 /* Until we support confirmed completions, this is an error */
2017 fct_queue_cmd_for_termination(cmd, s);
2018 return;
2019 }
2020 do {
2021 old = new = icmd->icmd_flags;
2022 if (old & ICMD_BEING_ABORTED) {
2023 return;
2024 }
2025 new &= ~ICMD_KNOWN_TO_FCA;
2026 } while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
2027
2028 cmd->cmd_comp_status = s;
2029 if (cmd->cmd_type == FCT_CMD_FCP_XCHG) {
2030 stmf_send_status_done((scsi_task_t *)cmd->cmd_specific, s,
2031 STMF_IOF_LPORT_DONE);
2032 return;
2033 }
2034
2035 if (cmd->cmd_type == FCT_CMD_RCVD_ELS) {
2036 fct_cmd_free(cmd);
2037 return;
2038 } else if (cmd->cmd_type == FCT_CMD_SOL_ELS) {
2039 fct_handle_sol_els_completion(iport, icmd);
2040 } else if (cmd->cmd_type == FCT_CMD_SOL_CT) {
2041 /* Tell the caller that we are done */
2042 atomic_or_32(&icmd->icmd_flags, ICMD_CMD_COMPLETE);
2043 } else {
2044 ASSERT(0);
2045 }
2046 }
2047
2048 void
2049 fct_cmd_free(fct_cmd_t *cmd)
2050 {
2051 char info[FCT_INFO_LEN];
2052 fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
2053 fct_local_port_t *port = cmd->cmd_port;
2054 fct_i_local_port_t *iport = (fct_i_local_port_t *)
2055 port->port_fct_private;
2056 fct_i_remote_port_t *irp = NULL;
2057 int do_abts_acc = 0;
2058 uint32_t old, new;
2059
2060 ASSERT(!mutex_owned(&iport->iport_worker_lock));
2061 /* Give the slot back */
2062 if (CMD_HANDLE_VALID(cmd->cmd_handle)) {
2063 uint16_t n = CMD_HANDLE_SLOT_INDEX(cmd->cmd_handle);
2064 fct_cmd_slot_t *slot;
2065
2066 /*
2067 * If anything went wrong, grab the lock as writer. This is
2068 * probably unnecessary.
2069 */
2070 if ((cmd->cmd_comp_status != FCT_SUCCESS) ||
2071 (icmd->icmd_flags & ICMD_ABTS_RECEIVED)) {
2072 rw_enter(&iport->iport_lock, RW_WRITER);
2073 } else {
2074 rw_enter(&iport->iport_lock, RW_READER);
2075 }
2076
2077 if ((icmd->icmd_flags & ICMD_ABTS_RECEIVED) &&
2078 (cmd->cmd_link != NULL)) {
2079 do_abts_acc = 1;
2080 }
2081
2082 /* XXX Validate slot before freeing */
2083
2084 slot = &iport->iport_cmd_slots[n];
2085 slot->slot_uniq_cntr++;
2086 slot->slot_cmd = NULL;
2087 do {
2088 old = iport->iport_next_free_slot;
2089 slot->slot_next = old & 0xFFFF;
2090 new = (old + 0x10000) & 0xFFFF0000;
2091 new |= slot->slot_no;
2092 } while (atomic_cas_32(&iport->iport_next_free_slot,
2093 old, new) != old);
2094 cmd->cmd_handle = 0;
2095 atomic_inc_16(&iport->iport_nslots_free);
2096 if (cmd->cmd_rp) {
2097 irp = (fct_i_remote_port_t *)
2098 cmd->cmd_rp->rp_fct_private;
2099 if (cmd->cmd_type == FCT_CMD_FCP_XCHG)
2100 atomic_dec_16(&irp->irp_fcp_xchg_count);
2101 else
2102 atomic_dec_16(&irp->irp_nonfcp_xchg_count);
2103 }
2104 rw_exit(&iport->iport_lock);
2105 } else if ((icmd->icmd_flags & ICMD_IMPLICIT) &&
2106 (icmd->icmd_flags & ICMD_IMPLICIT_CMD_HAS_RESOURCE)) {
2107 /* for implicit cmd, no cmd slot is used */
2108 if (cmd->cmd_rp) {
2109 irp = (fct_i_remote_port_t *)
2110 cmd->cmd_rp->rp_fct_private;
2111 if (cmd->cmd_type == FCT_CMD_FCP_XCHG)
2112 atomic_dec_16(&irp->irp_fcp_xchg_count);
2113 else
2114 atomic_dec_16(&irp->irp_nonfcp_xchg_count);
2115 }
2116 }
2117
2118 if (do_abts_acc) {
2119 fct_cmd_t *lcmd = cmd->cmd_link;
2120 fct_fill_abts_acc(lcmd);
2121 if (port->port_send_cmd_response(lcmd,
2122 FCT_IOF_FORCE_FCA_DONE) != FCT_SUCCESS) {
2123 /*
2124 * XXX Throw HBA fatal error event
2125 * Later shutdown svc will terminate the ABTS in the end
2126 */
2127 (void) snprintf(info, sizeof (info),
2128 "fct_cmd_free: iport-%p, ABTS_ACC"
2129 " port_send_cmd_response failed", (void *)iport);
2130 (void) fct_port_shutdown(iport->iport_port,
2131 STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
2132 return;
2133 } else {
2134 fct_cmd_free(lcmd);
2135 cmd->cmd_link = NULL;
2136 }
2137 }
2138
2139 /* Free the cmd */
2140 if (cmd->cmd_type == FCT_CMD_FCP_XCHG) {
2141 if (iport->iport_cached_ncmds < max_cached_ncmds) {
2142 icmd->icmd_flags = 0;
2143 mutex_enter(&iport->iport_cached_cmd_lock);
2144 list_insert_head(&iport->iport_cached_cmdlist, icmd);
2145 iport->iport_cached_ncmds++;
2146 mutex_exit(&iport->iport_cached_cmd_lock);
2147 } else {
2148 atomic_dec_32(&iport->iport_total_alloced_ncmds);
2149 fct_free(cmd);
2150 }
2151 } else {
2152 fct_free(cmd);
2153 }
2154 }
2155
2156 /* ARGSUSED */
2157 stmf_status_t
2158 fct_scsi_abort(stmf_local_port_t *lport, int abort_cmd, void *arg,
2159 uint32_t flags)
2160 {
2161 stmf_status_t ret = STMF_SUCCESS;
2162 scsi_task_t *task;
2163 fct_cmd_t *cmd;
2164 fct_i_cmd_t *icmd;
2165 fct_local_port_t *port;
2166 uint32_t old, new;
2167
2168 ASSERT(abort_cmd == STMF_LPORT_ABORT_TASK);
2169
2170 task = (scsi_task_t *)arg;
2171 cmd = (fct_cmd_t *)task->task_port_private;
2172 icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
2173 port = (fct_local_port_t *)lport->lport_port_private;
2174
2175 do {
2176 old = new = icmd->icmd_flags;
2177 if ((old & ICMD_KNOWN_TO_FCA) == 0)
2178 return (STMF_NOT_FOUND);
2179 ASSERT((old & ICMD_FCA_ABORT_CALLED) == 0);
2180 new |= ICMD_BEING_ABORTED | ICMD_FCA_ABORT_CALLED;
2181 } while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
2182 ret = port->port_abort_cmd(port, cmd, 0);
2183 if ((ret == FCT_NOT_FOUND) || (ret == FCT_ABORT_SUCCESS)) {
2184 atomic_and_32(&icmd->icmd_flags, ~ICMD_KNOWN_TO_FCA);
2185 } else if (ret == FCT_BUSY) {
2186 atomic_and_32(&icmd->icmd_flags, ~ICMD_FCA_ABORT_CALLED);
2187 }
2188
2189 return (ret);
2190 }
2191
2192 void
2193 fct_ctl(struct stmf_local_port *lport, int cmd, void *arg)
2194 {
2195 fct_local_port_t *port;
2196 fct_i_local_port_t *iport;
2197 stmf_change_status_t st;
2198 stmf_change_status_t *pst;
2199
2200 ASSERT((cmd == STMF_CMD_LPORT_ONLINE) ||
2201 (cmd == STMF_ACK_LPORT_ONLINE_COMPLETE) ||
2202 (cmd == STMF_CMD_LPORT_OFFLINE) ||
2203 (cmd == STMF_ACK_LPORT_OFFLINE_COMPLETE) ||
2204 (cmd == FCT_CMD_PORT_ONLINE_COMPLETE) ||
2205 (cmd == FCT_CMD_PORT_OFFLINE_COMPLETE));
2206
2207 port = (fct_local_port_t *)lport->lport_port_private;
2208 pst = (stmf_change_status_t *)arg;
2209 st.st_completion_status = STMF_SUCCESS;
2210 st.st_additional_info = NULL;
2211
2212 iport = (fct_i_local_port_t *)port->port_fct_private;
2213 /*
2214 * We are mostly a passthrough, except during offline.
2215 */
2216 switch (cmd) {
2217 case STMF_CMD_LPORT_ONLINE:
2218 if (iport->iport_state == FCT_STATE_ONLINE)
2219 st.st_completion_status = STMF_ALREADY;
2220 else if (iport->iport_state != FCT_STATE_OFFLINE)
2221 st.st_completion_status = STMF_INVALID_ARG;
2222 if (st.st_completion_status != STMF_SUCCESS) {
2223 (void) stmf_ctl(STMF_CMD_LPORT_ONLINE_COMPLETE, lport,
2224 &st);
2225 break;
2226 }
2227 iport->iport_state_not_acked = 1;
2228 iport->iport_state = FCT_STATE_ONLINING;
2229 port->port_ctl(port, FCT_CMD_PORT_ONLINE, arg);
2230 break;
2231 case FCT_CMD_PORT_ONLINE_COMPLETE:
2232 ASSERT(iport->iport_state == FCT_STATE_ONLINING);
2233 if (pst->st_completion_status != FCT_SUCCESS) {
2234 iport->iport_state = FCT_STATE_OFFLINE;
2235 iport->iport_state_not_acked = 0;
2236 } else {
2237 iport->iport_state = FCT_STATE_ONLINE;
2238 }
2239 (void) stmf_ctl(STMF_CMD_LPORT_ONLINE_COMPLETE, lport, arg);
2240 break;
2241 case STMF_ACK_LPORT_ONLINE_COMPLETE:
2242 ASSERT(iport->iport_state == FCT_STATE_ONLINE);
2243 iport->iport_state_not_acked = 0;
2244 port->port_ctl(port, FCT_ACK_PORT_ONLINE_COMPLETE, arg);
2245 break;
2246
2247 case STMF_CMD_LPORT_OFFLINE:
2248 if (iport->iport_state == FCT_STATE_OFFLINE)
2249 st.st_completion_status = STMF_ALREADY;
2250 else if (iport->iport_state != FCT_STATE_ONLINE)
2251 st.st_completion_status = STMF_INVALID_ARG;
2252 if (st.st_completion_status != STMF_SUCCESS) {
2253 (void) stmf_ctl(STMF_CMD_LPORT_OFFLINE_COMPLETE, lport,
2254 &st);
2255 break;
2256 }
2257 iport->iport_state_not_acked = 1;
2258 iport->iport_state = FCT_STATE_OFFLINING;
2259 port->port_ctl(port, FCT_CMD_PORT_OFFLINE, arg);
2260 break;
2261 case FCT_CMD_PORT_OFFLINE_COMPLETE:
2262 ASSERT(iport->iport_state == FCT_STATE_OFFLINING);
2263 if (pst->st_completion_status != FCT_SUCCESS) {
2264 iport->iport_state = FCT_STATE_ONLINE;
2265 iport->iport_state_not_acked = 0;
2266 (void) stmf_ctl(STMF_CMD_LPORT_OFFLINE_COMPLETE, lport,
2267 pst);
2268 break;
2269 }
2270
2271 /*
2272 * If FCA's offline was successful, we dont tell stmf yet.
2273 * Becasue now we have to do the cleanup before we go upto
2274 * stmf. That cleanup is done by the worker thread.
2275 */
2276
2277 /* FCA is offline, post a link down, its harmless anyway */
2278 fct_handle_event(port, FCT_EVENT_LINK_DOWN, 0, 0);
2279
2280 /* Trigger port offline processing by the worker */
2281 iport->iport_offline_prstate = FCT_OPR_START;
2282 break;
2283 case STMF_ACK_LPORT_OFFLINE_COMPLETE:
2284 ASSERT(iport->iport_state == FCT_STATE_OFFLINE);
2285 iport->iport_state_not_acked = 0;
2286 port->port_ctl(port, FCT_ACK_PORT_OFFLINE_COMPLETE, arg);
2287 break;
2288 }
2289 }
2290
2291 /* ARGSUSED */
2292 stmf_status_t
2293 fct_info(uint32_t cmd, stmf_local_port_t *lport, void *arg, uint8_t *buf,
2294 uint32_t *bufsizep)
2295 {
2296 return (STMF_NOT_SUPPORTED);
2297 }
2298
2299 /*
2300 * implicit: if it's true, it means it will only be used in fct module, or else
2301 * it will be sent to the link.
2302 */
2303 fct_cmd_t *
2304 fct_create_solels(fct_local_port_t *port, fct_remote_port_t *rp, int implicit,
2305 uchar_t elsop, uint32_t wkdid, fct_icmd_cb_t icmdcb)
2306 {
2307 fct_cmd_t *cmd = NULL;
2308 fct_i_cmd_t *icmd = NULL;
2309 fct_els_t *els = NULL;
2310 fct_i_remote_port_t *irp = NULL;
2311 uint8_t *p = NULL;
2312 uint32_t ptid = 0;
2313
2314 cmd = (fct_cmd_t *)fct_alloc(FCT_STRUCT_CMD_SOL_ELS,
2315 port->port_fca_sol_els_private_size, 0);
2316 if (!cmd) {
2317 return (NULL);
2318 }
2319
2320 if (rp) {
2321 irp = RP_TO_IRP(rp);
2322 } else if (((irp = fct_portid_to_portptr(PORT_TO_IPORT(port),
2323 wkdid)) == NULL) && (elsop != ELS_OP_PLOGI)) {
2324 stmf_trace(PORT_TO_IPORT(port)->iport_alias,
2325 "fct_create_solels: Must PLOGI to %x first", wkdid);
2326 fct_free(cmd);
2327 return (NULL);
2328 }
2329
2330 cmd->cmd_port = port;
2331 cmd->cmd_oxid = PTR2INT(cmd, uint16_t);
2332 cmd->cmd_rxid = 0xFFFF;
2333 cmd->cmd_handle = 0;
2334 icmd = CMD_TO_ICMD(cmd);
2335 els = ICMD_TO_ELS(icmd);
2336 icmd->icmd_cb = icmdcb;
2337 if (irp) {
2338 cmd->cmd_rp = irp->irp_rp;
2339 cmd->cmd_rp_handle = irp->irp_rp->rp_handle;
2340 cmd->cmd_rportid = irp->irp_rp->rp_id;
2341 } else {
2342 cmd->cmd_rp_handle = FCT_HANDLE_NONE;
2343 cmd->cmd_rportid = wkdid;
2344 }
2345 cmd->cmd_lportid = (PORT_TO_IPORT(port))->iport_link_info.portid;
2346
2347 if (implicit) {
2348 /*
2349 * Since we will not send it to FCA, so we only allocate space
2350 */
2351 ASSERT(elsop & (ELS_OP_LOGO | ELS_OP_PLOGI));
2352 icmd->icmd_flags |= ICMD_IMPLICIT;
2353 if (elsop == ELS_OP_LOGO) {
2354 /*
2355 * Handling implicit LOGO should dependent on as less
2356 * as resources. So a trick here.
2357 */
2358 els->els_req_size = 1;
2359 els->els_req_payload = cmd->cmd_fca_private;
2360 } else {
2361 els->els_req_alloc_size = els->els_req_size = 116;
2362 els->els_resp_alloc_size = els->els_resp_size = 116;
2363 els->els_req_payload = (uint8_t *)
2364 kmem_zalloc(els->els_req_size, KM_SLEEP);
2365 els->els_resp_payload = (uint8_t *)
2366 kmem_zalloc(els->els_resp_size, KM_SLEEP);
2367 }
2368 } else {
2369 /*
2370 * Allocate space for its request and response
2371 * Fill the request payload according to spec.
2372 */
2373 switch (elsop) {
2374 case ELS_OP_LOGO:
2375 els->els_resp_alloc_size = els->els_resp_size = 4;
2376 els->els_resp_payload = (uint8_t *)kmem_zalloc(
2377 els->els_resp_size, KM_SLEEP);
2378 els->els_req_alloc_size = els->els_req_size = 16;
2379 els->els_req_payload = (uint8_t *)kmem_zalloc(
2380 els->els_req_size, KM_SLEEP);
2381 ptid = PORT_TO_IPORT(port)->iport_link_info.portid;
2382 fct_value_to_netbuf(ptid, els->els_req_payload + 5, 3);
2383 bcopy(port->port_pwwn, els->els_req_payload + 8, 8);
2384 break;
2385
2386 case ELS_OP_RSCN:
2387 els->els_resp_alloc_size = els->els_resp_size = 4;
2388 els->els_resp_payload = (uint8_t *)kmem_zalloc(
2389 els->els_resp_size, KM_SLEEP);
2390 els->els_req_size = els->els_req_alloc_size = 8;
2391 els->els_req_payload = (uint8_t *)kmem_zalloc(
2392 els->els_req_size, KM_SLEEP);
2393 els->els_req_payload[1] = 0x04;
2394 els->els_req_payload[3] = 0x08;
2395 els->els_req_payload[4] |= 0x80;
2396 ptid = PORT_TO_IPORT(port)->iport_link_info.portid;
2397 fct_value_to_netbuf(ptid, els->els_req_payload + 5, 3);
2398 break;
2399
2400 case ELS_OP_PLOGI:
2401 els->els_resp_alloc_size = els->els_resp_size = 116;
2402 els->els_resp_payload = (uint8_t *)
2403 kmem_zalloc(els->els_resp_size, KM_SLEEP);
2404 els->els_req_alloc_size = els->els_req_size = 116;
2405 p = els->els_req_payload = (uint8_t *)
2406 kmem_zalloc(els->els_req_size, KM_SLEEP);
2407 bcopy(port->port_pwwn, p + 20, 8);
2408 bcopy(port->port_nwwn, p + 28, 8);
2409
2410 /*
2411 * Common service parameters
2412 */
2413 p[0x04] = 0x09; /* high version */
2414 p[0x05] = 0x08; /* low version */
2415 p[0x06] = 0x00; /* BB credit: 0x0065 */
2416 p[0x07] = 0x65;
2417
2418 /* CI0: Continuously Increasing Offset - 1 */
2419 /* RRO: Randomly Relative Offset - 0 */
2420 /* VVV: Vendor Version Level - 0 */
2421 /* N-F: N or F Port Payload Sender - 0 (N) */
2422 /* BBM: BB Credit Management - 0 (Normal) */
2423 p[0x08] = 0x80;
2424 p[0x09] = 0x00;
2425
2426 /* Max RX size */
2427 p[0x0A] = 0x08;
2428 p[0x0B] = 0x00;
2429
2430 /* NPTCS: N Port Total Concurrent Sequences - 0x0000 */
2431 p[0x0C] = 0x00;
2432 p[0x0D] = 0x00;
2433
2434 /* ROIC: Relative Offset By Info - 0xFFFF */
2435 p[0x0E] = 0xFF;
2436 p[0x0F] = 0xFF;
2437
2438 /* EDTOV: Error Detect Timeout - 0x000007D0 */
2439 p[0x10] = 0x00;
2440 p[0x11] = 0x00;
2441 p[0x12] = 0x07;
2442 p[0x13] = 0xD0;
2443
2444 /*
2445 * Class-3 Parameters
2446 */
2447 /* C3-VAL: Class 3 Value - 1 */
2448 /* C3-XID: X_ID Reassignment - 0 */
2449 /* C3-IPA: Initial Process Assignment */
2450 /* C3-AI-DCC: Data compression capable */
2451 /* C3-AI-DC-HB: Data compression history buffer size */
2452 /* C3-AI-DCE: Data encrytion capable */
2453 /* C3-AI-CSC: Clock synchronization capable */
2454 /* C3-ErrPol: Error pliciy */
2455 /* C3-CatSeq: Information Cat. Per Sequence */
2456 /* C3-AR-DCC: */
2457 /* C3-AR-DC-HB: */
2458 /* C3-AR-DCE: */
2459 /* C3-AR-CSC */
2460 p[0x44] = 0x80;
2461 p[0x45] = 0x00;
2462 p[0x46] = 0x00;
2463 p[0x47] = 0x00;
2464 p[0x48] = 0x00;
2465 p[0x49] = 0x00;
2466
2467 /* C3-RxSize: Class 3 receive data size */
2468 p[0x4A] = 0x08;
2469 p[0x4B] = 0x00;
2470
2471 /* C3-ConSeq: Class 3 Concourrent sequences */
2472 p[0x4C] = 0x00;
2473 p[0x4D] = 0xFF;
2474
2475 /* C3-OSPE: Class 3 open sequence per exchange */
2476 p[0x50] = 0x00;
2477 p[0x51] = 0x01;
2478
2479 break;
2480
2481 case ELS_OP_SCR:
2482 els->els_resp_alloc_size = els->els_resp_size = 4;
2483 els->els_resp_payload = (uint8_t *)
2484 kmem_zalloc(els->els_resp_size, KM_SLEEP);
2485 els->els_req_alloc_size = els->els_req_size = 8;
2486 p = els->els_req_payload = (uint8_t *)
2487 kmem_zalloc(els->els_req_size, KM_SLEEP);
2488 p[7] = FC_SCR_FULL_REGISTRATION;
2489 break;
2490 case ELS_OP_RLS:
2491 els->els_resp_alloc_size = els->els_resp_size = 28;
2492 els->els_resp_payload = (uint8_t *)
2493 kmem_zalloc(els->els_resp_size, KM_SLEEP);
2494 els->els_req_alloc_size = els->els_req_size = 8;
2495 p = els->els_req_payload = (uint8_t *)
2496 kmem_zalloc(els->els_req_size, KM_SLEEP);
2497 ptid = PORT_TO_IPORT(port)->iport_link_info.portid;
2498 fct_value_to_netbuf(ptid, els->els_req_payload + 5, 3);
2499 break;
2500
2501 default:
2502 ASSERT(0);
2503 }
2504 }
2505
2506 els->els_req_payload[0] = elsop;
2507 return (cmd);
2508 }
2509
2510 fct_cmd_t *
2511 fct_create_solct(fct_local_port_t *port, fct_remote_port_t *query_rp,
2512 uint16_t ctop, fct_icmd_cb_t icmdcb)
2513 {
2514 fct_cmd_t *cmd = NULL;
2515 fct_i_cmd_t *icmd = NULL;
2516 fct_sol_ct_t *ct = NULL;
2517 uint8_t *p = NULL;
2518 fct_i_remote_port_t *irp = NULL;
2519 fct_i_local_port_t *iport = NULL;
2520 char *nname = NULL;
2521 int namelen = 0;
2522
2523 /*
2524 * Allocate space
2525 */
2526 cmd = fct_alloc(FCT_STRUCT_CMD_SOL_CT,
2527 port->port_fca_sol_ct_private_size, 0);
2528 if (!cmd) {
2529 return (NULL);
2530 }
2531
2532 /*
2533 * We should have PLOGIed to the name server (0xFFFFFC)
2534 * Caution: this irp is not query_rp->rp_fct_private.
2535 */
2536 irp = fct_portid_to_portptr((fct_i_local_port_t *)
2537 port->port_fct_private, FS_NAME_SERVER);
2538 if (irp == NULL) {
2539 stmf_trace(PORT_TO_IPORT(port)->iport_alias,
2540 "fct_create_solct: Must PLOGI name server first");
2541 fct_free(cmd);
2542 return (NULL);
2543 }
2544
2545 cmd->cmd_port = port;
2546 cmd->cmd_rp = irp->irp_rp;
2547 cmd->cmd_rp_handle = irp->irp_rp->rp_handle;
2548 cmd->cmd_rportid = irp->irp_rp->rp_id;
2549 cmd->cmd_lportid = (PORT_TO_IPORT(port))->iport_link_info.portid;
2550 cmd->cmd_oxid = PTR2INT(cmd, uint16_t);
2551 cmd->cmd_rxid = 0xFFFF;
2552 cmd->cmd_handle = 0;
2553 icmd = CMD_TO_ICMD(cmd);
2554 ct = ICMD_TO_CT(icmd);
2555 icmd->icmd_cb = icmdcb;
2556 iport = ICMD_TO_IPORT(icmd);
2557
2558 switch (ctop) {
2559 case NS_GSNN_NN:
2560 /*
2561 * Allocate max space for its sybolic name
2562 */
2563 ct->ct_resp_alloc_size = ct->ct_resp_size = 272;
2564 ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2565 KM_SLEEP);
2566
2567 ct->ct_req_size = ct->ct_req_alloc_size = 24;
2568 p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2569 KM_SLEEP);
2570
2571 bcopy(query_rp->rp_nwwn, p + 16, 8);
2572 break;
2573
2574 case NS_RNN_ID:
2575 ct->ct_resp_alloc_size = ct->ct_resp_size = 16;
2576 ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2577 KM_SLEEP);
2578 ct->ct_req_size = ct->ct_req_alloc_size = 28;
2579 p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2580 KM_SLEEP);
2581
2582 /*
2583 * Port Identifier
2584 */
2585 p[17] = (iport->iport_link_info.portid >> 16) & 0xFF;
2586 p[18] = (iport->iport_link_info.portid >> 8) & 0xFF;
2587 p[19] = (iport->iport_link_info.portid >> 0) & 0xFF;
2588
2589 /*
2590 * Node Name
2591 */
2592 bcopy(port->port_nwwn, p + 20, 8);
2593 break;
2594
2595 case NS_RCS_ID:
2596 ct->ct_resp_alloc_size = ct->ct_resp_size = 16;
2597 ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2598 KM_SLEEP);
2599 ct->ct_req_size = ct->ct_req_alloc_size = 24;
2600 p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2601 KM_SLEEP);
2602
2603 /*
2604 * Port Identifier
2605 */
2606 p[17] = (iport->iport_link_info.portid >> 16) & 0xFF;
2607 p[18] = (iport->iport_link_info.portid >> 8) & 0xFF;
2608 p[19] = (iport->iport_link_info.portid >> 0) & 0xFF;
2609
2610 /*
2611 * Class of Service
2612 */
2613 *(p + 23) = FC_NS_CLASS3;
2614 break;
2615
2616 case NS_RFT_ID:
2617 ct->ct_resp_alloc_size = ct->ct_resp_size = 16;
2618 ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2619 KM_SLEEP);
2620 ct->ct_req_size = ct->ct_req_alloc_size = 52;
2621 p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2622 KM_SLEEP);
2623
2624 /*
2625 * Port Identifier
2626 */
2627 p[17] = (iport->iport_link_info.portid >> 16) & 0xFF;
2628 p[18] = (iport->iport_link_info.portid >> 8) & 0xFF;
2629 p[19] = (iport->iport_link_info.portid >> 0) & 0xFF;
2630
2631 /*
2632 * FC-4 Protocol Types
2633 */
2634 *(p + 22) = 0x1; /* 0x100 */
2635 break;
2636
2637 case NS_RSPN_ID:
2638 /*
2639 * If we get here, port->port_sym_port_name is always not NULL.
2640 */
2641 ASSERT(port->port_sym_port_name);
2642 namelen = strlen(port->port_sym_port_name);
2643 ct->ct_resp_alloc_size = ct->ct_resp_size = 16;
2644 ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2645 KM_SLEEP);
2646 ct->ct_req_size = ct->ct_req_alloc_size =
2647 (21 + namelen + 3) & ~3;
2648 p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2649 KM_SLEEP);
2650
2651 /*
2652 * Port Identifier
2653 */
2654 p[17] = (iport->iport_link_info.portid >> 16) & 0xFF;
2655 p[18] = (iport->iport_link_info.portid >> 8) & 0xFF;
2656 p[19] = (iport->iport_link_info.portid >> 0) & 0xFF;
2657
2658 /*
2659 * String length
2660 */
2661 p[20] = namelen;
2662
2663 /*
2664 * Symbolic port name
2665 */
2666 bcopy(port->port_sym_port_name, p + 21, ct->ct_req_size - 21);
2667 break;
2668
2669 case NS_RSNN_NN:
2670 namelen = port->port_sym_node_name == NULL ?
2671 strlen(utsname.nodename) :
2672 strlen(port->port_sym_node_name);
2673 nname = port->port_sym_node_name == NULL ?
2674 utsname.nodename : port->port_sym_node_name;
2675
2676 ct->ct_resp_alloc_size = ct->ct_resp_size = 16;
2677 ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2678 KM_SLEEP);
2679 ct->ct_req_size = ct->ct_req_alloc_size =
2680 (25 + namelen + 3) & ~3;
2681 p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2682 KM_SLEEP);
2683
2684 /*
2685 * Node name
2686 */
2687 bcopy(port->port_nwwn, p + 16, 8);
2688
2689 /*
2690 * String length
2691 */
2692 p[24] = namelen;
2693
2694 /*
2695 * Symbolic node name
2696 */
2697 bcopy(nname, p + 25, ct->ct_req_size - 25);
2698 break;
2699
2700 case NS_GSPN_ID:
2701 ct->ct_resp_alloc_size = ct->ct_resp_size = 272;
2702 ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2703 KM_SLEEP);
2704 ct->ct_req_size = ct->ct_req_alloc_size = 20;
2705 p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2706 KM_SLEEP);
2707 /*
2708 * Port Identifier
2709 */
2710 p[17] = (query_rp->rp_id >> 16) & 0xFF;
2711 p[18] = (query_rp->rp_id >> 8) & 0xFF;
2712 p[19] = (query_rp->rp_id >> 0) & 0xFF;
2713 break;
2714
2715 case NS_GCS_ID:
2716 ct->ct_resp_alloc_size = ct->ct_resp_size = 20;
2717 ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2718 KM_SLEEP);
2719 ct->ct_req_size = ct->ct_req_alloc_size = 20;
2720 p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2721 KM_SLEEP);
2722 /*
2723 * Port Identifier
2724 */
2725 p[17] = (query_rp->rp_id >> 16) & 0xFF;
2726 p[18] = (query_rp->rp_id >> 8) & 0xFF;
2727 p[19] = (query_rp->rp_id >> 0) & 0xFF;
2728 break;
2729
2730 case NS_GFT_ID:
2731 ct->ct_resp_alloc_size = ct->ct_resp_size = 48;
2732 ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2733 KM_SLEEP);
2734 ct->ct_req_size = ct->ct_req_alloc_size = 20;
2735 p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2736 KM_SLEEP);
2737 /*
2738 * Port Identifier
2739 */
2740 p[17] = (query_rp->rp_id >> 16) & 0xFF;
2741 p[18] = (query_rp->rp_id >> 8) & 0xFF;
2742 p[19] = (query_rp->rp_id >> 0) & 0xFF;
2743 break;
2744
2745 case NS_GID_PN:
2746 ct->ct_resp_alloc_size = ct->ct_resp_size = 20;
2747 ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2748 KM_SLEEP);
2749
2750 ct->ct_req_size = ct->ct_req_alloc_size = 24;
2751 p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2752 KM_SLEEP);
2753
2754 bcopy(query_rp->rp_pwwn, p + 16, 8);
2755 break;
2756
2757 default:
2758 /* CONSTCOND */
2759 ASSERT(0);
2760 }
2761
2762 FCT_FILL_CTIU_PREAMBLE(p, ctop);
2763 return (cmd);
2764 }
2765
2766 /*
2767 * Cmd can only be solicited CT/ELS. They will be dispatched to the discovery
2768 * queue eventually too.
2769 * We queue solicited cmds here to track solicited cmds and to take full use
2770 * of single thread mechanism.
2771 * But in current implmentation, we don't use this mechanism on SOL_CT, PLOGI.
2772 * To avoid to interrupt current flow, ICMD_IN_SOLCMD_QUEUE is used here.
2773 */
2774 void
2775 fct_post_to_solcmd_queue(fct_local_port_t *port, fct_cmd_t *cmd)
2776 {
2777 fct_i_local_port_t *iport = (fct_i_local_port_t *)
2778 port->port_fct_private;
2779 fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
2780
2781 mutex_enter(&iport->iport_worker_lock);
2782 icmd->icmd_solcmd_next = iport->iport_solcmd_queue;
2783 iport->iport_solcmd_queue = icmd;
2784 atomic_or_32(&icmd->icmd_flags, ICMD_IN_SOLCMD_QUEUE | ICMD_SOLCMD_NEW);
2785 if (IS_WORKER_SLEEPING(iport)) {
2786 cv_signal(&iport->iport_worker_cv);
2787 }
2788 mutex_exit(&iport->iport_worker_lock);
2789 }
2790
2791 /* ARGSUSED */
2792 void
2793 fct_event_handler(stmf_local_port_t *lport, int eventid, void *arg,
2794 uint32_t flags)
2795 {
2796 fct_local_port_t *port = (fct_local_port_t *)
2797 lport->lport_port_private;
2798 fct_i_local_port_t *iport = (fct_i_local_port_t *)
2799 port->port_fct_private;
2800 stmf_scsi_session_t *ss;
2801 fct_i_remote_port_t *irp;
2802
2803 switch (eventid) {
2804 case LPORT_EVENT_INITIAL_LUN_MAPPED:
2805 ss = (stmf_scsi_session_t *)arg;
2806 irp = (fct_i_remote_port_t *)ss->ss_port_private;
2807 stmf_trace(iport->iport_alias,
2808 "Initial LUN mapped to session ss-%p, irp-%p", ss, irp);
2809 break;
2810
2811 default:
2812 stmf_trace(iport->iport_alias,
2813 "Unknown event received, %d", eventid);
2814 }
2815 }
2816
2817 void
2818 fct_send_cmd_done(fct_cmd_t *cmd, fct_status_t s, uint32_t ioflags)
2819 {
2820 /* XXX For now just call send_resp_done() */
2821 fct_send_response_done(cmd, s, ioflags);
2822 }
2823
2824 void
2825 fct_cmd_fca_aborted(fct_cmd_t *cmd, fct_status_t s, uint32_t ioflags)
2826 {
2827 fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
2828 char info[FCT_INFO_LEN];
2829 unsigned long long st;
2830
2831 st = s; /* To make gcc happy */
2832 ASSERT(icmd->icmd_flags & ICMD_BEING_ABORTED);
2833 if ((((s != FCT_ABORT_SUCCESS) && (s != FCT_NOT_FOUND))) ||
2834 ((ioflags & FCT_IOF_FCA_DONE) == 0)) {
2835 (void) snprintf(info, sizeof (info),
2836 "fct_cmd_fca_aborted: cmd-%p, "
2837 "s-%llx, iofalgs-%x", (void *)cmd, st, ioflags);
2838 (void) fct_port_shutdown(cmd->cmd_port,
2839 STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
2840 return;
2841 }
2842
2843 atomic_and_32(&icmd->icmd_flags, ~ICMD_KNOWN_TO_FCA);
2844 /* For non FCP Rest of the work is done by the terminator */
2845 /* For FCP stuff just call stmf */
2846 if (cmd->cmd_type == FCT_CMD_FCP_XCHG) {
2847 stmf_task_lport_aborted_unlocked(
2848 (scsi_task_t *)cmd->cmd_specific, s, STMF_IOF_LPORT_DONE);
2849 }
2850 }
2851
2852 /*
2853 * FCA drivers will use it, when they want to abort some FC transactions
2854 * due to lack of resource.
2855 */
2856 uint16_t
2857 fct_get_rp_handle(fct_local_port_t *port, uint32_t rportid)
2858 {
2859 fct_i_remote_port_t *irp;
2860
2861 irp = fct_portid_to_portptr(
2862 (fct_i_local_port_t *)(port->port_fct_private), rportid);
2863 if (irp == NULL) {
2864 return (0xFFFF);
2865 } else {
2866 return (irp->irp_rp->rp_handle);
2867 }
2868 }
2869
2870 fct_cmd_t *
2871 fct_handle_to_cmd(fct_local_port_t *port, uint32_t fct_handle)
2872 {
2873 fct_cmd_slot_t *slot;
2874 uint16_t ndx;
2875
2876 if (!CMD_HANDLE_VALID(fct_handle))
2877 return (NULL);
2878 if ((ndx = CMD_HANDLE_SLOT_INDEX(fct_handle)) >= port->port_max_xchges)
2879 return (NULL);
2880
2881 slot = &((fct_i_local_port_t *)port->port_fct_private)->iport_cmd_slots[
2882 ndx];
2883
2884 if ((slot->slot_uniq_cntr | 0x80) != (fct_handle >> 24))
2885 return (NULL);
2886 return (slot->slot_cmd->icmd_cmd);
2887 }
2888
2889 void
2890 fct_queue_scsi_task_for_termination(fct_cmd_t *cmd, fct_status_t s)
2891 {
2892 fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
2893
2894 uint32_t old, new;
2895
2896 do {
2897 old = icmd->icmd_flags;
2898 if ((old & (ICMD_BEING_ABORTED | ICMD_KNOWN_TO_FCA)) !=
2899 ICMD_KNOWN_TO_FCA)
2900 return;
2901 new = old | ICMD_BEING_ABORTED;
2902 } while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
2903 stmf_abort(STMF_QUEUE_TASK_ABORT, (scsi_task_t *)cmd->cmd_specific,
2904 s, NULL);
2905 }
2906
2907 void
2908 fct_fill_abts_acc(fct_cmd_t *cmd)
2909 {
2910 fct_rcvd_abts_t *abts = (fct_rcvd_abts_t *)cmd->cmd_specific;
2911 uint8_t *p;
2912
2913 abts->abts_resp_rctl = BLS_OP_BA_ACC;
2914 p = abts->abts_resp_payload;
2915 bzero(p, 12);
2916 *((uint16_t *)(p+4)) = BE_16(cmd->cmd_oxid);
2917 *((uint16_t *)(p+6)) = BE_16(cmd->cmd_rxid);
2918 p[10] = p[11] = 0xff;
2919 }
2920
2921 /*
2922 * fct_cmd_unlink_els -- remove icmd from ELS queue
2923 *
2924 * The commands are found via the slot array of active commands and will be
2925 * terminated shortly after being removed.
2926 */
2927 void
2928 fct_cmd_unlink_els(fct_i_remote_port_t *irp, fct_i_cmd_t *icmd)
2929 {
2930 ASSERT(rw_write_held(&irp->irp_lock));
2931 if (icmd->icmd_node.list_next) {
2932 /*
2933 * Command is on two queues. Determine which queue and
2934 * handle appropriately.
2935 */
2936 if (icmd->icmd_flags & ICMD_IN_IRP_QUEUE) {
2937 /*
2938 * If the command is active on the IRP queue it
2939 * will be freed during command termination
2940 * processing. Unfortuntely the ELS processing will
2941 * peek at the command and possibly panic if it's
2942 * been freed already. Remove it from the ELS
2943 * queue to avoid that.
2944 */
2945 if (icmd->icmd_flags & ICMD_SESSION_AFFECTING)
2946 atomic_dec_16(&irp->irp_sa_elses_count);
2947 else
2948 atomic_dec_16(&irp->irp_nsa_elses_count);
2949 atomic_and_32(&icmd->icmd_flags, ~ICMD_IN_IRP_QUEUE);
2950 list_remove(&irp->irp_els_list, icmd);
2951 }
2952 /*
2953 * There's an else case here, but the processing is handled
2954 * in fct_check_solcmd_queue(). In this case the command
2955 * is on the solicited queue and will be marked as aborted.
2956 * During command termination processing the command will be
2957 * marked as complete, but not freed. The freeing of the memory
2958 * is done in fct_check_solcmd_queue(). If that routine, which
2959 * holds the appropriate lock, is run first it will remove the
2960 * command from the abort queue so that no memory access
2961 * is done after the command has been freed.
2962 */
2963 }
2964 }
2965
2966 void
2967 fct_handle_rcvd_abts(fct_cmd_t *cmd)
2968 {
2969 char info[FCT_INFO_LEN];
2970 fct_local_port_t *port = cmd->cmd_port;
2971 fct_i_local_port_t *iport =
2972 (fct_i_local_port_t *)port->port_fct_private;
2973 fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
2974 fct_i_remote_port_t *irp;
2975 fct_cmd_t *c = NULL, *term_cmd;
2976 fct_i_cmd_t *ic = NULL;
2977 int found = 0;
2978 int i;
2979 fct_status_t term_val;
2980
2981 icmd->icmd_start_time = ddi_get_lbolt();
2982 icmd->icmd_flags |= ICMD_KNOWN_TO_FCA;
2983
2984 rw_enter(&iport->iport_lock, RW_WRITER);
2985 /* Make sure local port is sane */
2986 if ((iport->iport_link_state & S_LINK_ONLINE) == 0) {
2987 rw_exit(&iport->iport_lock);
2988 stmf_trace(iport->iport_alias, "ABTS not posted becasue"
2989 "port state was %x", iport->iport_link_state);
2990 fct_queue_cmd_for_termination(cmd, FCT_LOCAL_PORT_OFFLINE);
2991 return;
2992 }
2993
2994 if (cmd->cmd_rp_handle == FCT_HANDLE_NONE)
2995 irp = fct_portid_to_portptr(iport, cmd->cmd_rportid);
2996 else if (cmd->cmd_rp_handle < port->port_max_logins)
2997 irp = iport->iport_rp_slots[cmd->cmd_rp_handle];
2998 else
2999 irp = NULL;
3000 if (irp == NULL) {
3001 /* XXX Throw a logout to the initiator */
3002 rw_exit(&iport->iport_lock);
3003 stmf_trace(iport->iport_alias, "ABTS received from"
3004 " %x without a session", cmd->cmd_rportid);
3005 fct_queue_cmd_for_termination(cmd, FCT_NOT_LOGGED_IN);
3006 return;
3007 }
3008
3009 DTRACE_FC_3(abts__receive,
3010 fct_cmd_t, cmd,
3011 fct_local_port_t, port,
3012 fct_i_remote_port_t, irp);
3013
3014 cmd->cmd_rp = irp->irp_rp;
3015
3016 /*
3017 * No need to allocate an xchg resource. ABTSes use the same
3018 * xchg resource as the cmd they are aborting.
3019 */
3020 rw_enter(&irp->irp_lock, RW_WRITER);
3021 mutex_enter(&iport->iport_worker_lock);
3022 /* Lets find the command first */
3023 for (i = 0; i < port->port_max_xchges; i++) {
3024 if ((ic = iport->iport_cmd_slots[i].slot_cmd) == NULL)
3025 continue;
3026 if ((ic->icmd_flags & ICMD_KNOWN_TO_FCA) == 0)
3027 continue;
3028 c = ic->icmd_cmd;
3029 if (!CMD_HANDLE_VALID(c->cmd_handle))
3030 continue;
3031 if ((c->cmd_rportid != cmd->cmd_rportid) ||
3032 (c->cmd_oxid != cmd->cmd_oxid))
3033 continue;
3034 /* Found the command */
3035 found = 1;
3036 break;
3037 }
3038 if (!found) {
3039 mutex_exit(&iport->iport_worker_lock);
3040 rw_exit(&irp->irp_lock);
3041 rw_exit(&iport->iport_lock);
3042 /* Dont even bother queueing it. Just respond */
3043 fct_fill_abts_acc(cmd);
3044 if (port->port_send_cmd_response(cmd,
3045 FCT_IOF_FORCE_FCA_DONE) != FCT_SUCCESS) {
3046 /*
3047 * XXX Throw HBA fatal error event
3048 * Later shutdown svc will terminate the ABTS in the end
3049 */
3050 (void) snprintf(info, sizeof (info),
3051 "fct_handle_rcvd_abts: iport-%p, "
3052 "ABTS_ACC port_send_cmd_response failed",
3053 (void *)iport);
3054 (void) fct_port_shutdown(iport->iport_port,
3055 STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
3056 } else {
3057 fct_cmd_free(cmd);
3058 }
3059 return;
3060 }
3061
3062 fct_cmd_unlink_els(irp, ic);
3063
3064 /* Check if this an abts retry */
3065 if (c->cmd_link && (ic->icmd_flags & ICMD_ABTS_RECEIVED)) {
3066 /* Kill this abts. */
3067 term_cmd = icmd->icmd_cmd;
3068 term_val = FCT_ABORTED;
3069 } else {
3070 c->cmd_link = cmd;
3071 atomic_or_32(&ic->icmd_flags, ICMD_ABTS_RECEIVED);
3072 cmd->cmd_link = c;
3073 term_cmd = c;
3074 term_val = FCT_ABTS_RECEIVED;
3075 }
3076 mutex_exit(&iport->iport_worker_lock);
3077 rw_exit(&irp->irp_lock);
3078 fct_queue_cmd_for_termination(term_cmd, term_val);
3079 rw_exit(&iport->iport_lock);
3080 }
3081
3082 void
3083 fct_queue_cmd_for_termination(fct_cmd_t *cmd, fct_status_t s)
3084 {
3085 fct_local_port_t *port = cmd->cmd_port;
3086 fct_i_local_port_t *iport = (fct_i_local_port_t *)
3087 port->port_fct_private;
3088 fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
3089
3090 if (cmd->cmd_type == FCT_CMD_FCP_XCHG) {
3091 fct_queue_scsi_task_for_termination(cmd, s);
3092 return;
3093 }
3094 mutex_enter(&iport->iport_worker_lock);
3095 fct_q_for_termination_lock_held(iport, icmd, s);
3096 if (IS_WORKER_SLEEPING(iport))
3097 cv_signal(&iport->iport_worker_cv);
3098 mutex_exit(&iport->iport_worker_lock);
3099 }
3100
3101 /*
3102 * This function will not be called for SCSI CMDS
3103 */
3104 void
3105 fct_q_for_termination_lock_held(fct_i_local_port_t *iport, fct_i_cmd_t *icmd,
3106 fct_status_t s)
3107 {
3108 uint32_t old, new;
3109
3110 do {
3111 old = icmd->icmd_flags;
3112 if (old & ICMD_BEING_ABORTED)
3113 return;
3114 new = old | ICMD_BEING_ABORTED;
3115 } while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
3116
3117 icmd->icmd_start_time = ddi_get_lbolt();
3118 icmd->icmd_cmd->cmd_comp_status = s;
3119
3120 list_insert_tail(&iport->iport_abort_queue, icmd);
3121 fct_abort_cnt++;
3122 }
3123
3124 /*
3125 * For those cmds, for which we called fca_abort but it has not yet completed,
3126 * reset the FCA_ABORT_CALLED flag, so that abort can be called again.
3127 * This is done after a FCA offline. The reason is that after offline, the
3128 * firmware is not running so abort will never complete. But if we call it
3129 * again, the FCA will detect that it is not offline and it will
3130 * not call the firmware at all. Most likely it will abort in a synchronous
3131 * manner i.e. return FCT_ABORT_SUCCESS or FCT_NOT_FOUND.
3132 */
3133 void
3134 fct_reset_flag_abort_called(fct_i_local_port_t *iport)
3135 {
3136 fct_i_cmd_t *icmd;
3137 uint32_t old, new;
3138 int i, do_clear;
3139
3140 ASSERT(mutex_owned(&iport->iport_worker_lock));
3141 mutex_exit(&iport->iport_worker_lock);
3142 rw_enter(&iport->iport_lock, RW_WRITER);
3143 mutex_enter(&iport->iport_worker_lock);
3144
3145 for (i = 0; i < iport->iport_port->port_max_xchges; i++) {
3146 if (iport->iport_cmd_slots[i].slot_cmd == NULL)
3147 continue;
3148
3149 icmd = iport->iport_cmd_slots[i].slot_cmd;
3150
3151 do {
3152 old = new = icmd->icmd_flags;
3153 if ((old & (ICMD_KNOWN_TO_FCA |
3154 ICMD_FCA_ABORT_CALLED)) == (ICMD_KNOWN_TO_FCA |
3155 ICMD_FCA_ABORT_CALLED)) {
3156 new &= ~ICMD_FCA_ABORT_CALLED;
3157 do_clear = 1;
3158 } else {
3159 do_clear = 0;
3160 break;
3161 }
3162 } while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
3163 if (do_clear &&
3164 (icmd->icmd_cmd->cmd_type == FCT_CMD_FCP_XCHG)) {
3165 stmf_abort(STMF_REQUEUE_TASK_ABORT_LPORT,
3166 icmd->icmd_cmd->cmd_specific, 0, NULL);
3167 }
3168 }
3169
3170 rw_exit(&iport->iport_lock);
3171 }
3172
3173 /*
3174 * Modify the irp_deregister_timer such that the ports start deregistering
3175 * quickly.
3176 */
3177 void
3178 fct_irp_deregister_speedup(fct_i_local_port_t *iport)
3179 {
3180 fct_i_remote_port_t *irp;
3181 int i;
3182
3183 if (!iport->iport_nrps)
3184 return;
3185
3186 for (i = 0; i < rportid_table_size; i++) {
3187 irp = iport->iport_rp_tb[i];
3188 while (irp) {
3189 irp->irp_deregister_timer = ddi_get_lbolt() - 1;
3190 irp = irp->irp_next;
3191 }
3192 }
3193 }
3194
3195 disc_action_t
3196 fct_handle_port_offline(fct_i_local_port_t *iport)
3197 {
3198 if (iport->iport_offline_prstate == FCT_OPR_START) {
3199 fct_reset_flag_abort_called(iport);
3200 iport->iport_offline_prstate = FCT_OPR_CMD_CLEANUP_WAIT;
3201 /* fct_ctl has already submitted a link offline event */
3202 return (DISC_ACTION_DELAY_RESCAN);
3203 }
3204 if (iport->iport_offline_prstate == FCT_OPR_CMD_CLEANUP_WAIT) {
3205 if (iport->iport_link_state != PORT_STATE_LINK_DOWN)
3206 return (DISC_ACTION_DELAY_RESCAN);
3207 /*
3208 * All I/Os have been killed at this time. Lets speedup
3209 * the port deregister process.
3210 */
3211 mutex_exit(&iport->iport_worker_lock);
3212 rw_enter(&iport->iport_lock, RW_WRITER);
3213 fct_irp_deregister_speedup(iport);
3214 rw_exit(&iport->iport_lock);
3215 mutex_enter(&iport->iport_worker_lock);
3216 iport->iport_offline_prstate = FCT_OPR_INT_CLEANUP_WAIT;
3217 return (DISC_ACTION_RESCAN);
3218 }
3219 if (iport->iport_offline_prstate == FCT_OPR_INT_CLEANUP_WAIT) {
3220 stmf_change_status_t st;
3221
3222 if (iport->iport_solcmd_queue) {
3223 return (DISC_ACTION_DELAY_RESCAN);
3224 }
3225
3226 if (iport->iport_nrps) {
3227 /*
3228 * A port logout may have gone when implicit logo all
3229 * was retried. So do the port speedup again here.
3230 */
3231 mutex_exit(&iport->iport_worker_lock);
3232 rw_enter(&iport->iport_lock, RW_WRITER);
3233 fct_irp_deregister_speedup(iport);
3234 rw_exit(&iport->iport_lock);
3235 mutex_enter(&iport->iport_worker_lock);
3236 return (DISC_ACTION_DELAY_RESCAN);
3237 }
3238
3239 if (iport->iport_event_head != NULL) {
3240 return (DISC_ACTION_DELAY_RESCAN);
3241 }
3242
3243 st.st_completion_status = STMF_SUCCESS;
3244 st.st_additional_info = NULL;
3245 iport->iport_offline_prstate = FCT_OPR_DONE;
3246 iport->iport_state = FCT_STATE_OFFLINE;
3247 mutex_exit(&iport->iport_worker_lock);
3248 (void) stmf_ctl(STMF_CMD_LPORT_OFFLINE_COMPLETE,
3249 iport->iport_port->port_lport, &st);
3250 mutex_enter(&iport->iport_worker_lock);
3251 return (DISC_ACTION_DELAY_RESCAN);
3252 }
3253
3254 /* NOTREACHED */
3255 return (0);
3256 }
3257
3258 /*
3259 * See stmf.h for information on rflags. Additional info is just a text
3260 * description of the reason for this call. Additional_info can be NULL.
3261 * Also the caller can declare additional info on the stack. stmf_ctl
3262 * makes a copy of it before returning.
3263 */
3264 fct_status_t
3265 fct_port_initialize(fct_local_port_t *port, uint32_t rflags,
3266 char *additional_info)
3267 {
3268 stmf_state_change_info_t st;
3269
3270 st.st_rflags = rflags;
3271 st.st_additional_info = additional_info;
3272 stmf_trace(NULL, "fct_port_initialize: port-%p, %s", port,
3273 additional_info? additional_info : "no more information");
3274 return (stmf_ctl(STMF_CMD_LPORT_ONLINE, port->port_lport, &st));
3275 }
3276
3277 fct_status_t
3278 fct_port_shutdown(fct_local_port_t *port, uint32_t rflags,
3279 char *additional_info)
3280 {
3281 stmf_state_change_info_t st;
3282
3283 st.st_rflags = rflags;
3284 st.st_additional_info = additional_info;
3285 stmf_trace(NULL, "fct_port_shutdown: port-%p, %s", port,
3286 additional_info? additional_info : "no more information");
3287 return (stmf_ctl(STMF_CMD_LPORT_OFFLINE, port->port_lport, &st));
3288 }
3289
3290 /*
3291 * Called by worker thread. The aim is to terminate the command
3292 * using whatever means it takes.
3293 * Called with worker lock held.
3294 */
3295 disc_action_t
3296 fct_cmd_terminator(fct_i_local_port_t *iport)
3297 {
3298 char info[FCT_INFO_LEN];
3299 clock_t endtime;
3300 fct_i_cmd_t *next;
3301 fct_i_cmd_t *icmd;
3302 fct_cmd_t *cmd;
3303 fct_local_port_t *port = iport->iport_port;
3304 disc_action_t ret = DISC_ACTION_NO_WORK;
3305 fct_status_t abort_ret;
3306 int fca_done, fct_done, cmd_implicit = 0;
3307 int flags;
3308 unsigned long long st;
3309
3310 /* Lets Limit each run to 20ms max. */
3311 endtime = ddi_get_lbolt() + drv_usectohz(20000);
3312
3313 /* Start from where we left off last time */
3314 if (iport->iport_ppicmd_term) {
3315 icmd = iport->iport_ppicmd_term;
3316 iport->iport_ppicmd_term = NULL;
3317 } else {
3318 icmd = list_head(&iport->iport_abort_queue);
3319 }
3320
3321 /*
3322 * Once a command gets on discovery queue, this is the only thread
3323 * which can access it. So no need for the lock here.
3324 */
3325 mutex_exit(&iport->iport_worker_lock);
3326
3327 while (icmd) {
3328 cmd = icmd->icmd_cmd;
3329
3330 /* Always remember that cmd->cmd_rp can be NULL */
3331 if ((icmd->icmd_flags & (ICMD_KNOWN_TO_FCA |
3332 ICMD_FCA_ABORT_CALLED)) == ICMD_KNOWN_TO_FCA) {
3333 atomic_or_32(&icmd->icmd_flags, ICMD_FCA_ABORT_CALLED);
3334 if (CMD_HANDLE_VALID(cmd->cmd_handle))
3335 flags = 0;
3336 else
3337 flags = FCT_IOF_FORCE_FCA_DONE;
3338 abort_ret = port->port_abort_cmd(port, cmd, flags);
3339 if ((abort_ret != FCT_SUCCESS) &&
3340 (abort_ret != FCT_ABORT_SUCCESS) &&
3341 (abort_ret != FCT_NOT_FOUND)) {
3342 if (flags & FCT_IOF_FORCE_FCA_DONE) {
3343 /*
3344 * XXX trigger port fatal,
3345 * Abort the termination, and shutdown
3346 * svc will trigger fct_cmd_termination
3347 * again.
3348 */
3349 (void) snprintf(info, sizeof (info),
3350 "fct_cmd_terminator:"
3351 " iport-%p, port_abort_cmd with "
3352 "FORCE_FCA_DONE failed",
3353 (void *)iport);
3354 (void) fct_port_shutdown(
3355 iport->iport_port,
3356 STMF_RFLAG_FATAL_ERROR |
3357 STMF_RFLAG_RESET, info);
3358
3359 mutex_enter(&iport->iport_worker_lock);
3360 iport->iport_ppicmd_term = icmd;
3361 return (DISC_ACTION_DELAY_RESCAN);
3362 }
3363 atomic_and_32(&icmd->icmd_flags,
3364 ~ICMD_FCA_ABORT_CALLED);
3365 } else if ((flags & FCT_IOF_FORCE_FCA_DONE) ||
3366 (abort_ret == FCT_ABORT_SUCCESS) ||
3367 (abort_ret == FCT_NOT_FOUND)) {
3368 atomic_and_32(&icmd->icmd_flags,
3369 ~ICMD_KNOWN_TO_FCA);
3370 }
3371 ret |= DISC_ACTION_DELAY_RESCAN;
3372 } else if (icmd->icmd_flags & ICMD_IMPLICIT) {
3373 if (cmd->cmd_type == FCT_CMD_SOL_ELS)
3374 cmd->cmd_comp_status = FCT_ABORTED;
3375 atomic_or_32(&icmd->icmd_flags, ICMD_FCA_ABORT_CALLED);
3376 cmd_implicit = 1;
3377 }
3378 if ((icmd->icmd_flags & ICMD_KNOWN_TO_FCA) == 0)
3379 fca_done = 1;
3380 else
3381 fca_done = 0;
3382 if ((icmd->icmd_flags & ICMD_IN_IRP_QUEUE) == 0)
3383 fct_done = 1;
3384 else
3385 fct_done = 0;
3386 if ((fca_done || cmd_implicit) && fct_done) {
3387 mutex_enter(&iport->iport_worker_lock);
3388 next = list_next(&iport->iport_abort_queue, icmd);
3389 list_remove(&iport->iport_abort_queue, icmd);
3390 mutex_exit(&iport->iport_worker_lock);
3391
3392 if ((cmd->cmd_type == FCT_CMD_RCVD_ELS) ||
3393 (cmd->cmd_type == FCT_CMD_RCVD_ABTS)) {
3394 /* Free the cmd */
3395 fct_cmd_free(cmd);
3396 } else if (cmd->cmd_type == FCT_CMD_SOL_ELS) {
3397 fct_handle_sol_els_completion(iport, icmd);
3398 if (icmd->icmd_flags & ICMD_IMPLICIT) {
3399 if (IS_LOGO_ELS(icmd)) {
3400 /* IMPLICIT LOGO is special */
3401 fct_cmd_free(cmd);
3402 }
3403 }
3404 } else if (cmd->cmd_type == FCT_CMD_SOL_CT) {
3405 fct_sol_ct_t *ct = ICMD_TO_CT(icmd);
3406
3407 /* Tell the caller that we are done */
3408 atomic_or_32(&icmd->icmd_flags,
3409 ICMD_CMD_COMPLETE);
3410 if (fct_netbuf_to_value(
3411 ct->ct_req_payload + 8, 2) == NS_GID_PN) {
3412 fct_i_remote_port_t *irp;
3413
3414 rw_enter(&iport->iport_lock, RW_READER);
3415 irp = fct_lookup_irp_by_portwwn(iport,
3416 ct->ct_req_payload + 16);
3417
3418 if (irp) {
3419 atomic_and_32(&irp->irp_flags,
3420 ~IRP_RSCN_QUEUED);
3421 }
3422 rw_exit(&iport->iport_lock);
3423 }
3424 } else {
3425 ASSERT(0);
3426 }
3427 } else {
3428 clock_t timeout_ticks;
3429 if (port->port_fca_abort_timeout)
3430 timeout_ticks = drv_usectohz(
3431 port->port_fca_abort_timeout*1000);
3432 else
3433 /* 10 seconds by default */
3434 timeout_ticks = drv_usectohz(10 * 1000000);
3435 if ((ddi_get_lbolt() >
3436 (icmd->icmd_start_time+timeout_ticks)) &&
3437 iport->iport_state == FCT_STATE_ONLINE) {
3438 /* timeout, reset the port */
3439 char cmd_type[10];
3440 if (cmd->cmd_type == FCT_CMD_RCVD_ELS ||
3441 cmd->cmd_type == FCT_CMD_SOL_ELS) {
3442 fct_els_t *els = cmd->cmd_specific;
3443 (void) snprintf(cmd_type,
3444 sizeof (cmd_type), "%x.%x",
3445 cmd->cmd_type,
3446 els->els_req_payload[0]);
3447 } else if (cmd->cmd_type == FCT_CMD_SOL_CT) {
3448 fct_sol_ct_t *ct = cmd->cmd_specific;
3449 (void) snprintf(cmd_type,
3450 sizeof (cmd_type), "%x.%02x%02x",
3451 cmd->cmd_type,
3452 ct->ct_req_payload[8],
3453 ct->ct_req_payload[9]);
3454 } else {
3455 cmd_type[0] = 0;
3456 }
3457 st = cmd->cmd_comp_status; /* gcc fix */
3458 (void) snprintf(info, sizeof (info),
3459 "fct_cmd_terminator:"
3460 " iport-%p, cmd_type(0x%s),"
3461 " reason(%llx)", (void *)iport, cmd_type,
3462 st);
3463 (void) fct_port_shutdown(port,
3464 STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET,
3465 info);
3466 }
3467 mutex_enter(&iport->iport_worker_lock);
3468 next = list_next(&iport->iport_abort_queue, icmd);
3469 mutex_exit(&iport->iport_worker_lock);
3470 }
3471
3472 if (ddi_get_lbolt() > endtime) {
3473 mutex_enter(&iport->iport_worker_lock);
3474 iport->iport_ppicmd_term = next;
3475 return (DISC_ACTION_DELAY_RESCAN);
3476 } else {
3477 icmd = next;
3478 }
3479 }
3480 mutex_enter(&iport->iport_worker_lock);
3481 if (!list_is_empty(&iport->iport_abort_queue))
3482 return (DISC_ACTION_DELAY_RESCAN);
3483 if (ret == DISC_ACTION_NO_WORK)
3484 return (DISC_ACTION_RESCAN);
3485 return (ret);
3486 }
3487
3488 /*
3489 * Send a syslog event for adapter port level events.
3490 */
3491 void
3492 fct_log_local_port_event(fct_local_port_t *port, char *subclass)
3493 {
3494 nvlist_t *attr_list;
3495 int port_instance;
3496 int rc, sleep = DDI_SLEEP;
3497
3498 if (!fct_dip)
3499 return;
3500 port_instance = ddi_get_instance(fct_dip);
3501
3502 if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE,
3503 KM_SLEEP) != DDI_SUCCESS) {
3504 goto alloc_failed;
3505 }
3506
3507 if (nvlist_add_uint32(attr_list, "instance", port_instance)
3508 != DDI_SUCCESS) {
3509 goto error;
3510 }
3511
3512 if (nvlist_add_byte_array(attr_list, "port-wwn",
3513 port->port_pwwn, 8) != DDI_SUCCESS) {
3514 goto error;
3515 }
3516
3517 if (fct_force_log == 0) {
3518 sleep = DDI_NOSLEEP;
3519 }
3520 rc = ddi_log_sysevent(fct_dip, DDI_VENDOR_SUNW, EC_SUNFC,
3521 subclass, attr_list, NULL, sleep);
3522 if (rc != DDI_SUCCESS) {
3523 cmn_err(CE_WARN, "%s: queue full event lost", __func__);
3524 goto error;
3525 }
3526
3527 nvlist_free(attr_list);
3528 return;
3529
3530 error:
3531 nvlist_free(attr_list);
3532 alloc_failed:
3533 stmf_trace(((fct_i_local_port_t *)port->port_fct_private)->iport_alias,
3534 "Unable to send %s event", subclass);
3535 }
3536
3537 void
3538 fct_log_remote_port_event(fct_local_port_t *port, char *subclass,
3539 uint8_t *rp_pwwn, uint32_t rp_id)
3540 {
3541 nvlist_t *attr_list;
3542 int port_instance;
3543 int rc, sleep = DDI_SLEEP;
3544
3545 if (!fct_dip)
3546 return;
3547 port_instance = ddi_get_instance(fct_dip);
3548
3549 if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE,
3550 KM_SLEEP) != DDI_SUCCESS) {
3551 goto alloc_failed;
3552 }
3553
3554 if (nvlist_add_uint32(attr_list, "instance", port_instance)
3555 != DDI_SUCCESS) {
3556 goto error;
3557 }
3558
3559 if (nvlist_add_byte_array(attr_list, "port-wwn",
3560 port->port_pwwn, 8) != DDI_SUCCESS) {
3561 goto error;
3562 }
3563
3564 if (nvlist_add_byte_array(attr_list, "target-port-wwn",
3565 rp_pwwn, 8) != DDI_SUCCESS) {
3566 goto error;
3567 }
3568
3569 if (nvlist_add_uint32(attr_list, "target-port-id",
3570 rp_id) != DDI_SUCCESS) {
3571 goto error;
3572 }
3573
3574 if (fct_force_log == 0) {
3575 sleep = DDI_NOSLEEP;
3576 }
3577 rc = ddi_log_sysevent(fct_dip, DDI_VENDOR_SUNW, EC_SUNFC,
3578 subclass, attr_list, NULL, sleep);
3579 if (rc != DDI_SUCCESS) {
3580 cmn_err(CE_WARN, "%s:event dropped", __func__);
3581 goto error;
3582 }
3583
3584 nvlist_free(attr_list);
3585 return;
3586
3587 error:
3588 nvlist_free(attr_list);
3589 alloc_failed:
3590 stmf_trace(((fct_i_local_port_t *)port->port_fct_private)->iport_alias,
3591 "Unable to send %s event", subclass);
3592 }
3593
3594 uint64_t
3595 fct_netbuf_to_value(uint8_t *buf, uint8_t nbytes)
3596 {
3597 uint64_t ret = 0;
3598 uint8_t idx = 0;
3599
3600 do {
3601 ret |= (buf[idx] << (8 * (nbytes -idx - 1)));
3602 } while (++idx < nbytes);
3603
3604 return (ret);
3605 }
3606
3607 void
3608 fct_value_to_netbuf(uint64_t value, uint8_t *buf, uint8_t nbytes)
3609 {
3610 uint8_t idx = 0;
3611
3612 for (idx = 0; idx < nbytes; idx++) {
3613 buf[idx] = 0xFF & (value >> (8 * (nbytes - idx - 1)));
3614 }
3615 }
3616
3617 /*
3618 * from_ptr: ptr to uchar_t array of size WWN_SIZE
3619 * to_ptr: char ptr to string of size WWN_SIZE*2+1
3620 */
3621 void
3622 fct_wwn_to_str(char *to_ptr, const uint8_t *from_ptr)
3623 {
3624 ASSERT(to_ptr != NULL && from_ptr != NULL);
3625
3626 (void) sprintf(to_ptr, "%02x%02x%02x%02x%02x%02x%02x%02x",
3627 from_ptr[0], from_ptr[1], from_ptr[2], from_ptr[3],
3628 from_ptr[4], from_ptr[5], from_ptr[6], from_ptr[7]);
3629 }
3630
3631 static int
3632 fct_update_stats(kstat_t *ks, int rw)
3633 {
3634 fct_i_local_port_t *iport;
3635 fct_port_stat_t *port_kstat;
3636 fct_port_link_status_t stat;
3637 uint32_t buf_size = sizeof (stat);
3638 int ret;
3639
3640 if (rw == KSTAT_WRITE)
3641 return (EACCES);
3642
3643 iport = (fct_i_local_port_t *)ks->ks_private;
3644 port_kstat = (fct_port_stat_t *)ks->ks_data;
3645
3646 if (iport->iport_port->port_info == NULL) {
3647 return (EIO);
3648 }
3649 ret = iport->iport_port->port_info(FC_TGT_PORT_RLS,
3650 iport->iport_port, NULL, (uint8_t *)&stat, &buf_size);
3651 if (ret != STMF_SUCCESS) {
3652 return (EIO);
3653 }
3654
3655 port_kstat->link_failure_cnt.value.ui32 =
3656 stat.LinkFailureCount;
3657 port_kstat->loss_of_sync_cnt.value.ui32 =
3658 stat.LossOfSyncCount;
3659 port_kstat->loss_of_signals_cnt.value.ui32 =
3660 stat.LossOfSignalsCount;
3661 port_kstat->prim_seq_protocol_err_cnt.value.ui32 =
3662 stat.PrimitiveSeqProtocolErrorCount;
3663 port_kstat->invalid_tx_word_cnt.value.ui32 =
3664 stat.InvalidTransmissionWordCount;
3665 port_kstat->invalid_crc_cnt.value.ui32 =
3666 stat.InvalidCRCCount;
3667
3668 return (0);
3669 }
3670
3671 void
3672 fct_init_kstats(fct_i_local_port_t *iport)
3673 {
3674 kstat_t *ks;
3675 fct_port_stat_t *port_kstat;
3676 char name[256];
3677
3678 if (iport->iport_alias)
3679 (void) sprintf(name, "iport_%s", iport->iport_alias);
3680 else
3681 (void) sprintf(name, "iport_%"PRIxPTR"", (uintptr_t)iport);
3682 ks = kstat_create(FCT_MODULE_NAME, 0, name, "rawdata",
3683 KSTAT_TYPE_NAMED, sizeof (fct_port_stat_t) / sizeof (kstat_named_t),
3684 0);
3685
3686 if (ks == NULL) {
3687 return;
3688 }
3689 port_kstat = (fct_port_stat_t *)ks->ks_data;
3690
3691 iport->iport_kstat_portstat = ks;
3692 kstat_named_init(&port_kstat->link_failure_cnt,
3693 "Link_failure_cnt", KSTAT_DATA_UINT32);
3694 kstat_named_init(&port_kstat->loss_of_sync_cnt,
3695 "Loss_of_sync_cnt", KSTAT_DATA_UINT32);
3696 kstat_named_init(&port_kstat->loss_of_signals_cnt,
3697 "Loss_of_signals_cnt", KSTAT_DATA_UINT32);
3698 kstat_named_init(&port_kstat->prim_seq_protocol_err_cnt,
3699 "Prim_seq_protocol_err_cnt", KSTAT_DATA_UINT32);
3700 kstat_named_init(&port_kstat->invalid_tx_word_cnt,
3701 "Invalid_tx_word_cnt", KSTAT_DATA_UINT32);
3702 kstat_named_init(&port_kstat->invalid_crc_cnt,
3703 "Invalid_crc_cnt", KSTAT_DATA_UINT32);
3704 ks->ks_update = fct_update_stats;
3705 ks->ks_private = (void *)iport;
3706 kstat_install(ks);
3707
3708 }