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 );