~pmdj/ubuntu/trusty/qemu/2.9+applesmc+fadtv3

« back to all changes in this revision

Viewing changes to roms/ipxe/src/net/fc.c

  • Committer: Phil Dennis-Jordan
  • Date: 2017-07-21 08:03:43 UTC
  • mfrom: (1.1.1)
  • Revision ID: phil@philjordan.eu-20170721080343-2yr2vdj7713czahv
New upstream release 2.9.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 );