From ce53f79c2692147164fe4e6ede856c2f26b3028f Mon Sep 17 00:00:00 2001
From: Ronnie Sahlberg <ronniesahlberg@gmail.com>
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@gmail.com>
---
 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 &lt;size&gt;</arg>
 		<arg choice="opt">-t --type &lt;media-type&gt;</arg>
 		<arg choice="opt">-f --file &lt;path&gt;</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@gmail.com
  *
@@ -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@vger.kernel.org>.\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
