]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/ata/libata-scsi.c
[libata] Prefer SCSI_SENSE_BUFFERSIZE to sizeof()
[linux-2.6-omap-h63xx.git] / drivers / ata / libata-scsi.c
index f752eddc19ed739e192e87643abe26aca5d0009d..3fd08201bef472d4b38774b545ee76a4592ffe66 100644 (file)
@@ -110,6 +110,74 @@ static struct scsi_transport_template ata_scsi_transport_template = {
 };
 
 
+static const struct {
+       enum link_pm    value;
+       const char      *name;
+} link_pm_policy[] = {
+       { NOT_AVAILABLE, "max_performance" },
+       { MIN_POWER, "min_power" },
+       { MAX_PERFORMANCE, "max_performance" },
+       { MEDIUM_POWER, "medium_power" },
+};
+
+static const char *ata_scsi_lpm_get(enum link_pm policy)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(link_pm_policy); i++)
+               if (link_pm_policy[i].value == policy)
+                       return link_pm_policy[i].name;
+
+       return NULL;
+}
+
+static ssize_t ata_scsi_lpm_put(struct class_device *class_dev,
+       const char *buf, size_t count)
+{
+       struct Scsi_Host *shost = class_to_shost(class_dev);
+       struct ata_port *ap = ata_shost_to_port(shost);
+       enum link_pm policy = 0;
+       int i;
+
+       /*
+        * we are skipping array location 0 on purpose - this
+        * is because a value of NOT_AVAILABLE is displayed
+        * to the user as max_performance, but when the user
+        * writes "max_performance", they actually want the
+        * value to match MAX_PERFORMANCE.
+        */
+       for (i = 1; i < ARRAY_SIZE(link_pm_policy); i++) {
+               const int len = strlen(link_pm_policy[i].name);
+               if (strncmp(link_pm_policy[i].name, buf, len) == 0 &&
+                  buf[len] == '\n') {
+                       policy = link_pm_policy[i].value;
+                       break;
+               }
+       }
+       if (!policy)
+               return -EINVAL;
+
+       ata_lpm_schedule(ap, policy);
+       return count;
+}
+
+static ssize_t
+ata_scsi_lpm_show(struct class_device *class_dev, char *buf)
+{
+       struct Scsi_Host *shost = class_to_shost(class_dev);
+       struct ata_port *ap = ata_shost_to_port(shost);
+       const char *policy =
+               ata_scsi_lpm_get(ap->pm_policy);
+
+       if (!policy)
+               return -EINVAL;
+
+       return snprintf(buf, 23, "%s\n", policy);
+}
+CLASS_DEVICE_ATTR(link_power_management_policy, S_IRUGO | S_IWUSR,
+               ata_scsi_lpm_show, ata_scsi_lpm_put);
+EXPORT_SYMBOL_GPL(class_device_attr_link_power_management_policy);
+
 static void ata_scsi_invalid_field(struct scsi_cmnd *cmd,
                                   void (*done)(struct scsi_cmnd *))
 {
@@ -449,7 +517,7 @@ static struct ata_queued_cmd *ata_scsi_qc_new(struct ata_device *dev,
                qc->scsicmd = cmd;
                qc->scsidone = done;
 
-               qc->__sg = scsi_sglist(cmd);
+               qc->sg = scsi_sglist(cmd);
                qc->n_elem = scsi_sg_count(cmd);
        } else {
                cmd->result = (DID_OK << 16) | (QUEUE_FULL << 1);
@@ -773,6 +841,12 @@ static void ata_scsi_dev_config(struct scsi_device *sdev,
                blk_queue_max_hw_segments(q, q->max_hw_segments - 1);
        }
 
+       if (dev->class == ATA_DEV_ATA)
+               sdev->manage_start_stop = 1;
+
+       if (dev->flags & ATA_DFLAG_AN)
+               set_bit(SDEV_EVT_MEDIA_CHANGE, sdev->supported_events);
+
        if (dev->flags & ATA_DFLAG_NCQ) {
                int depth;
 
@@ -801,8 +875,6 @@ int ata_scsi_slave_config(struct scsi_device *sdev)
 
        ata_scsi_sdev_config(sdev);
 
-       sdev->manage_start_stop = 1;
-
        if (dev)
                ata_scsi_dev_config(sdev, dev);
 
@@ -1040,6 +1112,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;
 }
 
@@ -2135,7 +2210,7 @@ unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf,
 
                /* sector size */
                ATA_SCSI_RBUF_SET(6, ATA_SECT_SIZE >> 8);
-               ATA_SCSI_RBUF_SET(7, ATA_SECT_SIZE);
+               ATA_SCSI_RBUF_SET(7, ATA_SECT_SIZE & 0xff);
        } else {
                /* sector count, 64-bit */
                ATA_SCSI_RBUF_SET(0, last_lba >> (8 * 7));
@@ -2149,7 +2224,7 @@ unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf,
 
                /* sector size */
                ATA_SCSI_RBUF_SET(10, ATA_SECT_SIZE >> 8);
-               ATA_SCSI_RBUF_SET(11, ATA_SECT_SIZE);
+               ATA_SCSI_RBUF_SET(11, ATA_SECT_SIZE & 0xff);
        }
 
        return 0;
@@ -2256,7 +2331,7 @@ static void atapi_request_sense(struct ata_queued_cmd *qc)
        DPRINTK("ATAPI request sense\n");
 
        /* FIXME: is this needed? */
-       memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+       memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
 
        ap->ops->tf_read(ap, &qc->tf);
 
@@ -2266,7 +2341,9 @@ static void atapi_request_sense(struct ata_queued_cmd *qc)
 
        ata_qc_reinit(qc);
 
-       ata_sg_init_one(qc, cmd->sense_buffer, sizeof(cmd->sense_buffer));
+       /* setup sg table and init transfer direction */
+       sg_init_one(&qc->sgent, cmd->sense_buffer, SCSI_SENSE_BUFFERSIZE);
+       ata_sg_init(qc, &qc->sgent, 1);
        qc->dma_dir = DMA_FROM_DEVICE;
 
        memset(&qc->cdb, 0, qc->dev->cdb_len);
@@ -2277,10 +2354,10 @@ static void atapi_request_sense(struct ata_queued_cmd *qc)
        qc->tf.command = ATA_CMD_PACKET;
 
        if (ata_pio_use_silly(ap)) {
-               qc->tf.protocol = ATA_PROT_ATAPI_DMA;
+               qc->tf.protocol = ATAPI_PROT_DMA;
                qc->tf.feature |= ATAPI_PKT_DMA;
        } else {
-               qc->tf.protocol = ATA_PROT_ATAPI;
+               qc->tf.protocol = ATAPI_PROT_PIO;
                qc->tf.lbam = SCSI_SENSE_BUFFERSIZE;
                qc->tf.lbah = 0;
        }
@@ -2411,23 +2488,52 @@ 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);
 
        if (using_pio || nodata) {
                /* no data, or PIO data xfer */
                if (nodata)
-                       qc->tf.protocol = ATA_PROT_ATAPI_NODATA;
+                       qc->tf.protocol = ATAPI_PROT_NODATA;
                else
-                       qc->tf.protocol = ATA_PROT_ATAPI;
+                       qc->tf.protocol = ATAPI_PROT_PIO;
        } else {
                /* DMA data xfer */
-               qc->tf.protocol = ATA_PROT_ATAPI_DMA;
+               qc->tf.protocol = ATAPI_PROT_DMA;
                qc->tf.feature |= ATAPI_PKT_DMA;
 
                if (atapi_dmadir && (scmd->sc_data_direction != DMA_TO_DEVICE))
@@ -2586,6 +2692,24 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
        if ((tf->protocol = ata_scsi_map_proto(cdb[1])) == ATA_PROT_UNKNOWN)
                goto invalid_fld;
 
+       /*
+        * Filter TPM commands by default. These provide an
+        * essentially uncontrolled encrypted "back door" between
+        * applications and the disk. Set libata.allow_tpm=1 if you
+        * have a real reason for wanting to use them. This ensures
+        * that installed software cannot easily mess stuff up without
+        * user intent. DVR type users will probably ship with this enabled
+        * for movie content management.
+        *
+        * Note that for ATA8 we can issue a DCS change and DCS freeze lock
+        * for this and should do in future but that it is not sufficient as
+        * DCS is an optional feature set. Thus we also do the software filter
+        * so that we comply with the TC consortium stated goal that the user
+        * can turn off TC features of their system.
+        */
+       if (tf->command >= 0x5C && tf->command <= 0x5F && !libata_allow_tpm)
+               goto invalid_fld;
+
        /* We may not issue DMA commands if no DMA mode is set */
        if (tf->protocol == ATA_PROT_DMA && dev->dma_mode == 0)
                goto invalid_fld;
@@ -2696,8 +2820,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;
 
@@ -2795,7 +2919,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;
@@ -3225,10 +3350,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);
 }
 
 /**