Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / 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 }