On Sun, 1 Apr 2012 08:26:27 +1000 ronnie sahlberg <ronniesahlberg@xxxxxxxxx> wrote: > 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> Users need to enable this explicitly? I mean that when a lu is added, tgtd can check if the backing storage supports FALLOC_FL_PUNCH_HOLE then enables it automatically? > </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 > -- To unsubscribe from this list: send the line "unsubscribe stgt" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html