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

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