Skip to content

Commit

Permalink
[aoe] Use an AoE config query to identify the target MAC address
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Michael Brown committed Nov 19, 2008
1 parent a2686a5 commit 246ddf5
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 56 deletions.
33 changes: 28 additions & 5 deletions src/include/gpxe/aoe.h
Expand Up @@ -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 */
Expand All @@ -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 */
Expand All @@ -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 */
Expand Down Expand Up @@ -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 */
Expand Down
196 changes: 145 additions & 51 deletions src/net/aoe.c
Expand Up @@ -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;
Expand All @@ -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 ) {
Expand All @@ -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 );
Expand All @@ -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;
Expand All @@ -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 */
Expand All @@ -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;
Expand All @@ -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;
}
Expand All @@ -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 ) {
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down

0 comments on commit 246ddf5

Please sign in to comment.