diff options
author | Michael Brown | 2017-03-27 17:20:34 +0200 |
---|---|---|
committer | Michael Brown | 2017-03-28 18:12:48 +0200 |
commit | 7cfdd769aac76d605aa31146c69ba518b194bea7 (patch) | |
tree | 7c09e144792833f81297e6dacf0823733a2a399a /src/core/sanboot.c | |
parent | [block] Ignore redundant xfer_window_changed() messages (diff) | |
download | ipxe-7cfdd769aac76d605aa31146c69ba518b194bea7.tar.gz ipxe-7cfdd769aac76d605aa31146c69ba518b194bea7.tar.xz ipxe-7cfdd769aac76d605aa31146c69ba518b194bea7.zip |
[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>
Diffstat (limited to 'src/core/sanboot.c')
-rw-r--r-- | src/core/sanboot.c | 101 |
1 files changed, 97 insertions, 4 deletions
diff --git a/src/core/sanboot.c b/src/core/sanboot.c index 03beae79..f134f76a 100644 --- a/src/core/sanboot.c +++ b/src/core/sanboot.c @@ -119,8 +119,10 @@ static void sandev_free ( struct refcnt *refcnt ) { assert ( ! timer_running ( &sandev->timer ) ); assert ( ! sandev->active ); assert ( list_empty ( &sandev->opened ) ); - for ( i = 0 ; i < sandev->paths ; i++ ) + for ( i = 0 ; i < sandev->paths ; i++ ) { uri_put ( sandev->path[i].uri ); + assert ( sandev->path[i].desc == NULL ); + } free ( sandev ); } @@ -199,6 +201,15 @@ static int sanpath_open ( struct san_path *sanpath ) { return rc; } + /* Update ACPI descriptor, if applicable */ + if ( ! ( sandev->flags & SAN_NO_DESCRIBE ) ) { + if ( sanpath->desc ) + acpi_del ( sanpath->desc ); + sanpath->desc = acpi_describe ( &sanpath->block ); + if ( sanpath->desc ) + acpi_add ( sanpath->desc ); + } + /* Start process */ process_add ( &sanpath->process ); @@ -607,6 +618,72 @@ int sandev_rw ( struct san_device *sandev, uint64_t lba, } /** + * Describe SAN device + * + * @v sandev SAN device + * @ret rc Return status code + * + * Allow connections to progress until all existent path descriptors + * are complete. + */ +static int sandev_describe ( struct san_device *sandev ) { + struct san_path *sanpath; + struct acpi_descriptor *desc; + int rc; + + /* Wait for all paths to be either described or closed */ + while ( 1 ) { + + /* Allow connections to progress */ + step(); + + /* Fail if any closed path has an incomplete descriptor */ + list_for_each_entry ( sanpath, &sandev->closed, list ) { + desc = sanpath->desc; + if ( ! desc ) + continue; + if ( ( rc = desc->model->complete ( desc ) ) != 0 ) { + DBGC ( sandev, "SAN %#02x.%d could not be " + "described: %s\n", sandev->drive, + sanpath->index, strerror ( rc ) ); + return rc; + } + } + + /* Succeed if no paths have an incomplete descriptor */ + rc = 0; + list_for_each_entry ( sanpath, &sandev->opened, list ) { + desc = sanpath->desc; + if ( ! desc ) + continue; + if ( ( rc = desc->model->complete ( desc ) ) != 0 ) + break; + } + if ( rc == 0 ) + return 0; + } +} + +/** + * Remove SAN device descriptors + * + * @v sandev SAN device + */ +static void sandev_undescribe ( struct san_device *sandev ) { + struct san_path *sanpath; + unsigned int i; + + /* Remove all ACPI descriptors */ + for ( i = 0 ; i < sandev->paths ; i++ ) { + sanpath = &sandev->path[i]; + if ( sanpath->desc ) { + acpi_del ( sanpath->desc ); + sanpath->desc = NULL; + } + } +} + +/** * Configure SAN device as a CD-ROM, if applicable * * @v sandev SAN device @@ -729,18 +806,25 @@ struct san_device * alloc_sandev ( struct uri **uris, unsigned int count, * Register SAN device * * @v sandev SAN device + * @v drive Drive number + * @v flags Flags * @ret rc Return status code */ -int register_sandev ( struct san_device *sandev ) { +int register_sandev ( struct san_device *sandev, unsigned int drive, + unsigned int flags ) { int rc; /* Check that drive number is not in use */ - if ( sandev_find ( sandev->drive ) != NULL ) { - DBGC ( sandev, "SAN %#02x is already in use\n", sandev->drive ); + if ( sandev_find ( drive ) != NULL ) { + DBGC ( sandev, "SAN %#02x is already in use\n", drive ); rc = -EADDRINUSE; goto err_in_use; } + /* Record drive number and flags */ + sandev->drive = drive; + sandev->flags = flags; + /* Check that device is capable of being opened (i.e. that all * URIs are well-formed and that at least one path is * working). @@ -748,6 +832,10 @@ int register_sandev ( struct san_device *sandev ) { if ( ( rc = sandev_reopen ( sandev ) ) != 0 ) goto err_reopen; + /* Describe device */ + if ( ( rc = sandev_describe ( sandev ) ) != 0 ) + goto err_describe; + /* Read device capacity */ if ( ( rc = sandev_command ( sandev, sandev_command_read_capacity, NULL ) ) != 0 ) @@ -766,8 +854,10 @@ int register_sandev ( struct san_device *sandev ) { list_del ( &sandev->list ); err_iso9660: err_capacity: + err_describe: err_reopen: sandev_restart ( sandev, rc ); + sandev_undescribe ( sandev ); err_in_use: return rc; } @@ -788,6 +878,9 @@ void unregister_sandev ( struct san_device *sandev ) { /* Shut down interfaces */ sandev_restart ( sandev, 0 ); + /* Remove ACPI descriptors */ + sandev_undescribe ( sandev ); + DBGC ( sandev, "SAN %#02x unregistered\n", sandev->drive ); } |