Commit 1558c0fe authored by Justin T. Gibbs's avatar Justin T. Gibbs

Aic79xx Driver Update (version 1.3.6)

 o Correct bus hang on SE->LVD/LVD->SE tranceiver changes
 o Close a race condition in handling bad scsi status that could
   allow the driver to modify the waiting for selection queue while
   selections were enabled.
 o Perform an audit on use of del_timer() and switch to del_timer_sync()
   where appropriate.
 o Remove the reboot notifier hook which is unused in 2.5.X.
 o Correct some driver unload bugs.
parent aa19646a
......@@ -37,7 +37,7 @@
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/aic7xxx/aic7xxx/aic79xx.c#178 $
* $Id: //depot/aic7xxx/aic7xxx/aic79xx.c#182 $
*
* $FreeBSD$
*/
......@@ -1166,7 +1166,7 @@ ahd_handle_scsiint(struct ahd_softc *ahd, u_int intstat)
/*
* A change in I/O mode is equivalent to a bus reset.
*/
ahd_reset_channel(ahd, 'A', /*Initiate Reset*/FALSE);
ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
ahd_pause(ahd);
ahd_setup_iocell_workaround(ahd);
ahd_unpause(ahd);
......@@ -4887,7 +4887,6 @@ ahd_free(struct ahd_softc *ahd)
{
int i;
ahd_fini_scbdata(ahd);
switch (ahd->init_level) {
default:
case 5:
......@@ -4919,6 +4918,7 @@ ahd_free(struct ahd_softc *ahd)
ahd_dma_tag_destroy(ahd, ahd->parent_dmat);
#endif
ahd_platform_free(ahd);
ahd_fini_scbdata(ahd);
for (i = 0; i < AHD_NUM_TARGETS; i++) {
struct ahd_tmode_tstate *tstate;
......@@ -5584,8 +5584,8 @@ ahd_alloc_scbs(struct ahd_softc *ahd)
if (scb_data->sgs_left != 0) {
int offset;
offset = ahd_sglist_allocsize(ahd)
- (scb_data->sgs_left * ahd_sglist_size(ahd));
offset = ((ahd_sglist_allocsize(ahd) / ahd_sglist_size(ahd))
- scb_data->sgs_left) * ahd_sglist_size(ahd);
sg_map = SLIST_FIRST(&scb_data->sg_maps);
segs = sg_map->vaddr + offset;
sg_busaddr = sg_map->physaddr + offset;
......@@ -6605,56 +6605,55 @@ ahd_enable_coalessing(struct ahd_softc *ahd, int enable)
void
ahd_pause_and_flushwork(struct ahd_softc *ahd)
{
ahd_mode_state saved_modes;
u_int intstat;
u_int maxloops;
int paused;
u_int qfreeze_cnt;
maxloops = 1000;
ahd->flags |= AHD_ALL_INTERRUPTS;
paused = FALSE;
ahd_pause(ahd);
/*
* Increment the QFreeze Count so that the sequencer
* will not start new selections. We do this only
* until we are safely paused without further selections
* pending.
*/
ahd_outw(ahd, QFREEZE_COUNT, ahd_inw(ahd, QFREEZE_COUNT) + 1);
ahd_outb(ahd, SEQ_FLAGS2, ahd_inb(ahd, SEQ_FLAGS2) | SELECTOUT_QFROZEN);
do {
struct scb *waiting_scb;
if (paused)
ahd_unpause(ahd);
ahd_unpause(ahd);
ahd_intr(ahd);
ahd_pause(ahd);
paused = TRUE;
ahd_clear_critical_section(ahd);
saved_modes = ahd_save_modes(ahd);
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
if ((ahd_inb(ahd, SSTAT0) & (SELDO|SELINGO)) == 0)
ahd_outb(ahd, SCSISEQ0,
ahd_inb(ahd, SCSISEQ0) & ~ENSELO);
/*
* In the non-packetized case, the sequencer (for Rev A),
* relies on ENSELO remaining set after SELDO. The hardware
* auto-clears ENSELO in the packetized case.
*/
waiting_scb = ahd_lookup_scb(ahd,
ahd_inw(ahd, WAITING_TID_HEAD));
if (waiting_scb != NULL
&& (waiting_scb->flags & SCB_PACKETIZED) == 0
&& (ahd_inb(ahd, SSTAT0) & (SELDO|SELINGO)) != 0)
ahd_outb(ahd, SCSISEQ0,
ahd_inb(ahd, SCSISEQ0) | ENSELO);
intstat = ahd_inb(ahd, INTSTAT);
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
} while (--maxloops
&& (intstat != 0xFF || (ahd->features & AHD_REMOVABLE) == 0)
&& ((intstat & INT_PEND) != 0
|| (ahd_inb(ahd, SSTAT0) & (SELDO|SELINGO))));
|| (ahd_inb(ahd, SCSISEQ0) & ENSELO) != 0
|| (ahd_inb(ahd, SSTAT0) & (SELDO|SELINGO)) != 0));
if (maxloops == 0) {
printf("Infinite interrupt loop, INTSTAT = %x",
ahd_inb(ahd, INTSTAT));
}
qfreeze_cnt = ahd_inw(ahd, QFREEZE_COUNT);
if (qfreeze_cnt == 0) {
printf("%s: ahd_pause_and_flushwork with 0 qfreeze count!\n",
ahd_name(ahd));
} else {
qfreeze_cnt--;
}
ahd_outw(ahd, QFREEZE_COUNT, qfreeze_cnt);
if (qfreeze_cnt == 0)
ahd_outb(ahd, SEQ_FLAGS2,
ahd_inb(ahd, SEQ_FLAGS2) & ~SELECTOUT_QFROZEN);
ahd_flush_qoutfifo(ahd);
ahd_platform_flushwork(ahd);
ahd->flags &= ~AHD_ALL_INTERRUPTS;
ahd_restore_modes(ahd, saved_modes);
}
int
......@@ -7514,14 +7513,17 @@ ahd_reset_channel(struct ahd_softc *ahd, char channel, int initiate_reset)
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
ahd_outb(ahd, DFFSTAT, next_fifo);
} while (next_fifo != fifo);
/*
* Reset the bus if we are initiating this reset
*/
ahd_clear_msg_state(ahd);
ahd_outb(ahd, SIMODE1,
ahd_inb(ahd, SIMODE1) & ~(ENBUSFREE|ENSCSIRST|ENBUSFREE));
if (initiate_reset)
ahd_reset_current_bus(ahd);
ahd_clear_intstat(ahd);
/*
......@@ -7709,6 +7711,7 @@ ahd_handle_scsi_status(struct ahd_softc *ahd, struct scb *scb)
{
struct hardware_scb *hscb;
u_int qfreeze_cnt;
u_int maxloops;
/*
* The sequencer freezes its select-out queue
......@@ -7718,10 +7721,23 @@ ahd_handle_scsi_status(struct ahd_softc *ahd, struct scb *scb)
*/
hscb = scb->hscb;
/*
* Wait until any pending selections have been processed.
*/
maxloops = 1000;
do {
ahd_pause(ahd);
ahd_clear_critical_section(ahd);
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
if (((ahd_inb(ahd, SCSISEQ0) & ENSELO) == 0
&& (ahd_inb(ahd, SSTAT0) & (SELDO|SELINGO)) == 0)
|| (ahd_inb(ahd, SSTAT1) & SELTO) != 0)
break;
ahd_unpause(ahd);
ahd_delay(200);
} while (--maxloops);
/* Freeze the queue until the client sees the error. */
ahd_pause(ahd);
ahd_clear_critical_section(ahd);
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
ahd_freeze_devq(ahd, scb);
ahd_freeze_scb(scb);
qfreeze_cnt = ahd_inw(ahd, QFREEZE_COUNT);
......
/*
* Adaptec AIC79xx device driver for Linux.
*
* $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm.c#141 $
* $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm.c#147 $
*
* --------------------------------------------------------------------------
* Copyright (c) 1994-2000 Justin T. Gibbs.
......@@ -548,7 +548,6 @@ static aic_option_callback_t ahd_linux_setup_dv;
static aic_option_callback_t ahd_linux_setup_iocell_info;
static int ahd_linux_next_unit(void);
static void ahd_runq_tasklet(unsigned long data);
static int ahd_linux_halt(struct notifier_block *nb, u_long event, void *buf);
static int aic79xx_setup(char *c);
/****************************** Inlines ***************************************/
......@@ -1087,7 +1086,8 @@ ahd_linux_slave_destroy(Scsi_Device *device)
&& (dev->flags & AHD_DEV_SLAVE_CONFIGURED) != 0) {
dev->flags |= AHD_DEV_UNCONFIGURED;
if (TAILQ_EMPTY(&dev->busyq)
&& dev->active == 0)
&& dev->active == 0
&& (dev->flags & AHD_DEV_TIMER_ACTIVE) == 0)
ahd_linux_free_device(ahd, dev);
}
ahd_midlayer_entrypoint_unlock(ahd, &flags);
......@@ -1476,7 +1476,7 @@ ahd_linux_abort(Scsi_Cmnd *cmd)
printf("Recovery code sleeping\n");
down(&ahd->platform_data->eh_sem);
printf("Recovery code awake\n");
ret = del_timer(&timer);
ret = del_timer_sync(&timer);
if (ret == 0) {
printf("Timer Expired\n");
retval = FAILED;
......@@ -1581,7 +1581,7 @@ ahd_linux_dev_reset(Scsi_Cmnd *cmd)
down(&ahd->platform_data->eh_sem);
printf("Recovery code awake\n");
retval = SUCCESS;
if (del_timer(&timer) == 0) {
if (del_timer_sync(&timer) == 0) {
printf("Timer Expired\n");
retval = FAILED;
}
......@@ -1717,35 +1717,6 @@ ahd_runq_tasklet(unsigned long data)
#endif
}
/************************ Shutdown/halt/reboot hook ***************************/
#include <linux/notifier.h>
#include <linux/reboot.h>
static struct notifier_block ahd_linux_notifier = {
ahd_linux_halt, NULL, 0
};
static int ahd_linux_halt(struct notifier_block *nb, u_long event, void *buf)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
struct ahd_softc *ahd;
/*
* In 2.5.X, this is called prior to the filesystems
* being synced and the SCSI layer being properly
* shutdown. A different API is required there,
* but the device hooks for this don't quite look
* right.
*/
if (event == SYS_DOWN || event == SYS_HALT) {
TAILQ_FOREACH(ahd, &ahd_tailq, links) {
ahd_shutdown(ahd);
}
}
#endif
return (NOTIFY_OK);
}
/******************************** Bus DMA *************************************/
int
ahd_dma_tag_create(struct ahd_softc *ahd, bus_dma_tag_t parent,
......@@ -1927,7 +1898,7 @@ ahd_softc_comp(struct ahd_softc *lahd, struct ahd_softc *rahd)
}
static void
ahd_linux_setup_tag_info(void *arg, int instance, int targ, int32_t value)
ahd_linux_setup_tag_info(u_long arg, int instance, int targ, int32_t value)
{
if ((instance >= 0) && (targ >= 0)
......@@ -1940,18 +1911,18 @@ ahd_linux_setup_tag_info(void *arg, int instance, int targ, int32_t value)
}
static void
ahd_linux_setup_rd_strm_info(void *arg, int instance, int targ, int32_t value)
ahd_linux_setup_rd_strm_info(u_long arg, int instance, int targ, int32_t value)
{
if ((instance >= 0)
&& (instance < NUM_ELEMENTS(aic79xx_rd_strm_info))) {
aic79xx_rd_strm_info[instance] = value * 0xFFFF;
aic79xx_rd_strm_info[instance] = value & 0xFFFF;
if (bootverbose)
printf("rd_strm[%d] = 0x%x\n", instance, value);
}
}
static void
ahd_linux_setup_dv(void *arg, int instance, int targ, int32_t value)
ahd_linux_setup_dv(u_long arg, int instance, int targ, int32_t value)
{
if ((instance >= 0)
&& (instance < NUM_ELEMENTS(aic79xx_dv_settings))) {
......@@ -1962,11 +1933,9 @@ ahd_linux_setup_dv(void *arg, int instance, int targ, int32_t value)
}
static void
ahd_linux_setup_iocell_info(void *arg, int instance, int targ, int32_t value)
ahd_linux_setup_iocell_info(u_long index, int instance, int targ, int32_t value)
{
u_int index;
index = (u_int)arg;
if ((instance >= 0)
&& (instance < NUM_ELEMENTS(aic79xx_iocell_info))) {
uint8_t *iocell_info;
......@@ -1974,7 +1943,7 @@ ahd_linux_setup_iocell_info(void *arg, int instance, int targ, int32_t value)
iocell_info = (uint8_t*)&aic79xx_iocell_info[instance];
iocell_info[index] = value & 0xFFFF;
if (bootverbose)
printf("iocell[%d:%d] = %d\n", instance, index, value);
printf("iocell[%d:%ld] = %d\n", instance, index, value);
}
}
......@@ -2053,26 +2022,26 @@ aic79xx_setup(char *s)
ahd_linux_setup_tag_info_global(p + n);
} else if (strncmp(p, "tag_info", n) == 0) {
s = aic_parse_brace_option("tag_info", p + n, end,
2, ahd_linux_setup_tag_info, NULL);
2, ahd_linux_setup_tag_info, 0);
} else if (strncmp(p, "rd_strm", n) == 0) {
printf("Calling brace parse for %s\n", p);
s = aic_parse_brace_option("rd_strm", p + n, end,
1, ahd_linux_setup_rd_strm_info, NULL);
1, ahd_linux_setup_rd_strm_info, 0);
} else if (strncmp(p, "dv", n) == 0) {
s = aic_parse_brace_option("dv", p + n, end, 1,
ahd_linux_setup_dv, NULL);
ahd_linux_setup_dv, 0);
} else if (strncmp(p, "slewrate", n) == 0) {
s = aic_parse_brace_option("slewrate",
p + n, end, 1, ahd_linux_setup_iocell_info,
(void *)AIC79XX_SLEWRATE_INDEX);
AIC79XX_SLEWRATE_INDEX);
} else if (strncmp(p, "precomp", n) == 0) {
s = aic_parse_brace_option("precomp",
p + n, end, 1, ahd_linux_setup_iocell_info,
(void *)AIC79XX_PRECOMP_INDEX);
AIC79XX_PRECOMP_INDEX);
} else if (strncmp(p, "amplitude", n) == 0) {
s = aic_parse_brace_option("amplitude",
p + n, end, 1, ahd_linux_setup_iocell_info,
(void *)AIC79XX_AMPLITUDE_INDEX);
AIC79XX_AMPLITUDE_INDEX);
} else if (p[n] == ':') {
*(options[i].flag) = simple_strtoul(p + n + 1, NULL, 0);
} else if (!strncmp(p, "verbose", n)) {
......@@ -2291,8 +2260,6 @@ ahd_platform_alloc(struct ahd_softc *ahd, void *platform_arg)
#endif
ahd_setup_runq_tasklet(ahd);
ahd->seltime = (aic79xx_seltime & 0x3) << 4;
if (TAILQ_EMPTY(&ahd_tailq))
register_reboot_notifier(&ahd_linux_notifier);
return (0);
}
......@@ -2304,6 +2271,7 @@ ahd_platform_free(struct ahd_softc *ahd)
int i, j;
if (ahd->platform_data != NULL) {
del_timer_sync(&ahd->platform_data->completeq_timer);
ahd_linux_kill_dv_thread(ahd);
ahd_teardown_runq_tasklet(ahd);
if (ahd->platform_data->host != NULL) {
......@@ -2317,15 +2285,20 @@ ahd_platform_free(struct ahd_softc *ahd)
for (i = 0; i < AHD_NUM_TARGETS; i++) {
targ = ahd->platform_data->targets[i];
if (targ != NULL) {
/* Keep target around through the loop. */
targ->refcount++;
for (j = 0; j < AHD_NUM_LUNS; j++) {
if (targ->devices[j] != NULL) {
dev = targ->devices[j];
ahd_linux_free_device(ahd, dev);
}
if (ahd->platform_data->targets[i] ==
NULL)
break;
if (targ->devices[j] == NULL)
continue;
dev = targ->devices[j];
ahd_linux_free_device(ahd, dev);
}
/*
* Forcibly free the target now that
* all devices are gone.
*/
ahd_linux_free_target(ahd, targ);
}
}
......@@ -2921,10 +2894,14 @@ ahd_linux_dv_target(struct ahd_softc *ahd, u_int target_offset)
}
ahd_lock(ahd, &s);
if (targ->dv_buffer != NULL)
if (targ->dv_buffer != NULL) {
free(targ->dv_buffer, M_DEVBUF);
if (targ->dv_buffer1 != NULL)
targ->dv_buffer = NULL;
}
if (targ->dv_buffer1 != NULL) {
free(targ->dv_buffer1, M_DEVBUF);
targ->dv_buffer1 = NULL;
}
targ->flags &= ~AHD_DV_REQUIRED;
if (targ->refcount == 0)
ahd_linux_free_target(ahd, targ);
......@@ -4542,7 +4519,8 @@ ahd_done(struct ahd_softc *ahd, struct scb *scb)
if (TAILQ_EMPTY(&dev->busyq)) {
if ((dev->flags & AHD_DEV_UNCONFIGURED) != 0
&& dev->active == 0)
&& dev->active == 0
&& (dev->flags & AHD_DEV_TIMER_ACTIVE) == 0)
ahd_linux_free_device(ahd, dev);
} else if ((dev->flags & AHD_DEV_ON_RUN_LIST) == 0) {
TAILQ_INSERT_TAIL(&ahd->platform_data->device_runq, dev, links);
......@@ -5065,6 +5043,9 @@ ahd_linux_dev_timed_unfreeze(u_long arg)
if (dev->qfrozen == 0
&& (dev->flags & AHD_DEV_ON_RUN_LIST) == 0)
ahd_linux_run_device_queue(ahd, dev);
if ((dev->flags & AHD_DEV_UNCONFIGURED) != 0
&& dev->active == 0)
ahd_linux_free_device(ahd, dev);
ahd_unlock(ahd, &s);
}
......@@ -5143,8 +5124,6 @@ ahd_linux_exit(void)
scsi_unregister_module(MODULE_SCSI_HA, &aic79xx_driver_template);
#endif
ahd_linux_pci_exit();
unregister_reboot_notifier(&ahd_linux_notifier);
}
module_init(ahd_linux_init);
......
......@@ -36,7 +36,7 @@
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm.h#121 $
* $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm.h#123 $
*
*/
#ifndef _AIC79XX_LINUX_H_
......@@ -255,7 +255,7 @@ typedef struct timer_list ahd_timer_t;
/***************************** Timer Facilities *******************************/
#define ahd_timer_init init_timer
#define ahd_timer_stop del_timer
#define ahd_timer_stop del_timer_sync
typedef void ahd_linux_callback_t (u_long);
static __inline void ahd_timer_reset(ahd_timer_t *timer, u_int usec,
ahd_callback_t *func, void *arg);
......@@ -293,7 +293,7 @@ ahd_scb_timer_reset(struct scb *scb, u_int usec)
#define AHD_SCSI_HAS_HOST_LOCK 0
#endif
#define AIC79XX_DRIVER_VERSION "1.3.5"
#define AIC79XX_DRIVER_VERSION "1.3.6"
/**************************** Front End Queues ********************************/
/*
......
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