patch-2.2.15 linux/drivers/scsi/qlogicfc.c
Next file: linux/drivers/scsi/qlogicfc_asm.c
Previous file: linux/drivers/scsi/psi_dale.h
Back to the patch index
Back to the overall index
- Lines: 374
- Date:
Fri Apr 21 12:46:31 2000
- Orig file:
v2.2.14/drivers/scsi/qlogicfc.c
- Orig date:
Tue Jan 4 21:18:58 2000
diff -u --new-file --recursive --exclude-from ../../exclude v2.2.14/drivers/scsi/qlogicfc.c linux/drivers/scsi/qlogicfc.c
@@ -64,7 +64,7 @@
/* Configuration section **************************************************** */
/* Set the following macro to 1 to reload the ISP2x00's firmware. This is
- version 1.15.37 of the isp2100's firmware and version 2.00.16 of the
+ version 1.17.30 of the isp2100's firmware and version 2.00.40 of the
isp2200's firmware.
*/
@@ -77,7 +77,7 @@
/* Set the following to 1 to include fabric support, fabric support is
* currently not as well tested as the other aspects of the driver */
-#define ISP2x00_FABRIC 0
+#define ISP2x00_FABRIC 1
/* Macros used for debugging */
/*
@@ -91,7 +91,7 @@
/* #define TRACE_ISP 1 */
-#define DEFAULT_LOOP_COUNT 10000000
+#define DEFAULT_LOOP_COUNT 1000000000
/* End Configuration section ************************************************ */
@@ -632,6 +632,17 @@
#define QLOGICFC_MAX_LUN 128
#define QLOGICFC_MAX_LOOP_ID 0x7d
+/* the following connection options only apply to the 2200. i have only
+ * had success with LOOP_ONLY and P2P_ONLY.
+ */
+
+#define LOOP_ONLY 0
+#define P2P_ONLY 1
+#define LOOP_PREFERED 2
+#define P2P_PREFERED 3
+
+#define CONNECTION_PREFERENCE LOOP_ONLY
+
/* adapter_state values */
#define AS_FIRMWARE_DEAD -1
#define AS_LOOP_DOWN 0
@@ -649,11 +660,11 @@
/* this is here so the queues are nicely aligned */
long send_marker; /* do we need to send a marker? */
- char res[RES_QUEUE_LEN + 1][QUEUE_ENTRY_LEN];
- char req[QLOGICFC_REQ_QUEUE_LEN + 1][QUEUE_ENTRY_LEN];
+ char * res;
+ char * req;
struct init_cb control_block;
int adapter_state;
- unsigned long int tag_ages[126];
+ unsigned long int tag_ages[QLOGICFC_MAX_ID + 1];
Scsi_Cmnd *handle_ptrs[QLOGICFC_REQ_QUEUE_LEN + 1];
unsigned long handle_serials[QLOGICFC_REQ_QUEUE_LEN + 1];
struct id_name_map port_db[QLOGICFC_MAX_ID + 1];
@@ -662,6 +673,7 @@
u_int port_id;
u_char queued;
u_char host_id;
+ struct timer_list explore_timer;
};
@@ -676,7 +688,7 @@
static int isp2x00_init(struct Scsi_Host *);
static int isp2x00_reset_hardware(struct Scsi_Host *);
static int isp2x00_mbox_command(struct Scsi_Host *, u_short[]);
-static int isp2x00_return_status(struct Status_Entry *);
+static int isp2x00_return_status(Scsi_Cmnd *, struct Status_Entry *);
static void isp2x00_intr_handler(int, void *, struct pt_regs *);
static void do_isp2x00_intr_handler(int, void *, struct pt_regs *);
static int isp2x00_make_portdb(struct Scsi_Host *);
@@ -751,13 +763,26 @@
memset(hostdata, 0, sizeof(struct isp2x00_hostdata));
hostdata->pci_dev = pdev;
+ hostdata->res = (char *) kmalloc((RES_QUEUE_LEN + 1)*QUEUE_ENTRY_LEN, GFP_KERNEL);
+ if (!hostdata->res){
+ printk("qlogicfc%d : could not allocate memory for response queue.\n", hostdata->host_id);
+ scsi_unregister(host);
+ continue;
+ }
+ hostdata->req = (char *) kmalloc((QLOGICFC_REQ_QUEUE_LEN + 1)*QUEUE_ENTRY_LEN, GFP_KERNEL);
+ if (!hostdata->req){
+ printk("qlogicfc%d : could not allocate memory for request queue.\n", hostdata->host_id);
+ kfree(hostdata->res);
+ scsi_unregister(host);
+ continue;
+ }
hostdata->queued = 0;
/* set up the control block */
hostdata->control_block.version = 0x1;
- hostdata->control_block.firm_opts = 0x0108;
+ hostdata->control_block.firm_opts = 0x800e;
hostdata->control_block.max_frame_len = 2048;
- hostdata->control_block.max_iocb = 256;
+ hostdata->control_block.max_iocb = QLOGICFC_REQ_QUEUE_LEN;
hostdata->control_block.exec_throttle = QLOGICFC_CMD_PER_LUN;
hostdata->control_block.retry_delay = 5;
hostdata->control_block.retry_cnt = 1;
@@ -768,16 +793,20 @@
hostdata->control_block.hard_addr = 0x0003;
hostdata->control_block.req_queue_len = QLOGICFC_REQ_QUEUE_LEN + 1;
hostdata->control_block.res_queue_len = RES_QUEUE_LEN + 1;
- hostdata->control_block.res_queue_addr_lo = virt_to_bus_low32(&hostdata->res);
- hostdata->control_block.res_queue_addr_high = virt_to_bus_high32(&hostdata->res);
- hostdata->control_block.req_queue_addr_lo = virt_to_bus_low32(&hostdata->req);
- hostdata->control_block.req_queue_addr_high = virt_to_bus_high32(&hostdata->req);
+ hostdata->control_block.res_queue_addr_lo = virt_to_bus_low32(hostdata->res);
+ hostdata->control_block.res_queue_addr_high = virt_to_bus_high32(hostdata->res);
+ hostdata->control_block.req_queue_addr_lo = virt_to_bus_low32(hostdata->req);
+ hostdata->control_block.req_queue_addr_high = virt_to_bus_high32(hostdata->req);
+ hostdata->control_block.add_firm_opts |= CONNECTION_PREFERENCE<<4;
hostdata->adapter_state = AS_LOOP_DOWN;
+ hostdata->explore_timer.data = 1;
hostdata->host_id = hosts;
-
+
if (isp2x00_init(host) || isp2x00_reset_hardware(host)) {
+ kfree(hostdata->res);
+ kfree(hostdata->req);
scsi_unregister(host);
continue;
}
@@ -786,6 +815,8 @@
if (request_irq(host->irq, do_isp2x00_intr_handler, SA_INTERRUPT | SA_SHIRQ, "qlogicfc", host)) {
printk("qlogicfc%d : interrupt %d already in use\n",
hostdata->host_id, host->irq);
+ kfree(hostdata->res);
+ kfree(hostdata->req);
scsi_unregister(host);
continue;
}
@@ -794,6 +825,8 @@
"in use\n",
hostdata->host_id, host->io_port, host->io_port + 0xff);
free_irq(host->irq, host);
+ kfree(hostdata->res);
+ kfree(hostdata->req);
scsi_unregister(host);
continue;
}
@@ -810,6 +843,7 @@
printk("qlogicfc%d : link is not up\n", hostdata->host_id);
}
hosts++;
+ hostdata->explore_timer.data = 0;
}
}
@@ -1085,6 +1119,9 @@
release_region(host->io_port, 0xff);
+ kfree(hostdata->res);
+ kfree(hostdata->req);
+
LEAVE("isp2x00_release");
return 0;
@@ -1140,7 +1177,12 @@
hostdata->adapter_state = AS_LOOP_GOOD;
printk("qlogicfc%d : Port Database\n", hostdata->host_id);
for (i = 0; hostdata->port_db[i].wwn != 0; i++) {
- printk("wwn: %08x%08x scsi_id: %x loop_id: %x\n", (u_int) (hostdata->port_db[i].wwn >> 32), (u_int) hostdata->port_db[i].wwn, i, hostdata->port_db[i].loop_id);
+ printk("wwn: %08x%08x scsi_id: %x loop_id: ", (u_int) (hostdata->port_db[i].wwn >> 32), (u_int) hostdata->port_db[i].wwn, i);
+ if (hostdata->port_db[i].loop_id != hostdata->port_db[0].loop_id || i == 0)
+ printk("%x", hostdata->port_db[i].loop_id);
+ else
+ printk("Not Available");
+ printk("\n");
}
}
if (hostdata->adapter_state == AS_FIRMWARE_DEAD) {
@@ -1155,7 +1197,7 @@
DEBUG(printk("qlogicfc%d : request queue depth %d\n", hostdata->host_id,
REQ_QUEUE_DEPTH(in_ptr, out_ptr)));
- cmd = (struct Command_Entry *) &hostdata->req[in_ptr][0];
+ cmd = (struct Command_Entry *) &hostdata->req[in_ptr*QUEUE_ENTRY_LEN];
in_ptr = (in_ptr + 1) & QLOGICFC_REQ_QUEUE_LEN;
if (in_ptr == out_ptr) {
DEBUG(printk("qlogicfc%d : request queue overflow\n", hostdata->host_id));
@@ -1182,7 +1224,7 @@
DEBUG(printk("qlogicfc%d : request queue overflow\n", hostdata->host_id));
return 1;
}
- cmd = (struct Command_Entry *) &hostdata->req[in_ptr][0];
+ cmd = (struct Command_Entry *) &hostdata->req[in_ptr*QUEUE_ENTRY_LEN];
in_ptr = (in_ptr + 1) & QLOGICFC_REQ_QUEUE_LEN;
}
TRACE("queue command", in_ptr, Cmnd);
@@ -1204,6 +1246,7 @@
printk("slot %d has %p\n", i, hostdata->handle_ptrs[i]);
}
}
+ return 1;
}
cmd->hdr.entry_type = ENTRY_COMMAND;
@@ -1241,7 +1284,7 @@
while (sg_count > 0) {
++cmd->hdr.entry_cnt;
cont = (struct Continuation_Entry *)
- &hostdata->req[in_ptr][0];
+ &hostdata->req[in_ptr*QUEUE_ENTRY_LEN];
memset(cont, 0, sizeof(struct Continuation_Entry));
in_ptr = (in_ptr + 1) & QLOGICFC_REQ_QUEUE_LEN;
if (in_ptr == out_ptr) {
@@ -1341,8 +1384,57 @@
}
-#define ASYNC_EVENT_INTERRUPT 0x01
+/* we have received an event, such as a lip or an RSCN, which may mean that
+ * our port database is incorrect so the port database must be recreated.
+ */
+static void redo_port_db(unsigned long arg)
+{
+
+ struct Scsi_Host * host = (struct Scsi_Host *) arg;
+ struct isp2x00_hostdata * hostdata;
+ unsigned long flags;
+ int i;
+
+ hostdata = (struct isp2x00_hostdata *) host->hostdata;
+ hostdata->explore_timer.data = 0;
+ del_timer(&hostdata->explore_timer);
+
+ spin_lock_irqsave(&io_request_lock, flags);
+ if (hostdata->adapter_state & AS_REDO_FABRIC_PORTDB || hostdata->adapter_state & AS_REDO_LOOP_PORTDB) {
+ isp2x00_make_portdb(host);
+ printk("qlogicfc%d : Port Database\n", hostdata->host_id);
+ for (i = 0; hostdata->port_db[i].wwn != 0; i++) {
+ printk("wwn: %08x%08x scsi_id: %x loop_id: ", (u_int) (hostdata->port_db[i].wwn >> 32), (u_int) hostdata->port_db[i].wwn, i);
+ if (hostdata->port_db[i].loop_id != hostdata->port_db[0].loop_id || i == 0)
+ printk("%x", hostdata->port_db[i].loop_id);
+ else
+ printk("Not Available");
+ printk("\n");
+ }
+
+ for (i = 0; i < QLOGICFC_REQ_QUEUE_LEN; i++){
+ if (hostdata->handle_ptrs[i] && (hostdata->port_db[hostdata->handle_ptrs[i]->target].loop_id > QLOGICFC_MAX_LOOP_ID || hostdata->adapter_state & AS_REDO_LOOP_PORTDB)){
+ if (hostdata->port_db[hostdata->handle_ptrs[i]->target].loop_id != hostdata->port_db[0].loop_id){
+ hostdata->handle_ptrs[i]->result = DID_SOFT_ERROR << 16;
+ if (hostdata->handle_ptrs[i]->scsi_done){
+ (*hostdata->handle_ptrs[i]->scsi_done) (hostdata->handle_ptrs[i]);
+ }
+ else printk("qlogicfc%d : done is null?\n", hostdata->host_id);
+ hostdata->handle_ptrs[i] = NULL;
+ hostdata->handle_serials[i] = 0;
+ }
+ }
+ }
+
+ hostdata->adapter_state = AS_LOOP_GOOD;
+ }
+
+ spin_unlock_irqrestore(&io_request_lock, flags);
+
+}
+
+#define ASYNC_EVENT_INTERRUPT 0x01
void do_isp2x00_intr_handler(int irq, void *dev_id, struct pt_regs *regs)
{
@@ -1385,23 +1477,24 @@
switch (status) {
case LOOP_UP:
case POINT_TO_POINT_UP:
- printk("qlogicfc%d : link is up\n", hostdata->host_id);
+ printk("qlogicfc%d : Link is Up\n", hostdata->host_id);
hostdata->adapter_state = AS_REDO_FABRIC_PORTDB | AS_REDO_LOOP_PORTDB;
break;
case LOOP_DOWN:
- printk("qlogicfc%d : link is down\n", hostdata->host_id);
+ printk("qlogicfc%d : Link is Down\n", hostdata->host_id);
hostdata->adapter_state = AS_LOOP_DOWN;
break;
case CONNECTION_MODE:
printk("received CONNECTION_MODE irq %x\n", inw(host->io_port + MBOX1));
break;
case CHANGE_NOTIFICATION:
+ printk("qlogicfc%d : RSCN Received\n", hostdata->host_id);
if (hostdata->adapter_state == AS_LOOP_GOOD)
hostdata->adapter_state = AS_REDO_FABRIC_PORTDB;
break;
case LIP_OCCURED:
- case PORT_DB_CHANGED:
case LIP_RECEIVED:
+ printk("qlogicfc%d : Loop Reinitialized\n", hostdata->host_id);
if (hostdata->adapter_state == AS_LOOP_GOOD)
hostdata->adapter_state = AS_REDO_LOOP_PORTDB;
break;
@@ -1436,22 +1529,27 @@
default:
printk("qlogicfc%d : got an unknown status? %x\n", hostdata->host_id, status);
}
+ if ((hostdata->adapter_state & AS_REDO_LOOP_PORTDB || hostdata->adapter_state & AS_REDO_FABRIC_PORTDB) && hostdata->explore_timer.data == 0){
+ hostdata->explore_timer.function = redo_port_db;
+ hostdata->explore_timer.data = (unsigned long)host;
+ hostdata->explore_timer.expires = jiffies + (HZ/4);
+ init_timer(&hostdata->explore_timer);
+ add_timer(&hostdata->explore_timer);
+ }
outw(0x0, host->io_port + PCI_SEMAPHORE);
} else {
DEBUG_INTR(printk("qlogicfc%d : response queue update\n", hostdata->host_id));
DEBUG_INTR(printk("qlogicfc%d : response queue depth %d\n", hostdata->host_id, RES_QUEUE_DEPTH(in_ptr, out_ptr)));
while (out_ptr != in_ptr) {
- sts = (struct Status_Entry *) &hostdata->res[out_ptr][0];
+ sts = (struct Status_Entry *) &hostdata->res[out_ptr*QUEUE_ENTRY_LEN];
out_ptr = (out_ptr + 1) & RES_QUEUE_LEN;
TRACE("done", out_ptr, Cmnd);
DEBUG_INTR(isp2x00_print_status_entry(sts));
if (sts->hdr.entry_type == ENTRY_STATUS && (Cmnd = hostdata->handle_ptrs[sts->handle])) {
- Cmnd->result = isp2x00_return_status(sts);
- hostdata->handle_ptrs[sts->handle] = NULL;
+ Cmnd->result = isp2x00_return_status(Cmnd, sts);
hostdata->queued--;
-
/*
* if any of the following are true we do not
* call scsi_done. if the status is CS_ABORTED
@@ -1461,6 +1559,7 @@
if (hostdata->handle_serials[sts->handle] != Cmnd->serial_number
|| sts->completion_status == CS_ABORTED){
hostdata->handle_serials[sts->handle] = 0;
+ hostdata->handle_ptrs[sts->handle] = NULL;
outw(out_ptr, host->io_port + MBOX5);
continue;
}
@@ -1481,6 +1580,8 @@
continue;
}
+ hostdata->handle_ptrs[sts->handle] = NULL;
+
if (sts->completion_status == CS_RESET_OCCURRED
|| (sts->status_flags & STF_BUS_RESET))
hostdata->send_marker = 1;
@@ -1522,7 +1623,7 @@
}
-static int isp2x00_return_status(struct Status_Entry *sts)
+static int isp2x00_return_status(Scsi_Cmnd *Cmnd, struct Status_Entry *sts)
{
int host_status = DID_ERROR;
#if DEBUG_ISP2x00_INTR
@@ -1566,7 +1667,10 @@
host_status = DID_ERROR;
break;
case CS_DATA_UNDERRUN:
- host_status = DID_OK;
+ if (Cmnd->underflow <= (Cmnd->request_bufflen - sts->residual))
+ host_status = DID_OK;
+ else
+ host_status = DID_ERROR;
break;
case CS_PORT_UNAVAILABLE:
case CS_PORT_LOGGED_OUT:
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)