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