SOURCES (LINUX_2_6_14): linux-2.6.14.7-sata-sil-mod15write-workaro...
mguevara
mguevara at pld-linux.org
Tue Feb 21 18:30:41 CET 2006
Author: mguevara Date: Tue Feb 21 17:30:41 2006 GMT
Module: SOURCES Tag: LINUX_2_6_14
---- Log message:
- 2.6.14.7 replacement for linux-2.6-sata-sil-mod15write-workaround.patch
---- Files affected:
SOURCES:
linux-2.6.14.7-sata-sil-mod15write-workaround.patch (NONE -> 1.1.2.1) (NEW)
---- Diffs:
================================================================
Index: SOURCES/linux-2.6.14.7-sata-sil-mod15write-workaround.patch
diff -u /dev/null SOURCES/linux-2.6.14.7-sata-sil-mod15write-workaround.patch:1.1.2.1
--- /dev/null Tue Feb 21 18:30:41 2006
+++ SOURCES/linux-2.6.14.7-sata-sil-mod15write-workaround.patch Tue Feb 21 18:30:36 2006
@@ -0,0 +1,531 @@
+Date: Sat, 26 Mar 2005 14:24:35 +0900
+From: Tejun Heo <htejun at gmail.com>
+To: Jeff Garzik <jgarzik at pobox.com>,
+
+ Hello, Jeff.
+ This is the updated version of mod15write workaround. Changes are
+ * sg list restoration on completion
+ * debug code to verify sg list doesn't get changed
+ * updated against the latest libata-dev-2.6
+ * more comments
+ I think the code is okay, but I left the debug code there just in
+case. The debug code is inside #ifdef and can be removed easily once
+testing is complete.
+
+ Signed-off-by: Tejun Heo <htejun at gmail.com>
+
+
+# This is a BitKeeper generated diff -Nru style patch.
+#
+# ChangeSet
+# 2005/03/26 14:16:23+09:00 tj at htj.dyndns.org
+# cosmetic
+#
+# drivers/scsi/sata_sil.c
+# 2005/03/26 14:13:44+09:00 tj at htj.dyndns.org +17 -4
+# xxx
+#
+# ChangeSet
+# 2005/03/26 12:32:07+09:00 tj at htj.dyndns.org
+# Merge htj.dyndns.org:/mnt/tj-work/os/ata/libata-dev-2.6
+# into htj.dyndns.org:/mnt/tj-work/os/ata/libata-work
+#
+# drivers/scsi/sata_sil.c
+# 2005/03/26 12:32:01+09:00 tj at htj.dyndns.org +0 -0
+# Auto merged
+#
+# ChangeSet
+# 2005/03/26 12:29:29+09:00 tj at htj.dyndns.org
+# sg restoration implemented
+# M15W_DEBUG
+#
+# drivers/scsi/sata_sil.c
+# 2005/03/26 12:29:21+09:00 tj at htj.dyndns.org +54 -2
+# sg restoration implemented
+# M15W_DEBUG
+#
+# ChangeSet
+# 2005/03/16 08:45:47+09:00 tj at htj.dyndns.org
+# comments
+#
+# drivers/scsi/sata_sil.c
+# 2005/03/16 08:45:39+09:00 tj at htj.dyndns.org +36 -10
+# comments
+#
+# ChangeSet
+# 2005/03/16 02:14:47+09:00 tj at htj.dyndns.org
+# m15w workaround works now
+#
+# drivers/scsi/sata_sil.c
+# 2005/03/16 02:14:40+09:00 tj at htj.dyndns.org +54 -25
+# m15w workaround works now
+#
+# ChangeSet
+# 2005/03/15 22:16:44+09:00 tj at htj.dyndns.org
+# xxx
+#
+# drivers/scsi/sata_sil.c
+# 2005/03/15 22:16:35+09:00 tj at htj.dyndns.org +89 -36
+# xxx
+#
+# ChangeSet
+# 2005/03/15 16:36:29+09:00 tj at htj.dyndns.org
+# initial implementaion of mod15write workaround
+#
+# drivers/scsi/sata_sil.c
+# 2005/03/15 16:36:21+09:00 tj at htj.dyndns.org +139 -11
+# initial implementaion of mod15write workaround
+#
+--- linux-2.6.11.6/drivers/scsi/sata_sil.c.orig 2005-03-31 19:40:17.000000000 +0200
++++ linux-2.6.11.6/drivers/scsi/sata_sil.c 2005-03-31 19:41:52.000000000 +0200
+@@ -71,9 +71,12 @@
+
+ static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
+ static void sil_dev_config(struct ata_port *ap, struct ata_device *dev);
++static void sil_qc_prep (struct ata_queued_cmd *qc);
++static void sil_eng_timeout (struct ata_port *ap);
+ static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg);
+ static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
+ static void sil_post_set_mode (struct ata_port *ap);
++static void sil_host_stop (struct ata_host_set *host_set);
+
+ static struct pci_device_id sil_pci_tbl[] = {
+ { 0x1095, 0x3112, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 },
+@@ -201,6 +205,53 @@
+ /* ... port 3 */
+ };
+
++/*
++ * Context to loop over write requests > 15 sectors for Mod15Write bug.
++ *
++ * The following libata layer fields are saved at the beginning and
++ * mangled as necessary.
++ *
++ * qc->sg : To fool ata_fill_sg().
++ * qc->n_elem : ditto.
++ * qc->flags : Except for the last iteration, ATA_QCFLAG_DMAMAP
++ * should be off on entering ata_interrupt() such
++ * that ata_qc_complete() doesn't call ata_sg_clean()
++ * before sil_m15w_chunk_complete(), but the flags
++ * should be set for ata_qc_prep() to work. This flag
++ * handling is the hackiest part of this workaround.
++ * qc->complete_fn : Overrided to sil_m15w_chunk_complete().
++ *
++ * The following cxt fields are used to iterate over write requests.
++ *
++ * next_block : The starting block of the next chunk.
++ * next_sg : The first sg entry of the next chunk.
++ * left : Total bytes left.
++ * cur_sg_ofs : Number of processed bytes in the first sg entry
++ * of this chunk.
++ * next_sg_ofs : Number of bytes to be processed in the last sg
++ * entry of this chunk.
++ * next_sg_len : Number of bytes to be processed in the first sg
++ * entry of the next chunk.
++ */
++/*#define M15W_DEBUG*/
++struct sil_m15w_cxt {
++ u64 next_block;
++ struct scatterlist * next_sg;
++ unsigned int left;
++ unsigned int cur_sg_ofs;
++ unsigned int next_sg_ofs;
++ unsigned int next_sg_len;
++ int timedout;
++
++ struct scatterlist * orig_sg;
++ unsigned int orig_nelem;
++ unsigned long orig_flags;
++ ata_qc_cb_t orig_complete_fn;
++#ifdef M15W_DEBUG
++ struct scatterlist sg_copy[LIBATA_MAX_PRD];
++#endif
++};
++
+ MODULE_AUTHOR("Jeff Garzik");
+ MODULE_DESCRIPTION("low-level driver for Silicon Image SATA controller");
+ MODULE_LICENSE("GPL");
+@@ -241,6 +292,227 @@
+ readl(addr); /* flush */
+ }
+
++static inline u64 sil_m15w_read_tf_block (struct ata_taskfile *tf)
++{
++ u64 block = 0;
++
++ BUG_ON(!(tf->flags & ATA_TFLAG_LBA48));
++
++ block |= (u64)tf->lbal;
++ block |= (u64)tf->lbam << 8;
++ block |= (u64)tf->lbah << 16;
++
++ if (tf->flags & ATA_TFLAG_LBA48) {
++ block |= (u64)tf->hob_lbal << 24;
++ block |= (u64)tf->hob_lbam << 32;
++ block |= (u64)tf->hob_lbah << 40;
++ } else
++ block |= (u64)(tf->device & 0xf) << 24;
++
++ return block;
++}
++
++static inline void sil_m15w_rewrite_tf (struct ata_taskfile *tf,
++ u64 block, u16 nsect)
++{
++ BUG_ON(!(tf->flags & ATA_TFLAG_LBA48));
++
++ tf->nsect = nsect & 0xff;
++ tf->lbal = block & 0xff;
++ tf->lbam = (block >> 8) & 0xff;
++ tf->lbah = (block >> 16) & 0xff;
++
++ if (tf->flags & ATA_TFLAG_LBA48) {
++ tf->hob_nsect = (nsect >> 8) & 0xff;
++ tf->hob_lbal = (block >> 24) & 0xff;
++ tf->hob_lbam = (block >> 32) & 0xff;
++ tf->hob_lbah = (block >> 40) & 0xff;
++ } else {
++ tf->device &= ~0xf;
++ tf->device |= (block >> 24) & 0xf;
++ }
++}
++
++static void sil_m15w_next(struct ata_queued_cmd *qc)
++{
++ struct sil_m15w_cxt *cxt = qc->private_data;
++ struct scatterlist *sg;
++ unsigned int todo, res, nelem;
++
++ if (qc->sg != cxt->next_sg) {
++ sg_dma_address(qc->sg) -= cxt->cur_sg_ofs;
++ sg_dma_len(qc->sg) += cxt->cur_sg_ofs;
++ cxt->cur_sg_ofs = 0;
++ }
++ cxt->cur_sg_ofs += cxt->next_sg_ofs;
++
++ qc->sg = sg = cxt->next_sg;
++ sg_dma_address(sg) += cxt->next_sg_ofs;
++ sg_dma_len(sg) = cxt->next_sg_len;
++
++ res = todo = min_t(unsigned int, cxt->left, 15 << 9);
++
++ nelem = 0;
++ while (sg_dma_len(sg) <= res) {
++ res -= sg_dma_len(sg);
++ sg++;
++ nelem++;
++ }
++
++ if (todo < cxt->left) {
++ cxt->next_sg = sg;
++ cxt->next_sg_ofs = res;
++ cxt->next_sg_len = sg_dma_len(sg) - res;
++ if (res) {
++ nelem++;
++ sg_dma_len(sg) = res;
++ }
++ } else {
++ cxt->next_sg = NULL;
++ cxt->next_sg_ofs = 0;
++ cxt->next_sg_len = 0;
++ }
++
++ DPRINTK("block=%llu nelem=%u todo=%u left=%u\n",
++ cxt->next_block, nelem, todo, cxt->left);
++
++ qc->n_elem = nelem;
++ sil_m15w_rewrite_tf(&qc->tf, cxt->next_block, todo >> 9);
++ cxt->left -= todo;
++ cxt->next_block += todo >> 9;
++}
++
++static inline void sil_m15w_restore_qc (struct ata_queued_cmd *qc)
++{
++ struct sil_m15w_cxt *cxt = qc->private_data;
++
++ DPRINTK("ENTER\n");
++
++ sg_dma_address(qc->sg) -= cxt->cur_sg_ofs;
++ sg_dma_len(qc->sg) += cxt->cur_sg_ofs;
++ if (cxt->next_sg_ofs)
++ sg_dma_len(cxt->next_sg) += cxt->next_sg_len;
++ qc->sg = cxt->orig_sg;
++ qc->n_elem = cxt->orig_nelem;
++ qc->flags |= cxt->orig_flags;
++ qc->complete_fn = cxt->orig_complete_fn;
++#ifdef M15W_DEBUG
++ {
++ int i, j;
++ for (i = 0; i < qc->n_elem; i++)
++ if (memcmp(&cxt->sg_copy[i], &qc->sg[i],
++ sizeof(qc->sg[0])))
++ break;
++ if (i < qc->n_elem) {
++ printk(KERN_ERR "sil_m15w: sg mismatch\n");
++ printk(KERN_ERR "orig: ");
++ for (j = 0; j < qc->n_elem; j++)
++ printk("%s%08x:%04u ",
++ i == j ? "*" : "",
++ (u32)sg_dma_address(&cxt->sg_copy[j]),
++ sg_dma_len(&cxt->sg_copy[j]));
++ printk("\n");
++ printk(KERN_ERR "used: ");
++ for (j = 0; j < qc->n_elem; j++)
++ printk("%s%08x:%04u ",
++ i == j ? "*" : "",
++ (u32)sg_dma_address(&qc->sg[j]),
++ sg_dma_len(&qc->sg[j]));
++ printk("\n");
++ }
++ }
++#endif
++}
++
++static int sil_m15w_chunk_complete (struct ata_queued_cmd *qc, u8 drv_stat)
++{
++ struct sil_m15w_cxt *cxt = qc->private_data;
++
++ DPRINTK("ENTER\n");
++
++ if (unlikely(cxt->timedout))
++ drv_stat |= ATA_BUSY; /* Any better error status? */
++
++ /* Complete the command immediately on error */
++ if (unlikely(drv_stat & (ATA_ERR | ATA_BUSY | ATA_DRQ))) {
++ sil_m15w_restore_qc(qc);
++ ata_qc_complete(qc, drv_stat);
++ return 1;
++ }
++
++ sil_m15w_next(qc);
++
++ qc->flags |= cxt->orig_flags;
++ ata_qc_prep(qc);
++ qc->flags &= ~ATA_QCFLAG_DMAMAP;
++
++ /* On last iteration, restore fields such that normal
++ * completion path is run */
++ if (!cxt->left)
++ sil_m15w_restore_qc(qc);
++ sil_ops.qc_issue(qc);
++ return 1;
++}
++
++static void sil_qc_prep (struct ata_queued_cmd *qc)
++{
++ struct sil_m15w_cxt *cxt = qc->private_data;
++
++ if (unlikely(cxt && qc->tf.flags & ATA_TFLAG_WRITE && qc->nsect > 15)) {
++ BUG_ON(cxt->left);
++ if (qc->tf.protocol == ATA_PROT_DMA) {
++ /* Okay, begin mod15write workaround */
++ cxt->next_block = sil_m15w_read_tf_block(&qc->tf);
++ cxt->next_sg = qc->sg;
++ cxt->left = qc->nsect << 9;
++ cxt->cur_sg_ofs = 0;
++ cxt->next_sg_ofs = 0;
++ cxt->next_sg_len = sg_dma_len(qc->sg);
++ cxt->timedout = 0;
++
++ /* Save fields we're gonna mess with. Read comments
++ * above struct sil_m15w_cxt for more info. */
++ cxt->orig_sg = qc->sg;
++ cxt->orig_nelem = qc->n_elem;
++ cxt->orig_flags = qc->flags & ATA_QCFLAG_DMAMAP;
++ cxt->orig_complete_fn = qc->complete_fn;
++ qc->complete_fn = sil_m15w_chunk_complete;
++#ifdef M15W_DEBUG
++ {
++ int i;
++ for (i = 0; i < qc->n_elem; i++)
++ cxt->sg_copy[i] = qc->sg[i];
++ }
++#endif
++ DPRINTK("MOD15WRITE, block=%llu nsect=%u\n",
++ cxt->next_block, qc->nsect);
++ sil_m15w_next(qc);
++
++ ata_qc_prep(qc);
++ qc->flags &= ~ATA_QCFLAG_DMAMAP;
++ return;
++ } else
++ printk(KERN_WARNING "ata%u(%u): write request > 15 "
++ "issued using non-DMA protocol. Drive may "
++ "lock up.\n", qc->ap->id, qc->dev->devno);
++ }
++
++ ata_qc_prep(qc);
++}
++
++static void sil_eng_timeout (struct ata_port *ap)
++{
++ struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag);
++
++ if (qc && qc->private_data) {
++ struct sil_m15w_cxt *cxt = qc->private_data;
++ if (cxt->left)
++ cxt->timedout = 1;
++ }
++
++ ata_eng_timeout(ap);
++}
++
+ static inline unsigned long sil_scr_addr(struct ata_port *ap, unsigned int sc_reg)
+ {
+ unsigned long offset = ap->ioaddr.scr_addr;
+@@ -275,6 +547,12 @@
+ writel(val, mmio);
+ }
+
++static void sil_host_stop (struct ata_host_set *host_set)
++{
++ /* Free mod15write context array. */
++ kfree(host_set->private_data);
++}
++
+ /**
+ * sil_dev_config - Apply device/host-specific errata fixups
+ * @ap: Port containing device to be examined
+@@ -285,17 +563,12 @@
+ * We apply two errata fixups which are specific to Silicon Image,
+ * a Seagate and a Maxtor fixup.
+ *
+- * For certain Seagate devices, we must limit the maximum sectors
+- * to under 8K.
++ * For certain Seagate devices, we cannot issue write requests
++ * larger than 15 sectors.
+ *
+ * For certain Maxtor devices, we must not program the drive
+ * beyond udma5.
+ *
+- * Both fixups are unfairly pessimistic. As soon as I get more
+- * information on these errata, I will create a more exhaustive
+- * list, and apply the fixups to only the specific
+- * devices/hosts/firmwares that need it.
+- *
+ * 20040111 - Seagate drives affected by the Mod15Write bug are blacklisted
+ * The Maxtor quirk is in the blacklist, but I'm keeping the original
+ * pessimistic fix for the following reasons...
+@@ -303,6 +576,15 @@
+ * Windows driver, maybe only one is affected. More info would be greatly
+ * appreciated.
+ * - But then again UDMA5 is hardly anything to complain about
++ *
++ * 20050316 Tejun Heo - Proper Mod15Write workaround implemented.
++ * sata_sil doesn't report affected Seagate drives as having max
++ * sectors of 15 anymore, but handle write requests larger than
++ * 15 sectors by looping over it inside this driver proper. This
++ * is messy but it's better to isolate this kind of peculiar bug
++ * handling inside individual drivers than tainting libata layer.
++ * This workaround results in unhampered read performance and
++ * much better write performance.
+ */
+ static void sil_dev_config(struct ata_port *ap, struct ata_device *dev)
+ {
+@@ -310,6 +592,7 @@
+ unsigned char model_num[40];
+ const char *s;
+ unsigned int len;
++ int i;
+
+ ata_dev_id_string(dev->id, model_num, ATA_ID_PROD_OFS,
+ sizeof(model_num));
+@@ -349,7 +640,8 @@
+ static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+ {
+ static int printed_version;
+- struct ata_probe_ent *probe_ent = NULL;
++ struct ata_probe_ent *probe_ent;
++ struct sil_m15w_cxt *m15w_cxt;
+ unsigned long base;
+ void *mmio_base;
+ int rc;
+@@ -382,11 +674,17 @@
+ if (rc)
+ goto err_out_regions;
+
+- probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
+- if (probe_ent == NULL) {
+- rc = -ENOMEM;
++ rc = -ENOMEM;
++
++ tmp = sizeof(m15w_cxt[0]) * ATA_MAX_PORTS * ATA_MAX_QUEUE;
++ m15w_cxt = kmalloc(tmp, GFP_KERNEL);
++ if (m15w_cxt == NULL)
+ goto err_out_regions;
+- }
++ memset(m15w_cxt, 0, tmp);
++
++ probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
++ if (probe_ent == NULL)
++ goto err_out_free_m15w;
+
+ memset(probe_ent, 0, sizeof(*probe_ent));
+ INIT_LIST_HEAD(&probe_ent->node);
+@@ -400,6 +698,7 @@
+ probe_ent->irq = pdev->irq;
+ probe_ent->irq_flags = SA_SHIRQ;
+ probe_ent->host_flags = sil_port_info[ent->driver_data].host_flags;
++ probe_ent->private_data = m15w_cxt;
+
+ mmio_base = ioremap(pci_resource_start(pdev, 5),
+ pci_resource_len(pdev, 5));
+@@ -466,6 +765,8 @@
+
+ err_out_free_ent:
+ kfree(probe_ent);
++err_out_free_m15w:
++ kfree(m15w_cxt);
+ err_out_regions:
+ pci_release_regions(pdev);
+ err_out:
+--- linux-2.6.14.7.orig/drivers/scsi/sata_sil.c 2006-02-21 18:09:06.000000000 +0100
++++ linux-2.6.14.7/drivers/scsi/sata_sil.c 2006-02-21 18:19:17.000000000 +0100
+@@ -167,16 +167,16 @@
+ .bmdma_start = ata_bmdma_start,
+ .bmdma_stop = ata_bmdma_stop,
+ .bmdma_status = ata_bmdma_status,
+- .qc_prep = ata_qc_prep,
++ .qc_prep = sil_qc_prep,
+ .qc_issue = ata_qc_issue_prot,
+- .eng_timeout = ata_eng_timeout,
++ .eng_timeout = sil_eng_timeout,
+ .irq_handler = ata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
+ .scr_read = sil_scr_read,
+ .scr_write = sil_scr_write,
+ .port_start = ata_port_start,
+ .port_stop = ata_port_stop,
+- .host_stop = ata_pci_host_stop,
++ .host_stop = sil_host_stop,
+ };
+
+ static struct ata_port_info sil_port_info[] = {
+@@ -636,15 +636,23 @@
+ break;
+ }
+
+- /* limit requests to 15 sectors */
++ /* Activate mod15write quirk workaround */
+ if ((ap->flags & SIL_FLAG_MOD15WRITE) && (quirks & SIL_QUIRK_MOD15WRITE)) {
++ struct sil_m15w_cxt *cxt;
++
+ printk(KERN_INFO "ata%u(%u): applying Seagate errata fix\n",
+ ap->id, dev->devno);
+- ap->host->max_sectors = 15;
+- ap->host->hostt->max_sectors = 15;
+- dev->flags |= ATA_DFLAG_LOCK_SECTORS;
++
++ cxt = ap->host_set->private_data;
++ cxt += ap->port_no * ATA_MAX_QUEUE;
++ for (i = 0; i < ATA_MAX_QUEUE; i++)
++ ap->qcmd[i].private_data = cxt++;
++
+ return;
+ }
++ /* Clear qcmd->private_data if mod15write quirk isn't present */
++ for (i = 0; i < ATA_MAX_QUEUE; i++)
++ ap->qcmd[i].private_data = NULL;
+
+ /* limit to udma5 */
+ if (quirks & SIL_QUIRK_UDMA5MAX) {
================================================================
More information about the pld-cvs-commit
mailing list