summaryrefslogtreecommitdiffstats
path: root/src/drivers
diff options
context:
space:
mode:
authorMichael Brown2017-03-09 13:45:45 +0100
committerMichael Brown2017-03-09 14:07:57 +0100
commit9db9221ea0e3a7a4b32cf7fbdb17a46af474ce2b (patch)
tree47cd05ed9dbe7f8ba87fb2790855b33bc7d26c37 /src/drivers
parent[block] Use intfs_shutdown() when shutting down multiple interfaces (diff)
downloadipxe-9db9221ea0e3a7a4b32cf7fbdb17a46af474ce2b.tar.gz
ipxe-9db9221ea0e3a7a4b32cf7fbdb17a46af474ce2b.tar.xz
ipxe-9db9221ea0e3a7a4b32cf7fbdb17a46af474ce2b.zip
[scsi] Avoid duplicate calls to scsicmd_close()
When a SCSI device is closed in error, the shutdown of the device's block data interface will probably lead to any outstanding commands being closed (by whichever object is currently connected to the block data interface). However, commands remain in the list of outstanding commands until the final reference is dropped. The result is that scsidev_close() will make a second call to scsicmd_close() for each command. This is harmless, but produces confusing debug messages. Fix by treating the outstanding command list as holding an explicit reference to each command, and removing the command from the list of outstanding commands in scsicmd_close(). Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/drivers')
-rw-r--r--src/drivers/block/scsi.c15
1 files changed, 7 insertions, 8 deletions
diff --git a/src/drivers/block/scsi.c b/src/drivers/block/scsi.c
index 51a1dc0c..cb4bb94c 100644
--- a/src/drivers/block/scsi.c
+++ b/src/drivers/block/scsi.c
@@ -371,8 +371,7 @@ static void scsicmd_free ( struct refcnt *refcnt ) {
struct scsi_command *scsicmd =
container_of ( refcnt, struct scsi_command, refcnt );
- /* Remove from list of commands */
- list_del ( &scsicmd->list );
+ /* Drop reference to SCSI device */
scsidev_put ( scsicmd->scsidev );
/* Free command */
@@ -395,6 +394,10 @@ static void scsicmd_close ( struct scsi_command *scsicmd, int rc ) {
/* Shut down interfaces */
intfs_shutdown ( rc, &scsicmd->scsi, &scsicmd->block, NULL );
+
+ /* Remove from list of commands and drop list's reference */
+ list_del ( &scsicmd->list );
+ scsicmd_put ( scsicmd );
}
/**
@@ -733,9 +736,8 @@ static int scsidev_command ( struct scsi_device *scsidev,
if ( ( rc = scsicmd_command ( scsicmd ) ) != 0 )
goto err_command;
- /* Attach to parent interface, mortalise self, and return */
+ /* Attach to parent interface, transfer reference to list, and return */
intf_plug_plug ( &scsicmd->block, block );
- ref_put ( &scsicmd->refcnt );
return 0;
err_command:
@@ -843,11 +845,8 @@ static void scsidev_close ( struct scsi_device *scsidev, int rc ) {
NULL );
/* Shut down any remaining commands */
- list_for_each_entry_safe ( scsicmd, tmp, &scsidev->cmds, list ) {
- scsicmd_get ( scsicmd );
+ list_for_each_entry_safe ( scsicmd, tmp, &scsidev->cmds, list )
scsicmd_close ( scsicmd, rc );
- scsicmd_put ( scsicmd );
- }
}
/** SCSI device block interface operations */