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
20 FILE_LICENCE ( GPL2_OR_LATER );
29 #include <ipxe/refcnt.h>
30 #include <ipxe/list.h>
31 #include <ipxe/tables.h>
32 #include <ipxe/timer.h>
33 #include <ipxe/retry.h>
34 #include <ipxe/interface.h>
35 #include <ipxe/xfer.h>
36 #include <ipxe/iobuf.h>
38 #include <ipxe/fcels.h>
39 #include <ipxe/fcns.h>
47 /** List of Fibre Channel ports */
48 LIST_HEAD ( fc_ports );
50 /** List of Fibre Channel peers */
51 LIST_HEAD ( fc_peers );
53 /******************************************************************************
55 * Well-known addresses
57 ******************************************************************************
60 /** Unassigned port ID */
61 struct fc_port_id fc_empty_port_id = { .bytes = { 0x00, 0x00, 0x00 } };
63 /** F_Port contoller port ID */
64 struct fc_port_id fc_f_port_id = { .bytes = { 0xff, 0xff, 0xfe } };
66 /** Generic services port ID */
67 struct fc_port_id fc_gs_port_id = { .bytes = { 0xff, 0xff, 0xfc } };
69 /** Point-to-point low port ID */
70 struct fc_port_id fc_ptp_low_port_id = { .bytes = { 0x01, 0x01, 0x01 } };
72 /** Point-to-point high port ID */
73 struct fc_port_id fc_ptp_high_port_id = { .bytes = { 0x01, 0x01, 0x02 } };
75 /******************************************************************************
79 ******************************************************************************
83 * Format Fibre Channel port ID
85 * @v id Fibre Channel port ID
86 * @ret id_text Port ID text
88 const char * fc_id_ntoa ( const struct fc_port_id *id ) {
89 static char id_text[ FC_PORT_ID_STRLEN + 1 /* NUL */ ];
91 snprintf ( id_text, sizeof ( id_text ), "%02x.%02x.%02x",
92 id->bytes[0], id->bytes[1], id->bytes[2] );
97 * Parse Fibre Channel port ID
99 * @v id_text Port ID text
100 * @ret id Fibre Channel port ID
101 * @ret rc Return status code
103 int fc_id_aton ( const char *id_text, struct fc_port_id *id ) {
104 char *ptr = ( ( char * ) id_text );
108 id->bytes[i++] = strtoul ( ptr, &ptr, 16 );
109 if ( i == sizeof ( id->bytes ) )
110 return ( ( *ptr == '\0' ) ? 0 : -EINVAL );
118 * Format Fibre Channel WWN
120 * @v wwn Fibre Channel WWN
121 * @ret wwn_text WWN text
123 const char * fc_ntoa ( const struct fc_name *wwn ) {
124 static char wwn_text[ FC_NAME_STRLEN + 1 /* NUL */ ];
126 snprintf ( wwn_text, sizeof ( wwn_text ),
127 "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
128 wwn->bytes[0], wwn->bytes[1], wwn->bytes[2], wwn->bytes[3],
129 wwn->bytes[4], wwn->bytes[5], wwn->bytes[6], wwn->bytes[7] );
134 * Parse Fibre Channel WWN
136 * @v wwn_text WWN text
137 * @ret wwn Fibre Channel WWN
138 * @ret rc Return status code
140 int fc_aton ( const char *wwn_text, struct fc_name *wwn ) {
141 char *ptr = ( ( char * ) wwn_text );
145 wwn->bytes[i++] = strtoul ( ptr, &ptr, 16 );
146 if ( i == sizeof ( wwn->bytes ) )
147 return ( ( *ptr == '\0' ) ? 0 : -EINVAL );
155 * Fill Fibre Channel socket address
157 * @v sa_fc Fibre Channel socket address to fill in
158 * @v id Fibre Channel port ID
159 * @ret sa Socket address
161 struct sockaddr * fc_fill_sockaddr ( struct sockaddr_fc *sa_fc,
162 struct fc_port_id *id ) {
165 struct sockaddr_fc fc;
166 } *u = container_of ( sa_fc, typeof ( *u ), fc );
168 memset ( sa_fc, 0, sizeof ( *sa_fc ) );
169 sa_fc->sfc_family = AF_FC;
170 memcpy ( &sa_fc->sfc_port_id, id, sizeof ( sa_fc->sfc_port_id ) );
174 /******************************************************************************
176 * Fibre Channel link state
178 ******************************************************************************
181 /** Default link status code */
182 #define EUNKNOWN_LINK_STATUS __einfo_error ( EINFO_EUNKNOWN_LINK_STATUS )
183 #define EINFO_EUNKNOWN_LINK_STATUS \
184 __einfo_uniqify ( EINFO_EINPROGRESS, 0x01, "Unknown" )
187 * Mark Fibre Channel link as up
189 * @v link Fibre Channel link state monitor
191 static void fc_link_up ( struct fc_link_state *link ) {
193 /* Stop retry timer */
194 stop_timer ( &link->timer );
196 /* Record link state */
201 * Mark Fibre Channel link as down
203 * @v link Fibre Channel link state monitor
206 static void fc_link_err ( struct fc_link_state *link, int rc ) {
208 /* Record link state */
210 rc = -EUNKNOWN_LINK_STATUS;
213 /* Schedule another link examination */
214 start_timer_fixed ( &link->timer, FC_LINK_RETRY_DELAY );
218 * Examine Fibre Channel link state
220 * @v link Fibre Channel link state monitor
222 static void fc_link_examine ( struct fc_link_state *link ) {
224 link->examine ( link );
228 * Handle Fibre Channel link retry timer expiry
230 static void fc_link_expired ( struct retry_timer *timer, int over __unused ) {
231 struct fc_link_state *link =
232 container_of ( timer, struct fc_link_state, timer );
234 /* Schedule another link examination */
235 start_timer_fixed ( &link->timer, FC_LINK_RETRY_DELAY );
238 fc_link_examine ( link );
242 * Initialise Fibre Channel link state monitor
244 * @v link Fibre Channel link state monitor
245 * @v examine Examine link state method
246 * @v refcnt Reference counter
248 static void fc_link_init ( struct fc_link_state *link,
249 void ( * examine ) ( struct fc_link_state *link ),
250 struct refcnt *refcnt ) {
252 link->rc = -EUNKNOWN_LINK_STATUS;
253 timer_init ( &link->timer, fc_link_expired, refcnt );
254 link->examine = examine;
258 * Start monitoring Fibre Channel link state
260 * @v link Fibre Channel link state monitor
262 static void fc_link_start ( struct fc_link_state *link ) {
263 start_timer_nodelay ( &link->timer );
267 * Stop monitoring Fibre Channel link state
269 * @v link Fibre Channel link state monitor
271 static void fc_link_stop ( struct fc_link_state *link ) {
272 stop_timer ( &link->timer );
275 /******************************************************************************
277 * Fibre Channel exchanges
279 ******************************************************************************
282 /** A Fibre Channel exchange */
284 /** Reference count */
285 struct refcnt refcnt;
286 /** Fibre Channel port */
287 struct fc_port *port;
288 /** List of active exchanges within this port */
289 struct list_head list;
292 struct fc_port_id peer_port_id;
293 /** Data structure type */
297 /** Local exchange ID */
299 /** Peer exchange ID */
300 uint16_t peer_xchg_id;
301 /** Active sequence ID */
303 /** Active sequence count */
307 struct retry_timer timer;
309 /** Upper-layer protocol interface */
310 struct interface ulp;
313 /** Fibre Channel exchange flags */
314 enum fc_exchange_flags {
315 /** We are the exchange originator */
316 FC_XCHG_ORIGINATOR = 0x0001,
317 /** We have the sequence initiative */
318 FC_XCHG_SEQ_INITIATIVE = 0x0002,
319 /** This is the first sequence of the exchange */
320 FC_XCHG_SEQ_FIRST = 0x0004,
323 /** Fibre Channel timeout */
324 #define FC_TIMEOUT ( 1 * TICKS_PER_SEC )
327 * Create local Fibre Channel exchange identifier
329 * @ret xchg_id Local exchange ID
331 static unsigned int fc_new_xchg_id ( void ) {
332 static uint16_t next_id = 0x0000;
334 /* We must avoid using FC_RX_ID_UNKNOWN (0xffff) */
340 * Create local Fibre Channel sequence identifier
342 * @ret seq_id Local sequence identifier
344 static unsigned int fc_new_seq_id ( void ) {
345 static uint8_t seq_id = 0x00;
351 * Free Fibre Channel exchange
353 * @v refcnt Reference count
355 static void fc_xchg_free ( struct refcnt *refcnt ) {
356 struct fc_exchange *xchg =
357 container_of ( refcnt, struct fc_exchange, refcnt );
359 assert ( ! timer_running ( &xchg->timer ) );
360 assert ( list_empty ( &xchg->list ) );
362 fc_port_put ( xchg->port );
367 * Close Fibre Channel exchange
369 * @v xchg Fibre Channel exchange
370 * @v rc Reason for close
372 static void fc_xchg_close ( struct fc_exchange *xchg, int rc ) {
373 struct fc_port *port = xchg->port;
376 DBGC2 ( port, "FCXCHG %s/%04x closed: %s\n",
377 port->name, xchg->xchg_id, strerror ( rc ) );
381 stop_timer ( &xchg->timer );
383 /* If list still holds a reference, remove from list of open
384 * exchanges and drop list's reference.
386 if ( ! list_empty ( &xchg->list ) ) {
387 list_del ( &xchg->list );
388 INIT_LIST_HEAD ( &xchg->list );
389 ref_put ( &xchg->refcnt );
392 /* Shutdown interfaces */
393 intf_shutdown ( &xchg->ulp, rc );
397 * Handle exchange timeout
399 * @v timer Timeout timer
400 * @v over Failure indicator
402 static void fc_xchg_expired ( struct retry_timer *timer, int over __unused ) {
403 struct fc_exchange *xchg =
404 container_of ( timer, struct fc_exchange, timer );
405 struct fc_port *port = xchg->port;
407 DBGC ( port, "FCXCHG %s/%04x timed out\n", port->name, xchg->xchg_id );
409 /* Terminate the exchange */
410 fc_xchg_close ( xchg, -ETIMEDOUT );
414 * Check Fibre Channel exchange window
416 * @v xchg Fibre Channel exchange
417 * @ret len Length opf window
419 static size_t fc_xchg_window ( struct fc_exchange *xchg __unused ) {
421 /* We don't currently store the path MTU */
422 return FC_LOGIN_DEFAULT_MTU;
426 * Allocate Fibre Channel I/O buffer
428 * @v xchg Fibre Channel exchange
429 * @v len Payload length
430 * @ret iobuf I/O buffer, or NULL
432 static struct io_buffer * fc_xchg_alloc_iob ( struct fc_exchange *xchg,
434 struct fc_port *port = xchg->port;
435 struct io_buffer *iobuf;
437 iobuf = xfer_alloc_iob ( &port->transport,
438 ( sizeof ( struct fc_frame_header ) + len ) );
440 iob_reserve ( iobuf, sizeof ( struct fc_frame_header ) );
446 * Transmit data as part of a Fibre Channel exchange
448 * @v xchg Fibre Channel exchange
449 * @v iobuf I/O buffer
450 * @v meta Data transfer metadata
451 * @ret rc Return status code
453 static int fc_xchg_tx ( struct fc_exchange *xchg, struct io_buffer *iobuf,
454 struct xfer_metadata *meta ) {
455 struct fc_port *port = xchg->port;
456 struct sockaddr_fc *dest = ( ( struct sockaddr_fc * ) meta->dest );
457 struct fc_frame_header *fchdr;
459 unsigned int f_ctl_es;
463 if ( ! ( xchg->flags & FC_XCHG_SEQ_INITIATIVE ) ) {
464 DBGC ( port, "FCXCHG %s/%04x cannot transmit while not "
465 "holding sequence initiative\n",
466 port->name, xchg->xchg_id );
471 /* Calculate routing control */
472 switch ( xchg->type ) {
474 r_ctl = FC_R_CTL_ELS;
475 if ( meta->flags & XFER_FL_RESPONSE ) {
476 r_ctl |= FC_R_CTL_SOL_CTRL;
478 r_ctl |= FC_R_CTL_UNSOL_CTRL;
482 r_ctl = FC_R_CTL_DATA;
483 if ( meta->flags & XFER_FL_RESPONSE ) {
484 r_ctl |= FC_R_CTL_SOL_CTRL;
486 r_ctl |= FC_R_CTL_UNSOL_CTRL;
490 r_ctl = FC_R_CTL_DATA;
491 switch ( meta->flags &
492 ( XFER_FL_CMD_STAT | XFER_FL_RESPONSE ) ) {
493 case ( XFER_FL_CMD_STAT | XFER_FL_RESPONSE ):
494 r_ctl |= FC_R_CTL_CMD_STAT;
496 case ( XFER_FL_CMD_STAT ):
497 r_ctl |= FC_R_CTL_UNSOL_CMD;
499 case ( XFER_FL_RESPONSE ):
500 r_ctl |= FC_R_CTL_SOL_DATA;
503 r_ctl |= FC_R_CTL_UNSOL_DATA;
509 /* Calculate exchange and sequence control */
511 if ( ! ( xchg->flags & FC_XCHG_ORIGINATOR ) )
512 f_ctl_es |= FC_F_CTL_ES_RESPONDER;
513 if ( xchg->flags & FC_XCHG_SEQ_FIRST )
514 f_ctl_es |= FC_F_CTL_ES_FIRST;
515 if ( meta->flags & XFER_FL_OUT )
516 f_ctl_es |= ( FC_F_CTL_ES_END | FC_F_CTL_ES_LAST );
517 if ( meta->flags & XFER_FL_OVER )
518 f_ctl_es |= ( FC_F_CTL_ES_END | FC_F_CTL_ES_TRANSFER );
520 /* Create frame header */
521 fchdr = iob_push ( iobuf, sizeof ( *fchdr ) );
522 memset ( fchdr, 0, sizeof ( *fchdr ) );
523 fchdr->r_ctl = r_ctl;
524 memcpy ( &fchdr->d_id,
525 ( dest ? &dest->sfc_port_id : &xchg->peer_port_id ),
526 sizeof ( fchdr->d_id ) );
527 memcpy ( &fchdr->s_id, &port->port_id, sizeof ( fchdr->s_id ) );
528 fchdr->type = xchg->type;
529 fchdr->f_ctl_es = f_ctl_es;
530 fchdr->seq_id = xchg->seq_id;
531 fchdr->seq_cnt = htons ( xchg->seq_cnt++ );
532 fchdr->ox_id = htons ( ( xchg->flags & FC_XCHG_ORIGINATOR ) ?
533 xchg->xchg_id : xchg->peer_xchg_id );
534 fchdr->rx_id = htons ( ( xchg->flags & FC_XCHG_ORIGINATOR ) ?
535 xchg->peer_xchg_id : xchg->xchg_id );
536 if ( meta->flags & XFER_FL_ABS_OFFSET ) {
537 fchdr->f_ctl_misc |= FC_F_CTL_MISC_REL_OFF;
538 fchdr->parameter = htonl ( meta->offset );
541 /* Relinquish sequence initiative if applicable */
542 if ( meta->flags & XFER_FL_OVER ) {
543 xchg->flags &= ~( FC_XCHG_SEQ_INITIATIVE | FC_XCHG_SEQ_FIRST );
548 start_timer_fixed ( &xchg->timer, FC_TIMEOUT );
551 if ( ( rc = xfer_deliver_iob ( &port->transport,
552 iob_disown ( iobuf ) ) ) != 0 ) {
553 DBGC ( port, "FCXCHG %s/%04x cannot transmit: %s\n",
554 port->name, xchg->xchg_id, strerror ( rc ) );
563 /** Mapping from Fibre Channel routing control information to xfer metadata */
564 static const uint8_t fc_r_ctl_info_meta_flags[ FC_R_CTL_INFO_MASK + 1 ] = {
565 [FC_R_CTL_UNCAT] = ( 0 ),
566 [FC_R_CTL_SOL_DATA] = ( XFER_FL_RESPONSE ),
567 [FC_R_CTL_UNSOL_CTRL] = ( XFER_FL_CMD_STAT ),
568 [FC_R_CTL_SOL_CTRL] = ( XFER_FL_CMD_STAT ),
569 [FC_R_CTL_UNSOL_DATA] = ( 0 ),
570 [FC_R_CTL_DATA_DESC] = ( XFER_FL_CMD_STAT ),
571 [FC_R_CTL_UNSOL_CMD] = ( XFER_FL_CMD_STAT ),
572 [FC_R_CTL_CMD_STAT] = ( XFER_FL_CMD_STAT | XFER_FL_RESPONSE ),
576 * Receive data as part of a Fibre Channel exchange
578 * @v xchg Fibre Channel exchange
579 * @v iobuf I/O buffer
580 * @v meta Data transfer metadata
581 * @ret rc Return status code
583 static int fc_xchg_rx ( struct fc_exchange *xchg, struct io_buffer *iobuf,
584 struct xfer_metadata *meta __unused ) {
585 struct fc_port *port = xchg->port;
586 struct fc_frame_header *fchdr = iobuf->data;
587 struct xfer_metadata fc_meta;
588 struct sockaddr_fc src;
589 struct sockaddr_fc dest;
592 /* Record peer exchange ID */
594 ntohs ( ( fchdr->f_ctl_es & FC_F_CTL_ES_RESPONDER ) ?
595 fchdr->rx_id : fchdr->ox_id );
597 /* Sequence checks */
598 if ( xchg->flags & FC_XCHG_SEQ_INITIATIVE ) {
599 DBGC ( port, "FCXCHG %s/%04x received frame while holding "
600 "sequence initiative\n", port->name, xchg->xchg_id );
604 if ( ntohs ( fchdr->seq_cnt ) != xchg->seq_cnt ) {
605 DBGC ( port, "FCXCHG %s/%04x received out-of-order frame %d "
606 "(expected %d)\n", port->name, xchg->xchg_id,
607 ntohs ( fchdr->seq_cnt ), xchg->seq_cnt );
611 if ( xchg->seq_cnt == 0 )
612 xchg->seq_id = fchdr->seq_id;
614 if ( fchdr->seq_id != xchg->seq_id ) {
615 DBGC ( port, "FCXCHG %s/%04x received frame for incorrect "
616 "sequence %02x (expected %02x)\n", port->name,
617 xchg->xchg_id, fchdr->seq_id, xchg->seq_id );
622 /* Check for end of sequence and transfer of sequence initiative */
623 if ( fchdr->f_ctl_es & FC_F_CTL_ES_END ) {
625 if ( fchdr->f_ctl_es & FC_F_CTL_ES_TRANSFER ) {
626 xchg->flags |= FC_XCHG_SEQ_INITIATIVE;
627 xchg->seq_id = fc_new_seq_id();
631 /* Construct metadata */
632 memset ( &fc_meta, 0, sizeof ( fc_meta ) );
634 fc_r_ctl_info_meta_flags[ fchdr->r_ctl & FC_R_CTL_INFO_MASK ];
635 if ( fchdr->f_ctl_es & FC_F_CTL_ES_TRANSFER ) {
636 fc_meta.flags |= XFER_FL_OVER;
638 if ( ( fchdr->f_ctl_es & FC_F_CTL_ES_LAST ) &&
639 ( fchdr->f_ctl_es & FC_F_CTL_ES_END ) ) {
640 fc_meta.flags |= XFER_FL_OUT;
642 if ( fchdr->f_ctl_misc & FC_F_CTL_MISC_REL_OFF ) {
643 fc_meta.flags |= XFER_FL_ABS_OFFSET;
644 fc_meta.offset = ntohl ( fchdr->parameter );
646 fc_meta.src = fc_fill_sockaddr ( &src, &fchdr->s_id );
647 fc_meta.dest = fc_fill_sockaddr ( &dest, &fchdr->d_id );
650 start_timer_fixed ( &xchg->timer, FC_TIMEOUT );
652 /* Deliver via exchange's ULP interface */
653 iob_pull ( iobuf, sizeof ( *fchdr ) );
654 if ( ( rc = xfer_deliver ( &xchg->ulp, iob_disown ( iobuf ),
655 &fc_meta ) ) != 0 ) {
656 DBGC ( port, "FCXCHG %s/%04x cannot deliver frame: %s\n",
657 port->name, xchg->xchg_id, strerror ( rc ) );
661 /* Close exchange if applicable */
662 if ( ( fchdr->f_ctl_es & FC_F_CTL_ES_LAST ) &&
663 ( fchdr->f_ctl_es & FC_F_CTL_ES_END ) ) {
664 fc_xchg_close ( xchg, 0 );
672 /** Fibre Channel exchange ULP interface operations */
673 static struct interface_operation fc_xchg_ulp_op[] = {
674 INTF_OP ( xfer_deliver, struct fc_exchange *, fc_xchg_tx ),
675 INTF_OP ( xfer_alloc_iob, struct fc_exchange *, fc_xchg_alloc_iob ),
676 INTF_OP ( xfer_window, struct fc_exchange *, fc_xchg_window ),
677 INTF_OP ( intf_close, struct fc_exchange *, fc_xchg_close ),
680 /** Fibre Channel exchange ULP interface descriptor */
681 static struct interface_descriptor fc_xchg_ulp_desc =
682 INTF_DESC ( struct fc_exchange, ulp, fc_xchg_ulp_op );
685 * Create new Fibre Channel exchange
687 * @v port Fibre Channel port
688 * @v peer_port_id Peer port ID
689 * @ret xchg Exchange, or NULL
691 static struct fc_exchange * fc_xchg_create ( struct fc_port *port,
692 struct fc_port_id *peer_port_id,
693 unsigned int type ) {
694 struct fc_exchange *xchg;
696 /* Allocate and initialise structure */
697 xchg = zalloc ( sizeof ( *xchg ) );
700 ref_init ( &xchg->refcnt, fc_xchg_free );
701 intf_init ( &xchg->ulp, &fc_xchg_ulp_desc, &xchg->refcnt );
702 timer_init ( &xchg->timer, fc_xchg_expired, &xchg->refcnt );
703 xchg->port = fc_port_get ( port );
704 memcpy ( &xchg->peer_port_id, peer_port_id,
705 sizeof ( xchg->peer_port_id ) );
707 xchg->xchg_id = fc_new_xchg_id();
708 xchg->peer_xchg_id = FC_RX_ID_UNKNOWN;
709 xchg->seq_id = fc_new_seq_id();
711 /* Transfer reference to list of exchanges and return */
712 list_add ( &xchg->list, &port->xchgs );
717 * Originate a new Fibre Channel exchange
719 * @v parent Interface to which to attach
720 * @v port Fibre Channel port
721 * @v peer_port_id Peer port ID
722 * @ret xchg_id Exchange ID, or negative error
724 int fc_xchg_originate ( struct interface *parent, struct fc_port *port,
725 struct fc_port_id *peer_port_id, unsigned int type ) {
726 struct fc_exchange *xchg;
728 /* Allocate and initialise structure */
729 xchg = fc_xchg_create ( port, peer_port_id, type );
732 xchg->flags = ( FC_XCHG_ORIGINATOR | FC_XCHG_SEQ_INITIATIVE |
735 DBGC2 ( port, "FCXCHG %s/%04x originating to %s (type %02x)\n",
736 port->name, xchg->xchg_id, fc_id_ntoa ( &xchg->peer_port_id ),
739 /* Attach to parent interface and return */
740 intf_plug_plug ( &xchg->ulp, parent );
741 return xchg->xchg_id;
745 * Open a new responder Fibre Channel exchange
747 * @v port Fibre Channel port
748 * @v fchdr Fibre Channel frame header
749 * @ret xchg Fibre Channel exchange, or NULL
751 static struct fc_exchange * fc_xchg_respond ( struct fc_port *port,
752 struct fc_frame_header *fchdr ) {
753 struct fc_exchange *xchg;
754 struct fc_responder *responder;
755 unsigned int type = fchdr->type;
758 /* Allocate and initialise structure */
759 xchg = fc_xchg_create ( port, &fchdr->s_id, type );
762 xchg->seq_id = fchdr->seq_id;
764 DBGC2 ( port, "FCXCHG %s/%04x responding to %s xchg %04x (type "
765 "%02x)\n", port->name, xchg->xchg_id,
766 fc_id_ntoa ( &xchg->peer_port_id ),
767 ntohs ( fchdr->ox_id ), xchg->type );
769 /* Find a responder, if any */
770 for_each_table_entry ( responder, FC_RESPONDERS ) {
771 if ( responder->type == type ) {
772 if ( ( rc = responder->respond ( &xchg->ulp, port,
774 &fchdr->s_id ) ) !=0 ){
775 DBGC ( port, "FCXCHG %s/%04x could not "
776 "respond: %s\n", port->name,
777 xchg->xchg_id, strerror ( rc ) );
783 /* We may or may not have a ULP attached at this point, but
784 * the exchange does exist.
789 /******************************************************************************
791 * Fibre Channel ports
793 ******************************************************************************
797 * Close Fibre Channel port
799 * @v port Fibre Channel port
800 * @v rc Reason for close
802 static void fc_port_close ( struct fc_port *port, int rc ) {
803 struct fc_exchange *xchg;
804 struct fc_exchange *tmp;
806 DBGC ( port, "FCPORT %s closed\n", port->name );
808 /* Log out port, if necessary */
809 if ( fc_link_ok ( &port->link ) )
810 fc_port_logout ( port, rc );
812 /* Stop link monitor */
813 fc_link_stop ( &port->link );
815 /* Shut down interfaces */
816 intf_shutdown ( &port->transport, rc );
817 intf_shutdown ( &port->flogi, rc );
818 intf_shutdown ( &port->ns_plogi, rc );
820 /* Shut down any remaining exchanges */
821 list_for_each_entry_safe ( xchg, tmp, &port->xchgs, list )
822 fc_xchg_close ( xchg, rc );
824 /* Remove from list of ports */
825 list_del ( &port->list );
826 INIT_LIST_HEAD ( &port->list );
830 * Identify Fibre Channel exchange by local exchange ID
832 * @v port Fibre Channel port
833 * @v xchg_id Local exchange ID
834 * @ret xchg Fibre Channel exchange, or NULL
836 static struct fc_exchange * fc_port_demux ( struct fc_port *port,
837 unsigned int xchg_id ) {
838 struct fc_exchange *xchg;
840 list_for_each_entry ( xchg, &port->xchgs, list ) {
841 if ( xchg->xchg_id == xchg_id )
848 * Handle received frame from Fibre Channel port
850 * @v port Fibre Channel port
851 * @v iobuf I/O buffer
852 * @v meta Data transfer metadata
853 * @ret rc Return status code
855 static int fc_port_deliver ( struct fc_port *port, struct io_buffer *iobuf,
856 struct xfer_metadata *meta ) {
857 struct fc_frame_header *fchdr = iobuf->data;
858 unsigned int xchg_id;
859 struct fc_exchange *xchg;
863 if ( iob_len ( iobuf ) < sizeof ( *fchdr ) ) {
864 DBGC ( port, "FCPORT %s received underlength frame (%zd "
865 "bytes)\n", port->name, iob_len ( iobuf ) );
870 /* Verify local port ID */
871 if ( ( memcmp ( &fchdr->d_id, &port->port_id,
872 sizeof ( fchdr->d_id ) ) != 0 ) &&
873 ( memcmp ( &fchdr->d_id, &fc_f_port_id,
874 sizeof ( fchdr->d_id ) ) != 0 ) &&
875 ( memcmp ( &port->port_id, &fc_empty_port_id,
876 sizeof ( port->port_id ) ) != 0 ) ) {
877 DBGC ( port, "FCPORT %s received frame for incorrect port ID "
878 "%s\n", port->name, fc_id_ntoa ( &fchdr->d_id ) );
883 /* Demultiplex amongst active exchanges */
884 xchg_id = ntohs ( ( fchdr->f_ctl_es & FC_F_CTL_ES_RESPONDER ) ?
885 fchdr->ox_id : fchdr->rx_id );
886 xchg = fc_port_demux ( port, xchg_id );
888 /* If we have no active exchange and this frame starts a new
889 * exchange, try to create a new responder exchange
891 if ( ( fchdr->f_ctl_es & FC_F_CTL_ES_FIRST ) &&
892 ( fchdr->seq_cnt == 0 ) ) {
894 /* Create new exchange */
895 xchg = fc_xchg_respond ( port, fchdr );
897 DBGC ( port, "FCPORT %s cannot create new exchange\n",
904 /* Fail if no exchange exists */
906 DBGC ( port, "FCPORT %s xchg %04x unknown\n",
907 port->name, xchg_id );
912 /* Pass received frame to exchange */
913 ref_get ( &xchg->refcnt );
914 if ( ( rc = fc_xchg_rx ( xchg, iob_disown ( iobuf ), meta ) ) != 0 )
918 ref_put ( &xchg->refcnt );
928 * Log in Fibre Channel port
930 * @v port Fibre Channel port
931 * @v port_id Local port ID
932 * @v link_node_wwn Link node name
933 * @v link_port_wwn Link port name
934 * @v has_fabric Link is to a fabric
935 * @ret rc Return status code
937 int fc_port_login ( struct fc_port *port, struct fc_port_id *port_id,
938 const struct fc_name *link_node_wwn,
939 const struct fc_name *link_port_wwn, int has_fabric ) {
940 struct fc_peer *peer;
944 /* Perform implicit logout if logged in and details differ */
945 if ( fc_link_ok ( &port->link ) &&
946 ( ( ( !! ( port->flags & FC_PORT_HAS_FABRIC ) ) !=
947 ( !! has_fabric ) ) ||
948 ( memcmp ( &port->link_node_wwn, link_node_wwn,
949 sizeof ( port->link_node_wwn ) ) != 0 ) ||
950 ( memcmp ( &port->link_port_wwn, link_port_wwn,
951 sizeof ( port->link_port_wwn ) ) != 0 ) ||
953 ( memcmp ( &port->port_id, port_id,
954 sizeof ( port->port_id ) ) != 0 ) ) ) ) {
955 fc_port_logout ( port, 0 );
958 /* Log in, if applicable */
959 if ( ! fc_link_ok ( &port->link ) ) {
961 /* Record link port name */
962 memcpy ( &port->link_node_wwn, link_node_wwn,
963 sizeof ( port->link_node_wwn ) );
964 memcpy ( &port->link_port_wwn, link_port_wwn,
965 sizeof ( port->link_port_wwn ) );
966 DBGC ( port, "FCPORT %s logged in to %s",
967 port->name, fc_ntoa ( &port->link_node_wwn ) );
968 DBGC ( port, " port %s\n", fc_ntoa ( &port->link_port_wwn ) );
970 /* Calculate local (and possibly remote) port IDs */
972 port->flags |= FC_PORT_HAS_FABRIC;
973 memcpy ( &port->port_id, port_id,
974 sizeof ( port->port_id ) );
976 port->flags &= ~FC_PORT_HAS_FABRIC;
977 if ( memcmp ( &port->port_wwn, link_port_wwn,
978 sizeof ( port->port_wwn ) ) > 0 ) {
979 memcpy ( &port->port_id, &fc_ptp_high_port_id,
980 sizeof ( port->port_id ) );
981 memcpy ( &port->ptp_link_port_id,
983 sizeof ( port->ptp_link_port_id ) );
985 memcpy ( &port->port_id, &fc_ptp_low_port_id,
986 sizeof ( port->port_id ) );
987 memcpy ( &port->ptp_link_port_id,
988 &fc_ptp_high_port_id,
989 sizeof ( port->ptp_link_port_id ) );
992 DBGC ( port, "FCPORT %s logged in via a %s, with local ID "
994 ( ( port->flags & FC_PORT_HAS_FABRIC ) ?
995 "fabric" : "point-to-point link" ),
996 fc_id_ntoa ( &port->port_id ) );
999 /* Log in to name server, if attached to a fabric */
1000 if ( has_fabric && ! ( port->flags & FC_PORT_HAS_NS ) ) {
1002 DBGC ( port, "FCPORT %s attempting login to name server\n",
1005 intf_restart ( &port->ns_plogi, -ECANCELED );
1006 if ( ( rc = fc_els_plogi ( &port->ns_plogi, port,
1007 &fc_gs_port_id ) ) != 0 ) {
1008 DBGC ( port, "FCPORT %s could not initiate name "
1009 "server PLOGI: %s\n",
1010 port->name, strerror ( rc ) );
1011 fc_port_logout ( port, rc );
1017 fc_link_up ( &port->link );
1019 /* Notify peers of link state change */
1020 list_for_each_entry_safe ( peer, tmp, &fc_peers, list ) {
1021 fc_peer_get ( peer );
1022 fc_link_examine ( &peer->link );
1023 fc_peer_put ( peer );
1030 * Log out Fibre Channel port
1032 * @v port Fibre Channel port
1033 * @v rc Reason for logout
1035 void fc_port_logout ( struct fc_port *port, int rc ) {
1036 struct fc_peer *peer;
1037 struct fc_peer *tmp;
1039 DBGC ( port, "FCPORT %s logged out: %s\n",
1040 port->name, strerror ( rc ) );
1042 /* Erase port details */
1043 memset ( &port->port_id, 0, sizeof ( port->port_id ) );
1047 fc_link_err ( &port->link, rc );
1049 /* Notify peers of link state change */
1050 list_for_each_entry_safe ( peer, tmp, &fc_peers, list ) {
1051 fc_peer_get ( peer );
1052 fc_link_examine ( &peer->link );
1053 fc_peer_put ( peer );
1058 * Handle FLOGI completion
1060 * @v port Fibre Channel port
1061 * @v rc Reason for completion
1063 static void fc_port_flogi_done ( struct fc_port *port, int rc ) {
1065 intf_restart ( &port->flogi, rc );
1068 fc_port_logout ( port, rc );
1072 * Handle name server PLOGI completion
1074 * @v port Fibre Channel port
1075 * @v rc Reason for completion
1077 static void fc_port_ns_plogi_done ( struct fc_port *port, int rc ) {
1079 intf_restart ( &port->ns_plogi, rc );
1082 port->flags |= FC_PORT_HAS_NS;
1083 DBGC ( port, "FCPORT %s logged in to name server\n",
1086 DBGC ( port, "FCPORT %s could not log in to name server: %s\n",
1087 port->name, strerror ( rc ) );
1088 /* Absence of a name server is not a fatal error */
1093 * Examine Fibre Channel port link state
1095 * @ link Fibre Channel link state monitor
1097 static void fc_port_examine ( struct fc_link_state *link ) {
1098 struct fc_port *port = container_of ( link, struct fc_port, link );
1101 /* Do nothing if already logged in */
1102 if ( fc_link_ok ( &port->link ) )
1105 DBGC ( port, "FCPORT %s attempting login\n", port->name );
1107 /* Try to create FLOGI ELS */
1108 intf_restart ( &port->flogi, -ECANCELED );
1109 if ( ( rc = fc_els_flogi ( &port->flogi, port ) ) != 0 ) {
1110 DBGC ( port, "FCPORT %s could not initiate FLOGI: %s\n",
1111 port->name, strerror ( rc ) );
1112 fc_port_logout ( port, rc );
1118 * Handle change of flow control window
1120 * @v port Fibre Channel port
1122 static void fc_port_window_changed ( struct fc_port *port ) {
1125 /* Check if transport layer is ready */
1126 window = xfer_window ( &port->transport );
1129 /* Transport layer is ready. Start login if the link
1130 * is not already up.
1132 if ( ! fc_link_ok ( &port->link ) )
1133 fc_link_start ( &port->link );
1137 /* Transport layer is not ready. Log out port and
1138 * wait for transport layer before attempting log in
1141 fc_port_logout ( port, -ENOTCONN );
1142 fc_link_stop ( &port->link );
1146 /** Fibre Channel port transport interface operations */
1147 static struct interface_operation fc_port_transport_op[] = {
1148 INTF_OP ( xfer_deliver, struct fc_port *, fc_port_deliver ),
1149 INTF_OP ( xfer_window_changed, struct fc_port *,
1150 fc_port_window_changed ),
1151 INTF_OP ( intf_close, struct fc_port *, fc_port_close ),
1154 /** Fibre Channel port transport interface descriptor */
1155 static struct interface_descriptor fc_port_transport_desc =
1156 INTF_DESC ( struct fc_port, transport, fc_port_transport_op );
1158 /** Fibre Channel port FLOGI interface operations */
1159 static struct interface_operation fc_port_flogi_op[] = {
1160 INTF_OP ( intf_close, struct fc_port *, fc_port_flogi_done ),
1163 /** Fibre Channel port FLOGI interface descriptor */
1164 static struct interface_descriptor fc_port_flogi_desc =
1165 INTF_DESC ( struct fc_port, flogi, fc_port_flogi_op );
1167 /** Fibre Channel port name server PLOGI interface operations */
1168 static struct interface_operation fc_port_ns_plogi_op[] = {
1169 INTF_OP ( intf_close, struct fc_port *, fc_port_ns_plogi_done ),
1172 /** Fibre Channel port name server PLOGI interface descriptor */
1173 static struct interface_descriptor fc_port_ns_plogi_desc =
1174 INTF_DESC ( struct fc_port, ns_plogi, fc_port_ns_plogi_op );
1177 * Create Fibre Channel port
1179 * @v transport Transport interface
1180 * @v node Fibre Channel node name
1181 * @v port Fibre Channel port name
1182 * @v name Symbolic port name
1183 * @ret rc Return status code
1185 int fc_port_open ( struct interface *transport, const struct fc_name *node_wwn,
1186 const struct fc_name *port_wwn, const char *name ) {
1187 struct fc_port *port;
1189 /* Allocate and initialise structure */
1190 port = zalloc ( sizeof ( *port ) );
1193 ref_init ( &port->refcnt, NULL );
1194 intf_init ( &port->transport, &fc_port_transport_desc, &port->refcnt );
1195 fc_link_init ( &port->link, fc_port_examine, &port->refcnt );
1196 intf_init ( &port->flogi, &fc_port_flogi_desc, &port->refcnt );
1197 intf_init ( &port->ns_plogi, &fc_port_ns_plogi_desc, &port->refcnt );
1198 list_add_tail ( &port->list, &fc_ports );
1199 INIT_LIST_HEAD ( &port->xchgs );
1200 memcpy ( &port->node_wwn, node_wwn, sizeof ( port->node_wwn ) );
1201 memcpy ( &port->port_wwn, port_wwn, sizeof ( port->port_wwn ) );
1202 snprintf ( port->name, sizeof ( port->name ), "%s", name );
1204 DBGC ( port, "FCPORT %s opened as %s",
1205 port->name, fc_ntoa ( &port->node_wwn ) );
1206 DBGC ( port, " port %s\n", fc_ntoa ( &port->port_wwn ) );
1208 /* Attach to transport layer, mortalise self, and return */
1209 intf_plug_plug ( &port->transport, transport );
1210 ref_put ( &port->refcnt );
1215 * Find Fibre Channel port by name
1217 * @v name Fibre Channel port name
1218 * @ret port Fibre Channel port, or NULL
1220 struct fc_port * fc_port_find ( const char *name ) {
1221 struct fc_port *port;
1223 list_for_each_entry ( port, &fc_ports, list ) {
1224 if ( strcmp ( name, port->name ) == 0 )
1230 /******************************************************************************
1232 * Fibre Channel peers
1234 ******************************************************************************
1238 * Close Fibre Channel peer
1240 * @v peer Fibre Channel peer
1241 * @v rc Reason for close
1243 static void fc_peer_close ( struct fc_peer *peer, int rc ) {
1245 DBGC ( peer, "FCPEER %s closed: %s\n",
1246 fc_ntoa ( &peer->port_wwn ) , strerror ( rc ) );
1249 assert ( list_empty ( &peer->ulps ) );
1251 /* Stop link timer */
1252 fc_link_stop ( &peer->link );
1254 /* Shut down interfaces */
1255 intf_shutdown ( &peer->plogi, rc );
1257 /* Remove from list of peers */
1258 list_del ( &peer->list );
1259 INIT_LIST_HEAD ( &peer->list );
1263 * Increment Fibre Channel peer active usage count
1265 * @v peer Fibre Channel peer
1267 static void fc_peer_increment ( struct fc_peer *peer ) {
1269 /* Increment our usage count */
1274 * Decrement Fibre Channel peer active usage count
1276 * @v peer Fibre Channel peer
1278 static void fc_peer_decrement ( struct fc_peer *peer ) {
1281 assert ( peer->usage > 0 );
1283 /* Decrement our usage count and log out if we reach zero */
1284 if ( --(peer->usage) == 0 )
1285 fc_peer_logout ( peer, 0 );
1289 * Log in Fibre Channel peer
1291 * @v peer Fibre Channel peer
1292 * @v port Fibre Channel port
1293 * @v port_id Port ID
1294 * @ret rc Return status code
1296 int fc_peer_login ( struct fc_peer *peer, struct fc_port *port,
1297 struct fc_port_id *port_id ) {
1301 /* Perform implicit logout if logged in and details differ */
1302 if ( fc_link_ok ( &peer->link ) &&
1303 ( ( peer->port != port ) ||
1304 ( memcmp ( &peer->port_id, port_id,
1305 sizeof ( peer->port_id ) ) !=0 ) ) ) {
1306 fc_peer_logout ( peer, 0 );
1309 /* Log in, if applicable */
1310 if ( ! fc_link_ok ( &peer->link ) ) {
1312 /* Record peer details */
1313 assert ( peer->port == NULL );
1314 peer->port = fc_port_get ( port );
1315 memcpy ( &peer->port_id, port_id, sizeof ( peer->port_id ) );
1316 DBGC ( peer, "FCPEER %s logged in via %s as %s\n",
1317 fc_ntoa ( &peer->port_wwn ), peer->port->name,
1318 fc_id_ntoa ( &peer->port_id ) );
1320 /* Add login reference */
1321 fc_peer_get ( peer );
1325 fc_link_up ( &peer->link );
1327 /* Notify ULPs of link state change */
1328 list_for_each_entry_safe ( ulp, tmp, &peer->ulps, list ) {
1330 fc_link_examine ( &ulp->link );
1338 * Log out Fibre Channel peer
1340 * @v peer Fibre Channel peer
1341 * @v rc Reason for logout
1343 void fc_peer_logout ( struct fc_peer *peer, int rc ) {
1347 DBGC ( peer, "FCPEER %s logged out: %s\n",
1348 fc_ntoa ( &peer->port_wwn ), strerror ( rc ) );
1350 /* Drop login reference, if applicable */
1351 if ( fc_link_ok ( &peer->link ) )
1352 fc_peer_put ( peer );
1354 /* Erase peer details */
1355 fc_port_put ( peer->port );
1359 fc_link_err ( &peer->link, rc );
1361 /* Notify ULPs of link state change */
1362 list_for_each_entry_safe ( ulp, tmp, &peer->ulps, list ) {
1364 fc_link_examine ( &ulp->link );
1368 /* Close peer if there are no active users */
1369 if ( peer->usage == 0 )
1370 fc_peer_close ( peer, rc );
1374 * Handle PLOGI completion
1376 * @v peer Fibre Channel peer
1377 * @v rc Reason for completion
1379 static void fc_peer_plogi_done ( struct fc_peer *peer, int rc ) {
1381 intf_restart ( &peer->plogi, rc );
1384 fc_peer_logout ( peer, rc );
1390 * @v peer Fibre Channel peer
1391 * @v port Fibre Channel port
1392 * @v peer_port_id Peer port ID
1393 * @ret rc Return status code
1395 static int fc_peer_plogi ( struct fc_peer *peer, struct fc_port *port,
1396 struct fc_port_id *peer_port_id ) {
1399 /* Try to create PLOGI ELS */
1400 intf_restart ( &peer->plogi, -ECANCELED );
1401 if ( ( rc = fc_els_plogi ( &peer->plogi, port, peer_port_id ) ) != 0 ) {
1402 DBGC ( peer, "FCPEER %s could not initiate PLOGI: %s\n",
1403 fc_ntoa ( &peer->port_wwn ), strerror ( rc ) );
1404 fc_peer_logout ( peer, rc );
1412 * Examine Fibre Channel peer link state
1414 * @ link Fibre Channel link state monitor
1416 static void fc_peer_examine ( struct fc_link_state *link ) {
1417 struct fc_peer *peer = container_of ( link, struct fc_peer, link );
1418 struct fc_port *port;
1421 /* Check to see if underlying port link has gone down */
1422 if ( peer->port && ( ! fc_link_ok ( &peer->port->link ) ) ) {
1423 fc_peer_logout ( peer, -ENOTCONN );
1427 /* Do nothing if already logged in */
1428 if ( fc_link_ok ( &peer->link ) )
1431 DBGC ( peer, "FCPEER %s attempting login\n",
1432 fc_ntoa ( &peer->port_wwn ) );
1435 assert ( peer->port == NULL );
1437 /* First, look for a port with the peer attached via a
1438 * point-to-point link.
1440 list_for_each_entry ( port, &fc_ports, list ) {
1441 if ( fc_link_ok ( &port->link ) &&
1442 ( ! ( port->flags & FC_PORT_HAS_FABRIC ) ) &&
1443 ( memcmp ( &peer->port_wwn, &port->link_port_wwn,
1444 sizeof ( peer->port_wwn ) ) == 0 ) ) {
1445 /* Use this peer port ID, and stop looking */
1446 fc_peer_plogi ( peer, port, &port->ptp_link_port_id );
1451 /* If the peer is not directly attached, try initiating a name
1452 * server lookup on any suitable ports.
1454 list_for_each_entry ( port, &fc_ports, list ) {
1455 if ( fc_link_ok ( &port->link ) &&
1456 ( port->flags & FC_PORT_HAS_FABRIC ) &&
1457 ( port->flags & FC_PORT_HAS_NS ) ) {
1458 if ( ( rc = fc_ns_query ( peer, port,
1459 fc_peer_plogi ) ) != 0 ) {
1460 DBGC ( peer, "FCPEER %s could not attempt "
1461 "name server lookup on %s: %s\n",
1462 fc_ntoa ( &peer->port_wwn ), port->name,
1470 /** Fibre Channel peer PLOGI interface operations */
1471 static struct interface_operation fc_peer_plogi_op[] = {
1472 INTF_OP ( intf_close, struct fc_peer *, fc_peer_plogi_done ),
1475 /** Fibre Channel peer PLOGI interface descriptor */
1476 static struct interface_descriptor fc_peer_plogi_desc =
1477 INTF_DESC ( struct fc_peer, plogi, fc_peer_plogi_op );
1480 * Create Fibre Channel peer
1482 * @v port_wwn Node name
1483 * @ret peer Fibre Channel peer, or NULL
1485 static struct fc_peer * fc_peer_create ( const struct fc_name *port_wwn ) {
1486 struct fc_peer *peer;
1488 /* Allocate and initialise structure */
1489 peer = zalloc ( sizeof ( *peer ) );
1492 ref_init ( &peer->refcnt, NULL );
1493 fc_link_init ( &peer->link, fc_peer_examine, &peer->refcnt );
1494 intf_init ( &peer->plogi, &fc_peer_plogi_desc, &peer->refcnt );
1495 list_add_tail ( &peer->list, &fc_peers );
1496 memcpy ( &peer->port_wwn, port_wwn, sizeof ( peer->port_wwn ) );
1497 INIT_LIST_HEAD ( &peer->ulps );
1499 /* Start link monitor */
1500 fc_link_start ( &peer->link );
1502 DBGC ( peer, "FCPEER %s created\n", fc_ntoa ( &peer->port_wwn ) );
1507 * Get Fibre Channel peer by node name
1509 * @v port_wwn Node name
1510 * @ret peer Fibre Channel peer, or NULL
1512 struct fc_peer * fc_peer_get_wwn ( const struct fc_name *port_wwn ) {
1513 struct fc_peer *peer;
1515 /* Look for an existing peer */
1516 list_for_each_entry ( peer, &fc_peers, list ) {
1517 if ( memcmp ( &peer->port_wwn, port_wwn,
1518 sizeof ( peer->port_wwn ) ) == 0 )
1519 return fc_peer_get ( peer );
1522 /* Create a new peer */
1523 peer = fc_peer_create ( port_wwn );
1531 * Get Fibre Channel peer by port ID
1533 * @v port Fibre Channel port
1534 * @v peer_port_id Peer port ID
1535 * @ret peer Fibre Channel peer, or NULL
1537 struct fc_peer * fc_peer_get_port_id ( struct fc_port *port,
1538 const struct fc_port_id *peer_port_id ){
1539 struct fc_peer *peer;
1541 /* Look for an existing peer */
1542 list_for_each_entry ( peer, &fc_peers, list ) {
1543 if ( ( peer->port == port ) &&
1544 ( memcmp ( &peer->port_id, peer_port_id,
1545 sizeof ( peer->port_id ) ) == 0 ) )
1546 return fc_peer_get ( peer );
1549 /* Cannot create a new peer, since we have no port name to use */
1553 /******************************************************************************
1555 * Fibre Channel upper-layer protocols
1557 ******************************************************************************
1561 * Free Fibre Channel upper-layer protocol
1563 * @v refcnt Reference count
1565 static void fc_ulp_free ( struct refcnt *refcnt ) {
1566 struct fc_ulp *ulp = container_of ( refcnt, struct fc_ulp, refcnt );
1568 fc_peer_put ( ulp->peer );
1573 * Close Fibre Channel upper-layer protocol
1575 * @v ulp Fibre Channel upper-layer protocol
1576 * @v rc Reason for close
1578 static void fc_ulp_close ( struct fc_ulp *ulp, int rc ) {
1580 DBGC ( ulp, "FCULP %s/%02x closed: %s\n",
1581 fc_ntoa ( &ulp->peer->port_wwn ), ulp->type, strerror ( rc ) );
1584 assert ( list_empty ( &ulp->users ) );
1586 /* Stop link monitor */
1587 fc_link_stop ( &ulp->link );
1589 /* Shut down interfaces */
1590 intf_shutdown ( &ulp->prli, rc );
1592 /* Remove from list of ULPs */
1593 list_del ( &ulp->list );
1594 INIT_LIST_HEAD ( &ulp->list );
1598 * Attach Fibre Channel upper-layer protocol user
1600 * @v ulp Fibre Channel upper-layer protocol
1601 * @v user Fibre Channel upper-layer protocol user
1603 void fc_ulp_attach ( struct fc_ulp *ulp, struct fc_ulp_user *user ) {
1606 assert ( user->ulp == NULL );
1608 /* Increment peer's usage count */
1609 fc_peer_increment ( ulp->peer );
1612 user->ulp = fc_ulp_get ( ulp );
1613 list_add ( &user->list, &ulp->users );
1617 * Detach Fibre Channel upper-layer protocol user
1619 * @v user Fibre Channel upper-layer protocol user
1621 void fc_ulp_detach ( struct fc_ulp_user *user ) {
1622 struct fc_ulp *ulp = user->ulp;
1624 /* Do nothing if not attached */
1629 list_check_contains_entry ( user, &ulp->users, list );
1631 /* Detach user and log out if no users remain */
1632 list_del ( &user->list );
1633 if ( list_empty ( &ulp->users ) )
1634 fc_ulp_logout ( ulp, 0 );
1636 /* Decrement our peer's usage count */
1637 fc_peer_decrement ( ulp->peer );
1639 /* Drop reference */
1645 * Log in Fibre Channel upper-layer protocol
1647 * @v ulp Fibre Channel upper-layer protocol
1648 * @v param Service parameters
1649 * @v param_len Length of service parameters
1650 * @v originated Login was originated by us
1651 * @ret rc Return status code
1653 int fc_ulp_login ( struct fc_ulp *ulp, const void *param, size_t param_len,
1655 struct fc_ulp_user *user;
1656 struct fc_ulp_user *tmp;
1658 /* Perform implicit logout if logged in and service parameters differ */
1659 if ( fc_link_ok ( &ulp->link ) &&
1660 ( ( ulp->param_len != param_len ) ||
1661 ( memcmp ( ulp->param, param, ulp->param_len ) != 0 ) ) ) {
1662 fc_ulp_logout ( ulp, 0 );
1665 /* Work around a bug in some versions of the Linux Fibre
1666 * Channel stack, which fail to fully initialise image pairs
1667 * established via a PRLI originated by the Linux stack
1671 ulp->flags |= FC_ULP_ORIGINATED_LOGIN_OK;
1672 if ( ! ( ulp->flags & FC_ULP_ORIGINATED_LOGIN_OK ) ) {
1673 DBGC ( ulp, "FCULP %s/%02x sending extra PRLI to work around "
1675 fc_ntoa ( &ulp->peer->port_wwn ), ulp->type );
1676 fc_link_stop ( &ulp->link );
1677 fc_link_start ( &ulp->link );
1681 /* Log in, if applicable */
1682 if ( ! fc_link_ok ( &ulp->link ) ) {
1684 /* Record service parameters */
1685 assert ( ulp->param == NULL );
1686 assert ( ulp->param_len == 0 );
1687 ulp->param = malloc ( param_len );
1688 if ( ! ulp->param ) {
1689 DBGC ( ulp, "FCULP %s/%02x could not record "
1691 fc_ntoa ( &ulp->peer->port_wwn ), ulp->type );
1694 memcpy ( ulp->param, param, param_len );
1695 ulp->param_len = param_len;
1696 DBGC ( ulp, "FCULP %s/%02x logged in with parameters:\n",
1697 fc_ntoa ( &ulp->peer->port_wwn ), ulp->type );
1698 DBGC_HDA ( ulp, 0, ulp->param, ulp->param_len );
1700 /* Add login reference */
1705 fc_link_up ( &ulp->link );
1707 /* Notify users of link state change */
1708 list_for_each_entry_safe ( user, tmp, &ulp->users, list ) {
1709 fc_ulp_user_get ( user );
1710 user->examine ( user );
1711 fc_ulp_user_put ( user );
1718 * Log out Fibre Channel upper-layer protocol
1720 * @v ulp Fibre Channel upper-layer protocol
1721 * @v rc Reason for logout
1723 void fc_ulp_logout ( struct fc_ulp *ulp, int rc ) {
1724 struct fc_ulp_user *user;
1725 struct fc_ulp_user *tmp;
1727 DBGC ( ulp, "FCULP %s/%02x logged out: %s\n",
1728 fc_ntoa ( &ulp->peer->port_wwn ), ulp->type, strerror ( rc ) );
1730 /* Drop login reference, if applicable */
1731 if ( fc_link_ok ( &ulp->link ) )
1734 /* Discard service parameters */
1735 free ( ulp->param );
1741 fc_link_err ( &ulp->link, rc );
1743 /* Notify users of link state change */
1744 list_for_each_entry_safe ( user, tmp, &ulp->users, list ) {
1745 fc_ulp_user_get ( user );
1746 user->examine ( user );
1747 fc_ulp_user_put ( user );
1750 /* Close ULP if there are no clients attached */
1751 if ( list_empty ( &ulp->users ) )
1752 fc_ulp_close ( ulp, rc );
1756 * Handle PRLI completion
1758 * @v ulp Fibre Channel upper-layer protocol
1759 * @v rc Reason for completion
1761 static void fc_ulp_prli_done ( struct fc_ulp *ulp, int rc ) {
1763 intf_restart ( &ulp->prli, rc );
1766 fc_ulp_logout ( ulp, rc );
1770 * Examine Fibre Channel upper-layer protocol link state
1772 * @ link Fibre Channel link state monitor
1774 static void fc_ulp_examine ( struct fc_link_state *link ) {
1775 struct fc_ulp *ulp = container_of ( link, struct fc_ulp, link );
1778 /* Check to see if underlying peer link has gone down */
1779 if ( ! fc_link_ok ( &ulp->peer->link ) ) {
1780 fc_ulp_logout ( ulp, -ENOTCONN );
1784 /* Do nothing if already logged in */
1785 if ( fc_link_ok ( &ulp->link ) &&
1786 ( ulp->flags & FC_ULP_ORIGINATED_LOGIN_OK ) )
1789 DBGC ( ulp, "FCULP %s/%02x attempting login\n",
1790 fc_ntoa ( &ulp->peer->port_wwn ), ulp->type );
1792 /* Try to create PRLI ELS */
1793 intf_restart ( &ulp->prli, -ECANCELED );
1794 if ( ( rc = fc_els_prli ( &ulp->prli, ulp->peer->port,
1795 &ulp->peer->port_id, ulp->type ) ) != 0 ) {
1796 DBGC ( ulp, "FCULP %s/%02x could not initiate PRLI: %s\n",
1797 fc_ntoa ( &ulp->peer->port_wwn ), ulp->type,
1799 fc_ulp_logout ( ulp, rc );
1804 /** Fibre Channel upper-layer protocol PRLI interface operations */
1805 static struct interface_operation fc_ulp_prli_op[] = {
1806 INTF_OP ( intf_close, struct fc_ulp *, fc_ulp_prli_done ),
1809 /** Fibre Channel upper-layer protocol PRLI interface descriptor */
1810 static struct interface_descriptor fc_ulp_prli_desc =
1811 INTF_DESC ( struct fc_ulp, prli, fc_ulp_prli_op );
1814 * Create Fibre Channel upper-layer protocl
1816 * @v peer Fibre Channel peer
1818 * @ret ulp Fibre Channel upper-layer protocol, or NULL
1820 static struct fc_ulp * fc_ulp_create ( struct fc_peer *peer,
1821 unsigned int type ) {
1824 /* Allocate and initialise structure */
1825 ulp = zalloc ( sizeof ( *ulp ) );
1828 ref_init ( &ulp->refcnt, fc_ulp_free );
1829 fc_link_init ( &ulp->link, fc_ulp_examine, &ulp->refcnt );
1830 intf_init ( &ulp->prli, &fc_ulp_prli_desc, &ulp->refcnt );
1831 ulp->peer = fc_peer_get ( peer );
1832 list_add_tail ( &ulp->list, &peer->ulps );
1834 INIT_LIST_HEAD ( &ulp->users );
1836 /* Start link state monitor */
1837 fc_link_start ( &ulp->link );
1839 DBGC ( ulp, "FCULP %s/%02x created\n",
1840 fc_ntoa ( &ulp->peer->port_wwn ), ulp->type );
1845 * Get Fibre Channel upper-layer protocol by peer and type
1847 * @v peer Fibre Channel peer
1849 * @ret ulp Fibre Channel upper-layer protocol, or NULL
1851 static struct fc_ulp * fc_ulp_get_type ( struct fc_peer *peer,
1852 unsigned int type ) {
1855 /* Look for an existing ULP */
1856 list_for_each_entry ( ulp, &peer->ulps, list ) {
1857 if ( ulp->type == type )
1858 return fc_ulp_get ( ulp );
1861 /* Create a new ULP */
1862 ulp = fc_ulp_create ( peer, type );
1870 * Get Fibre Channel upper-layer protocol by port name and type
1872 * @v port_wwn Port name
1874 * @ret ulp Fibre Channel upper-layer protocol, or NULL
1876 struct fc_ulp * fc_ulp_get_wwn_type ( const struct fc_name *port_wwn,
1877 unsigned int type ) {
1879 struct fc_peer *peer;
1882 peer = fc_peer_get_wwn ( port_wwn );
1884 goto err_peer_get_wwn;
1887 ulp = fc_ulp_get_type ( peer, type );
1889 goto err_ulp_get_type;
1891 /* Drop temporary reference to peer */
1892 fc_peer_put ( peer );
1898 fc_peer_put ( peer );
1904 * Get Fibre Channel upper-layer protocol by port ID and type
1906 * @v port Fibre Channel port
1907 * @v peer_port_id Peer port ID
1909 * @ret ulp Fibre Channel upper-layer protocol, or NULL
1911 struct fc_ulp * fc_ulp_get_port_id_type ( struct fc_port *port,
1912 const struct fc_port_id *peer_port_id,
1913 unsigned int type ) {
1915 struct fc_peer *peer;
1918 peer = fc_peer_get_port_id ( port, peer_port_id );
1920 goto err_peer_get_wwn;
1923 ulp = fc_ulp_get_type ( peer, type );
1925 goto err_ulp_get_type;
1927 /* Drop temporary reference to peer */
1928 fc_peer_put ( peer );
1934 fc_peer_put ( peer );