|
|
|
[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=<0|1></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
![]() |
![]() |