]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/ata/libata-core.c
libata: fix off-by-one error in ata_tf_read_block()
[karo-tx-linux.git] / drivers / ata / libata-core.c
index 2c6aedaef7185e649f506b7c656ce8eb042c3e9e..df31deac5c8224fce9e4e0571c4484d35d966966 100644 (file)
@@ -709,7 +709,13 @@ u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev)
                head = tf->device & 0xf;
                sect = tf->lbal;
 
-               block = (cyl * dev->heads + head) * dev->sectors + sect;
+               if (!sect) {
+                       ata_dev_printk(dev, KERN_WARNING, "device reported "
+                                      "invalid CHS sector 0\n");
+                       sect = 1; /* oh well */
+               }
+
+               block = (cyl * dev->heads + head) * dev->sectors + sect - 1;
        }
 
        return block;
@@ -1515,6 +1521,7 @@ static int ata_hpa_resize(struct ata_device *dev)
 
                return rc;
        }
+       dev->n_native_sectors = native_sectors;
 
        /* nothing to do? */
        if (native_sectors <= sectors || !ata_ignore_hpa) {
@@ -2298,29 +2305,49 @@ static inline u8 ata_dev_knobble(struct ata_device *dev)
        return ((ap->cbl == ATA_CBL_SATA) && (!ata_id_is_sata(dev->id)));
 }
 
-static void ata_dev_config_ncq(struct ata_device *dev,
+static int ata_dev_config_ncq(struct ata_device *dev,
                               char *desc, size_t desc_sz)
 {
        struct ata_port *ap = dev->link->ap;
        int hdepth = 0, ddepth = ata_id_queue_depth(dev->id);
+       unsigned int err_mask;
+       char *aa_desc = "";
 
        if (!ata_id_has_ncq(dev->id)) {
                desc[0] = '\0';
-               return;
+               return 0;
        }
        if (dev->horkage & ATA_HORKAGE_NONCQ) {
                snprintf(desc, desc_sz, "NCQ (not used)");
-               return;
+               return 0;
        }
        if (ap->flags & ATA_FLAG_NCQ) {
                hdepth = min(ap->scsi_host->can_queue, ATA_MAX_QUEUE - 1);
                dev->flags |= ATA_DFLAG_NCQ;
        }
 
+       if (!(dev->horkage & ATA_HORKAGE_BROKEN_FPDMA_AA) &&
+               (ap->flags & ATA_FLAG_FPDMA_AA) &&
+               ata_id_has_fpdma_aa(dev->id)) {
+               err_mask = ata_dev_set_feature(dev, SETFEATURES_SATA_ENABLE,
+                       SATA_FPDMA_AA);
+               if (err_mask) {
+                       ata_dev_printk(dev, KERN_ERR, "failed to enable AA"
+                               "(error_mask=0x%x)\n", err_mask);
+                       if (err_mask != AC_ERR_DEV) {
+                               dev->horkage |= ATA_HORKAGE_BROKEN_FPDMA_AA;
+                               return -EIO;
+                       }
+               } else
+                       aa_desc = ", AA";
+       }
+
        if (hdepth >= ddepth)
-               snprintf(desc, desc_sz, "NCQ (depth %d)", ddepth);
+               snprintf(desc, desc_sz, "NCQ (depth %d)%s", ddepth, aa_desc);
        else
-               snprintf(desc, desc_sz, "NCQ (depth %d/%d)", hdepth, ddepth);
+               snprintf(desc, desc_sz, "NCQ (depth %d/%d)%s", hdepth,
+                       ddepth, aa_desc);
+       return 0;
 }
 
 /**
@@ -2460,7 +2487,7 @@ int ata_dev_configure(struct ata_device *dev)
 
                if (ata_id_has_lba(id)) {
                        const char *lba_desc;
-                       char ncq_desc[20];
+                       char ncq_desc[24];
 
                        lba_desc = "LBA";
                        dev->flags |= ATA_DFLAG_LBA;
@@ -2474,7 +2501,9 @@ int ata_dev_configure(struct ata_device *dev)
                        }
 
                        /* config NCQ */
-                       ata_dev_config_ncq(dev, ncq_desc, sizeof(ncq_desc));
+                       rc = ata_dev_config_ncq(dev, ncq_desc, sizeof(ncq_desc));
+                       if (rc)
+                               return rc;
 
                        /* print device info to dmesg */
                        if (ata_msg_drv(ap) && print_info) {
@@ -4099,6 +4128,7 @@ int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class,
                       unsigned int readid_flags)
 {
        u64 n_sectors = dev->n_sectors;
+       u64 n_native_sectors = dev->n_native_sectors;
        int rc;
 
        if (!ata_dev_enabled(dev))
@@ -4128,16 +4158,30 @@ int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class,
        /* verify n_sectors hasn't changed */
        if (dev->class == ATA_DEV_ATA && n_sectors &&
            dev->n_sectors != n_sectors) {
-               ata_dev_printk(dev, KERN_INFO, "n_sectors mismatch "
+               ata_dev_printk(dev, KERN_WARNING, "n_sectors mismatch "
                               "%llu != %llu\n",
                               (unsigned long long)n_sectors,
                               (unsigned long long)dev->n_sectors);
-
-               /* restore original n_sectors */
-               dev->n_sectors = n_sectors;
-
-               rc = -ENODEV;
-               goto fail;
+               /*
+                * Something could have caused HPA to be unlocked
+                * involuntarily.  If n_native_sectors hasn't changed
+                * and the new size matches it, keep the device.
+                */
+               if (dev->n_native_sectors == n_native_sectors &&
+                   dev->n_sectors > n_sectors &&
+                   dev->n_sectors == n_native_sectors) {
+                       ata_dev_printk(dev, KERN_WARNING,
+                                      "new n_sectors matches native, probably "
+                                      "late HPA unlock, continuing\n");
+                       /* keep using the old n_sectors */
+                       dev->n_sectors = n_sectors;
+               } else {
+                       /* restore original n_[native]_sectors and fail */
+                       dev->n_native_sectors = n_native_sectors;
+                       dev->n_sectors = n_sectors;
+                       rc = -ENODEV;
+                       goto fail;
+               }
        }
 
        return 0;
@@ -4286,6 +4330,9 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
        { "WDC WD2500JD-00HBB0", "WD-WMAL71490727", ATA_HORKAGE_BROKEN_HPA },
        { "MAXTOR 6L080L4",     "A93.0500",     ATA_HORKAGE_BROKEN_HPA },
 
+       /* this one allows HPA unlocking but fails IOs on the area */
+       { "OCZ-VERTEX",             "1.30",     ATA_HORKAGE_BROKEN_HPA },
+
        /* Devices which report 1 sector over size HPA */
        { "ST340823A",          NULL,           ATA_HORKAGE_HPA_SIZE, },
        { "ST320413A",          NULL,           ATA_HORKAGE_HPA_SIZE, },