Tomo, Please find attached updated patches for UNMAP and thin provisioning support. regards ronnie sahlberg
Attachment:
0001-TGTIMG-Add-support-for-thin-provisioning.patch.gz
Description: GNU Zip compressed data
From ce53f79c2692147164fe4e6ede856c2f26b3028f Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg <ronniesahlberg@xxxxxxxxx> Date: Sun, 1 Apr 2012 07:09:10 +1000 Subject: [PATCH 1/2] TGTIMG: Add support for thin-provisioning A new flag --ting-provisioning is added to the tgtimg command. When used this flag will create a sparse file without any allocation guarantee. It will also verify that FALLOC_FL_PUNCH_HOLE works. If such files are created, a future patch to tgtd will add support for the UNMAP command that is used by initiators to release blocks that are no longer in use. Signed-off-by: Ronnie Sahlberg <ronniesahlberg@xxxxxxxxx> --- doc/tgtimg.8.xml | 22 ++++++++++++++++++++++ usr/tgtimg.c | 37 ++++++++++++++++++++++++++++--------- usr/util.h | 16 ++++++++++++++++ 3 files changed, 66 insertions(+), 9 deletions(-) diff --git a/doc/tgtimg.8.xml b/doc/tgtimg.8.xml index 5a787af..e067db0 100644 --- a/doc/tgtimg.8.xml +++ b/doc/tgtimg.8.xml @@ -21,6 +21,7 @@ <arg choice="opt">-s --size <size></arg> <arg choice="opt">-t --type <media-type></arg> <arg choice="opt">-f --file <path></arg> + <arg choice="opt">-T --thin-provisioning</arg> </cmdsynopsis> <cmdsynopsis> <command>tgtimg --help</command> @@ -126,6 +127,27 @@ Supported media types for tape devices are : </listitem> </varlistentry> + <varlistentry><term><option>-T, --thin-provisioning</option></term> + <listitem> + <para> + This argument makes the allocation of the image format use + thin-provisioning. This means that the file created will be a + sparse file that will allocate blocks from the filesystem on demand. + </para> + <para> + Be careful when using thin-provisioning. If the filesystem + fills up a iSCSI write to a thin-provisioned LUN + can fail. Initiators generally do not handle "out of space" errors + gracefully. + </para> + <para> + Thin-provisioning uses FALLOC_FL_PUNCH_HOLE which is only available + on some linux filesystems. Thin-provisioning can only be used for + DISK images. + </para> + </listitem> + </varlistentry> + </variablelist> </refsect1> diff --git a/usr/tgtimg.c b/usr/tgtimg.c index f4388d9..286e333 100644 --- a/usr/tgtimg.c +++ b/usr/tgtimg.c @@ -1,5 +1,5 @@ /* - * Create blank media files for bs_tape backing store + * Create media files for TGTD devices * * Copyright (C) 2008 Mark Harvey markh794@xxxxxxxxx * @@ -38,6 +38,7 @@ #include "ssc.h" #include "libssc.h" #include "scsi.h" +#include "util.h" #define NO_LOGGING #include "log.h" @@ -59,6 +60,7 @@ struct option const long_options[] = { {"size", required_argument, NULL, 's'}, {"type", required_argument, NULL, 't'}, {"file", required_argument, NULL, 'f'}, + {"thin-provisioning", no_argument, NULL, 'T'}, {NULL, 0, NULL, 0}, }; @@ -71,7 +73,7 @@ static void usage(int status) printf("\ Linux SCSI Target Framework Image File Utility, version %s\n\ \n\ - --op new --device-type tape --barcode=[code] --size=[size] --type=[type] --file=[path]\n\ + --op new --device-type tape --barcode=[code] --size=[size] --type=[type] --file=[path] [--thin-provisioning]\n\ create a new tape image file.\n\ [code] is a string of chars.\n\ [size] is media size(in megabytes).\n\ @@ -83,6 +85,7 @@ Linux SCSI Target Framework Image File Utility, version %s\n\ --op show --device-type tape --file=[path]\n\ dump the tape image file contents.\n\ [path] is the tape image file\n\ + --thin-provisioning create a sparse file for the media\n\ --help display this help and exit\n\ \n\ Report bugs to <stgt@xxxxxxxxxxxxxxx>.\n", TGT_VERSION); @@ -414,7 +417,7 @@ static int mmc_ops(int op, char *path, char *media_type) return 0; } -static int sbc_new(int op, char *path, char *capacity, char *media_type) +static int sbc_new(int op, char *path, char *capacity, char *media_type, int thin) { int fd; @@ -438,9 +441,21 @@ static int sbc_new(int op, char *path, char *capacity, char *media_type) perror("Failed creating file"); exit(2); } - if (posix_fallocate(fd, 0, size*1024*1024LL) == -1) { - perror("posix_fallocate failed."); - exit(3); + if (thin) { + if (ftruncate(fd, size*1024*1024LL) != 0) { + perror("Failed to set file size"); + exit(6); + } + if (unmap_file_region(fd, 0, size*1024*1024LL) != 0) { + perror("Thin provisioning not available on" + " this file"); + exit(5); + } + } else { + if (posix_fallocate(fd, 0, size*1024*1024LL) == -1) { + perror("posix_fallocate failed."); + exit(3); + } } free(buf); @@ -456,7 +471,7 @@ static int sbc_new(int op, char *path, char *capacity, char *media_type) return 0; } -static int sbc_ops(int op, char *path, char *capacity, char *media_type) +static int sbc_ops(int op, char *path, char *capacity, char *media_type, int thin) { if (op == OP_NEW) { if (!media_type) { @@ -471,7 +486,7 @@ static int sbc_ops(int op, char *path, char *capacity, char *media_type) eprintf("Missing the capacity param\n"); usage(1); } - return sbc_new(op, path, capacity, media_type); + return sbc_new(op, path, capacity, media_type, thin); } else { eprintf("unknown the operation type\n"); usage(1); @@ -489,6 +504,7 @@ int main(int argc, char **argv) int dev_type = TYPE_TAPE; int op = -1; char *path = NULL; + int thin = 0; while ((ch = getopt_long(argc, argv, short_options, long_options, &longindex)) >= 0) { @@ -514,6 +530,9 @@ int main(int argc, char **argv) case 'h': usage(0); break; + case 'T': + thin = 1; + break; default: eprintf("unrecognized option '%s'\n", optarg); usage(1); @@ -543,7 +562,7 @@ int main(int argc, char **argv) mmc_ops(op, path, media_type); break; case TYPE_DISK: - sbc_ops(op, path, media_capacity, media_type); + sbc_ops(op, path, media_capacity, media_type, thin); break; default: eprintf("unsupported the device type operation\n"); diff --git a/usr/util.h b/usr/util.h index e75b23a..4bf157d 100644 --- a/usr/util.h +++ b/usr/util.h @@ -5,6 +5,7 @@ #include <endian.h> #include <errno.h> #include <fcntl.h> +#include <linux/falloc.h> #include <signal.h> #include <syscall.h> #include <unistd.h> @@ -159,4 +160,19 @@ int concat_buf_finish(struct concat_buf *b); int concat_write(struct concat_buf *b, int fd, int offset); void concat_buf_release(struct concat_buf *b); + +/* If we have recent enough glibc to support PUNCH HOLE we try to unmap + * the region. + */ +static inline int unmap_file_region(int fd, off_t offset, off_t length) +{ +#ifdef FALLOC_FL_PUNCH_HOLE + if (fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, + offset, length) == 0) + return 0; +#endif + return -1; +} + + #endif -- 1.7.3.1
Attachment:
0002-SBC-UNMAP-Add-support-for-thin-provisioning-and-the-.patch.gz
Description: GNU Zip compressed data
From 2830abc3935294eba621b781f72aecda604c65a7 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg <ronniesahlberg@xxxxxxxxx> Date: Sun, 1 Apr 2012 08:04:06 +1000 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 | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ usr/sbc.c | 76 +++++++++++++++++++++++++++++++++++++++++- usr/scsi.h | 1 + usr/spc.c | 43 +++++++++++++++++++++-- usr/target.c | 2 + usr/tgtd.h | 2 + 7 files changed, 241 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..7d614f8 100644 --- a/usr/bs_rdwr.c +++ b/usr/bs_rdwr.c @@ -27,6 +27,8 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/types.h> +#include <sys/stat.h> #include <unistd.h> #include <linux/fs.h> @@ -63,6 +65,7 @@ static void bs_rdwr_request(struct scsi_cmd *cmd) uint8_t key; uint16_t asc; char *tmpbuf; + struct stat st; ret = length = 0; key = asc = 0; @@ -160,6 +163,100 @@ static void bs_rdwr_request(struct scsi_cmd *cmd) free(tmpbuf); break; + case UNMAP: + if (!cmd->dev->attrs.thinprovisioning) { + result = SAM_STAT_CHECK_CONDITION; + key = ILLEGAL_REQUEST; + asc = ASC_INVALID_FIELD_IN_CDB; + break; + } + + if (fstat(fd, &st) != 0) { + eprintf("Failed to fstat() file for UNMAP\n"); + result = SAM_STAT_CHECK_CONDITION; + key = HARDWARE_ERROR; + asc = ASC_INTERNAL_TGT_FAILURE; + break; + } + + 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 offset; + uint64_t len; + + offset = get_unaligned_be64(&tmpbuf[0]); + offset = offset << cmd->dev->blk_shift; + + len = get_unaligned_be32(&tmpbuf[8]); + len = len << cmd->dev->blk_shift; + + if (offset + len > st.st_size) { + eprintf("UNMAP beyond EOF\n"); + result = SAM_STAT_CHECK_CONDITION; + key = ILLEGAL_REQUEST; + asc = ASC_LBA_OUT_OF_RANGE; + break; + } + + /* we can only punch holes aligned for full blocks. + * if not aligned to a block boundary we overwrite + * the first partial block with zero + */ + if (len > 0 && offset % st.st_blksize) { + char *buf; + uint64_t l; + + l = st.st_blksize - offset % st.st_blksize; + if (l > len) + l = len; + + buf = zalloc(l); + pwrite64(fd, buf, l, offset); + free(buf); + + offset += l; + len -= l; + } + + if (len >= st.st_blksize) { + if (unmap_file_region(fd, offset, + len - len % st.st_blksize) != 0) { + eprintf("Failed to punch hole for" + " UNMAP at offset:" PRIu64 + " length: " PRIu64 "\n", + offset, + len - len % st.st_blksize); + result = SAM_STAT_CHECK_CONDITION; + key = HARDWARE_ERROR; + asc = ASC_INTERNAL_TGT_FAILURE; + break; + } + offset += len - len % st.st_blksize; + len = len % st.st_blksize; + + } + + /* zero the tail if there is one */ + if (len > 0) { + char *buf; + + buf = zalloc(len); + pwrite64(fd, buf, len, offset); + free(buf); + } + + length -= 16; + tmpbuf += 16; + } + break; 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