Annotation of qemu/roms/ipxe/src/net/fc.c, revision 1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.