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