patch-2.1.97 linux/drivers/fc4/fc.c
Next file: linux/drivers/fc4/fc_syms.c
Previous file: linux/drivers/char/vt.c
Back to the patch index
Back to the overall index
- Lines: 522
- Date:
Tue Apr 14 17:44:21 1998
- Orig file:
v2.1.96/linux/drivers/fc4/fc.c
- Orig date:
Mon Jan 12 15:19:36 1998
diff -u --recursive --new-file v2.1.96/linux/drivers/fc4/fc.c linux/drivers/fc4/fc.c
@@ -1,7 +1,7 @@
/* fc.c: Generic Fibre Channel and FC4 SCSI driver.
*
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- * Copyright (C) 1997 Jiri Hanika (geo@ff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jiri Hanika (geo@ff.cuni.cz)
*
* Sources:
* Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994
@@ -51,9 +51,9 @@
return mmu_get_scsi_one (buf, len, fc->dev->my_bus);
}
-static inline void fc_sync_dma_exit(void *buf, long size, fc_channel *fc)
+static inline void fc_sync_dma_exit(dma_handle dmh, long size, fc_channel *fc)
{
- mmu_release_scsi_one ((u32)(long)buf, size, fc->dev->my_bus);
+ mmu_release_scsi_one (dmh, size, fc->dev->my_bus);
}
static inline void fc_sync_dma_entry_sg(struct scatterlist *list, int count, fc_channel *fc)
@@ -73,28 +73,28 @@
#define FC_SCMND(SCpnt) ((fc_channel *)(SCpnt->host->hostdata[0]))
#define SC_FCMND(fcmnd) ((Scsi_Cmnd *)((long)fcmnd - (long)&(((Scsi_Cmnd *)0)->SCp)))
-static void fcp_scsi_insert_queue (fc_channel *fc, int no, fcp_cmnd *fcmd)
+static void fcp_scsi_insert_queue (fc_channel *fc, fcp_cmnd *fcmd)
{
- if (!fc->scsi_que[no]) {
- fc->scsi_que[no] = fcmd;
+ if (!fc->scsi_que) {
+ fc->scsi_que = fcmd;
fcmd->next = fcmd;
fcmd->prev = fcmd;
} else {
- fc->scsi_que[no]->prev->next = fcmd;
- fcmd->prev = fc->scsi_que[no]->prev;
- fc->scsi_que[no]->prev = fcmd;
- fcmd->next = fc->scsi_que[no];
+ fc->scsi_que->prev->next = fcmd;
+ fcmd->prev = fc->scsi_que->prev;
+ fc->scsi_que->prev = fcmd;
+ fcmd->next = fc->scsi_que;
}
}
-static void fcp_scsi_remove_queue (fc_channel *fc, int no, fcp_cmnd *fcmd)
+static void fcp_scsi_remove_queue (fc_channel *fc, fcp_cmnd *fcmd)
{
if (fcmd == fcmd->next) {
- fc->scsi_que[no] = NULL;
+ fc->scsi_que = NULL;
return;
}
- if (fcmd == fc->scsi_que[no])
- fc->scsi_que[no] = fcmd->next;
+ if (fcmd == fc->scsi_que)
+ fc->scsi_que = fcmd->next;
fcmd->prev->next = fcmd->next;
fcmd->next->prev = fcmd->prev;
}
@@ -151,7 +151,7 @@
fc->state = FC_STATE_FPORT_OK;
fcmd = l->fcmds + i;
plogi = l->logi + 3 * i;
- fc_sync_dma_exit (plogi, 3 * sizeof(logi), fc);
+ fc_sync_dma_exit (fcmd->cmd, 3 * sizeof(logi), fc);
plogi->code = LS_PLOGI;
memcpy (&plogi->nport_wwn, &fc->wwn_nport, sizeof(fc_wwn));
memcpy (&plogi->node_wwn, &fc->wwn_node, sizeof(fc_wwn));
@@ -159,7 +159,6 @@
memcpy (&plogi->class1, fc->class_svcs, 3*sizeof(svc_parm));
fch = &fcmd->fch;
fcmd->token += l->count;
- fcmd->rsp += sizeof(logi);
FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, fc->did);
FILL_FCHDR_SID(fch, fc->sid);
#ifdef FCDEBUG
@@ -172,7 +171,8 @@
printk ("\n");
}
#endif
- fc_sync_dma_entry (plogi, 3 * sizeof(logi), fc);
+ fcmd->cmd = fc_sync_dma_entry (plogi, 3 * sizeof(logi), fc);
+ fcmd->rsp = fcmd->cmd + 2 * sizeof(logi);
if (fc->hw_enque (fc, fcmd))
printk ("FC: Cannot enque PLOGI packet on %s\n", fc->name);
break;
@@ -194,7 +194,7 @@
switch (status) {
case FC_STATUS_OK:
plogi = l->logi + 3 * i;
- fc_sync_dma_exit (plogi, 3 * sizeof(logi), fc);
+ fc_sync_dma_exit (l->fcmds[i].cmd, 3 * sizeof(logi), fc);
if (!fc->wwn_dest.lo && !fc->wwn_dest.hi) {
memcpy (&fc->wwn_dest, &plogi[1].node_wwn, sizeof(fc_wwn));
FCD(("Dest WWN %08x%08x\n", *(u32 *)&fc->wwn_dest, fc->wwn_dest.lo))
@@ -212,7 +212,7 @@
break;
case FC_STATUS_ERR_OFFLINE:
fc->state = FC_STATE_OFFLINE;
- fc_sync_dma_exit (l->logi + 3 * i, 3 * sizeof(logi), fc);
+ fc_sync_dma_exit (l->fcmds[i].cmd, 3 * sizeof(logi), fc);
printk ("%s: FC is offline\n", fc->name);
if (atomic_dec_and_test (&l->todo))
up(&l->sem);
@@ -228,26 +228,41 @@
void fcp_register(fc_channel *fc, u8 type, int unregister)
{
int size, i;
+ int slots = (fc->can_queue * 3) >> 1;
+
+ FCND(("Going to %sregister\n", unregister ? "un" : ""))
if (type == TYPE_SCSI_FCP) {
if (!unregister) {
fc->scsi_cmd_pool =
- (fcp_cmd *) fc_dma_alloc (fc->can_queue * (sizeof (fcp_cmd) + fc->rsp_size),
+ (fcp_cmd *) fc_dma_alloc (slots * (sizeof (fcp_cmd) + fc->rsp_size),
"FCP SCSI cmd & rsp queues", &fc->dma_scsi_cmd);
- fc->scsi_rsp_pool = (char *)(fc->scsi_cmd_pool + fc->can_queue);
- fc->dma_scsi_rsp = fc->dma_scsi_cmd + fc->can_queue * sizeof (fcp_cmd);
- fc->scsi_bitmap_end = ((fc->can_queue + 63) & ~63);
+ fc->scsi_rsp_pool = (char *)(fc->scsi_cmd_pool + slots);
+ fc->dma_scsi_rsp = fc->dma_scsi_cmd + slots * sizeof (fcp_cmd);
+ fc->scsi_bitmap_end = (slots + 63) & ~63;
size = fc->scsi_bitmap_end / 8;
fc->scsi_bitmap = kmalloc (size, GFP_KERNEL);
memset (fc->scsi_bitmap, 0, size);
+ set_bit (0, fc->scsi_bitmap);
for (i = fc->can_queue; i < fc->scsi_bitmap_end; i++)
set_bit (i, fc->scsi_bitmap);
fc->scsi_free = fc->can_queue;
- fc->token_tab = (fcp_cmnd **)kmalloc(fc->can_queue * sizeof(fcp_cmnd*), GFP_KERNEL);
+ fc->token_tab = (fcp_cmnd **)kmalloc(slots * sizeof(fcp_cmnd*), GFP_KERNEL);
+ fc->abort_count = 0;
} else {
fc->scsi_name[0] = 0;
kfree (fc->scsi_bitmap);
kfree (fc->token_tab);
+ FCND(("Unregistering\n"));
+ if (fc->rst_pkt) {
+ if (fc->rst_pkt->eh_state == SCSI_STATE_UNUSED)
+ kfree(fc->rst_pkt);
+ else {
+ /* Can't happen. Some memory would be lost. */
+ printk("FC: Reset in progress. Now?!");
+ }
+ }
+ FCND(("Unregistered\n"));
}
} else
printk ("FC: %segistering unknown type %02x\n", unregister ? "Unr" : "R", type);
@@ -273,6 +288,7 @@
return;
rsp_status = rsp->fcp_status;
+ FCD(("rsp_status %08x status %08x\n", rsp_status, status))
switch (status) {
case FC_STATUS_OK:
host_status=DID_OK;
@@ -305,11 +321,11 @@
if (SCpnt->use_sg)
fc_sync_dma_exit_sg((struct scatterlist *)SCpnt->buffer, SCpnt->use_sg, fc);
else
- fc_sync_dma_exit(SCpnt->request_buffer, SCpnt->request_bufflen, fc);
+ fc_sync_dma_exit(fcmd->data, SCpnt->request_bufflen, fc);
}
break;
default:
- host_status=DID_ERROR;
+ host_status=DID_ERROR; /* FIXME */
FCD(("Wrong FC status %d for token %d\n", status, token))
break;
}
@@ -319,16 +335,21 @@
}
SCpnt->result = (host_status << 16) | (rsp_status & 0xff);
+#ifdef FCDEBUG
+ if (host_status || SCpnt->result || rsp_status) printk("FC: host_status %d, packet status %d\n",
+ host_status, SCpnt->result);
+#endif
SCpnt->done = fcmd->done;
fcmd->done=NULL;
clear_bit(token, fc->scsi_bitmap);
fc->scsi_free++;
+ FCD(("Calling scsi_done with %08lx\n", SCpnt->result))
SCpnt->scsi_done(SCpnt);
}
void fcp_receive_solicited(fc_channel *fc, int proto, int token, int status, fc_hdr *fch)
{
- FCD(("Receive solicited %d %d %d\n", proto, token, status))
+ FCD(("receive_solicited %d %d %d\n", proto, token, status))
switch (proto) {
case TYPE_SCSI_FCP:
fcp_scsi_receive(fc, token, status, fch); break;
@@ -337,7 +358,7 @@
ls *l = (ls *)fc->ls;
int i = (token >= l->count) ? token - l->count : token;
- /* Make us sure */
+ /* Let's be sure */
if ((unsigned)i < l->count && l->fcmds[i].fc == fc) {
fcp_login_done(fc, token, status);
break;
@@ -417,6 +438,7 @@
fcmd = l->fcmds + i;
fc->login = fcmd;
fc->ls = (void *)l;
+ fc->rst_pkt = NULL; /* kmalloc when first used */
fch = &fcmd->fch;
FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, FS_FABRIC_F_PORT);
FILL_FCHDR_SID(fch, 0);
@@ -448,7 +470,7 @@
} else {
fc->state = FC_STATE_OFFLINE;
enable_irq(fc->irq);
- fc_sync_dma_exit (l->logi + 3 * i, 3 * sizeof(logi), fc);
+ fc_sync_dma_exit (l->fcmds[i].cmd, 3 * sizeof(logi), fc);
if (atomic_dec_and_test (&l->todo))
goto all_done;
}
@@ -472,7 +494,7 @@
switch (fc->state) {
case FC_STATE_ONLINE: break;
case FC_STATE_OFFLINE: break;
- default: fc_sync_dma_exit (l->logi + i, 3 * sizeof(logi), fc);
+ default: fc_sync_dma_exit (l->fcmds[i].cmd, 3 * sizeof(logi), fc);
break;
}
fc->ls = NULL;
@@ -535,24 +557,54 @@
{
fc_channel *fc;
int count=0;
+ int ret;
for (fc = fcchain; fc; fc = fc->next) {
fc->fcp_register = fcp_register;
- fc->fcp_state_change = fcp_state_change;
count++;
}
- fcp_initialize (fcchain, count);
+ ret = fcp_initialize (fcchain, count);
+
+ if (!ret) {
+ if (!fc_channels)
+ fc_channels = fcchain;
+ else {
+ for (fc = fc_channels; fc->next; fc = fc->next);
+ fc->next = fcchain;
+ }
+ }
+ return ret;
+}
+
+void fcp_release(fc_channel *fcchain, int count) /* count must > 0 */
+{
+ fc_channel *fc;
+ fc_channel *fcx;
+
+ for (fc = fcchain; --count && fc->next; fc = fc->next);
+ if (count) {
+ printk("FC: nothing to release\n");
+ return;
+ }
- if (!fc_channels)
- fc_channels = fcchain;
+ if (fc_channels == fcchain)
+ fc_channels = fc->next;
else {
- for (fc = fc_channels; fc->next; fc = fc->next);
- fc->next = fcchain;
+ for (fcx = fc_channels; fcx->next != fcchain; fcx = fcx->next);
+ fcx->next = fc->next;
}
- return 0;
+ fc->next = NULL;
+
+ /*
+ * We've just grabbed fcchain out of the fc_channel list
+ * and zero-terminated it, while destroying the count.
+ *
+ * Freeing the fc's is the low level driver's responsibility.
+ */
}
+
static void fcp_scsi_done (Scsi_Cmnd *SCpnt)
{
if (FCP_CMND(SCpnt)->done)
@@ -561,16 +613,15 @@
static int fcp_scsi_queue_it(fc_channel *fc, Scsi_Cmnd *SCpnt, fcp_cmnd *fcmd, int prepare)
{
+ long i;
+ fcp_cmd *cmd;
+ u32 fcp_cntl;
if (prepare) {
- long i;
- fcp_cmd *cmd;
- u32 fcp_cntl;
-
i = find_first_zero_bit (fc->scsi_bitmap, fc->scsi_bitmap_end);
set_bit (i, fc->scsi_bitmap);
fcmd->token = i;
cmd = fc->scsi_cmd_pool + i;
- FCD(("Chose token %ld %08lx\n", i, (long)cmd))
+
if (fc->encode_addr (SCpnt, cmd->fcp_addr)) {
/* Invalid channel/id/lun and couldn't map it into fcp_addr */
clear_bit (i, fc->scsi_bitmap);
@@ -620,14 +671,14 @@
FCD(("XXX: %04x.%04x.%04x.%04x - %08x%08x%08x\n", cmd->fcp_addr[0], cmd->fcp_addr[1], cmd->fcp_addr[2], cmd->fcp_addr[3], *(u32 *)SCpnt->cmnd, *(u32 *)(SCpnt->cmnd+4), *(u32 *)(SCpnt->cmnd+8)))
}
FCD(("Trying to enque %08x\n", (int)fcmd))
- if (!fc->scsi_que[1]) {
+ if (!fc->scsi_que) {
if (!fc->hw_enque (fc, fcmd)) {
FCD(("hw_enque succeeded for %08x\n", (int)fcmd))
return 0;
}
}
FCD(("Putting into que1 %08x\n", (int)fcmd))
- fcp_scsi_insert_queue (fc, 1, fcmd);
+ fcp_scsi_insert_queue (fc, fcmd);
return 0;
}
@@ -643,8 +694,10 @@
SCpnt->scsi_done = done;
fcmd->proto = TYPE_SCSI_FCP;
if (!fc->scsi_free) {
- FCD(("Putting into que0\n"))
- fcp_scsi_insert_queue (fc, 0, fcmd);
+ FCD(("FC: !scsi_free, putting cmd on ML queue\n"))
+#if (FCP_SCSI_USE_NEW_EH_CODE == 0)
+ printk("fcp_scsi_queue_command: queue full, losing cmd, bad\n");
+#endif
return 1;
}
return fcp_scsi_queue_it(fc, SCpnt, fcmd, 1);
@@ -656,24 +709,166 @@
{
fcp_cmnd *fcmd;
FCD(("Queue empty\n"))
- while ((fcmd = fc->scsi_que[1])) {
- /* The hw tell us we can try again queue some packet */
+ while ((fcmd = fc->scsi_que)) {
+ /* The hw told us we can try again queue some packet */
if (fc->hw_enque (fc, fcmd))
return;
- fcp_scsi_remove_queue (fc, 1, fcmd);
+ fcp_scsi_remove_queue (fc, fcmd);
}
}
+int fcp_old_abort(Scsi_Cmnd *SCpnt)
+{
+ printk("FC: Abort not implemented\n");
+ return 1;
+}
+
int fcp_scsi_abort(Scsi_Cmnd *SCpnt)
{
- printk ("FC: AIEEE, abort!\n");
- return 0;
+ /* Internal bookkeeping only. Lose 1 token_tab slot. */
+ fcp_cmnd *fcmd = FCP_CMND(SCpnt);
+ fc_channel *fc = FC_SCMND(SCpnt);
+
+ /*
+ * We react to abort requests by simply forgetting
+ * about the command and pretending everything's sweet.
+ * This may or may not be silly. We can't, however,
+ * immediately reuse the command's token_tab slot,
+ * as its result may arrive later and we cannot
+ * check whether it is the aborted one, can't we?
+ *
+ * Therefore, after the first few aborts are done,
+ * we tell the scsi error handler to do something clever.
+ * It will eventually call host reset, refreshing
+ * token_tab for us.
+ *
+ * There is a theoretical chance that we sometimes allow
+ * more than can_queue packets to the jungle this way,
+ * but the worst outcome possible is a series of
+ * more aborts and eventually the dev_reset catharsis.
+ */
+
+ if (++fc->abort_count < (fc->can_queue >> 1)) {
+ SCpnt->result = DID_ABORT;
+ fcmd->done(SCpnt);
+ printk("FC: soft abort\n");
+ return SUCCESS;
+ } else {
+ printk("FC: hard abort refused\n");
+ return FAILED;
+ }
}
-int fcp_scsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+void fcp_scsi_reset_done(Scsi_Cmnd *SCpnt)
{
- printk ("FC: AIEEE, reset!\n");
- return 0;
+ fc_channel *fc = FC_SCMND(SCpnt);
+
+ fc->rst_pkt->eh_state = SCSI_STATE_FINISHED;
+ up(fc->rst_pkt->host->eh_action);
+}
+
+#define FCP_RESET_TIMEOUT (2*HZ)
+
+int fcp_scsi_dev_reset(Scsi_Cmnd *SCpnt)
+{
+ fcp_cmd *cmd;
+ fcp_cmnd *fcmd;
+ fc_channel *fc = FC_SCMND(SCpnt);
+ struct semaphore sem = MUTEX_LOCKED;
+
+ if (!fc->rst_pkt) {
+ fc->rst_pkt = (Scsi_Cmnd *) kmalloc(sizeof(SCpnt), GFP_KERNEL);
+ if (!fc->rst_pkt) return FAILED;
+
+ fcmd = FCP_CMND(fc->rst_pkt);
+
+
+ fcmd->token = 0;
+ cmd = fc->scsi_cmd_pool + 0;
+ FCD(("Preparing rst packet\n"))
+ if (fc->encode_addr (SCpnt, /*?*/cmd->fcp_addr))
+ fc->rst_pkt->channel = SCpnt->channel;
+ fc->rst_pkt->target = SCpnt->target;
+ fc->rst_pkt->lun = 0;
+ fc->rst_pkt->cmd_len = 0;
+
+ fc->token_tab[0] = fcmd;
+
+ cmd->fcp_cntl = FCP_CNTL_QTYPE_ORDERED | FCP_CNTL_RESET;
+ fcmd->data = (dma_handle)NULL;
+ fcmd->proto = TYPE_SCSI_FCP;
+
+ memcpy (cmd->fcp_cdb, SCpnt->cmnd, SCpnt->cmd_len);
+ memset (cmd->fcp_cdb+SCpnt->cmd_len, 0, sizeof(cmd->fcp_cdb)-SCpnt->cmd_len);
+ FCD(("XXX: %04x.%04x.%04x.%04x - %08x%08x%08x\n", cmd->fcp_addr[0], cmd->fcp_addr[1], cmd->fcp_addr[2], cmd->fcp_addr[3], *(u32 *)SCpnt->cmnd, *(u32 *)(SCpnt->cmnd+4), *(u32 *)(SCpnt->cmnd+8)))
+ } else {
+ fcmd = FCP_CMND(fc->rst_pkt);
+ if (fc->rst_pkt->eh_state == SCSI_STATE_QUEUED)
+ return FAILED; /* or SUCCESS. Only these */
+ }
+ fc->rst_pkt->done = NULL;
+
+
+ fc->rst_pkt->eh_state = SCSI_STATE_QUEUED;
+
+ fc->rst_pkt->eh_timeout.data = (unsigned long) fc->rst_pkt;
+ fc->rst_pkt->eh_timeout.expires = jiffies + FCP_RESET_TIMEOUT;
+ fc->rst_pkt->eh_timeout.function = (void (*)(unsigned long))fcp_scsi_reset_done;
+
+ add_timer(&fc->rst_pkt->eh_timeout);
+
+ /*
+ * Set up the semaphore so we wait for the command to complete.
+ */
+
+ fc->rst_pkt->host->eh_action = &sem;
+ fc->rst_pkt->request.rq_status = RQ_SCSI_BUSY;
+
+ fc->rst_pkt->done = fcp_scsi_reset_done;
+ fcp_scsi_queue_it(fc, fc->rst_pkt, fcmd, 0);
+
+ down(&sem);
+
+ fc->rst_pkt->host->eh_action = NULL;
+ del_timer(&fc->rst_pkt->eh_timeout);
+
+ /*
+ * See if timeout. If so, tell the host to forget about it.
+ * In other words, we don't want a callback any more.
+ */
+ if (fc->rst_pkt->eh_state == SCSI_STATE_TIMEOUT ) {
+ fc->rst_pkt->eh_state = SCSI_STATE_UNUSED;
+ return FAILED;
+ }
+ fc->rst_pkt->eh_state = SCSI_STATE_UNUSED;
+ return SUCCESS;
+}
+
+int fcp_scsi_bus_reset(Scsi_Cmnd *SCpnt)
+{
+ printk ("FC: bus reset!\n");
+ return FAILED;
+}
+
+int fcp_scsi_host_reset(Scsi_Cmnd *SCpnt)
+{
+ fc_channel *fc = FC_SCMND(SCpnt);
+ fcp_cmnd *fcmd = FCP_CMND(SCpnt);
+ int i;
+
+ printk ("FC: host reset\n");
+
+ for (i=0; i < fc->can_queue; i++) {
+ if (fc->token_tab[i] && SCpnt->result != DID_ABORT) {
+ SCpnt->result = DID_RESET;
+ fcmd->done(SCpnt);
+ fc->token_tab[i] = NULL;
+ }
+ }
+ fc->reset(fc);
+ fc->abort_count = 0;
+ if (fcp_initialize(fc, 1)) return SUCCESS;
+ else return FAILED;
}
#ifdef MODULE
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov