]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/ata/libata-scsi.c
[SCSI] libata: fix corruption induced by relaxed DMA alignment in SCSI
[linux-2.6-omap-h63xx.git] / drivers / ata / libata-scsi.c
index 93bd36c19690bd4013381131b84fa8d13671cfcc..bc5cf6b8a4b885a32569ea6166cdba4a4ec5ee3c 100644 (file)
@@ -120,7 +120,7 @@ static const struct {
        { MEDIUM_POWER, "medium_power" },
 };
 
-const char *ata_scsi_lpm_get(enum link_pm policy)
+static const char *ata_scsi_lpm_get(enum link_pm policy)
 {
        int i;
 
@@ -839,7 +839,17 @@ static void ata_scsi_dev_config(struct scsi_device *sdev,
        if (dev->class == ATA_DEV_ATAPI) {
                struct request_queue *q = sdev->request_queue;
                blk_queue_max_hw_segments(q, q->max_hw_segments - 1);
-       }
+
+               /* set the min alignment */
+               blk_queue_update_dma_alignment(sdev->request_queue,
+                                              ATA_DMA_PAD_SZ - 1);
+       } else
+               /* ATA devices must be sector aligned */
+               blk_queue_update_dma_alignment(sdev->request_queue,
+                                              ATA_SECT_SIZE - 1);
+
+       if (dev->flags & ATA_DFLAG_AN)
+               set_bit(SDEV_EVT_MEDIA_CHANGE, sdev->supported_events);
 
        if (dev->flags & ATA_DFLAG_NCQ) {
                int depth;
@@ -869,12 +879,13 @@ int ata_scsi_slave_config(struct scsi_device *sdev)
 
        ata_scsi_sdev_config(sdev);
 
-       sdev->manage_start_stop = 1;
+       if (dev->class == ATA_DEV_ATA)
+               sdev->manage_start_stop = 1;
 
        if (dev)
                ata_scsi_dev_config(sdev, dev);
 
-       return 0;       /* scsi layer doesn't check return value, sigh */
+       return 0;
 }
 
 /**
@@ -1108,6 +1119,9 @@ static unsigned int ata_scsi_flush_xlat(struct ata_queued_cmd *qc)
        else
                tf->command = ATA_CMD_FLUSH;
 
+       /* flush is critical for IO integrity, consider it an IO command */
+       qc->flags |= ATA_QCFLAG_IO;
+
        return 0;
 }
 
@@ -2479,11 +2493,40 @@ static unsigned int atapi_xlat(struct ata_queued_cmd *qc)
        if (!using_pio && ata_check_atapi_dma(qc))
                using_pio = 1;
 
-       /* Some controller variants snoop this value for Packet transfers
-          to do state machine and FIFO management. Thus we want to set it
-          properly, and for DMA where it is effectively meaningless */
+       /* Some controller variants snoop this value for Packet
+        * transfers to do state machine and FIFO management.  Thus we
+        * want to set it properly, and for DMA where it is
+        * effectively meaningless.
+        */
        nbytes = min(qc->nbytes, (unsigned int)63 * 1024);
 
+       /* Most ATAPI devices which honor transfer chunk size don't
+        * behave according to the spec when odd chunk size which
+        * matches the transfer length is specified.  If the number of
+        * bytes to transfer is 2n+1.  According to the spec, what
+        * should happen is to indicate that 2n+1 is going to be
+        * transferred and transfer 2n+2 bytes where the last byte is
+        * padding.
+        *
+        * In practice, this doesn't happen.  ATAPI devices first
+        * indicate and transfer 2n bytes and then indicate and
+        * transfer 2 bytes where the last byte is padding.
+        *
+        * This inconsistency confuses several controllers which
+        * perform PIO using DMA such as Intel AHCIs and sil3124/32.
+        * These controllers use actual number of transferred bytes to
+        * update DMA poitner and transfer of 4n+2 bytes make those
+        * controller push DMA pointer by 4n+4 bytes because SATA data
+        * FISes are aligned to 4 bytes.  This causes data corruption
+        * and buffer overrun.
+        *
+        * Always setting nbytes to even number solves this problem
+        * because then ATAPI devices don't have to split data at 2n
+        * boundaries.
+        */
+       if (nbytes & 0x1)
+               nbytes++;
+
        qc->tf.lbam = (nbytes & 0xFF);
        qc->tf.lbah = (nbytes >> 8);
 
@@ -2764,8 +2807,8 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
         */
        qc->nbytes = scsi_bufflen(scmd);
 
-       /* request result TF */
-       qc->flags |= ATA_QCFLAG_RESULT_TF;
+       /* request result TF and be quiet about device error */
+       qc->flags |= ATA_QCFLAG_RESULT_TF | ATA_QCFLAG_QUIET;
 
        return 0;
 
@@ -2863,7 +2906,8 @@ static inline int __ata_scsi_queuecmd(struct scsi_cmnd *scmd,
                xlat_func = NULL;
                if (likely((scsi_op != ATA_16) || !atapi_passthru16)) {
                        /* relay SCSI command to ATAPI device */
-                       if (unlikely(scmd->cmd_len > dev->cdb_len))
+                       int len = COMMAND_SIZE(scsi_op);
+                       if (unlikely(len > scmd->cmd_len || len > dev->cdb_len))
                                goto bad_cdb_len;
 
                        xlat_func = atapi_xlat;
@@ -3293,10 +3337,9 @@ static void ata_scsi_handle_link_detach(struct ata_link *link)
  */
 void ata_scsi_media_change_notify(struct ata_device *dev)
 {
-#ifdef OTHER_AN_PATCHES_HAVE_BEEN_APPLIED
        if (dev->sdev)
-               scsi_device_event_notify(dev->sdev, SDEV_MEDIA_CHANGE);
-#endif
+               sdev_evt_send_simple(dev->sdev, SDEV_EVT_MEDIA_CHANGE,
+                                    GFP_ATOMIC);
 }
 
 /**