Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 3654

kernel-2.6.18-194.11.1.el5.src.rpm

From: David Milburn <dmilburn@redhat.com>
Date: Mon, 30 Nov 2009 20:02:40 -0500
Subject: [scsi] stex: update driver for RHEL-5.5
Message-id: <20091130200240.GA4229@dhcp-210.hsv.redhat.com>
Patchwork-id: 21551
O-Subject: [RHEL5.5 PATCH] stex: update driver reset code
Bugzilla: 516881

This stex driver update patch is from Ed Lin
at Promise, it is based upon this upstream commit

commit 69cb48750b02034350bc78d8053647d7464cdde0
Author: Ed Lin <ed.lin@promise.com>
Date:   Tue Aug 18 12:15:14 2009 -0700

    [SCSI] stex: Add reset code for st_yel (v2)

And these commits to scsi-misc-2.6:

commit cce9c8aed7d3ac0a14815e99b4602ae6c854a0ba
Author: Ed Lin <ed.lin@promise.com>
Date:   Mon Sep 28 22:58:36 2009 -0800

    [SCSI] stex: update version to 4.6.0000.4

commit 9eb46d2a08de537e14e92216bf18e7cb541d2f67
Author: Ed Lin <ed.lin@promise.com>
Date:   Mon Sep 28 22:58:33 2009 -0800

    [SCSI] stex: add support for reset request from firmware

commit cbacfb5fd9a4689b55157753b8ba4455415fb85c
Author: Ed Lin <ed.lin@promise.com>
Date:   Mon Sep 28 22:58:17 2009 -0800

    [SCSI] stex: add small dma buffer support

Promise has verified a 2.6.18-169.el5 test kernel built
with this patch, and I have stress tested a SuperTrak
EX8350/EX16300 with no problems encountered.

This resolves BZ 516881, please review and ACK.

Thanks,
David

 drivers/scsi/stex.c |  272 ++++++++++++++++++++++++++++++++++++---------------
 1 files changed, 191 insertions(+), 81 deletions(-)

Signed-off-by: Don Zickus <dzickus@redhat.com>

diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c
index 685a84c..46105d0 100644
--- a/drivers/scsi/stex.c
+++ b/drivers/scsi/stex.c
@@ -35,11 +35,11 @@
 #include <scsi/scsi_dbg.h>
 
 #define DRV_NAME "stex"
-#define ST_DRIVER_VERSION "4.6.0102.3"
+#define ST_DRIVER_VERSION "4.6.0102.4"
 #define ST_VER_MAJOR		4
 #define ST_VER_MINOR		6
 #define ST_OEM			102
-#define ST_BUILD_VER		3
+#define ST_BUILD_VER		4
 
 enum {
 	/* MU register offset */
@@ -54,6 +54,7 @@ enum {
 	OIS	= 0x30,	/* MU_OUTBOUND_INTERRUPT_STATUS */
 	OIM	= 0x3c,	/* MU_OUTBOUND_INTERRUPT_MASK */
 
+	YIOA_STATUS				= 0x00,
 	YH2I_INT				= 0x20,
 	YINT_EN					= 0x34,
 	YI2H_INT				= 0x9c,
@@ -62,24 +63,24 @@ enum {
 	YH2I_REQ_HI				= 0xc4,
 
 	/* MU register value */
-	MU_INBOUND_DOORBELL_HANDSHAKE		= 1,
-	MU_INBOUND_DOORBELL_REQHEADCHANGED	= 2,
-	MU_INBOUND_DOORBELL_STATUSTAILCHANGED	= 4,
-	MU_INBOUND_DOORBELL_HMUSTOPPED		= 8,
-	MU_INBOUND_DOORBELL_RESET		= 16,
-
-	MU_OUTBOUND_DOORBELL_HANDSHAKE		= 1,
-	MU_OUTBOUND_DOORBELL_REQUESTTAILCHANGED	= 2,
-	MU_OUTBOUND_DOORBELL_STATUSHEADCHANGED	= 4,
-	MU_OUTBOUND_DOORBELL_BUSCHANGE		= 8,
-	MU_OUTBOUND_DOORBELL_HASEVENT		= 16,
+	MU_INBOUND_DOORBELL_HANDSHAKE		= (1 << 0),
+	MU_INBOUND_DOORBELL_REQHEADCHANGED	= (1 << 1),
+	MU_INBOUND_DOORBELL_STATUSTAILCHANGED	= (1 << 2),
+	MU_INBOUND_DOORBELL_HMUSTOPPED		= (1 << 3),
+	MU_INBOUND_DOORBELL_RESET		= (1 << 4),
+
+	MU_OUTBOUND_DOORBELL_HANDSHAKE		= (1 << 0),
+	MU_OUTBOUND_DOORBELL_REQUESTTAILCHANGED	= (1 << 1),
+	MU_OUTBOUND_DOORBELL_STATUSHEADCHANGED	= (1 << 2),
+	MU_OUTBOUND_DOORBELL_BUSCHANGE		= (1 << 3),
+	MU_OUTBOUND_DOORBELL_HASEVENT		= (1 << 4),
+	MU_OUTBOUND_DOORBELL_REQUEST_RESET	= (1 << 27),
 
 	/* MU status code */
 	MU_STATE_STARTING			= 1,
-	MU_STATE_FMU_READY_FOR_HANDSHAKE	= 2,
-	MU_STATE_SEND_HANDSHAKE_FRAME		= 3,
-	MU_STATE_STARTED			= 4,
-	MU_STATE_RESETTING			= 5,
+	MU_STATE_STARTED			= 2,
+	MU_STATE_RESETTING			= 3,
+	MU_STATE_FAILED				= 4,
 
 	MU_MAX_DELAY				= 120,
 	MU_HANDSHAKE_SIGNATURE			= 0x55aaaa55,
@@ -107,6 +108,12 @@ enum {
 
 	SS_HEAD_HANDSHAKE			= 0x80,
 
+	SS_H2I_INT_RESET			= 0x100,
+
+	SS_I2H_REQUEST_RESET			= 0x2000,
+
+	SS_MU_OPERATIONAL			= 0x80000000,
+
 	STEX_CDB_LENGTH				= 16,
 	STATUS_VAR_LEN				= 128,
 
@@ -307,6 +314,10 @@ struct st_hba {
 	struct st_ccb *wait_ccb;
 	__le32 *scratch;
 
+	char work_q_name[20];
+	struct workqueue_struct *work_q;
+	struct work_struct reset_work;
+	wait_queue_head_t reset_waitq;
 	unsigned int mu_status;
 	unsigned int cardtype;
 	int msi_enabled;
@@ -672,6 +683,9 @@ stex_queuecommand(struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *))
 	lun = cmd->device->lun;
 	hba = (struct st_hba *) &host->hostdata[0];
 
+	if (unlikely(hba->mu_status == MU_STATE_RESETTING))
+		return SCSI_MLQUEUE_HOST_BUSY;
+
 	switch (cmd->cmnd[0]) {
 	case MODE_SENSE_10:
 	{
@@ -950,7 +964,6 @@ static irqreturn_t stex_intr(int irq, void *__hba, struct pt_regs *regs)
 	void __iomem *base = hba->mmio_base;
 	u32 data;
 	unsigned long flags;
-	int handled = 0;
 
 	spin_lock_irqsave(hba->host->host_lock, flags);
 
@@ -961,12 +974,16 @@ static irqreturn_t stex_intr(int irq, void *__hba, struct pt_regs *regs)
 		writel(data, base + ODBL);
 		readl(base + ODBL); /* flush */
 		stex_mu_intr(hba, data);
-		handled = 1;
+		spin_unlock_irqrestore(hba->host->host_lock, flags);
+		if (unlikely(data & MU_OUTBOUND_DOORBELL_REQUEST_RESET &&
+			hba->cardtype == st_shasta))
+			queue_work(hba->work_q, &hba->reset_work);
+		return IRQ_HANDLED;
 	}
 
 	spin_unlock_irqrestore(hba->host->host_lock, flags);
 
-	return IRQ_RETVAL(handled);
+	return IRQ_NONE;
 }
 
 static void stex_ss_mu_intr(struct st_hba *hba)
@@ -1050,7 +1067,6 @@ static irqreturn_t stex_ss_intr(int irq, void *__hba, struct pt_regs *regs)
 	void __iomem *base = hba->mmio_base;
 	u32 data;
 	unsigned long flags;
-	int handled = 0;
 
 	spin_lock_irqsave(hba->host->host_lock, flags);
 
@@ -1059,12 +1075,15 @@ static irqreturn_t stex_ss_intr(int irq, void *__hba, struct pt_regs *regs)
 		/* clear the interrupt */
 		writel(data, base + YI2H_INT_C);
 		stex_ss_mu_intr(hba);
-		handled = 1;
+		spin_unlock_irqrestore(hba->host->host_lock, flags);
+		if (unlikely(data & SS_I2H_REQUEST_RESET))
+			queue_work(hba->work_q, &hba->reset_work);
+		return IRQ_HANDLED;
 	}
 
 	spin_unlock_irqrestore(hba->host->host_lock, flags);
 
-	return IRQ_RETVAL(handled);
+	return IRQ_NONE;
 }
 
 static int stex_common_handshake(struct st_hba *hba)
@@ -1112,7 +1131,7 @@ static int stex_common_handshake(struct st_hba *hba)
 	h->partner_type = HMU_PARTNER_TYPE;
 	if (hba->extra_offset) {
 		h->extra_offset = cpu_to_le32(hba->extra_offset);
-		h->extra_size = cpu_to_le32(ST_ADDITIONAL_MEM);
+		h->extra_size = cpu_to_le32(hba->dma_size - hba->extra_offset);
 	} else
 		h->extra_offset = h->extra_size = 0;
 
@@ -1157,15 +1176,26 @@ static int stex_ss_handshake(struct st_hba *hba)
 	struct st_msg_header *msg_h;
 	struct handshake_frame *h;
 	__le32 *scratch;
-	u32 data;
+	u32 data, scratch_size;
 	unsigned long before;
 	int ret = 0;
 
-	h = (struct handshake_frame *)(hba->alloc_rq(hba));
-	msg_h = (struct st_msg_header *)h - 1;
+	before = jiffies;
+	while ((readl(base + YIOA_STATUS) & SS_MU_OPERATIONAL) == 0) {
+		if (time_after(jiffies, before + MU_MAX_DELAY * HZ)) {
+			printk(KERN_ERR DRV_NAME
+				"(%s): firmware not operational\n",
+				pci_name(hba->pdev));
+			return -1;
+		}
+		msleep(1);
+	}
+
+	msg_h = (struct st_msg_header *)hba->dma_mem;
 	msg_h->handle = cpu_to_le64(hba->dma_handle);
 	msg_h->flag = SS_HEAD_HANDSHAKE;
 
+	h = (struct handshake_frame *)(msg_h + 1);
 	h->rb_phy = cpu_to_le64(hba->dma_handle);
 	h->req_sz = cpu_to_le16(hba->rq_size);
 	h->req_cnt = cpu_to_le16(hba->rq_count+1);
@@ -1174,13 +1204,16 @@ static int stex_ss_handshake(struct st_hba *hba)
 	stex_gettime(&h->hosttime);
 	h->partner_type = HMU_PARTNER_TYPE;
 	h->extra_offset = h->extra_size = 0;
-	h->scratch_size = cpu_to_le32((hba->sts_count+1)*sizeof(u32));
+	scratch_size = (hba->sts_count+1)*sizeof(u32);
+	h->scratch_size = cpu_to_le32(scratch_size);
 
 	data = readl(base + YINT_EN);
 	data &= ~4;
 	writel(data, base + YINT_EN);
 	writel((hba->dma_handle >> 16) >> 16, base + YH2I_REQ_HI);
+	readl(base + YH2I_REQ_HI);
 	writel(hba->dma_handle, base + YH2I_REQ);
+	readl(base + YH2I_REQ); /* flush */
 
 	scratch = hba->scratch;
 	before = jiffies;
@@ -1196,7 +1229,7 @@ static int stex_ss_handshake(struct st_hba *hba)
 		msleep(1);
 	}
 
-	*scratch = 0;
+	memset(scratch, 0, scratch_size);
 	msg_h->flag = 0;
 	return ret;
 }
@@ -1205,13 +1238,15 @@ static int stex_handshake(struct st_hba *hba)
 {
 	int err, size;
 	unsigned long flags;
+	unsigned int mu_status;
 
 	err = (hba->cardtype == st_yel) ?
 		stex_ss_handshake(hba) : stex_common_handshake(hba);
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	mu_status = hba->mu_status;
 	if (err == 0) {
 		size = ALIGN(hba->host->can_queue, BITS_PER_LONG)/BITS_PER_LONG;
 		size *= sizeof(unsigned long);
-		spin_lock_irqsave(hba->host->host_lock, flags);
 		hba->req_head = 0;
 		hba->req_tail = 0;
 		hba->status_head = 0;
@@ -1219,8 +1254,11 @@ static int stex_handshake(struct st_hba *hba)
 		hba->out_req_cnt = 0;
 		memset(hba->tag, 0, size);
 		hba->mu_status = MU_STATE_STARTED;
-		spin_unlock_irqrestore(hba->host->host_lock, flags);
-	}
+	} else
+		hba->mu_status = MU_STATE_FAILED;
+	if (mu_status == MU_STATE_RESETTING)
+		wake_up_all(&hba->reset_waitq);
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
 	return err;
 }
 
@@ -1241,7 +1279,7 @@ static int stex_abort(struct scsi_cmnd *cmd)
 	base = hba->mmio_base;
 	spin_lock_irqsave(host->host_lock, flags);
 	for (tag = 0; tag < host->can_queue; tag++)
-		if (hba->ccb[tag].cmd == cmd) {
+		if (hba->ccb[tag].req && hba->ccb[tag].cmd == cmd) {
 			hba->wait_ccb = &hba->ccb[tag];
 			break;
 		}
@@ -1320,68 +1358,124 @@ static void stex_hard_reset(struct st_hba *hba)
 			hba->pdev->saved_config_space[i]);
 }
 
-static int stex_reset(struct scsi_cmnd *cmd)
+static int stex_yos_reset(struct st_hba *hba)
 {
-	struct st_hba *hba;
 	void __iomem *base;
 	unsigned long flags, before;
-	int size;
-
-	hba = (struct st_hba *) &cmd->device->host->hostdata[0];
-
-	printk(KERN_INFO DRV_NAME
-		"(%s): resetting host\n", pci_name(hba->pdev));
-	scsi_print_command(cmd);
-
-	hba->mu_status = MU_STATE_RESETTING;
-
-	if (hba->cardtype == st_shasta)
-		stex_hard_reset(hba);
-
-	if (hba->cardtype != st_yosemite) {
-		if (stex_handshake(hba)) {
-			printk(KERN_WARNING DRV_NAME
-				"(%s): resetting: handshake failed\n",
-				pci_name(hba->pdev));
-			return FAILED;
-		}
-		return SUCCESS;
-	}
+	int ret = 0;
 
-	/* st_yosemite */
-	writel(MU_INBOUND_DOORBELL_RESET, hba->mmio_base + IDBL);
-	readl(hba->mmio_base + IDBL); /* flush */
+	base = hba->mmio_base;
+	writel(MU_INBOUND_DOORBELL_RESET, base + IDBL);
+	readl(base + IDBL); /* flush */
 	before = jiffies;
 	while (hba->out_req_cnt > 0) {
 		if (time_after(jiffies, before + ST_INTERNAL_TIMEOUT * HZ)) {
 			printk(KERN_WARNING DRV_NAME
 				"(%s): reset timeout\n", pci_name(hba->pdev));
-			return FAILED;
+			ret = -1;
+			break;
 		}
 		msleep(1);
 	}
 
-	base = hba->mmio_base;
-	writel(0, base + IMR0);
-	readl(base + IMR0);
-	writel(0, base + OMR0);
-	readl(base + OMR0);
-	writel(0, base + IMR1);
-	readl(base + IMR1);
-	writel(0, base + OMR1);
-	readl(base + OMR1); /* flush */
-	size = ALIGN(hba->host->can_queue, BITS_PER_LONG)/BITS_PER_LONG;
-	size *= sizeof(unsigned long);
 	spin_lock_irqsave(hba->host->host_lock, flags);
-	hba->req_head = 0;
-	hba->req_tail = 0;
-	hba->status_head = 0;
-	hba->status_tail = 0;
-	hba->out_req_cnt = 0;
-	memset(hba->tag, 0, size);
-	hba->mu_status = MU_STATE_STARTED;
+	if (ret == -1)
+		hba->mu_status = MU_STATE_FAILED;
+	else
+		hba->mu_status = MU_STATE_STARTED;
+	wake_up_all(&hba->reset_waitq);
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+	return ret;
+}
+
+static void stex_ss_reset(struct st_hba *hba)
+{
+	writel(SS_H2I_INT_RESET, hba->mmio_base + YH2I_INT);
+	readl(hba->mmio_base + YH2I_INT);
+	ssleep(5);
+}
+
+static int stex_do_reset(struct st_hba *hba)
+{
+	struct st_ccb *ccb;
+	unsigned long flags;
+	unsigned int mu_status = MU_STATE_RESETTING;
+	u16 tag;
+
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	if (hba->mu_status == MU_STATE_STARTING) {
+		spin_unlock_irqrestore(hba->host->host_lock, flags);
+		printk(KERN_INFO DRV_NAME "(%s): request reset during init\n",
+			pci_name(hba->pdev));
+		return 0;
+	}
+	while (hba->mu_status == MU_STATE_RESETTING) {
+		spin_unlock_irqrestore(hba->host->host_lock, flags);
+		wait_event_timeout(hba->reset_waitq,
+				   hba->mu_status != MU_STATE_RESETTING,
+				   MU_MAX_DELAY * HZ);
+		spin_lock_irqsave(hba->host->host_lock, flags);
+		mu_status = hba->mu_status;
+	}
+
+	if (mu_status != MU_STATE_RESETTING) {
+		spin_unlock_irqrestore(hba->host->host_lock, flags);
+		return (mu_status == MU_STATE_STARTED) ? 0 : -1;
+	}
+
+	hba->mu_status = MU_STATE_RESETTING;
 	spin_unlock_irqrestore(hba->host->host_lock, flags);
-	return SUCCESS;
+
+	if (hba->cardtype == st_yosemite)
+		return stex_yos_reset(hba);
+
+	if (hba->cardtype == st_shasta)
+		stex_hard_reset(hba);
+	else if (hba->cardtype == st_yel)
+		stex_ss_reset(hba);
+
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	for (tag = 0; tag < hba->host->can_queue; tag++) {
+		ccb = &hba->ccb[tag];
+		if (ccb->req == NULL)
+			continue;
+		ccb->req = NULL;
+		if (ccb->cmd) {
+			stex_unmap_sg(hba, ccb->cmd);
+			ccb->cmd->result = DID_RESET << 16;
+			ccb->cmd->scsi_done(ccb->cmd);
+			ccb->cmd = NULL;
+		}
+	}
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+	if (stex_handshake(hba) == 0)
+		return 0;
+
+	printk(KERN_WARNING DRV_NAME "(%s): resetting: handshake failed\n",
+		pci_name(hba->pdev));
+	return -1;
+}
+
+static int stex_reset(struct scsi_cmnd *cmd)
+{
+	struct st_hba *hba;
+
+	hba = (struct st_hba *) &cmd->device->host->hostdata[0];
+
+	printk(KERN_INFO DRV_NAME
+		"(%s): resetting host\n", pci_name(hba->pdev));
+	scsi_print_command(cmd);
+
+	return stex_do_reset(hba) ? FAILED : SUCCESS;
+}
+
+static void stex_reset_work(void *data)
+{
+	struct st_hba *hba = data;
+
+	stex_do_reset(hba);
 }
 
 static int stex_biosparam(struct scsi_device *sdev,
@@ -1680,12 +1774,24 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
 	hba->host = host;
 	hba->pdev = pdev;
+	init_waitqueue_head(&hba->reset_waitq);
+
+	snprintf(hba->work_q_name, sizeof(hba->work_q_name),
+		 "stex_wq_%d", host->host_no);
+	hba->work_q = create_singlethread_workqueue(hba->work_q_name);
+	if (!hba->work_q) {
+		printk(KERN_ERR DRV_NAME "(%s): create workqueue failed\n",
+			pci_name(pdev));
+		err = -ENOMEM;
+		goto out_ccb_free;
+	}
+	INIT_WORK(&hba->reset_work, stex_reset_work, hba);
 
 	err = stex_request_irq(hba);
 	if (err) {
 		printk(KERN_ERR DRV_NAME "(%s): request irq failed\n",
 			pci_name(pdev));
-		goto out_ccb_free;
+		goto out_free_wq;
 	}
 
 	err = stex_handshake(hba);
@@ -1707,6 +1813,8 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
 out_free_irq:
 	stex_free_irq(hba);
+out_free_wq:
+	destroy_workqueue(hba->work_q);
 out_ccb_free:
 	kfree(hba->ccb);
 out_pci_free:
@@ -1785,6 +1893,8 @@ static void stex_hba_free(struct st_hba *hba)
 {
 	stex_free_irq(hba);
 
+	destroy_workqueue(hba->work_q);
+
 	iounmap(hba->mmio_base);
 
 	pci_release_regions(hba->pdev);