2 * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL
);
33 #include <ipxe/refcnt.h>
34 #include <ipxe/list.h>
35 #include <ipxe/tables.h>
36 #include <ipxe/timer.h>
37 #include <ipxe/retry.h>
38 #include <ipxe/interface.h>
39 #include <ipxe/xfer.h>
40 #include <ipxe/iobuf.h>
42 #include <ipxe/fcels.h>
43 #include <ipxe/fcns.h>
51 /** List of Fibre Channel ports */
52 LIST_HEAD ( fc_ports
);
54 /** List of Fibre Channel peers */
55 LIST_HEAD ( fc_peers
);
57 /******************************************************************************
59 * Well-known addresses
61 ******************************************************************************
64 /** Unassigned port ID */
65 struct fc_port_id fc_empty_port_id
= { .bytes
= { 0x00, 0x00, 0x00 } };
67 /** F_Port contoller port ID */
68 struct fc_port_id fc_f_port_id
= { .bytes
= { 0xff, 0xff, 0xfe } };
70 /** Generic services port ID */
71 struct fc_port_id fc_gs_port_id
= { .bytes
= { 0xff, 0xff, 0xfc } };
73 /** Point-to-point low port ID */
74 struct fc_port_id fc_ptp_low_port_id
= { .bytes
= { 0x01, 0x01, 0x01 } };
76 /** Point-to-point high port ID */
77 struct fc_port_id fc_ptp_high_port_id
= { .bytes
= { 0x01, 0x01, 0x02 } };
79 /******************************************************************************
83 ******************************************************************************
87 * Format Fibre Channel port ID
89 * @v id Fibre Channel port ID
90 * @ret id_text Port ID text
92 const char * fc_id_ntoa ( const struct fc_port_id
*id
) {
93 static char id_text
[ FC_PORT_ID_STRLEN
+ 1 /* NUL */ ];
95 snprintf ( id_text
, sizeof ( id_text
), "%02x.%02x.%02x",
96 id
->bytes
[0], id
->bytes
[1], id
->bytes
[2] );
101 * Parse Fibre Channel port ID
103 * @v id_text Port ID text
104 * @ret id Fibre Channel port ID
105 * @ret rc Return status code
107 int fc_id_aton ( const char *id_text
, struct fc_port_id
*id
) {
108 char *ptr
= ( ( char * ) id_text
);
112 id
->bytes
[i
++] = strtoul ( ptr
, &ptr
, 16 );
113 if ( i
== sizeof ( id
->bytes
) )
114 return ( ( *ptr
== '\0' ) ?
0 : -EINVAL
);
122 * Format Fibre Channel WWN
124 * @v wwn Fibre Channel WWN
125 * @ret wwn_text WWN text
127 const char * fc_ntoa ( const struct fc_name
*wwn
) {
128 static char wwn_text
[ FC_NAME_STRLEN
+ 1 /* NUL */ ];
130 snprintf ( wwn_text
, sizeof ( wwn_text
),
131 "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
132 wwn
->bytes
[0], wwn
->bytes
[1], wwn
->bytes
[2], wwn
->bytes
[3],
133 wwn
->bytes
[4], wwn
->bytes
[5], wwn
->bytes
[6], wwn
->bytes
[7] );
138 * Parse Fibre Channel WWN
140 * @v wwn_text WWN text
141 * @ret wwn Fibre Channel WWN
142 * @ret rc Return status code
144 int fc_aton ( const char *wwn_text
, struct fc_name
*wwn
) {
145 char *ptr
= ( ( char * ) wwn_text
);
149 wwn
->bytes
[i
++] = strtoul ( ptr
, &ptr
, 16 );
150 if ( i
== sizeof ( wwn
->bytes
) )
151 return ( ( *ptr
== '\0' ) ?
0 : -EINVAL
);
159 * Fill Fibre Channel socket address
161 * @v sa_fc Fibre Channel socket address to fill in
162 * @v id Fibre Channel port ID
163 * @ret sa Socket address
165 struct sockaddr
* fc_fill_sockaddr ( struct sockaddr_fc
*sa_fc
,
166 struct fc_port_id
*id
) {
169 struct sockaddr_fc fc
;
170 } *u
= container_of ( sa_fc
, typeof ( *u
), fc
);
172 memset ( sa_fc
, 0, sizeof ( *sa_fc
) );
173 sa_fc
->sfc_family
= AF_FC
;
174 memcpy ( &sa_fc
->sfc_port_id
, id
, sizeof ( sa_fc
->sfc_port_id
) );
178 /******************************************************************************
180 * Fibre Channel link state
182 ******************************************************************************
185 /** Default link status code */
186 #define EUNKNOWN_LINK_STATUS __einfo_error ( EINFO_EUNKNOWN_LINK_STATUS )
187 #define EINFO_EUNKNOWN_LINK_STATUS \
188 __einfo_uniqify ( EINFO_EINPROGRESS, 0x01, "Unknown" )
191 * Mark Fibre Channel link as up
193 * @v link Fibre Channel link state monitor
195 static void fc_link_up ( struct fc_link_state
*link
) {
197 /* Stop retry timer */
198 stop_timer ( &link
->timer
);
200 /* Record link state */
205 * Mark Fibre Channel link as down
207 * @v link Fibre Channel link state monitor
210 static void fc_link_err ( struct fc_link_state
*link
, int rc
) {
212 /* Record link state */
214 rc
= -EUNKNOWN_LINK_STATUS
;
217 /* Schedule another link examination */
218 start_timer_fixed ( &link
->timer
, FC_LINK_RETRY_DELAY
);
222 * Examine Fibre Channel link state
224 * @v link Fibre Channel link state monitor
226 static void fc_link_examine ( struct fc_link_state
*link
) {
228 link
->examine ( link
);
232 * Handle Fibre Channel link retry timer expiry
234 static void fc_link_expired ( struct retry_timer
*timer
, int over __unused
) {
235 struct fc_link_state
*link
=
236 container_of ( timer
, struct fc_link_state
, timer
);
238 /* Schedule another link examination */
239 start_timer_fixed ( &link
->timer
, FC_LINK_RETRY_DELAY
);
242 fc_link_examine ( link
);
246 * Initialise Fibre Channel link state monitor
248 * @v link Fibre Channel link state monitor
249 * @v examine Examine link state method
250 * @v refcnt Reference counter
252 static void fc_link_init ( struct fc_link_state
*link
,
253 void ( * examine
) ( struct fc_link_state
*link
),
254 struct refcnt
*refcnt
) {
256 link
->rc
= -EUNKNOWN_LINK_STATUS
;
257 timer_init ( &link
->timer
, fc_link_expired
, refcnt
);
258 link
->examine
= examine
;
262 * Start monitoring Fibre Channel link state
264 * @v link Fibre Channel link state monitor
266 static void fc_link_start ( struct fc_link_state
*link
) {
267 start_timer_nodelay ( &link
->timer
);
271 * Stop monitoring Fibre Channel link state
273 * @v link Fibre Channel link state monitor
275 static void fc_link_stop ( struct fc_link_state
*link
) {
276 stop_timer ( &link
->timer
);
279 /******************************************************************************
281 * Fibre Channel exchanges
283 ******************************************************************************
286 /** A Fibre Channel exchange */
288 /** Reference count */
289 struct refcnt refcnt
;
290 /** Fibre Channel port */
291 struct fc_port
*port
;
292 /** List of active exchanges within this port */
293 struct list_head list
;
296 struct fc_port_id peer_port_id
;
297 /** Data structure type */
301 /** Local exchange ID */
303 /** Peer exchange ID */
304 uint16_t peer_xchg_id
;
305 /** Active sequence ID */
307 /** Active sequence count */
311 struct retry_timer timer
;
313 /** Upper-layer protocol interface */
314 struct interface ulp
;
317 /** Fibre Channel exchange flags */
318 enum fc_exchange_flags
{
319 /** We are the exchange originator */
320 FC_XCHG_ORIGINATOR
= 0x0001,
321 /** We have the sequence initiative */
322 FC_XCHG_SEQ_INITIATIVE
= 0x0002,
323 /** This is the first sequence of the exchange */
324 FC_XCHG_SEQ_FIRST
= 0x0004,
327 /** Fibre Channel timeout */
328 #define FC_TIMEOUT ( 1 * TICKS_PER_SEC )
331 * Create local Fibre Channel exchange identifier
333 * @ret xchg_id Local exchange ID
335 static unsigned int fc_new_xchg_id ( void ) {
336 static uint16_t next_id
= 0x0000;
338 /* We must avoid using FC_RX_ID_UNKNOWN (0xffff) */
344 * Create local Fibre Channel sequence identifier
346 * @ret seq_id Local sequence identifier
348 static unsigned int fc_new_seq_id ( void ) {
349 static uint8_t seq_id
= 0x00;
355 * Free Fibre Channel exchange
357 * @v refcnt Reference count
359 static void fc_xchg_free ( struct refcnt
*refcnt
) {
360 struct fc_exchange
*xchg
=
361 container_of ( refcnt
, struct fc_exchange
, refcnt
);
363 assert ( ! timer_running ( &xchg
->timer
) );
364 assert ( list_empty ( &xchg
->list
) );
366 fc_port_put ( xchg
->port
);
371 * Close Fibre Channel exchange
373 * @v xchg Fibre Channel exchange
374 * @v rc Reason for close
376 static void fc_xchg_close ( struct fc_exchange
*xchg
, int rc
) {
377 struct fc_port
*port
= xchg
->port
;
380 DBGC2 ( port
, "FCXCHG %s/%04x closed: %s\n",
381 port
->name
, xchg
->xchg_id
, strerror ( rc
) );
385 stop_timer ( &xchg
->timer
);
387 /* If list still holds a reference, remove from list of open
388 * exchanges and drop list's reference.
390 if ( ! list_empty ( &xchg
->list
) ) {
391 list_del ( &xchg
->list
);
392 INIT_LIST_HEAD ( &xchg
->list
);
393 ref_put ( &xchg
->refcnt
);
396 /* Shutdown interfaces */
397 intf_shutdown ( &xchg
->ulp
, rc
);
401 * Handle exchange timeout
403 * @v timer Timeout timer
404 * @v over Failure indicator
406 static void fc_xchg_expired ( struct retry_timer
*timer
, int over __unused
) {
407 struct fc_exchange
*xchg
=
408 container_of ( timer
, struct fc_exchange
, timer
);
409 struct fc_port
*port
= xchg
->port
;
411 DBGC ( port
, "FCXCHG %s/%04x timed out\n", port
->name
, xchg
->xchg_id
);
413 /* Terminate the exchange */
414 fc_xchg_close ( xchg
, -ETIMEDOUT
);
418 * Check Fibre Channel exchange window
420 * @v xchg Fibre Channel exchange
421 * @ret len Length opf window
423 static size_t fc_xchg_window ( struct fc_exchange
*xchg __unused
) {
425 /* We don't currently store the path MTU */
426 return FC_LOGIN_DEFAULT_MTU
;
430 * Allocate Fibre Channel I/O buffer
432 * @v xchg Fibre Channel exchange
433 * @v len Payload length
434 * @ret iobuf I/O buffer, or NULL
436 static struct io_buffer
* fc_xchg_alloc_iob ( struct fc_exchange
*xchg
,
438 struct fc_port
*port
= xchg
->port
;
439 struct io_buffer
*iobuf
;
441 iobuf
= xfer_alloc_iob ( &port
->transport
,
442 ( sizeof ( struct fc_frame_header
) + len
) );
444 iob_reserve ( iobuf
, sizeof ( struct fc_frame_header
) );
450 * Transmit data as part of a Fibre Channel exchange
452 * @v xchg Fibre Channel exchange
453 * @v iobuf I/O buffer
454 * @v meta Data transfer metadata
455 * @ret rc Return status code
457 static int fc_xchg_tx ( struct fc_exchange
*xchg
, struct io_buffer
*iobuf
,
458 struct xfer_metadata
*meta
) {
459 struct fc_port
*port
= xchg
->port
;
460 struct sockaddr_fc
*dest
= ( ( struct sockaddr_fc
* ) meta
->dest
);
461 struct fc_frame_header
*fchdr
;
463 unsigned int f_ctl_es
;
467 if ( ! ( xchg
->flags
& FC_XCHG_SEQ_INITIATIVE
) ) {
468 DBGC ( port
, "FCXCHG %s/%04x cannot transmit while not "
469 "holding sequence initiative\n",
470 port
->name
, xchg
->xchg_id
);
475 /* Calculate routing control */
476 switch ( xchg
->type
) {
478 r_ctl
= FC_R_CTL_ELS
;
479 if ( meta
->flags
& XFER_FL_RESPONSE
) {
480 r_ctl
|= FC_R_CTL_SOL_CTRL
;
482 r_ctl
|= FC_R_CTL_UNSOL_CTRL
;
486 r_ctl
= FC_R_CTL_DATA
;
487 if ( meta
->flags
& XFER_FL_RESPONSE
) {
488 r_ctl
|= FC_R_CTL_SOL_CTRL
;
490 r_ctl
|= FC_R_CTL_UNSOL_CTRL
;
494 r_ctl
= FC_R_CTL_DATA
;
495 switch ( meta
->flags
&
496 ( XFER_FL_CMD_STAT
| XFER_FL_RESPONSE
) ) {
497 case ( XFER_FL_CMD_STAT
| XFER_FL_RESPONSE
):
498 r_ctl
|= FC_R_CTL_CMD_STAT
;
500 case ( XFER_FL_CMD_STAT
):
501 r_ctl
|= FC_R_CTL_UNSOL_CMD
;
503 case ( XFER_FL_RESPONSE
):
504 r_ctl
|= FC_R_CTL_SOL_DATA
;
507 r_ctl
|= FC_R_CTL_UNSOL_DATA
;
513 /* Calculate exchange and sequence control */
515 if ( ! ( xchg
->flags
& FC_XCHG_ORIGINATOR
) )
516 f_ctl_es
|= FC_F_CTL_ES_RESPONDER
;
517 if ( xchg
->flags
& FC_XCHG_SEQ_FIRST
)
518 f_ctl_es
|= FC_F_CTL_ES_FIRST
;
519 if ( meta
->flags
& XFER_FL_OUT
)
520 f_ctl_es
|= ( FC_F_CTL_ES_END
| FC_F_CTL_ES_LAST
);
521 if ( meta
->flags
& XFER_FL_OVER
)
522 f_ctl_es
|= ( FC_F_CTL_ES_END
| FC_F_CTL_ES_TRANSFER
);
524 /* Create frame header */
525 fchdr
= iob_push ( iobuf
, sizeof ( *fchdr
) );
526 memset ( fchdr
, 0, sizeof ( *fchdr
) );
527 fchdr
->r_ctl
= r_ctl
;
528 memcpy ( &fchdr
->d_id
,
529 ( dest ?
&dest
->sfc_port_id
: &xchg
->peer_port_id
),
530 sizeof ( fchdr
->d_id
) );
531 memcpy ( &fchdr
->s_id
, &port
->port_id
, sizeof ( fchdr
->s_id
) );
532 fchdr
->type
= xchg
->type
;
533 fchdr
->f_ctl_es
= f_ctl_es
;
534 fchdr
->seq_id
= xchg
->seq_id
;
535 fchdr
->seq_cnt
= htons ( xchg
->seq_cnt
++ );
536 fchdr
->ox_id
= htons ( ( xchg
->flags
& FC_XCHG_ORIGINATOR
) ?
537 xchg
->xchg_id
: xchg
->peer_xchg_id
);
538 fchdr
->rx_id
= htons ( ( xchg
->flags
& FC_XCHG_ORIGINATOR
) ?
539 xchg
->peer_xchg_id
: xchg
->xchg_id
);
540 if ( meta
->flags
& XFER_FL_ABS_OFFSET
) {
541 fchdr
->f_ctl_misc
|= FC_F_CTL_MISC_REL_OFF
;
542 fchdr
->parameter
= htonl ( meta
->offset
);
545 /* Relinquish sequence initiative if applicable */
546 if ( meta
->flags
& XFER_FL_OVER
) {
547 xchg
->flags
&= ~( FC_XCHG_SEQ_INITIATIVE
| FC_XCHG_SEQ_FIRST
);
552 start_timer_fixed ( &xchg
->timer
, FC_TIMEOUT
);
555 if ( ( rc
= xfer_deliver_iob ( &port
->transport
,
556 iob_disown ( iobuf
) ) ) != 0 ) {
557 DBGC ( port
, "FCXCHG %s/%04x cannot transmit: %s\n",
558 port
->name
, xchg
->xchg_id
, strerror ( rc
) );
567 /** Mapping from Fibre Channel routing control information to xfer metadata */
568 static const uint8_t fc_r_ctl_info_meta_flags
[ FC_R_CTL_INFO_MASK
+ 1 ] = {
569 [FC_R_CTL_UNCAT
] = ( 0 ),
570 [FC_R_CTL_SOL_DATA
] = ( XFER_FL_RESPONSE
),
571 [FC_R_CTL_UNSOL_CTRL
] = ( XFER_FL_CMD_STAT
),
572 [FC_R_CTL_SOL_CTRL
] = ( XFER_FL_CMD_STAT
),
573 [FC_R_CTL_UNSOL_DATA
] = ( 0 ),
574 [FC_R_CTL_DATA_DESC
] = ( XFER_FL_CMD_STAT
),
575 [FC_R_CTL_UNSOL_CMD
] = ( XFER_FL_CMD_STAT
),
576 [FC_R_CTL_CMD_STAT
] = ( XFER_FL_CMD_STAT
| XFER_FL_RESPONSE
),
580 * Receive data as part of a Fibre Channel exchange
582 * @v xchg Fibre Channel exchange
583 * @v iobuf I/O buffer
584 * @v meta Data transfer metadata
585 * @ret rc Return status code
587 static int fc_xchg_rx ( struct fc_exchange
*xchg
, struct io_buffer
*iobuf
,
588 struct xfer_metadata
*meta __unused
) {
589 struct fc_port
*port
= xchg
->port
;
590 struct fc_frame_header
*fchdr
= iobuf
->data
;
591 struct xfer_metadata fc_meta
;
592 struct sockaddr_fc src
;
593 struct sockaddr_fc dest
;
596 /* Record peer exchange ID */
598 ntohs ( ( fchdr
->f_ctl_es
& FC_F_CTL_ES_RESPONDER
) ?
599 fchdr
->rx_id
: fchdr
->ox_id
);
601 /* Sequence checks */
602 if ( xchg
->flags
& FC_XCHG_SEQ_INITIATIVE
) {
603 DBGC ( port
, "FCXCHG %s/%04x received frame while holding "
604 "sequence initiative\n", port
->name
, xchg
->xchg_id
);
608 if ( ntohs ( fchdr
->seq_cnt
) != xchg
->seq_cnt
) {
609 DBGC ( port
, "FCXCHG %s/%04x received out-of-order frame %d "
610 "(expected %d)\n", port
->name
, xchg
->xchg_id
,
611 ntohs ( fchdr
->seq_cnt
), xchg
->seq_cnt
);
615 if ( xchg
->seq_cnt
== 0 )
616 xchg
->seq_id
= fchdr
->seq_id
;
618 if ( fchdr
->seq_id
!= xchg
->seq_id
) {
619 DBGC ( port
, "FCXCHG %s/%04x received frame for incorrect "
620 "sequence %02x (expected %02x)\n", port
->name
,
621 xchg
->xchg_id
, fchdr
->seq_id
, xchg
->seq_id
);
626 /* Check for end of sequence and transfer of sequence initiative */
627 if ( fchdr
->f_ctl_es
& FC_F_CTL_ES_END
) {
629 if ( fchdr
->f_ctl_es
& FC_F_CTL_ES_TRANSFER
) {
630 xchg
->flags
|= FC_XCHG_SEQ_INITIATIVE
;
631 xchg
->seq_id
= fc_new_seq_id();
635 /* Construct metadata */
636 memset ( &fc_meta
, 0, sizeof ( fc_meta
) );
638 fc_r_ctl_info_meta_flags
[ fchdr
->r_ctl
& FC_R_CTL_INFO_MASK
];
639 if ( fchdr
->f_ctl_es
& FC_F_CTL_ES_TRANSFER
) {
640 fc_meta
.flags
|= XFER_FL_OVER
;
642 if ( ( fchdr
->f_ctl_es
& FC_F_CTL_ES_LAST
) &&
643 ( fchdr
->f_ctl_es
& FC_F_CTL_ES_END
) ) {
644 fc_meta
.flags
|= XFER_FL_OUT
;
646 if ( fchdr
->f_ctl_misc
& FC_F_CTL_MISC_REL_OFF
) {
647 fc_meta
.flags
|= XFER_FL_ABS_OFFSET
;
648 fc_meta
.offset
= ntohl ( fchdr
->parameter
);
650 fc_meta
.src
= fc_fill_sockaddr ( &src
, &fchdr
->s_id
);
651 fc_meta
.dest
= fc_fill_sockaddr ( &dest
, &fchdr
->d_id
);
654 start_timer_fixed ( &xchg
->timer
, FC_TIMEOUT
);
656 /* Deliver via exchange's ULP interface */
657 iob_pull ( iobuf
, sizeof ( *fchdr
) );
658 if ( ( rc
= xfer_deliver ( &xchg
->ulp
, iob_disown ( iobuf
),
659 &fc_meta
) ) != 0 ) {
660 DBGC ( port
, "FCXCHG %s/%04x cannot deliver frame: %s\n",
661 port
->name
, xchg
->xchg_id
, strerror ( rc
) );
665 /* Close exchange if applicable */
666 if ( ( fchdr
->f_ctl_es
& FC_F_CTL_ES_LAST
) &&
667 ( fchdr
->f_ctl_es
& FC_F_CTL_ES_END
) ) {
668 fc_xchg_close ( xchg
, 0 );
676 /** Fibre Channel exchange ULP interface operations */
677 static struct interface_operation fc_xchg_ulp_op
[] = {
678 INTF_OP ( xfer_deliver
, struct fc_exchange
*, fc_xchg_tx
),
679 INTF_OP ( xfer_alloc_iob
, struct fc_exchange
*, fc_xchg_alloc_iob
),
680 INTF_OP ( xfer_window
, struct fc_exchange
*, fc_xchg_window
),
681 INTF_OP ( intf_close
, struct fc_exchange
*, fc_xchg_close
),
684 /** Fibre Channel exchange ULP interface descriptor */
685 static struct interface_descriptor fc_xchg_ulp_desc
=
686 INTF_DESC ( struct fc_exchange
, ulp
, fc_xchg_ulp_op
);
689 * Create new Fibre Channel exchange
691 * @v port Fibre Channel port
692 * @v peer_port_id Peer port ID
693 * @ret xchg Exchange, or NULL
695 static struct fc_exchange
* fc_xchg_create ( struct fc_port
*port
,
696 struct fc_port_id
*peer_port_id
,
697 unsigned int type
) {
698 struct fc_exchange
*xchg
;
700 /* Allocate and initialise structure */
701 xchg
= zalloc ( sizeof ( *xchg
) );
704 ref_init ( &xchg
->refcnt
, fc_xchg_free
);
705 intf_init ( &xchg
->ulp
, &fc_xchg_ulp_desc
, &xchg
->refcnt
);
706 timer_init ( &xchg
->timer
, fc_xchg_expired
, &xchg
->refcnt
);
707 xchg
->port
= fc_port_get ( port
);
708 memcpy ( &xchg
->peer_port_id
, peer_port_id
,
709 sizeof ( xchg
->peer_port_id
) );
711 xchg
->xchg_id
= fc_new_xchg_id();
712 xchg
->peer_xchg_id
= FC_RX_ID_UNKNOWN
;
713 xchg
->seq_id
= fc_new_seq_id();
715 /* Transfer reference to list of exchanges and return */
716 list_add ( &xchg
->list
, &port
->xchgs
);
721 * Originate a new Fibre Channel exchange
723 * @v parent Interface to which to attach
724 * @v port Fibre Channel port
725 * @v peer_port_id Peer port ID
726 * @ret xchg_id Exchange ID, or negative error
728 int fc_xchg_originate ( struct interface
*parent
, struct fc_port
*port
,
729 struct fc_port_id
*peer_port_id
, unsigned int type
) {
730 struct fc_exchange
*xchg
;
732 /* Allocate and initialise structure */
733 xchg
= fc_xchg_create ( port
, peer_port_id
, type
);
736 xchg
->flags
= ( FC_XCHG_ORIGINATOR
| FC_XCHG_SEQ_INITIATIVE
|
739 DBGC2 ( port
, "FCXCHG %s/%04x originating to %s (type %02x)\n",
740 port
->name
, xchg
->xchg_id
, fc_id_ntoa ( &xchg
->peer_port_id
),
743 /* Attach to parent interface and return */
744 intf_plug_plug ( &xchg
->ulp
, parent
);
745 return xchg
->xchg_id
;
749 * Open a new responder Fibre Channel exchange
751 * @v port Fibre Channel port
752 * @v fchdr Fibre Channel frame header
753 * @ret xchg Fibre Channel exchange, or NULL
755 static struct fc_exchange
* fc_xchg_respond ( struct fc_port
*port
,
756 struct fc_frame_header
*fchdr
) {
757 struct fc_exchange
*xchg
;
758 struct fc_responder
*responder
;
759 unsigned int type
= fchdr
->type
;
762 /* Allocate and initialise structure */
763 xchg
= fc_xchg_create ( port
, &fchdr
->s_id
, type
);
766 xchg
->seq_id
= fchdr
->seq_id
;
768 DBGC2 ( port
, "FCXCHG %s/%04x responding to %s xchg %04x (type "
769 "%02x)\n", port
->name
, xchg
->xchg_id
,
770 fc_id_ntoa ( &xchg
->peer_port_id
),
771 ntohs ( fchdr
->ox_id
), xchg
->type
);
773 /* Find a responder, if any */
774 for_each_table_entry ( responder
, FC_RESPONDERS
) {
775 if ( responder
->type
== type
) {
776 if ( ( rc
= responder
->respond ( &xchg
->ulp
, port
,
778 &fchdr
->s_id
) ) !=0 ){
779 DBGC ( port
, "FCXCHG %s/%04x could not "
780 "respond: %s\n", port
->name
,
781 xchg
->xchg_id
, strerror ( rc
) );
787 /* We may or may not have a ULP attached at this point, but
788 * the exchange does exist.
793 /******************************************************************************
795 * Fibre Channel ports
797 ******************************************************************************
801 * Close Fibre Channel port
803 * @v port Fibre Channel port
804 * @v rc Reason for close
806 static void fc_port_close ( struct fc_port
*port
, int rc
) {
807 struct fc_exchange
*xchg
;
808 struct fc_exchange
*tmp
;
810 DBGC ( port
, "FCPORT %s closed\n", port
->name
);
812 /* Log out port, if necessary */
813 if ( fc_link_ok ( &port
->link
) )
814 fc_port_logout ( port
, rc
);
816 /* Stop link monitor */
817 fc_link_stop ( &port
->link
);
819 /* Shut down interfaces */
820 intf_shutdown ( &port
->transport
, rc
);
821 intf_shutdown ( &port
->flogi
, rc
);
822 intf_shutdown ( &port
->ns_plogi
, rc
);
824 /* Shut down any remaining exchanges */
825 list_for_each_entry_safe ( xchg
, tmp
, &port
->xchgs
, list
)
826 fc_xchg_close ( xchg
, rc
);
828 /* Remove from list of ports */
829 list_del ( &port
->list
);
830 INIT_LIST_HEAD ( &port
->list
);
834 * Identify Fibre Channel exchange by local exchange ID
836 * @v port Fibre Channel port
837 * @v xchg_id Local exchange ID
838 * @ret xchg Fibre Channel exchange, or NULL
840 static struct fc_exchange
* fc_port_demux ( struct fc_port
*port
,
841 unsigned int xchg_id
) {
842 struct fc_exchange
*xchg
;
844 list_for_each_entry ( xchg
, &port
->xchgs
, list
) {
845 if ( xchg
->xchg_id
== xchg_id
)
852 * Handle received frame from Fibre Channel port
854 * @v port Fibre Channel port
855 * @v iobuf I/O buffer
856 * @v meta Data transfer metadata
857 * @ret rc Return status code
859 static int fc_port_deliver ( struct fc_port
*port
, struct io_buffer
*iobuf
,
860 struct xfer_metadata
*meta
) {
861 struct fc_frame_header
*fchdr
= iobuf
->data
;
862 unsigned int xchg_id
;
863 struct fc_exchange
*xchg
;
867 if ( iob_len ( iobuf
) < sizeof ( *fchdr
) ) {
868 DBGC ( port
, "FCPORT %s received underlength frame (%zd "
869 "bytes)\n", port
->name
, iob_len ( iobuf
) );
874 /* Verify local port ID */
875 if ( ( memcmp ( &fchdr
->d_id
, &port
->port_id
,
876 sizeof ( fchdr
->d_id
) ) != 0 ) &&
877 ( memcmp ( &fchdr
->d_id
, &fc_f_port_id
,
878 sizeof ( fchdr
->d_id
) ) != 0 ) &&
879 ( memcmp ( &port
->port_id
, &fc_empty_port_id
,
880 sizeof ( port
->port_id
) ) != 0 ) ) {
881 DBGC ( port
, "FCPORT %s received frame for incorrect port ID "
882 "%s\n", port
->name
, fc_id_ntoa ( &fchdr
->d_id
) );
887 /* Demultiplex amongst active exchanges */
888 xchg_id
= ntohs ( ( fchdr
->f_ctl_es
& FC_F_CTL_ES_RESPONDER
) ?
889 fchdr
->ox_id
: fchdr
->rx_id
);
890 xchg
= fc_port_demux ( port
, xchg_id
);
892 /* If we have no active exchange and this frame starts a new
893 * exchange, try to create a new responder exchange
895 if ( ( fchdr
->f_ctl_es
& FC_F_CTL_ES_FIRST
) &&
896 ( fchdr
->seq_cnt
== 0 ) ) {
898 /* Create new exchange */
899 xchg
= fc_xchg_respond ( port
, fchdr
);
901 DBGC ( port
, "FCPORT %s cannot create new exchange\n",
908 /* Fail if no exchange exists */
910 DBGC ( port
, "FCPORT %s xchg %04x unknown\n",
911 port
->name
, xchg_id
);
916 /* Pass received frame to exchange */
917 ref_get ( &xchg
->refcnt
);
918 if ( ( rc
= fc_xchg_rx ( xchg
, iob_disown ( iobuf
), meta
) ) != 0 )
922 ref_put ( &xchg
->refcnt
);
932 * Log in Fibre Channel port
934 * @v port Fibre Channel port
935 * @v port_id Local port ID
936 * @v link_node_wwn Link node name
937 * @v link_port_wwn Link port name
938 * @v has_fabric Link is to a fabric
939 * @ret rc Return status code
941 int fc_port_login ( struct fc_port
*port
, struct fc_port_id
*port_id
,
942 const struct fc_name
*link_node_wwn
,
943 const struct fc_name
*link_port_wwn
, int has_fabric
) {
944 struct fc_peer
*peer
;
948 /* Perform implicit logout if logged in and details differ */
949 if ( fc_link_ok ( &port
->link
) &&
950 ( ( ( !! ( port
->flags
& FC_PORT_HAS_FABRIC
) ) !=
951 ( !! has_fabric
) ) ||
952 ( memcmp ( &port
->link_node_wwn
, link_node_wwn
,
953 sizeof ( port
->link_node_wwn
) ) != 0 ) ||
954 ( memcmp ( &port
->link_port_wwn
, link_port_wwn
,
955 sizeof ( port
->link_port_wwn
) ) != 0 ) ||
957 ( memcmp ( &port
->port_id
, port_id
,
958 sizeof ( port
->port_id
) ) != 0 ) ) ) ) {
959 fc_port_logout ( port
, 0 );
962 /* Log in, if applicable */
963 if ( ! fc_link_ok ( &port
->link
) ) {
965 /* Record link port name */
966 memcpy ( &port
->link_node_wwn
, link_node_wwn
,
967 sizeof ( port
->link_node_wwn
) );
968 memcpy ( &port
->link_port_wwn
, link_port_wwn
,
969 sizeof ( port
->link_port_wwn
) );
970 DBGC ( port
, "FCPORT %s logged in to %s",
971 port
->name
, fc_ntoa ( &port
->link_node_wwn
) );
972 DBGC ( port
, " port %s\n", fc_ntoa ( &port
->link_port_wwn
) );
974 /* Calculate local (and possibly remote) port IDs */
976 port
->flags
|= FC_PORT_HAS_FABRIC
;
977 memcpy ( &port
->port_id
, port_id
,
978 sizeof ( port
->port_id
) );
980 port
->flags
&= ~FC_PORT_HAS_FABRIC
;
981 if ( memcmp ( &port
->port_wwn
, link_port_wwn
,
982 sizeof ( port
->port_wwn
) ) > 0 ) {
983 memcpy ( &port
->port_id
, &fc_ptp_high_port_id
,
984 sizeof ( port
->port_id
) );
985 memcpy ( &port
->ptp_link_port_id
,
987 sizeof ( port
->ptp_link_port_id
) );
989 memcpy ( &port
->port_id
, &fc_ptp_low_port_id
,
990 sizeof ( port
->port_id
) );
991 memcpy ( &port
->ptp_link_port_id
,
992 &fc_ptp_high_port_id
,
993 sizeof ( port
->ptp_link_port_id
) );
996 DBGC ( port
, "FCPORT %s logged in via a %s, with local ID "
998 ( ( port
->flags
& FC_PORT_HAS_FABRIC
) ?
999 "fabric" : "point-to-point link" ),
1000 fc_id_ntoa ( &port
->port_id
) );
1003 /* Log in to name server, if attached to a fabric */
1004 if ( has_fabric
&& ! ( port
->flags
& FC_PORT_HAS_NS
) ) {
1006 DBGC ( port
, "FCPORT %s attempting login to name server\n",
1009 intf_restart ( &port
->ns_plogi
, -ECANCELED
);
1010 if ( ( rc
= fc_els_plogi ( &port
->ns_plogi
, port
,
1011 &fc_gs_port_id
) ) != 0 ) {
1012 DBGC ( port
, "FCPORT %s could not initiate name "
1013 "server PLOGI: %s\n",
1014 port
->name
, strerror ( rc
) );
1015 fc_port_logout ( port
, rc
);
1021 fc_link_up ( &port
->link
);
1023 /* Notify peers of link state change */
1024 list_for_each_entry_safe ( peer
, tmp
, &fc_peers
, list
) {
1025 fc_peer_get ( peer
);
1026 fc_link_examine ( &peer
->link
);
1027 fc_peer_put ( peer
);
1034 * Log out Fibre Channel port
1036 * @v port Fibre Channel port
1037 * @v rc Reason for logout
1039 void fc_port_logout ( struct fc_port
*port
, int rc
) {
1040 struct fc_peer
*peer
;
1041 struct fc_peer
*tmp
;
1043 DBGC ( port
, "FCPORT %s logged out: %s\n",
1044 port
->name
, strerror ( rc
) );
1046 /* Erase port details */
1047 memset ( &port
->port_id
, 0, sizeof ( port
->port_id
) );
1051 fc_link_err ( &port
->link
, rc
);
1053 /* Notify peers of link state change */
1054 list_for_each_entry_safe ( peer
, tmp
, &fc_peers
, list
) {
1055 fc_peer_get ( peer
);
1056 fc_link_examine ( &peer
->link
);
1057 fc_peer_put ( peer
);
1062 * Handle FLOGI completion
1064 * @v port Fibre Channel port
1065 * @v rc Reason for completion
1067 static void fc_port_flogi_done ( struct fc_port
*port
, int rc
) {
1069 intf_restart ( &port
->flogi
, rc
);
1072 fc_port_logout ( port
, rc
);
1076 * Handle name server PLOGI completion
1078 * @v port Fibre Channel port
1079 * @v rc Reason for completion
1081 static void fc_port_ns_plogi_done ( struct fc_port
*port
, int rc
) {
1083 intf_restart ( &port
->ns_plogi
, rc
);
1086 port
->flags
|= FC_PORT_HAS_NS
;
1087 DBGC ( port
, "FCPORT %s logged in to name server\n",
1090 DBGC ( port
, "FCPORT %s could not log in to name server: %s\n",
1091 port
->name
, strerror ( rc
) );
1092 /* Absence of a name server is not a fatal error */
1097 * Examine Fibre Channel port link state
1099 * @ link Fibre Channel link state monitor
1101 static void fc_port_examine ( struct fc_link_state
*link
) {
1102 struct fc_port
*port
= container_of ( link
, struct fc_port
, link
);
1105 /* Do nothing if already logged in */
1106 if ( fc_link_ok ( &port
->link
) )
1109 DBGC ( port
, "FCPORT %s attempting login\n", port
->name
);
1111 /* Try to create FLOGI ELS */
1112 intf_restart ( &port
->flogi
, -ECANCELED
);
1113 if ( ( rc
= fc_els_flogi ( &port
->flogi
, port
) ) != 0 ) {
1114 DBGC ( port
, "FCPORT %s could not initiate FLOGI: %s\n",
1115 port
->name
, strerror ( rc
) );
1116 fc_port_logout ( port
, rc
);
1122 * Handle change of flow control window
1124 * @v port Fibre Channel port
1126 static void fc_port_window_changed ( struct fc_port
*port
) {
1129 /* Check if transport layer is ready */
1130 window
= xfer_window ( &port
->transport
);
1133 /* Transport layer is ready. Start login if the link
1134 * is not already up.
1136 if ( ! fc_link_ok ( &port
->link
) )
1137 fc_link_start ( &port
->link
);
1141 /* Transport layer is not ready. Log out port and
1142 * wait for transport layer before attempting log in
1145 fc_port_logout ( port
, -ENOTCONN
);
1146 fc_link_stop ( &port
->link
);
1150 /** Fibre Channel port transport interface operations */
1151 static struct interface_operation fc_port_transport_op
[] = {
1152 INTF_OP ( xfer_deliver
, struct fc_port
*, fc_port_deliver
),
1153 INTF_OP ( xfer_window_changed
, struct fc_port
*,
1154 fc_port_window_changed
),
1155 INTF_OP ( intf_close
, struct fc_port
*, fc_port_close
),
1158 /** Fibre Channel port transport interface descriptor */
1159 static struct interface_descriptor fc_port_transport_desc
=
1160 INTF_DESC ( struct fc_port
, transport
, fc_port_transport_op
);
1162 /** Fibre Channel port FLOGI interface operations */
1163 static struct interface_operation fc_port_flogi_op
[] = {
1164 INTF_OP ( intf_close
, struct fc_port
*, fc_port_flogi_done
),
1167 /** Fibre Channel port FLOGI interface descriptor */
1168 static struct interface_descriptor fc_port_flogi_desc
=
1169 INTF_DESC ( struct fc_port
, flogi
, fc_port_flogi_op
);
1171 /** Fibre Channel port name server PLOGI interface operations */
1172 static struct interface_operation fc_port_ns_plogi_op
[] = {
1173 INTF_OP ( intf_close
, struct fc_port
*, fc_port_ns_plogi_done
),
1176 /** Fibre Channel port name server PLOGI interface descriptor */
1177 static struct interface_descriptor fc_port_ns_plogi_desc
=
1178 INTF_DESC ( struct fc_port
, ns_plogi
, fc_port_ns_plogi_op
);
1181 * Create Fibre Channel port
1183 * @v transport Transport interface
1184 * @v node Fibre Channel node name
1185 * @v port Fibre Channel port name
1186 * @v name Symbolic port name
1187 * @ret rc Return status code
1189 int fc_port_open ( struct interface
*transport
, const struct fc_name
*node_wwn
,
1190 const struct fc_name
*port_wwn
, const char *name
) {
1191 struct fc_port
*port
;
1193 /* Allocate and initialise structure */
1194 port
= zalloc ( sizeof ( *port
) );
1197 ref_init ( &port
->refcnt
, NULL
);
1198 intf_init ( &port
->transport
, &fc_port_transport_desc
, &port
->refcnt
);
1199 fc_link_init ( &port
->link
, fc_port_examine
, &port
->refcnt
);
1200 intf_init ( &port
->flogi
, &fc_port_flogi_desc
, &port
->refcnt
);
1201 intf_init ( &port
->ns_plogi
, &fc_port_ns_plogi_desc
, &port
->refcnt
);
1202 list_add_tail ( &port
->list
, &fc_ports
);
1203 INIT_LIST_HEAD ( &port
->xchgs
);
1204 memcpy ( &port
->node_wwn
, node_wwn
, sizeof ( port
->node_wwn
) );
1205 memcpy ( &port
->port_wwn
, port_wwn
, sizeof ( port
->port_wwn
) );
1206 snprintf ( port
->name
, sizeof ( port
->name
), "%s", name
);
1208 DBGC ( port
, "FCPORT %s opened as %s",
1209 port
->name
, fc_ntoa ( &port
->node_wwn
) );
1210 DBGC ( port
, " port %s\n", fc_ntoa ( &port
->port_wwn
) );
1212 /* Attach to transport layer, mortalise self, and return */
1213 intf_plug_plug ( &port
->transport
, transport
);
1214 ref_put ( &port
->refcnt
);
1219 * Find Fibre Channel port by name
1221 * @v name Fibre Channel port name
1222 * @ret port Fibre Channel port, or NULL
1224 struct fc_port
* fc_port_find ( const char *name
) {
1225 struct fc_port
*port
;
1227 list_for_each_entry ( port
, &fc_ports
, list
) {
1228 if ( strcmp ( name
, port
->name
) == 0 )
1234 /******************************************************************************
1236 * Fibre Channel peers
1238 ******************************************************************************
1242 * Close Fibre Channel peer
1244 * @v peer Fibre Channel peer
1245 * @v rc Reason for close
1247 static void fc_peer_close ( struct fc_peer
*peer
, int rc
) {
1249 DBGC ( peer
, "FCPEER %s closed: %s\n",
1250 fc_ntoa ( &peer
->port_wwn
) , strerror ( rc
) );
1253 assert ( list_empty ( &peer
->ulps
) );
1255 /* Stop link timer */
1256 fc_link_stop ( &peer
->link
);
1258 /* Shut down interfaces */
1259 intf_shutdown ( &peer
->plogi
, rc
);
1261 /* Remove from list of peers */
1262 list_del ( &peer
->list
);
1263 INIT_LIST_HEAD ( &peer
->list
);
1267 * Increment Fibre Channel peer active usage count
1269 * @v peer Fibre Channel peer
1271 static void fc_peer_increment ( struct fc_peer
*peer
) {
1273 /* Increment our usage count */
1278 * Decrement Fibre Channel peer active usage count
1280 * @v peer Fibre Channel peer
1282 static void fc_peer_decrement ( struct fc_peer
*peer
) {
1285 assert ( peer
->usage
> 0 );
1287 /* Decrement our usage count and log out if we reach zero */
1288 if ( --(peer
->usage
) == 0 )
1289 fc_peer_logout ( peer
, 0 );
1293 * Log in Fibre Channel peer
1295 * @v peer Fibre Channel peer
1296 * @v port Fibre Channel port
1297 * @v port_id Port ID
1298 * @ret rc Return status code
1300 int fc_peer_login ( struct fc_peer
*peer
, struct fc_port
*port
,
1301 struct fc_port_id
*port_id
) {
1305 /* Perform implicit logout if logged in and details differ */
1306 if ( fc_link_ok ( &peer
->link
) &&
1307 ( ( peer
->port
!= port
) ||
1308 ( memcmp ( &peer
->port_id
, port_id
,
1309 sizeof ( peer
->port_id
) ) !=0 ) ) ) {
1310 fc_peer_logout ( peer
, 0 );
1313 /* Log in, if applicable */
1314 if ( ! fc_link_ok ( &peer
->link
) ) {
1316 /* Record peer details */
1317 assert ( peer
->port
== NULL
);
1318 peer
->port
= fc_port_get ( port
);
1319 memcpy ( &peer
->port_id
, port_id
, sizeof ( peer
->port_id
) );
1320 DBGC ( peer
, "FCPEER %s logged in via %s as %s\n",
1321 fc_ntoa ( &peer
->port_wwn
), peer
->port
->name
,
1322 fc_id_ntoa ( &peer
->port_id
) );
1324 /* Add login reference */
1325 fc_peer_get ( peer
);
1329 fc_link_up ( &peer
->link
);
1331 /* Notify ULPs of link state change */
1332 list_for_each_entry_safe ( ulp
, tmp
, &peer
->ulps
, list
) {
1334 fc_link_examine ( &ulp
->link
);
1342 * Log out Fibre Channel peer
1344 * @v peer Fibre Channel peer
1345 * @v rc Reason for logout
1347 void fc_peer_logout ( struct fc_peer
*peer
, int rc
) {
1351 DBGC ( peer
, "FCPEER %s logged out: %s\n",
1352 fc_ntoa ( &peer
->port_wwn
), strerror ( rc
) );
1354 /* Drop login reference, if applicable */
1355 if ( fc_link_ok ( &peer
->link
) )
1356 fc_peer_put ( peer
);
1358 /* Erase peer details */
1359 fc_port_put ( peer
->port
);
1363 fc_link_err ( &peer
->link
, rc
);
1365 /* Notify ULPs of link state change */
1366 list_for_each_entry_safe ( ulp
, tmp
, &peer
->ulps
, list
) {
1368 fc_link_examine ( &ulp
->link
);
1372 /* Close peer if there are no active users */
1373 if ( peer
->usage
== 0 )
1374 fc_peer_close ( peer
, rc
);
1378 * Handle PLOGI completion
1380 * @v peer Fibre Channel peer
1381 * @v rc Reason for completion
1383 static void fc_peer_plogi_done ( struct fc_peer
*peer
, int rc
) {
1385 intf_restart ( &peer
->plogi
, rc
);
1388 fc_peer_logout ( peer
, rc
);
1394 * @v peer Fibre Channel peer
1395 * @v port Fibre Channel port
1396 * @v peer_port_id Peer port ID
1397 * @ret rc Return status code
1399 static int fc_peer_plogi ( struct fc_peer
*peer
, struct fc_port
*port
,
1400 struct fc_port_id
*peer_port_id
) {
1403 /* Try to create PLOGI ELS */
1404 intf_restart ( &peer
->plogi
, -ECANCELED
);
1405 if ( ( rc
= fc_els_plogi ( &peer
->plogi
, port
, peer_port_id
) ) != 0 ) {
1406 DBGC ( peer
, "FCPEER %s could not initiate PLOGI: %s\n",
1407 fc_ntoa ( &peer
->port_wwn
), strerror ( rc
) );
1408 fc_peer_logout ( peer
, rc
);
1416 * Examine Fibre Channel peer link state
1418 * @ link Fibre Channel link state monitor
1420 static void fc_peer_examine ( struct fc_link_state
*link
) {
1421 struct fc_peer
*peer
= container_of ( link
, struct fc_peer
, link
);
1422 struct fc_port
*port
;
1425 /* Check to see if underlying port link has gone down */
1426 if ( peer
->port
&& ( ! fc_link_ok ( &peer
->port
->link
) ) ) {
1427 fc_peer_logout ( peer
, -ENOTCONN
);
1431 /* Do nothing if already logged in */
1432 if ( fc_link_ok ( &peer
->link
) )
1435 DBGC ( peer
, "FCPEER %s attempting login\n",
1436 fc_ntoa ( &peer
->port_wwn
) );
1439 assert ( peer
->port
== NULL
);
1441 /* First, look for a port with the peer attached via a
1442 * point-to-point link.
1444 list_for_each_entry ( port
, &fc_ports
, list
) {
1445 if ( fc_link_ok ( &port
->link
) &&
1446 ( ! ( port
->flags
& FC_PORT_HAS_FABRIC
) ) &&
1447 ( memcmp ( &peer
->port_wwn
, &port
->link_port_wwn
,
1448 sizeof ( peer
->port_wwn
) ) == 0 ) ) {
1449 /* Use this peer port ID, and stop looking */
1450 fc_peer_plogi ( peer
, port
, &port
->ptp_link_port_id
);
1455 /* If the peer is not directly attached, try initiating a name
1456 * server lookup on any suitable ports.
1458 list_for_each_entry ( port
, &fc_ports
, list
) {
1459 if ( fc_link_ok ( &port
->link
) &&
1460 ( port
->flags
& FC_PORT_HAS_FABRIC
) &&
1461 ( port
->flags
& FC_PORT_HAS_NS
) ) {
1462 if ( ( rc
= fc_ns_query ( peer
, port
,
1463 fc_peer_plogi
) ) != 0 ) {
1464 DBGC ( peer
, "FCPEER %s could not attempt "
1465 "name server lookup on %s: %s\n",
1466 fc_ntoa ( &peer
->port_wwn
), port
->name
,
1474 /** Fibre Channel peer PLOGI interface operations */
1475 static struct interface_operation fc_peer_plogi_op
[] = {
1476 INTF_OP ( intf_close
, struct fc_peer
*, fc_peer_plogi_done
),
1479 /** Fibre Channel peer PLOGI interface descriptor */
1480 static struct interface_descriptor fc_peer_plogi_desc
=
1481 INTF_DESC ( struct fc_peer
, plogi
, fc_peer_plogi_op
);
1484 * Create Fibre Channel peer
1486 * @v port_wwn Node name
1487 * @ret peer Fibre Channel peer, or NULL
1489 static struct fc_peer
* fc_peer_create ( const struct fc_name
*port_wwn
) {
1490 struct fc_peer
*peer
;
1492 /* Allocate and initialise structure */
1493 peer
= zalloc ( sizeof ( *peer
) );
1496 ref_init ( &peer
->refcnt
, NULL
);
1497 fc_link_init ( &peer
->link
, fc_peer_examine
, &peer
->refcnt
);
1498 intf_init ( &peer
->plogi
, &fc_peer_plogi_desc
, &peer
->refcnt
);
1499 list_add_tail ( &peer
->list
, &fc_peers
);
1500 memcpy ( &peer
->port_wwn
, port_wwn
, sizeof ( peer
->port_wwn
) );
1501 INIT_LIST_HEAD ( &peer
->ulps
);
1503 /* Start link monitor */
1504 fc_link_start ( &peer
->link
);
1506 DBGC ( peer
, "FCPEER %s created\n", fc_ntoa ( &peer
->port_wwn
) );
1511 * Get Fibre Channel peer by node name
1513 * @v port_wwn Node name
1514 * @ret peer Fibre Channel peer, or NULL
1516 struct fc_peer
* fc_peer_get_wwn ( const struct fc_name
*port_wwn
) {
1517 struct fc_peer
*peer
;
1519 /* Look for an existing peer */
1520 list_for_each_entry ( peer
, &fc_peers
, list
) {
1521 if ( memcmp ( &peer
->port_wwn
, port_wwn
,
1522 sizeof ( peer
->port_wwn
) ) == 0 )
1523 return fc_peer_get ( peer
);
1526 /* Create a new peer */
1527 peer
= fc_peer_create ( port_wwn
);
1535 * Get Fibre Channel peer by port ID
1537 * @v port Fibre Channel port
1538 * @v peer_port_id Peer port ID
1539 * @ret peer Fibre Channel peer, or NULL
1541 struct fc_peer
* fc_peer_get_port_id ( struct fc_port
*port
,
1542 const struct fc_port_id
*peer_port_id
){
1543 struct fc_peer
*peer
;
1545 /* Look for an existing peer */
1546 list_for_each_entry ( peer
, &fc_peers
, list
) {
1547 if ( ( peer
->port
== port
) &&
1548 ( memcmp ( &peer
->port_id
, peer_port_id
,
1549 sizeof ( peer
->port_id
) ) == 0 ) )
1550 return fc_peer_get ( peer
);
1553 /* Cannot create a new peer, since we have no port name to use */
1557 /******************************************************************************
1559 * Fibre Channel upper-layer protocols
1561 ******************************************************************************
1565 * Free Fibre Channel upper-layer protocol
1567 * @v refcnt Reference count
1569 static void fc_ulp_free ( struct refcnt
*refcnt
) {
1570 struct fc_ulp
*ulp
= container_of ( refcnt
, struct fc_ulp
, refcnt
);
1572 fc_peer_put ( ulp
->peer
);
1577 * Close Fibre Channel upper-layer protocol
1579 * @v ulp Fibre Channel upper-layer protocol
1580 * @v rc Reason for close
1582 static void fc_ulp_close ( struct fc_ulp
*ulp
, int rc
) {
1584 DBGC ( ulp
, "FCULP %s/%02x closed: %s\n",
1585 fc_ntoa ( &ulp
->peer
->port_wwn
), ulp
->type
, strerror ( rc
) );
1588 assert ( list_empty ( &ulp
->users
) );
1590 /* Stop link monitor */
1591 fc_link_stop ( &ulp
->link
);
1593 /* Shut down interfaces */
1594 intf_shutdown ( &ulp
->prli
, rc
);
1596 /* Remove from list of ULPs */
1597 list_del ( &ulp
->list
);
1598 INIT_LIST_HEAD ( &ulp
->list
);
1602 * Attach Fibre Channel upper-layer protocol user
1604 * @v ulp Fibre Channel upper-layer protocol
1605 * @v user Fibre Channel upper-layer protocol user
1607 void fc_ulp_attach ( struct fc_ulp
*ulp
, struct fc_ulp_user
*user
) {
1610 assert ( user
->ulp
== NULL
);
1612 /* Increment peer's usage count */
1613 fc_peer_increment ( ulp
->peer
);
1616 user
->ulp
= fc_ulp_get ( ulp
);
1617 list_add ( &user
->list
, &ulp
->users
);
1621 * Detach Fibre Channel upper-layer protocol user
1623 * @v user Fibre Channel upper-layer protocol user
1625 void fc_ulp_detach ( struct fc_ulp_user
*user
) {
1626 struct fc_ulp
*ulp
= user
->ulp
;
1628 /* Do nothing if not attached */
1633 list_check_contains_entry ( user
, &ulp
->users
, list
);
1635 /* Detach user and log out if no users remain */
1636 list_del ( &user
->list
);
1637 if ( list_empty ( &ulp
->users
) )
1638 fc_ulp_logout ( ulp
, 0 );
1640 /* Decrement our peer's usage count */
1641 fc_peer_decrement ( ulp
->peer
);
1643 /* Drop reference */
1649 * Log in Fibre Channel upper-layer protocol
1651 * @v ulp Fibre Channel upper-layer protocol
1652 * @v param Service parameters
1653 * @v param_len Length of service parameters
1654 * @v originated Login was originated by us
1655 * @ret rc Return status code
1657 int fc_ulp_login ( struct fc_ulp
*ulp
, const void *param
, size_t param_len
,
1659 struct fc_ulp_user
*user
;
1660 struct fc_ulp_user
*tmp
;
1662 /* Perform implicit logout if logged in and service parameters differ */
1663 if ( fc_link_ok ( &ulp
->link
) &&
1664 ( ( ulp
->param_len
!= param_len
) ||
1665 ( memcmp ( ulp
->param
, param
, ulp
->param_len
) != 0 ) ) ) {
1666 fc_ulp_logout ( ulp
, 0 );
1669 /* Work around a bug in some versions of the Linux Fibre
1670 * Channel stack, which fail to fully initialise image pairs
1671 * established via a PRLI originated by the Linux stack
1675 ulp
->flags
|= FC_ULP_ORIGINATED_LOGIN_OK
;
1676 if ( ! ( ulp
->flags
& FC_ULP_ORIGINATED_LOGIN_OK
) ) {
1677 DBGC ( ulp
, "FCULP %s/%02x sending extra PRLI to work around "
1679 fc_ntoa ( &ulp
->peer
->port_wwn
), ulp
->type
);
1680 fc_link_stop ( &ulp
->link
);
1681 fc_link_start ( &ulp
->link
);
1685 /* Log in, if applicable */
1686 if ( ! fc_link_ok ( &ulp
->link
) ) {
1688 /* Record service parameters */
1689 assert ( ulp
->param
== NULL
);
1690 assert ( ulp
->param_len
== 0 );
1691 ulp
->param
= malloc ( param_len
);
1692 if ( ! ulp
->param
) {
1693 DBGC ( ulp
, "FCULP %s/%02x could not record "
1695 fc_ntoa ( &ulp
->peer
->port_wwn
), ulp
->type
);
1698 memcpy ( ulp
->param
, param
, param_len
);
1699 ulp
->param_len
= param_len
;
1700 DBGC ( ulp
, "FCULP %s/%02x logged in with parameters:\n",
1701 fc_ntoa ( &ulp
->peer
->port_wwn
), ulp
->type
);
1702 DBGC_HDA ( ulp
, 0, ulp
->param
, ulp
->param_len
);
1704 /* Add login reference */
1709 fc_link_up ( &ulp
->link
);
1711 /* Notify users of link state change */
1712 list_for_each_entry_safe ( user
, tmp
, &ulp
->users
, list
) {
1713 fc_ulp_user_get ( user
);
1714 user
->examine ( user
);
1715 fc_ulp_user_put ( user
);
1722 * Log out Fibre Channel upper-layer protocol
1724 * @v ulp Fibre Channel upper-layer protocol
1725 * @v rc Reason for logout
1727 void fc_ulp_logout ( struct fc_ulp
*ulp
, int rc
) {
1728 struct fc_ulp_user
*user
;
1729 struct fc_ulp_user
*tmp
;
1731 DBGC ( ulp
, "FCULP %s/%02x logged out: %s\n",
1732 fc_ntoa ( &ulp
->peer
->port_wwn
), ulp
->type
, strerror ( rc
) );
1734 /* Drop login reference, if applicable */
1735 if ( fc_link_ok ( &ulp
->link
) )
1738 /* Discard service parameters */
1739 free ( ulp
->param
);
1745 fc_link_err ( &ulp
->link
, rc
);
1747 /* Notify users of link state change */
1748 list_for_each_entry_safe ( user
, tmp
, &ulp
->users
, list
) {
1749 fc_ulp_user_get ( user
);
1750 user
->examine ( user
);
1751 fc_ulp_user_put ( user
);
1754 /* Close ULP if there are no clients attached */
1755 if ( list_empty ( &ulp
->users
) )
1756 fc_ulp_close ( ulp
, rc
);
1760 * Handle PRLI completion
1762 * @v ulp Fibre Channel upper-layer protocol
1763 * @v rc Reason for completion
1765 static void fc_ulp_prli_done ( struct fc_ulp
*ulp
, int rc
) {
1767 intf_restart ( &ulp
->prli
, rc
);
1770 fc_ulp_logout ( ulp
, rc
);
1774 * Examine Fibre Channel upper-layer protocol link state
1776 * @ link Fibre Channel link state monitor
1778 static void fc_ulp_examine ( struct fc_link_state
*link
) {
1779 struct fc_ulp
*ulp
= container_of ( link
, struct fc_ulp
, link
);
1782 /* Check to see if underlying peer link has gone down */
1783 if ( ! fc_link_ok ( &ulp
->peer
->link
) ) {
1784 fc_ulp_logout ( ulp
, -ENOTCONN
);
1788 /* Do nothing if already logged in */
1789 if ( fc_link_ok ( &ulp
->link
) &&
1790 ( ulp
->flags
& FC_ULP_ORIGINATED_LOGIN_OK
) )
1793 DBGC ( ulp
, "FCULP %s/%02x attempting login\n",
1794 fc_ntoa ( &ulp
->peer
->port_wwn
), ulp
->type
);
1796 /* Try to create PRLI ELS */
1797 intf_restart ( &ulp
->prli
, -ECANCELED
);
1798 if ( ( rc
= fc_els_prli ( &ulp
->prli
, ulp
->peer
->port
,
1799 &ulp
->peer
->port_id
, ulp
->type
) ) != 0 ) {
1800 DBGC ( ulp
, "FCULP %s/%02x could not initiate PRLI: %s\n",
1801 fc_ntoa ( &ulp
->peer
->port_wwn
), ulp
->type
,
1803 fc_ulp_logout ( ulp
, rc
);
1808 /** Fibre Channel upper-layer protocol PRLI interface operations */
1809 static struct interface_operation fc_ulp_prli_op
[] = {
1810 INTF_OP ( intf_close
, struct fc_ulp
*, fc_ulp_prli_done
),
1813 /** Fibre Channel upper-layer protocol PRLI interface descriptor */
1814 static struct interface_descriptor fc_ulp_prli_desc
=
1815 INTF_DESC ( struct fc_ulp
, prli
, fc_ulp_prli_op
);
1818 * Create Fibre Channel upper-layer protocl
1820 * @v peer Fibre Channel peer
1822 * @ret ulp Fibre Channel upper-layer protocol, or NULL
1824 static struct fc_ulp
* fc_ulp_create ( struct fc_peer
*peer
,
1825 unsigned int type
) {
1828 /* Allocate and initialise structure */
1829 ulp
= zalloc ( sizeof ( *ulp
) );
1832 ref_init ( &ulp
->refcnt
, fc_ulp_free
);
1833 fc_link_init ( &ulp
->link
, fc_ulp_examine
, &ulp
->refcnt
);
1834 intf_init ( &ulp
->prli
, &fc_ulp_prli_desc
, &ulp
->refcnt
);
1835 ulp
->peer
= fc_peer_get ( peer
);
1836 list_add_tail ( &ulp
->list
, &peer
->ulps
);
1838 INIT_LIST_HEAD ( &ulp
->users
);
1840 /* Start link state monitor */
1841 fc_link_start ( &ulp
->link
);
1843 DBGC ( ulp
, "FCULP %s/%02x created\n",
1844 fc_ntoa ( &ulp
->peer
->port_wwn
), ulp
->type
);
1849 * Get Fibre Channel upper-layer protocol by peer and type
1851 * @v peer Fibre Channel peer
1853 * @ret ulp Fibre Channel upper-layer protocol, or NULL
1855 static struct fc_ulp
* fc_ulp_get_type ( struct fc_peer
*peer
,
1856 unsigned int type
) {
1859 /* Look for an existing ULP */
1860 list_for_each_entry ( ulp
, &peer
->ulps
, list
) {
1861 if ( ulp
->type
== type
)
1862 return fc_ulp_get ( ulp
);
1865 /* Create a new ULP */
1866 ulp
= fc_ulp_create ( peer
, type
);
1874 * Get Fibre Channel upper-layer protocol by port name and type
1876 * @v port_wwn Port name
1878 * @ret ulp Fibre Channel upper-layer protocol, or NULL
1880 struct fc_ulp
* fc_ulp_get_wwn_type ( const struct fc_name
*port_wwn
,
1881 unsigned int type
) {
1883 struct fc_peer
*peer
;
1886 peer
= fc_peer_get_wwn ( port_wwn
);
1888 goto err_peer_get_wwn
;
1891 ulp
= fc_ulp_get_type ( peer
, type
);
1893 goto err_ulp_get_type
;
1895 /* Drop temporary reference to peer */
1896 fc_peer_put ( peer
);
1902 fc_peer_put ( peer
);
1908 * Get Fibre Channel upper-layer protocol by port ID and type
1910 * @v port Fibre Channel port
1911 * @v peer_port_id Peer port ID
1913 * @ret ulp Fibre Channel upper-layer protocol, or NULL
1915 struct fc_ulp
* fc_ulp_get_port_id_type ( struct fc_port
*port
,
1916 const struct fc_port_id
*peer_port_id
,
1917 unsigned int type
) {
1919 struct fc_peer
*peer
;
1922 peer
= fc_peer_get_port_id ( port
, peer_port_id
);
1924 goto err_peer_get_wwn
;
1927 ulp
= fc_ulp_get_type ( peer
, type
);
1929 goto err_ulp_get_type
;
1931 /* Drop temporary reference to peer */
1932 fc_peer_put ( peer
);
1938 fc_peer_put ( peer
);
1943 /* Drag in objects via fc_ports */
1944 REQUIRING_SYMBOL ( fc_ports
);
1946 /* Drag in Fibre Channel configuration */
1947 REQUIRE_OBJECT ( config_fc
);