Skip to contentSkip to conversation

scsi_debug: bug fixes and cleanups for data integrity support

PATCH Superseded Akinobu Mita 1 participant 6 messages May 19, 2013 SCSI

v2 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

v2 cover scsi_debug: bug fixes and cleanups for data integrity support

This patch set includes bug fixes which I hit when I was tried testing the data integrity support in scsi_debug on x86_32.

And it also includes cleanups which helps increasing readability and further bug fixing in data integrity support.

* Changes from v1
- Split the patch "fix data integrity support on highmem machine" into two separate patches.
- Add new cleanup patch "reduce duplication between prot_verify_read and prot_verify_write".

Cc "James E.J. Bottomley" <[email protected]>
Cc Douglas Gilbert <[email protected]>
Cc "Martin K. Petersen" <[email protected]>

Akinobu Mita (5):
scsi_debug: fix invalid address passed to kunmap_atomic()
scsi_debug: fix incorrectly nested kmap_atomic()
scsi_debug: fix NULL pointer dereference with parameters dif=0 dix=1
scsi_debug: simplify offset calculation for dif_storep
scsi_debug: reduce duplication between prot_verify_read and
prot_verify_write

 drivers/scsi/scsi_debug.c | 164 +++++++++++++++++++---------------------------
 1 file changed, 66 insertions(+), 98 deletions(-)
Signature · 1 line
1.8.1.4
#1 lore raw

PATCH v2 1/5 scsi_debug: fix invalid address passed to kunmap_atomic()

In the function prot_verify_write(), the kmap address 'daddr' is incremented in the loop for each data page. Finally 'daddr' reaches the next page boundary in the end of the loop, and the invalid address is passed to kunmap_atomic().

Fix it by passing original value to kunmap_atomic().

Cc "James E.J. Bottomley" <[email protected]>
Cc Douglas Gilbert <[email protected]>
Cc "Martin K. Petersen" <[email protected]>

---

* New patch from v2
- Splitted from the patch "fix data integrity support on highmem machine" in v1

 drivers/scsi/scsi_debug.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/scsi/scsi_debug.c +1 -1
index 0a537a0..caf6a94 100644
@@ -1981,7 +1981,7 @@ static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec,
 			ppage_offset += sizeof(struct sd_dif_tuple);
 		}
 
-		kunmap_atomic(daddr);
+		kunmap_atomic(daddr - dsgl->length);
 	}
 
 	kunmap_atomic(paddr);
Signature · 1 line
1.8.1.4
#2 lore raw

PATCH v2 2/5 scsi_debug: fix incorrectly nested kmap_atomic()

In reply to Akinobu Mita

In the function prot_verify_write(), kmap_atomic()/kunmap_atomic() for data page and kmap_atomic()/kunmap_atomic() for protection information page are not nested each other.

It worked perfectly before commit 3e4d3af501cccdc8a8cca41bdbe57d54ad7e7e73 ("mm: stack based kmap_atomic()"). Because the kmap_atomic slot KM_IRQ0 was used for data page and the slot KM_IRQ1 was used for protection page.

But KM_types are gone and kmap_atomic() is using stack based implementation. So two different kmap_atomic() usages must be strictly nested now.

This change ensures kmap_atomic() usage is strictly nested.

Cc "James E.J. Bottomley" <[email protected]>
Cc Douglas Gilbert <[email protected]>
Cc "Martin K. Petersen" <[email protected]>

---

* New patch from v2
- Splitted from the patch "fix data integrity support on highmem machine" in v1

 drivers/scsi/scsi_debug.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)
drivers/scsi/scsi_debug.c +3 -4
index caf6a94..0a428b6 100644
@@ -1891,12 +1891,12 @@ static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec,
 	BUG_ON(scsi_sg_count(SCpnt) == 0);
 	BUG_ON(scsi_prot_sg_count(SCpnt) == 0);
 
-	paddr = kmap_atomic(sg_page(psgl)) + psgl->offset;
 	ppage_offset = 0;
 
 	/* For each data page */
 	scsi_for_each_sg(SCpnt, dsgl, scsi_sg_count(SCpnt), i) {
 		daddr = kmap_atomic(sg_page(dsgl)) + dsgl->offset;
+		paddr = kmap_atomic(sg_page(psgl)) + psgl->offset;
 
 		/* For each sector-sized chunk in data page */
 		for (j = 0 ; j < dsgl->length ; j += scsi_debug_sector_size) {
@@ -1981,19 +1981,18 @@ static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec,
 			ppage_offset += sizeof(struct sd_dif_tuple);
 		}
 
+		kunmap_atomic(paddr);
 		kunmap_atomic(daddr - dsgl->length);
 	}
 
-	kunmap_atomic(paddr);
-
 	dix_writes++;
 
 	return 0;
 
 out:
 	dif_errors++;
-	kunmap_atomic(daddr);
 	kunmap_atomic(paddr);
+	kunmap_atomic(daddr);
 	return ret;
 }
 
Signature · 1 line
1.8.1.4
#3 lore raw

PATCH v2 3/5 scsi_debug: fix NULL pointer dereference with parameters dif=0 dix=1

In reply to Akinobu Mita

The protection info dif_storep is allocated only when parameter dif is not zero. But it will be accessed when reading or writing to the storage installed with parameter dix is not zero.

So kernel crashes if scsi_debug module is loaded with parameters dix=1 and dif=0.

This fixes it by making dif_storep available if parameter dix is not zero instead of checking if parameter dif is not zero.

---

* No changes from v1

 drivers/scsi/scsi_debug.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/scsi/scsi_debug.c +1 -1
index 0a428b6..631e9ab 100644
@@ -3373,7 +3373,7 @@ static int __init scsi_debug_init(void)
 	if (scsi_debug_num_parts > 0)
 		sdebug_build_parts(fake_storep, sz);
 
-	if (scsi_debug_dif) {
+	if (scsi_debug_dix) {
 		int dif_size;
 
 		dif_size = sdebug_store_sectors * sizeof(struct sd_dif_tuple);
Signature · 1 line
1.8.1.4
#4 lore raw

PATCH v2 4/5 scsi_debug: simplify offset calculation for dif_storep

In reply to Akinobu Mita

dif_storep is declared as pointer to unsigned char type. But it is actually used to store vmalloced array of struct sd_dif_tuple.

This changes the type of dif_storep to the pointer to struct sd_dif_tuple. It simplifies offset calculation for dif_storep and enables to remove hardcoded size of struct sd_dif_tuple.

Cc "James E.J. Bottomley" <[email protected]>
Cc Douglas Gilbert <[email protected]>
Cc "Martin K. Petersen" <[email protected]>

---

* No changes from v1

 drivers/scsi/scsi_debug.c | 18 +++++++-----------
 1 file changed, 7 insertions(+), 11 deletions(-)
drivers/scsi/scsi_debug.c +7 -11
index 631e9ab..152ead6 100644
@@ -258,7 +258,7 @@ struct sdebug_queued_cmd {
 static struct sdebug_queued_cmd queued_arr[SCSI_DEBUG_CANQUEUE];
 
 static unsigned char * fake_storep;	/* ramdisk storage */
-static unsigned char *dif_storep;	/* protection info */
+static struct sd_dif_tuple *dif_storep;	/* protection info */
 static void *map_storep;		/* provisioning map */
 
 static unsigned long map_size;
@@ -277,11 +277,6 @@ static char sdebug_proc_name[] = "scsi_debug";
 
 static struct bus_type pseudo_lld_bus;
 
-static inline sector_t dif_offset(sector_t sector)
-{
-	return sector << 3;
-}
-
 static struct device_driver sdebug_driverfs_driver = {
 	.name 		= sdebug_proc_name,
 	.bus		= &pseudo_lld_bus,
@@ -1727,7 +1722,7 @@ static int prot_verify_read(struct scsi_cmnd *SCpnt, sector_t start_sec,
 
 	start_sec = do_div(tmp_sec, sdebug_store_sectors);
 
-	sdt = (struct sd_dif_tuple *)(dif_storep + dif_offset(start_sec));
+	sdt = dif_storep + start_sec;
 
 	for (i = 0 ; i < sectors ; i++) {
 		u16 csum;
@@ -1782,16 +1777,17 @@ static int prot_verify_read(struct scsi_cmnd *SCpnt, sector_t start_sec,
 		ei_lba++;
 	}
 
-	resid = sectors * 8; /* Bytes of protection data to copy into sgl */
+	/* Bytes of protection data to copy into sgl */
+	resid = sectors * sizeof(*dif_storep);
 	sector = start_sec;
 
 	scsi_for_each_prot_sg(SCpnt, psgl, scsi_prot_sg_count(SCpnt), i) {
 		int len = min(psgl->length, resid);
 
 		paddr = kmap_atomic(sg_page(psgl)) + psgl->offset;
-		memcpy(paddr, dif_storep + dif_offset(sector), len);
+		memcpy(paddr, dif_storep + sector, len);
 
-		sector += len >> 3;
+		sector += len / sizeof(*dif_storep);
 		if (sector >= sdebug_store_sectors) {
 			/* Force wrap */
 			tmp_sec = sector;
@@ -1968,7 +1964,7 @@ static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec,
 			 * correctness we need to verify each sector
 			 * before writing it to "stable" storage
 			 */
-			memcpy(dif_storep + dif_offset(sector), sdt, 8);
+			memcpy(dif_storep + sector, sdt, sizeof(*sdt));
 
 			sector++;
 
Signature · 1 line
1.8.1.4
#5 lore raw

PATCH v2 5/5 scsi_debug: reduce duplication between prot_verify_read and prot_verify_write

In reply to Akinobu Mita

In order to reduce code duplication between prot_verify_read() and prot_verify_write(), this moves common code into the new functions.

Cc "James E.J. Bottomley" <[email protected]>
Cc Douglas Gilbert <[email protected]>
Cc "Martin K. Petersen" <[email protected]>

---

* New patch from v2

 drivers/scsi/scsi_debug.c | 135 +++++++++++++++++++---------------------------
 1 file changed, 54 insertions(+), 81 deletions(-)
drivers/scsi/scsi_debug.c +54 -81
index 152ead6..bd14e12 100644
@@ -1710,6 +1710,50 @@ static int do_device_access(struct scsi_cmnd *scmd,
 	return ret;
 }
 
+static u16 dif_compute_csum(const void *buf, int len)
+{
+	u16 csum;
+
+	switch (scsi_debug_guard) {
+	case 1:
+		csum = ip_compute_csum(buf, len);
+		break;
+	case 0:
+		csum = cpu_to_be16(crc_t10dif(buf, len));
+		break;
+	default:
+		BUG();
+	}
+	return csum;
+}
+
+static int dif_verify(struct sd_dif_tuple *sdt, u16 csum, sector_t sector,
+		      u32 ei_lba)
+{
+	if (sdt->guard_tag != csum) {
+		pr_err("%s: GUARD check failed on sector %lu rcvd 0x%04x, data 0x%04x\n",
+			__func__,
+			(unsigned long)sector,
+			be16_to_cpu(sdt->guard_tag),
+			be16_to_cpu(csum));
+		return 0x01;
+	}
+	if (scsi_debug_dif == SD_DIF_TYPE1_PROTECTION &&
+	    be32_to_cpu(sdt->ref_tag) != (sector & 0xffffffff)) {
+		pr_err("%s: REF check failed on sector %lu\n",
+			__func__, (unsigned long)sector);
+		return 0x03;
+	}
+	if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION &&
+	    be32_to_cpu(sdt->ref_tag) != ei_lba) {
+		pr_err("%s: REF check failed on sector %lu\n",
+			__func__, (unsigned long)sector);
+			dif_errors++;
+		return 0x03;
+	}
+	return 0;
+}
+
 static int prot_verify_read(struct scsi_cmnd *SCpnt, sector_t start_sec,
 			    unsigned int sectors, u32 ei_lba)
 {
@@ -1726,52 +1770,21 @@ static int prot_verify_read(struct scsi_cmnd *SCpnt, sector_t start_sec,
 
 	for (i = 0 ; i < sectors ; i++) {
 		u16 csum;
+		int ret;
 
 		if (sdt[i].app_tag == 0xffff)
 			continue;
 
 		sector = start_sec + i;
 
-		switch (scsi_debug_guard) {
-		case 1:
-			csum = ip_compute_csum(fake_storep +
-					       sector * scsi_debug_sector_size,
-					       scsi_debug_sector_size);
-			break;
-		case 0:
-			csum = crc_t10dif(fake_storep +
-					  sector * scsi_debug_sector_size,
-					  scsi_debug_sector_size);
-			csum = cpu_to_be16(csum);
-			break;
-		default:
-			BUG();
-		}
+		csum = dif_compute_csum(fake_storep +
+					sector * scsi_debug_sector_size,
+					scsi_debug_sector_size);
 
-		if (sdt[i].guard_tag != csum) {
-			printk(KERN_ERR "%s: GUARD check failed on sector %lu" \
-			       " rcvd 0x%04x, data 0x%04x\n", __func__,
-			       (unsigned long)sector,
-			       be16_to_cpu(sdt[i].guard_tag),
-			       be16_to_cpu(csum));
+		ret = dif_verify(&sdt[i], csum, sector, ei_lba);
+		if (ret) {
 			dif_errors++;
-			return 0x01;
-		}
-
-		if (scsi_debug_dif == SD_DIF_TYPE1_PROTECTION &&
-		    be32_to_cpu(sdt[i].ref_tag) != (sector & 0xffffffff)) {
-			printk(KERN_ERR "%s: REF check failed on sector %lu\n",
-			       __func__, (unsigned long)sector);
-			dif_errors++;
-			return 0x03;
-		}
-
-		if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION &&
-		    be32_to_cpu(sdt[i].ref_tag) != ei_lba) {
-			printk(KERN_ERR "%s: REF check failed on sector %lu\n",
-			       __func__, (unsigned long)sector);
-			dif_errors++;
-			return 0x03;
+			return ret;
 		}
 
 		ei_lba++;
@@ -1911,50 +1924,10 @@ static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec,
 
 			sdt = paddr + ppage_offset;
 
-			switch (scsi_debug_guard) {
-			case 1:
-				csum = ip_compute_csum(daddr,
-						       scsi_debug_sector_size);
-				break;
-			case 0:
-				csum = cpu_to_be16(crc_t10dif(daddr,
-						      scsi_debug_sector_size));
-				break;
-			default:
-				BUG();
-				ret = 0;
-				goto out;
-			}
-
-			if (sdt->guard_tag != csum) {
-				printk(KERN_ERR
-				       "%s: GUARD check failed on sector %lu " \
-				       "rcvd 0x%04x, calculated 0x%04x\n",
-				       __func__, (unsigned long)sector,
-				       be16_to_cpu(sdt->guard_tag),
-				       be16_to_cpu(csum));
-				ret = 0x01;
-				dump_sector(daddr, scsi_debug_sector_size);
-				goto out;
-			}
-
-			if (scsi_debug_dif == SD_DIF_TYPE1_PROTECTION &&
-			    be32_to_cpu(sdt->ref_tag)
-			    != (start_sec & 0xffffffff)) {
-				printk(KERN_ERR
-				       "%s: REF check failed on sector %lu\n",
-				       __func__, (unsigned long)sector);
-				ret = 0x03;
-				dump_sector(daddr, scsi_debug_sector_size);
-				goto out;
-			}
+			csum = dif_compute_csum(daddr, scsi_debug_sector_size);
 
-			if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION &&
-			    be32_to_cpu(sdt->ref_tag) != ei_lba) {
-				printk(KERN_ERR
-				       "%s: REF check failed on sector %lu\n",
-				       __func__, (unsigned long)sector);
-				ret = 0x03;
+			ret = dif_verify(sdt, csum, start_sec, ei_lba);
+			if (ret) {
 				dump_sector(daddr, scsi_debug_sector_size);
 				goto out;
 			}
Signature · 1 line
1.8.1.4