[block] Describe all SAN devices via ACPI tables

Describe all SAN devices via ACPI tables such as the iBFT.  For tables
that can describe only a single device (i.e. the aBFT and sBFT), one
table is installed per device.  For multi-device tables (i.e. the
iBFT), all devices are described in a single table.

An underlying SAN device connection may be closed at the time that we
need to construct an ACPI table.  We therefore introduce the concept
of an "ACPI descriptor" which enables the SAN boot code to maintain an
opaque pointer to the underlying object, and an "ACPI model" which can
build tables from a list of such descriptors.  This separates the
lifecycles of ACPI descriptions from the lifecycles of the block
device interfaces, and allows for construction of the ACPI tables even
if the block device interface has been closed.

For a multipath SAN device, iPXE will wait until sufficient
information is available to describe all devices but will not wait for
all paths to connect successfully.  For example: with a multipath
iSCSI boot iPXE will wait until at least one path has become available
and name resolution has completed on all other paths.  We do this
since the iBFT has to include IP addresses rather than DNS names.  We
will commence booting without waiting for the inactive paths to either
become available or close; this avoids unnecessary boot delays.

Note that the Linux kernel will refuse to accept an iBFT with more
than two NIC or target structures.  We therefore describe only the
NICs that are actually required in order to reach the described
targets.  Any iBFT with at most two targets is therefore guaranteed to
describe at most two NICs.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2017-03-27 18:20:34 +03:00
parent 414b4fc9c5
commit 7cfdd769aa
21 changed files with 930 additions and 491 deletions

View File

@@ -53,6 +53,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
FEATURE ( FEATURE_PROTOCOL, "AoE", DHCP_EB_FEATURE_AOE, 1 );
struct net_protocol aoe_protocol __net_protocol;
struct acpi_model abft_model __acpi_model;
/******************************************************************************
*
@@ -91,6 +92,9 @@ struct aoe_device {
struct interface config;
/** Device is configued */
int configured;
/** ACPI descriptor */
struct acpi_descriptor desc;
};
/** An AoE command */
@@ -790,32 +794,13 @@ static struct device * aoedev_identify_device ( struct aoe_device *aoedev ) {
}
/**
* Describe AoE device in an ACPI table
* Get AoE ACPI descriptor
*
* @v aoedev AoE device
* @v acpi ACPI table
* @v len Length of ACPI table
* @ret rc Return status code
* @ret desc ACPI descriptor
*/
static int aoedev_describe ( struct aoe_device *aoedev,
struct acpi_description_header *acpi,
size_t len ) {
struct abft_table *abft =
container_of ( acpi, struct abft_table, acpi );
/* Sanity check */
if ( len < sizeof ( *abft ) )
return -ENOBUFS;
/* Populate table */
abft->acpi.signature = cpu_to_le32 ( ABFT_SIG );
abft->acpi.length = cpu_to_le32 ( sizeof ( *abft ) );
abft->acpi.revision = 1;
abft->shelf = cpu_to_le16 ( aoedev->major );
abft->slot = aoedev->minor;
memcpy ( abft->mac, aoedev->netdev->ll_addr, sizeof ( abft->mac ) );
return 0;
static struct acpi_descriptor * aoedev_describe ( struct aoe_device *aoedev ) {
return &aoedev->desc;
}
/** AoE device ATA interface operations */
@@ -869,6 +854,7 @@ static int aoedev_open ( struct interface *parent, struct net_device *netdev,
aoedev->minor = minor;
memcpy ( aoedev->target, netdev->ll_broadcast,
netdev->ll_protocol->ll_addr_len );
acpi_init ( &aoedev->desc, &abft_model, &aoedev->refcnt );
/* Initiate configuration */
if ( ( rc = aoedev_cfg_command ( aoedev, &aoedev->config ) ) < 0 ) {
@@ -1059,3 +1045,61 @@ struct uri_opener aoe_uri_opener __uri_opener = {
.scheme = "aoe",
.open = aoe_open,
};
/******************************************************************************
*
* AoE boot firmware table (aBFT)
*
******************************************************************************
*/
/**
* Check if AoE boot firmware table descriptor is complete
*
* @v desc ACPI descriptor
* @ret rc Return status code
*/
static int abft_complete ( struct acpi_descriptor *desc __unused ) {
return 0;
}
/**
* Install AoE boot firmware table(s)
*
* @v install Installation method
* @ret rc Return status code
*/
static int abft_install ( int ( * install ) ( struct acpi_header *acpi ) ) {
struct aoe_device *aoedev;
struct abft_table abft;
int rc;
list_for_each_entry ( aoedev, &abft_model.descs, desc.list ) {
/* Populate table */
memset ( &abft, 0, sizeof ( abft ) );
abft.acpi.signature = cpu_to_le32 ( ABFT_SIG );
abft.acpi.length = cpu_to_le32 ( sizeof ( abft ) );
abft.acpi.revision = 1;
abft.shelf = cpu_to_le16 ( aoedev->major );
abft.slot = aoedev->minor;
memcpy ( abft.mac, aoedev->netdev->ll_addr,
sizeof ( abft.mac ) );
/* Install table */
if ( ( rc = install ( &abft.acpi ) ) != 0 ) {
DBGC ( aoedev, "AoE %s could not install aBFT: %s\n",
aoedev_name ( aoedev ), strerror ( rc ) );
return rc;
}
}
return 0;
}
/** aBFT model */
struct acpi_model abft_model __acpi_model = {
.descs = LIST_HEAD_INIT ( abft_model.descs ),
.complete = abft_complete,
.install = abft_install,
};