From 8e718df5e1e18cca3ab204e9344ed2a76e6ed276 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 4 Nov 2010 03:31:15 +0000 Subject: [PATCH] [fc] Add support for Fibre Channel name server lookups Signed-off-by: Michael Brown --- src/include/ipxe/errfile.h | 1 + src/include/ipxe/fc.h | 7 ++ src/include/ipxe/fcns.h | 217 +++++++++++++++++++++++++++++++++ src/net/fc.c | 158 ++++++++++++++++++------ src/net/fcns.c | 240 +++++++++++++++++++++++++++++++++++++ 5 files changed, 585 insertions(+), 38 deletions(-) create mode 100644 src/include/ipxe/fcns.h create mode 100644 src/net/fcns.c diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 10bc49d8..2255f8a3 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -189,6 +189,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_fcels ( ERRFILE_NET | 0x002c0000 ) #define ERRFILE_fcp ( ERRFILE_NET | 0x002d0000 ) #define ERRFILE_fcoe ( ERRFILE_NET | 0x002e0000 ) +#define ERRFILE_fcns ( ERRFILE_NET | 0x002f0000 ) #define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 ) diff --git a/src/include/ipxe/fc.h b/src/include/ipxe/fc.h index 8dbc19e7..3a80d56c 100644 --- a/src/include/ipxe/fc.h +++ b/src/include/ipxe/fc.h @@ -64,6 +64,7 @@ struct sockaddr_fc { extern struct fc_port_id fc_empty_port_id; extern struct fc_port_id fc_f_port_id; +extern struct fc_port_id fc_gs_port_id; extern struct fc_port_id fc_ptp_low_port_id; extern struct fc_port_id fc_ptp_high_port_id; @@ -190,6 +191,7 @@ enum fc_type { FC_TYPE_BLS = 0x00, /**< Basic Link Service */ FC_TYPE_ELS = 0x01, /**< Extended Link Service */ FC_TYPE_FCP = 0x08, /**< Fibre Channel Protocol */ + FC_TYPE_CT = 0x20, /**< Common Transport */ }; /** Fibre Channel Frame Control - Exchange and Sequence */ @@ -277,6 +279,9 @@ struct fc_port { /** Link port ID (for point-to-point links only) */ struct fc_port_id ptp_link_port_id; + /** Name server PLOGI interface */ + struct interface ns_plogi; + /** List of active exchanges */ struct list_head xchgs; }; @@ -285,6 +290,8 @@ struct fc_port { enum fc_port_flags { /** Port is attached to a fabric */ FC_PORT_HAS_FABRIC = 0x0001, + /** Port is logged in to a name server */ + FC_PORT_HAS_NS = 0x0002, }; /** diff --git a/src/include/ipxe/fcns.h b/src/include/ipxe/fcns.h new file mode 100644 index 00000000..e25d9b9d --- /dev/null +++ b/src/include/ipxe/fcns.h @@ -0,0 +1,217 @@ +#ifndef _IPXE_FCNS_H +#define _IPXE_FCNS_H + +/** + * @file + * + * Fibre Channel name server lookups + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include + +/** A Fibre Channel Common Transport header */ +struct fc_ct_header { + /** Revision */ + uint8_t revision; + /** Original requestor ID */ + struct fc_port_id in_id; + /** Generic service type */ + uint8_t type; + /** Generic service subtype */ + uint8_t subtype; + /** Options */ + uint8_t options; + /** Reserved */ + uint8_t reserved; + /** Command/response code */ + uint16_t code; + /** Maximum/residual size */ + uint16_t size; + /** Fragment ID */ + uint8_t fragment; + /** Reason code */ + uint8_t reason; + /** Reason code explanation */ + uint8_t explanation; + /** Vendor specific */ + uint8_t vendor; +} __attribute__ (( packed )); + +/** Fibre Channel Common Transport revision */ +#define FC_CT_REVISION 1 + +/** Fibre Channel generic service type */ +enum fc_gs_type { + /** Directory service */ + FC_GS_TYPE_DS = 0xfc, +}; + +/** Fibre Channel generic service response codes */ +enum fc_gs_response_code { + /** Accepted */ + FC_GS_ACCEPT = 0x8002, + /** Rejected */ + FC_GS_REJECT = 0x8001, +}; + +/** Fibre Channel generic service rejection reason codes */ +enum fc_gs_reason_code { + /** Invalid command code */ + FC_GS_BAD_COMMAND = 0x01, + /** Invalid version level */ + FC_GS_BAD_VERSION = 0x02, + /** Logical error */ + FC_GS_ERROR = 0x03, + /** Invalid CT_IU size */ + FC_GS_BAD_SIZE = 0x04, + /** Logical busy */ + FC_GS_BUSY = 0x05, + /** Protocol error */ + FC_GS_EPROTO = 0x07, + /** Unable to perform command request */ + FC_GS_UNABLE = 0x09, + /** Command not supported */ + FC_GS_ENOTSUP = 0x0b, + /** Server not available */ + FC_GS_UNAVAILABLE = 0x0d, + /** Session could not be established */ + FC_GS_SESSION = 0x0e, +}; + +/** Fibre Channel directory service subtype */ +enum fc_ds_subtype { + /** Name server */ + FC_DS_SUBTYPE_NAME = 0x02, +}; + +/** Fibre Channel name server commands */ +enum fc_ns_command_nibble { + /** Get */ + FC_NS_GET = 0x1, + /** Register */ + FC_NS_REGISTER = 0x2, + /** De-register */ + FC_NS_DEREGISTER = 0x3, +}; + +/** Fibre Channel name server objects */ +enum fc_ns_object_nibble { + /** Port ID */ + FC_NS_PORT_ID = 0x1, + /** Port name */ + FC_NS_PORT_NAME = 0x2, + /** Node name */ + FC_NS_NODE_NAME = 0x3, + /** FC-4 types */ + FC_NS_FC4_TYPES = 0x7, + /** Symbolic port name */ + FC_NS_SYM_PORT_NAME = 0x8, + /** Symbolic node name */ + FC_NS_SYM_NODE_NAME = 0x9, + /** FC-4 features */ + FC_NS_FC4_FEATURES = 0xf, +}; + +/** Construct Fibre Channel name server command code + * + * @v command Name server command + * @v key Name server key + * @v value Name server value + * @ret code Name server command code + */ +#define FC_NS_CODE( command, key, value ) \ + ( ( (command) << 8 ) | ( (key) << 4 ) | ( (value) << 0 ) ) + +/** Construct Fibre Channel name server "get" command code + * + * @v key Name server key + * @v value Name server value to get + * @ret code Name server command code + */ +#define FC_NS_GET( key, value ) FC_NS_CODE ( FC_NS_GET, key, value ) + +/** Construct Fibre Channel name server "register" command code + * + * @v key Name server key + * @v value Name server value to register + * @ret code Name server command code + */ +#define FC_NS_REGISTER( key, value ) FC_NS_CODE ( FC_NS_REGISTER, key, value ) + +/** Extract Fibre Channel name server command + * + * @v code Name server command code + * @ret command Name server command + */ +#define FC_NS_COMMAND( code ) ( ( (code) >> 8 ) & 0xf ) + +/** Extract Fibre Channel name server key + * + * @v code Name server command code + * @ret key Name server key + */ +#define FC_NS_KEY( code ) ( ( (code) >> 4 ) & 0xf ) + +/** Extract Fibre Channel name server value + * + * @v code Name server command code + * @ret value NAme server value + */ +#define FC_NS_VALUE( code ) ( ( (code) >> 0 ) & 0xf ) + +/** A Fibre Channel name server port ID */ +struct fc_ns_port_id { + /** Reserved */ + uint8_t reserved; + /** Port ID */ + struct fc_port_id port_id; +} __attribute__ (( packed )); + +/** A Fibre Channel name server GID_PN request */ +struct fc_ns_gid_pn_request { + /** Common Transport header */ + struct fc_ct_header ct; + /** Port name */ + struct fc_name port_wwn; +} __attribute__ (( packed )); + +/** A Fibre Channel name server request */ +union fc_ns_request { + /** Get ID by port name */ + struct fc_ns_gid_pn_request gid_pn; +}; + +/** A Fibre Channel name server rejection response */ +struct fc_ns_reject_response { + /** Common Transport header */ + struct fc_ct_header ct; +} __attribute__ (( packed )); + +/** A Fibre Channel name server GID_PN response */ +struct fc_ns_gid_pn_response { + /** Common Transport header */ + struct fc_ct_header ct; + /** Port ID */ + struct fc_ns_port_id port_id; +} __attribute__ (( packed )); + +/** A Fibre Channel name server response */ +union fc_ns_response { + /** Common Transport header */ + struct fc_ct_header ct; + /** Rejection */ + struct fc_ns_reject_response reject; + /** Get ID by port name */ + struct fc_ns_gid_pn_response gid_pn; +}; + +extern int fc_ns_query ( struct fc_peer *peer, struct fc_port *port, + int ( * done ) ( struct fc_peer *peer, + struct fc_port *port, + struct fc_port_id *peer_port_id ) ); + +#endif /* _IPXE_FCNS_H */ diff --git a/src/net/fc.c b/src/net/fc.c index d321c1fd..eecb1adb 100644 --- a/src/net/fc.c +++ b/src/net/fc.c @@ -35,6 +35,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include /** @file * @@ -61,6 +62,9 @@ struct fc_port_id fc_empty_port_id = { .bytes = { 0x00, 0x00, 0x00 } }; /** F_Port contoller port ID */ struct fc_port_id fc_f_port_id = { .bytes = { 0xff, 0xff, 0xfe } }; +/** Generic services port ID */ +struct fc_port_id fc_gs_port_id = { .bytes = { 0xff, 0xff, 0xfc } }; + /** Point-to-point low port ID */ struct fc_port_id fc_ptp_low_port_id = { .bytes = { 0x01, 0x01, 0x01 } }; @@ -464,14 +468,24 @@ static int fc_xchg_tx ( struct fc_exchange *xchg, struct io_buffer *iobuf, } /* Calculate routing control */ - if ( xchg->type == FC_TYPE_ELS ) { + switch ( xchg->type ) { + case FC_TYPE_ELS: r_ctl = FC_R_CTL_ELS; if ( meta->flags & XFER_FL_RESPONSE ) { r_ctl |= FC_R_CTL_SOL_CTRL; } else { r_ctl |= FC_R_CTL_UNSOL_CTRL; } - } else { + break; + case FC_TYPE_CT: + r_ctl = FC_R_CTL_DATA; + if ( meta->flags & XFER_FL_RESPONSE ) { + r_ctl |= FC_R_CTL_SOL_CTRL; + } else { + r_ctl |= FC_R_CTL_UNSOL_CTRL; + } + break; + default: r_ctl = FC_R_CTL_DATA; switch ( meta->flags & ( XFER_FL_CMD_STAT | XFER_FL_RESPONSE ) ) { @@ -488,6 +502,7 @@ static int fc_xchg_tx ( struct fc_exchange *xchg, struct io_buffer *iobuf, r_ctl |= FC_R_CTL_UNSOL_DATA; break; } + break; } /* Calculate exchange and sequence control */ @@ -799,6 +814,7 @@ static void fc_port_close ( struct fc_port *port, int rc ) { /* Shut down interfaces */ intf_shutdown ( &port->transport, rc ); intf_shutdown ( &port->flogi, rc ); + intf_shutdown ( &port->ns_plogi, rc ); /* Shut down any remaining exchanges */ list_for_each_entry_safe ( xchg, tmp, &port->xchgs, list ) @@ -922,6 +938,7 @@ int fc_port_login ( struct fc_port *port, struct fc_port_id *port_id, const struct fc_name *link_port_wwn, int has_fabric ) { struct fc_peer *peer; struct fc_peer *tmp; + int rc; /* Perform implicit logout if logged in and details differ */ if ( fc_link_ok ( &port->link ) && @@ -978,6 +995,23 @@ int fc_port_login ( struct fc_port *port, struct fc_port_id *port_id, fc_id_ntoa ( &port->port_id ) ); } + /* Log in to name server, if attached to a fabric */ + if ( has_fabric && ! ( port->flags & FC_PORT_HAS_NS ) ) { + + DBGC ( port, "FCPORT %s attempting login to name server\n", + port->name ); + + intf_restart ( &port->ns_plogi, -ECANCELED ); + if ( ( rc = fc_els_plogi ( &port->ns_plogi, port, + &fc_gs_port_id ) ) != 0 ) { + DBGC ( port, "FCPORT %s could not initiate name " + "server PLOGI: %s\n", + port->name, strerror ( rc ) ); + fc_port_logout ( port, rc ); + return rc; + } + } + /* Record login */ fc_link_up ( &port->link ); @@ -1006,6 +1040,7 @@ void fc_port_logout ( struct fc_port *port, int rc ) { /* Erase port details */ memset ( &port->port_id, 0, sizeof ( port->port_id ) ); + port->flags = 0; /* Record logout */ fc_link_err ( &port->link, rc ); @@ -1032,6 +1067,27 @@ static void fc_port_flogi_done ( struct fc_port *port, int rc ) { fc_port_logout ( port, rc ); } +/** + * Handle name server PLOGI completion + * + * @v port Fibre Channel port + * @v rc Reason for completion + */ +static void fc_port_ns_plogi_done ( struct fc_port *port, int rc ) { + + intf_restart ( &port->ns_plogi, rc ); + + if ( rc == 0 ) { + port->flags |= FC_PORT_HAS_NS; + DBGC ( port, "FCPORT %s logged in to name server\n", + port->name ); + } else { + DBGC ( port, "FCPORT %s could not log in to name server: %s\n", + port->name, strerror ( rc ) ); + /* Absence of a name server is not a fatal error */ + } +} + /** * Examine Fibre Channel port link state * @@ -1107,6 +1163,15 @@ static struct interface_operation fc_port_flogi_op[] = { static struct interface_descriptor fc_port_flogi_desc = INTF_DESC ( struct fc_port, flogi, fc_port_flogi_op ); +/** Fibre Channel port name server PLOGI interface operations */ +static struct interface_operation fc_port_ns_plogi_op[] = { + INTF_OP ( intf_close, struct fc_port *, fc_port_ns_plogi_done ), +}; + +/** Fibre Channel port name server PLOGI interface descriptor */ +static struct interface_descriptor fc_port_ns_plogi_desc = + INTF_DESC ( struct fc_port, ns_plogi, fc_port_ns_plogi_op ); + /** * Create Fibre Channel port * @@ -1128,6 +1193,7 @@ int fc_port_open ( struct interface *transport, const struct fc_name *node_wwn, intf_init ( &port->transport, &fc_port_transport_desc, &port->refcnt ); fc_link_init ( &port->link, fc_port_examine, &port->refcnt ); intf_init ( &port->flogi, &fc_port_flogi_desc, &port->refcnt ); + intf_init ( &port->ns_plogi, &fc_port_ns_plogi_desc, &port->refcnt ); list_add_tail ( &port->list, &fc_ports ); INIT_LIST_HEAD ( &port->xchgs ); memcpy ( &port->node_wwn, node_wwn, sizeof ( port->node_wwn ) ); @@ -1162,26 +1228,6 @@ struct fc_port * fc_port_find ( const char *name ) { return NULL; } -/** - * Find Fibre Channel port by link node name - * - * @v link_port_wwn Link node name - * @ret port Fibre Channel port, or NULL - */ -static struct fc_port * -fc_port_find_link_wwn ( struct fc_name *link_port_wwn ) { - struct fc_port *port; - - list_for_each_entry ( port, &fc_ports, list ) { - if ( fc_link_ok ( &port->link ) && - ( memcmp ( &port->link_port_wwn, link_port_wwn, - sizeof ( port->link_port_wwn ) ) == 0 ) ) { - return port; - } - } - return NULL; -} - /****************************************************************************** * * Fibre Channel peers @@ -1339,6 +1385,30 @@ static void fc_peer_plogi_done ( struct fc_peer *peer, int rc ) { fc_peer_logout ( peer, rc ); } +/** + * Initiate PLOGI + * + * @v peer Fibre Channel peer + * @v port Fibre Channel port + * @v peer_port_id Peer port ID + * @ret rc Return status code + */ +static int fc_peer_plogi ( struct fc_peer *peer, struct fc_port *port, + struct fc_port_id *peer_port_id ) { + int rc; + + /* Try to create PLOGI ELS */ + intf_restart ( &peer->plogi, -ECANCELED ); + if ( ( rc = fc_els_plogi ( &peer->plogi, port, peer_port_id ) ) != 0 ) { + DBGC ( peer, "FCPEER %s could not initiate PLOGI: %s\n", + fc_ntoa ( &peer->port_wwn ), strerror ( rc ) ); + fc_peer_logout ( peer, rc ); + return rc; + } + + return 0; +} + /** * Examine Fibre Channel peer link state * @@ -1347,7 +1417,6 @@ static void fc_peer_plogi_done ( struct fc_peer *peer, int rc ) { static void fc_peer_examine ( struct fc_link_state *link ) { struct fc_peer *peer = container_of ( link, struct fc_peer, link ); struct fc_port *port; - struct fc_port_id *peer_port_id; int rc; /* Check to see if underlying port link has gone down */ @@ -1366,23 +1435,36 @@ static void fc_peer_examine ( struct fc_link_state *link ) { /* Sanity check */ assert ( peer->port == NULL ); - /* Look for a port with the peer attached via a point-to-point link */ - port = fc_port_find_link_wwn ( &peer->port_wwn ); - if ( ! port ) { - DBGC ( peer, "FCPEER %s could not find a point-to-point " - "link\n", fc_ntoa ( &peer->port_wwn ) ); - fc_peer_logout ( peer, -ENOENT ); - return; + /* First, look for a port with the peer attached via a + * point-to-point link. + */ + list_for_each_entry ( port, &fc_ports, list ) { + if ( fc_link_ok ( &port->link ) && + ( ! ( port->flags & FC_PORT_HAS_FABRIC ) ) && + ( memcmp ( &peer->port_wwn, &port->link_port_wwn, + sizeof ( peer->port_wwn ) ) == 0 ) ) { + /* Use this peer port ID, and stop looking */ + fc_peer_plogi ( peer, port, &port->ptp_link_port_id ); + return; + } } - peer_port_id = &port->ptp_link_port_id; - /* Try to create PLOGI ELS */ - intf_restart ( &peer->plogi, -ECANCELED ); - if ( ( rc = fc_els_plogi ( &peer->plogi, port, peer_port_id ) ) != 0 ) { - DBGC ( peer, "FCPEER %s could not initiate PLOGI: %s\n", - fc_ntoa ( &peer->port_wwn ), strerror ( rc ) ); - fc_peer_logout ( peer, rc ); - return; + /* If the peer is not directly attached, try initiating a name + * server lookup on any suitable ports. + */ + list_for_each_entry ( port, &fc_ports, list ) { + if ( fc_link_ok ( &port->link ) && + ( port->flags & FC_PORT_HAS_FABRIC ) && + ( port->flags & FC_PORT_HAS_NS ) ) { + if ( ( rc = fc_ns_query ( peer, port, + fc_peer_plogi ) ) != 0 ) { + DBGC ( peer, "FCPEER %s could not attempt " + "name server lookup on %s: %s\n", + fc_ntoa ( &peer->port_wwn ), port->name, + strerror ( rc ) ); + /* Non-fatal */ + } + } } } diff --git a/src/net/fcns.c b/src/net/fcns.c new file mode 100644 index 00000000..7769e255 --- /dev/null +++ b/src/net/fcns.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2010 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Fibre Channel name server lookups + * + */ + +/** A Fibre Channel name server query */ +struct fc_ns_query { + /** Reference count */ + struct refcnt refcnt; + /** Fibre Channel exchange */ + struct interface xchg; + + /** Fibre Channel peer */ + struct fc_peer *peer; + /** Fibre Channel port */ + struct fc_port *port; + + /** Process */ + struct process process; + /** Success handler + * + * @v peer Fibre Channel peer + * @v port Fibre Channel port + * @v peer_port_id Peer port ID + * @ret rc Return status code + */ + int ( * done ) ( struct fc_peer *peer, struct fc_port *port, + struct fc_port_id *peer_port_id ); +}; + +/** + * Free name server query + * + * @v refcnt Reference count + */ +static void fc_ns_query_free ( struct refcnt *refcnt ) { + struct fc_ns_query *query = + container_of ( refcnt, struct fc_ns_query, refcnt ); + + fc_peer_put ( query->peer ); + fc_port_put ( query->port ); + free ( query ); +} + +/** + * Close name server query + * + * @v query Name server query + * @v rc Reason for close + */ +static void fc_ns_query_close ( struct fc_ns_query *query, int rc ) { + + /* Stop process */ + process_del ( &query->process ); + + /* Shut down interfaces */ + intf_shutdown ( &query->xchg, rc ); +} + +/** + * Receive name server query response + * + * @v query Name server query + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int fc_ns_query_deliver ( struct fc_ns_query *query, + struct io_buffer *iobuf, + struct xfer_metadata *meta __unused ) { + union fc_ns_response *resp = iobuf->data; + struct fc_port_id *peer_port_id; + int rc; + + /* Sanity check */ + if ( iob_len ( iobuf ) < sizeof ( resp->ct ) ) { + DBGC ( query, "FCNS %p received underlength response (%zd " + "bytes)\n", query, iob_len ( iobuf ) ); + rc = -EINVAL; + goto done; + } + + /* Handle response */ + switch ( ntohs ( resp->ct.code ) ) { + case FC_GS_ACCEPT: + if ( iob_len ( iobuf ) < sizeof ( resp->gid_pn ) ) { + DBGC ( query, "FCNS %p received underlength accept " + "response (%zd bytes)\n", + query, iob_len ( iobuf ) ); + rc = -EINVAL; + goto done; + } + peer_port_id = &resp->gid_pn.port_id.port_id; + DBGC ( query, "FCNS %p resolved %s to %s via %s\n", + query, fc_ntoa ( &query->peer->port_wwn ), + fc_id_ntoa ( peer_port_id ), query->port->name ); + if ( ( rc = query->done ( query->peer, query->port, + peer_port_id ) ) != 0 ) + goto done; + break; + case FC_GS_REJECT: + DBGC ( query, "FCNS %p rejected (reason %02x explanation " + "%02x)\n", query, resp->reject.ct.reason, + resp->reject.ct.explanation ); + break; + default: + DBGC ( query, "FCNS %p received invalid response code %04x\n", + query, ntohs ( resp->ct.code ) ); + rc = -ENOTSUP; + goto done; + } + + rc = 0; + done: + free_iob ( iobuf ); + fc_ns_query_close ( query, rc ); + return rc; +} + +/** + * Name server query process + * + * @v process Process + */ +static void fc_ns_query_step ( struct process *process ) { + struct fc_ns_query *query = + container_of ( process, struct fc_ns_query, process ); + struct xfer_metadata meta; + struct fc_ns_gid_pn_request gid_pn; + int xchg_id; + int rc; + + /* Stop process */ + process_del ( &query->process ); + + /* Create exchange */ + if ( ( xchg_id = fc_xchg_originate ( &query->xchg, query->port, + &fc_gs_port_id, + FC_TYPE_CT ) ) < 0 ) { + rc = xchg_id; + DBGC ( query, "FCNS %p could not create exchange: %s\n", + query, strerror ( rc ) ); + fc_ns_query_close ( query, rc ); + return; + } + + /* Construct query request */ + memset ( &gid_pn, 0, sizeof ( gid_pn ) ); + gid_pn.ct.revision = FC_CT_REVISION; + gid_pn.ct.type = FC_GS_TYPE_DS; + gid_pn.ct.subtype = FC_DS_SUBTYPE_NAME; + gid_pn.ct.code = htons ( FC_NS_GET ( FC_NS_PORT_NAME, FC_NS_PORT_ID )); + memcpy ( &gid_pn.port_wwn, &query->peer->port_wwn, + sizeof ( gid_pn.port_wwn ) ); + memset ( &meta, 0, sizeof ( meta ) ); + meta.flags = XFER_FL_OVER; + + /* Send query */ + if ( ( rc = xfer_deliver_raw_meta ( &query->xchg, &gid_pn, + sizeof ( gid_pn ), &meta ) ) != 0){ + DBGC ( query, "FCNS %p could not deliver query: %s\n", + query, strerror ( rc ) ); + fc_ns_query_close ( query, rc ); + return; + } +} + +/** Name server exchange interface operations */ +static struct interface_operation fc_ns_query_xchg_op[] = { + INTF_OP ( xfer_deliver, struct fc_ns_query *, fc_ns_query_deliver ), + INTF_OP ( intf_close, struct fc_ns_query *, fc_ns_query_close ), +}; + +/** Name server exchange interface descriptor */ +static struct interface_descriptor fc_ns_query_xchg_desc = + INTF_DESC ( struct fc_ns_query, xchg, fc_ns_query_xchg_op ); + +/** + * Issue Fibre Channel name server query + * + * @v peer Fibre Channel peer + * @v port Fibre Channel port + * @ret rc Return status code + */ +int fc_ns_query ( struct fc_peer *peer, struct fc_port *port, + int ( * done ) ( struct fc_peer *peer, struct fc_port *port, + struct fc_port_id *peer_port_id ) ) { + struct fc_ns_query *query; + + /* Allocate and initialise structure */ + query = zalloc ( sizeof ( *query ) ); + if ( ! query ) + return -ENOMEM; + ref_init ( &query->refcnt, fc_ns_query_free ); + intf_init ( &query->xchg, &fc_ns_query_xchg_desc, &query->refcnt ); + process_init ( &query->process, fc_ns_query_step, &query->refcnt ); + query->peer = fc_peer_get ( peer ); + query->port = fc_port_get ( port ); + query->done = done; + + DBGC ( query, "FCNS %p querying %s via %s\n", + query, fc_ntoa ( &query->peer->port_wwn ), port->name ); + + /* Mortalise self and return */ + ref_put ( &query->refcnt ); + return 0; +}