[PATCH] losetup: Add support for logical block size

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Kernel since 4.14 supports setting of logical block size[1]. It allows to
create loop devices that report logical block size different from 512.

Add support for this feature to losetup.

References:
[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/drivers/block/loop.c?id=89e4fdecb51cf5535867026274bc97de9480ade5

Signed-off-by: Stanislav Brabec <sbrabec@xxxxxxx>
Cc: Ming Lei <ming.lei@xxxxxxxxxx>
Cc: Hannes Reinecke <hare@xxxxxxxx>
Cc: Omar Sandoval <osandov@xxxxxx>
Cc: Jens Axboe <axboe@xxxxxxxxx>
---
 include/loopdev.h   |  4 ++++
 lib/loopdev.c       | 46 ++++++++++++++++++++++++++++++++++++++++++++
 sys-utils/losetup.8 |  3 +++
 sys-utils/losetup.c | 55 ++++++++++++++++++++++++++++++++++++++++-------------
 4 files changed, 95 insertions(+), 13 deletions(-)

diff --git a/include/loopdev.h b/include/loopdev.h
index 953d2db89..20cf9b183 100644
--- a/include/loopdev.h
+++ b/include/loopdev.h
@@ -24,6 +24,7 @@
 /* #define LOOP_CHANGE_FD	0x4C06 */
 #define LOOP_SET_CAPACITY	0x4C07
 #define LOOP_SET_DIRECT_IO	0x4C08
+#define LOOP_SET_BLOCK_SIZE	0x4C09
 
 /* /dev/loop-control interface */
 #ifndef LOOP_CTL_ADD
@@ -55,6 +56,7 @@ struct loop_info64 {
 	uint64_t	lo_rdevice;
 	uint64_t	lo_offset;
 	uint64_t	lo_sizelimit; /* bytes, 0 == max available */
+	uint64_t	lo_blocksize;
 	uint32_t	lo_number;
 	uint32_t	lo_encrypt_type;
 	uint32_t	lo_encrypt_key_size;
@@ -173,11 +175,13 @@ int loopcxt_set_offset(struct loopdev_cxt *lc, uint64_t offset);
 int loopcxt_set_sizelimit(struct loopdev_cxt *lc, uint64_t sizelimit);
 int loopcxt_set_flags(struct loopdev_cxt *lc, uint32_t flags);
 int loopcxt_set_backing_file(struct loopdev_cxt *lc, const char *filename);
+int loopcxt_set_blocksize(struct loopdev_cxt *lc, uint64_t blocksize);
 
 extern char *loopcxt_get_backing_file(struct loopdev_cxt *lc);
 extern int loopcxt_get_backing_devno(struct loopdev_cxt *lc, dev_t *devno);
 extern int loopcxt_get_backing_inode(struct loopdev_cxt *lc, ino_t *ino);
 extern int loopcxt_get_offset(struct loopdev_cxt *lc, uint64_t *offset);
+extern int loopcxt_get_blocksize(struct loopdev_cxt *lc, uint64_t *blocksize);
 extern int loopcxt_get_sizelimit(struct loopdev_cxt *lc, uint64_t *size);
 extern int loopcxt_get_encrypt_type(struct loopdev_cxt *lc, uint32_t *type);
 extern const char *loopcxt_get_crypt_name(struct loopdev_cxt *lc);
diff --git a/lib/loopdev.c b/lib/loopdev.c
index 8c653a361..4863219c5 100644
--- a/lib/loopdev.c
+++ b/lib/loopdev.c
@@ -735,6 +735,34 @@ int loopcxt_get_offset(struct loopdev_cxt *lc, uint64_t *offset)
 	return rc;
 }
 
+/*
+ * @lc: context
+ * @blocksize: returns logical blocksize for the given device
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_blocksize(struct loopdev_cxt *lc, uint64_t *blocksize)
+{
+	struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc);
+	int rc = -EINVAL;
+
+	if (sysfs)
+		rc = sysfs_read_u64(sysfs, "queue/logical_block_size", blocksize);
+
+	if (rc && loopcxt_ioctl_enabled(lc)) {
+		struct loop_info64 *lo = loopcxt_get_info(lc);
+		if (lo) {
+			if (blocksize)
+				*blocksize = lo->lo_blocksize;
+			rc = 0;
+		} else
+			rc = -errno;
+	}
+
+	DBG(CXT, ul_debugobj(lc, "get_blocksize [rc=%d]", rc));
+	return rc;
+}
+
 /*
  * @lc: context
  * @sizelimit: returns size limit for the given device
@@ -1398,6 +1426,24 @@ int loopcxt_set_dio(struct loopdev_cxt *lc, unsigned long use_dio)
 	return 0;
 }
 
+int loopcxt_set_blocksize(struct loopdev_cxt *lc, unsigned long blocksize)
+{
+	int fd = loopcxt_get_fd(lc);
+
+	if (fd < 0)
+		return -EINVAL;
+
+	/* Kernels prior to v4.14 don't support this ioctl */
+	if (ioctl(fd, LOOP_SET_BLOCK_SIZE, blocksize) < 0) {
+		int rc = -errno;
+		DBG(CXT, ul_debugobj(lc, "LOOP_SET_BLOCK_SIZE failed: %m"));
+		return rc;
+	}
+
+	DBG(CXT, ul_debugobj(lc, "logical block size set"));
+	return 0;
+}
+
 int loopcxt_delete_device(struct loopdev_cxt *lc)
 {
 	int fd = loopcxt_get_fd(lc);
diff --git a/sys-utils/losetup.8 b/sys-utils/losetup.8
index 98a8e70d6..41aedd8e5 100644
--- a/sys-utils/losetup.8
+++ b/sys-utils/losetup.8
@@ -116,6 +116,9 @@ The data start is moved \fIoffset\fP bytes into the specified file or device.
 .IP "\fB\-\-sizelimit \fIsize\fP"
 The data end is set to no more than \fIsize\fP bytes after the data start.
 .TP
+.BR \-b , " \-\-logical-blocksize " \fIsize
+Set the logical block size of the loop device in bytes.
+.TP
 .BR \-c , " \-\-set\-capacity " \fIloopdev
 Force the loop driver to reread the size of the file associated with the
 specified loop device.
diff --git a/sys-utils/losetup.c b/sys-utils/losetup.c
index bbff98389..7837f5833 100644
--- a/sys-utils/losetup.c
+++ b/sys-utils/losetup.c
@@ -36,6 +36,7 @@ enum {
 	A_FIND_FREE,		/* find first unused */
 	A_SET_CAPACITY,		/* set device capacity */
 	A_SET_DIRECT_IO,	/* set accessing backing file by direct io */
+	A_SET_BLOCKSIZE,	/* set logical block size of the loop device */
 };
 
 enum {
@@ -50,6 +51,7 @@ enum {
 	COL_RO,
 	COL_SIZELIMIT,
 	COL_DIO,
+	COL_BLOCKSIZE,
 };
 
 /* basic output flags */
@@ -76,6 +78,7 @@ static struct colinfo infos[] = {
 	[COL_SIZELIMIT]   = { "SIZELIMIT",    5, SCOLS_FL_RIGHT, N_("size limit of the file in bytes")},
 	[COL_MAJMIN]      = { "MAJ:MIN",      3, 0, N_("loop device major:minor number")},
 	[COL_DIO]         = { "DIO",          1, SCOLS_FL_RIGHT, N_("access backing file with direct-io")},
+	[COL_BLOCKSIZE]   = { "BLOCKSIZE",    4, SCOLS_FL_RIGHT, N_("logical block size in bytes")},
 };
 
 static int columns[ARRAY_SIZE(infos) * 2] = {-1};
@@ -136,6 +139,8 @@ static int printf_loopdev(struct loopdev_cxt *lc)
 
 		if (loopcxt_get_sizelimit(lc, &x) == 0 && x)
 				printf(_(", sizelimit %ju"), x);
+		if (loopcxt_get_blocksize(lc, &x) == 0 && x)
+				printf(_(", blocksize %ju"), x);
 		printf("\n");
 		return 0;
 	}
@@ -157,6 +162,8 @@ static int printf_loopdev(struct loopdev_cxt *lc)
 		if (e && *e)
 			printf(_(", encryption %s (type %u)"), e, type);
 	}
+	if (loopcxt_get_blocksize(lc, &x) == 0 && x)
+			printf(_(", blocksize %ju"), x);
 	printf("\n");
 	return 0;
 }
@@ -280,6 +287,10 @@ static int set_scols_data(struct loopdev_cxt *lc, struct libscols_line *ln)
 		case COL_PARTSCAN:
 			p = loopcxt_is_partscan(lc) ? "1" : "0";
 			break;
+		case COL_BLOCKSIZE:
+			if (loopcxt_get_blocksize(lc, &x) == 0)
+				xasprintf(&np, "%jd", x);
+			break;
 		default:
 			return -EINVAL;
 		}
@@ -404,6 +415,7 @@ static void __attribute__((__noreturn__)) usage(void)
 	fputs(USAGE_SEPARATOR, out);
 	fputs(_(" -o, --offset <num>            start at offset <num> into file\n"), out);
 	fputs(_("     --sizelimit <num>         device is limited to <num> bytes of the file\n"), out);
+	fputs(_(" -b  --logical-blocksize <size> set the logical block size to <size>\n"), out);
 	fputs(_(" -P, --partscan                create a partitioned loop device\n"), out);
 	fputs(_(" -r, --read-only               set up a read-only loop device\n"), out);
 	fputs(_("     --direct-io[=<on|off>]    open backing file with O_DIRECT\n"), out);
@@ -566,11 +578,11 @@ int main(int argc, char **argv)
 	struct loopdev_cxt lc;
 	int act = 0, flags = 0, no_overlap = 0, c;
 	char *file = NULL;
-	uint64_t offset = 0, sizelimit = 0;
+	uint64_t offset = 0, sizelimit = 0, blocksize = 0;
 	int res = 0, showdev = 0, lo_flags = 0;
 	char *outarg = NULL;
 	int list = 0;
-	unsigned long use_dio = 0, set_dio = 0;
+	unsigned long use_dio = 0, set_dio = 0, set_blocksize = 0;
 
 	enum {
 		OPT_SIZELIMIT = CHAR_MAX + 1,
@@ -589,6 +601,7 @@ int main(int argc, char **argv)
 		{ "associated",   required_argument, NULL, 'j'           },
 		{ "json",         no_argument,       NULL, 'J'           },
 		{ "list",         no_argument,       NULL, 'l'           },
+		{ "logical-blocksize", required_argument, NULL, 'b'      },
 		{ "noheadings",   no_argument,       NULL, 'n'           },
 		{ "offset",       required_argument, NULL, 'o'           },
 		{ "output",       required_argument, NULL, 'O'           },
@@ -620,7 +633,7 @@ int main(int argc, char **argv)
 	if (loopcxt_init(&lc, 0))
 		err(EXIT_FAILURE, _("failed to initialize loopcxt"));
 
-	while ((c = getopt_long(argc, argv, "ac:d:Dfhj:JlLno:O:PrvV",
+	while ((c = getopt_long(argc, argv, "ab:c:d:Dfhj:JlLno:O:PrvV",
 				longopts, NULL)) != -1) {
 
 		err_exclusive_options(c, longopts, excl, excl_st);
@@ -629,6 +642,10 @@ int main(int argc, char **argv)
 		case 'a':
 			act = A_SHOW;
 			break;
+		case 'b':
+			set_blocksize = 1;
+			blocksize = strtosize_or_err(optarg, _("failed to parse logical block size"));
+			break;
 		case 'c':
 			act = A_SET_CAPACITY;
 			if (!is_loopdev(optarg) ||
@@ -727,6 +744,7 @@ int main(int argc, char **argv)
 		columns[ncolumns++] = COL_RO;
 		columns[ncolumns++] = COL_BACK_FILE;
 		columns[ncolumns++] = COL_DIO;
+		columns[ncolumns++] = COL_BLOCKSIZE;
 	}
 
 	if (act == A_FIND_FREE && optind < argc) {
@@ -747,12 +765,14 @@ int main(int argc, char **argv)
 		/*
 		 * losetup [--list] <device>
 		 * OR
-		 * losetup --direct-io[=off] <device>
+		 * losetup {--direct-io[=off]|--logical-blocksize=size}... <device>
 		 */
-		if (!set_dio)
+		if (!(set_dio || set_blocksize))
 			act = A_SHOW_ONE;
-		else
+		if (set_dio)
 			act = A_SET_DIRECT_IO;
+		if (set_blocksize)
+			act = A_SET_BLOCKSIZE;
 		if (!is_loopdev(argv[optind]) ||
 		    loopcxt_set_device(&lc, argv[optind]))
 			err(EXIT_FAILURE, _("%s: failed to use device"),
@@ -799,8 +819,8 @@ int main(int argc, char **argv)
 			if (showdev)
 				printf("%s\n", loopcxt_get_device(&lc));
 			warn_size(file, sizelimit);
-			if (set_dio)
-				goto lo_set_dio;
+			if (set_dio || set_blocksize)
+				goto lo_set_post;
 		}
 		break;
 	case A_DELETE:
@@ -853,11 +873,20 @@ int main(int argc, char **argv)
 			        loopcxt_get_device(&lc));
 		break;
 	case A_SET_DIRECT_IO:
- lo_set_dio:
-		res = loopcxt_set_dio(&lc, use_dio);
-		if (res)
-			warn(_("%s: set direct io failed"),
-			        loopcxt_get_device(&lc));
+	case A_SET_BLOCKSIZE:
+ lo_set_post:
+		if (set_dio) {
+			res = loopcxt_set_dio(&lc, use_dio);
+			if (res)
+				warn(_("%s: set direct io failed"),
+				        loopcxt_get_device(&lc));
+		}
+		if (set_blocksize) {
+			res = loopcxt_set_blocksize(&lc, blocksize);
+			if (res)
+				warn(_("%s: set logical block size"),
+				        loopcxt_get_device(&lc));
+		}
 		break;
 	default:
 		warnx(_("bad usage"));
-- 
2.14.1

-- 
Best Regards / S pozdravem,

Stanislav Brabec
software developer
---------------------------------------------------------------------
SUSE LINUX, s. r. o.                         e-mail: sbrabec@xxxxxxxx
Křižíkova 148/34 (Corso IIa)                  tel: +49 911 7405384547
186 00 Praha 8-Karlín                          fax:  +420 284 084 001
Czech Republic                                    http://www.suse.cz/
PGP: 830B 40D5 9E05 35D8 5E27 6FA3 717C 209F A04F CD76
--
To unsubscribe from this list: send the line "unsubscribe util-linux" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux