[SCSI] sd: Correctly handle all combinations of DIF and DIX
authorMartin K. Petersen <martin.petersen@oracle.com>
Fri, 19 Sep 2008 22:47:21 +0000 (18:47 -0400)
committerJames Bottomley <James.Bottomley@HansenPartnership.com>
Mon, 13 Oct 2008 13:28:58 +0000 (09:28 -0400)
The old detection code couldn't handle all possible combinations of
DIX and DIF.  This version does, giving priority to DIX if the
controller is capable.

Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
drivers/scsi/sd.c
drivers/scsi/sd.h
drivers/scsi/sd_dif.c

index a494a2e..7c4d2e6 100644 (file)
@@ -575,7 +575,8 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
 
        /* If DIF or DIX is enabled, tell HBA how to handle request */
        if (host_dif || scsi_prot_sg_count(SCpnt))
-               sd_dif_op(SCpnt, sdkp->protection_type, scsi_prot_sg_count(SCpnt));
+               sd_dif_op(SCpnt, host_dif, scsi_prot_sg_count(SCpnt),
+                         sdkp->protection_type);
 
        /*
         * We shouldn't disconnect in the middle of a sector, so with a dumb
index 95b9f06..a92b991 100644 (file)
@@ -99,7 +99,7 @@ struct sd_dif_tuple {
 
 #if defined(CONFIG_BLK_DEV_INTEGRITY)
 
-extern void sd_dif_op(struct scsi_cmnd *, unsigned int, unsigned int);
+extern void sd_dif_op(struct scsi_cmnd *, unsigned int, unsigned int, unsigned int);
 extern void sd_dif_config_host(struct scsi_disk *);
 extern int sd_dif_prepare(struct request *rq, sector_t, unsigned int);
 extern void sd_dif_complete(struct scsi_cmnd *, unsigned int);
index 943fde7..194c770 100644 (file)
@@ -311,24 +311,26 @@ void sd_dif_config_host(struct scsi_disk *sdkp)
        struct scsi_device *sdp = sdkp->device;
        struct gendisk *disk = sdkp->disk;
        u8 type = sdkp->protection_type;
+       int dif, dix;
 
-       /* If this HBA doesn't support DIX, resort to normal I/O or DIF */
-       if (scsi_host_dix_capable(sdp->host, type) == 0) {
+       dif = scsi_host_dif_capable(sdp->host, type);
+       dix = scsi_host_dix_capable(sdp->host, type);
 
-               if (type == SD_DIF_TYPE0_PROTECTION)
-                       return;
-
-               if (scsi_host_dif_capable(sdp->host, type) == 0) {
-                       sd_printk(KERN_INFO, sdkp, "Type %d protection " \
-                                 "unsupported by HBA. Disabling DIF.\n", type);
-                       return;
-               }
+       if (!dix && scsi_host_dix_capable(sdp->host, 0)) {
+               dif = 0; dix = 1;
+       }
 
-               sd_printk(KERN_INFO, sdkp, "Enabling DIF Type %d protection\n",
-                         type);
+       if (type) {
+               if (dif)
+                       sd_printk(KERN_INFO, sdkp,
+                                 "Enabling DIF Type %d protection\n", type);
+               else
+                       sd_printk(KERN_INFO, sdkp,
+                                 "Disabling DIF Type %d protection\n", type);
+       }
 
+       if (!dix)
                return;
-       }
 
        /* Enable DMA of protection information */
        if (scsi_host_get_guard(sdkp->device->host) & SHOST_DIX_GUARD_IP)
@@ -343,10 +345,10 @@ void sd_dif_config_host(struct scsi_disk *sdkp)
                        blk_integrity_register(disk, &dif_type1_integrity_crc);
 
        sd_printk(KERN_INFO, sdkp,
-                 "Enabling %s integrity protection\n", disk->integrity->name);
+                 "Enabling DIX %s protection\n", disk->integrity->name);
 
        /* Signal to block layer that we support sector tagging */
-       if (type && sdkp->ATO) {
+       if (dif && type && sdkp->ATO) {
                if (type == SD_DIF_TYPE3_PROTECTION)
                        disk->integrity->tag_size = sizeof(u16) + sizeof(u32);
                else
@@ -360,7 +362,7 @@ void sd_dif_config_host(struct scsi_disk *sdkp)
 /*
  * DIF DMA operation magic decoder ring.
  */
-void sd_dif_op(struct scsi_cmnd *scmd, unsigned int dif, unsigned int dix)
+void sd_dif_op(struct scsi_cmnd *scmd, unsigned int dif, unsigned int dix, unsigned int type)
 {
        int csum_convert, prot_op;
 
@@ -405,7 +407,8 @@ void sd_dif_op(struct scsi_cmnd *scmd, unsigned int dif, unsigned int dix)
        }
 
        scsi_set_prot_op(scmd, prot_op);
-       scsi_set_prot_type(scmd, dif);
+       if (dif)
+               scsi_set_prot_type(scmd, type);
 }
 
 /*