From e58b5bf485c3eee24e04970b05aaba4472d0fa35 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg 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 --- 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 + + + + 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. + + + This parameter only applies to DISK devices. + + + Thin-provisioning only works for LUNs stored on filesystems + that support FALLOC_FL_PUNCH_HOLE. + + + + + +tgtadm --lld iscsi --mode logicalunit --op update --tid 1 --lun 1 \ + --params thin_provisioning=1 + + 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 #include #include +#include +#include #include #include @@ -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) { + 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 */ + 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