Blob Blame History Raw
From 0c351a48bedf100ecc8cf10db3e295c6d377b5bf Mon Sep 17 00:00:00 2001
From: David Disseldorp <ddiss@suse.de>
Date: Thu, 1 Oct 2020 01:16:31 +0200
Subject: [PATCH] target/rbd: conditionally fix off-by-one bug in get_blocks()
References: bsc#1177109
Patch-mainline: Not yet, SES clustered LIO/RBD

SCSI READ CAPACITY responses carry an LBA which corresponds to "the LBA
of the last logical block on the direct access block device...". LIO's
backstore API carries a .get_blocks() hook, which is used directly when
filling this response. Unlike the iblock and file backstores, the RBD
backend doesn't subtract one block from the block-size-translated image
mapping size, resulting in initiators being advertised one block more
than is actually carried.

The fixed behaviour is conditional based on the emulate_legacy_capacity
device attribute, which defaults to 1 (retain off-by-one capacity). It
can be explicitly changed via configfs. The reason to make this user
configurable is so that LIO administrators can resize legacy images to
allow access to the off-by-one end of LUN block alongside disabling the
legacy capacity reporting.

Signed-off-by: David Disseldorp <ddiss@suse.de>
Reviewed-by: Roman Penyaev <rpenyaev@suse.com>
---
 drivers/target/target_core_rbd.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/drivers/target/target_core_rbd.c b/drivers/target/target_core_rbd.c
index 0ac02631faff..d0c2d7ac4975 100644
--- a/drivers/target/target_core_rbd.c
+++ b/drivers/target/target_core_rbd.c
@@ -155,6 +155,10 @@ static sector_t tcm_rbd_get_blocks(struct se_device *dev)
 	u64 blocks_long = div_u64(tcm_rbd_dev->rbd_dev->mapping.size,
 				  dev->dev_attrib.block_size);
 
+	/* READ CAPACITY should return the LUN's last LBA */
+	if (blocks_long && !tcm_rbd_dev->emulate_legacy_capacity)
+		blocks_long -= 1;
+
 	return blocks_long;
 }
 
@@ -259,6 +263,7 @@ tcm_rbd_execute_cmd(struct se_cmd *cmd, struct rbd_device *rbd_dev,
 		    enum obj_operation_type op_type,
 		    u64 offset, u64 length, bool sync)
 {
+	struct tcm_rbd_dev *tcm_rbd_dev = TCM_RBD_DEV(cmd->se_dev);
 	struct tcm_rbd_cmd *trc;
 	struct rbd_img_request *img_request;
 	struct ceph_snap_context *snapc = NULL;
@@ -313,8 +318,10 @@ tcm_rbd_execute_cmd(struct se_cmd *cmd, struct rbd_device *rbd_dev,
 	if (offset + length > mapping_size) {
 		pr_warn("beyond EOD (%llu~%llu > %llu)", offset,
 			length, mapping_size);
-		sense = TCM_ADDRESS_OUT_OF_RANGE;
-		goto err_snapc;
+		if (!tcm_rbd_dev->emulate_legacy_capacity) {
+			sense = TCM_ADDRESS_OUT_OF_RANGE;
+			goto err_snapc;
+		}
 	}
 
 	trc = kzalloc(sizeof(struct tcm_rbd_cmd), GFP_KERNEL);
-- 
2.26.2