[PATCH 2/2] Add SBC UNMAP command and thin provisioning to tgtd

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

 



Tomo,

Please find attached a patch to TGTD that adds emulation of SBC UNMAP command.

The unmap command is implemented so that when the LUN attribute
"--params thin_provisioning=1" is set
it will try to release the affected region of the LUN image by calling
fallocate(... FALLOC_FL_PUNCH_HOLE ...) to release the blocks back to
the underlying filesystem.
This only works for some filesystems under linux at this point.


READ_CAPACITY16 is updated to set LPBME and LBPRZ for thin-provisioned LUNs.


VPD page b2 is added which reports the support for provisioning.
When the thin_provisioning attribute is updated we update the VPD page
accordingly too.


regards
ronnie sahlberg

Attachment: 0002-SBC-UNMAP-Add-support-for-thin-provisioning-and-the-.patch.gz
Description: GNU Zip compressed data

From 152c5a8947828f0f1681d4f1ab1b91ed7567a1fe Mon Sep 17 00:00:00 2001
From: Ronnie Sahlberg <ronniesahlberg@xxxxxxxxx>
Date: Sat, 31 Mar 2012 14:10:39 +1100
Subject: [PATCH 2/2] SBC UNMAP: Add support for thin-provisioning and the UNMAP command.

The UNMAP command is implemented using FALLOC_FL_PUNCH_HOLE and will
release UNMAPPED blocks back to the underlying filesystem.

FALLOC_FL_PUNCH_HOLE is fairly new addition to Linux but works on
ext4 and XFS filesystems currently.

Signed-off-by: Ronnie Sahlberg <ronniesahlberg@xxxxxxxxx>
---
 doc/tgtadm.8.xml |   25 +++++++++++++++++
 usr/bs_rdwr.c    |   45 ++++++++++++++++++++++++++++++++
 usr/sbc.c        |   76 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 usr/scsi.h       |    1 +
 usr/spc.c        |   43 +++++++++++++++++++++++++++---
 usr/target.c     |    2 +
 usr/tgtd.h       |    2 +
 7 files changed, 189 insertions(+), 5 deletions(-)

diff --git a/doc/tgtadm.8.xml b/doc/tgtadm.8.xml
index 668e184..a40f659 100644
--- a/doc/tgtadm.8.xml
+++ b/doc/tgtadm.8.xml
@@ -352,6 +352,31 @@ tgtadm --lld iscsi --mode logicalunit --op update --tid 1 --lun 1 \
          --params readonly=1
       </screen>
 
+      <varlistentry><term><option>thin_provisioning=&lt;0|1&gt;</option></term>
+        <listitem>
+          <para>
+	    This controls the provisioning for the LUN. A thin-provisioned
+	    LUN is represented as a sparse file.
+	    TGTD supports provisioning type 2 for sparse files.
+	    When initiators use the SCSI UNMAP command TGTD will release
+	    the affected areas back to the filesystem using 
+	    FALLOC_FL_PUNCH_HOLE.
+          </para>
+          <para>
+	    This parameter only applies to DISK devices.
+          </para>
+          <para>
+	    Thin-provisioning only works for LUNs stored on filesystems
+	    that support FALLOC_FL_PUNCH_HOLE.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <screen format="linespecific">
+tgtadm --lld iscsi --mode logicalunit --op update --tid 1 --lun 1 \
+         --params thin_provisioning=1
+      </screen>
+
     </variablelist>
   </refsect1>
 
diff --git a/usr/bs_rdwr.c b/usr/bs_rdwr.c
index 84ed278..d3b6bec 100644
--- a/usr/bs_rdwr.c
+++ b/usr/bs_rdwr.c
@@ -23,6 +23,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/falloc.h>
 #include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -160,6 +161,50 @@ static void bs_rdwr_request(struct scsi_cmd *cmd)
 
 		free(tmpbuf);
 		break;
+#ifdef FALLOC_FL_PUNCH_HOLE
+	case UNMAP:
+		length = scsi_get_out_length(cmd);
+		tmpbuf = scsi_get_out_buffer(cmd);
+
+		if (length < 8)
+			break;
+
+		length -= 8;
+		tmpbuf += 8;
+
+		while (length >= 16) {
+			uint64_t lba;
+			uint64_t tl;
+
+			lba = (uint64_t)tmpbuf[0] << 56
+			    | (uint64_t)tmpbuf[1] << 48
+			    | (uint64_t)tmpbuf[2] << 40
+			    | (uint64_t)tmpbuf[3] << 32
+			    | (uint64_t)tmpbuf[4] << 24
+			    | (uint64_t)tmpbuf[5] << 16
+			    | (uint64_t)tmpbuf[6] << 8
+			    | (uint64_t)tmpbuf[7];
+
+			tl  = (uint64_t)tmpbuf[8] << 24
+			    | (uint64_t)tmpbuf[9] << 16
+			    | (uint64_t)tmpbuf[10] << 8
+			    | (uint64_t)tmpbuf[11];
+
+			if (fallocate(fd,
+			    FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE,
+			    lba << cmd->dev->blk_shift,
+			    tl  << cmd->dev->blk_shift) != 0) {
+				eprintf("Failed to punch hole for UNMAP"
+						" at LBA:" PRIu64
+						" TL: " PRIu64 "\n",
+						lba, tl);
+			}
+
+			length -= 16;
+			tmpbuf += 16;
+		}
+		break;
+#endif
 	default:
 		break;
 	}
diff --git a/usr/sbc.c b/usr/sbc.c
index 0c3681e..c93d5de 100644
--- a/usr/sbc.c
+++ b/usr/sbc.c
@@ -141,6 +141,60 @@ sense:
 	return SAM_STAT_CHECK_CONDITION;
 }
 
+static int sbc_unmap(int host_no, struct scsi_cmd *cmd)
+{
+	int ret;
+	unsigned char key = ILLEGAL_REQUEST;
+	uint16_t asc = ASC_LUN_NOT_SUPPORTED;
+	struct scsi_lu *lu = cmd->dev;
+	int anchor;
+
+	ret = device_reserved(cmd);
+	if (ret)
+		return SAM_STAT_RESERVATION_CONFLICT;
+
+	/* We dont support anchored blocks */
+	anchor = cmd->scb[1] & 0x01;
+	if (anchor) {
+		key = ILLEGAL_REQUEST;
+		asc = ASC_INVALID_FIELD_IN_CDB;
+		goto sense;
+	}
+
+	if (!lu->attrs.thinprovisioning) {
+		key = ILLEGAL_REQUEST;
+		asc = ASC_INVALID_OP_CODE;
+		goto sense;
+	}
+
+	if (lu->attrs.removable && !lu->attrs.online) {
+		key = NOT_READY;
+		asc = ASC_MEDIUM_NOT_PRESENT;
+		goto sense;
+	}
+
+	if (lu->attrs.readonly) {
+		key = DATA_PROTECT;
+		asc = ASC_WRITE_PROTECT;
+		goto sense;
+	}
+
+	ret = cmd->dev->bst->bs_cmd_submit(cmd);
+	if (ret) {
+		key = HARDWARE_ERROR;
+		asc = ASC_INTERNAL_TGT_FAILURE;
+		goto sense;
+	}
+
+sense:
+	cmd->offset = 0;
+	scsi_set_in_resid_by_actual(cmd, 0);
+	scsi_set_out_resid_by_actual(cmd, 0);
+
+	sense_data_build(cmd, key, asc);
+	return SAM_STAT_CHECK_CONDITION;
+}
+
 static int sbc_rw(int host_no, struct scsi_cmd *cmd)
 {
 	int ret;
@@ -370,8 +424,11 @@ static int sbc_service_action(int host_no, struct scsi_cmd *cmd)
 	data[2] = __cpu_to_be32(1UL << bshift);
 
 	val = (cmd->dev->attrs.lbppbe << 16) | cmd->dev->attrs.la_lba;
+	if (cmd->dev->attrs.thinprovisioning)
+		val |= (3 << 14); /* set LBPME and LBPRZ */
 	data[3] = __cpu_to_be32(val);
 
+
 overflow:
 	scsi_set_in_resid_by_actual(cmd, len);
 	return SAM_STAT_GOOD;
@@ -549,7 +606,24 @@ static struct device_type_template sbc_template = {
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 
-		[0x40 ... 0x4f] = {spc_illegal_op,},
+		/* 0x40 */
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{sbc_unmap,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
 
 		/* 0x50 */
 		{spc_illegal_op,},
diff --git a/usr/scsi.h b/usr/scsi.h
index ca1109a..1508cc6 100644
--- a/usr/scsi.h
+++ b/usr/scsi.h
@@ -60,6 +60,7 @@
 #define WRITE_LONG            0x3f
 #define CHANGE_DEFINITION     0x40
 #define WRITE_SAME            0x41
+#define UNMAP		      0x42
 #define READ_TOC              0x43
 #define LOG_SELECT            0x4c
 #define LOG_SENSE             0x4d
diff --git a/usr/spc.c b/usr/spc.c
index 44cd193..1834038 100644
--- a/usr/spc.c
+++ b/usr/spc.c
@@ -139,6 +139,24 @@ static void update_vpd_83(struct scsi_lu *lu, void *id)
 	strncpy((char *)data + 4, id, SCSI_ID_LEN);
 }
 
+static void update_vpd_b2(struct scsi_lu *lu, void *id)
+{
+	struct vpd *vpd_pg = lu->attrs.lu_vpd[PCODE_OFFSET(0xb2)];
+	uint8_t	*data = vpd_pg->data;
+
+	if (lu->attrs.thinprovisioning) {
+		data[0] = 0;		/* threshold exponent */
+		data[1] = 0x84;		/* LBPU LBPRZ */
+		data[2] = 0x02;		/* provisioning type */
+		data[3] = 0;
+	} else {
+		data[0] = 0;
+		data[1] = 0;
+		data[2] = 0;
+		data[3] = 0;
+	}
+}
+
 static void update_b0_opt_xfer_gran(struct scsi_lu *lu, int opt_xfer_gran)
 {
 	struct vpd *vpd_pg = lu->attrs.lu_vpd[PCODE_OFFSET(0xb0)];
@@ -1661,7 +1679,7 @@ enum {
 	Opt_removable, Opt_readonly, Opt_online,
 	Opt_mode_page,
 	Opt_path,
-	Opt_bsoflags,
+	Opt_bsoflags, Opt_thinprovisioning,
 	Opt_err,
 };
 
@@ -1682,6 +1700,7 @@ static match_table_t tokens = {
 	{Opt_mode_page, "mode_page=%s"},
 	{Opt_path, "path=%s"},
 	{Opt_bsoflags, "bsoflags=%s"},
+	{Opt_thinprovisioning, "thin_provisioning=%s"},
 	{Opt_err, NULL},
 };
 
@@ -1772,6 +1791,12 @@ tgtadm_err lu_config(struct scsi_lu *lu, char *params, match_fn_t *fn)
 			match_strncpy(buf, &args[0], sizeof(buf));
 			attrs->readonly = atoi(buf);
 			break;
+		case Opt_thinprovisioning:
+			match_strncpy(buf, &args[0], sizeof(buf));
+			attrs->thinprovisioning = atoi(buf);
+			/* update the provisioning vpd page */
+			lu_vpd[PCODE_OFFSET(0xb2)]->vpd_update(lu, NULL);
+			break;
 		case Opt_online:
 			match_strncpy(buf, &args[0], sizeof(buf));
 			if (atoi(buf))
@@ -1807,6 +1832,10 @@ int spc_lu_init(struct scsi_lu *lu)
 
 	lu->attrs.device_type = lu->dev_type_template.type;
 	lu->attrs.qualifier = 0x0;
+	lu->attrs.thinprovisioning = 0;
+	lu->attrs.removable = 0;
+	lu->attrs.readonly = 0;
+	lu->attrs.sense_format = 0;
 
 	snprintf(lu->attrs.vendor_id, sizeof(lu->attrs.vendor_id),
 		 "%-16s", VENDOR_ID);
@@ -1839,9 +1868,15 @@ int spc_lu_init(struct scsi_lu *lu)
 	if (!lu_vpd[pg])
 		return -ENOMEM;
 
-	lu->attrs.removable = 0;
-	lu->attrs.readonly = 0;
-	lu->attrs.sense_format = 0;
+	/* VPD page 0xb2 LOGICAL BLOCK PROVISIONING*/
+	pg = PCODE_OFFSET(0xb2);
+	lu_vpd[pg] = alloc_vpd(LBP_VPD_LEN);
+	if (!lu_vpd[pg])
+		return -ENOMEM;
+	lu_vpd[pg]->vpd_update = update_vpd_b2;
+	lu_vpd[pg]->vpd_update(lu, NULL);
+
+
 	lu->dev_type_template.lu_offline(lu);
 
 	return 0;
diff --git a/usr/target.c b/usr/target.c
index 0b6be13..dd3ca91 100644
--- a/usr/target.c
+++ b/usr/target.c
@@ -1857,6 +1857,7 @@ tgtadm_err tgt_target_show_all(struct concat_buf *b)
 				 _TAB3 "Removable media: %s\n"
 				 _TAB3 "Prevent removal: %s\n"
 				 _TAB3 "Readonly: %s\n"
+				 _TAB3 "Thin-provisioning: %s\n"
 				 _TAB3 "Backing store type: %s\n"
 				 _TAB3 "Backing store path: %s\n"
 				 _TAB3 "Backing store flags: %s\n",
@@ -1871,6 +1872,7 @@ tgtadm_err tgt_target_show_all(struct concat_buf *b)
 				 lu_prevent_removal(lu) ?
 					"Yes" : "No",
 				 lu->attrs.readonly ? "Yes" : "No",
+				 lu->attrs.thinprovisioning ? "Yes" : "No",
 				 lu->bst ?
 					(lu->bst->bs_name ? : "Unknown") :
 					"None",
diff --git a/usr/tgtd.h b/usr/tgtd.h
index 726a3f5..03036ba 100644
--- a/usr/tgtd.h
+++ b/usr/tgtd.h
@@ -14,6 +14,7 @@ struct concat_buf;
 #define PRODUCT_ID_LEN		16
 #define PRODUCT_REV_LEN		4
 #define BLOCK_LIMITS_VPD_LEN	0x3C
+#define LBP_VPD_LEN		4
 
 #define PCODE_SHIFT		7
 #define PCODE_OFFSET(x) (x & ((1 << PCODE_SHIFT) - 1))
@@ -72,6 +73,7 @@ struct lu_phy_attr {
 	char qualifier;		/* Peripheral Qualifier */
 	char removable;		/* Removable media */
 	char readonly;          /* Read-Only media */
+	char thinprovisioning;  /* Use thin-provisioning for this LUN */
 	char online;		/* Logical Unit online */
 	char sense_format;	/* Descrptor format sense data supported */
 				/* For the following see READ CAPACITY (16) */
-- 
1.7.3.1


[Index of Archives]     [Linux SCSI]     [Linux RAID]     [Linux Clusters]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]

  Powered by Linux