summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown2008-11-19 22:42:33 +0100
committerMichael Brown2008-11-19 22:42:33 +0100
commit246ddf5ee4fe0d95f056ad00c670a4a851097418 (patch)
tree6cbb0f629e450a1edebec1a5900b28122a95ce52
parent[blockdev] Move block device operations to structure block_device_operations (diff)
downloadipxe-246ddf5ee4fe0d95f056ad00c670a4a851097418.tar.gz
ipxe-246ddf5ee4fe0d95f056ad00c670a4a851097418.tar.xz
ipxe-246ddf5ee4fe0d95f056ad00c670a4a851097418.zip
[aoe] Use an AoE config query to identify the target MAC address
The AoE spec does not specify that the source MAC address of a received packet actually matches the MAC address of the AoE target. In principle an AoE server can respond to an AoE request on any interface available to it, which may not be an address configured to accept AoE requests. This issue is resolved by implementing AoE device discovery. The purpose of AoE discovery is to find out which addresses an AoE target can use for requests. An AoE configuration command is sent when the AoE attach is attempted. The AoE target must respond to that configuration query from an interface that can accept requests. Based on a patch from Ryan Thomas <ryan@coraid.com>
-rw-r--r--src/include/gpxe/aoe.h33
-rw-r--r--src/net/aoe.c196
2 files changed, 173 insertions, 56 deletions
diff --git a/src/include/gpxe/aoe.h b/src/include/gpxe/aoe.h
index 4aab4291..6de6b965 100644
--- a/src/include/gpxe/aoe.h
+++ b/src/include/gpxe/aoe.h
@@ -13,8 +13,24 @@
#include <gpxe/retry.h>
#include <gpxe/ata.h>
+/** An AoE config command */
+struct aoecfg {
+ /** AoE Queue depth */
+ uint16_t bufcnt;
+ /** ATA target firmware version */
+ uint16_t fwver;
+ /** ATA target sector count */
+ uint8_t scnt;
+ /** AoE config string subcommand */
+ uint8_t aoeccmd;
+ /** AoE config string length */
+ uint16_t cfglen;
+ /** AoE config string */
+ uint8_t data[0];
+} __attribute__ (( packed ));
+
/** An AoE ATA command */
-struct aoecmd {
+struct aoeata {
/** AoE command flags */
uint8_t aflags;
/** ATA error/feature register */
@@ -37,6 +53,14 @@ struct aoecmd {
#define AOE_FL_ASYNC 0x02 /**< Asynchronous write */
#define AOE_FL_WRITE 0x01 /**< Write command */
+/** An AoE command */
+union aoecmd {
+ /** Config command */
+ struct aoecfg cfg;
+ /** ATA command */
+ struct aoeata ata;
+};
+
/** An AoE header */
struct aoehdr {
/** Protocol version number and flags */
@@ -52,10 +76,7 @@ struct aoehdr {
/** Tag, in network byte order */
uint32_t tag;
/** Payload */
- union {
- /** ATA command */
- struct aoecmd command[0];
- } arg;
+ union aoecmd cmd[0];
} __attribute__ (( packed ));
#define AOE_VERSION 0x10 /**< Version 1 */
@@ -99,6 +120,8 @@ struct aoe_session {
/** Tag for current AoE command */
uint32_t tag;
+ /** Current AOE command */
+ uint8_t aoe_cmd_type;
/** Current ATA command */
struct ata_command *command;
/** Overall status of current ATA command */
diff --git a/src/net/aoe.c b/src/net/aoe.c
index ec3814bf..08887fe0 100644
--- a/src/net/aoe.c
+++ b/src/net/aoe.c
@@ -64,8 +64,13 @@ static void aoe_free ( struct refcnt *refcnt ) {
static void aoe_done ( struct aoe_session *aoe, int rc ) {
/* Record overall command status */
- aoe->command->cb.cmd_stat = aoe->status;
- aoe->command = NULL;
+ if ( aoe->command ) {
+ aoe->command->cb.cmd_stat = aoe->status;
+ aoe->command = NULL;
+ }
+
+ /* Stop retransmission timer */
+ stop_timer ( &aoe->timer );
/* Mark operation as complete */
aoe->rc = rc;
@@ -84,9 +89,11 @@ static int aoe_send_command ( struct aoe_session *aoe ) {
struct ata_command *command = aoe->command;
struct io_buffer *iobuf;
struct aoehdr *aoehdr;
- struct aoecmd *aoecmd;
+ union aoecmd *aoecmd;
+ struct aoeata *aoeata;
unsigned int count;
unsigned int data_out_len;
+ unsigned int aoecmdlen;
/* Fail immediately if we have no netdev to send on */
if ( ! aoe->netdev ) {
@@ -102,42 +109,71 @@ static int aoe_send_command ( struct aoe_session *aoe ) {
start_timer ( &aoe->timer );
/* Calculate count and data_out_len for this subcommand */
- count = command->cb.count.native;
- if ( count > AOE_MAX_COUNT )
- count = AOE_MAX_COUNT;
- data_out_len = ( command->data_out ? ( count * ATA_SECTOR_SIZE ) : 0 );
+ switch ( aoe->aoe_cmd_type ) {
+ case AOE_CMD_ATA:
+ count = command->cb.count.native;
+ if ( count > AOE_MAX_COUNT )
+ count = AOE_MAX_COUNT;
+ data_out_len = ( command->data_out ?
+ ( count * ATA_SECTOR_SIZE ) : 0 );
+ aoecmdlen = sizeof ( aoecmd->ata );
+ break;
+ case AOE_CMD_CONFIG:
+ count = 0;
+ data_out_len = 0;
+ aoecmdlen = sizeof ( aoecmd->cfg );
+ break;
+ default:
+ return -ENOTSUP;
+ }
/* Create outgoing I/O buffer */
iobuf = alloc_iob ( ETH_HLEN + sizeof ( *aoehdr ) +
- sizeof ( *aoecmd ) + data_out_len );
+ aoecmdlen + data_out_len );
+
if ( ! iobuf )
return -ENOMEM;
iob_reserve ( iobuf, ETH_HLEN );
aoehdr = iob_put ( iobuf, sizeof ( *aoehdr ) );
- aoecmd = iob_put ( iobuf, sizeof ( *aoecmd ) );
- memset ( aoehdr, 0, ( sizeof ( *aoehdr ) + sizeof ( *aoecmd ) ) );
+ aoecmd = iob_put ( iobuf, aoecmdlen );
+ memset ( aoehdr, 0, ( sizeof ( *aoehdr ) + aoecmdlen ) );
/* Fill AoE header */
aoehdr->ver_flags = AOE_VERSION;
aoehdr->major = htons ( aoe->major );
aoehdr->minor = aoe->minor;
+ aoehdr->command = aoe->aoe_cmd_type;
aoehdr->tag = htonl ( ++aoe->tag );
- /* Fill AoE command */
- linker_assert ( AOE_FL_DEV_HEAD == ATA_DEV_SLAVE, __fix_ata_h__ );
- aoecmd->aflags = ( ( command->cb.lba48 ? AOE_FL_EXTENDED : 0 ) |
- ( command->cb.device & ATA_DEV_SLAVE ) |
- ( data_out_len ? AOE_FL_WRITE : 0 ) );
- aoecmd->err_feat = command->cb.err_feat.bytes.cur;
- aoecmd->count = count;
- aoecmd->cmd_stat = command->cb.cmd_stat;
- aoecmd->lba.u64 = cpu_to_le64 ( command->cb.lba.native );
- if ( ! command->cb.lba48 )
- aoecmd->lba.bytes[3] |= ( command->cb.device & ATA_DEV_MASK );
-
- /* Fill data payload */
- copy_from_user ( iob_put ( iobuf, data_out_len ), command->data_out,
- aoe->command_offset, data_out_len );
+ /* Fill AoE payload */
+ switch ( aoe->aoe_cmd_type ) {
+ case AOE_CMD_ATA:
+ /* Fill AoE command */
+ aoeata = &aoecmd->ata;
+ linker_assert ( AOE_FL_DEV_HEAD == ATA_DEV_SLAVE,
+ __fix_ata_h__ );
+ aoeata->aflags = ( ( command->cb.lba48 ? AOE_FL_EXTENDED : 0 )|
+ ( command->cb.device & ATA_DEV_SLAVE ) |
+ ( data_out_len ? AOE_FL_WRITE : 0 ) );
+ aoeata->err_feat = command->cb.err_feat.bytes.cur;
+ aoeata->count = count;
+ aoeata->cmd_stat = command->cb.cmd_stat;
+ aoeata->lba.u64 = cpu_to_le64 ( command->cb.lba.native );
+ if ( ! command->cb.lba48 )
+ aoeata->lba.bytes[3] |=
+ ( command->cb.device & ATA_DEV_MASK );
+
+ /* Fill data payload */
+ copy_from_user ( iob_put ( iobuf, data_out_len ),
+ command->data_out, aoe->command_offset,
+ data_out_len );
+ break;
+ case AOE_CMD_CONFIG:
+ /* Nothing to do */
+ break;
+ default:
+ assert ( 0 );
+ }
/* Send packet */
return net_tx ( iobuf, aoe->netdev, &aoe_protocol, aoe->target );
@@ -161,38 +197,46 @@ static void aoe_timer_expired ( struct retry_timer *timer, int fail ) {
}
/**
- * Handle AoE response
+ * Handle AoE configuration command response
+ *
+ * @v aoe AoE session
+ * @v ll_source Link-layer source address
+ * @ret rc Return status code
+ */
+static int aoe_rx_cfg ( struct aoe_session *aoe, const void *ll_source ) {
+
+ /* Record target MAC address */
+ memcpy ( aoe->target, ll_source, sizeof ( aoe->target ) );
+ DBGC ( aoe, "AoE %p target MAC address %s\n",
+ aoe, eth_ntoa ( aoe->target ) );
+
+ /* Mark config request as complete */
+ aoe_done ( aoe, 0 );
+
+ return 0;
+}
+
+/**
+ * Handle AoE ATA command response
*
* @v aoe AoE session
- * @v aoehdr AoE header
+ * @v aoeata AoE ATA command
+ * @v len Length of AoE ATA command
* @ret rc Return status code
*/
-static int aoe_rx_response ( struct aoe_session *aoe, struct aoehdr *aoehdr,
- unsigned int len ) {
- struct aoecmd *aoecmd = aoehdr->arg.command;
+static int aoe_rx_ata ( struct aoe_session *aoe, struct aoeata *aoeata,
+ size_t len ) {
struct ata_command *command = aoe->command;
unsigned int rx_data_len;
unsigned int count;
unsigned int data_len;
-
+
/* Sanity check */
- if ( len < ( sizeof ( *aoehdr ) + sizeof ( *aoecmd ) ) ) {
+ if ( len < sizeof ( *aoeata ) ) {
/* Ignore packet; allow timer to trigger retransmit */
return -EINVAL;
}
- rx_data_len = ( len - sizeof ( *aoehdr ) - sizeof ( *aoecmd ) );
-
- /* Stop retry timer. After this point, every code path must
- * either terminate the AoE operation via aoe_done(), or
- * transmit a new packet.
- */
- stop_timer ( &aoe->timer );
-
- /* Check for fatal errors */
- if ( aoehdr->ver_flags & AOE_FL_ERROR ) {
- aoe_done ( aoe, -EIO );
- return 0;
- }
+ rx_data_len = ( len - sizeof ( *aoeata ) );
/* Calculate count and data_len for this subcommand */
count = command->cb.count.native;
@@ -201,14 +245,14 @@ static int aoe_rx_response ( struct aoe_session *aoe, struct aoehdr *aoehdr,
data_len = count * ATA_SECTOR_SIZE;
/* Merge into overall ATA status */
- aoe->status |= aoecmd->cmd_stat;
+ aoe->status |= aoeata->cmd_stat;
/* Copy data payload */
if ( command->data_in ) {
if ( rx_data_len > data_len )
rx_data_len = data_len;
copy_to_user ( command->data_in, aoe->command_offset,
- aoecmd->data, rx_data_len );
+ aoeata->data, rx_data_len );
}
/* Update ATA command and offset */
@@ -223,6 +267,7 @@ static int aoe_rx_response ( struct aoe_session *aoe, struct aoehdr *aoehdr,
}
/* Transmit next portion of request */
+ stop_timer ( &aoe->timer );
aoe_send_command ( aoe );
return 0;
@@ -241,12 +286,11 @@ static int aoe_rx ( struct io_buffer *iobuf,
struct net_device *netdev __unused,
const void *ll_source ) {
struct aoehdr *aoehdr = iobuf->data;
- unsigned int len = iob_len ( iobuf );
struct aoe_session *aoe;
int rc = 0;
/* Sanity checks */
- if ( len < sizeof ( *aoehdr ) ) {
+ if ( iob_len ( iobuf ) < sizeof ( *aoehdr ) ) {
rc = -EINVAL;
goto done;
}
@@ -258,6 +302,7 @@ static int aoe_rx ( struct io_buffer *iobuf,
/* Ignore AoE requests that we happen to see */
goto done;
}
+ iob_pull ( iobuf, sizeof ( *aoehdr ) );
/* Demultiplex amongst active AoE sessions */
list_for_each_entry ( aoe, &aoe_sessions, list ) {
@@ -267,8 +312,22 @@ static int aoe_rx ( struct io_buffer *iobuf,
continue;
if ( ntohl ( aoehdr->tag ) != aoe->tag )
continue;
- memcpy ( aoe->target, ll_source, sizeof ( aoe->target ) );
- rc = aoe_rx_response ( aoe, aoehdr, len );
+ if ( aoehdr->ver_flags & AOE_FL_ERROR ) {
+ aoe_done ( aoe, -EIO );
+ break;
+ }
+ switch ( aoehdr->command ) {
+ case AOE_CMD_ATA:
+ rc = aoe_rx_ata ( aoe, iobuf->data, iob_len ( iobuf ));
+ break;
+ case AOE_CMD_CONFIG:
+ rc = aoe_rx_cfg ( aoe, ll_source );
+ break;
+ default:
+ DBGC ( aoe, "AoE %p ignoring command %02x\n",
+ aoe, aoehdr->command );
+ break;
+ }
break;
}
@@ -300,6 +359,32 @@ static int aoe_command ( struct ata_device *ata,
aoe->command = command;
aoe->status = 0;
aoe->command_offset = 0;
+ aoe->aoe_cmd_type = AOE_CMD_ATA;
+
+ aoe_send_command ( aoe );
+
+ aoe->rc = -EINPROGRESS;
+ while ( aoe->rc == -EINPROGRESS )
+ step();
+ rc = aoe->rc;
+
+ return rc;
+}
+
+
+/**
+ * Issue AoE config query for AoE target discovery
+ *
+ * @v aoe AoE session
+ * @ret rc Return status code
+ */
+static int aoe_discover ( struct aoe_session *aoe ) {
+ int rc;
+
+ aoe->status = 0;
+ aoe->aoe_cmd_type = AOE_CMD_CONFIG;
+ aoe->command = NULL;
+
aoe_send_command ( aoe );
aoe->rc = -EINPROGRESS;
@@ -374,6 +459,15 @@ int aoe_attach ( struct ata_device *ata, struct net_device *netdev,
ata->backend = ref_get ( &aoe->refcnt );
ata->command = aoe_command;
list_add ( &aoe->list, &aoe_sessions );
+
+ /* Send discovery packet to find the target MAC address.
+ * Ideally, this ought to be done asynchronously, but the
+ * block device interface does not yet support asynchronous
+ * operation.
+ */
+ if ( ( rc = aoe_discover( aoe ) ) != 0 )
+ goto err;
+
return 0;
err: