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 #include +/** 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 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 ); + /* 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 aoehdr AoE header + * @v ll_source Link-layer source address * @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_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 aoeata AoE ATA command + * @v len Length of AoE ATA command + * @ret rc Return status code + */ +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: