|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.