summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown2017-03-26 14:12:11 +0200
committerMichael Brown2017-03-26 15:06:02 +0200
commitbb5a54b79a414082d0b39d478a8b3332c56d68e5 (patch)
treec022d5b8ed56be56414eb007273aeacd57c44d37
parent[block] Add dummy SAN device (diff)
downloadipxe-bb5a54b79a414082d0b39d478a8b3332c56d68e5.tar.gz
ipxe-bb5a54b79a414082d0b39d478a8b3332c56d68e5.tar.xz
ipxe-bb5a54b79a414082d0b39d478a8b3332c56d68e5.zip
[block] Add basic multipath support
Add basic support for multipath block devices. The "sanboot" and "sanhook" commands now accept a list of SAN URIs. We open all URIs concurrently. The first connection to become available for issuing block device commands is marked as the active path and used for all subsequent commands; all other connections are then closed. Whenever the active path fails, we reopen all URIs and repeat the process. Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r--src/arch/x86/interface/pcbios/int13.c20
-rw-r--r--src/core/dummy_sanboot.c8
-rw-r--r--src/core/null_sanboot.c5
-rw-r--r--src/core/sanboot.c294
-rw-r--r--src/hci/commands/sanboot_cmd.c30
-rw-r--r--src/include/ipxe/sanboot.h46
-rw-r--r--src/include/usr/autoboot.h3
-rw-r--r--src/interface/efi/efi_block.c25
-rw-r--r--src/usr/autoboot.c14
-rw-r--r--src/usr/pxemenu.c2
10 files changed, 323 insertions, 124 deletions
diff --git a/src/arch/x86/interface/pcbios/int13.c b/src/arch/x86/interface/pcbios/int13.c
index 23cfefca..c322440e 100644
--- a/src/arch/x86/interface/pcbios/int13.c
+++ b/src/arch/x86/interface/pcbios/int13.c
@@ -833,6 +833,7 @@ static int int13_extended_seek ( struct san_device *sandev,
*/
static int int13_device_path_info ( struct san_device *sandev,
struct edd_device_path_information *dpi ) {
+ struct san_path *sanpath;
struct device *device;
struct device_description *desc;
unsigned int i;
@@ -843,9 +844,11 @@ static int int13_device_path_info ( struct san_device *sandev,
if ( sandev_needs_reopen ( sandev ) &&
( ( rc = sandev_reopen ( sandev ) ) != 0 ) )
return rc;
+ sanpath = sandev->active;
+ assert ( sanpath != NULL );
/* Get underlying hardware device */
- device = identify_device ( &sandev->block );
+ device = identify_device ( &sanpath->block );
if ( ! device ) {
DBGC ( sandev, "INT13 drive %02x cannot identify hardware "
"device\n", sandev->drive );
@@ -869,7 +872,7 @@ static int int13_device_path_info ( struct san_device *sandev,
}
/* Get EDD block device description */
- if ( ( rc = edd_describe ( &sandev->block, &dpi->interface_type,
+ if ( ( rc = edd_describe ( &sanpath->block, &dpi->interface_type,
&dpi->device_path ) ) != 0 ) {
DBGC ( sandev, "INT13 drive %02x cannot identify block device: "
"%s\n", sandev->drive, strerror ( rc ) );
@@ -1199,14 +1202,16 @@ static void int13_unhook_vector ( void ) {
/**
* Hook INT 13 SAN device
*
- * @v uri URI
* @v drive Drive number
+ * @v uris List of URIs
+ * @v count Number of URIs
* @ret drive Drive number, or negative error
*
* Registers the drive with the INT 13 emulation subsystem, and hooks
* the INT 13 interrupt vector (if not already hooked).
*/
-static int int13_hook ( struct uri *uri, unsigned int drive ) {
+static int int13_hook ( unsigned int drive, struct uri **uris,
+ unsigned int count ) {
struct san_device *sandev;
struct int13_data *int13;
unsigned int natural_drive;
@@ -1223,7 +1228,7 @@ static int int13_hook ( struct uri *uri, unsigned int drive ) {
drive = natural_drive;
/* Allocate SAN device */
- sandev = alloc_sandev ( uri, sizeof ( *int13 ) );
+ sandev = alloc_sandev ( uris, count, sizeof ( *int13 ) );
if ( ! sandev ) {
rc = -ENOMEM;
goto err_alloc;
@@ -1525,6 +1530,7 @@ static union xbft_table __bss16 ( xbftab ) __attribute__ (( aligned ( 16 ) ));
*/
static int int13_describe ( unsigned int drive ) {
struct san_device *sandev;
+ struct san_path *sanpath;
struct segoff xbft_address;
int rc;
@@ -1539,6 +1545,8 @@ static int int13_describe ( unsigned int drive ) {
if ( sandev_needs_reopen ( sandev ) &&
( ( rc = sandev_reopen ( sandev ) ) != 0 ) )
return rc;
+ sanpath = sandev->active;
+ assert ( sanpath != NULL );
/* Clear table */
memset ( &xbftab, 0, sizeof ( xbftab ) );
@@ -1550,7 +1558,7 @@ static int int13_describe ( unsigned int drive ) {
sizeof ( xbftab.acpi.oem_table_id ) );
/* Fill in remaining parameters */
- if ( ( rc = acpi_describe ( &sandev->block, &xbftab.acpi,
+ if ( ( rc = acpi_describe ( &sanpath->block, &xbftab.acpi,
sizeof ( xbftab ) ) ) != 0 ) {
DBGC ( sandev, "INT13 drive %02x could not create ACPI "
"description: %s\n", sandev->drive, strerror ( rc ) );
diff --git a/src/core/dummy_sanboot.c b/src/core/dummy_sanboot.c
index 16347747..64d5206f 100644
--- a/src/core/dummy_sanboot.c
+++ b/src/core/dummy_sanboot.c
@@ -35,16 +35,18 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* Hook dummy SAN device
*
- * @v uri URI
* @v drive Drive number
+ * @v uris List of URIs
+ * @v count Number of URIs
* @ret drive Drive number, or negative error
*/
-static int dummy_san_hook ( struct uri *uri, unsigned int drive ) {
+static int dummy_san_hook ( unsigned int drive, struct uri **uris,
+ unsigned int count ) {
struct san_device *sandev;
int rc;
/* Allocate SAN device */
- sandev = alloc_sandev ( uri, 0 );
+ sandev = alloc_sandev ( uris, count, 0 );
if ( ! sandev ) {
rc = -ENOMEM;
goto err_alloc;
diff --git a/src/core/null_sanboot.c b/src/core/null_sanboot.c
index 31a8a56b..42fb0682 100644
--- a/src/core/null_sanboot.c
+++ b/src/core/null_sanboot.c
@@ -26,8 +26,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <errno.h>
#include <ipxe/sanboot.h>
-static int null_san_hook ( struct uri *uri __unused,
- unsigned int drive __unused ) {
+static int null_san_hook ( unsigned int drive __unused,
+ struct uri **uris __unused,
+ unsigned int count __unused ) {
return -EOPNOTSUPP;
}
diff --git a/src/core/sanboot.c b/src/core/sanboot.c
index 88d254ff..90bf763b 100644
--- a/src/core/sanboot.c
+++ b/src/core/sanboot.c
@@ -101,9 +101,13 @@ struct san_device * sandev_find ( unsigned int drive ) {
static void sandev_free ( struct refcnt *refcnt ) {
struct san_device *sandev =
container_of ( refcnt, struct san_device, refcnt );
+ struct san_path *sanpath;
assert ( ! timer_running ( &sandev->timer ) );
- uri_put ( sandev->uri );
+ assert ( ! sandev->active );
+ assert ( list_empty ( &sandev->opened ) );
+ list_for_each_entry ( sanpath, &sandev->closed, list )
+ uri_put ( sanpath->uri );
free ( sandev );
}
@@ -163,85 +167,92 @@ static void sandev_command_expired ( struct retry_timer *timer,
}
/**
- * Restart SAN device interface
+ * Open SAN path
*
- * @v sandev SAN device
- * @v rc Reason for restart
+ * @v sanpath SAN path
+ * @ret rc Return status code
*/
-static void sandev_restart ( struct san_device *sandev, int rc ) {
+static int sanpath_open ( struct san_path *sanpath ) {
+ struct san_device *sandev = sanpath->sandev;
+ int rc;
- /* Restart block device interface */
- intfs_restart ( rc, &sandev->command, &sandev->block, NULL );
+ /* Sanity check */
+ list_check_contains_entry ( sanpath, &sandev->closed, list );
- /* Close any outstanding command */
- sandev_command_close ( sandev, rc );
+ /* Open interface */
+ if ( ( rc = xfer_open_uri ( &sanpath->block, sanpath->uri ) ) != 0 ) {
+ DBGC ( sandev, "SAN %#02x.%d could not (re)open URI: "
+ "%s\n", sandev->drive, sanpath->index, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Start process */
+ process_add ( &sanpath->process );
- /* Record device error */
- sandev->block_rc = rc;
+ /* Mark as opened */
+ list_del ( &sanpath->list );
+ list_add_tail ( &sanpath->list, &sandev->opened );
+
+ /* Record as in progress */
+ sanpath->path_rc = -EINPROGRESS;
+
+ return 0;
}
/**
- * (Re)open SAN device
+ * Close SAN path
*
- * @v sandev SAN device
- * @ret rc Return status code
- *
- * This function will block until the device is available.
+ * @v sanpath SAN path
+ * @v rc Reason for close
*/
-int sandev_reopen ( struct san_device *sandev ) {
- int rc;
-
- /* Close any outstanding command and restart interface */
- sandev_restart ( sandev, -ECONNRESET );
-
- /* Mark device as being not yet open */
- sandev->block_rc = -EINPROGRESS;
-
- /* Open block device interface */
- if ( ( rc = xfer_open_uri ( &sandev->block, sandev->uri ) ) != 0 ) {
- DBGC ( sandev, "SAN %#02x could not (re)open URI: %s\n",
- sandev->drive, strerror ( rc ) );
- return rc;
+static void sanpath_close ( struct san_path *sanpath, int rc ) {
+ struct san_device *sandev = sanpath->sandev;
+
+ /* Record status */
+ sanpath->path_rc = rc;
+
+ /* Mark as closed */
+ list_del ( &sanpath->list );
+ list_add_tail ( &sanpath->list, &sandev->closed );
+
+ /* Stop process */
+ process_del ( &sanpath->process );
+
+ /* Restart interfaces, avoiding potential loops */
+ if ( sanpath == sandev->active ) {
+ intfs_restart ( rc, &sandev->command, &sanpath->block, NULL );
+ sandev->active = NULL;
+ sandev_command_close ( sandev, rc );
+ } else {
+ intf_restart ( &sanpath->block, rc );
}
-
- /* Wait for device to become available */
- while ( sandev->block_rc == -EINPROGRESS ) {
- step();
- if ( xfer_window ( &sandev->block ) != 0 ) {
- sandev->block_rc = 0;
- return 0;
- }
- }
-
- DBGC ( sandev, "SAN %#02x never became available: %s\n",
- sandev->drive, strerror ( sandev->block_rc ) );
- return sandev->block_rc;
}
/**
* Handle closure of underlying block device interface
*
- * @v sandev SAN device
- * @ret rc Reason for close
+ * @v sanpath SAN path
+ * @v rc Reason for close
*/
-static void sandev_block_close ( struct san_device *sandev, int rc ) {
+static void sanpath_block_close ( struct san_path *sanpath, int rc ) {
+ struct san_device *sandev = sanpath->sandev;
/* Any closure is an error from our point of view */
if ( rc == 0 )
rc = -ENOTCONN;
- DBGC ( sandev, "SAN %#02x went away: %s\n",
- sandev->drive, strerror ( rc ) );
+ DBGC ( sandev, "SAN %#02x.%d closed: %s\n",
+ sandev->drive, sanpath->index, strerror ( rc ) );
- /* Close any outstanding command and restart interface */
- sandev_restart ( sandev, rc );
+ /* Close path */
+ sanpath_close ( sanpath, rc );
}
/**
- * Check SAN device flow control window
+ * Check flow control window
*
- * @v sandev SAN device
+ * @v sanpath SAN path
*/
-static size_t sandev_block_window ( struct san_device *sandev __unused ) {
+static size_t sanpath_block_window ( struct san_path *sanpath __unused ) {
/* We are never ready to receive data via this interface.
* This prevents objects that support both block and stream
@@ -250,15 +261,125 @@ static size_t sandev_block_window ( struct san_device *sandev __unused ) {
return 0;
}
-/** SAN device block interface operations */
-static struct interface_operation sandev_block_op[] = {
- INTF_OP ( intf_close, struct san_device *, sandev_block_close ),
- INTF_OP ( xfer_window, struct san_device *, sandev_block_window ),
+/**
+ * SAN path process
+ *
+ * @v sanpath SAN path
+ */
+static void sanpath_step ( struct san_path *sanpath ) {
+ struct san_device *sandev = sanpath->sandev;
+
+ /* Wait until path has become available */
+ if ( ! xfer_window ( &sanpath->block ) )
+ return;
+
+ /* Record status */
+ sanpath->path_rc = 0;
+
+ /* Mark as active path or close as applicable */
+ if ( ! sandev->active ) {
+ DBGC ( sandev, "SAN %#02x.%d is active\n",
+ sandev->drive, sanpath->index );
+ sandev->active = sanpath;
+ } else {
+ DBGC ( sandev, "SAN %#02x.%d is available\n",
+ sandev->drive, sanpath->index );
+ sanpath_close ( sanpath, 0 );
+ }
+}
+
+/** SAN path block interface operations */
+static struct interface_operation sanpath_block_op[] = {
+ INTF_OP ( intf_close, struct san_path *, sanpath_block_close ),
+ INTF_OP ( xfer_window, struct san_path *, sanpath_block_window ),
+ INTF_OP ( xfer_window_changed, struct san_path *, sanpath_step ),
};
-/** SAN device block interface descriptor */
-static struct interface_descriptor sandev_block_desc =
- INTF_DESC ( struct san_device, block, sandev_block_op );
+/** SAN path block interface descriptor */
+static struct interface_descriptor sanpath_block_desc =
+ INTF_DESC ( struct san_path, block, sanpath_block_op );
+
+/** SAN path process descriptor */
+static struct process_descriptor sanpath_process_desc =
+ PROC_DESC_ONCE ( struct san_path, process, sanpath_step );
+
+/**
+ * Restart SAN device interface
+ *
+ * @v sandev SAN device
+ * @v rc Reason for restart
+ */
+static void sandev_restart ( struct san_device *sandev, int rc ) {
+ struct san_path *sanpath;
+
+ /* Restart all block device interfaces */
+ while ( ( sanpath = list_first_entry ( &sandev->opened,
+ struct san_path, list ) ) ) {
+ sanpath_close ( sanpath, rc );
+ }
+
+ /* Clear active path */
+ sandev->active = NULL;
+
+ /* Close any outstanding command */
+ sandev_command_close ( sandev, rc );
+}
+
+/**
+ * (Re)open SAN device
+ *
+ * @v sandev SAN device
+ * @ret rc Return status code
+ *
+ * This function will block until the device is available.
+ */
+int sandev_reopen ( struct san_device *sandev ) {
+ struct san_path *sanpath;
+ int rc;
+
+ /* Close any outstanding command and restart interfaces */
+ sandev_restart ( sandev, -ECONNRESET );
+ assert ( sandev->active == NULL );
+ assert ( list_empty ( &sandev->opened ) );
+
+ /* Open all paths */
+ while ( ( sanpath = list_first_entry ( &sandev->closed,
+ struct san_path, list ) ) ) {
+ if ( ( rc = sanpath_open ( sanpath ) ) != 0 )
+ goto err_open;
+ }
+
+ /* Wait for any device to become available, or for all devices
+ * to fail.
+ */
+ while ( sandev->active == NULL ) {
+ step();
+ if ( list_empty ( &sandev->opened ) ) {
+ /* Get status of the first device to be
+ * closed. Do this on the basis that earlier
+ * errors (e.g. "invalid IQN") are probably
+ * more interesting than later errors
+ * (e.g. "TCP timeout").
+ */
+ rc = -ENODEV;
+ list_for_each_entry ( sanpath, &sandev->closed, list ) {
+ rc = sanpath->path_rc;
+ break;
+ }
+ DBGC ( sandev, "SAN %#02x never became available: %s\n",
+ sandev->drive, strerror ( rc ) );
+ goto err_none;
+ }
+ }
+
+ assert ( ! list_empty ( &sandev->opened ) );
+ return 0;
+
+ err_none:
+ err_open:
+ sandev_restart ( sandev, rc );
+ return rc;
+}
/** SAN device read/write command parameters */
struct san_command_rw_params {
@@ -289,15 +410,19 @@ union san_command_params {
*/
static int sandev_command_rw ( struct san_device *sandev,
const union san_command_params *params ) {
+ struct san_path *sanpath = sandev->active;
size_t len = ( params->rw.count * sandev->capacity.blksize );
int rc;
+ /* Sanity check */
+ assert ( sanpath != NULL );
+
/* Initiate read/write command */
- if ( ( rc = params->rw.block_rw ( &sandev->block, &sandev->command,
+ if ( ( rc = params->rw.block_rw ( &sanpath->block, &sandev->command,
params->rw.lba, params->rw.count,
params->rw.buffer, len ) ) != 0 ) {
- DBGC ( sandev, "SAN %#02x could not initiate read/write: "
- "%s\n", sandev->drive, strerror ( rc ) );
+ DBGC ( sandev, "SAN %#02x.%d could not initiate read/write: "
+ "%s\n", sandev->drive, sanpath->index, strerror ( rc ) );
return rc;
}
@@ -314,13 +439,17 @@ static int sandev_command_rw ( struct san_device *sandev,
static int
sandev_command_read_capacity ( struct san_device *sandev,
const union san_command_params *params __unused){
+ struct san_path *sanpath = sandev->active;
int rc;
+ /* Sanity check */
+ assert ( sanpath != NULL );
+
/* Initiate read capacity command */
- if ( ( rc = block_read_capacity ( &sandev->block,
+ if ( ( rc = block_read_capacity ( &sanpath->block,
&sandev->command ) ) != 0 ) {
- DBGC ( sandev, "SAN %#02x could not initiate read capacity: "
- "%s\n", sandev->drive, strerror ( rc ) );
+ DBGC ( sandev, "SAN %#02x.%d could not initiate read capacity: "
+ "%s\n", sandev->drive, sanpath->index, strerror ( rc ) );
return rc;
}
@@ -526,22 +655,41 @@ static int sandev_parse_iso9660 ( struct san_device *sandev ) {
/**
* Allocate SAN device
*
+ * @v uris List of URIs
+ * @v count Number of URIs
+ * @v priv_size Size of private data
* @ret sandev SAN device, or NULL
*/
-struct san_device * alloc_sandev ( struct uri *uri, size_t priv_size ) {
+struct san_device * alloc_sandev ( struct uri **uris, unsigned int count,
+ size_t priv_size ) {
struct san_device *sandev;
+ struct san_path *sanpath;
+ size_t size;
+ unsigned int i;
/* Allocate and initialise structure */
- sandev = zalloc ( sizeof ( *sandev ) + priv_size );
+ size = ( sizeof ( *sandev ) + ( count * sizeof ( sandev->path[0] ) ) );
+ sandev = zalloc ( size + priv_size );
if ( ! sandev )
return NULL;
ref_init ( &sandev->refcnt, sandev_free );
- sandev->uri = uri_get ( uri );
- intf_init ( &sandev->block, &sandev_block_desc, &sandev->refcnt );
- sandev->block_rc = -EINPROGRESS;
intf_init ( &sandev->command, &sandev_command_desc, &sandev->refcnt );
timer_init ( &sandev->timer, sandev_command_expired, &sandev->refcnt );
- sandev->priv = ( ( ( void * ) sandev ) + sizeof ( *sandev ) );
+ sandev->priv = ( ( ( void * ) sandev ) + size );
+ INIT_LIST_HEAD ( &sandev->opened );
+ INIT_LIST_HEAD ( &sandev->closed );
+ for ( i = 0 ; i < count ; i++ ) {
+ sanpath = &sandev->path[i];
+ sanpath->sandev = sandev;
+ sanpath->index = i;
+ sanpath->uri = uri_get ( uris[i] );
+ list_add_tail ( &sanpath->list, &sandev->closed );
+ intf_init ( &sanpath->block, &sanpath_block_desc,
+ &sandev->refcnt );
+ process_init_stopped ( &sanpath->process, &sanpath_process_desc,
+ &sandev->refcnt );
+ sanpath->path_rc = -EINPROGRESS;
+ }
return sandev;
}
@@ -588,7 +736,7 @@ void unregister_sandev ( struct san_device *sandev ) {
assert ( ! timer_running ( &sandev->timer ) );
/* Shut down interfaces */
- intfs_shutdown ( 0, &sandev->block, &sandev->command, NULL );
+ sandev_restart ( sandev, 0 );
/* Remove from list of SAN devices */
list_del ( &sandev->list );
diff --git a/src/hci/commands/sanboot_cmd.c b/src/hci/commands/sanboot_cmd.c
index 24ec8bc4..9965ec15 100644
--- a/src/hci/commands/sanboot_cmd.c
+++ b/src/hci/commands/sanboot_cmd.c
@@ -71,12 +71,12 @@ static union {
/** "sanhook" command descriptor */
static struct command_descriptor sanhook_cmd =
- COMMAND_DESC ( struct sanboot_options, opts.sanhook, 1, 1,
+ COMMAND_DESC ( struct sanboot_options, opts.sanhook, 1, MAX_ARGUMENTS,
"<root-path>" );
/** "sanboot" command descriptor */
static struct command_descriptor sanboot_cmd =
- COMMAND_DESC ( struct sanboot_options, opts.sanboot, 0, 1,
+ COMMAND_DESC ( struct sanboot_options, opts.sanboot, 0, MAX_ARGUMENTS,
"[<root-path>]" );
/** "sanunhook" command descriptor */
@@ -96,9 +96,10 @@ static int sanboot_core_exec ( int argc, char **argv,
struct command_descriptor *cmd,
int default_flags, int no_root_path_flags ) {
struct sanboot_options opts;
- const char *root_path;
- struct uri *uri;
+ struct uri *uris[argc];
+ int count;
int flags;
+ int i;
int rc;
/* Initialise options */
@@ -109,17 +110,14 @@ static int sanboot_core_exec ( int argc, char **argv,
if ( ( rc = reparse_options ( argc, argv, cmd, &opts ) ) != 0 )
goto err_parse_options;
- /* Parse root path, if present */
- if ( argc > optind ) {
- root_path = argv[optind];
- uri = parse_uri ( root_path );
- if ( ! uri ) {
+ /* Parse root paths, if present */
+ count = ( argc - optind );
+ for ( i = 0 ; i < count ; i++ ) {
+ uris[i] = parse_uri ( argv[ optind + i ] );
+ if ( ! uris[i] ) {
rc = -ENOMEM;
goto err_parse_uri;
}
- } else {
- root_path = NULL;
- uri = NULL;
}
/* Construct flags */
@@ -128,16 +126,18 @@ static int sanboot_core_exec ( int argc, char **argv,
flags |= URIBOOT_NO_SAN_DESCRIBE;
if ( opts.keep )
flags |= URIBOOT_NO_SAN_UNHOOK;
- if ( ! root_path )
+ if ( ! count )
flags |= no_root_path_flags;
/* Boot from root path */
- if ( ( rc = uriboot ( NULL, uri, opts.drive, flags ) ) != 0 )
+ if ( ( rc = uriboot ( NULL, uris, count, opts.drive, flags ) ) != 0 )
goto err_uriboot;
err_uriboot:
- uri_put ( uri );
+ i = count;
err_parse_uri:
+ for ( i-- ; i >= 0 ; i-- )
+ uri_put ( uris[i] );
err_parse_options:
return rc;
}
diff --git a/src/include/ipxe/sanboot.h b/src/include/ipxe/sanboot.h
index c2e57f71..a8b0291d 100644
--- a/src/include/ipxe/sanboot.h
+++ b/src/include/ipxe/sanboot.h
@@ -16,9 +16,29 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/list.h>
#include <ipxe/uri.h>
#include <ipxe/retry.h>
+#include <ipxe/process.h>
#include <ipxe/blockdev.h>
#include <config/sanboot.h>
+/** A SAN path */
+struct san_path {
+ /** Containing SAN device */
+ struct san_device *sandev;
+ /** Path index */
+ unsigned int index;
+ /** SAN device URI */
+ struct uri *uri;
+ /** List of open/closed paths */
+ struct list_head list;
+
+ /** Underlying block device interface */
+ struct interface block;
+ /** Process */
+ struct process process;
+ /** Path status */
+ int path_rc;
+};
+
/** A SAN device */
struct san_device {
/** Reference count */
@@ -26,16 +46,9 @@ struct san_device {
/** List of SAN devices */
struct list_head list;
- /** SAN device URI */
- struct uri *uri;
/** Drive number */
unsigned int drive;
- /** Underlying block device interface */
- struct interface block;
- /** Current device status */
- int block_rc;
-
/** Command interface */
struct interface command;
/** Command timeout timer */
@@ -57,6 +70,15 @@ struct san_device {
/** Driver private data */
void *priv;
+
+ /** Current active path */
+ struct san_path *active;
+ /** List of opened SAN paths */
+ struct list_head opened;
+ /** List of closed SAN paths */
+ struct list_head closed;
+ /** SAN paths */
+ struct san_path path[0];
};
/**
@@ -99,11 +121,12 @@ struct san_device {
/**
* Hook SAN device
*
- * @v uri URI
* @v drive Drive number
+ * @v uris List of URIs
+ * @v count Number of URIs
* @ret drive Drive number, or negative error
*/
-int san_hook ( struct uri *uri, unsigned int drive );
+int san_hook ( unsigned int drive, struct uri **uris, unsigned int count );
/**
* Unhook SAN device
@@ -191,7 +214,7 @@ static inline uint64_t sandev_capacity ( struct san_device *sandev ) {
* @ret needs_reopen SAN device needs to be reopened
*/
static inline int sandev_needs_reopen ( struct san_device *sandev ) {
- return ( sandev->block_rc != 0 );
+ return ( sandev->active == NULL );
}
extern struct san_device * sandev_find ( unsigned int drive );
@@ -203,7 +226,8 @@ extern int sandev_rw ( struct san_device *sandev, uint64_t lba,
struct interface *data,
uint64_t lba, unsigned int count,
userptr_t buffer, size_t len ) );
-extern struct san_device * alloc_sandev ( struct uri *uri, size_t priv_size );
+extern struct san_device * alloc_sandev ( struct uri **uris, unsigned int count,
+ size_t priv_size );
extern int register_sandev ( struct san_device *sandev );
extern void unregister_sandev ( struct san_device *sandev );
extern unsigned int san_default_drive ( void );
diff --git a/src/include/usr/autoboot.h b/src/include/usr/autoboot.h
index 4db226b9..c62d06c6 100644
--- a/src/include/usr/autoboot.h
+++ b/src/include/usr/autoboot.h
@@ -30,7 +30,8 @@ extern void set_autoboot_busloc ( unsigned int bus_type,
unsigned int location );
extern void set_autoboot_ll_addr ( const void *ll_addr, size_t len );
-extern int uriboot ( struct uri *filename, struct uri *root_path, int drive,
+extern int uriboot ( struct uri *filename, struct uri **root_paths,
+ unsigned int root_path_count, int drive,
unsigned int flags );
extern struct uri *
fetch_next_server_and_filename ( struct settings *settings );
diff --git a/src/interface/efi/efi_block.c b/src/interface/efi/efi_block.c
index 10504f6a..9679fc0d 100644
--- a/src/interface/efi/efi_block.c
+++ b/src/interface/efi/efi_block.c
@@ -251,11 +251,13 @@ static void efi_block_connect ( struct san_device *sandev ) {
/**
* Hook EFI block device
*
- * @v uri URI
* @v drive Drive number
+ * @v uris List of URIs
+ * @v count Number of URIs
* @ret drive Drive number, or negative error
*/
-static int efi_block_hook ( struct uri *uri, unsigned int drive ) {
+static int efi_block_hook ( unsigned int drive, struct uri **uris,
+ unsigned int count ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
EFI_DEVICE_PATH_PROTOCOL *end;
struct efi_block_vendor_path *vendor;
@@ -270,6 +272,13 @@ static int efi_block_hook ( struct uri *uri, unsigned int drive ) {
EFI_STATUS efirc;
int rc;
+ /* Sanity check */
+ if ( ! count ) {
+ DBG ( "EFIBLK has no URIs\n" );
+ rc = -ENOTTY;
+ goto err_no_uris;
+ }
+
/* Find an appropriate parent device handle */
snpdev = last_opened_snpdev();
if ( ! snpdev ) {
@@ -280,14 +289,14 @@ static int efi_block_hook ( struct uri *uri, unsigned int drive ) {
/* Calculate length of private data */
prefix_len = efi_devpath_len ( snpdev->path );
- uri_len = format_uri ( uri, NULL, 0 );
+ uri_len = format_uri ( uris[0], NULL, 0 );
vendor_len = ( sizeof ( *vendor ) +
( ( uri_len + 1 /* NUL */ ) * sizeof ( wchar_t ) ) );
len = ( sizeof ( *block ) + uri_len + 1 /* NUL */ + prefix_len +
vendor_len + sizeof ( *end ) );
/* Allocate and initialise structure */
- sandev = alloc_sandev ( uri, len );
+ sandev = alloc_sandev ( uris, count, len );
if ( ! sandev ) {
rc = -ENOMEM;
goto err_alloc;
@@ -315,7 +324,7 @@ static int efi_block_hook ( struct uri *uri, unsigned int drive ) {
vendor->vendor.Header.Length[1] = ( vendor_len >> 8 );
memcpy ( &vendor->vendor.Guid, &ipxe_block_device_path_guid,
sizeof ( vendor->vendor.Guid ) );
- format_uri ( uri, uri_buf, ( uri_len + 1 /* NUL */ ) );
+ format_uri ( uris[0], uri_buf, ( uri_len + 1 /* NUL */ ) );
efi_snprintf ( vendor->uri, ( uri_len + 1 /* NUL */ ), "%s", uri_buf );
end = ( ( ( void * ) vendor ) + vendor_len );
end->Type = END_DEVICE_PATH_TYPE;
@@ -364,6 +373,7 @@ static int efi_block_hook ( struct uri *uri, unsigned int drive ) {
sandev_put ( sandev );
err_alloc:
err_no_snpdev:
+ err_no_uris:
return rc;
}
@@ -413,6 +423,7 @@ static int efi_block_describe ( unsigned int drive ) {
} xbftab;
static UINTN key;
struct san_device *sandev;
+ struct san_path *sanpath;
size_t len;
EFI_STATUS efirc;
int rc;
@@ -446,6 +457,8 @@ static int efi_block_describe ( unsigned int drive ) {
if ( sandev_needs_reopen ( sandev ) &&
( ( rc = sandev_reopen ( sandev ) ) != 0 ) )
return rc;
+ sanpath = sandev->active;
+ assert ( sanpath != NULL );
/* Clear table */
memset ( &xbftab, 0, sizeof ( xbftab ) );
@@ -457,7 +470,7 @@ static int efi_block_describe ( unsigned int drive ) {
sizeof ( xbftab.acpi.oem_table_id ) );
/* Fill in remaining parameters */
- if ( ( rc = acpi_describe ( &sandev->block, &xbftab.acpi,
+ if ( ( rc = acpi_describe ( &sanpath->block, &xbftab.acpi,
sizeof ( xbftab ) ) ) != 0 ) {
DBGC ( sandev, "EFIBLK %#02x could not create ACPI "
"description: %s\n", sandev->drive, strerror ( rc ) );
diff --git a/src/usr/autoboot.c b/src/usr/autoboot.c
index 57bf96ef..a0c79351 100644
--- a/src/usr/autoboot.c
+++ b/src/usr/autoboot.c
@@ -109,7 +109,8 @@ const struct setting skip_san_boot_setting __setting ( SETTING_SANBOOT_EXTRA,
* Boot from filename and root-path URIs
*
* @v filename Filename
- * @v root_path Root path
+ * @v root_paths Root path(s)
+ * @v root_path_count Number of root paths
* @v drive SAN drive (if applicable)
* @v flags Boot action flags
* @ret rc Return status code
@@ -120,14 +121,14 @@ const struct setting skip_san_boot_setting __setting ( SETTING_SANBOOT_EXTRA,
* provide backwards compatibility for the "keep-san" and
* "skip-san-boot" options.
*/
-int uriboot ( struct uri *filename, struct uri *root_path, int drive,
- unsigned int flags ) {
+int uriboot ( struct uri *filename, struct uri **root_paths,
+ unsigned int root_path_count, int drive, unsigned int flags ) {
struct image *image;
int rc;
/* Hook SAN device, if applicable */
- if ( root_path ) {
- drive = san_hook ( root_path, drive );
+ if ( root_path_count ) {
+ drive = san_hook ( drive, root_paths, root_path_count );
if ( drive < 0 ) {
rc = drive;
printf ( "Could not open SAN device: %s\n",
@@ -396,7 +397,8 @@ int netboot ( struct net_device *netdev ) {
}
/* Boot using next server, filename and root path */
- if ( ( rc = uriboot ( filename, root_path, san_default_drive(),
+ if ( ( rc = uriboot ( filename, &root_path, ( root_path ? 1 : 0 ),
+ san_default_drive(),
( root_path ? 0 : URIBOOT_NO_SAN ) ) ) != 0 )
goto err_uriboot;
diff --git a/src/usr/pxemenu.c b/src/usr/pxemenu.c
index 2d05d3f5..391d698a 100644
--- a/src/usr/pxemenu.c
+++ b/src/usr/pxemenu.c
@@ -378,7 +378,7 @@ int pxe_menu_boot ( struct net_device *netdev ) {
return -ENOMEM;
/* Attempt boot */
- rc = uriboot ( uri, NULL, 0, URIBOOT_NO_SAN );
+ rc = uriboot ( uri, NULL, 0, 0, URIBOOT_NO_SAN );
uri_put ( uri );
return rc;
}