This is based on a patch from Jeff from 2004, but backported to 2.6.23 and furthermore, it will use the 7.5kiB/512B splitoff for blacklisted drives only. Jeff, why did you replace ATA_SHT_USE_CLUSTERING and ATA_DMA_BOUNDARY? drivers/ata/libata-core.c | 9 ++++- drivers/ata/sata_sil.c | 58 ++++++++++++++++++++++++++++++------ include/linux/libata.h | 6 +++ 3 files changed, 62 insertions(+), 11 deletions(-) Signed-off-by: Bernd Schubert <bs@xxxxxxxxx> Index: linux-2.6.23-rc9/drivers/ata/libata-core.c =================================================================== --- linux-2.6.23-rc9.orig/drivers/ata/libata-core.c 2007-10-02 17:21:12.000000000 +0200 +++ linux-2.6.23-rc9/drivers/ata/libata-core.c 2007-10-11 10:46:18.000000000 +0200 @@ -4073,7 +4073,7 @@ void ata_sg_clean(struct ata_queued_cmd * spin_lock_irqsave(host lock) * */ -static void ata_fill_sg(struct ata_queued_cmd *qc) +void ata_fill_sg(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; struct scatterlist *sg; @@ -4217,10 +4217,15 @@ int ata_check_atapi_dma(struct ata_queue */ void ata_qc_prep(struct ata_queued_cmd *qc) { + struct ata_port *ap = qc->ap; + if (!(qc->flags & ATA_QCFLAG_DMAMAP)) return; - ata_fill_sg(qc); + if (ap->ops->fill_sg) + ap->ops->fill_sg(qc); + else + ata_fill_sg(qc); } /** Index: linux-2.6.23-rc9/drivers/ata/sata_sil.c =================================================================== --- linux-2.6.23-rc9.orig/drivers/ata/sata_sil.c 2007-10-11 10:45:08.000000000 +0200 +++ linux-2.6.23-rc9/drivers/ata/sata_sil.c 2007-10-11 10:57:51.000000000 +0200 @@ -120,6 +120,7 @@ static int sil_scr_write(struct ata_port static int sil_set_mode (struct ata_port *ap, struct ata_device **r_failed); static void sil_freeze(struct ata_port *ap); static void sil_thaw(struct ata_port *ap); +static void sil_fill_sg(struct ata_queued_cmd *qc); static const struct pci_device_id sil_pci_tbl[] = { @@ -174,12 +175,12 @@ static struct scsi_host_template sil_sht .queuecommand = ata_scsi_queuecmd, .can_queue = ATA_DEF_QUEUE, .this_id = ATA_SHT_THIS_ID, - .sg_tablesize = LIBATA_MAX_PRD, + .sg_tablesize = 120, /* max 15 kiB sectors ? */ .cmd_per_lun = ATA_SHT_CMD_PER_LUN, .emulated = ATA_SHT_EMULATED, - .use_clustering = ATA_SHT_USE_CLUSTERING, + .use_clustering = 1, .proc_name = DRV_NAME, - .dma_boundary = ATA_DMA_BOUNDARY, + .dma_boundary = 0x1fff, .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, @@ -187,6 +188,7 @@ static struct scsi_host_template sil_sht static const struct ata_port_operations sil_ops = { .port_disable = ata_port_disable, + .fill_sg = sil_fill_sg, .dev_config = sil_dev_config, .tf_load = ata_tf_load, .tf_read = ata_tf_read, @@ -278,9 +280,9 @@ MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, sil_pci_tbl); MODULE_VERSION(DRV_VERSION); -static int slow_down = 0; -module_param(slow_down, int, 0444); -MODULE_PARM_DESC(slow_down, "Sledgehammer used to work around random problems, by limiting commands to 15 sectors (0=off, 1=on)"); +static int mod15_quirk = 0; +module_param(mod15_quirk, int, 0444); +MODULE_PARM_DESC(mod15_quirk, "Some disks from Seagate need a mod15 workaround."); static unsigned char sil_get_device_cache_line(struct pci_dev *pdev) @@ -534,6 +536,44 @@ static void sil_thaw(struct ata_port *ap writel(tmp, mmio_base + SIL_SYSCFG); } +static void sil_fill_sg(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + u32 addr, len; + unsigned int idx; + + ata_fill_sg(qc); + + /* check if we need the MOD15 workaround */ + if (!(qc->dev->quirk & SIL_FLAG_MOD15WRITE)) + return; + + if (unlikely(qc->n_elem < 1)) + return; + + /* hardware S/G list may be longer (or shorter) than number of + * PCI-mapped S/G entries (qc->n_elem), due to splitting + * in ata_fill_sg(). Start at zero, and skip to end + * of list, if we're not already there. + */ + idx = 0; + while ((le32_to_cpu(ap->prd[idx].flags_len) & ATA_PRD_EOT) == 0) + idx++; + + /* Errata workaround: if last segment is exactly 8K, split + * into 7.5K and 512b pieces. + */ + len = le32_to_cpu(ap->prd[idx].flags_len) & 0xffff; + if (len == 8192) { + addr = le32_to_cpu(ap->prd[idx].addr); + ap->prd[idx].flags_len = cpu_to_le32(15 * 512); + + idx++; + ap->prd[idx].addr = cpu_to_le32(addr + (15 * 512)); + ap->prd[idx].flags_len = cpu_to_le32(512 | ATA_PRD_EOT); + } +} + /** * sil_dev_config - Apply device/host-specific errata fixups * @dev: Device to be examined @@ -577,14 +617,14 @@ static void sil_dev_config(struct ata_de break; } - /* limit requests to 15 sectors */ - if (slow_down || + /* mod15 bug */ + if (mod15_quirk || ((ap->flags & SIL_FLAG_MOD15WRITE) && (quirks & SIL_QUIRK_MOD15WRITE))) { if (print_info) ata_dev_printk(dev, KERN_INFO, "applying Seagate " "errata fix (mod15write workaround)\n"); - dev->max_sectors = 15; + dev->quirk |= SIL_FLAG_MOD15WRITE; return; } Index: linux-2.6.23-rc9/include/linux/libata.h =================================================================== --- linux-2.6.23-rc9.orig/include/linux/libata.h 2007-10-02 17:21:28.000000000 +0200 +++ linux-2.6.23-rc9/include/linux/libata.h 2007-10-11 10:53:31.000000000 +0200 @@ -471,6 +471,9 @@ struct ata_device { /* error history */ struct ata_ering ering; int spdn_cnt; + + /* driver specific flags */ + unsigned int quirk; }; /* Offset into struct ata_device. Fields above it are maintained @@ -641,6 +644,8 @@ struct ata_port_operations { void (*bmdma_stop) (struct ata_queued_cmd *qc); u8 (*bmdma_status) (struct ata_port *ap); + + void (*fill_sg) (struct ata_queued_cmd *qc); }; struct ata_port_info { @@ -789,6 +794,7 @@ extern void ata_data_xfer_noirq(struct a unsigned int buflen, int write_data); extern void ata_dumb_qc_prep(struct ata_queued_cmd *qc); extern void ata_qc_prep(struct ata_queued_cmd *qc); +extern void ata_fill_sg(struct ata_queued_cmd *qc); extern void ata_noop_qc_prep(struct ata_queued_cmd *qc); extern unsigned int ata_qc_issue_prot(struct ata_queued_cmd *qc); extern void ata_sg_init_one(struct ata_queued_cmd *qc, void *buf, -- Bernd Schubert Q-Leap Networks GmbH - To unsubscribe from this list: send the line "unsubscribe linux-ide" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html