mirror of https://github.com/ipxe/ipxe.git
[fc] Add support for Fibre Channel name server lookups
Signed-off-by: Michael Brown <mcb30@ipxe.org>pull/1/head
parent
41231fda9c
commit
8e718df5e1
|
@ -189,6 +189,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
||||||
#define ERRFILE_fcels ( ERRFILE_NET | 0x002c0000 )
|
#define ERRFILE_fcels ( ERRFILE_NET | 0x002c0000 )
|
||||||
#define ERRFILE_fcp ( ERRFILE_NET | 0x002d0000 )
|
#define ERRFILE_fcp ( ERRFILE_NET | 0x002d0000 )
|
||||||
#define ERRFILE_fcoe ( ERRFILE_NET | 0x002e0000 )
|
#define ERRFILE_fcoe ( ERRFILE_NET | 0x002e0000 )
|
||||||
|
#define ERRFILE_fcns ( ERRFILE_NET | 0x002f0000 )
|
||||||
|
|
||||||
#define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 )
|
#define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 )
|
||||||
#define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 )
|
#define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 )
|
||||||
|
|
|
@ -64,6 +64,7 @@ struct sockaddr_fc {
|
||||||
|
|
||||||
extern struct fc_port_id fc_empty_port_id;
|
extern struct fc_port_id fc_empty_port_id;
|
||||||
extern struct fc_port_id fc_f_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_low_port_id;
|
||||||
extern struct fc_port_id fc_ptp_high_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_BLS = 0x00, /**< Basic Link Service */
|
||||||
FC_TYPE_ELS = 0x01, /**< Extended Link Service */
|
FC_TYPE_ELS = 0x01, /**< Extended Link Service */
|
||||||
FC_TYPE_FCP = 0x08, /**< Fibre Channel Protocol */
|
FC_TYPE_FCP = 0x08, /**< Fibre Channel Protocol */
|
||||||
|
FC_TYPE_CT = 0x20, /**< Common Transport */
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Fibre Channel Frame Control - Exchange and Sequence */
|
/** Fibre Channel Frame Control - Exchange and Sequence */
|
||||||
|
@ -277,6 +279,9 @@ struct fc_port {
|
||||||
/** Link port ID (for point-to-point links only) */
|
/** Link port ID (for point-to-point links only) */
|
||||||
struct fc_port_id ptp_link_port_id;
|
struct fc_port_id ptp_link_port_id;
|
||||||
|
|
||||||
|
/** Name server PLOGI interface */
|
||||||
|
struct interface ns_plogi;
|
||||||
|
|
||||||
/** List of active exchanges */
|
/** List of active exchanges */
|
||||||
struct list_head xchgs;
|
struct list_head xchgs;
|
||||||
};
|
};
|
||||||
|
@ -285,6 +290,8 @@ struct fc_port {
|
||||||
enum fc_port_flags {
|
enum fc_port_flags {
|
||||||
/** Port is attached to a fabric */
|
/** Port is attached to a fabric */
|
||||||
FC_PORT_HAS_FABRIC = 0x0001,
|
FC_PORT_HAS_FABRIC = 0x0001,
|
||||||
|
/** Port is logged in to a name server */
|
||||||
|
FC_PORT_HAS_NS = 0x0002,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,217 @@
|
||||||
|
#ifndef _IPXE_FCNS_H
|
||||||
|
#define _IPXE_FCNS_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
*
|
||||||
|
* Fibre Channel name server lookups
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
FILE_LICENCE ( GPL2_OR_LATER );
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <ipxe/fc.h>
|
||||||
|
|
||||||
|
/** 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 */
|
158
src/net/fc.c
158
src/net/fc.c
|
@ -35,6 +35,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
||||||
#include <ipxe/iobuf.h>
|
#include <ipxe/iobuf.h>
|
||||||
#include <ipxe/fc.h>
|
#include <ipxe/fc.h>
|
||||||
#include <ipxe/fcels.h>
|
#include <ipxe/fcels.h>
|
||||||
|
#include <ipxe/fcns.h>
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
*
|
*
|
||||||
|
@ -61,6 +62,9 @@ struct fc_port_id fc_empty_port_id = { .bytes = { 0x00, 0x00, 0x00 } };
|
||||||
/** F_Port contoller port ID */
|
/** F_Port contoller port ID */
|
||||||
struct fc_port_id fc_f_port_id = { .bytes = { 0xff, 0xff, 0xfe } };
|
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 */
|
/** Point-to-point low port ID */
|
||||||
struct fc_port_id fc_ptp_low_port_id = { .bytes = { 0x01, 0x01, 0x01 } };
|
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 */
|
/* Calculate routing control */
|
||||||
if ( xchg->type == FC_TYPE_ELS ) {
|
switch ( xchg->type ) {
|
||||||
|
case FC_TYPE_ELS:
|
||||||
r_ctl = FC_R_CTL_ELS;
|
r_ctl = FC_R_CTL_ELS;
|
||||||
if ( meta->flags & XFER_FL_RESPONSE ) {
|
if ( meta->flags & XFER_FL_RESPONSE ) {
|
||||||
r_ctl |= FC_R_CTL_SOL_CTRL;
|
r_ctl |= FC_R_CTL_SOL_CTRL;
|
||||||
} else {
|
} else {
|
||||||
r_ctl |= FC_R_CTL_UNSOL_CTRL;
|
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;
|
r_ctl = FC_R_CTL_DATA;
|
||||||
switch ( meta->flags &
|
switch ( meta->flags &
|
||||||
( XFER_FL_CMD_STAT | XFER_FL_RESPONSE ) ) {
|
( 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;
|
r_ctl |= FC_R_CTL_UNSOL_DATA;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculate exchange and sequence control */
|
/* Calculate exchange and sequence control */
|
||||||
|
@ -799,6 +814,7 @@ static void fc_port_close ( struct fc_port *port, int rc ) {
|
||||||
/* Shut down interfaces */
|
/* Shut down interfaces */
|
||||||
intf_shutdown ( &port->transport, rc );
|
intf_shutdown ( &port->transport, rc );
|
||||||
intf_shutdown ( &port->flogi, rc );
|
intf_shutdown ( &port->flogi, rc );
|
||||||
|
intf_shutdown ( &port->ns_plogi, rc );
|
||||||
|
|
||||||
/* Shut down any remaining exchanges */
|
/* Shut down any remaining exchanges */
|
||||||
list_for_each_entry_safe ( xchg, tmp, &port->xchgs, list )
|
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 ) {
|
const struct fc_name *link_port_wwn, int has_fabric ) {
|
||||||
struct fc_peer *peer;
|
struct fc_peer *peer;
|
||||||
struct fc_peer *tmp;
|
struct fc_peer *tmp;
|
||||||
|
int rc;
|
||||||
|
|
||||||
/* Perform implicit logout if logged in and details differ */
|
/* Perform implicit logout if logged in and details differ */
|
||||||
if ( fc_link_ok ( &port->link ) &&
|
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 ) );
|
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 */
|
/* Record login */
|
||||||
fc_link_up ( &port->link );
|
fc_link_up ( &port->link );
|
||||||
|
|
||||||
|
@ -1006,6 +1040,7 @@ void fc_port_logout ( struct fc_port *port, int rc ) {
|
||||||
|
|
||||||
/* Erase port details */
|
/* Erase port details */
|
||||||
memset ( &port->port_id, 0, sizeof ( port->port_id ) );
|
memset ( &port->port_id, 0, sizeof ( port->port_id ) );
|
||||||
|
port->flags = 0;
|
||||||
|
|
||||||
/* Record logout */
|
/* Record logout */
|
||||||
fc_link_err ( &port->link, rc );
|
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 );
|
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
|
* 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 =
|
static struct interface_descriptor fc_port_flogi_desc =
|
||||||
INTF_DESC ( struct fc_port, flogi, fc_port_flogi_op );
|
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
|
* 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 );
|
intf_init ( &port->transport, &fc_port_transport_desc, &port->refcnt );
|
||||||
fc_link_init ( &port->link, fc_port_examine, &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->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 );
|
list_add_tail ( &port->list, &fc_ports );
|
||||||
INIT_LIST_HEAD ( &port->xchgs );
|
INIT_LIST_HEAD ( &port->xchgs );
|
||||||
memcpy ( &port->node_wwn, node_wwn, sizeof ( port->node_wwn ) );
|
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;
|
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
|
* Fibre Channel peers
|
||||||
|
@ -1339,6 +1385,30 @@ static void fc_peer_plogi_done ( struct fc_peer *peer, int rc ) {
|
||||||
fc_peer_logout ( peer, 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
|
* 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 ) {
|
static void fc_peer_examine ( struct fc_link_state *link ) {
|
||||||
struct fc_peer *peer = container_of ( link, struct fc_peer, link );
|
struct fc_peer *peer = container_of ( link, struct fc_peer, link );
|
||||||
struct fc_port *port;
|
struct fc_port *port;
|
||||||
struct fc_port_id *peer_port_id;
|
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Check to see if underlying port link has gone down */
|
/* 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 */
|
/* Sanity check */
|
||||||
assert ( peer->port == NULL );
|
assert ( peer->port == NULL );
|
||||||
|
|
||||||
/* Look for a port with the peer attached via a point-to-point link */
|
/* First, look for a port with the peer attached via a
|
||||||
port = fc_port_find_link_wwn ( &peer->port_wwn );
|
* point-to-point link.
|
||||||
if ( ! port ) {
|
*/
|
||||||
DBGC ( peer, "FCPEER %s could not find a point-to-point "
|
list_for_each_entry ( port, &fc_ports, list ) {
|
||||||
"link\n", fc_ntoa ( &peer->port_wwn ) );
|
if ( fc_link_ok ( &port->link ) &&
|
||||||
fc_peer_logout ( peer, -ENOENT );
|
( ! ( port->flags & FC_PORT_HAS_FABRIC ) ) &&
|
||||||
return;
|
( 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 */
|
/* If the peer is not directly attached, try initiating a name
|
||||||
intf_restart ( &peer->plogi, -ECANCELED );
|
* server lookup on any suitable ports.
|
||||||
if ( ( rc = fc_els_plogi ( &peer->plogi, port, peer_port_id ) ) != 0 ) {
|
*/
|
||||||
DBGC ( peer, "FCPEER %s could not initiate PLOGI: %s\n",
|
list_for_each_entry ( port, &fc_ports, list ) {
|
||||||
fc_ntoa ( &peer->port_wwn ), strerror ( rc ) );
|
if ( fc_link_ok ( &port->link ) &&
|
||||||
fc_peer_logout ( peer, rc );
|
( port->flags & FC_PORT_HAS_FABRIC ) &&
|
||||||
return;
|
( 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 */
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,240 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
|
||||||
|
*
|
||||||
|
* 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 <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <byteswap.h>
|
||||||
|
#include <ipxe/interface.h>
|
||||||
|
#include <ipxe/iobuf.h>
|
||||||
|
#include <ipxe/process.h>
|
||||||
|
#include <ipxe/xfer.h>
|
||||||
|
#include <ipxe/fc.h>
|
||||||
|
#include <ipxe/fcns.h>
|
||||||
|
|
||||||
|
/** @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;
|
||||||
|
}
|
Loading…
Reference in New Issue