[infiniband] Use "%#lx" as format specifier for queue pair numbers
[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 * 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.
22 */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 #include <stddef.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdio.h>
30 #include <errno.h>
31 #include <assert.h>
32 #include <byteswap.h>
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>
41 #include <ipxe/fc.h>
42 #include <ipxe/fcels.h>
43 #include <ipxe/fcns.h>
44
45 /** @file
46 *
47 * Fibre Channel
48 *
49 */
50
51 /** List of Fibre Channel ports */
52 LIST_HEAD ( fc_ports );
53
54 /** List of Fibre Channel peers */
55 LIST_HEAD ( fc_peers );
56
57 /******************************************************************************
58 *
59 * Well-known addresses
60 *
61 ******************************************************************************
62 */
63
64 /** Unassigned port ID */
65 struct fc_port_id fc_empty_port_id = { .bytes = { 0x00, 0x00, 0x00 } };
66
67 /** F_Port contoller port ID */
68 struct fc_port_id fc_f_port_id = { .bytes = { 0xff, 0xff, 0xfe } };
69
70 /** Generic services port ID */
71 struct fc_port_id fc_gs_port_id = { .bytes = { 0xff, 0xff, 0xfc } };
72
73 /** Point-to-point low port ID */
74 struct fc_port_id fc_ptp_low_port_id = { .bytes = { 0x01, 0x01, 0x01 } };
75
76 /** Point-to-point high port ID */
77 struct fc_port_id fc_ptp_high_port_id = { .bytes = { 0x01, 0x01, 0x02 } };
78
79 /******************************************************************************
80 *
81 * Utility functions
82 *
83 ******************************************************************************
84 */
85
86 /**
87 * Format Fibre Channel port ID
88 *
89 * @v id Fibre Channel port ID
90 * @ret id_text Port ID text
91 */
92 const char * fc_id_ntoa ( const struct fc_port_id *id ) {
93 static char id_text[ FC_PORT_ID_STRLEN + 1 /* NUL */ ];
94
95 snprintf ( id_text, sizeof ( id_text ), "%02x.%02x.%02x",
96 id->bytes[0], id->bytes[1], id->bytes[2] );
97 return id_text;
98 }
99
100 /**
101 * Parse Fibre Channel port ID
102 *
103 * @v id_text Port ID text
104 * @ret id Fibre Channel port ID
105 * @ret rc Return status code
106 */
107 int fc_id_aton ( const char *id_text, struct fc_port_id *id ) {
108 char *ptr = ( ( char * ) id_text );
109 unsigned int i = 0;
110
111 while ( 1 ) {
112 id->bytes[i++] = strtoul ( ptr, &ptr, 16 );
113 if ( i == sizeof ( id->bytes ) )
114 return ( ( *ptr == '\0' ) ? 0 : -EINVAL );
115 if ( *ptr != '.' )
116 return -EINVAL;
117 ptr++;
118 }
119 }
120
121 /**
122 * Format Fibre Channel WWN
123 *
124 * @v wwn Fibre Channel WWN
125 * @ret wwn_text WWN text
126 */
127 const char * fc_ntoa ( const struct fc_name *wwn ) {
128 static char wwn_text[ FC_NAME_STRLEN + 1 /* NUL */ ];
129
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] );
134 return wwn_text;
135 }
136
137 /**
138 * Parse Fibre Channel WWN
139 *
140 * @v wwn_text WWN text
141 * @ret wwn Fibre Channel WWN
142 * @ret rc Return status code
143 */
144 int fc_aton ( const char *wwn_text, struct fc_name *wwn ) {
145 char *ptr = ( ( char * ) wwn_text );
146 unsigned int i = 0;
147
148 while ( 1 ) {
149 wwn->bytes[i++] = strtoul ( ptr, &ptr, 16 );
150 if ( i == sizeof ( wwn->bytes ) )
151 return ( ( *ptr == '\0' ) ? 0 : -EINVAL );
152 if ( *ptr != ':' )
153 return -EINVAL;
154 ptr++;
155 }
156 }
157
158 /**
159 * Fill Fibre Channel socket address
160 *
161 * @v sa_fc Fibre Channel socket address to fill in
162 * @v id Fibre Channel port ID
163 * @ret sa Socket address
164 */
165 struct sockaddr * fc_fill_sockaddr ( struct sockaddr_fc *sa_fc,
166 struct fc_port_id *id ) {
167 union {
168 struct sockaddr sa;
169 struct sockaddr_fc fc;
170 } *u = container_of ( sa_fc, typeof ( *u ), fc );
171
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 ) );
175 return &u->sa;
176 }
177
178 /******************************************************************************
179 *
180 * Fibre Channel link state
181 *
182 ******************************************************************************
183 */
184
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" )
189
190 /**
191 * Mark Fibre Channel link as up
192 *
193 * @v link Fibre Channel link state monitor
194 */
195 static void fc_link_up ( struct fc_link_state *link ) {
196
197 /* Stop retry timer */
198 stop_timer ( &link->timer );
199
200 /* Record link state */
201 link->rc = 0;
202 }
203
204 /**
205 * Mark Fibre Channel link as down
206 *
207 * @v link Fibre Channel link state monitor
208 * @v rc Link state
209 */
210 static void fc_link_err ( struct fc_link_state *link, int rc ) {
211
212 /* Record link state */
213 if ( rc == 0 )
214 rc = -EUNKNOWN_LINK_STATUS;
215 link->rc = rc;
216
217 /* Schedule another link examination */
218 start_timer_fixed ( &link->timer, FC_LINK_RETRY_DELAY );
219 }
220
221 /**
222 * Examine Fibre Channel link state
223 *
224 * @v link Fibre Channel link state monitor
225 */
226 static void fc_link_examine ( struct fc_link_state *link ) {
227
228 link->examine ( link );
229 }
230
231 /**
232 * Handle Fibre Channel link retry timer expiry
233 */
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 );
237
238 /* Schedule another link examination */
239 start_timer_fixed ( &link->timer, FC_LINK_RETRY_DELAY );
240
241 /* Examine link */
242 fc_link_examine ( link );
243 }
244
245 /**
246 * Initialise Fibre Channel link state monitor
247 *
248 * @v link Fibre Channel link state monitor
249 * @v examine Examine link state method
250 * @v refcnt Reference counter
251 */
252 static void fc_link_init ( struct fc_link_state *link,
253 void ( * examine ) ( struct fc_link_state *link ),
254 struct refcnt *refcnt ) {
255
256 link->rc = -EUNKNOWN_LINK_STATUS;
257 timer_init ( &link->timer, fc_link_expired, refcnt );
258 link->examine = examine;
259 }
260
261 /**
262 * Start monitoring Fibre Channel link state
263 *
264 * @v link Fibre Channel link state monitor
265 */
266 static void fc_link_start ( struct fc_link_state *link ) {
267 start_timer_nodelay ( &link->timer );
268 }
269
270 /**
271 * Stop monitoring Fibre Channel link state
272 *
273 * @v link Fibre Channel link state monitor
274 */
275 static void fc_link_stop ( struct fc_link_state *link ) {
276 stop_timer ( &link->timer );
277 }
278
279 /******************************************************************************
280 *
281 * Fibre Channel exchanges
282 *
283 ******************************************************************************
284 */
285
286 /** A Fibre Channel exchange */
287 struct fc_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;
294
295 /** Peer port ID */
296 struct fc_port_id peer_port_id;
297 /** Data structure type */
298 unsigned int type;
299 /** Flags */
300 unsigned int flags;
301 /** Local exchange ID */
302 uint16_t xchg_id;
303 /** Peer exchange ID */
304 uint16_t peer_xchg_id;
305 /** Active sequence ID */
306 uint8_t seq_id;
307 /** Active sequence count */
308 uint16_t seq_cnt;
309
310 /** Timeout timer */
311 struct retry_timer timer;
312
313 /** Upper-layer protocol interface */
314 struct interface ulp;
315 };
316
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,
325 };
326
327 /** Fibre Channel timeout */
328 #define FC_TIMEOUT ( 1 * TICKS_PER_SEC )
329
330 /**
331 * Create local Fibre Channel exchange identifier
332 *
333 * @ret xchg_id Local exchange ID
334 */
335 static unsigned int fc_new_xchg_id ( void ) {
336 static uint16_t next_id = 0x0000;
337
338 /* We must avoid using FC_RX_ID_UNKNOWN (0xffff) */
339 next_id += 2;
340 return next_id;
341 }
342
343 /**
344 * Create local Fibre Channel sequence identifier
345 *
346 * @ret seq_id Local sequence identifier
347 */
348 static unsigned int fc_new_seq_id ( void ) {
349 static uint8_t seq_id = 0x00;
350
351 return (++seq_id);
352 }
353
354 /**
355 * Free Fibre Channel exchange
356 *
357 * @v refcnt Reference count
358 */
359 static void fc_xchg_free ( struct refcnt *refcnt ) {
360 struct fc_exchange *xchg =
361 container_of ( refcnt, struct fc_exchange, refcnt );
362
363 assert ( ! timer_running ( &xchg->timer ) );
364 assert ( list_empty ( &xchg->list ) );
365
366 fc_port_put ( xchg->port );
367 free ( xchg );
368 }
369
370 /**
371 * Close Fibre Channel exchange
372 *
373 * @v xchg Fibre Channel exchange
374 * @v rc Reason for close
375 */
376 static void fc_xchg_close ( struct fc_exchange *xchg, int rc ) {
377 struct fc_port *port = xchg->port;
378
379 if ( rc != 0 ) {
380 DBGC2 ( port, "FCXCHG %s/%04x closed: %s\n",
381 port->name, xchg->xchg_id, strerror ( rc ) );
382 }
383
384 /* Stop timer */
385 stop_timer ( &xchg->timer );
386
387 /* If list still holds a reference, remove from list of open
388 * exchanges and drop list's reference.
389 */
390 if ( ! list_empty ( &xchg->list ) ) {
391 list_del ( &xchg->list );
392 INIT_LIST_HEAD ( &xchg->list );
393 ref_put ( &xchg->refcnt );
394 }
395
396 /* Shutdown interfaces */
397 intf_shutdown ( &xchg->ulp, rc );
398 }
399
400 /**
401 * Handle exchange timeout
402 *
403 * @v timer Timeout timer
404 * @v over Failure indicator
405 */
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;
410
411 DBGC ( port, "FCXCHG %s/%04x timed out\n", port->name, xchg->xchg_id );
412
413 /* Terminate the exchange */
414 fc_xchg_close ( xchg, -ETIMEDOUT );
415 }
416
417 /**
418 * Check Fibre Channel exchange window
419 *
420 * @v xchg Fibre Channel exchange
421 * @ret len Length opf window
422 */
423 static size_t fc_xchg_window ( struct fc_exchange *xchg __unused ) {
424
425 /* We don't currently store the path MTU */
426 return FC_LOGIN_DEFAULT_MTU;
427 }
428
429 /**
430 * Allocate Fibre Channel I/O buffer
431 *
432 * @v xchg Fibre Channel exchange
433 * @v len Payload length
434 * @ret iobuf I/O buffer, or NULL
435 */
436 static struct io_buffer * fc_xchg_alloc_iob ( struct fc_exchange *xchg,
437 size_t len ) {
438 struct fc_port *port = xchg->port;
439 struct io_buffer *iobuf;
440
441 iobuf = xfer_alloc_iob ( &port->transport,
442 ( sizeof ( struct fc_frame_header ) + len ) );
443 if ( iobuf ) {
444 iob_reserve ( iobuf, sizeof ( struct fc_frame_header ) );
445 }
446 return iobuf;
447 }
448
449 /**
450 * Transmit data as part of a Fibre Channel exchange
451 *
452 * @v xchg Fibre Channel exchange
453 * @v iobuf I/O buffer
454 * @v meta Data transfer metadata
455 * @ret rc Return status code
456 */
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;
462 unsigned int r_ctl;
463 unsigned int f_ctl_es;
464 int rc;
465
466 /* Sanity checks */
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 );
471 rc = -EBUSY;
472 goto done;
473 }
474
475 /* Calculate routing control */
476 switch ( xchg->type ) {
477 case FC_TYPE_ELS:
478 r_ctl = FC_R_CTL_ELS;
479 if ( meta->flags & XFER_FL_RESPONSE ) {
480 r_ctl |= FC_R_CTL_SOL_CTRL;
481 } else {
482 r_ctl |= FC_R_CTL_UNSOL_CTRL;
483 }
484 break;
485 case FC_TYPE_CT:
486 r_ctl = FC_R_CTL_DATA;
487 if ( meta->flags & XFER_FL_RESPONSE ) {
488 r_ctl |= FC_R_CTL_SOL_CTRL;
489 } else {
490 r_ctl |= FC_R_CTL_UNSOL_CTRL;
491 }
492 break;
493 default:
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;
499 break;
500 case ( XFER_FL_CMD_STAT ):
501 r_ctl |= FC_R_CTL_UNSOL_CMD;
502 break;
503 case ( XFER_FL_RESPONSE ):
504 r_ctl |= FC_R_CTL_SOL_DATA;
505 break;
506 default:
507 r_ctl |= FC_R_CTL_UNSOL_DATA;
508 break;
509 }
510 break;
511 }
512
513 /* Calculate exchange and sequence control */
514 f_ctl_es = 0;
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 );
523
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 );
543 }
544
545 /* Relinquish sequence initiative if applicable */
546 if ( meta->flags & XFER_FL_OVER ) {
547 xchg->flags &= ~( FC_XCHG_SEQ_INITIATIVE | FC_XCHG_SEQ_FIRST );
548 xchg->seq_cnt = 0;
549 }
550
551 /* Reset timeout */
552 start_timer_fixed ( &xchg->timer, FC_TIMEOUT );
553
554 /* Deliver frame */
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 ) );
559 goto done;
560 }
561
562 done:
563 free_iob ( iobuf );
564 return rc;
565 }
566
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 ),
577 };
578
579 /**
580 * Receive data as part of a Fibre Channel exchange
581 *
582 * @v xchg Fibre Channel exchange
583 * @v iobuf I/O buffer
584 * @v meta Data transfer metadata
585 * @ret rc Return status code
586 */
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;
594 int rc;
595
596 /* Record peer exchange ID */
597 xchg->peer_xchg_id =
598 ntohs ( ( fchdr->f_ctl_es & FC_F_CTL_ES_RESPONDER ) ?
599 fchdr->rx_id : fchdr->ox_id );
600
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 );
605 rc = -EBUSY;
606 goto done;
607 }
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 );
612 rc = -EPIPE;
613 goto done;
614 }
615 if ( xchg->seq_cnt == 0 )
616 xchg->seq_id = fchdr->seq_id;
617 xchg->seq_cnt++;
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 );
622 rc = -EPIPE;
623 goto done;
624 }
625
626 /* Check for end of sequence and transfer of sequence initiative */
627 if ( fchdr->f_ctl_es & FC_F_CTL_ES_END ) {
628 xchg->seq_cnt = 0;
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();
632 }
633 }
634
635 /* Construct metadata */
636 memset ( &fc_meta, 0, sizeof ( fc_meta ) );
637 fc_meta.flags =
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;
641 }
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;
645 }
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 );
649 }
650 fc_meta.src = fc_fill_sockaddr ( &src, &fchdr->s_id );
651 fc_meta.dest = fc_fill_sockaddr ( &dest, &fchdr->d_id );
652
653 /* Reset timeout */
654 start_timer_fixed ( &xchg->timer, FC_TIMEOUT );
655
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 ) );
662 goto done;
663 }
664
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 );
669 }
670
671 done:
672 free_iob ( iobuf );
673 return rc;
674 }
675
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 ),
682 };
683
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 );
687
688 /**
689 * Create new Fibre Channel exchange
690 *
691 * @v port Fibre Channel port
692 * @v peer_port_id Peer port ID
693 * @ret xchg Exchange, or NULL
694 */
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;
699
700 /* Allocate and initialise structure */
701 xchg = zalloc ( sizeof ( *xchg ) );
702 if ( ! xchg )
703 return NULL;
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 ) );
710 xchg->type = type;
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();
714
715 /* Transfer reference to list of exchanges and return */
716 list_add ( &xchg->list, &port->xchgs );
717 return xchg;
718 }
719
720 /**
721 * Originate a new Fibre Channel exchange
722 *
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
727 */
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;
731
732 /* Allocate and initialise structure */
733 xchg = fc_xchg_create ( port, peer_port_id, type );
734 if ( ! xchg )
735 return -ENOMEM;
736 xchg->flags = ( FC_XCHG_ORIGINATOR | FC_XCHG_SEQ_INITIATIVE |
737 FC_XCHG_SEQ_FIRST );
738
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 ),
741 xchg->type );
742
743 /* Attach to parent interface and return */
744 intf_plug_plug ( &xchg->ulp, parent );
745 return xchg->xchg_id;
746 }
747
748 /**
749 * Open a new responder Fibre Channel exchange
750 *
751 * @v port Fibre Channel port
752 * @v fchdr Fibre Channel frame header
753 * @ret xchg Fibre Channel exchange, or NULL
754 */
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;
760 int rc;
761
762 /* Allocate and initialise structure */
763 xchg = fc_xchg_create ( port, &fchdr->s_id, type );
764 if ( ! xchg )
765 return NULL;
766 xchg->seq_id = fchdr->seq_id;
767
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 );
772
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,
777 &fchdr->d_id,
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 ) );
782 }
783 }
784 break;
785 }
786
787 /* We may or may not have a ULP attached at this point, but
788 * the exchange does exist.
789 */
790 return xchg;
791 }
792
793 /******************************************************************************
794 *
795 * Fibre Channel ports
796 *
797 ******************************************************************************
798 */
799
800 /**
801 * Close Fibre Channel port
802 *
803 * @v port Fibre Channel port
804 * @v rc Reason for close
805 */
806 static void fc_port_close ( struct fc_port *port, int rc ) {
807 struct fc_exchange *xchg;
808 struct fc_exchange *tmp;
809
810 DBGC ( port, "FCPORT %s closed\n", port->name );
811
812 /* Log out port, if necessary */
813 if ( fc_link_ok ( &port->link ) )
814 fc_port_logout ( port, rc );
815
816 /* Stop link monitor */
817 fc_link_stop ( &port->link );
818
819 /* Shut down interfaces */
820 intf_shutdown ( &port->transport, rc );
821 intf_shutdown ( &port->flogi, rc );
822 intf_shutdown ( &port->ns_plogi, rc );
823
824 /* Shut down any remaining exchanges */
825 list_for_each_entry_safe ( xchg, tmp, &port->xchgs, list )
826 fc_xchg_close ( xchg, rc );
827
828 /* Remove from list of ports */
829 list_del ( &port->list );
830 INIT_LIST_HEAD ( &port->list );
831 }
832
833 /**
834 * Identify Fibre Channel exchange by local exchange ID
835 *
836 * @v port Fibre Channel port
837 * @v xchg_id Local exchange ID
838 * @ret xchg Fibre Channel exchange, or NULL
839 */
840 static struct fc_exchange * fc_port_demux ( struct fc_port *port,
841 unsigned int xchg_id ) {
842 struct fc_exchange *xchg;
843
844 list_for_each_entry ( xchg, &port->xchgs, list ) {
845 if ( xchg->xchg_id == xchg_id )
846 return xchg;
847 }
848 return NULL;
849 }
850
851 /**
852 * Handle received frame from Fibre Channel port
853 *
854 * @v port Fibre Channel port
855 * @v iobuf I/O buffer
856 * @v meta Data transfer metadata
857 * @ret rc Return status code
858 */
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;
864 int rc;
865
866 /* Sanity check */
867 if ( iob_len ( iobuf ) < sizeof ( *fchdr ) ) {
868 DBGC ( port, "FCPORT %s received underlength frame (%zd "
869 "bytes)\n", port->name, iob_len ( iobuf ) );
870 rc = -EINVAL;
871 goto err_sanity;
872 }
873
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 ) );
883 rc = -ENOTCONN;
884 goto err_port_id;
885 }
886
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 );
891
892 /* If we have no active exchange and this frame starts a new
893 * exchange, try to create a new responder exchange
894 */
895 if ( ( fchdr->f_ctl_es & FC_F_CTL_ES_FIRST ) &&
896 ( fchdr->seq_cnt == 0 ) ) {
897
898 /* Create new exchange */
899 xchg = fc_xchg_respond ( port, fchdr );
900 if ( ! xchg ) {
901 DBGC ( port, "FCPORT %s cannot create new exchange\n",
902 port->name );
903 rc = -ENOMEM;
904 goto err_respond;
905 }
906 }
907
908 /* Fail if no exchange exists */
909 if ( ! xchg ) {
910 DBGC ( port, "FCPORT %s xchg %04x unknown\n",
911 port->name, xchg_id );
912 rc = -ENOTCONN;
913 goto err_no_xchg;
914 }
915
916 /* Pass received frame to exchange */
917 ref_get ( &xchg->refcnt );
918 if ( ( rc = fc_xchg_rx ( xchg, iob_disown ( iobuf ), meta ) ) != 0 )
919 goto err_xchg_rx;
920
921 err_xchg_rx:
922 ref_put ( &xchg->refcnt );
923 err_no_xchg:
924 err_respond:
925 err_port_id:
926 err_sanity:
927 free_iob ( iobuf );
928 return rc;
929 }
930
931 /**
932 * Log in Fibre Channel port
933 *
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
940 */
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;
945 struct fc_peer *tmp;
946 int rc;
947
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 ) ||
956 ( has_fabric &&
957 ( memcmp ( &port->port_id, port_id,
958 sizeof ( port->port_id ) ) != 0 ) ) ) ) {
959 fc_port_logout ( port, 0 );
960 }
961
962 /* Log in, if applicable */
963 if ( ! fc_link_ok ( &port->link ) ) {
964
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 ) );
973
974 /* Calculate local (and possibly remote) port IDs */
975 if ( has_fabric ) {
976 port->flags |= FC_PORT_HAS_FABRIC;
977 memcpy ( &port->port_id, port_id,
978 sizeof ( port->port_id ) );
979 } else {
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,
986 &fc_ptp_low_port_id,
987 sizeof ( port->ptp_link_port_id ) );
988 } else {
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 ) );
994 }
995 }
996 DBGC ( port, "FCPORT %s logged in via a %s, with local ID "
997 "%s\n", port->name,
998 ( ( port->flags & FC_PORT_HAS_FABRIC ) ?
999 "fabric" : "point-to-point link" ),
1000 fc_id_ntoa ( &port->port_id ) );
1001 }
1002
1003 /* Log in to name server, if attached to a fabric */
1004 if ( has_fabric && ! ( port->flags & FC_PORT_HAS_NS ) ) {
1005
1006 DBGC ( port, "FCPORT %s attempting login to name server\n",
1007 port->name );
1008
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 );
1016 return rc;
1017 }
1018 }
1019
1020 /* Record login */
1021 fc_link_up ( &port->link );
1022
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 );
1028 }
1029
1030 return 0;
1031 }
1032
1033 /**
1034 * Log out Fibre Channel port
1035 *
1036 * @v port Fibre Channel port
1037 * @v rc Reason for logout
1038 */
1039 void fc_port_logout ( struct fc_port *port, int rc ) {
1040 struct fc_peer *peer;
1041 struct fc_peer *tmp;
1042
1043 DBGC ( port, "FCPORT %s logged out: %s\n",
1044 port->name, strerror ( rc ) );
1045
1046 /* Erase port details */
1047 memset ( &port->port_id, 0, sizeof ( port->port_id ) );
1048 port->flags = 0;
1049
1050 /* Record logout */
1051 fc_link_err ( &port->link, rc );
1052
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 );
1058 }
1059 }
1060
1061 /**
1062 * Handle FLOGI completion
1063 *
1064 * @v port Fibre Channel port
1065 * @v rc Reason for completion
1066 */
1067 static void fc_port_flogi_done ( struct fc_port *port, int rc ) {
1068
1069 intf_restart ( &port->flogi, rc );
1070
1071 if ( rc != 0 )
1072 fc_port_logout ( port, rc );
1073 }
1074
1075 /**
1076 * Handle name server PLOGI completion
1077 *
1078 * @v port Fibre Channel port
1079 * @v rc Reason for completion
1080 */
1081 static void fc_port_ns_plogi_done ( struct fc_port *port, int rc ) {
1082
1083 intf_restart ( &port->ns_plogi, rc );
1084
1085 if ( rc == 0 ) {
1086 port->flags |= FC_PORT_HAS_NS;
1087 DBGC ( port, "FCPORT %s logged in to name server\n",
1088 port->name );
1089 } else {
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 */
1093 }
1094 }
1095
1096 /**
1097 * Examine Fibre Channel port link state
1098 *
1099 * @ link Fibre Channel link state monitor
1100 */
1101 static void fc_port_examine ( struct fc_link_state *link ) {
1102 struct fc_port *port = container_of ( link, struct fc_port, link );
1103 int rc;
1104
1105 /* Do nothing if already logged in */
1106 if ( fc_link_ok ( &port->link ) )
1107 return;
1108
1109 DBGC ( port, "FCPORT %s attempting login\n", port->name );
1110
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 );
1117 return;
1118 }
1119 }
1120
1121 /**
1122 * Handle change of flow control window
1123 *
1124 * @v port Fibre Channel port
1125 */
1126 static void fc_port_window_changed ( struct fc_port *port ) {
1127 size_t window;
1128
1129 /* Check if transport layer is ready */
1130 window = xfer_window ( &port->transport );
1131 if ( window > 0 ) {
1132
1133 /* Transport layer is ready. Start login if the link
1134 * is not already up.
1135 */
1136 if ( ! fc_link_ok ( &port->link ) )
1137 fc_link_start ( &port->link );
1138
1139 } else {
1140
1141 /* Transport layer is not ready. Log out port and
1142 * wait for transport layer before attempting log in
1143 * again.
1144 */
1145 fc_port_logout ( port, -ENOTCONN );
1146 fc_link_stop ( &port->link );
1147 }
1148 }
1149
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 ),
1156 };
1157
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 );
1161
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 ),
1165 };
1166
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 );
1170
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 ),
1174 };
1175
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 );
1179
1180 /**
1181 * Create Fibre Channel port
1182 *
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
1188 */
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;
1192
1193 /* Allocate and initialise structure */
1194 port = zalloc ( sizeof ( *port ) );
1195 if ( ! port )
1196 return -ENOMEM;
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 );
1207
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 ) );
1211
1212 /* Attach to transport layer, mortalise self, and return */
1213 intf_plug_plug ( &port->transport, transport );
1214 ref_put ( &port->refcnt );
1215 return 0;
1216 }
1217
1218 /**
1219 * Find Fibre Channel port by name
1220 *
1221 * @v name Fibre Channel port name
1222 * @ret port Fibre Channel port, or NULL
1223 */
1224 struct fc_port * fc_port_find ( const char *name ) {
1225 struct fc_port *port;
1226
1227 list_for_each_entry ( port, &fc_ports, list ) {
1228 if ( strcmp ( name, port->name ) == 0 )
1229 return port;
1230 }
1231 return NULL;
1232 }
1233
1234 /******************************************************************************
1235 *
1236 * Fibre Channel peers
1237 *
1238 ******************************************************************************
1239 */
1240
1241 /**
1242 * Close Fibre Channel peer
1243 *
1244 * @v peer Fibre Channel peer
1245 * @v rc Reason for close
1246 */
1247 static void fc_peer_close ( struct fc_peer *peer, int rc ) {
1248
1249 DBGC ( peer, "FCPEER %s closed: %s\n",
1250 fc_ntoa ( &peer->port_wwn ) , strerror ( rc ) );
1251
1252 /* Sanity check */
1253 assert ( list_empty ( &peer->ulps ) );
1254
1255 /* Stop link timer */
1256 fc_link_stop ( &peer->link );
1257
1258 /* Shut down interfaces */
1259 intf_shutdown ( &peer->plogi, rc );
1260
1261 /* Remove from list of peers */
1262 list_del ( &peer->list );
1263 INIT_LIST_HEAD ( &peer->list );
1264 }
1265
1266 /**
1267 * Increment Fibre Channel peer active usage count
1268 *
1269 * @v peer Fibre Channel peer
1270 */
1271 static void fc_peer_increment ( struct fc_peer *peer ) {
1272
1273 /* Increment our usage count */
1274 peer->usage++;
1275 }
1276
1277 /**
1278 * Decrement Fibre Channel peer active usage count
1279 *
1280 * @v peer Fibre Channel peer
1281 */
1282 static void fc_peer_decrement ( struct fc_peer *peer ) {
1283
1284 /* Sanity check */
1285 assert ( peer->usage > 0 );
1286
1287 /* Decrement our usage count and log out if we reach zero */
1288 if ( --(peer->usage) == 0 )
1289 fc_peer_logout ( peer, 0 );
1290 }
1291
1292 /**
1293 * Log in Fibre Channel peer
1294 *
1295 * @v peer Fibre Channel peer
1296 * @v port Fibre Channel port
1297 * @v port_id Port ID
1298 * @ret rc Return status code
1299 */
1300 int fc_peer_login ( struct fc_peer *peer, struct fc_port *port,
1301 struct fc_port_id *port_id ) {
1302 struct fc_ulp *ulp;
1303 struct fc_ulp *tmp;
1304
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 );
1311 }
1312
1313 /* Log in, if applicable */
1314 if ( ! fc_link_ok ( &peer->link ) ) {
1315
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 ) );
1323
1324 /* Add login reference */
1325 fc_peer_get ( peer );
1326 }
1327
1328 /* Record login */
1329 fc_link_up ( &peer->link );
1330
1331 /* Notify ULPs of link state change */
1332 list_for_each_entry_safe ( ulp, tmp, &peer->ulps, list ) {
1333 fc_ulp_get ( ulp );
1334 fc_link_examine ( &ulp->link );
1335 fc_ulp_put ( ulp );
1336 }
1337
1338 return 0;
1339 }
1340
1341 /**
1342 * Log out Fibre Channel peer
1343 *
1344 * @v peer Fibre Channel peer
1345 * @v rc Reason for logout
1346 */
1347 void fc_peer_logout ( struct fc_peer *peer, int rc ) {
1348 struct fc_ulp *ulp;
1349 struct fc_ulp *tmp;
1350
1351 DBGC ( peer, "FCPEER %s logged out: %s\n",
1352 fc_ntoa ( &peer->port_wwn ), strerror ( rc ) );
1353
1354 /* Drop login reference, if applicable */
1355 if ( fc_link_ok ( &peer->link ) )
1356 fc_peer_put ( peer );
1357
1358 /* Erase peer details */
1359 fc_port_put ( peer->port );
1360 peer->port = NULL;
1361
1362 /* Record logout */
1363 fc_link_err ( &peer->link, rc );
1364
1365 /* Notify ULPs of link state change */
1366 list_for_each_entry_safe ( ulp, tmp, &peer->ulps, list ) {
1367 fc_ulp_get ( ulp );
1368 fc_link_examine ( &ulp->link );
1369 fc_ulp_put ( ulp );
1370 }
1371
1372 /* Close peer if there are no active users */
1373 if ( peer->usage == 0 )
1374 fc_peer_close ( peer, rc );
1375 }
1376
1377 /**
1378 * Handle PLOGI completion
1379 *
1380 * @v peer Fibre Channel peer
1381 * @v rc Reason for completion
1382 */
1383 static void fc_peer_plogi_done ( struct fc_peer *peer, int rc ) {
1384
1385 intf_restart ( &peer->plogi, rc );
1386
1387 if ( rc != 0 )
1388 fc_peer_logout ( peer, rc );
1389 }
1390
1391 /**
1392 * Initiate PLOGI
1393 *
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
1398 */
1399 static int fc_peer_plogi ( struct fc_peer *peer, struct fc_port *port,
1400 struct fc_port_id *peer_port_id ) {
1401 int rc;
1402
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 );
1409 return rc;
1410 }
1411
1412 return 0;
1413 }
1414
1415 /**
1416 * Examine Fibre Channel peer link state
1417 *
1418 * @ link Fibre Channel link state monitor
1419 */
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;
1423 int rc;
1424
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 );
1428 return;
1429 }
1430
1431 /* Do nothing if already logged in */
1432 if ( fc_link_ok ( &peer->link ) )
1433 return;
1434
1435 DBGC ( peer, "FCPEER %s attempting login\n",
1436 fc_ntoa ( &peer->port_wwn ) );
1437
1438 /* Sanity check */
1439 assert ( peer->port == NULL );
1440
1441 /* First, look for a port with the peer attached via a
1442 * point-to-point link.
1443 */
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 );
1451 return;
1452 }
1453 }
1454
1455 /* If the peer is not directly attached, try initiating a name
1456 * server lookup on any suitable ports.
1457 */
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,
1467 strerror ( rc ) );
1468 /* Non-fatal */
1469 }
1470 }
1471 }
1472 }
1473
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 ),
1477 };
1478
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 );
1482
1483 /**
1484 * Create Fibre Channel peer
1485 *
1486 * @v port_wwn Node name
1487 * @ret peer Fibre Channel peer, or NULL
1488 */
1489 static struct fc_peer * fc_peer_create ( const struct fc_name *port_wwn ) {
1490 struct fc_peer *peer;
1491
1492 /* Allocate and initialise structure */
1493 peer = zalloc ( sizeof ( *peer ) );
1494 if ( ! peer )
1495 return NULL;
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 );
1502
1503 /* Start link monitor */
1504 fc_link_start ( &peer->link );
1505
1506 DBGC ( peer, "FCPEER %s created\n", fc_ntoa ( &peer->port_wwn ) );
1507 return peer;
1508 }
1509
1510 /**
1511 * Get Fibre Channel peer by node name
1512 *
1513 * @v port_wwn Node name
1514 * @ret peer Fibre Channel peer, or NULL
1515 */
1516 struct fc_peer * fc_peer_get_wwn ( const struct fc_name *port_wwn ) {
1517 struct fc_peer *peer;
1518
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 );
1524 }
1525
1526 /* Create a new peer */
1527 peer = fc_peer_create ( port_wwn );
1528 if ( ! peer )
1529 return NULL;
1530
1531 return peer;
1532 }
1533
1534 /**
1535 * Get Fibre Channel peer by port ID
1536 *
1537 * @v port Fibre Channel port
1538 * @v peer_port_id Peer port ID
1539 * @ret peer Fibre Channel peer, or NULL
1540 */
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;
1544
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 );
1551 }
1552
1553 /* Cannot create a new peer, since we have no port name to use */
1554 return NULL;
1555 }
1556
1557 /******************************************************************************
1558 *
1559 * Fibre Channel upper-layer protocols
1560 *
1561 ******************************************************************************
1562 */
1563
1564 /**
1565 * Free Fibre Channel upper-layer protocol
1566 *
1567 * @v refcnt Reference count
1568 */
1569 static void fc_ulp_free ( struct refcnt *refcnt ) {
1570 struct fc_ulp *ulp = container_of ( refcnt, struct fc_ulp, refcnt );
1571
1572 fc_peer_put ( ulp->peer );
1573 free ( ulp );
1574 }
1575
1576 /**
1577 * Close Fibre Channel upper-layer protocol
1578 *
1579 * @v ulp Fibre Channel upper-layer protocol
1580 * @v rc Reason for close
1581 */
1582 static void fc_ulp_close ( struct fc_ulp *ulp, int rc ) {
1583
1584 DBGC ( ulp, "FCULP %s/%02x closed: %s\n",
1585 fc_ntoa ( &ulp->peer->port_wwn ), ulp->type, strerror ( rc ) );
1586
1587 /* Sanity check */
1588 assert ( list_empty ( &ulp->users ) );
1589
1590 /* Stop link monitor */
1591 fc_link_stop ( &ulp->link );
1592
1593 /* Shut down interfaces */
1594 intf_shutdown ( &ulp->prli, rc );
1595
1596 /* Remove from list of ULPs */
1597 list_del ( &ulp->list );
1598 INIT_LIST_HEAD ( &ulp->list );
1599 }
1600
1601 /**
1602 * Attach Fibre Channel upper-layer protocol user
1603 *
1604 * @v ulp Fibre Channel upper-layer protocol
1605 * @v user Fibre Channel upper-layer protocol user
1606 */
1607 void fc_ulp_attach ( struct fc_ulp *ulp, struct fc_ulp_user *user ) {
1608
1609 /* Sanity check */
1610 assert ( user->ulp == NULL );
1611
1612 /* Increment peer's usage count */
1613 fc_peer_increment ( ulp->peer );
1614
1615 /* Attach user */
1616 user->ulp = fc_ulp_get ( ulp );
1617 list_add ( &user->list, &ulp->users );
1618 }
1619
1620 /**
1621 * Detach Fibre Channel upper-layer protocol user
1622 *
1623 * @v user Fibre Channel upper-layer protocol user
1624 */
1625 void fc_ulp_detach ( struct fc_ulp_user *user ) {
1626 struct fc_ulp *ulp = user->ulp;
1627
1628 /* Do nothing if not attached */
1629 if ( ! ulp )
1630 return;
1631
1632 /* Sanity checks */
1633 list_check_contains_entry ( user, &ulp->users, list );
1634
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 );
1639
1640 /* Decrement our peer's usage count */
1641 fc_peer_decrement ( ulp->peer );
1642
1643 /* Drop reference */
1644 user->ulp = NULL;
1645 fc_ulp_put ( ulp );
1646 }
1647
1648 /**
1649 * Log in Fibre Channel upper-layer protocol
1650 *
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
1656 */
1657 int fc_ulp_login ( struct fc_ulp *ulp, const void *param, size_t param_len,
1658 int originated ) {
1659 struct fc_ulp_user *user;
1660 struct fc_ulp_user *tmp;
1661
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 );
1667 }
1668
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
1672 * itself.
1673 */
1674 if ( originated )
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 "
1678 "Linux bug\n",
1679 fc_ntoa ( &ulp->peer->port_wwn ), ulp->type );
1680 fc_link_stop ( &ulp->link );
1681 fc_link_start ( &ulp->link );
1682 return 0;
1683 }
1684
1685 /* Log in, if applicable */
1686 if ( ! fc_link_ok ( &ulp->link ) ) {
1687
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 "
1694 "parameters\n",
1695 fc_ntoa ( &ulp->peer->port_wwn ), ulp->type );
1696 return -ENOMEM;
1697 }
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 );
1703
1704 /* Add login reference */
1705 fc_ulp_get ( ulp );
1706 }
1707
1708 /* Record login */
1709 fc_link_up ( &ulp->link );
1710
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 );
1716 }
1717
1718 return 0;
1719 }
1720
1721 /**
1722 * Log out Fibre Channel upper-layer protocol
1723 *
1724 * @v ulp Fibre Channel upper-layer protocol
1725 * @v rc Reason for logout
1726 */
1727 void fc_ulp_logout ( struct fc_ulp *ulp, int rc ) {
1728 struct fc_ulp_user *user;
1729 struct fc_ulp_user *tmp;
1730
1731 DBGC ( ulp, "FCULP %s/%02x logged out: %s\n",
1732 fc_ntoa ( &ulp->peer->port_wwn ), ulp->type, strerror ( rc ) );
1733
1734 /* Drop login reference, if applicable */
1735 if ( fc_link_ok ( &ulp->link ) )
1736 fc_ulp_put ( ulp );
1737
1738 /* Discard service parameters */
1739 free ( ulp->param );
1740 ulp->param = NULL;
1741 ulp->param_len = 0;
1742 ulp->flags = 0;
1743
1744 /* Record logout */
1745 fc_link_err ( &ulp->link, rc );
1746
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 );
1752 }
1753
1754 /* Close ULP if there are no clients attached */
1755 if ( list_empty ( &ulp->users ) )
1756 fc_ulp_close ( ulp, rc );
1757 }
1758
1759 /**
1760 * Handle PRLI completion
1761 *
1762 * @v ulp Fibre Channel upper-layer protocol
1763 * @v rc Reason for completion
1764 */
1765 static void fc_ulp_prli_done ( struct fc_ulp *ulp, int rc ) {
1766
1767 intf_restart ( &ulp->prli, rc );
1768
1769 if ( rc != 0 )
1770 fc_ulp_logout ( ulp, rc );
1771 }
1772
1773 /**
1774 * Examine Fibre Channel upper-layer protocol link state
1775 *
1776 * @ link Fibre Channel link state monitor
1777 */
1778 static void fc_ulp_examine ( struct fc_link_state *link ) {
1779 struct fc_ulp *ulp = container_of ( link, struct fc_ulp, link );
1780 int rc;
1781
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 );
1785 return;
1786 }
1787
1788 /* Do nothing if already logged in */
1789 if ( fc_link_ok ( &ulp->link ) &&
1790 ( ulp->flags & FC_ULP_ORIGINATED_LOGIN_OK ) )
1791 return;
1792
1793 DBGC ( ulp, "FCULP %s/%02x attempting login\n",
1794 fc_ntoa ( &ulp->peer->port_wwn ), ulp->type );
1795
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,
1802 strerror ( rc ) );
1803 fc_ulp_logout ( ulp, rc );
1804 return;
1805 }
1806 }
1807
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 ),
1811 };
1812
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 );
1816
1817 /**
1818 * Create Fibre Channel upper-layer protocl
1819 *
1820 * @v peer Fibre Channel peer
1821 * @v type Type
1822 * @ret ulp Fibre Channel upper-layer protocol, or NULL
1823 */
1824 static struct fc_ulp * fc_ulp_create ( struct fc_peer *peer,
1825 unsigned int type ) {
1826 struct fc_ulp *ulp;
1827
1828 /* Allocate and initialise structure */
1829 ulp = zalloc ( sizeof ( *ulp ) );
1830 if ( ! ulp )
1831 return NULL;
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 );
1837 ulp->type = type;
1838 INIT_LIST_HEAD ( &ulp->users );
1839
1840 /* Start link state monitor */
1841 fc_link_start ( &ulp->link );
1842
1843 DBGC ( ulp, "FCULP %s/%02x created\n",
1844 fc_ntoa ( &ulp->peer->port_wwn ), ulp->type );
1845 return ulp;
1846 }
1847
1848 /**
1849 * Get Fibre Channel upper-layer protocol by peer and type
1850 *
1851 * @v peer Fibre Channel peer
1852 * @v type Type
1853 * @ret ulp Fibre Channel upper-layer protocol, or NULL
1854 */
1855 static struct fc_ulp * fc_ulp_get_type ( struct fc_peer *peer,
1856 unsigned int type ) {
1857 struct fc_ulp *ulp;
1858
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 );
1863 }
1864
1865 /* Create a new ULP */
1866 ulp = fc_ulp_create ( peer, type );
1867 if ( ! ulp )
1868 return NULL;
1869
1870 return ulp;
1871 }
1872
1873 /**
1874 * Get Fibre Channel upper-layer protocol by port name and type
1875 *
1876 * @v port_wwn Port name
1877 * @v type Type
1878 * @ret ulp Fibre Channel upper-layer protocol, or NULL
1879 */
1880 struct fc_ulp * fc_ulp_get_wwn_type ( const struct fc_name *port_wwn,
1881 unsigned int type ) {
1882 struct fc_ulp *ulp;
1883 struct fc_peer *peer;
1884
1885 /* Get peer */
1886 peer = fc_peer_get_wwn ( port_wwn );
1887 if ( ! peer )
1888 goto err_peer_get_wwn;
1889
1890 /* Get ULP */
1891 ulp = fc_ulp_get_type ( peer, type );
1892 if ( ! ulp )
1893 goto err_ulp_get_type;
1894
1895 /* Drop temporary reference to peer */
1896 fc_peer_put ( peer );
1897
1898 return ulp;
1899
1900 fc_ulp_put ( ulp );
1901 err_ulp_get_type:
1902 fc_peer_put ( peer );
1903 err_peer_get_wwn:
1904 return NULL;
1905 }
1906
1907 /**
1908 * Get Fibre Channel upper-layer protocol by port ID and type
1909 *
1910 * @v port Fibre Channel port
1911 * @v peer_port_id Peer port ID
1912 * @v type Type
1913 * @ret ulp Fibre Channel upper-layer protocol, or NULL
1914 */
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 ) {
1918 struct fc_ulp *ulp;
1919 struct fc_peer *peer;
1920
1921 /* Get peer */
1922 peer = fc_peer_get_port_id ( port, peer_port_id );
1923 if ( ! peer )
1924 goto err_peer_get_wwn;
1925
1926 /* Get ULP */
1927 ulp = fc_ulp_get_type ( peer, type );
1928 if ( ! ulp )
1929 goto err_ulp_get_type;
1930
1931 /* Drop temporary reference to peer */
1932 fc_peer_put ( peer );
1933
1934 return ulp;
1935
1936 fc_ulp_put ( ulp );
1937 err_ulp_get_type:
1938 fc_peer_put ( peer );
1939 err_peer_get_wwn:
1940 return NULL;
1941 }
1942
1943 /* Drag in objects via fc_ports */
1944 REQUIRING_SYMBOL ( fc_ports );
1945
1946 /* Drag in Fibre Channel configuration */
1947 REQUIRE_OBJECT ( config_fc );