Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f41b5cec authored by Hannes Reinecke's avatar Hannes Reinecke Committed by James Bottomley
Browse files

[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: default avatarHannes Reinecke <hare@suse.de>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent 4d7db04a
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -372,7 +372,7 @@ typedef enum {
	AHD_CURRENT_SENSING   = 0x40000,
	AHD_SCB_CONFIG_USED   = 0x80000,/* No SEEPROM but SCB had info. */
	AHD_HP_BOARD	      = 0x100000,
	AHD_RESET_POLL_ACTIVE = 0x200000,
	AHD_BUS_RESET_ACTIVE  = 0x200000,
	AHD_UPDATE_PEND_CMDS  = 0x400000,
	AHD_RUNNING_QOUTFIFO  = 0x800000,
	AHD_HAD_FIRST_SEL     = 0x1000000
+53 −61
Original line number Diff line number Diff line
@@ -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,
				     u_int prev, u_int next, u_int tid);
static void		ahd_reset_current_bus(struct ahd_softc *ahd);
static ahd_callback_t	ahd_reset_poll;
static ahd_callback_t	ahd_stat_timer;
#ifdef AHD_DUMP_SEQ
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);
	lqostat0 = ahd_inb(ahd, LQOSTAT0);
	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) {
		u_int simode0;

@@ -7847,6 +7858,17 @@ ahd_reset_channel(struct ahd_softc *ahd, char channel, int initiate_reset)
	int	found;
	u_int	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;

@@ -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. */
	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
	if ((ahd->flags & AHD_TARGETROLE) != 0) {
		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);

	/*
	 * Revert to async/narrow transfers until we renegotiate.
	 * Reenable selections
	 */
	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);
		}
	}
	ahd_outb(ahd, SIMODE1, ahd_inb(ahd, SIMODE1) | ENSCSIRST);
	scsiseq = ahd_inb(ahd, SCSISEQ_TEMPLATE);
	ahd_outb(ahd, SCSISEQ1, scsiseq & (ENSELI|ENRSELI|ENAUTOATNP));

#ifdef AHD_TARGET_MODE
	max_scsiid = (ahd->features & AHD_WIDE) ? 15 : 7;

#ifdef AHD_TARGET_MODE
	/*
	 * Send an immediate notify ccb to all target more peripheral
	 * drivers affected by this action.
@@ -7975,51 +7987,31 @@ ahd_reset_channel(struct ahd_softc *ahd, char channel, int initiate_reset)
	/* Notify the XPT that a bus reset occurred */
	ahd_send_async(ahd, devinfo.channel, CAM_TARGET_WILDCARD,
		       CAM_LUN_WILDCARD, AC_BUS_RESET, NULL);
	ahd_restart(ahd);

	/*
	 * Freeze the SIMQ until our poller can determine that
	 * 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.
	 * Revert to async/narrow transfers until we renegotiate.
	 */
	if ((ahd->flags & AHD_RESET_POLL_ACTIVE) == 0) {
		ahd->flags |= AHD_RESET_POLL_ACTIVE;
		ahd_freeze_simq(ahd);
		ahd_timer_reset(&ahd->reset_timer, 0, ahd_reset_poll, ahd);
	}
	return (found);
}

	for (target = 0; target <= max_scsiid; target++) {

#define AHD_RESET_POLL_US 1000
static void
ahd_reset_poll(void *arg)
{
	struct	ahd_softc *ahd = arg;
	u_int	scsiseq1;
	u_long	s;
		if (ahd->enabled_targets[target] == NULL)
			continue;
		for (initiator = 0; initiator <= max_scsiid; initiator++) {
			struct ahd_devinfo devinfo;

	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;
			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);
		}
	}

	/* 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);
	ahd_restart(ahd);

	return (found);
}

/**************************** Statistics Processing ***************************/
+4 −0
Original line number Diff line number Diff line
@@ -782,6 +782,7 @@ ahd_linux_bus_reset(struct scsi_cmnd *cmd)
{
	struct ahd_softc *ahd;
	int    found;
	unsigned long flags;

	ahd = *(struct ahd_softc **)cmd->device->host->hostdata;
#ifdef AHD_DEBUG
@@ -789,8 +790,11 @@ ahd_linux_bus_reset(struct scsi_cmnd *cmd)
		printf("%s: Bus reset called for cmd %p\n",
		       ahd_name(ahd), cmd);
#endif
	ahd_lock(ahd, &flags);

	found = ahd_reset_channel(ahd, scmd_channel(cmd) + 'A',
				  /*initiate reset*/TRUE);
	ahd_unlock(ahd, &flags);

	if (bootverbose)
		printf("%s: SCSI bus reset delivered. "