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