On Tue, Apr 17, 2012 at 1:55 PM, FUJITA Tomonori <fujita.tomonori@xxxxxxxxxxxxx> wrote: > On Sun, 15 Apr 2012 13:48:12 +1000 > ronnie sahlberg <ronniesahlberg@xxxxxxxxx> wrote: > >> From e58b5bf485c3eee24e04970b05aaba4472d0fa35 Mon Sep 17 00:00:00 2001 >> From: Ronnie Sahlberg <ronniesahlberg@xxxxxxxxx> >> Date: Sun, 15 Apr 2012 12:07:32 +1000 >> Subject: [PATCH 1/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 | 54 ++++++++++++++++++++++++++++++++++++++ >> usr/sbc.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++- >> usr/scsi.h | 1 + >> usr/spc.c | 43 ++++++++++++++++++++++++++++--- >> usr/target.c | 2 + >> usr/tgtd.h | 2 + >> 7 files changed, 197 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..9e15603 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> >> @@ -160,6 +162,58 @@ 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; >> + } >> + >> + 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 > cmd->dev->size) { >> + eprintf("UNMAP beyond EOF\n"); >> + result = SAM_STAT_CHECK_CONDITION; >> + key = ILLEGAL_REQUEST; >> + asc = ASC_LBA_OUT_OF_RANGE; >> + break; >> + } >> + >> + if (len > 0) { > > We should check len right after getting it and return sense if it's > invalid? I think this is ok. SBC 5.29.2 : ... If the NUMBER OF LOGICAL BLOCKS is set to zero, then no LBAs shall be unmapped for this UNMAP block descriptor. This condition shall not be considered an error. ... > > >> + if (unmap_file_region(fd, offset, len) != 0) { >> + eprintf("Failed to punch hole for" >> + " UNMAP at offset:%" PRIu64 >> + " length:%" PRIu64 "\n", >> + offset, len); >> + result = SAM_STAT_CHECK_CONDITION; >> + key = HARDWARE_ERROR; >> + asc = ASC_INTERNAL_TGT_FAILURE; >> + break; >> + } >> + } >> + >> + length -= 16; >> + tmpbuf += 16; >> + } >> + break; >> default: >> break; >> } >> diff --git a/usr/sbc.c b/usr/sbc.c >> index 0c3681e..55145cf 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,6 +424,8 @@ 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: >> @@ -549,7 +605,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 8d40139..459993c 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 */ > > Should be non-zero? What version of sbc spec you read? Mine might be > old. Treshold exponent set ot 0 just means we dont support tresholds. SBC 6.5.4 : ... The THRESHOLD EXPONENT field indicates the threshold set size in LBAs as a power of 2 (i.e., the threshold set size is equal to 2(threshold exponent)). A THRESHOLD EXPONENT field set to zero indicates that the logical unit does not support logical block provisioning thresholds. ... > >> + 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)]; >> @@ -1641,7 +1659,7 @@ enum { >> Opt_removable, Opt_readonly, Opt_online, >> Opt_mode_page, >> Opt_path, >> - Opt_bsoflags, >> + Opt_bsoflags, Opt_thinprovisioning, >> Opt_err, >> }; >> >> @@ -1662,6 +1680,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}, >> }; >> >> @@ -1752,6 +1771,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)) >> @@ -1787,6 +1812,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); >> @@ -1819,9 +1848,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