}
}
+/*
+ * We used to try to deal with 64-bit BARs here, but don't any more.
+ * There are many parts of this driver which would need to be modified
+ * to handle a 64-bit base address, including scripts. I'm uncomfortable
+ * with making those changes when I have no way of testing it, so I'm
+ * just going to disable it.
+ *
+ * Note that some machines (eg HP rx8620 and Superdome) have bus addresses
+ * below 4GB and physical addresses above 4GB. These will continue to work.
+ */
+static int __devinit
+pci_get_base_address(struct pci_dev *pdev, int index, unsigned long *basep)
+{
+ u32 tmp;
+ unsigned long base;
+#define PCI_BAR_OFFSET(index) (PCI_BASE_ADDRESS_0 + (index<<2))
+
+ pci_read_config_dword(pdev, PCI_BAR_OFFSET(index++), &tmp);
+ base = tmp;
+ if ((tmp & 0x7) == PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ pci_read_config_dword(pdev, PCI_BAR_OFFSET(index++), &tmp);
+ if (tmp > 0) {
+ dev_err(&pdev->dev,
+ "BAR %d is 64-bit, disabling\n", index - 1);
+ base = 0;
+ }
+ }
+
+ if ((base & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
+ base &= PCI_BASE_ADDRESS_IO_MASK;
+ } else {
+ base &= PCI_BASE_ADDRESS_MEM_MASK;
+ }
+
+ *basep = base;
+ return index;
+#undef PCI_BAR_OFFSET
+}
+
static struct scsi_transport_template *sym2_transport_template = NULL;
+/*
+ * Used by the eh thread to wait for command completion.
+ * It is allocated on the eh thread stack.
+ */
+struct sym_eh_wait {
+ struct completion done;
+ struct timer_list timer;
+ void (*old_done)(struct scsi_cmnd *);
+ int to_do;
+ int timed_out;
+};
+
/*
* Driver private area in the SCSI command structure.
*/
struct sym_ucmd { /* Override the SCSI pointer structure */
- dma_addr_t data_mapping;
- unsigned char data_mapped;
- unsigned char to_do; /* For error handling */
- void (*old_done)(struct scsi_cmnd *); /* For error handling */
- struct completion *eh_done; /* For error handling */
+ dma_addr_t data_mapping;
+ u_char data_mapped;
+ struct sym_eh_wait *eh_wait;
};
#define SYM_UCMD_PTR(cmd) ((struct sym_ucmd *)(&(cmd)->SCp))
*/
int sym_setup_data_and_start(struct sym_hcb *np, struct scsi_cmnd *cmd, struct sym_ccb *cp)
{
+ struct sym_tcb *tp = &np->target[cp->target];
+ struct sym_lcb *lp = sym_lp(tp, cp->lun);
u32 lastp, goalp;
int dir;
/*
* activate this job.
*/
- sym_put_start_queue(np, cp);
+ sym_start_next_ccbs(np, lp, 2);
return 0;
out_abort:
* What we will do regarding the involved SCSI command.
*/
#define SYM_EH_DO_IGNORE 0
+#define SYM_EH_DO_COMPLETE 1
#define SYM_EH_DO_WAIT 2
/*
- * scsi_done() alias when error recovery is in progress.
+ * Our general completion handler.
*/
-static void sym_eh_done(struct scsi_cmnd *cmd)
+static void __sym_eh_done(struct scsi_cmnd *cmd, int timed_out)
{
- struct sym_ucmd *ucmd = SYM_UCMD_PTR(cmd);
- BUILD_BUG_ON(sizeof(struct scsi_pointer) < sizeof(struct sym_ucmd));
+ struct sym_eh_wait *ep = SYM_UCMD_PTR(cmd)->eh_wait;
+ if (!ep)
+ return;
+
+ /* Try to avoid a race here (not 100% safe) */
+ if (!timed_out) {
+ ep->timed_out = 0;
+ if (ep->to_do == SYM_EH_DO_WAIT && !del_timer(&ep->timer))
+ return;
+ }
- cmd->scsi_done = ucmd->old_done;
+ /* Revert everything */
+ SYM_UCMD_PTR(cmd)->eh_wait = NULL;
+ cmd->scsi_done = ep->old_done;
- if (ucmd->to_do == SYM_EH_DO_WAIT)
- complete(ucmd->eh_done);
+ /* Wake up the eh thread if it wants to sleep */
+ if (ep->to_do == SYM_EH_DO_WAIT)
+ complete(&ep->done);
}
+/*
+ * scsi_done() alias when error recovery is in progress.
+ */
+static void sym_eh_done(struct scsi_cmnd *cmd) { __sym_eh_done(cmd, 0); }
+
+/*
+ * Some timeout handler to avoid waiting too long.
+ */
+static void sym_eh_timeout(u_long p) { __sym_eh_done((struct scsi_cmnd *)p, 1); }
+
/*
* Generic method for our eh processing.
* The 'op' argument tells what we have to do.
static int sym_eh_handler(int op, char *opname, struct scsi_cmnd *cmd)
{
struct sym_hcb *np = SYM_SOFTC_PTR(cmd);
- struct sym_ucmd *ucmd = SYM_UCMD_PTR(cmd);
- struct Scsi_Host *host = cmd->device->host;
SYM_QUEHEAD *qp;
int to_do = SYM_EH_DO_IGNORE;
int sts = -1;
- struct completion eh_done;
+ struct sym_eh_wait eh, *ep = &eh;
dev_warn(&cmd->device->sdev_gendev, "%s operation started.\n", opname);
- spin_lock_irq(host->host_lock);
/* This one is queued in some place -> to wait for completion */
FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) {
struct sym_ccb *cp = sym_que_entry(qp, struct sym_ccb, link_ccbq);
if (cp->cmd == cmd) {
to_do = SYM_EH_DO_WAIT;
- break;
+ goto prepare;
}
}
- if (to_do == SYM_EH_DO_WAIT) {
- init_completion(&eh_done);
- ucmd->old_done = cmd->scsi_done;
- ucmd->eh_done = &eh_done;
- wmb();
+prepare:
+ /* Prepare stuff to either ignore, complete or wait for completion */
+ switch(to_do) {
+ default:
+ case SYM_EH_DO_IGNORE:
+ break;
+ case SYM_EH_DO_WAIT:
+ init_completion(&ep->done);
+ /* fall through */
+ case SYM_EH_DO_COMPLETE:
+ ep->old_done = cmd->scsi_done;
cmd->scsi_done = sym_eh_done;
+ SYM_UCMD_PTR(cmd)->eh_wait = ep;
}
/* Try to proceed the operation we have been asked for */
/* On error, restore everything and cross fingers :) */
if (sts) {
- cmd->scsi_done = ucmd->old_done;
+ SYM_UCMD_PTR(cmd)->eh_wait = NULL;
+ cmd->scsi_done = ep->old_done;
to_do = SYM_EH_DO_IGNORE;
}
- ucmd->to_do = to_do;
- spin_unlock_irq(host->host_lock);
+ ep->to_do = to_do;
+ /* Complete the command with locks held as required by the driver */
+ if (to_do == SYM_EH_DO_COMPLETE)
+ sym_xpt_done2(np, cmd, DID_ABORT);
+ /* Wait for completion with locks released, as required by kernel */
if (to_do == SYM_EH_DO_WAIT) {
- if (!wait_for_completion_timeout(&eh_done, 5*HZ)) {
- ucmd->to_do = SYM_EH_DO_IGNORE;
- wmb();
+ init_timer(&ep->timer);
+ ep->timer.expires = jiffies + (5*HZ);
+ ep->timer.function = sym_eh_timeout;
+ ep->timer.data = (u_long)cmd;
+ ep->timed_out = 1; /* Be pessimistic for once :) */
+ add_timer(&ep->timer);
+ spin_unlock_irq(np->s.host->host_lock);
+ wait_for_completion(&ep->done);
+ spin_lock_irq(np->s.host->host_lock);
+ if (ep->timed_out)
sts = -2;
- }
}
dev_warn(&cmd->device->sdev_gendev, "%s operation %s.\n", opname,
sts==0 ? "complete" :sts==-2 ? "timed-out" : "failed");
*/
static int sym53c8xx_eh_abort_handler(struct scsi_cmnd *cmd)
{
- return sym_eh_handler(SYM_EH_ABORT, "ABORT", cmd);
+ int rc;
+
+ spin_lock_irq(cmd->device->host->host_lock);
+ rc = sym_eh_handler(SYM_EH_ABORT, "ABORT", cmd);
+ spin_unlock_irq(cmd->device->host->host_lock);
+
+ return rc;
}
static int sym53c8xx_eh_device_reset_handler(struct scsi_cmnd *cmd)
{
- return sym_eh_handler(SYM_EH_DEVICE_RESET, "DEVICE RESET", cmd);
+ int rc;
+
+ spin_lock_irq(cmd->device->host->host_lock);
+ rc = sym_eh_handler(SYM_EH_DEVICE_RESET, "DEVICE RESET", cmd);
+ spin_unlock_irq(cmd->device->host->host_lock);
+
+ return rc;
}
static int sym53c8xx_eh_bus_reset_handler(struct scsi_cmnd *cmd)
{
- return sym_eh_handler(SYM_EH_BUS_RESET, "BUS RESET", cmd);
+ int rc;
+
+ spin_lock_irq(cmd->device->host->host_lock);
+ rc = sym_eh_handler(SYM_EH_BUS_RESET, "BUS RESET", cmd);
+ spin_unlock_irq(cmd->device->host->host_lock);
+
+ return rc;
}
static int sym53c8xx_eh_host_reset_handler(struct scsi_cmnd *cmd)
{
- return sym_eh_handler(SYM_EH_HOST_RESET, "HOST RESET", cmd);
+ int rc;
+
+ spin_lock_irq(cmd->device->host->host_lock);
+ rc = sym_eh_handler(SYM_EH_HOST_RESET, "HOST RESET", cmd);
+ spin_unlock_irq(cmd->device->host->host_lock);
+
+ return rc;
}
/*
if (reqtags > lp->s.scdev_depth)
reqtags = lp->s.scdev_depth;
+ lp->started_limit = reqtags ? reqtags : 2;
+ lp->started_max = 1;
lp->s.reqtags = reqtags;
if (reqtags != oldtags) {
dev_info(&tp->starget->dev,
"tagged command queuing %s, command queue depth %d.\n",
- lp->s.reqtags ? "enabled" : "disabled", reqtags);
+ lp->s.reqtags ? "enabled" : "disabled",
+ lp->started_limit);
}
}
static void __devinit
sym_init_device(struct pci_dev *pdev, struct sym_device *device)
{
- int i = 2;
- struct pci_bus_region bus_addr;
+ int i;
device->host_id = SYM_SETUP_HOST_ID;
device->pdev = pdev;
- pcibios_resource_to_bus(pdev, &bus_addr, &pdev->resource[1]);
- device->mmio_base = bus_addr.start;
-
- /*
- * If the BAR is 64-bit, resource 2 will be occupied by the
- * upper 32 bits
- */
- if (!pdev->resource[i].flags)
- i++;
- pcibios_resource_to_bus(pdev, &bus_addr, &pdev->resource[i]);
- device->ram_base = bus_addr.start;
+ i = pci_get_base_address(pdev, 1, &device->mmio_base);
+ pci_get_base_address(pdev, i, &device->ram_base);
-#ifdef CONFIG_SCSI_SYM53C8XX_MMIO
+#ifndef CONFIG_SCSI_SYM53C8XX_IOMAPPED
if (device->mmio_base)
device->s.ioaddr = pci_iomap(pdev, 1,
pci_resource_len(pdev, 1));
.eh_bus_reset_handler = sym53c8xx_eh_bus_reset_handler,
.eh_host_reset_handler = sym53c8xx_eh_host_reset_handler,
.this_id = 7,
- .use_clustering = ENABLE_CLUSTERING,
- .max_sectors = 0xFFFF,
+ .use_clustering = DISABLE_CLUSTERING,
#ifdef SYM_LINUX_PROC_INFO_SUPPORT
.proc_info = sym53c8xx_proc_info,
.proc_name = NAME53C8XX,