[PATCH 1/2] sd: Rework ZBC integration

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Remove most of the zone request specific code from sd_done() and
move it to sd_zbc_complete(), which is called at the end of sd_done()
so that good_bytes is set for REQ_OP_ZONE_REPORT and REQ_OP_ZONE_RESET.
In sd_zbc_complete(), enhance checks on the value of good_bytes for
report zones to avoid eventual problems with bogus hardware.

Signed-off-by: Damien Le Moal <damien.lemoal@xxxxxxx>
---
 drivers/scsi/sd.c     |  36 ++++++------------
 drivers/scsi/sd.h     |  11 +++---
 drivers/scsi/sd_zbc.c | 101 +++++++++++++++++++++++++++++++++-----------------
 3 files changed, 84 insertions(+), 64 deletions(-)

diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index c7839f6..1bcd80a 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -1786,15 +1786,11 @@ static int sd_done(struct scsi_cmnd *SCpnt)
 	struct scsi_sense_hdr sshdr;
 	struct scsi_disk *sdkp = scsi_disk(SCpnt->request->rq_disk);
 	struct request *req = SCpnt->request;
-	int sense_valid = 0;
-	int sense_deferred = 0;
-	unsigned char op = SCpnt->cmnd[0];
-	unsigned char unmap = SCpnt->cmnd[1] & 8;
+	bool sense_valid = false;
 
 	switch (req_op(req)) {
 	case REQ_OP_DISCARD:
 	case REQ_OP_WRITE_SAME:
-	case REQ_OP_ZONE_RESET:
 		if (!result) {
 			good_bytes = blk_rq_bytes(req);
 			scsi_set_resid(SCpnt, 0);
@@ -1803,27 +1799,15 @@ static int sd_done(struct scsi_cmnd *SCpnt)
 			scsi_set_resid(SCpnt, blk_rq_bytes(req));
 		}
 		break;
-	case REQ_OP_ZONE_REPORT:
-		if (!result) {
-			good_bytes = scsi_bufflen(SCpnt)
-				- scsi_get_resid(SCpnt);
-			scsi_set_resid(SCpnt, 0);
-		} else {
-			good_bytes = 0;
-			scsi_set_resid(SCpnt, blk_rq_bytes(req));
-		}
-		break;
 	}
 
 	if (result) {
-		sense_valid = scsi_command_normalize_sense(SCpnt, &sshdr);
-		if (sense_valid)
-			sense_deferred = scsi_sense_is_deferred(&sshdr);
+		sense_valid = scsi_command_normalize_sense(SCpnt, &sshdr) &&
+			!scsi_sense_is_deferred(&sshdr);
 	}
 	sdkp->medium_access_timed_out = 0;
 
-	if (driver_byte(result) != DRIVER_SENSE &&
-	    (!sense_valid || sense_deferred))
+	if (driver_byte(result) != DRIVER_SENSE && !sense_valid)
 		goto out;
 
 	switch (sshdr.sense_key) {
@@ -1847,10 +1831,14 @@ static int sd_done(struct scsi_cmnd *SCpnt)
 			good_bytes = sd_completed_bytes(SCpnt);
 		break;
 	case ILLEGAL_REQUEST:
-		if (sshdr.asc == 0x10)  /* DIX: Host detected corruption */
+		if (sshdr.asc == 0x10) {
+			/* DIX: Host detected corruption */
 			good_bytes = sd_completed_bytes(SCpnt);
-		/* INVALID COMMAND OPCODE or INVALID FIELD IN CDB */
-		if (sshdr.asc == 0x20 || sshdr.asc == 0x24) {
+		} else if (sshdr.asc == 0x20 || sshdr.asc == 0x24) {
+			unsigned char op = SCpnt->cmnd[0];
+			unsigned char unmap = SCpnt->cmnd[1] & 8;
+
+			/* INVALID COMMAND OPCODE or INVALID FIELD IN CDB */
 			switch (op) {
 			case UNMAP:
 				sd_config_discard(sdkp, SD_LBP_DISABLE);
@@ -1876,7 +1864,7 @@ static int sd_done(struct scsi_cmnd *SCpnt)
 
  out:
 	if (sd_is_zoned(sdkp))
-		sd_zbc_complete(SCpnt, good_bytes, &sshdr);
+		good_bytes = sd_zbc_complete(SCpnt, good_bytes, &sshdr);
 
 	SCSI_LOG_HLCOMPLETE(1, scmd_printk(KERN_INFO, SCpnt,
 					   "sd_done: completed %d of %d bytes\n",
diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h
index 4dac35e..0fd4886 100644
--- a/drivers/scsi/sd.h
+++ b/drivers/scsi/sd.h
@@ -270,8 +270,9 @@ extern int sd_zbc_setup_write_cmnd(struct scsi_cmnd *cmd);
 extern void sd_zbc_cancel_write_cmnd(struct scsi_cmnd *cmd);
 extern int sd_zbc_setup_report_cmnd(struct scsi_cmnd *cmd);
 extern int sd_zbc_setup_reset_cmnd(struct scsi_cmnd *cmd);
-extern void sd_zbc_complete(struct scsi_cmnd *cmd, unsigned int good_bytes,
-			    struct scsi_sense_hdr *sshdr);
+extern unsigned int sd_zbc_complete(struct scsi_cmnd *cmd,
+				    unsigned int good_bytes,
+				    struct scsi_sense_hdr *sshdr);
 
 #else /* CONFIG_BLK_DEV_ZONED */
 
@@ -303,9 +304,9 @@ static inline int sd_zbc_setup_reset_cmnd(struct scsi_cmnd *cmd)
 	return BLKPREP_INVALID;
 }
 
-static inline void sd_zbc_complete(struct scsi_cmnd *cmd,
-				   unsigned int good_bytes,
-				   struct scsi_sense_hdr *sshdr) {}
+static inline unsigned int sd_zbc_complete(struct scsi_cmnd *cmd,
+					   unsigned int good_bytes,
+					   struct scsi_sense_hdr *sshdr) {}
 
 #endif /* CONFIG_BLK_DEV_ZONED */
 
diff --git a/drivers/scsi/sd_zbc.c b/drivers/scsi/sd_zbc.c
index 92620c8..789c970 100644
--- a/drivers/scsi/sd_zbc.c
+++ b/drivers/scsi/sd_zbc.c
@@ -177,8 +177,18 @@ static void sd_zbc_report_zones_complete(struct scsi_cmnd *scmd,
 	unsigned long flags;
 	u8 *buf;
 
-	if (good_bytes < 64)
-		return;
+	/*
+	 * Make sure good_bytes make sense and is aligned on 64 B.
+	 * If it is less than 64B, force the nr_zones field of the
+	 * report header to become 0 by setting good_bytes to 64.
+	 */
+	if (!good_bytes || good_bytes & 0x3f) {
+		sdev_printk(KERN_INFO, scmd->device,
+			"Invalid report zone result (xfer=%u, resid=%u)\n",
+			scsi_bufflen(scmd), scsi_get_resid(scmd));
+		good_bytes = max_t(unsigned int,
+				   round_down(good_bytes, 64), 64);
+	}
 
 	memset(&hdr, 0, sizeof(struct blk_zone_report_hdr));
 
@@ -320,55 +330,76 @@ void sd_zbc_cancel_write_cmnd(struct scsi_cmnd *cmd)
 	sd_zbc_unlock_zone(cmd->request);
 }
 
-void sd_zbc_complete(struct scsi_cmnd *cmd,
-		     unsigned int good_bytes,
-		     struct scsi_sense_hdr *sshdr)
+unsigned int sd_zbc_complete(struct scsi_cmnd *scmd,
+			     unsigned int good_bytes,
+			     struct scsi_sense_hdr *sshdr)
 {
-	int result = cmd->result;
-	struct request *rq = cmd->request;
+	int result = scmd->result;
+	struct request *req = scmd->request;
+
+	switch (req_op(req)) {
+
+	case REQ_OP_ZONE_REPORT:
+
+		if (result) {
+			/* Failed */
+			good_bytes = 0;
+			scsi_set_resid(scmd, blk_rq_bytes(req));
+			break;
+		}
+
+		/* Check good bytes */
+		if (good_bytes >= scsi_get_resid(scmd))
+			good_bytes -= scsi_get_resid(scmd);
+		sd_zbc_report_zones_complete(scmd, good_bytes);
+		scsi_set_resid(scmd, 0);
+
+		break;
+
+	case REQ_OP_ZONE_RESET:
+
+		if (!result) {
+			good_bytes = blk_rq_bytes(req);
+			scsi_set_resid(scmd, 0);
+		} else {
+			/* Failed */
+			good_bytes = 0;
+			scsi_set_resid(scmd, blk_rq_bytes(req));
+			if (sshdr->sense_key == ILLEGAL_REQUEST &&
+			    sshdr->asc == 0x24)
+				/*
+				 * ILLEGAL REQUEST / INVALID FIELD IN CDB
+				 * For a zone reset, this means that a reset
+				 * of a conventional zone was attempted.
+				 * Nothing to worry about, so be quiet about
+				 * the error.
+				 */
+				req->rq_flags |= RQF_QUIET;
+		}
+
+		/* Fallthru (for unlocking the zone) */
 
-	switch (req_op(rq)) {
 	case REQ_OP_WRITE:
 	case REQ_OP_WRITE_SAME:
-	case REQ_OP_ZONE_RESET:
 
 		/* Unlock the zone */
-		sd_zbc_unlock_zone(rq);
+		sd_zbc_unlock_zone(req);
 
-		if (!result ||
-		    sshdr->sense_key != ILLEGAL_REQUEST)
-			break;
-
-		switch (sshdr->asc) {
-		case 0x24:
-			/*
-			 * INVALID FIELD IN CDB error: For a zone reset,
-			 * this means that a reset of a conventional
-			 * zone was attempted. Nothing to worry about in
-			 * this case, so be quiet about the error.
-			 */
-			if (req_op(rq) == REQ_OP_ZONE_RESET)
-				rq->rq_flags |= RQF_QUIET;
-			break;
-		case 0x21:
+		if (!result &&
+		    sshdr->sense_key == ILLEGAL_REQUEST &&
+		    sshdr->asc == 0x21)
 			/*
 			 * INVALID ADDRESS FOR WRITE error: It is unlikely that
 			 * retrying write requests failed with any kind of
 			 * alignement error will result in success. So don't.
 			 */
-			cmd->allowed = 0;
-			break;
-		}
-
-		break;
-
-	case REQ_OP_ZONE_REPORT:
+			scmd->allowed = 0;
 
-		if (!result)
-			sd_zbc_report_zones_complete(cmd, good_bytes);
 		break;
 
 	}
+
+	return good_bytes;
 }
 
 /**
-- 
2.9.3




[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]

  Powered by Linux