*/
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;
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 );
}
/* 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 ) );
/**
* 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;
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;
*/
static int int13_describe ( unsigned int drive ) {
struct san_device *sandev;
+ struct san_path *sanpath;
struct segoff xbft_address;
int rc;
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 ) );
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 ) );
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 );
}
}
/**
- * 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
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 {
*/
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;
}
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;
}
/**
* 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;
}
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 );
/**
* 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;
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 ) {
/* 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;
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;
sandev_put ( sandev );
err_alloc:
err_no_snpdev:
+ err_no_uris:
return rc;
}
} xbftab;
static UINTN key;
struct san_device *sandev;
+ struct san_path *sanpath;
size_t len;
EFI_STATUS efirc;
int rc;
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 ) );
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 ) );