mirror of
https://github.com/torvalds/linux.git
synced 2024-11-17 01:22:07 +00:00
[SCSI] aic79xx bus reset update
As James B. correctly noted, ahd_reset_channel() in ahd_linux_bus_reset() should be protected by ahd_lock(). However, the main reason for not doing so was a deadlock with the interesting polling mechanism to detect the end a bus reset. This patch replaces the polling mechanism with a saner signalling via flags; it also gives us the benefit of detecting any multiple calls to ahd_reset_channel(). Signed-off-by: Hannes Reinecke <hare@suse.de> Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
This commit is contained in:
parent
4d7db04a7a
commit
f41b5cec9b
@ -372,7 +372,7 @@ typedef enum {
|
|||||||
AHD_CURRENT_SENSING = 0x40000,
|
AHD_CURRENT_SENSING = 0x40000,
|
||||||
AHD_SCB_CONFIG_USED = 0x80000,/* No SEEPROM but SCB had info. */
|
AHD_SCB_CONFIG_USED = 0x80000,/* No SEEPROM but SCB had info. */
|
||||||
AHD_HP_BOARD = 0x100000,
|
AHD_HP_BOARD = 0x100000,
|
||||||
AHD_RESET_POLL_ACTIVE = 0x200000,
|
AHD_BUS_RESET_ACTIVE = 0x200000,
|
||||||
AHD_UPDATE_PEND_CMDS = 0x400000,
|
AHD_UPDATE_PEND_CMDS = 0x400000,
|
||||||
AHD_RUNNING_QOUTFIFO = 0x800000,
|
AHD_RUNNING_QOUTFIFO = 0x800000,
|
||||||
AHD_HAD_FIRST_SEL = 0x1000000
|
AHD_HAD_FIRST_SEL = 0x1000000
|
||||||
|
@ -207,7 +207,6 @@ static void ahd_add_scb_to_free_list(struct ahd_softc *ahd,
|
|||||||
static u_int ahd_rem_wscb(struct ahd_softc *ahd, u_int scbid,
|
static u_int ahd_rem_wscb(struct ahd_softc *ahd, u_int scbid,
|
||||||
u_int prev, u_int next, u_int tid);
|
u_int prev, u_int next, u_int tid);
|
||||||
static void ahd_reset_current_bus(struct ahd_softc *ahd);
|
static void ahd_reset_current_bus(struct ahd_softc *ahd);
|
||||||
static ahd_callback_t ahd_reset_poll;
|
|
||||||
static ahd_callback_t ahd_stat_timer;
|
static ahd_callback_t ahd_stat_timer;
|
||||||
#ifdef AHD_DUMP_SEQ
|
#ifdef AHD_DUMP_SEQ
|
||||||
static void ahd_dumpseq(struct ahd_softc *ahd);
|
static void ahd_dumpseq(struct ahd_softc *ahd);
|
||||||
@ -1534,6 +1533,18 @@ ahd_handle_scsiint(struct ahd_softc *ahd, u_int intstat)
|
|||||||
lqistat1 = ahd_inb(ahd, LQISTAT1);
|
lqistat1 = ahd_inb(ahd, LQISTAT1);
|
||||||
lqostat0 = ahd_inb(ahd, LQOSTAT0);
|
lqostat0 = ahd_inb(ahd, LQOSTAT0);
|
||||||
busfreetime = ahd_inb(ahd, SSTAT2) & BUSFREETIME;
|
busfreetime = ahd_inb(ahd, SSTAT2) & BUSFREETIME;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ignore external resets after a bus reset.
|
||||||
|
*/
|
||||||
|
if (((status & SCSIRSTI) != 0) && (ahd->flags & AHD_BUS_RESET_ACTIVE))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clear bus reset flag
|
||||||
|
*/
|
||||||
|
ahd->flags &= ~AHD_BUS_RESET_ACTIVE;
|
||||||
|
|
||||||
if ((status0 & (SELDI|SELDO)) != 0) {
|
if ((status0 & (SELDI|SELDO)) != 0) {
|
||||||
u_int simode0;
|
u_int simode0;
|
||||||
|
|
||||||
@ -7847,6 +7858,17 @@ ahd_reset_channel(struct ahd_softc *ahd, char channel, int initiate_reset)
|
|||||||
int found;
|
int found;
|
||||||
u_int fifo;
|
u_int fifo;
|
||||||
u_int next_fifo;
|
u_int next_fifo;
|
||||||
|
uint8_t scsiseq;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the last bus reset is cleared
|
||||||
|
*/
|
||||||
|
if (ahd->flags & AHD_BUS_RESET_ACTIVE) {
|
||||||
|
printf("%s: bus reset still active\n",
|
||||||
|
ahd_name(ahd));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ahd->flags |= AHD_BUS_RESET_ACTIVE;
|
||||||
|
|
||||||
ahd->pending_device = NULL;
|
ahd->pending_device = NULL;
|
||||||
|
|
||||||
@ -7860,6 +7882,12 @@ ahd_reset_channel(struct ahd_softc *ahd, char channel, int initiate_reset)
|
|||||||
/* Make sure the sequencer is in a safe location. */
|
/* Make sure the sequencer is in a safe location. */
|
||||||
ahd_clear_critical_section(ahd);
|
ahd_clear_critical_section(ahd);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Run our command complete fifos to ensure that we perform
|
||||||
|
* completion processing on any commands that 'completed'
|
||||||
|
* before the reset occurred.
|
||||||
|
*/
|
||||||
|
ahd_run_qoutfifo(ahd);
|
||||||
#ifdef AHD_TARGET_MODE
|
#ifdef AHD_TARGET_MODE
|
||||||
if ((ahd->flags & AHD_TARGETROLE) != 0) {
|
if ((ahd->flags & AHD_TARGETROLE) != 0) {
|
||||||
ahd_run_tqinfifo(ahd, /*paused*/TRUE);
|
ahd_run_tqinfifo(ahd, /*paused*/TRUE);
|
||||||
@ -7924,30 +7952,14 @@ ahd_reset_channel(struct ahd_softc *ahd, char channel, int initiate_reset)
|
|||||||
ahd_clear_fifo(ahd, 1);
|
ahd_clear_fifo(ahd, 1);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Revert to async/narrow transfers until we renegotiate.
|
* Reenable selections
|
||||||
*/
|
*/
|
||||||
|
ahd_outb(ahd, SIMODE1, ahd_inb(ahd, SIMODE1) | ENSCSIRST);
|
||||||
|
scsiseq = ahd_inb(ahd, SCSISEQ_TEMPLATE);
|
||||||
|
ahd_outb(ahd, SCSISEQ1, scsiseq & (ENSELI|ENRSELI|ENAUTOATNP));
|
||||||
|
|
||||||
max_scsiid = (ahd->features & AHD_WIDE) ? 15 : 7;
|
max_scsiid = (ahd->features & AHD_WIDE) ? 15 : 7;
|
||||||
for (target = 0; target <= max_scsiid; target++) {
|
|
||||||
|
|
||||||
if (ahd->enabled_targets[target] == NULL)
|
|
||||||
continue;
|
|
||||||
for (initiator = 0; initiator <= max_scsiid; initiator++) {
|
|
||||||
struct ahd_devinfo devinfo;
|
|
||||||
|
|
||||||
ahd_compile_devinfo(&devinfo, target, initiator,
|
|
||||||
CAM_LUN_WILDCARD,
|
|
||||||
'A', ROLE_UNKNOWN);
|
|
||||||
ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
|
|
||||||
AHD_TRANS_CUR, /*paused*/TRUE);
|
|
||||||
ahd_set_syncrate(ahd, &devinfo, /*period*/0,
|
|
||||||
/*offset*/0, /*ppr_options*/0,
|
|
||||||
AHD_TRANS_CUR, /*paused*/TRUE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef AHD_TARGET_MODE
|
#ifdef AHD_TARGET_MODE
|
||||||
max_scsiid = (ahd->features & AHD_WIDE) ? 15 : 7;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Send an immediate notify ccb to all target more peripheral
|
* Send an immediate notify ccb to all target more peripheral
|
||||||
* drivers affected by this action.
|
* drivers affected by this action.
|
||||||
@ -7975,53 +7987,33 @@ ahd_reset_channel(struct ahd_softc *ahd, char channel, int initiate_reset)
|
|||||||
/* Notify the XPT that a bus reset occurred */
|
/* Notify the XPT that a bus reset occurred */
|
||||||
ahd_send_async(ahd, devinfo.channel, CAM_TARGET_WILDCARD,
|
ahd_send_async(ahd, devinfo.channel, CAM_TARGET_WILDCARD,
|
||||||
CAM_LUN_WILDCARD, AC_BUS_RESET, NULL);
|
CAM_LUN_WILDCARD, AC_BUS_RESET, NULL);
|
||||||
ahd_restart(ahd);
|
|
||||||
/*
|
/*
|
||||||
* Freeze the SIMQ until our poller can determine that
|
* Revert to async/narrow transfers until we renegotiate.
|
||||||
* the bus reset has really gone away. We set the initial
|
|
||||||
* timer to 0 to have the check performed as soon as possible
|
|
||||||
* from the timer context.
|
|
||||||
*/
|
*/
|
||||||
if ((ahd->flags & AHD_RESET_POLL_ACTIVE) == 0) {
|
for (target = 0; target <= max_scsiid; target++) {
|
||||||
ahd->flags |= AHD_RESET_POLL_ACTIVE;
|
|
||||||
ahd_freeze_simq(ahd);
|
if (ahd->enabled_targets[target] == NULL)
|
||||||
ahd_timer_reset(&ahd->reset_timer, 0, ahd_reset_poll, ahd);
|
continue;
|
||||||
|
for (initiator = 0; initiator <= max_scsiid; initiator++) {
|
||||||
|
struct ahd_devinfo devinfo;
|
||||||
|
|
||||||
|
ahd_compile_devinfo(&devinfo, target, initiator,
|
||||||
|
CAM_LUN_WILDCARD,
|
||||||
|
'A', ROLE_UNKNOWN);
|
||||||
|
ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
|
||||||
|
AHD_TRANS_CUR, /*paused*/TRUE);
|
||||||
|
ahd_set_syncrate(ahd, &devinfo, /*period*/0,
|
||||||
|
/*offset*/0, /*ppr_options*/0,
|
||||||
|
AHD_TRANS_CUR, /*paused*/TRUE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ahd_restart(ahd);
|
||||||
|
|
||||||
return (found);
|
return (found);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define AHD_RESET_POLL_US 1000
|
|
||||||
static void
|
|
||||||
ahd_reset_poll(void *arg)
|
|
||||||
{
|
|
||||||
struct ahd_softc *ahd = arg;
|
|
||||||
u_int scsiseq1;
|
|
||||||
u_long s;
|
|
||||||
|
|
||||||
ahd_lock(ahd, &s);
|
|
||||||
ahd_pause(ahd);
|
|
||||||
ahd_update_modes(ahd);
|
|
||||||
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
|
|
||||||
ahd_outb(ahd, CLRSINT1, CLRSCSIRSTI);
|
|
||||||
if ((ahd_inb(ahd, SSTAT1) & SCSIRSTI) != 0) {
|
|
||||||
ahd_timer_reset(&ahd->reset_timer, AHD_RESET_POLL_US,
|
|
||||||
ahd_reset_poll, ahd);
|
|
||||||
ahd_unpause(ahd);
|
|
||||||
ahd_unlock(ahd, &s);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reset is now low. Complete chip reinitialization. */
|
|
||||||
ahd_outb(ahd, SIMODE1, ahd_inb(ahd, SIMODE1) | ENSCSIRST);
|
|
||||||
scsiseq1 = ahd_inb(ahd, SCSISEQ_TEMPLATE);
|
|
||||||
ahd_outb(ahd, SCSISEQ1, scsiseq1 & (ENSELI|ENRSELI|ENAUTOATNP));
|
|
||||||
ahd_unpause(ahd);
|
|
||||||
ahd->flags &= ~AHD_RESET_POLL_ACTIVE;
|
|
||||||
ahd_unlock(ahd, &s);
|
|
||||||
ahd_release_simq(ahd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**************************** Statistics Processing ***************************/
|
/**************************** Statistics Processing ***************************/
|
||||||
static void
|
static void
|
||||||
ahd_stat_timer(void *arg)
|
ahd_stat_timer(void *arg)
|
||||||
|
@ -782,6 +782,7 @@ ahd_linux_bus_reset(struct scsi_cmnd *cmd)
|
|||||||
{
|
{
|
||||||
struct ahd_softc *ahd;
|
struct ahd_softc *ahd;
|
||||||
int found;
|
int found;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
ahd = *(struct ahd_softc **)cmd->device->host->hostdata;
|
ahd = *(struct ahd_softc **)cmd->device->host->hostdata;
|
||||||
#ifdef AHD_DEBUG
|
#ifdef AHD_DEBUG
|
||||||
@ -789,8 +790,11 @@ ahd_linux_bus_reset(struct scsi_cmnd *cmd)
|
|||||||
printf("%s: Bus reset called for cmd %p\n",
|
printf("%s: Bus reset called for cmd %p\n",
|
||||||
ahd_name(ahd), cmd);
|
ahd_name(ahd), cmd);
|
||||||
#endif
|
#endif
|
||||||
|
ahd_lock(ahd, &flags);
|
||||||
|
|
||||||
found = ahd_reset_channel(ahd, scmd_channel(cmd) + 'A',
|
found = ahd_reset_channel(ahd, scmd_channel(cmd) + 'A',
|
||||||
/*initiate reset*/TRUE);
|
/*initiate reset*/TRUE);
|
||||||
|
ahd_unlock(ahd, &flags);
|
||||||
|
|
||||||
if (bootverbose)
|
if (bootverbose)
|
||||||
printf("%s: SCSI bus reset delivered. "
|
printf("%s: SCSI bus reset delivered. "
|
||||||
|
Loading…
Reference in New Issue
Block a user