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