patch-2.3.41 linux/drivers/scsi/qlogicfc.c
Next file: linux/drivers/scsi/qlogicfc_asm.c
Previous file: linux/drivers/scsi/pci2220i.h
Back to the patch index
Back to the overall index
- Lines: 359
- Date:
Wed Jan 26 13:29:39 2000
- Orig file:
v2.3.40/linux/drivers/scsi/qlogicfc.c
- Orig date:
Thu Nov 11 20:11:48 1999
diff -u --recursive --new-file v2.3.40/linux/drivers/scsi/qlogicfc.c linux/drivers/scsi/qlogicfc.c
@@ -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 */
/*
@@ -649,11 +649,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 +662,7 @@
u_int port_id;
u_char queued;
u_char host_id;
+ struct timer_list explore_timer;
};
@@ -676,7 +677,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 *);
@@ -744,14 +745,27 @@
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 = 0x000e;
hostdata->control_block.max_frame_len = 2048;
- hostdata->control_block.max_iocb = 256;
- hostdata->control_block.exec_throttle = 8;
+ 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;
hostdata->control_block.node_name[0] = 0x0020;
@@ -761,16 +775,18 @@
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->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;
}
@@ -779,6 +795,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;
}
@@ -787,6 +805,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;
}
@@ -803,6 +823,7 @@
printk("qlogicfc%d : link is not up\n", hostdata->host_id);
}
hosts++;
+ hostdata->explore_timer.data = 0;
}
}
@@ -1078,6 +1099,9 @@
release_region(host->io_port, 0xff);
+ kfree(hostdata->res);
+ kfree(hostdata->req);
+
LEAVE("isp2x00_release");
return 0;
@@ -1133,7 +1157,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) {
@@ -1148,7 +1177,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));
@@ -1175,7 +1204,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);
@@ -1197,6 +1226,7 @@
printk("slot %d has %p\n", i, hostdata->handle_ptrs[i]);
}
}
+ return 1;
}
cmd->hdr.entry_type = ENTRY_COMMAND;
@@ -1234,7 +1264,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) {
@@ -1280,8 +1310,9 @@
/* scsi.c expects sense info in a different buffer */
cmd->dataseg[0].d_base = virt_to_bus_low32(Cmnd->sense_buffer);
#if BITS_PER_LONG > 32
- cmd->dataseg[0].d_base_hi = virt_to_bus_high32(Cmnd->request_buffer);
+ cmd->dataseg[0].d_base_hi = virt_to_bus_high32(Cmnd->sense_buffer);
#endif
+ cmd->dataseg[0].d_count = sizeof(Cmnd->sense_buffer);
cmd->segment_cnt = 1;
cmd->control_flags = CFLAG_READ;
break;
@@ -1333,8 +1364,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)
{
@@ -1377,23 +1457,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;
@@ -1428,22 +1509,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
@@ -1453,6 +1539,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;
}
@@ -1473,6 +1560,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;
@@ -1514,7 +1603,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
@@ -1558,7 +1647,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:
@@ -1848,7 +1940,7 @@
static int isp2x00_init(struct Scsi_Host *sh)
{
- u_int io_base;
+ u_long io_base;
struct isp2x00_hostdata *hostdata;
u_char revision;
u_int irq;
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)