From 68125bc44173893fb3af08085b0b6b823b60d030 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 31 May 2006 14:34:17 +0000 Subject: [PATCH] Added generic asynchronous operations code. Removed data_in_len and data_out_len from ata_command structure; the lengths are implied by the sector count and the presence of the data_in or data_out pointers. Changed AoE code to use subcommands by default, and made aoe_issue() nonblocking (with completion via async_wait()). --- src/drivers/ata/aoedev.c | 3 +- src/drivers/block/ata.c | 9 +- src/include/gpxe/aoe.h | 20 +++-- src/include/gpxe/async.h | 62 ++++++++++++++ src/include/gpxe/ata.h | 18 ++-- src/net/aoe.c | 176 +++++++++++++++++---------------------- 6 files changed, 166 insertions(+), 122 deletions(-) create mode 100644 src/include/gpxe/async.h diff --git a/src/drivers/ata/aoedev.c b/src/drivers/ata/aoedev.c index 0350bc09..9679fc1b 100644 --- a/src/drivers/ata/aoedev.c +++ b/src/drivers/ata/aoedev.c @@ -37,7 +37,8 @@ static int aoe_command ( struct ata_device *ata, struct aoe_device *aoedev = container_of ( ata, struct aoe_device, ata ); - return aoe_issue_split ( &aoedev->aoe, command ); + aoe_issue ( &aoedev->aoe, command ); + return async_wait ( &aoedev->aoe.aop ); } /** diff --git a/src/drivers/block/ata.c b/src/drivers/block/ata.c index 3fcfa71c..e0df3673 100644 --- a/src/drivers/block/ata.c +++ b/src/drivers/block/ata.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -73,7 +74,6 @@ static int ata_read ( struct block_device *blockdev, uint64_t block, command.cb.device |= command.cb.lba.bytes.low_prev; command.cb.cmd_stat = ( ata->lba48 ? ATA_CMD_READ_EXT : ATA_CMD_READ ); command.data_in = buffer; - command.data_in_len = ( count * blockdev->blksize ); return ata_command ( ata, &command ); } @@ -101,7 +101,6 @@ static int ata_write ( struct block_device *blockdev, uint64_t block, command.cb.cmd_stat = ( ata->lba48 ? ATA_CMD_WRITE_EXT : ATA_CMD_WRITE ); command.data_out = buffer; - command.data_out_len = ( count * blockdev->blksize ); return ata_command ( ata, &command ); } @@ -119,12 +118,12 @@ static int ata_identify ( struct block_device *blockdev ) { /* Issue IDENTIFY */ memset ( &command, 0, sizeof ( command ) ); - command.cb.count.native = 1; /* n/a according to spec, but at least - * AoE vblade devices require it. */ + command.cb.count.native = 1; command.cb.device = ( ata->device | ATA_DEV_OBSOLETE | ATA_DEV_LBA ); command.cb.cmd_stat = ATA_CMD_IDENTIFY; command.data_in = virt_to_user ( &identity ); - command.data_in_len = sizeof ( identity ); + linker_assert ( sizeof ( identity ) == ATA_SECTOR_SIZE, + __ata_identity_bad_size__ ); if ( ( rc = ata_command ( ata, &command ) ) != 0 ) return rc; diff --git a/src/include/gpxe/aoe.h b/src/include/gpxe/aoe.h index 2e13d7a8..fb692224 100644 --- a/src/include/gpxe/aoe.h +++ b/src/include/gpxe/aoe.h @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include /** An AoE ATA command */ @@ -89,29 +91,31 @@ struct aoe_session { /** Target MAC address */ uint8_t target[ETH_ALEN]; - /** Tag for current command */ + /** Tag for current AoE command */ uint32_t tag; + /** Current ATA command */ struct ata_command *command; - /** Status of the command */ - int status; + /** Overall status of current ATA command */ + unsigned int status; /** Byte offset within command's data buffer */ unsigned int command_offset; + /** Asynchronous operation for this command */ + struct async_operation aop; + + /** Retransmission timer */ + struct retry_timer timer; }; #define AOE_STATUS_ERR_MASK 0x0f /**< Error portion of status code */ #define AOE_STATUS_PENDING 0x80 /**< Command pending */ -#define AOE_STATUS_UNDERRUN 0x40 /**< Buffer overrun */ -#define AOE_STATUS_OVERRUN 0x20 /**< Buffer underrun */ /** Maximum number of sectors per packet */ #define AOE_MAX_COUNT 2 extern void aoe_open ( struct aoe_session *aoe ); extern void aoe_close ( struct aoe_session *aoe ); -extern int aoe_issue ( struct aoe_session *aoe, struct ata_command *command ); -extern int aoe_issue_split ( struct aoe_session *aoe, - struct ata_command *command ); +extern void aoe_issue ( struct aoe_session *aoe, struct ata_command *command ); /** An AoE device */ struct aoe_device { diff --git a/src/include/gpxe/async.h b/src/include/gpxe/async.h new file mode 100644 index 00000000..8a681978 --- /dev/null +++ b/src/include/gpxe/async.h @@ -0,0 +1,62 @@ +#ifndef _GPXE_ASYNC_H +#define _GPXE_ASYNC_H + +/** @file + * + * Asynchronous operations + * + */ + +#include +#include + +/** An asynchronous operation */ +struct async_operation { + /** Operation status + * + * This is an error code as defined in errno.h, plus an offset + * of EINPROGRESS. This means that a status value of 0 + * corresponds to a return status code of -EINPROGRESS, + * i.e. that the default state of an asynchronous operation is + * "not yet completed". + */ + int status; +}; + +/** + * Set asynchronous operation status + * + * @v aop Asynchronous operation + * @v rc Return status code + */ +static inline __attribute__ (( always_inline )) void +async_set_status ( struct async_operation *aop, int rc ) { + aop->status = ( rc + EINPROGRESS ); +} + +/** + * Get asynchronous operation status + * + * @v aop Asynchronous operation + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +async_status ( struct async_operation *aop ) { + return ( aop->status - EINPROGRESS ); +} + +/** + * Flag asynchronous operation as complete + * + * @v aop Asynchronous operation + * @v rc Return status code + */ +static inline __attribute__ (( always_inline )) void +async_done ( struct async_operation *aop, int rc ) { + assert ( rc != -EINPROGRESS ); + async_set_status ( aop, rc ); +} + +extern int async_wait ( struct async_operation *aop ); + +#endif /* _GPXE_ASYNC_H */ diff --git a/src/include/gpxe/ata.h b/src/include/gpxe/ata.h index 195e361c..e0fca7af 100644 --- a/src/include/gpxe/ata.h +++ b/src/include/gpxe/ata.h @@ -139,20 +139,18 @@ struct ata_cb { struct ata_command { /** ATA command block */ struct ata_cb cb; - /** Data-out buffer (may be NULL) */ + /** Data-out buffer (may be NULL) + * + * If non-NULL, this buffer must be ata_command::cb::count + * sectors in size. + */ userptr_t data_out; - /** Data-out buffer length + /** Data-in buffer (may be NULL) * - * Must be zero if @c data_out is NULL + * If non-NULL, this buffer must be ata_command::cb::count + * sectors in size. */ - size_t data_out_len; - /** Data-in buffer (may be NULL) */ userptr_t data_in; - /** Data-in buffer length - * - * Must be zero if @c data_in is NULL - */ - size_t data_in_len; }; /** diff --git a/src/net/aoe.c b/src/net/aoe.c index 7267cf0e..a91e0097 100644 --- a/src/net/aoe.c +++ b/src/net/aoe.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include /** @file @@ -42,6 +42,22 @@ struct net_protocol aoe_protocol; /** List of all AoE sessions */ static LIST_HEAD ( aoe_sessions ); +/** + * Mark current AoE command complete + * + * @v aoe AoE session + * @v rc Return status code + */ +static void aoe_done ( struct aoe_session *aoe, int rc ) { + + /* Record overall command status */ + aoe->command->cb.cmd_stat = aoe->status; + aoe->command = NULL; + + /* Mark async operation as complete */ + async_done ( &aoe->aop, rc ); +} + /** * Send AoE command * @@ -56,10 +72,18 @@ static int aoe_send_command ( struct aoe_session *aoe ) { struct pk_buff *pkb; struct aoehdr *aoehdr; struct aoecmd *aoecmd; + unsigned int count; + unsigned int data_out_len; + + /* 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 ); /* Create outgoing packet buffer */ pkb = alloc_pkb ( ETH_HLEN + sizeof ( *aoehdr ) + sizeof ( *aoecmd ) + - command->data_out_len ); + data_out_len ); if ( ! pkb ) return -ENOMEM; pkb->net_protocol = &aoe_protocol; @@ -78,18 +102,17 @@ static int aoe_send_command ( struct aoe_session *aoe ) { 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 ) | - ( command->data_out_len ? AOE_FL_WRITE : 0 ) ); + ( data_out_len ? AOE_FL_WRITE : 0 ) ); aoecmd->err_feat = command->cb.err_feat.bytes.cur; - aoecmd->count = command->cb.count.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 ( pkb_put ( pkb, command->data_out_len ), - command->data_out, aoe->command_offset, - command->data_out_len ); + copy_from_user ( pkb_put ( pkb, data_out_len ), command->data_out, + aoe->command_offset, data_out_len ); /* Send packet */ return net_transmit_via ( pkb, aoe->netdev ); @@ -106,33 +129,51 @@ static int aoe_rx_response ( struct aoe_session *aoe, struct aoehdr *aoehdr, unsigned int len ) { struct aoecmd *aoecmd = aoehdr->arg.command; struct ata_command *command = aoe->command; - unsigned int data_in_len; + unsigned int rx_data_len; + unsigned int count; + unsigned int data_len; /* Sanity check */ if ( len < ( sizeof ( *aoehdr ) + sizeof ( *aoecmd ) ) ) return -EINVAL; + rx_data_len = ( len - sizeof ( *aoehdr ) - sizeof ( *aoecmd ) ); - /* Set overall status code */ - aoe->status = ( ( aoehdr->ver_flags & AOE_FL_ERROR ) ? - aoehdr->error : 0 ); - - /* Copy ATA results */ - command->cb.err_feat.bytes.cur = aoecmd->err_feat; - command->cb.count.bytes.cur = aoecmd->count; - command->cb.cmd_stat = aoecmd->cmd_stat; - command->cb.lba.native = le64_to_cpu ( aoecmd->lba.u64 ); - command->cb.lba.bytes.pad = 0; - - /* Copy data payload */ - data_in_len = ( len - sizeof ( *aoehdr ) - sizeof ( *aoecmd ) ); - if ( data_in_len > command->data_in_len ) { - data_in_len = command->data_in_len; - aoe->status |= AOE_STATUS_OVERRUN; - } else if ( data_in_len < command->data_in_len ) { - aoe->status |= AOE_STATUS_UNDERRUN; + /* Check for fatal errors */ + if ( aoehdr->ver_flags & AOE_FL_ERROR ) { + aoe_done ( aoe, -EIO ); + return 0; } - copy_to_user ( command->data_in, aoe->command_offset, - aoecmd->data, data_in_len ); + + /* Calculate count and data_len for this subcommand */ + count = command->cb.count.native; + if ( count > AOE_MAX_COUNT ) + count = AOE_MAX_COUNT; + data_len = count * ATA_SECTOR_SIZE; + + /* Merge into overall ATA status */ + aoe->status |= aoecmd->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 ); + } + + /* Update ATA command and offset */ + aoe->command_offset += data_len; + command->cb.lba.native += count; + command->cb.count.native -= count; + + /* Check for operation complete */ + if ( ! command->cb.count.native ) { + aoe_done ( aoe, 0 ); + return 0; + } + + /* Transmit next portion of request */ + aoe_send_command ( aoe ); return 0; } @@ -229,79 +270,18 @@ void aoe_close ( struct aoe_session *aoe ) { } /** - * Kick an AoE session into life + * Issue ATA command via an open AoE session * * @v aoe AoE session + * @v command ATA command * - * Transmits an AoE request. Call this function to issue a new - * command, or when a retransmission timer expires. + * Only one command may be issued concurrently per session. This call + * is non-blocking; use async_wait() to wait for the command to + * complete. */ -void aoe_kick ( struct aoe_session *aoe ) { +void aoe_issue ( struct aoe_session *aoe, struct ata_command *command ) { + aoe->command = command; + aoe->status = 0; + aoe->command_offset = 0; aoe_send_command ( aoe ); } - -/** - * Issue ATA command via an open AoE session - * - * @v aoe AoE session - * @v command ATA command - * @ret rc Return status code - * - * The ATA command must fit within a single AoE frame (i.e. the sector - * count must not exceed AOE_MAX_COUNT). - */ -int aoe_issue ( struct aoe_session *aoe, struct ata_command *command ) { - aoe->command = command; - aoe->status = AOE_STATUS_PENDING; - - aoe_kick ( aoe ); - while ( aoe->status & AOE_STATUS_PENDING ) { - step(); - } - aoe->command = NULL; - - return ( ( aoe->status & AOE_STATUS_ERR_MASK ) ? -EIO : 0 ); -} - -/** - * Issue ATA command via an open AoE session - * - * @v aoe AoE session - * @v command ATA command - * @ret rc Return status code - * - * The ATA command will be split into several smaller ATA commands, - * each with a sector count no larger than AOE_MAX_COUNT. - */ -int aoe_issue_split ( struct aoe_session *aoe, struct ata_command *command ) { - struct ata_command subcommand; - unsigned int offset; - unsigned int count; - unsigned int data_len; - unsigned int status = 0; - int rc = 0; - - /* Split ATA command into AoE-sized subcommands */ - for ( offset = 0; offset < command->cb.count.native; offset += count ){ - memcpy ( &subcommand, command, sizeof ( subcommand ) ); - count = ( command->cb.count.native - offset ); - if ( count > AOE_MAX_COUNT ) - count = AOE_MAX_COUNT; - data_len = count * ATA_SECTOR_SIZE; - if ( subcommand.data_in_len ) - subcommand.data_in_len = data_len; - if ( subcommand.data_out_len ) - subcommand.data_out_len = data_len; - aoe->command_offset = ( offset * ATA_SECTOR_SIZE ); - subcommand.cb.lba.native += offset; - subcommand.cb.count.native = count; - if ( ( rc = aoe_issue ( aoe, &subcommand ) ) != 0 ) - goto done; - status |= subcommand.cb.cmd_stat; - } - command->cb.cmd_stat = status; - - done: - aoe->command_offset = 0; - return rc; -}