Commit e3c3da67 authored by Finn Thain's avatar Finn Thain Committed by Christoph Hellwig

atari_NCR5380: Refactor Falcon locking

Simplify falcon_release_lock_if_possible() by making callers responsible for
disabling local IRQ's, which they must do anyway to correctly synchronize
the ST DMA "lock" with core driver data structures. Move this
synchronization logic to the core driver with which it is tightly coupled.

Other LLD's like sun3_scsi and mac_scsi that can make use of this core
driver can just stub out the NCR5380_acquire_dma_irq() and
NCR5380_release_dma_irq() calls so the compiler will eliminate the
ST DMA code.

Remove a redundant local_irq_save/restore pair (irq's are disabled for
interrupt handlers these days). Revise the locking for
atari_scsi_bus_reset(): use local_irq_save/restore() instead of
atari_turnoff/turnon_irq(). There is no guarantee that atari_scsi still
holds the ST DMA lock during EH, so atari_turnoff/turnon_irq() could
end up dropping an IDE or floppy interrupt.
Signed-off-by: default avatarFinn Thain <fthain@telegraphics.com.au>
Tested-by: default avatarMichael Schmitz <schmitzmic@gmail.com>
Reviewed-by: default avatarHannes Reinecke <hare@suse.de>
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
parent ef1081cb
...@@ -925,7 +925,7 @@ static int NCR5380_queue_command(struct Scsi_Host *instance, ...@@ -925,7 +925,7 @@ static int NCR5380_queue_command(struct Scsi_Host *instance,
* because also a timer int can trigger an abort or reset, which would * because also a timer int can trigger an abort or reset, which would
* alter queues and touch the lock. * alter queues and touch the lock.
*/ */
if (!falcon_get_lock()) if (!NCR5380_acquire_dma_irq(instance))
return SCSI_MLQUEUE_HOST_BUSY; return SCSI_MLQUEUE_HOST_BUSY;
local_irq_save(flags); local_irq_save(flags);
...@@ -960,6 +960,18 @@ static int NCR5380_queue_command(struct Scsi_Host *instance, ...@@ -960,6 +960,18 @@ static int NCR5380_queue_command(struct Scsi_Host *instance,
return 0; return 0;
} }
static inline void maybe_release_dma_irq(struct Scsi_Host *instance)
{
struct NCR5380_hostdata *hostdata = shost_priv(instance);
/* Caller does the locking needed to set & test these data atomically */
if (!hostdata->disconnected_queue &&
!hostdata->issue_queue &&
!hostdata->connected &&
!hostdata->retain_dma_intr)
NCR5380_release_dma_irq(instance);
}
/* /*
* Function : NCR5380_main (void) * Function : NCR5380_main (void)
* *
...@@ -1082,9 +1094,11 @@ static void NCR5380_main(struct work_struct *work) ...@@ -1082,9 +1094,11 @@ static void NCR5380_main(struct work_struct *work)
cmd_get_tag(tmp, tmp->cmnd[0] != REQUEST_SENSE); cmd_get_tag(tmp, tmp->cmnd[0] != REQUEST_SENSE);
#endif #endif
if (!NCR5380_select(instance, tmp)) { if (!NCR5380_select(instance, tmp)) {
local_irq_disable();
hostdata->retain_dma_intr--; hostdata->retain_dma_intr--;
/* release if target did not response! */ /* release if target did not response! */
falcon_release_lock_if_possible(hostdata); maybe_release_dma_irq(instance);
local_irq_restore(flags);
break; break;
} else { } else {
local_irq_disable(); local_irq_disable();
...@@ -2083,11 +2097,12 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) ...@@ -2083,11 +2097,12 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance)
case COMMAND_COMPLETE: case COMMAND_COMPLETE:
/* Accept message by clearing ACK */ /* Accept message by clearing ACK */
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
/* ++guenther: possible race with Falcon locking */
hostdata->retain_dma_intr++;
hostdata->connected = NULL;
dprintk(NDEBUG_QUEUES, "scsi%d: command for target %d, lun %llu " dprintk(NDEBUG_QUEUES, "scsi%d: command for target %d, lun %llu "
"completed\n", HOSTNO, cmd->device->id, cmd->device->lun); "completed\n", HOSTNO, cmd->device->id, cmd->device->lun);
local_irq_save(flags);
hostdata->retain_dma_intr++;
hostdata->connected = NULL;
#ifdef SUPPORT_TAGS #ifdef SUPPORT_TAGS
cmd_free_tag(cmd); cmd_free_tag(cmd);
if (status_byte(cmd->SCp.Status) == QUEUE_FULL) { if (status_byte(cmd->SCp.Status) == QUEUE_FULL) {
...@@ -2146,17 +2161,17 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) ...@@ -2146,17 +2161,17 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance)
dprintk(NDEBUG_AUTOSENSE, "scsi%d: performing request sense\n", HOSTNO); dprintk(NDEBUG_AUTOSENSE, "scsi%d: performing request sense\n", HOSTNO);
local_irq_save(flags);
LIST(cmd,hostdata->issue_queue); LIST(cmd,hostdata->issue_queue);
SET_NEXT(cmd, hostdata->issue_queue); SET_NEXT(cmd, hostdata->issue_queue);
hostdata->issue_queue = (struct scsi_cmnd *) cmd; hostdata->issue_queue = (struct scsi_cmnd *) cmd;
local_irq_restore(flags);
dprintk(NDEBUG_QUEUES, "scsi%d: REQUEST SENSE added to head of " dprintk(NDEBUG_QUEUES, "scsi%d: REQUEST SENSE added to head of "
"issue queue\n", H_NO(cmd)); "issue queue\n", H_NO(cmd));
} else { } else {
cmd->scsi_done(cmd); cmd->scsi_done(cmd);
} }
local_irq_restore(flags);
NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
/* /*
* Restore phase bits to 0 so an interrupted selection, * Restore phase bits to 0 so an interrupted selection,
...@@ -2167,12 +2182,14 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) ...@@ -2167,12 +2182,14 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance)
while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected) while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
barrier(); barrier();
local_irq_save(flags);
hostdata->retain_dma_intr--; hostdata->retain_dma_intr--;
/* ++roman: For Falcon SCSI, release the lock on the /* ++roman: For Falcon SCSI, release the lock on the
* ST-DMA here if no other commands are waiting on the * ST-DMA here if no other commands are waiting on the
* disconnected queue. * disconnected queue.
*/ */
falcon_release_lock_if_possible(hostdata); maybe_release_dma_irq(instance);
local_irq_restore(flags);
return; return;
case MESSAGE_REJECT: case MESSAGE_REJECT:
/* Accept message by clearing ACK */ /* Accept message by clearing ACK */
...@@ -2331,6 +2348,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) ...@@ -2331,6 +2348,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance)
hostdata->last_message = msgout; hostdata->last_message = msgout;
NCR5380_transfer_pio(instance, &phase, &len, &data); NCR5380_transfer_pio(instance, &phase, &len, &data);
if (msgout == ABORT) { if (msgout == ABORT) {
local_irq_save(flags);
#ifdef SUPPORT_TAGS #ifdef SUPPORT_TAGS
cmd_free_tag(cmd); cmd_free_tag(cmd);
#else #else
...@@ -2338,9 +2356,10 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) ...@@ -2338,9 +2356,10 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance)
#endif #endif
hostdata->connected = NULL; hostdata->connected = NULL;
cmd->result = DID_ERROR << 16; cmd->result = DID_ERROR << 16;
cmd->scsi_done(cmd);
NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
falcon_release_lock_if_possible(hostdata); maybe_release_dma_irq(instance);
local_irq_restore(flags);
cmd->scsi_done(cmd);
return; return;
} }
msgout = NOP; msgout = NOP;
...@@ -2393,7 +2412,6 @@ static void NCR5380_reselect(struct Scsi_Host *instance) ...@@ -2393,7 +2412,6 @@ static void NCR5380_reselect(struct Scsi_Host *instance)
unsigned char msg[3]; unsigned char msg[3];
unsigned char *data; unsigned char *data;
struct scsi_cmnd *tmp = NULL, *prev; struct scsi_cmnd *tmp = NULL, *prev;
/* unsigned long flags; */
/* /*
* Disable arbitration, etc. since the host adapter obviously * Disable arbitration, etc. since the host adapter obviously
...@@ -2473,8 +2491,6 @@ static void NCR5380_reselect(struct Scsi_Host *instance) ...@@ -2473,8 +2491,6 @@ static void NCR5380_reselect(struct Scsi_Host *instance)
&& (tag == tmp->tag) && (tag == tmp->tag)
#endif #endif
) { ) {
/* ++guenther: prevent race with falcon_release_lock */
hostdata->retain_dma_intr++;
if (prev) { if (prev) {
REMOVE(prev, NEXT(prev), tmp, NEXT(tmp)); REMOVE(prev, NEXT(prev), tmp, NEXT(tmp));
SET_NEXT(prev, NEXT(tmp)); SET_NEXT(prev, NEXT(tmp));
...@@ -2512,7 +2528,6 @@ static void NCR5380_reselect(struct Scsi_Host *instance) ...@@ -2512,7 +2528,6 @@ static void NCR5380_reselect(struct Scsi_Host *instance)
hostdata->connected = tmp; hostdata->connected = tmp;
dprintk(NDEBUG_RESELECTION, "scsi%d: nexus established, target = %d, lun = %llu, tag = %d\n", dprintk(NDEBUG_RESELECTION, "scsi%d: nexus established, target = %d, lun = %llu, tag = %d\n",
HOSTNO, tmp->device->id, tmp->device->lun, tmp->tag); HOSTNO, tmp->device->id, tmp->device->lun, tmp->tag);
hostdata->retain_dma_intr--;
} }
...@@ -2587,12 +2602,12 @@ int NCR5380_abort(struct scsi_cmnd *cmd) ...@@ -2587,12 +2602,12 @@ int NCR5380_abort(struct scsi_cmnd *cmd)
#else #else
hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
#endif #endif
maybe_release_dma_irq(instance);
local_irq_restore(flags); local_irq_restore(flags);
cmd->scsi_done(cmd); cmd->scsi_done(cmd);
falcon_release_lock_if_possible(hostdata);
return SUCCESS; return SUCCESS;
} else { } else {
/* local_irq_restore(flags); */ local_irq_restore(flags);
printk("scsi%d: abort of connected command failed!\n", HOSTNO); printk("scsi%d: abort of connected command failed!\n", HOSTNO);
return FAILED; return FAILED;
} }
...@@ -2611,13 +2626,13 @@ int NCR5380_abort(struct scsi_cmnd *cmd) ...@@ -2611,13 +2626,13 @@ int NCR5380_abort(struct scsi_cmnd *cmd)
(*prev) = NEXT(tmp); (*prev) = NEXT(tmp);
SET_NEXT(tmp, NULL); SET_NEXT(tmp, NULL);
tmp->result = DID_ABORT << 16; tmp->result = DID_ABORT << 16;
maybe_release_dma_irq(instance);
local_irq_restore(flags); local_irq_restore(flags);
dprintk(NDEBUG_ABORT, "scsi%d: abort removed command from issue queue.\n", dprintk(NDEBUG_ABORT, "scsi%d: abort removed command from issue queue.\n",
HOSTNO); HOSTNO);
/* Tagged queuing note: no tag to free here, hasn't been assigned /* Tagged queuing note: no tag to free here, hasn't been assigned
* yet... */ * yet... */
tmp->scsi_done(tmp); tmp->scsi_done(tmp);
falcon_release_lock_if_possible(hostdata);
return SUCCESS; return SUCCESS;
} }
} }
...@@ -2695,15 +2710,22 @@ int NCR5380_abort(struct scsi_cmnd *cmd) ...@@ -2695,15 +2710,22 @@ int NCR5380_abort(struct scsi_cmnd *cmd)
#else #else
hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
#endif #endif
maybe_release_dma_irq(instance);
local_irq_restore(flags); local_irq_restore(flags);
tmp->scsi_done(tmp); tmp->scsi_done(tmp);
falcon_release_lock_if_possible(hostdata);
return SUCCESS; return SUCCESS;
} }
} }
} }
} }
/* Maybe it is sufficient just to release the ST-DMA lock... (if
* possible at all) At least, we should check if the lock could be
* released after the abort, in case it is kept due to some bug.
*/
maybe_release_dma_irq(instance);
local_irq_restore(flags);
/* /*
* Case 5 : If we reached this point, the command was not found in any of * Case 5 : If we reached this point, the command was not found in any of
* the queues. * the queues.
...@@ -2714,15 +2736,8 @@ int NCR5380_abort(struct scsi_cmnd *cmd) ...@@ -2714,15 +2736,8 @@ int NCR5380_abort(struct scsi_cmnd *cmd)
* broke. * broke.
*/ */
local_irq_restore(flags);
printk(KERN_INFO "scsi%d: warning : SCSI command probably completed successfully before abortion\n", HOSTNO); printk(KERN_INFO "scsi%d: warning : SCSI command probably completed successfully before abortion\n", HOSTNO);
/* Maybe it is sufficient just to release the ST-DMA lock... (if
* possible at all) At least, we should check if the lock could be
* released after the abort, in case it is kept due to some bug.
*/
falcon_release_lock_if_possible(hostdata);
return FAILED; return FAILED;
} }
...@@ -2738,14 +2753,15 @@ int NCR5380_abort(struct scsi_cmnd *cmd) ...@@ -2738,14 +2753,15 @@ int NCR5380_abort(struct scsi_cmnd *cmd)
static int NCR5380_bus_reset(struct scsi_cmnd *cmd) static int NCR5380_bus_reset(struct scsi_cmnd *cmd)
{ {
SETUP_HOSTDATA(cmd->device->host); struct Scsi_Host *instance = cmd->device->host;
struct NCR5380_hostdata *hostdata = shost_priv(instance);
int i; int i;
unsigned long flags; unsigned long flags;
#if defined(RESET_RUN_DONE) #if defined(RESET_RUN_DONE)
struct scsi_cmnd *connected, *disconnected_queue; struct scsi_cmnd *connected, *disconnected_queue;
#endif #endif
NCR5380_print_status(cmd->device->host); NCR5380_print_status(instance);
/* get in phase */ /* get in phase */
NCR5380_write(TARGET_COMMAND_REG, NCR5380_write(TARGET_COMMAND_REG,
...@@ -2870,6 +2886,8 @@ static int NCR5380_bus_reset(struct scsi_cmnd *cmd) ...@@ -2870,6 +2886,8 @@ static int NCR5380_bus_reset(struct scsi_cmnd *cmd)
#ifdef REAL_DMA #ifdef REAL_DMA
hostdata->dma_len = 0; hostdata->dma_len = 0;
#endif #endif
maybe_release_dma_irq(instance);
local_irq_restore(flags); local_irq_restore(flags);
/* we did no complete reset of all commands, so a wakeup is required */ /* we did no complete reset of all commands, so a wakeup is required */
......
...@@ -109,6 +109,9 @@ ...@@ -109,6 +109,9 @@
#define NCR5380_dma_xfer_len(instance, cmd, phase) \ #define NCR5380_dma_xfer_len(instance, cmd, phase) \
atari_dma_xfer_len(cmd->SCp.this_residual, cmd, !((phase) & SR_IO)) atari_dma_xfer_len(cmd->SCp.this_residual, cmd, !((phase) & SR_IO))
#define NCR5380_acquire_dma_irq(instance) falcon_get_lock()
#define NCR5380_release_dma_irq(instance) falcon_release_lock()
#include "NCR5380.h" #include "NCR5380.h"
...@@ -448,23 +451,13 @@ static void atari_scsi_fetch_restbytes(void) ...@@ -448,23 +451,13 @@ static void atari_scsi_fetch_restbytes(void)
* connected command and the disconnected queue is empty. * connected command and the disconnected queue is empty.
*/ */
static void falcon_release_lock_if_possible(struct NCR5380_hostdata *hostdata) static void falcon_release_lock(void)
{ {
unsigned long flags;
if (IS_A_TT()) if (IS_A_TT())
return; return;
local_irq_save(flags); if (stdma_is_locked_by(scsi_falcon_intr))
if (!hostdata->disconnected_queue &&
!hostdata->issue_queue &&
!hostdata->connected &&
!hostdata->retain_dma_intr &&
stdma_is_locked_by(scsi_falcon_intr))
stdma_release(); stdma_release();
local_irq_restore(flags);
} }
/* This function manages the locking of the ST-DMA. /* This function manages the locking of the ST-DMA.
...@@ -787,36 +780,30 @@ static void atari_scsi_falcon_reg_write(unsigned char reg, unsigned char value) ...@@ -787,36 +780,30 @@ static void atari_scsi_falcon_reg_write(unsigned char reg, unsigned char value)
static int atari_scsi_bus_reset(struct scsi_cmnd *cmd) static int atari_scsi_bus_reset(struct scsi_cmnd *cmd)
{ {
int rv; int rv;
struct NCR5380_hostdata *hostdata = shost_priv(cmd->device->host); unsigned long flags;
local_irq_save(flags);
/* For doing the reset, SCSI interrupts must be disabled first,
* since the 5380 raises its IRQ line while _RST is active and we
* can't disable interrupts completely, since we need the timer.
*/
/* And abort a maybe active DMA transfer */
if (IS_A_TT()) {
atari_turnoff_irq(IRQ_TT_MFP_SCSI);
#ifdef REAL_DMA #ifdef REAL_DMA
/* Abort a maybe active DMA transfer */
if (IS_A_TT()) {
tt_scsi_dma.dma_ctrl = 0; tt_scsi_dma.dma_ctrl = 0;
#endif
} else { } else {
atari_turnoff_irq(IRQ_MFP_FSCSI);
#ifdef REAL_DMA
st_dma.dma_mode_status = 0x90; st_dma.dma_mode_status = 0x90;
atari_dma_active = 0; atari_dma_active = 0;
atari_dma_orig_addr = NULL; atari_dma_orig_addr = NULL;
#endif
} }
#endif
rv = NCR5380_bus_reset(cmd); rv = NCR5380_bus_reset(cmd);
if (IS_A_TT()) /* The 5380 raises its IRQ line while _RST is active but the ST DMA
atari_turnon_irq(IRQ_TT_MFP_SCSI); * "lock" has been released so this interrupt may end up handled by
else * floppy or IDE driver (if one of them holds the lock). The NCR5380
atari_turnon_irq(IRQ_MFP_FSCSI); * interrupt flag has been cleared already.
*/
if (rv == SUCCESS) local_irq_restore(flags);
falcon_release_lock_if_possible(hostdata);
return rv; return rv;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment