[zbin] Fix check for existence of most recent output byte
[ipxe.git] / src / net / fc.c
1 /*
2 * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
3 *
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.
8 *
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.
13 *
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
17 * 02110-1301, USA.
18 */
19
20 FILE_LICENCE ( GPL2_OR_LATER );
21
22 #include <stddef.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdio.h>
26 #include <errno.h>
27 #include <assert.h>
28 #include <byteswap.h>
29 #include <ipxe/refcnt.h>
30 #include <ipxe/list.h>
31 #include <ipxe/tables.h>
32 #include <ipxe/timer.h>
33 #include <ipxe/retry.h>
34 #include <ipxe/interface.h>
35 #include <ipxe/xfer.h>
36 #include <ipxe/iobuf.h>
37 #include <ipxe/fc.h>
38 #include <ipxe/fcels.h>
39 #include <ipxe/fcns.h>
40
41 /** @file
42 *
43 * Fibre Channel
44 *
45 */
46
47 /** List of Fibre Channel ports */
48 LIST_HEAD ( fc_ports );
49
50 /** List of Fibre Channel peers */
51 LIST_HEAD ( fc_peers );
52
53 /******************************************************************************
54 *
55 * Well-known addresses
56 *
57 ******************************************************************************
58 */
59
60 /** Unassigned port ID */
61 struct fc_port_id fc_empty_port_id = { .bytes = { 0x00, 0x00, 0x00 } };
62
63 /** F_Port contoller port ID */
64 struct fc_port_id fc_f_port_id = { .bytes = { 0xff, 0xff, 0xfe } };
65
66 /** Generic services port ID */
67 struct fc_port_id fc_gs_port_id = { .bytes = { 0xff, 0xff, 0xfc } };
68
69 /** Point-to-point low port ID */
70 struct fc_port_id fc_ptp_low_port_id = { .bytes = { 0x01, 0x01, 0x01 } };
71
72 /** Point-to-point high port ID */
73 struct fc_port_id fc_ptp_high_port_id = { .bytes = { 0x01, 0x01, 0x02 } };
74
75 /******************************************************************************
76 *
77 * Utility functions
78 *
79 ******************************************************************************
80 */
81
82 /**
83 * Format Fibre Channel port ID
84 *
85 * @v id Fibre Channel port ID
86 * @ret id_text Port ID text
87 */
88 const char * fc_id_ntoa ( const struct fc_port_id *id ) {
89 static char id_text[ FC_PORT_ID_STRLEN + 1 /* NUL */ ];
90
91 snprintf ( id_text, sizeof ( id_text ), "%02x.%02x.%02x",
92 id->bytes[0], id->bytes[1], id->bytes[2] );
93 return id_text;
94 }
95
96 /**
97 * Parse Fibre Channel port ID
98 *
99 * @v id_text Port ID text
100 * @ret id Fibre Channel port ID
101 * @ret rc Return status code
102 */
103 int fc_id_aton ( const char *id_text, struct fc_port_id *id ) {
104 char *ptr = ( ( char * ) id_text );
105 unsigned int i = 0;
106
107 while ( 1 ) {
108 id->bytes[i++] = strtoul ( ptr, &ptr, 16 );
109 if ( i == sizeof ( id->bytes ) )
110 return ( ( *ptr == '\0' ) ? 0 : -EINVAL );
111 if ( *ptr != '.' )
112 return -EINVAL;
113 ptr++;
114 }
115 }
116
117 /**
118 * Format Fibre Channel WWN
119 *
120 * @v wwn Fibre Channel WWN
121 * @ret wwn_text WWN text
122 */
123 const char * fc_ntoa ( const struct fc_name *wwn ) {
124 static char wwn_text[ FC_NAME_STRLEN + 1 /* NUL */ ];
125
126 snprintf ( wwn_text, sizeof ( wwn_text ),
127 "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
128 wwn->bytes[0], wwn->bytes[1], wwn->bytes[2], wwn->bytes[3],
129 wwn->bytes[4], wwn->bytes[5], wwn->bytes[6], wwn->bytes[7] );
130 return wwn_text;
131 }
132
133 /**
134 * Parse Fibre Channel WWN
135 *
136 * @v wwn_text WWN text
137 * @ret wwn Fibre Channel WWN
138 * @ret rc Return status code
139 */
140 int fc_aton ( const char *wwn_text, struct fc_name *wwn ) {
141 char *ptr = ( ( char * ) wwn_text );
142 unsigned int i = 0;
143
144 while ( 1 ) {
145 wwn->bytes[i++] = strtoul ( ptr, &ptr, 16 );
146 if ( i == sizeof ( wwn->bytes ) )
147 return ( ( *ptr == '\0' ) ? 0 : -EINVAL );
148 if ( *ptr != ':' )
149 return -EINVAL;
150 ptr++;
151 }
152 }
153
154 /**
155 * Fill Fibre Channel socket address
156 *
157 * @v sa_fc Fibre Channel socket address to fill in
158 * @v id Fibre Channel port ID
159 * @ret sa Socket address
160 */
161 struct sockaddr * fc_fill_sockaddr ( struct sockaddr_fc *sa_fc,
162 struct fc_port_id *id ) {
163 union {
164 struct sockaddr sa;
165 struct sockaddr_fc fc;
166 } *u = container_of ( sa_fc, typeof ( *u ), fc );
167
168 memset ( sa_fc, 0, sizeof ( *sa_fc ) );
169 sa_fc->sfc_family = AF_FC;
170 memcpy ( &sa_fc->sfc_port_id, id, sizeof ( sa_fc->sfc_port_id ) );
171 return &u->sa;
172 }
173
174 /******************************************************************************
175 *
176 * Fibre Channel link state
177 *
178 ******************************************************************************
179 */
180
181 /** Default link status code */
182 #define EUNKNOWN_LINK_STATUS __einfo_error ( EINFO_EUNKNOWN_LINK_STATUS )
183 #define EINFO_EUNKNOWN_LINK_STATUS \
184 __einfo_uniqify ( EINFO_EINPROGRESS, 0x01, "Unknown" )
185
186 /**
187 * Mark Fibre Channel link as up
188 *
189 * @v link Fibre Channel link state monitor
190 */
191 static void fc_link_up ( struct fc_link_state *link ) {
192
193 /* Stop retry timer */
194 stop_timer ( &link->timer );
195
196 /* Record link state */
197 link->rc = 0;
198 }
199
200 /**
201 * Mark Fibre Channel link as down
202 *
203 * @v link Fibre Channel link state monitor
204 * @v rc Link state
205 */
206 static void fc_link_err ( struct fc_link_state *link, int rc ) {
207
208 /* Record link state */
209 if ( rc == 0 )
210 rc = -EUNKNOWN_LINK_STATUS;
211 link->rc = rc;
212
213 /* Schedule another link examination */
214 start_timer_fixed ( &link->timer, FC_LINK_RETRY_DELAY );
215 }
216
217 /**
218 * Examine Fibre Channel link state
219 *
220 * @v link Fibre Channel link state monitor
221 */
222 static void fc_link_examine ( struct fc_link_state *link ) {
223
224 link->examine ( link );
225 }
226
227 /**
228 * Handle Fibre Channel link retry timer expiry
229 */
230 static void fc_link_expired ( struct retry_timer *timer, int over __unused ) {
231 struct fc_link_state *link =
232 container_of ( timer, struct fc_link_state, timer );
233
234 /* Schedule another link examination */
235 start_timer_fixed ( &link->timer, FC_LINK_RETRY_DELAY );
236
237 /* Examine link */
238 fc_link_examine ( link );
239 }
240
241 /**
242 * Initialise Fibre Channel link state monitor
243 *
244 * @v link Fibre Channel link state monitor
245 * @v examine Examine link state method
246 * @v refcnt Reference counter
247 */
248 static void fc_link_init ( struct fc_link_state *link,
249 void ( * examine ) ( struct fc_link_state *link ),
250 struct refcnt *refcnt ) {
251
252 link->rc = -EUNKNOWN_LINK_STATUS;
253 timer_init ( &link->timer, fc_link_expired, refcnt );
254 link->examine = examine;
255 }
256
257 /**
258 * Start monitoring Fibre Channel link state
259 *
260 * @v link Fibre Channel link state monitor
261 */
262 static void fc_link_start ( struct fc_link_state *link ) {
263 start_timer_nodelay ( &link->timer );
264 }
265
266 /**
267 * Stop monitoring Fibre Channel link state
268 *
269 * @v link Fibre Channel link state monitor
270 */
271 static void fc_link_stop ( struct fc_link_state *link ) {
272 stop_timer ( &link->timer );
273 }
274
275 /******************************************************************************
276 *
277 * Fibre Channel exchanges
278 *
279 ******************************************************************************
280 */
281
282 /** A Fibre Channel exchange */
283 struct fc_exchange {
284 /** Reference count */
285 struct refcnt refcnt;
286 /** Fibre Channel port */
287 struct fc_port *port;
288 /** List of active exchanges within this port */
289 struct list_head list;
290
291 /** Peer port ID */
292 struct fc_port_id peer_port_id;
293 /** Data structure type */
294 unsigned int type;
295 /** Flags */
296 unsigned int flags;
297 /** Local exchange ID */
298 uint16_t xchg_id;
299 /** Peer exchange ID */
300 uint16_t peer_xchg_id;
301 /** Active sequence ID */
302 uint8_t seq_id;
303 /** Active sequence count */
304 uint16_t seq_cnt;
305
306 /** Timeout timer */
307 struct retry_timer timer;
308
309 /** Upper-layer protocol interface */
310 struct interface ulp;
311 };
312
313 /** Fibre Channel exchange flags */
314 enum fc_exchange_flags {
315 /** We are the exchange originator */
316 FC_XCHG_ORIGINATOR = 0x0001,
317 /** We have the sequence initiative */
318 FC_XCHG_SEQ_INITIATIVE = 0x0002,
319 /** This is the first sequence of the exchange */
320 FC_XCHG_SEQ_FIRST = 0x0004,
321 };
322
323 /** Fibre Channel timeout */
324 #define FC_TIMEOUT ( 1 * TICKS_PER_SEC )
325
326 /**
327 * Create local Fibre Channel exchange identifier
328 *
329 * @ret xchg_id Local exchange ID
330 */
331 static unsigned int fc_new_xchg_id ( void ) {
332 static uint16_t next_id = 0x0000;
333
334 /* We must avoid using FC_RX_ID_UNKNOWN (0xffff) */
335 next_id += 2;
336 return next_id;
337 }
338
339 /**
340 * Create local Fibre Channel sequence identifier
341 *
342 * @ret seq_id Local sequence identifier
343 */
344 static unsigned int fc_new_seq_id ( void ) {
345 static uint8_t seq_id = 0x00;
346
347 return (++seq_id);
348 }
349
350 /**
351 * Free Fibre Channel exchange
352 *
353 * @v refcnt Reference count
354 */
355 static void fc_xchg_free ( struct refcnt *refcnt ) {
356 struct fc_exchange *xchg =
357 container_of ( refcnt, struct fc_exchange, refcnt );
358
359 assert ( ! timer_running ( &xchg->timer ) );
360 assert ( list_empty ( &xchg->list ) );
361
362 fc_port_put ( xchg->port );
363 free ( xchg );
364 }
365
366 /**
367 * Close Fibre Channel exchange
368 *
369 * @v xchg Fibre Channel exchange
370 * @v rc Reason for close
371 */
372 static void fc_xchg_close ( struct fc_exchange *xchg, int rc ) {
373 struct fc_port *port = xchg->port;
374
375 if ( rc != 0 ) {
376 DBGC2 ( port, "FCXCHG %s/%04x closed: %s\n",
377 port->name, xchg->xchg_id, strerror ( rc ) );
378 }
379
380 /* Stop timer */
381 stop_timer ( &xchg->timer );
382
383 /* If list still holds a reference, remove from list of open
384 * exchanges and drop list's reference.
385 */
386 if ( ! list_empty ( &xchg->list ) ) {
387 list_del ( &xchg->list );
388 INIT_LIST_HEAD ( &xchg->list );
389 ref_put ( &xchg->refcnt );
390 }
391
392 /* Shutdown interfaces */
393 intf_shutdown ( &xchg->ulp, rc );
394 }
395
396 /**
397 * Handle exchange timeout
398 *
399 * @v timer Timeout timer
400 * @v over Failure indicator
401 */
402 static void fc_xchg_expired ( struct retry_timer *timer, int over __unused ) {
403 struct fc_exchange *xchg =
404 container_of ( timer, struct fc_exchange, timer );
405 struct fc_port *port = xchg->port;
406
407 DBGC ( port, "FCXCHG %s/%04x timed out\n", port->name, xchg->xchg_id );
408
409 /* Terminate the exchange */
410 fc_xchg_close ( xchg, -ETIMEDOUT );
411 }
412
413 /**
414 * Check Fibre Channel exchange window
415 *
416 * @v xchg Fibre Channel exchange
417 * @ret len Length opf window
418 */
419 static size_t fc_xchg_window ( struct fc_exchange *xchg __unused ) {
420
421 /* We don't currently store the path MTU */
422 return FC_LOGIN_DEFAULT_MTU;
423 }
424
425 /**
426 * Allocate Fibre Channel I/O buffer
427 *
428 * @v xchg Fibre Channel exchange
429 * @v len Payload length
430 * @ret iobuf I/O buffer, or NULL
431 */
432 static struct io_buffer * fc_xchg_alloc_iob ( struct fc_exchange *xchg,
433 size_t len ) {
434 struct fc_port *port = xchg->port;
435 struct io_buffer *iobuf;
436
437 iobuf = xfer_alloc_iob ( &port->transport,
438 ( sizeof ( struct fc_frame_header ) + len ) );
439 if ( iobuf ) {
440 iob_reserve ( iobuf, sizeof ( struct fc_frame_header ) );
441 }
442 return iobuf;
443 }
444
445 /**
446 * Transmit data as part of a Fibre Channel exchange
447 *
448 * @v xchg Fibre Channel exchange
449 * @v iobuf I/O buffer
450 * @v meta Data transfer metadata
451 * @ret rc Return status code
452 */
453 static int fc_xchg_tx ( struct fc_exchange *xchg, struct io_buffer *iobuf,
454 struct xfer_metadata *meta ) {
455 struct fc_port *port = xchg->port;
456 struct sockaddr_fc *dest = ( ( struct sockaddr_fc * ) meta->dest );
457 struct fc_frame_header *fchdr;
458 unsigned int r_ctl;
459 unsigned int f_ctl_es;
460 int rc;
461
462 /* Sanity checks */
463 if ( ! ( xchg->flags & FC_XCHG_SEQ_INITIATIVE ) ) {
464 DBGC ( port, "FCXCHG %s/%04x cannot transmit while not "
465 "holding sequence initiative\n",
466 port->name, xchg->xchg_id );
467 rc = -EBUSY;
468 goto done;
469 }
470
471 /* Calculate routing control */
472 switch ( xchg->type ) {
473 case FC_TYPE_ELS:
474 r_ctl = FC_R_CTL_ELS;
475 if ( meta->flags & XFER_FL_RESPONSE ) {
476 r_ctl |= FC_R_CTL_SOL_CTRL;
477 } else {
478 r_ctl |= FC_R_CTL_UNSOL_CTRL;
479 }
480 break;
481 case FC_TYPE_CT:
482 r_ctl = FC_R_CTL_DATA;
483 if ( meta->flags & XFER_FL_RESPONSE ) {
484 r_ctl |= FC_R_CTL_SOL_CTRL;
485 } else {
486 r_ctl |= FC_R_CTL_UNSOL_CTRL;
487 }
488 break;
489 default:
490 r_ctl = FC_R_CTL_DATA;
491 switch ( meta->flags &
492 ( XFER_FL_CMD_STAT | XFER_FL_RESPONSE ) ) {
493 case ( XFER_FL_CMD_STAT | XFER_FL_RESPONSE ):
494 r_ctl |= FC_R_CTL_CMD_STAT;
495 break;
496 case ( XFER_FL_CMD_STAT ):
497 r_ctl |= FC_R_CTL_UNSOL_CMD;
498 break;
499 case ( XFER_FL_RESPONSE ):
500 r_ctl |= FC_R_CTL_SOL_DATA;
501 break;
502 default:
503 r_ctl |= FC_R_CTL_UNSOL_DATA;
504 break;
505 }
506 break;
507 }
508
509 /* Calculate exchange and sequence control */
510 f_ctl_es = 0;
511 if ( ! ( xchg->flags & FC_XCHG_ORIGINATOR ) )
512 f_ctl_es |= FC_F_CTL_ES_RESPONDER;
513 if ( xchg->flags & FC_XCHG_SEQ_FIRST )
514 f_ctl_es |= FC_F_CTL_ES_FIRST;
515 if ( meta->flags & XFER_FL_OUT )
516 f_ctl_es |= ( FC_F_CTL_ES_END | FC_F_CTL_ES_LAST );
517 if ( meta->flags & XFER_FL_OVER )
518 f_ctl_es |= ( FC_F_CTL_ES_END | FC_F_CTL_ES_TRANSFER );
519
520 /* Create frame header */
521 fchdr = iob_push ( iobuf, sizeof ( *fchdr ) );
522 memset ( fchdr, 0, sizeof ( *fchdr ) );
523 fchdr->r_ctl = r_ctl;
524 memcpy ( &fchdr->d_id,
525 ( dest ? &dest->sfc_port_id : &xchg->peer_port_id ),
526 sizeof ( fchdr->d_id ) );
527 memcpy ( &fchdr->s_id, &port->port_id, sizeof ( fchdr->s_id ) );
528 fchdr->type = xchg->type;
529 fchdr->f_ctl_es = f_ctl_es;
530 fchdr->seq_id = xchg->seq_id;
531 fchdr->seq_cnt = htons ( xchg->seq_cnt++ );
532 fchdr->ox_id = htons ( ( xchg->flags & FC_XCHG_ORIGINATOR ) ?
533 xchg->xchg_id : xchg->peer_xchg_id );
534 fchdr->rx_id = htons ( ( xchg->flags & FC_XCHG_ORIGINATOR ) ?
535 xchg->peer_xchg_id : xchg->xchg_id );
536 if ( meta->flags & XFER_FL_ABS_OFFSET ) {
537 fchdr->f_ctl_misc |= FC_F_CTL_MISC_REL_OFF;
538 fchdr->parameter = htonl ( meta->offset );
539 }
540
541 /* Relinquish sequence initiative if applicable */
542 if ( meta->flags & XFER_FL_OVER ) {
543 xchg->flags &= ~( FC_XCHG_SEQ_INITIATIVE | FC_XCHG_SEQ_FIRST );
544 xchg->seq_cnt = 0;
545 }
546
547 /* Reset timeout */
548 start_timer_fixed ( &xchg->timer, FC_TIMEOUT );
549
550 /* Deliver frame */
551 if ( ( rc = xfer_deliver_iob ( &port->transport,
552 iob_disown ( iobuf ) ) ) != 0 ) {
553 DBGC ( port, "FCXCHG %s/%04x cannot transmit: %s\n",
554 port->name, xchg->xchg_id, strerror ( rc ) );
555 goto done;
556 }
557
558 done:
559 free_iob ( iobuf );
560 return rc;
561 }
562
563 /** Mapping from Fibre Channel routing control information to xfer metadata */
564 static const uint8_t fc_r_ctl_info_meta_flags[ FC_R_CTL_INFO_MASK + 1 ] = {
565 [FC_R_CTL_UNCAT] = ( 0 ),
566 [FC_R_CTL_SOL_DATA] = ( XFER_FL_RESPONSE ),
567 [FC_R_CTL_UNSOL_CTRL] = ( XFER_FL_CMD_STAT ),
568 [FC_R_CTL_SOL_CTRL] = ( XFER_FL_CMD_STAT ),
569 [FC_R_CTL_UNSOL_DATA] = ( 0 ),
570 [FC_R_CTL_DATA_DESC] = ( XFER_FL_CMD_STAT ),
571 [FC_R_CTL_UNSOL_CMD] = ( XFER_FL_CMD_STAT ),
572 [FC_R_CTL_CMD_STAT] = ( XFER_FL_CMD_STAT | XFER_FL_RESPONSE ),
573 };
574
575 /**
576 * Receive data as part of a Fibre Channel exchange
577 *
578 * @v xchg Fibre Channel exchange
579 * @v iobuf I/O buffer
580 * @v meta Data transfer metadata
581 * @ret rc Return status code
582 */
583 static int fc_xchg_rx ( struct fc_exchange *xchg, struct io_buffer *iobuf,
584 struct xfer_metadata *meta __unused ) {
585 struct fc_port *port = xchg->port;
586 struct fc_frame_header *fchdr = iobuf->data;
587 struct xfer_metadata fc_meta;
588 struct sockaddr_fc src;
589 struct sockaddr_fc dest;
590 int rc;
591
592 /* Record peer exchange ID */
593 xchg->peer_xchg_id =
594 ntohs ( ( fchdr->f_ctl_es & FC_F_CTL_ES_RESPONDER ) ?
595 fchdr->rx_id : fchdr->ox_id );
596
597 /* Sequence checks */
598 if ( xchg->flags & FC_XCHG_SEQ_INITIATIVE ) {
599 DBGC ( port, "FCXCHG %s/%04x received frame while holding "
600 "sequence initiative\n", port->name, xchg->xchg_id );
601 rc = -EBUSY;
602 goto done;
603 }
604 if ( ntohs ( fchdr->seq_cnt ) != xchg->seq_cnt ) {
605 DBGC ( port, "FCXCHG %s/%04x received out-of-order frame %d "
606 "(expected %d)\n", port->name, xchg->xchg_id,
607 ntohs ( fchdr->seq_cnt ), xchg->seq_cnt );
608 rc = -EPIPE;
609 goto done;
610 }
611 if ( xchg->seq_cnt == 0 )
612 xchg->seq_id = fchdr->seq_id;
613 xchg->seq_cnt++;
614 if ( fchdr->seq_id != xchg->seq_id ) {
615 DBGC ( port, "FCXCHG %s/%04x received frame for incorrect "
616 "sequence %02x (expected %02x)\n", port->name,
617 xchg->xchg_id, fchdr->seq_id, xchg->seq_id );
618 rc = -EPIPE;
619 goto done;
620 }
621
622 /* Check for end of sequence and transfer of sequence initiative */
623 if ( fchdr->f_ctl_es & FC_F_CTL_ES_END ) {
624 xchg->seq_cnt = 0;
625 if ( fchdr->f_ctl_es & FC_F_CTL_ES_TRANSFER ) {
626 xchg->flags |= FC_XCHG_SEQ_INITIATIVE;
627 xchg->seq_id = fc_new_seq_id();
628 }
629 }
630
631 /* Construct metadata */
632 memset ( &fc_meta, 0, sizeof ( fc_meta ) );
633 fc_meta.flags =
634 fc_r_ctl_info_meta_flags[ fchdr->r_ctl & FC_R_CTL_INFO_MASK ];
635 if ( fchdr->f_ctl_es & FC_F_CTL_ES_TRANSFER ) {
636 fc_meta.flags |= XFER_FL_OVER;
637 }
638 if ( ( fchdr->f_ctl_es & FC_F_CTL_ES_LAST ) &&
639 ( fchdr->f_ctl_es & FC_F_CTL_ES_END ) ) {
640 fc_meta.flags |= XFER_FL_OUT;
641 }
642 if ( fchdr->f_ctl_misc & FC_F_CTL_MISC_REL_OFF ) {
643 fc_meta.flags |= XFER_FL_ABS_OFFSET;
644 fc_meta.offset = ntohl ( fchdr->parameter );
645 }
646 fc_meta.src = fc_fill_sockaddr ( &src, &fchdr->s_id );
647 fc_meta.dest = fc_fill_sockaddr ( &dest, &fchdr->d_id );
648
649 /* Reset timeout */
650 start_timer_fixed ( &xchg->timer, FC_TIMEOUT );
651
652 /* Deliver via exchange's ULP interface */
653 iob_pull ( iobuf, sizeof ( *fchdr ) );
654 if ( ( rc = xfer_deliver ( &xchg->ulp, iob_disown ( iobuf ),
655 &fc_meta ) ) != 0 ) {
656 DBGC ( port, "FCXCHG %s/%04x cannot deliver frame: %s\n",
657 port->name, xchg->xchg_id, strerror ( rc ) );
658 goto done;
659 }
660
661 /* Close exchange if applicable */
662 if ( ( fchdr->f_ctl_es & FC_F_CTL_ES_LAST ) &&
663 ( fchdr->f_ctl_es & FC_F_CTL_ES_END ) ) {
664 fc_xchg_close ( xchg, 0 );
665 }
666
667 done:
668 free_iob ( iobuf );
669 return rc;
670 }
671
672 /** Fibre Channel exchange ULP interface operations */
673 static struct interface_operation fc_xchg_ulp_op[] = {
674 INTF_OP ( xfer_deliver, struct fc_exchange *, fc_xchg_tx ),
675 INTF_OP ( xfer_alloc_iob, struct fc_exchange *, fc_xchg_alloc_iob ),
676 INTF_OP ( xfer_window, struct fc_exchange *, fc_xchg_window ),
677 INTF_OP ( intf_close, struct fc_exchange *, fc_xchg_close ),
678 };
679
680 /** Fibre Channel exchange ULP interface descriptor */
681 static struct interface_descriptor fc_xchg_ulp_desc =
682 INTF_DESC ( struct fc_exchange, ulp, fc_xchg_ulp_op );
683
684 /**
685 * Create new Fibre Channel exchange
686 *
687 * @v port Fibre Channel port
688 * @v peer_port_id Peer port ID
689 * @ret xchg Exchange, or NULL
690 */
691 static struct fc_exchange * fc_xchg_create ( struct fc_port *port,
692 struct fc_port_id *peer_port_id,
693 unsigned int type ) {
694 struct fc_exchange *xchg;
695
696 /* Allocate and initialise structure */
697 xchg = zalloc ( sizeof ( *xchg ) );
698 if ( ! xchg )
699 return NULL;
700 ref_init ( &xchg->refcnt, fc_xchg_free );
701 intf_init ( &xchg->ulp, &fc_xchg_ulp_desc, &xchg->refcnt );
702 timer_init ( &xchg->timer, fc_xchg_expired, &xchg->refcnt );
703 xchg->port = fc_port_get ( port );
704 memcpy ( &xchg->peer_port_id, peer_port_id,
705 sizeof ( xchg->peer_port_id ) );
706 xchg->type = type;
707 xchg->xchg_id = fc_new_xchg_id();
708 xchg->peer_xchg_id = FC_RX_ID_UNKNOWN;
709 xchg->seq_id = fc_new_seq_id();
710
711 /* Transfer reference to list of exchanges and return */
712 list_add ( &xchg->list, &port->xchgs );
713 return xchg;
714 }
715
716 /**
717 * Originate a new Fibre Channel exchange
718 *
719 * @v parent Interface to which to attach
720 * @v port Fibre Channel port
721 * @v peer_port_id Peer port ID
722 * @ret xchg_id Exchange ID, or negative error
723 */
724 int fc_xchg_originate ( struct interface *parent, struct fc_port *port,
725 struct fc_port_id *peer_port_id, unsigned int type ) {
726 struct fc_exchange *xchg;
727
728 /* Allocate and initialise structure */
729 xchg = fc_xchg_create ( port, peer_port_id, type );
730 if ( ! xchg )
731 return -ENOMEM;
732 xchg->flags = ( FC_XCHG_ORIGINATOR | FC_XCHG_SEQ_INITIATIVE |
733 FC_XCHG_SEQ_FIRST );
734
735 DBGC2 ( port, "FCXCHG %s/%04x originating to %s (type %02x)\n",
736 port->name, xchg->xchg_id, fc_id_ntoa ( &xchg->peer_port_id ),
737 xchg->type );
738
739 /* Attach to parent interface and return */
740 intf_plug_plug ( &xchg->ulp, parent );
741 return xchg->xchg_id;
742 }
743
744 /**
745 * Open a new responder Fibre Channel exchange
746 *
747 * @v port Fibre Channel port
748 * @v fchdr Fibre Channel frame header
749 * @ret xchg Fibre Channel exchange, or NULL
750 */
751 static struct fc_exchange * fc_xchg_respond ( struct fc_port *port,
752 struct fc_frame_header *fchdr ) {
753 struct fc_exchange *xchg;
754 struct fc_responder *responder;
755 unsigned int type = fchdr->type;
756 int rc;
757
758 /* Allocate and initialise structure */
759 xchg = fc_xchg_create ( port, &fchdr->s_id, type );
760 if ( ! xchg )
761 return NULL;
762 xchg->seq_id = fchdr->seq_id;
763
764 DBGC2 ( port, "FCXCHG %s/%04x responding to %s xchg %04x (type "
765 "%02x)\n", port->name, xchg->xchg_id,
766 fc_id_ntoa ( &xchg->peer_port_id ),
767 ntohs ( fchdr->ox_id ), xchg->type );
768
769 /* Find a responder, if any */
770 for_each_table_entry ( responder, FC_RESPONDERS ) {
771 if ( responder->type == type ) {
772 if ( ( rc = responder->respond ( &xchg->ulp, port,
773 &fchdr->d_id,
774 &fchdr->s_id ) ) !=0 ){
775 DBGC ( port, "FCXCHG %s/%04x could not "
776 "respond: %s\n", port->name,
777 xchg->xchg_id, strerror ( rc ) );
778 }
779 }
780 break;
781 }
782
783 /* We may or may not have a ULP attached at this point, but
784 * the exchange does exist.
785 */
786 return xchg;
787 }
788
789 /******************************************************************************
790 *
791 * Fibre Channel ports
792 *
793 ******************************************************************************
794 */
795
796 /**
797 * Close Fibre Channel port
798 *
799 * @v port Fibre Channel port
800 * @v rc Reason for close
801 */
802 static void fc_port_close ( struct fc_port *port, int rc ) {
803 struct fc_exchange *xchg;
804 struct fc_exchange *tmp;
805
806 DBGC ( port, "FCPORT %s closed\n", port->name );
807
808 /* Log out port, if necessary */
809 if ( fc_link_ok ( &port->link ) )
810 fc_port_logout ( port, rc );
811
812 /* Stop link monitor */
813 fc_link_stop ( &port->link );
814
815 /* Shut down interfaces */
816 intf_shutdown ( &port->transport, rc );
817 intf_shutdown ( &port->flogi, rc );
818 intf_shutdown ( &port->ns_plogi, rc );
819
820 /* Shut down any remaining exchanges */
821 list_for_each_entry_safe ( xchg, tmp, &port->xchgs, list )
822 fc_xchg_close ( xchg, rc );
823
824 /* Remove from list of ports */
825 list_del ( &port->list );
826 INIT_LIST_HEAD ( &port->list );
827 }
828
829 /**
830 * Identify Fibre Channel exchange by local exchange ID
831 *
832 * @v port Fibre Channel port
833 * @v xchg_id Local exchange ID
834 * @ret xchg Fibre Channel exchange, or NULL
835 */
836 static struct fc_exchange * fc_port_demux ( struct fc_port *port,
837 unsigned int xchg_id ) {
838 struct fc_exchange *xchg;
839
840 list_for_each_entry ( xchg, &port->xchgs, list ) {
841 if ( xchg->xchg_id == xchg_id )
842 return xchg;
843 }
844 return NULL;
845 }
846
847 /**
848 * Handle received frame from Fibre Channel port
849 *
850 * @v port Fibre Channel port
851 * @v iobuf I/O buffer
852 * @v meta Data transfer metadata
853 * @ret rc Return status code
854 */
855 static int fc_port_deliver ( struct fc_port *port, struct io_buffer *iobuf,
856 struct xfer_metadata *meta ) {
857 struct fc_frame_header *fchdr = iobuf->data;
858 unsigned int xchg_id;
859 struct fc_exchange *xchg;
860 int rc;
861
862 /* Sanity check */
863 if ( iob_len ( iobuf ) < sizeof ( *fchdr ) ) {
864 DBGC ( port, "FCPORT %s received underlength frame (%zd "
865 "bytes)\n", port->name, iob_len ( iobuf ) );
866 rc = -EINVAL;
867 goto err_sanity;
868 }
869
870 /* Verify local port ID */
871 if ( ( memcmp ( &fchdr->d_id, &port->port_id,
872 sizeof ( fchdr->d_id ) ) != 0 ) &&
873 ( memcmp ( &fchdr->d_id, &fc_f_port_id,
874 sizeof ( fchdr->d_id ) ) != 0 ) &&
875 ( memcmp ( &port->port_id, &fc_empty_port_id,
876 sizeof ( port->port_id ) ) != 0 ) ) {
877 DBGC ( port, "FCPORT %s received frame for incorrect port ID "
878 "%s\n", port->name, fc_id_ntoa ( &fchdr->d_id ) );
879 rc = -ENOTCONN;
880 goto err_port_id;
881 }
882
883 /* Demultiplex amongst active exchanges */
884 xchg_id = ntohs ( ( fchdr->f_ctl_es & FC_F_CTL_ES_RESPONDER ) ?
885 fchdr->ox_id : fchdr->rx_id );
886 xchg = fc_port_demux ( port, xchg_id );
887
888 /* If we have no active exchange and this frame starts a new
889 * exchange, try to create a new responder exchange
890 */
891 if ( ( fchdr->f_ctl_es & FC_F_CTL_ES_FIRST ) &&
892 ( fchdr->seq_cnt == 0 ) ) {
893
894 /* Create new exchange */
895 xchg = fc_xchg_respond ( port, fchdr );
896 if ( ! xchg ) {
897 DBGC ( port, "FCPORT %s cannot create new exchange\n",
898 port->name );
899 rc = -ENOMEM;
900 goto err_respond;
901 }
902 }
903
904 /* Fail if no exchange exists */
905 if ( ! xchg ) {
906 DBGC ( port, "FCPORT %s xchg %04x unknown\n",
907 port->name, xchg_id );
908 rc = -ENOTCONN;
909 goto err_no_xchg;
910 }
911
912 /* Pass received frame to exchange */
913 ref_get ( &xchg->refcnt );
914 if ( ( rc = fc_xchg_rx ( xchg, iob_disown ( iobuf ), meta ) ) != 0 )
915 goto err_xchg_rx;
916
917 err_xchg_rx:
918 ref_put ( &xchg->refcnt );
919 err_no_xchg:
920 err_respond:
921 err_port_id:
922 err_sanity:
923 free_iob ( iobuf );
924 return rc;
925 }
926
927 /**
928 * Log in Fibre Channel port
929 *
930 * @v port Fibre Channel port
931 * @v port_id Local port ID
932 * @v link_node_wwn Link node name
933 * @v link_port_wwn Link port name
934 * @v has_fabric Link is to a fabric
935 * @ret rc Return status code
936 */
937 int fc_port_login ( struct fc_port *port, struct fc_port_id *port_id,
938 const struct fc_name *link_node_wwn,
939 const struct fc_name *link_port_wwn, int has_fabric ) {
940 struct fc_peer *peer;
941 struct fc_peer *tmp;
942 int rc;
943
944 /* Perform implicit logout if logged in and details differ */
945 if ( fc_link_ok ( &port->link ) &&
946 ( ( ( !! ( port->flags & FC_PORT_HAS_FABRIC ) ) !=
947 ( !! has_fabric ) ) ||
948 ( memcmp ( &port->link_node_wwn, link_node_wwn,
949 sizeof ( port->link_node_wwn ) ) != 0 ) ||
950 ( memcmp ( &port->link_port_wwn, link_port_wwn,
951 sizeof ( port->link_port_wwn ) ) != 0 ) ||
952 ( has_fabric &&
953 ( memcmp ( &port->port_id, port_id,
954 sizeof ( port->port_id ) ) != 0 ) ) ) ) {
955 fc_port_logout ( port, 0 );
956 }
957
958 /* Log in, if applicable */
959 if ( ! fc_link_ok ( &port->link ) ) {
960
961 /* Record link port name */
962 memcpy ( &port->link_node_wwn, link_node_wwn,
963 sizeof ( port->link_node_wwn ) );
964 memcpy ( &port->link_port_wwn, link_port_wwn,
965 sizeof ( port->link_port_wwn ) );
966 DBGC ( port, "FCPORT %s logged in to %s",
967 port->name, fc_ntoa ( &port->link_node_wwn ) );
968 DBGC ( port, " port %s\n", fc_ntoa ( &port->link_port_wwn ) );
969
970 /* Calculate local (and possibly remote) port IDs */
971 if ( has_fabric ) {
972 port->flags |= FC_PORT_HAS_FABRIC;
973 memcpy ( &port->port_id, port_id,
974 sizeof ( port->port_id ) );
975 } else {
976 port->flags &= ~FC_PORT_HAS_FABRIC;
977 if ( memcmp ( &port->port_wwn, link_port_wwn,
978 sizeof ( port->port_wwn ) ) > 0 ) {
979 memcpy ( &port->port_id, &fc_ptp_high_port_id,
980 sizeof ( port->port_id ) );
981 memcpy ( &port->ptp_link_port_id,
982 &fc_ptp_low_port_id,
983 sizeof ( port->ptp_link_port_id ) );
984 } else {
985 memcpy ( &port->port_id, &fc_ptp_low_port_id,
986 sizeof ( port->port_id ) );
987 memcpy ( &port->ptp_link_port_id,
988 &fc_ptp_high_port_id,
989 sizeof ( port->ptp_link_port_id ) );
990 }
991 }
992 DBGC ( port, "FCPORT %s logged in via a %s, with local ID "
993 "%s\n", port->name,
994 ( ( port->flags & FC_PORT_HAS_FABRIC ) ?
995 "fabric" : "point-to-point link" ),
996 fc_id_ntoa ( &port->port_id ) );
997 }
998
999 /* Log in to name server, if attached to a fabric */
1000 if ( has_fabric && ! ( port->flags & FC_PORT_HAS_NS ) ) {
1001
1002 DBGC ( port, "FCPORT %s attempting login to name server\n",
1003 port->name );
1004
1005 intf_restart ( &port->ns_plogi, -ECANCELED );
1006 if ( ( rc = fc_els_plogi ( &port->ns_plogi, port,
1007 &fc_gs_port_id ) ) != 0 ) {
1008 DBGC ( port, "FCPORT %s could not initiate name "
1009 "server PLOGI: %s\n",
1010 port->name, strerror ( rc ) );
1011 fc_port_logout ( port, rc );
1012 return rc;
1013 }
1014 }
1015
1016 /* Record login */
1017 fc_link_up ( &port->link );
1018
1019 /* Notify peers of link state change */
1020 list_for_each_entry_safe ( peer, tmp, &fc_peers, list ) {
1021 fc_peer_get ( peer );
1022 fc_link_examine ( &peer->link );
1023 fc_peer_put ( peer );
1024 }
1025
1026 return 0;
1027 }
1028
1029 /**
1030 * Log out Fibre Channel port
1031 *
1032 * @v port Fibre Channel port
1033 * @v rc Reason for logout
1034 */
1035 void fc_port_logout ( struct fc_port *port, int rc ) {
1036 struct fc_peer *peer;
1037 struct fc_peer *tmp;
1038
1039 DBGC ( port, "FCPORT %s logged out: %s\n",
1040 port->name, strerror ( rc ) );
1041
1042 /* Erase port details */
1043 memset ( &port->port_id, 0, sizeof ( port->port_id ) );
1044 port->flags = 0;
1045
1046 /* Record logout */
1047 fc_link_err ( &port->link, rc );
1048
1049 /* Notify peers of link state change */
1050 list_for_each_entry_safe ( peer, tmp, &fc_peers, list ) {
1051 fc_peer_get ( peer );
1052 fc_link_examine ( &peer->link );
1053 fc_peer_put ( peer );
1054 }
1055 }
1056
1057 /**
1058 * Handle FLOGI completion
1059 *
1060 * @v port Fibre Channel port
1061 * @v rc Reason for completion
1062 */
1063 static void fc_port_flogi_done ( struct fc_port *port, int rc ) {
1064
1065 intf_restart ( &port->flogi, rc );
1066
1067 if ( rc != 0 )
1068 fc_port_logout ( port, rc );
1069 }
1070
1071 /**
1072 * Handle name server PLOGI completion
1073 *
1074 * @v port Fibre Channel port
1075 * @v rc Reason for completion
1076 */
1077 static void fc_port_ns_plogi_done ( struct fc_port *port, int rc ) {
1078
1079 intf_restart ( &port->ns_plogi, rc );
1080
1081 if ( rc == 0 ) {
1082 port->flags |= FC_PORT_HAS_NS;
1083 DBGC ( port, "FCPORT %s logged in to name server\n",
1084 port->name );
1085 } else {
1086 DBGC ( port, "FCPORT %s could not log in to name server: %s\n",
1087 port->name, strerror ( rc ) );
1088 /* Absence of a name server is not a fatal error */
1089 }
1090 }
1091
1092 /**
1093 * Examine Fibre Channel port link state
1094 *
1095 * @ link Fibre Channel link state monitor
1096 */
1097 static void fc_port_examine ( struct fc_link_state *link ) {
1098 struct fc_port *port = container_of ( link, struct fc_port, link );
1099 int rc;
1100
1101 /* Do nothing if already logged in */
1102 if ( fc_link_ok ( &port->link ) )
1103 return;
1104
1105 DBGC ( port, "FCPORT %s attempting login\n", port->name );
1106
1107 /* Try to create FLOGI ELS */
1108 intf_restart ( &port->flogi, -ECANCELED );
1109 if ( ( rc = fc_els_flogi ( &port->flogi, port ) ) != 0 ) {
1110 DBGC ( port, "FCPORT %s could not initiate FLOGI: %s\n",
1111 port->name, strerror ( rc ) );
1112 fc_port_logout ( port, rc );
1113 return;
1114 }
1115 }
1116
1117 /**
1118 * Handle change of flow control window
1119 *
1120 * @v port Fibre Channel port
1121 */
1122 static void fc_port_window_changed ( struct fc_port *port ) {
1123 size_t window;
1124
1125 /* Check if transport layer is ready */
1126 window = xfer_window ( &port->transport );
1127 if ( window > 0 ) {
1128
1129 /* Transport layer is ready. Start login if the link
1130 * is not already up.
1131 */
1132 if ( ! fc_link_ok ( &port->link ) )
1133 fc_link_start ( &port->link );
1134
1135 } else {
1136
1137 /* Transport layer is not ready. Log out port and
1138 * wait for transport layer before attempting log in
1139 * again.
1140 */
1141 fc_port_logout ( port, -ENOTCONN );
1142 fc_link_stop ( &port->link );
1143 }
1144 }
1145
1146 /** Fibre Channel port transport interface operations */
1147 static struct interface_operation fc_port_transport_op[] = {
1148 INTF_OP ( xfer_deliver, struct fc_port *, fc_port_deliver ),
1149 INTF_OP ( xfer_window_changed, struct fc_port *,
1150 fc_port_window_changed ),
1151 INTF_OP ( intf_close, struct fc_port *, fc_port_close ),
1152 };
1153
1154 /** Fibre Channel port transport interface descriptor */
1155 static struct interface_descriptor fc_port_transport_desc =
1156 INTF_DESC ( struct fc_port, transport, fc_port_transport_op );
1157
1158 /** Fibre Channel port FLOGI interface operations */
1159 static struct interface_operation fc_port_flogi_op[] = {
1160 INTF_OP ( intf_close, struct fc_port *, fc_port_flogi_done ),
1161 };
1162
1163 /** Fibre Channel port FLOGI interface descriptor */
1164 static struct interface_descriptor fc_port_flogi_desc =
1165 INTF_DESC ( struct fc_port, flogi, fc_port_flogi_op );
1166
1167 /** Fibre Channel port name server PLOGI interface operations */
1168 static struct interface_operation fc_port_ns_plogi_op[] = {
1169 INTF_OP ( intf_close, struct fc_port *, fc_port_ns_plogi_done ),
1170 };
1171
1172 /** Fibre Channel port name server PLOGI interface descriptor */
1173 static struct interface_descriptor fc_port_ns_plogi_desc =
1174 INTF_DESC ( struct fc_port, ns_plogi, fc_port_ns_plogi_op );
1175
1176 /**
1177 * Create Fibre Channel port
1178 *
1179 * @v transport Transport interface
1180 * @v node Fibre Channel node name
1181 * @v port Fibre Channel port name
1182 * @v name Symbolic port name
1183 * @ret rc Return status code
1184 */
1185 int fc_port_open ( struct interface *transport, const struct fc_name *node_wwn,
1186 const struct fc_name *port_wwn, const char *name ) {
1187 struct fc_port *port;
1188
1189 /* Allocate and initialise structure */
1190 port = zalloc ( sizeof ( *port ) );
1191 if ( ! port )
1192 return -ENOMEM;
1193 ref_init ( &port->refcnt, NULL );
1194 intf_init ( &port->transport, &fc_port_transport_desc, &port->refcnt );
1195 fc_link_init ( &port->link, fc_port_examine, &port->refcnt );
1196 intf_init ( &port->flogi, &fc_port_flogi_desc, &port->refcnt );
1197 intf_init ( &port->ns_plogi, &fc_port_ns_plogi_desc, &port->refcnt );
1198 list_add_tail ( &port->list, &fc_ports );
1199 INIT_LIST_HEAD ( &port->xchgs );
1200 memcpy ( &port->node_wwn, node_wwn, sizeof ( port->node_wwn ) );
1201 memcpy ( &port->port_wwn, port_wwn, sizeof ( port->port_wwn ) );
1202 snprintf ( port->name, sizeof ( port->name ), "%s", name );
1203
1204 DBGC ( port, "FCPORT %s opened as %s",
1205 port->name, fc_ntoa ( &port->node_wwn ) );
1206 DBGC ( port, " port %s\n", fc_ntoa ( &port->port_wwn ) );
1207
1208 /* Attach to transport layer, mortalise self, and return */
1209 intf_plug_plug ( &port->transport, transport );
1210 ref_put ( &port->refcnt );
1211 return 0;
1212 }
1213
1214 /**
1215 * Find Fibre Channel port by name
1216 *
1217 * @v name Fibre Channel port name
1218 * @ret port Fibre Channel port, or NULL
1219 */
1220 struct fc_port * fc_port_find ( const char *name ) {
1221 struct fc_port *port;
1222
1223 list_for_each_entry ( port, &fc_ports, list ) {
1224 if ( strcmp ( name, port->name ) == 0 )
1225 return port;
1226 }
1227 return NULL;
1228 }
1229
1230 /******************************************************************************
1231 *
1232 * Fibre Channel peers
1233 *
1234 ******************************************************************************
1235 */
1236
1237 /**
1238 * Close Fibre Channel peer
1239 *
1240 * @v peer Fibre Channel peer
1241 * @v rc Reason for close
1242 */
1243 static void fc_peer_close ( struct fc_peer *peer, int rc ) {
1244
1245 DBGC ( peer, "FCPEER %s closed: %s\n",
1246 fc_ntoa ( &peer->port_wwn ) , strerror ( rc ) );
1247
1248 /* Sanity check */
1249 assert ( list_empty ( &peer->ulps ) );
1250
1251 /* Stop link timer */
1252 fc_link_stop ( &peer->link );
1253
1254 /* Shut down interfaces */
1255 intf_shutdown ( &peer->plogi, rc );
1256
1257 /* Remove from list of peers */
1258 list_del ( &peer->list );
1259 INIT_LIST_HEAD ( &peer->list );
1260 }
1261
1262 /**
1263 * Increment Fibre Channel peer active usage count
1264 *
1265 * @v peer Fibre Channel peer
1266 */
1267 static void fc_peer_increment ( struct fc_peer *peer ) {
1268
1269 /* Increment our usage count */
1270 peer->usage++;
1271 }
1272
1273 /**
1274 * Decrement Fibre Channel peer active usage count
1275 *
1276 * @v peer Fibre Channel peer
1277 */
1278 static void fc_peer_decrement ( struct fc_peer *peer ) {
1279
1280 /* Sanity check */
1281 assert ( peer->usage > 0 );
1282
1283 /* Decrement our usage count and log out if we reach zero */
1284 if ( --(peer->usage) == 0 )
1285 fc_peer_logout ( peer, 0 );
1286 }
1287
1288 /**
1289 * Log in Fibre Channel peer
1290 *
1291 * @v peer Fibre Channel peer
1292 * @v port Fibre Channel port
1293 * @v port_id Port ID
1294 * @ret rc Return status code
1295 */
1296 int fc_peer_login ( struct fc_peer *peer, struct fc_port *port,
1297 struct fc_port_id *port_id ) {
1298 struct fc_ulp *ulp;
1299 struct fc_ulp *tmp;
1300
1301 /* Perform implicit logout if logged in and details differ */
1302 if ( fc_link_ok ( &peer->link ) &&
1303 ( ( peer->port != port ) ||
1304 ( memcmp ( &peer->port_id, port_id,
1305 sizeof ( peer->port_id ) ) !=0 ) ) ) {
1306 fc_peer_logout ( peer, 0 );
1307 }
1308
1309 /* Log in, if applicable */
1310 if ( ! fc_link_ok ( &peer->link ) ) {
1311
1312 /* Record peer details */
1313 assert ( peer->port == NULL );
1314 peer->port = fc_port_get ( port );
1315 memcpy ( &peer->port_id, port_id, sizeof ( peer->port_id ) );
1316 DBGC ( peer, "FCPEER %s logged in via %s as %s\n",
1317 fc_ntoa ( &peer->port_wwn ), peer->port->name,
1318 fc_id_ntoa ( &peer->port_id ) );
1319
1320 /* Add login reference */
1321 fc_peer_get ( peer );
1322 }
1323
1324 /* Record login */
1325 fc_link_up ( &peer->link );
1326
1327 /* Notify ULPs of link state change */
1328 list_for_each_entry_safe ( ulp, tmp, &peer->ulps, list ) {
1329 fc_ulp_get ( ulp );
1330 fc_link_examine ( &ulp->link );
1331 fc_ulp_put ( ulp );
1332 }
1333
1334 return 0;
1335 }
1336
1337 /**
1338 * Log out Fibre Channel peer
1339 *
1340 * @v peer Fibre Channel peer
1341 * @v rc Reason for logout
1342 */
1343 void fc_peer_logout ( struct fc_peer *peer, int rc ) {
1344 struct fc_ulp *ulp;
1345 struct fc_ulp *tmp;
1346
1347 DBGC ( peer, "FCPEER %s logged out: %s\n",
1348 fc_ntoa ( &peer->port_wwn ), strerror ( rc ) );
1349
1350 /* Drop login reference, if applicable */
1351 if ( fc_link_ok ( &peer->link ) )
1352 fc_peer_put ( peer );
1353
1354 /* Erase peer details */
1355 fc_port_put ( peer->port );
1356 peer->port = NULL;
1357
1358 /* Record logout */
1359 fc_link_err ( &peer->link, rc );
1360
1361 /* Notify ULPs of link state change */
1362 list_for_each_entry_safe ( ulp, tmp, &peer->ulps, list ) {
1363 fc_ulp_get ( ulp );
1364 fc_link_examine ( &ulp->link );
1365 fc_ulp_put ( ulp );
1366 }
1367
1368 /* Close peer if there are no active users */
1369 if ( peer->usage == 0 )
1370 fc_peer_close ( peer, rc );
1371 }
1372
1373 /**
1374 * Handle PLOGI completion
1375 *
1376 * @v peer Fibre Channel peer
1377 * @v rc Reason for completion
1378 */
1379 static void fc_peer_plogi_done ( struct fc_peer *peer, int rc ) {
1380
1381 intf_restart ( &peer->plogi, rc );
1382
1383 if ( rc != 0 )
1384 fc_peer_logout ( peer, rc );
1385 }
1386
1387 /**
1388 * Initiate PLOGI
1389 *
1390 * @v peer Fibre Channel peer
1391 * @v port Fibre Channel port
1392 * @v peer_port_id Peer port ID
1393 * @ret rc Return status code
1394 */
1395 static int fc_peer_plogi ( struct fc_peer *peer, struct fc_port *port,
1396 struct fc_port_id *peer_port_id ) {
1397 int rc;
1398
1399 /* Try to create PLOGI ELS */
1400 intf_restart ( &peer->plogi, -ECANCELED );
1401 if ( ( rc = fc_els_plogi ( &peer->plogi, port, peer_port_id ) ) != 0 ) {
1402 DBGC ( peer, "FCPEER %s could not initiate PLOGI: %s\n",
1403 fc_ntoa ( &peer->port_wwn ), strerror ( rc ) );
1404 fc_peer_logout ( peer, rc );
1405 return rc;
1406 }
1407
1408 return 0;
1409 }
1410
1411 /**
1412 * Examine Fibre Channel peer link state
1413 *
1414 * @ link Fibre Channel link state monitor
1415 */
1416 static void fc_peer_examine ( struct fc_link_state *link ) {
1417 struct fc_peer *peer = container_of ( link, struct fc_peer, link );
1418 struct fc_port *port;
1419 int rc;
1420
1421 /* Check to see if underlying port link has gone down */
1422 if ( peer->port && ( ! fc_link_ok ( &peer->port->link ) ) ) {
1423 fc_peer_logout ( peer, -ENOTCONN );
1424 return;
1425 }
1426
1427 /* Do nothing if already logged in */
1428 if ( fc_link_ok ( &peer->link ) )
1429 return;
1430
1431 DBGC ( peer, "FCPEER %s attempting login\n",
1432 fc_ntoa ( &peer->port_wwn ) );
1433
1434 /* Sanity check */
1435 assert ( peer->port == NULL );
1436
1437 /* First, look for a port with the peer attached via a
1438 * point-to-point link.
1439 */
1440 list_for_each_entry ( port, &fc_ports, list ) {
1441 if ( fc_link_ok ( &port->link ) &&
1442 ( ! ( port->flags & FC_PORT_HAS_FABRIC ) ) &&
1443 ( memcmp ( &peer->port_wwn, &port->link_port_wwn,
1444 sizeof ( peer->port_wwn ) ) == 0 ) ) {
1445 /* Use this peer port ID, and stop looking */
1446 fc_peer_plogi ( peer, port, &port->ptp_link_port_id );
1447 return;
1448 }
1449 }
1450
1451 /* If the peer is not directly attached, try initiating a name
1452 * server lookup on any suitable ports.
1453 */
1454 list_for_each_entry ( port, &fc_ports, list ) {
1455 if ( fc_link_ok ( &port->link ) &&
1456 ( port->flags & FC_PORT_HAS_FABRIC ) &&
1457 ( port->flags & FC_PORT_HAS_NS ) ) {
1458 if ( ( rc = fc_ns_query ( peer, port,
1459 fc_peer_plogi ) ) != 0 ) {
1460 DBGC ( peer, "FCPEER %s could not attempt "
1461 "name server lookup on %s: %s\n",
1462 fc_ntoa ( &peer->port_wwn ), port->name,
1463 strerror ( rc ) );
1464 /* Non-fatal */
1465 }
1466 }
1467 }
1468 }
1469
1470 /** Fibre Channel peer PLOGI interface operations */
1471 static struct interface_operation fc_peer_plogi_op[] = {
1472 INTF_OP ( intf_close, struct fc_peer *, fc_peer_plogi_done ),
1473 };
1474
1475 /** Fibre Channel peer PLOGI interface descriptor */
1476 static struct interface_descriptor fc_peer_plogi_desc =
1477 INTF_DESC ( struct fc_peer, plogi, fc_peer_plogi_op );
1478
1479 /**
1480 * Create Fibre Channel peer
1481 *
1482 * @v port_wwn Node name
1483 * @ret peer Fibre Channel peer, or NULL
1484 */
1485 static struct fc_peer * fc_peer_create ( const struct fc_name *port_wwn ) {
1486 struct fc_peer *peer;
1487
1488 /* Allocate and initialise structure */
1489 peer = zalloc ( sizeof ( *peer ) );
1490 if ( ! peer )
1491 return NULL;
1492 ref_init ( &peer->refcnt, NULL );
1493 fc_link_init ( &peer->link, fc_peer_examine, &peer->refcnt );
1494 intf_init ( &peer->plogi, &fc_peer_plogi_desc, &peer->refcnt );
1495 list_add_tail ( &peer->list, &fc_peers );
1496 memcpy ( &peer->port_wwn, port_wwn, sizeof ( peer->port_wwn ) );
1497 INIT_LIST_HEAD ( &peer->ulps );
1498
1499 /* Start link monitor */
1500 fc_link_start ( &peer->link );
1501
1502 DBGC ( peer, "FCPEER %s created\n", fc_ntoa ( &peer->port_wwn ) );
1503 return peer;
1504 }
1505
1506 /**
1507 * Get Fibre Channel peer by node name
1508 *
1509 * @v port_wwn Node name
1510 * @ret peer Fibre Channel peer, or NULL
1511 */
1512 struct fc_peer * fc_peer_get_wwn ( const struct fc_name *port_wwn ) {
1513 struct fc_peer *peer;
1514
1515 /* Look for an existing peer */
1516 list_for_each_entry ( peer, &fc_peers, list ) {
1517 if ( memcmp ( &peer->port_wwn, port_wwn,
1518 sizeof ( peer->port_wwn ) ) == 0 )
1519 return fc_peer_get ( peer );
1520 }
1521
1522 /* Create a new peer */
1523 peer = fc_peer_create ( port_wwn );
1524 if ( ! peer )
1525 return NULL;
1526
1527 return peer;
1528 }
1529
1530 /**
1531 * Get Fibre Channel peer by port ID
1532 *
1533 * @v port Fibre Channel port
1534 * @v peer_port_id Peer port ID
1535 * @ret peer Fibre Channel peer, or NULL
1536 */
1537 struct fc_peer * fc_peer_get_port_id ( struct fc_port *port,
1538 const struct fc_port_id *peer_port_id ){
1539 struct fc_peer *peer;
1540
1541 /* Look for an existing peer */
1542 list_for_each_entry ( peer, &fc_peers, list ) {
1543 if ( ( peer->port == port ) &&
1544 ( memcmp ( &peer->port_id, peer_port_id,
1545 sizeof ( peer->port_id ) ) == 0 ) )
1546 return fc_peer_get ( peer );
1547 }
1548
1549 /* Cannot create a new peer, since we have no port name to use */
1550 return NULL;
1551 }
1552
1553 /******************************************************************************
1554 *
1555 * Fibre Channel upper-layer protocols
1556 *
1557 ******************************************************************************
1558 */
1559
1560 /**
1561 * Free Fibre Channel upper-layer protocol
1562 *
1563 * @v refcnt Reference count
1564 */
1565 static void fc_ulp_free ( struct refcnt *refcnt ) {
1566 struct fc_ulp *ulp = container_of ( refcnt, struct fc_ulp, refcnt );
1567
1568 fc_peer_put ( ulp->peer );
1569 free ( ulp );
1570 }
1571
1572 /**
1573 * Close Fibre Channel upper-layer protocol
1574 *
1575 * @v ulp Fibre Channel upper-layer protocol
1576 * @v rc Reason for close
1577 */
1578 static void fc_ulp_close ( struct fc_ulp *ulp, int rc ) {
1579
1580 DBGC ( ulp, "FCULP %s/%02x closed: %s\n",
1581 fc_ntoa ( &ulp->peer->port_wwn ), ulp->type, strerror ( rc ) );
1582
1583 /* Sanity check */
1584 assert ( list_empty ( &ulp->users ) );
1585
1586 /* Stop link monitor */
1587 fc_link_stop ( &ulp->link );
1588
1589 /* Shut down interfaces */
1590 intf_shutdown ( &ulp->prli, rc );
1591
1592 /* Remove from list of ULPs */
1593 list_del ( &ulp->list );
1594 INIT_LIST_HEAD ( &ulp->list );
1595 }
1596
1597 /**
1598 * Attach Fibre Channel upper-layer protocol user
1599 *
1600 * @v ulp Fibre Channel upper-layer protocol
1601 * @v user Fibre Channel upper-layer protocol user
1602 */
1603 void fc_ulp_attach ( struct fc_ulp *ulp, struct fc_ulp_user *user ) {
1604
1605 /* Sanity check */
1606 assert ( user->ulp == NULL );
1607
1608 /* Increment peer's usage count */
1609 fc_peer_increment ( ulp->peer );
1610
1611 /* Attach user */
1612 user->ulp = fc_ulp_get ( ulp );
1613 list_add ( &user->list, &ulp->users );
1614 }
1615
1616 /**
1617 * Detach Fibre Channel upper-layer protocol user
1618 *
1619 * @v user Fibre Channel upper-layer protocol user
1620 */
1621 void fc_ulp_detach ( struct fc_ulp_user *user ) {
1622 struct fc_ulp *ulp = user->ulp;
1623
1624 /* Do nothing if not attached */
1625 if ( ! ulp )
1626 return;
1627
1628 /* Sanity checks */
1629 list_check_contains_entry ( user, &ulp->users, list );
1630
1631 /* Detach user and log out if no users remain */
1632 list_del ( &user->list );
1633 if ( list_empty ( &ulp->users ) )
1634 fc_ulp_logout ( ulp, 0 );
1635
1636 /* Decrement our peer's usage count */
1637 fc_peer_decrement ( ulp->peer );
1638
1639 /* Drop reference */
1640 user->ulp = NULL;
1641 fc_ulp_put ( ulp );
1642 }
1643
1644 /**
1645 * Log in Fibre Channel upper-layer protocol
1646 *
1647 * @v ulp Fibre Channel upper-layer protocol
1648 * @v param Service parameters
1649 * @v param_len Length of service parameters
1650 * @v originated Login was originated by us
1651 * @ret rc Return status code
1652 */
1653 int fc_ulp_login ( struct fc_ulp *ulp, const void *param, size_t param_len,
1654 int originated ) {
1655 struct fc_ulp_user *user;
1656 struct fc_ulp_user *tmp;
1657
1658 /* Perform implicit logout if logged in and service parameters differ */
1659 if ( fc_link_ok ( &ulp->link ) &&
1660 ( ( ulp->param_len != param_len ) ||
1661 ( memcmp ( ulp->param, param, ulp->param_len ) != 0 ) ) ) {
1662 fc_ulp_logout ( ulp, 0 );
1663 }
1664
1665 /* Work around a bug in some versions of the Linux Fibre
1666 * Channel stack, which fail to fully initialise image pairs
1667 * established via a PRLI originated by the Linux stack
1668 * itself.
1669 */
1670 if ( originated )
1671 ulp->flags |= FC_ULP_ORIGINATED_LOGIN_OK;
1672 if ( ! ( ulp->flags & FC_ULP_ORIGINATED_LOGIN_OK ) ) {
1673 DBGC ( ulp, "FCULP %s/%02x sending extra PRLI to work around "
1674 "Linux bug\n",
1675 fc_ntoa ( &ulp->peer->port_wwn ), ulp->type );
1676 fc_link_stop ( &ulp->link );
1677 fc_link_start ( &ulp->link );
1678 return 0;
1679 }
1680
1681 /* Log in, if applicable */
1682 if ( ! fc_link_ok ( &ulp->link ) ) {
1683
1684 /* Record service parameters */
1685 assert ( ulp->param == NULL );
1686 assert ( ulp->param_len == 0 );
1687 ulp->param = malloc ( param_len );
1688 if ( ! ulp->param ) {
1689 DBGC ( ulp, "FCULP %s/%02x could not record "
1690 "parameters\n",
1691 fc_ntoa ( &ulp->peer->port_wwn ), ulp->type );
1692 return -ENOMEM;
1693 }
1694 memcpy ( ulp->param, param, param_len );
1695 ulp->param_len = param_len;
1696 DBGC ( ulp, "FCULP %s/%02x logged in with parameters:\n",
1697 fc_ntoa ( &ulp->peer->port_wwn ), ulp->type );
1698 DBGC_HDA ( ulp, 0, ulp->param, ulp->param_len );
1699
1700 /* Add login reference */
1701 fc_ulp_get ( ulp );
1702 }
1703
1704 /* Record login */
1705 fc_link_up ( &ulp->link );
1706
1707 /* Notify users of link state change */
1708 list_for_each_entry_safe ( user, tmp, &ulp->users, list ) {
1709 fc_ulp_user_get ( user );
1710 user->examine ( user );
1711 fc_ulp_user_put ( user );
1712 }
1713
1714 return 0;
1715 }
1716
1717 /**
1718 * Log out Fibre Channel upper-layer protocol
1719 *
1720 * @v ulp Fibre Channel upper-layer protocol
1721 * @v rc Reason for logout
1722 */
1723 void fc_ulp_logout ( struct fc_ulp *ulp, int rc ) {
1724 struct fc_ulp_user *user;
1725 struct fc_ulp_user *tmp;
1726
1727 DBGC ( ulp, "FCULP %s/%02x logged out: %s\n",
1728 fc_ntoa ( &ulp->peer->port_wwn ), ulp->type, strerror ( rc ) );
1729
1730 /* Drop login reference, if applicable */
1731 if ( fc_link_ok ( &ulp->link ) )
1732 fc_ulp_put ( ulp );
1733
1734 /* Discard service parameters */
1735 free ( ulp->param );
1736 ulp->param = NULL;
1737 ulp->param_len = 0;
1738 ulp->flags = 0;
1739
1740 /* Record logout */
1741 fc_link_err ( &ulp->link, rc );
1742
1743 /* Notify users of link state change */
1744 list_for_each_entry_safe ( user, tmp, &ulp->users, list ) {
1745 fc_ulp_user_get ( user );
1746 user->examine ( user );
1747 fc_ulp_user_put ( user );
1748 }
1749
1750 /* Close ULP if there are no clients attached */
1751 if ( list_empty ( &ulp->users ) )
1752 fc_ulp_close ( ulp, rc );
1753 }
1754
1755 /**
1756 * Handle PRLI completion
1757 *
1758 * @v ulp Fibre Channel upper-layer protocol
1759 * @v rc Reason for completion
1760 */
1761 static void fc_ulp_prli_done ( struct fc_ulp *ulp, int rc ) {
1762
1763 intf_restart ( &ulp->prli, rc );
1764
1765 if ( rc != 0 )
1766 fc_ulp_logout ( ulp, rc );
1767 }
1768
1769 /**
1770 * Examine Fibre Channel upper-layer protocol link state
1771 *
1772 * @ link Fibre Channel link state monitor
1773 */
1774 static void fc_ulp_examine ( struct fc_link_state *link ) {
1775 struct fc_ulp *ulp = container_of ( link, struct fc_ulp, link );
1776 int rc;
1777
1778 /* Check to see if underlying peer link has gone down */
1779 if ( ! fc_link_ok ( &ulp->peer->link ) ) {
1780 fc_ulp_logout ( ulp, -ENOTCONN );
1781 return;
1782 }
1783
1784 /* Do nothing if already logged in */
1785 if ( fc_link_ok ( &ulp->link ) &&
1786 ( ulp->flags & FC_ULP_ORIGINATED_LOGIN_OK ) )
1787 return;
1788
1789 DBGC ( ulp, "FCULP %s/%02x attempting login\n",
1790 fc_ntoa ( &ulp->peer->port_wwn ), ulp->type );
1791
1792 /* Try to create PRLI ELS */
1793 intf_restart ( &ulp->prli, -ECANCELED );
1794 if ( ( rc = fc_els_prli ( &ulp->prli, ulp->peer->port,
1795 &ulp->peer->port_id, ulp->type ) ) != 0 ) {
1796 DBGC ( ulp, "FCULP %s/%02x could not initiate PRLI: %s\n",
1797 fc_ntoa ( &ulp->peer->port_wwn ), ulp->type,
1798 strerror ( rc ) );
1799 fc_ulp_logout ( ulp, rc );
1800 return;
1801 }
1802 }
1803
1804 /** Fibre Channel upper-layer protocol PRLI interface operations */
1805 static struct interface_operation fc_ulp_prli_op[] = {
1806 INTF_OP ( intf_close, struct fc_ulp *, fc_ulp_prli_done ),
1807 };
1808
1809 /** Fibre Channel upper-layer protocol PRLI interface descriptor */
1810 static struct interface_descriptor fc_ulp_prli_desc =
1811 INTF_DESC ( struct fc_ulp, prli, fc_ulp_prli_op );
1812
1813 /**
1814 * Create Fibre Channel upper-layer protocl
1815 *
1816 * @v peer Fibre Channel peer
1817 * @v type Type
1818 * @ret ulp Fibre Channel upper-layer protocol, or NULL
1819 */
1820 static struct fc_ulp * fc_ulp_create ( struct fc_peer *peer,
1821 unsigned int type ) {
1822 struct fc_ulp *ulp;
1823
1824 /* Allocate and initialise structure */
1825 ulp = zalloc ( sizeof ( *ulp ) );
1826 if ( ! ulp )
1827 return NULL;
1828 ref_init ( &ulp->refcnt, fc_ulp_free );
1829 fc_link_init ( &ulp->link, fc_ulp_examine, &ulp->refcnt );
1830 intf_init ( &ulp->prli, &fc_ulp_prli_desc, &ulp->refcnt );
1831 ulp->peer = fc_peer_get ( peer );
1832 list_add_tail ( &ulp->list, &peer->ulps );
1833 ulp->type = type;
1834 INIT_LIST_HEAD ( &ulp->users );
1835
1836 /* Start link state monitor */
1837 fc_link_start ( &ulp->link );
1838
1839 DBGC ( ulp, "FCULP %s/%02x created\n",
1840 fc_ntoa ( &ulp->peer->port_wwn ), ulp->type );
1841 return ulp;
1842 }
1843
1844 /**
1845 * Get Fibre Channel upper-layer protocol by peer and type
1846 *
1847 * @v peer Fibre Channel peer
1848 * @v type Type
1849 * @ret ulp Fibre Channel upper-layer protocol, or NULL
1850 */
1851 static struct fc_ulp * fc_ulp_get_type ( struct fc_peer *peer,
1852 unsigned int type ) {
1853 struct fc_ulp *ulp;
1854
1855 /* Look for an existing ULP */
1856 list_for_each_entry ( ulp, &peer->ulps, list ) {
1857 if ( ulp->type == type )
1858 return fc_ulp_get ( ulp );
1859 }
1860
1861 /* Create a new ULP */
1862 ulp = fc_ulp_create ( peer, type );
1863 if ( ! ulp )
1864 return NULL;
1865
1866 return ulp;
1867 }
1868
1869 /**
1870 * Get Fibre Channel upper-layer protocol by port name and type
1871 *
1872 * @v port_wwn Port name
1873 * @v type Type
1874 * @ret ulp Fibre Channel upper-layer protocol, or NULL
1875 */
1876 struct fc_ulp * fc_ulp_get_wwn_type ( const struct fc_name *port_wwn,
1877 unsigned int type ) {
1878 struct fc_ulp *ulp;
1879 struct fc_peer *peer;
1880
1881 /* Get peer */
1882 peer = fc_peer_get_wwn ( port_wwn );
1883 if ( ! peer )
1884 goto err_peer_get_wwn;
1885
1886 /* Get ULP */
1887 ulp = fc_ulp_get_type ( peer, type );
1888 if ( ! ulp )
1889 goto err_ulp_get_type;
1890
1891 /* Drop temporary reference to peer */
1892 fc_peer_put ( peer );
1893
1894 return ulp;
1895
1896 fc_ulp_put ( ulp );
1897 err_ulp_get_type:
1898 fc_peer_put ( peer );
1899 err_peer_get_wwn:
1900 return NULL;
1901 }
1902
1903 /**
1904 * Get Fibre Channel upper-layer protocol by port ID and type
1905 *
1906 * @v port Fibre Channel port
1907 * @v peer_port_id Peer port ID
1908 * @v type Type
1909 * @ret ulp Fibre Channel upper-layer protocol, or NULL
1910 */
1911 struct fc_ulp * fc_ulp_get_port_id_type ( struct fc_port *port,
1912 const struct fc_port_id *peer_port_id,
1913 unsigned int type ) {
1914 struct fc_ulp *ulp;
1915 struct fc_peer *peer;
1916
1917 /* Get peer */
1918 peer = fc_peer_get_port_id ( port, peer_port_id );
1919 if ( ! peer )
1920 goto err_peer_get_wwn;
1921
1922 /* Get ULP */
1923 ulp = fc_ulp_get_type ( peer, type );
1924 if ( ! ulp )
1925 goto err_ulp_get_type;
1926
1927 /* Drop temporary reference to peer */
1928 fc_peer_put ( peer );
1929
1930 return ulp;
1931
1932 fc_ulp_put ( ulp );
1933 err_ulp_get_type:
1934 fc_peer_put ( peer );
1935 err_peer_get_wwn:
1936 return NULL;
1937 }