Skip to contentSkip to conversation

Add Promise SuperTrak EX 'stex' driver

PATCH In review Jeff Garzik 2 participants 6 messages Aug 8, 2006 – Aug 9, 2006 SCSI

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Add Promise SuperTrak EX 'stex' driver

Adds the 'stex' driver for Promise SuperTrak EX storage controllers. These controllers present themselves as SCSI, though like 3ware, megaraid and others, the underlying storage may or may not be SCSI.

As discussed, the block tagging stuff is a post-merge todo item.

drivers/scsi/Kconfig +7 -0
index 96a81cd..82f6780 100644
@@ -1053,6 +1053,13 @@ config 53C700_LE_ON_BE
 	depends on SCSI_LASI700
 	default y
 
+config SCSI_STEX
+	tristate "Promise SuperTrak EX Series support"
+	depends on PCI && SCSI
+	---help---
+	  This driver supports Promise SuperTrak EX8350/8300/16350/16300
+	  Storage controllers.
+
 config SCSI_SYM53C8XX_2
 	tristate "SYM53C8XX Version 2 SCSI support"
 	depends on PCI && SCSI
drivers/scsi/Makefile +1 -0
index ebd0cf0..5ea6d8c 100644
@@ -122,6 +122,7 @@ obj-$(CONFIG_SCSI_LASI700)	+= 53c700.o l
 obj-$(CONFIG_SCSI_NSP32)	+= nsp32.o
 obj-$(CONFIG_SCSI_IPR)		+= ipr.o
 obj-$(CONFIG_SCSI_IBMVSCSI)	+= ibmvscsi/
+obj-$(CONFIG_SCSI_STEX)		+= stex.o
 obj-$(CONFIG_SCSI_SATA_AHCI)	+= libata.o ahci.o
 obj-$(CONFIG_SCSI_SATA_SVW)	+= libata.o sata_svw.o
 obj-$(CONFIG_SCSI_ATA_PIIX)	+= libata.o ata_piix.o
drivers/scsi/stex.c new file +1267 -0

1,267 lines not on this page · raw message

#1 lore raw
Quoted 5 lines · Jeff Garzik

Adds the 'stex' driver for Promise SuperTrak EX storage controllers. These controllers present themselves as SCSI, though like 3ware, megaraid and others, the underlying storage may or may not be SCSI.

As discussed, the block tagging stuff is a post-merge todo item.

That's not exactly my recollection of the discussion: I thought we were still discussing the chicken and egg issue (which is we have APIs to do this per host tagging which stex duplicates on the grounds no-one's using the current APIs). Jens and I seem to be in agreement that stex should try the API's and well make any changes that become necessary to block or SCSI happen.

drivers/scsi/stex.c line 528

Quoted 10 lines
drivers/scsi/stex.c new file +10 -0
+	switch (cmd->cmnd[0]) {
+	case MODE_SENSE_10:
+	{
+		static char mode_sense10[8] = { 0, 6, 0, 0, 0, 0, 0, 0 };
+
+		stex_direct_copy(cmd, mode_sense10, sizeof(mode_sense10));
+		cmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
+		done(cmd);
+		return 0;
+	}

This looks like it will trick the sd driver into reading uninitialised data for the drive caching parameters ... there are obviously faults on both sides, but I think when you ask for a mode page and you get a success return, you're entitled to think you got it ...

drivers/scsi/stex.c line 538

Quoted 10 lines
drivers/scsi/stex.c new file +10 -0
+	case INQUIRY:
+		if (id != ST_MAX_ARRAY_SUPPORTED)
+			break;
+		if (lun == 0 && (cmd->cmnd[1] & INQUIRY_EVPD) == 0) {
+			stex_direct_copy(cmd, console_inq_page,
+				sizeof(console_inq_page));
+			cmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
+		} else
+			cmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8;
+		done(cmd);

The error return isn't correct you should never use DID_ERROR for an uncorrectable error because it will cause a retry (which you'll fail again). For an unsupported inquiry the correct return should be Check Condition/Illegal Request/Invalid Field in CDB

drivers/scsi/stex.c line 538

Quoted 10 lines
drivers/scsi/stex.c new file +10 -0
+	case INQUIRY:
+		if (id != ST_MAX_ARRAY_SUPPORTED)
+			break;
+		if (lun == 0 && (cmd->cmnd[1] & INQUIRY_EVPD) == 0) {
+			stex_direct_copy(cmd, console_inq_page,
+				sizeof(console_inq_page));
+			cmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
+		} else
+			cmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8;
+		done(cmd);

The error return here looks like it shouldn't be DID_ERROR either. I assume the error is a format one and uncorrectable by a retry?

drivers/scsi/stex.c line 1103

Quoted 10 lines
drivers/scsi/stex.c new file +10 -0
+	}
+
+	hba->dma_mem = pci_alloc_consistent(pdev,
+		STEX_BUFFER_SIZE, &hba->dma_handle);
+	if (!hba->dma_mem) {
+		err = -ENOMEM;
+		printk(KERN_ERR DRV_NAME "(%s): dma mem alloc failed\n",
+			pci_name(pdev));
+		goto out_iounmap;
+	}

This should be dma_alloc_coherent, not pci_alloc_consistent.

James

#2 lore raw

Some more I missed in the first glance through:

drivers/scsi/stex.c line 1125

Quoted 1 line
drivers/scsi/stex.c new file +1 -0
+	err = request_irq(pdev->irq, stex_intr, SA_SHIRQ, DRV_NAME, hba);

Needs to be IRQF_SHARED now.

Quoted 9 lines
drivers/scsi/stex.c new file +9 -0
+static int stex_reset(struct scsi_cmnd *cmd)
+{
+	struct st_hba *hba;
+	int tag;
+	int i = 0;
+	unsigned long flags;
+	hba = (struct st_hba *) &cmd->device->host->hostdata[0];
+
+wait_cmds:

drivers/scsi/stex.c line 937

Quoted 10 lines
drivers/scsi/stex.c new file +10 -0
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	for (tag = 0; tag < MU_MAX_REQUEST; tag++)
+		if ((hba->tag & (1 << tag)) && hba->ccb[tag].req != NULL)
+			break;
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+	if (tag < MU_MAX_REQUEST) {
+		ssleep(1);
+		if (++i < 10)
+			goto wait_cmds;
+	}

This implementation isn't correct. Either a reset is triggered as part of error handling, in which case every command is guaranteed to be completed or timed out, or it's been done from sg in which case the user wants an immediate reset. In either case, you shouldn't be waiting another 10 seconds for active commands.

drivers/scsi/stex.c line 954

Quoted 2 lines
drivers/scsi/stex.c new file +2 -0
+	if (hba->cardtype == st_shasta)
+		stex_hard_reset(hba);

Don't you also want some type of processing for st_vsc? Otherwise it looks like it will drop straight through the error handler and be offlined.

stex_handshake is touching the doorbell without locking, is that OK? It looks like it might be since it only happens either at start of day or after reset, but what happens (as the kexec people will remind us) if the bios hasn't quesced the card (the handshake is done after the interrupt is added ... it could fire immediately)?

Quoted 18 lines
drivers/scsi/stex.c new file +18 -0
+static void stex_hba_stop(struct st_hba *hba)
+{
+	unsigned long flags;
+	int i;
+	u16 tag;
+
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	if ((tag = stex_alloc_tag((unsigned long *)&hba->tag))
+		== TAG_BITMAP_LENGTH) {
+		spin_unlock_irqrestore(hba->host->host_lock, flags);
+		printk(KERN_ERR DRV_NAME "(%s): unable to alloc tag\n",
+			pci_name(hba->pdev));
+		return;
+	}
+	for (i=0; i<(ST_MAX_ARRAY_SUPPORTED*ST_MAX_LUN_PER_TARGET*2); i++) {
+		stex_internal_flush(hba, i, tag);
+		spin_unlock_irqrestore(hba->host->host_lock, flags);
+

drivers/scsi/stex.c line 1193

Quoted 10 lines
drivers/scsi/stex.c new file +10 -0
+		wait_event_timeout(hba->waitq,
+			!(hba->ccb[tag].req_type), ST_INTERNAL_TIMEOUT*HZ);
+		if (hba->ccb[tag].req_type & PASSTHRU_REQ_TYPE)
+			return;
+		spin_lock_irqsave(hba->host->host_lock, flags);
+	}
+
+	stex_free_tag((unsigned long *)&hba->tag, tag);
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+}

This is really inefficient (looping over all targets whether present or not). Just implement slave destroy, it will keep track of allocated in-use targets for you.

James

#3 lore raw
In reply to James Bottomley
Quoted 5 lines · James Bottomley
Quoted 4 lines · Jeff Garzik

Adds the 'stex' driver for Promise SuperTrak EX storage controllers. These controllers present themselves as SCSI, though like 3ware, megaraid and others, the underlying storage may or may not be SCSI.

Quoted 8 lines
Quoted 1 line

As discussed, the block tagging stuff is a post-merge todo item.

That's not exactly my recollection of the discussion: I thought we were still discussing the chicken and egg issue (which is we have APIs to do this per host tagging which stex duplicates on the grounds no-one's using the current APIs). Jens and I seem to be in agreement that stex should try the API's and well make any changes that become necessary to block or SCSI happen.

Please re-read the end of the thread. The last word was "ok, let's go ahead and get this merged."

It is unreasonable to require use of an API that no-one else uses, for initial merge. That has higher potential to take a working driver to a non-working state.

If you use the API _after_ the initial merge, then you can easily debug the problem with 'git bisect' should the driver stop working. With your suggested path, it causes needless delay and reduces the useful information a tester can give us to "it works" or "it doesn't work." With my way, the tester can give us "<this> change broke the driver" information.

Quoted 15 lines
Quoted 10 lines
(quoted diff) +1 -0
+	switch (cmd->cmnd[0]) {

Review

Quoted 9 lines
(quoted diff) +9 -0
+	case MODE_SENSE_10:
+	{
+		static char mode_sense10[8] = { 0, 6, 0, 0, 0, 0, 0, 0 };
+
+		stex_direct_copy(cmd, mode_sense10, sizeof(mode_sense10));
+		cmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
+		done(cmd);
+		return 0;
+	}

This looks like it will trick the sd driver into reading uninitialised data for the drive caching parameters ... there are obviously faults on both sides, but I think when you ask for a mode page and you get a success return, you're entitled to think you got it ...

Agreed.

Quoted 15 lines
Quoted 10 lines
(quoted diff) +1 -0
+	case INQUIRY:

Review

Quoted 9 lines
(quoted diff) +9 -0
+		if (id != ST_MAX_ARRAY_SUPPORTED)
+			break;
+		if (lun == 0 && (cmd->cmnd[1] & INQUIRY_EVPD) == 0) {
+			stex_direct_copy(cmd, console_inq_page,
+				sizeof(console_inq_page));
+			cmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
+		} else
+			cmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8;
+		done(cmd);

The error return isn't correct you should never use DID_ERROR for an uncorrectable error because it will cause a retry (which you'll fail again). For an unsupported inquiry the correct return should be Check Condition/Illegal Request/Invalid Field in CDB

Fixed.

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
(quoted diff) +5 -0
+	case INQUIRY:
+		if (id != ST_MAX_ARRAY_SUPPORTED)
+			break;
+		if (lun == 0 && (cmd->cmnd[1] & INQUIRY_EVPD) == 0) {
+			stex_direct_copy(cmd, console_inq_page,
Quoted 8 lines
Quoted 5 lines

Review

Quoted 5 lines
(quoted diff) +5 -0
+				sizeof(console_inq_page));
+			cmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
+		} else
+			cmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8;
+		done(cmd);

The error return here looks like it shouldn't be DID_ERROR either. I assume the error is a format one and uncorrectable by a retry?

There is only one 'case INQUIRY' in the entire driver, so I assume you accidentally responded to the same code segment twice.

Quoted 4 lines
Quoted 4 lines
(quoted diff) +4 -0
+	}
+
+	hba->dma_mem = pci_alloc_consistent(pdev,
+		STEX_BUFFER_SIZE, &hba->dma_handle);
Quoted 8 lines

Review

Quoted 6 lines
(quoted diff) +6 -0
+	if (!hba->dma_mem) {
+		err = -ENOMEM;
+		printk(KERN_ERR DRV_NAME "(%s): dma mem alloc failed\n",
+			pci_name(pdev));
+		goto out_iounmap;
+	}

This should be dma_alloc_coherent, not pci_alloc_consistent.

This is perfectly normal and proper in a PCI-only driver. pci_xxx is not a deprecated API, it is a convenience API.

Using dma_xxx only causes needless work.

For the INQUIRY and irq flags fix, I checked in the attached patch to 'stex' branch of git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik/netdev-2.6.git

Jeff

commit 43ebb4ccf4bf705e7963b8b7162812a8ebd64e22

Author: Jeff Garzik <[email protected]>
Date:   Tue Aug 8 18:41:31 2006 -0400

[SCSI] stex: minor fixes: irq flag, error return value

- Don't use deprecated SA_SHIRQ irq flag.
- Return CHECK CONDITION (invalid field in CDB) where warranted.

43ebb4ccf4bf705e7963b8b7162812a8ebd64e22

drivers/scsi/stex.c +22 -3
index acf626f..f35833a 100644
@@ -334,6 +334,25 @@ static struct status_msg *stex_get_statu
 	return status;
 }
 
+static void stex_set_sense(struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq)
+{
+	cmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;
+
+	cmd->sense_buffer[0] = 0x70;    /* fixed format, current */
+	cmd->sense_buffer[2] = sk;
+	cmd->sense_buffer[7] = 18 - 8;  /* additional sense length */
+	cmd->sense_buffer[12] = asc;
+	cmd->sense_buffer[13] = ascq;
+}
+
+static void stex_invalid_field(struct scsi_cmnd *cmd,
+			       void (*done)(struct scsi_cmnd *))
+{
+	/* "Invalid field in cbd" */
+	stex_set_sense(cmd, ILLEGAL_REQUEST, 0x24, 0x0);
+	done(cmd);
+}
+
 static struct req_msg *stex_alloc_req(struct st_hba *hba)
 {
 	struct req_msg *req = ((struct req_msg *)hba->dma_mem) +
@@ -533,9 +552,9 @@ stex_queuecommand(struct scsi_cmnd *cmd,
 			stex_direct_copy(cmd, console_inq_page,
 				sizeof(console_inq_page));
 			cmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
+			done(cmd);
 		} else
-			cmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8;
-		done(cmd);
+			stex_invalid_field(cmd, done);
 		return 0;
 	case PASSTHRU_CMD:
 		if (cmd->cmnd[1] == PASSTHRU_GET_DRVVER) {
@@ -1122,7 +1141,7 @@ stex_probe(struct pci_dev *pdev, const s
 	hba->pdev = pdev;
 	init_waitqueue_head(&hba->waitq);
 
-	err = request_irq(pdev->irq, stex_intr, SA_SHIRQ, DRV_NAME, hba);
+	err = request_irq(pdev->irq, stex_intr, IRQF_SHARED, DRV_NAME, hba);
 	if (err) {
 		printk(KERN_ERR DRV_NAME "(%s): request irq failed\n",
 			pci_name(pdev));
#4 lore raw
Quoted 15 lines · Jeff Garzik
Quoted 5 lines · James Bottomley
Quoted 4 lines · Jeff Garzik

Adds the 'stex' driver for Promise SuperTrak EX storage controllers. These controllers present themselves as SCSI, though like 3ware, megaraid and others, the underlying storage may or may not be SCSI.

Quoted 8 lines
Quoted 1 line

As discussed, the block tagging stuff is a post-merge todo item.

That's not exactly my recollection of the discussion: I thought we were still discussing the chicken and egg issue (which is we have APIs to do this per host tagging which stex duplicates on the grounds no-one's using the current APIs). Jens and I seem to be in agreement that stex should try the API's and well make any changes that become necessary to block or SCSI happen.

Quoted 2 lines

Please re-read the end of the thread. The last word was "ok, let's go ahead and get this merged."

Those weren't my last words ...

However, I'll take on some of this ... I'll convert the aic7xxx driver which is our current shared host tag driver ... then you only need copy it to do stex.

Quoted 5 lines
Quoted 2 lines

The error return here looks like it shouldn't be DID_ERROR either. I assume the error is a format one and uncorrectable by a retry?

There is only one 'case INQUIRY' in the entire driver, so I assume you accidentally responded to the same code segment twice.

No, sorry, misquoted ... the above comment applies to the case PASSTHRU_CMD, which has the same problem (it would repeat a malformed command).

Quoted 6 lines
Quoted 1 line

This should be dma_alloc_coherent, not pci_alloc_consistent.

This is perfectly normal and proper in a PCI-only driver. pci_xxx is not a deprecated API, it is a convenience API.

Using dma_xxx only causes needless work.

What work? it's an exact drop in replacement. However, the only usage of pci_xxx I'm requiring to be fixed is the pci_alloc_consistent, primarily because pci_alloc_consistent *is* deprecated: it forces a GFP_ATOMIC allocation of a potentially large amount of data. dma_alloc_coherent allows you to specify gfp flags, which, in this case, should be GFP_KERNEL.

James

#5 lore raw
Quoted 3 lines · James Bottomley

However, I'll take on some of this ... I'll convert the aic7xxx driver which is our current shared host tag driver ... then you only need copy it to do stex.

No, that approach still has the problems outlined for stex.

You are taking a WORKING, IN-TREE driver and modifying it. Thus 'git bisect' can easily identify any problems you introduce.

As has been stated repeatedly, stex should have the same conditions you are giving yourself: take a working, in-tree driver and update it to use host-wide tags.

Otherwise, you deny testers and developers the utility of 'git bisect' if there are problems that do not show up immediately.

Quoted 3 lines

No, sorry, misquoted ... the above comment applies to the case PASSTHRU_CMD, which has the same problem (it would repeat a malformed command).

DID_ERROR is not flagging a malformed command.

PASSTHRU_CMD either (a) passes the command to the firmware, using normal queue/complete paths, or (b) handles the command in the driver.

For the (b) case, DID_ERROR is only asserted if scsi_kmap_atomic_sg() returns NULL -- presumably a transient condition.

Quoted 6 lines
Quoted 4 lines
Quoted 1 line

This should be dma_alloc_coherent, not pci_alloc_consistent.

This is perfectly normal and proper in a PCI-only driver. pci_xxx is not a deprecated API, it is a convenience API.

Using dma_xxx only causes needless work.

What work? it's an exact drop in replacement. However, the only usage

No, it's not. Using struct device rather than struct pci_dev introduces additional indirection into the driver, rather than hiding it in a convenience layer. Nice and clean 'pdev' reference becomes '&pdev->dev' or 'to_pci_dev(dev)'.

Quoted 5 lines

of pci_xxx I'm requiring to be fixed is the pci_alloc_consistent, primarily because pci_alloc_consistent *is* deprecated: it forces a GFP_ATOMIC allocation of a potentially large amount of data. dma_alloc_coherent allows you to specify gfp flags, which, in this case, should be GFP_KERNEL.

Good point, I had forgotten about GFP_KERNEL. Agreed. Updated as shown in the attached patch.

Sounds like we need a pci_{alloc,free}_coherent wrapper API.

Jeff

commit 5b5464d78665b1b2199b02d827a3c5f85dbe2c4b

Author: Jeff Garzik <[email protected]>
Date:   Tue Aug 8 21:34:17 2006 -0400

[SCSI] stex: use dma_alloc_coherent()

pci_alloc_consistent() API prevents us from using GFP_KERNEL.

5b5464d78665b1b2199b02d827a3c5f85dbe2c4b

drivers/scsi/stex.c +6 -6
index f35833a..fceae17 100644
@@ -1112,8 +1112,8 @@ stex_probe(struct pci_dev *pdev, const s
 		goto out_iounmap;
 	}
 
-	hba->dma_mem = pci_alloc_consistent(pdev,
-		STEX_BUFFER_SIZE, &hba->dma_handle);
+	hba->dma_mem = dma_alloc_coherent(&pdev->dev,
+		STEX_BUFFER_SIZE, &hba->dma_handle, GFP_KERNEL);
 	if (!hba->dma_mem) {
 		err = -ENOMEM;
 		printk(KERN_ERR DRV_NAME "(%s): dma mem alloc failed\n",
@@ -1168,8 +1168,8 @@ stex_probe(struct pci_dev *pdev, const s
 out_free_irq:
 	free_irq(pdev->irq, hba);
 out_pci_free:
-	pci_free_consistent(pdev, STEX_BUFFER_SIZE,
-		hba->dma_mem, hba->dma_handle);
+	dma_free_coherent(&pdev->dev, STEX_BUFFER_SIZE,
+			  hba->dma_mem, hba->dma_handle);
 out_iounmap:
 	iounmap(hba->mmio_base);
 out_release_regions:
@@ -1219,8 +1219,8 @@ static void stex_hba_free(struct st_hba 
 
 	pci_release_regions(hba->pdev);
 
-	pci_free_consistent(hba->pdev, STEX_BUFFER_SIZE,
-			hba->dma_mem, hba->dma_handle);
+	dma_free_coherent(&hba->pdev->dev, STEX_BUFFER_SIZE,
+			  hba->dma_mem, hba->dma_handle);
 }
 
 static void stex_remove(struct pci_dev *pdev)