diff --git a/include/libxfs.h b/include/libxfs.h
index 923a376bd71a..72c0b525f9db 100644
--- a/include/libxfs.h
+++ b/include/libxfs.h
@@ -50,6 +50,7 @@ struct iomap;
  * This mirrors the kernel include for xfs_buf.h - it's implicitly included in
  * every files via a similar include in the kernel xfs_linux.h.
  */
+#include "xfs_buftarg.h"
 #include "libxfs_io.h"
 
 #include "xfs_bit.h"
diff --git a/include/xfs_inode.h b/include/xfs_inode.h
index 29086a7d5e2e..f30ce8792fba 100644
--- a/include/xfs_inode.h
+++ b/include/xfs_inode.h
@@ -67,7 +67,6 @@ typedef struct xfs_inode {
 	struct xfs_mount	*i_mount;	/* fs mount struct ptr */
 	xfs_ino_t		i_ino;		/* inode number (agno/agino) */
 	struct xfs_imap		i_imap;		/* location for xfs_imap() */
-	struct xfs_buftarg	i_dev;		/* dev for this inode */
 	struct xfs_ifork	*i_afp;		/* attribute fork pointer */
 	struct xfs_ifork	*i_cowfp;	/* copy on write extents */
 	struct xfs_ifork	i_df;		/* data fork */
diff --git a/libfrog/linux.c b/libfrog/linux.c
index a45d99ab5bbe..8287b0d90b56 100644
--- a/libfrog/linux.c
+++ b/libfrog/linux.c
@@ -129,7 +129,19 @@ platform_check_iswritable(char *name, char *block, struct stat *s)
 int
 platform_set_blocksize(int fd, char *path, dev_t device, int blocksize, int fatal)
 {
-	int error = 0;
+	struct stat	st;
+	int		error = 0;
+
+	if (fstat(fd, &st) < 0) {
+		fprintf(stderr, _("%s: "
+			"cannot stat the device file \"%s\": %s\n"),
+			progname, path, strerror(errno));
+		exit(1);
+	}
+
+	/* Can't set block sizes on image files. */
+	if ((st.st_mode & S_IFMT) != S_IFBLK)
+		return 0;
 
 	if (major(device) != RAMDISK_MAJOR) {
 		if ((error = ioctl(fd, BLKBSZSET, &blocksize)) < 0) {
diff --git a/libxfs/Makefile b/libxfs/Makefile
index de595b7cd49f..7f2fc0f878e2 100644
--- a/libxfs/Makefile
+++ b/libxfs/Makefile
@@ -30,6 +30,7 @@ HFILES = \
 	xfs_bmap_btree.h \
 	xfs_btree.h \
 	xfs_btree_staging.h \
+	xfs_buftarg.h \
 	xfs_attr_remote.h \
 	xfs_cksum.h \
 	xfs_da_btree.h \
@@ -54,7 +55,8 @@ HFILES = \
 	libxfs_priv.h \
 	xfs_dir2_priv.h
 
-CFILES = cache.c \
+CFILES = buftarg.c \
+	cache.c \
 	defer_item.c \
 	init.c \
 	kmem.c \
diff --git a/libxfs/buftarg.c b/libxfs/buftarg.c
new file mode 100644
index 000000000000..d4bcb2936f01
--- /dev/null
+++ b/libxfs/buftarg.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2000-2006 Silicon Graphics, Inc.
+ * Copyright (c) 2019 Red Hat, Inc.
+ * All Rights Reserved.
+ */
+
+#include "libxfs_priv.h"
+#include "libfrog/platform.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_shared.h"
+#include "xfs_trans_resv.h"
+#include "xfs_sb.h"
+#include "xfs_mount.h"
+#include "xfs_trace.h"
+#include "xfs_errortag.h"
+
+#include "libxfs.h"		/* for libxfs_device_to_fd */
+
+int
+xfs_buftarg_setsize(
+	struct xfs_buftarg	*btp,
+	unsigned int		sectorsize)
+{
+	long long		size;
+	int			bsize;
+
+	/* Set up metadata sector size info */
+	btp->bt_meta_sectorsize = sectorsize;
+	btp->bt_meta_sectormask = sectorsize - 1;
+
+	if (platform_set_blocksize(btp->bt_fd, NULL, btp->bt_bdev,
+					sectorsize, true)) {
+		xfs_warn(btp->bt_mount,
+			"Cannot set_blocksize to %u on device %pg",
+			sectorsize, btp->bt_bdev);
+		return -EINVAL;
+	}
+
+	/* Set up device logical sector size mask */
+	platform_findsizes(NULL, btp->bt_fd, &size, &bsize);
+	btp->bt_logical_sectorsize = bsize;
+	btp->bt_logical_sectormask = bsize - 1;
+
+	return 0;
+}
+
+/*
+ * When allocating the initial buffer target we have not yet read in the
+ * superblock, so don't know what sized sectors are being used at this early
+ * stage.  Play safe.
+ */
+STATIC int
+xfs_buftarg_setsize_early(
+	struct xfs_buftarg	*btp)
+{
+	long long		size;
+	int			bsize;
+
+	platform_findsizes(NULL, btp->bt_fd, &size, &bsize);
+	return xfs_buftarg_setsize(btp, bsize);
+}
+
+struct xfs_buftarg *
+xfs_buftarg_alloc(
+	struct xfs_mount	*mp,
+	dev_t			bdev)
+{
+	struct xfs_buftarg	*btp;
+
+	btp = kmem_zalloc(sizeof(*btp), KM_NOFS);
+
+	btp->bt_mount = mp;
+	btp->bt_fd = libxfs_device_to_fd(bdev);
+	btp->bt_bdev = bdev;
+
+	if (xfs_buftarg_setsize_early(btp))
+		goto error_free;
+
+	if (percpu_counter_init(&btp->bt_io_count, 0, GFP_KERNEL))
+		goto error_free;
+
+	return btp;
+
+error_free:
+	free(btp);
+	return NULL;
+}
+
+void
+xfs_buftarg_free(
+	struct xfs_buftarg	*btp)
+{
+	ASSERT(percpu_counter_sum(&btp->bt_io_count) == 0);
+	percpu_counter_destroy(&btp->bt_io_count);
+	platform_flush_device(btp->bt_fd, btp->bt_bdev);
+	free(btp);
+}
diff --git a/libxfs/init.c b/libxfs/init.c
index fc30f92d6fb2..3ab622e9ee3b 100644
--- a/libxfs/init.c
+++ b/libxfs/init.c
@@ -590,26 +590,6 @@ out_unwind:
 	return error;
 }
 
-static struct xfs_buftarg *
-libxfs_buftarg_alloc(
-	struct xfs_mount	*mp,
-	dev_t			dev)
-{
-	struct xfs_buftarg	*btp;
-
-	btp = malloc(sizeof(*btp));
-	if (!btp) {
-		fprintf(stderr, _("%s: buftarg init failed\n"),
-			progname);
-		exit(1);
-	}
-	btp->bt_mount = mp;
-	btp->bt_bdev = dev;
-	btp->flags = 0;
-
-	return btp;
-}
-
 void
 libxfs_buftarg_init(
 	struct xfs_mount	*mp,
@@ -650,12 +630,24 @@ libxfs_buftarg_init(
 		return;
 	}
 
-	mp->m_ddev_targp = libxfs_buftarg_alloc(mp, dev);
+	mp->m_ddev_targp = xfs_buftarg_alloc(mp, dev);
+	if (!mp->m_ddev_targp)
+		goto out_fail;
 	if (!logdev || logdev == dev)
 		mp->m_logdev_targp = mp->m_ddev_targp;
 	else
-		mp->m_logdev_targp = libxfs_buftarg_alloc(mp, logdev);
-	mp->m_rtdev_targp = libxfs_buftarg_alloc(mp, rtdev);
+		mp->m_logdev_targp = xfs_buftarg_alloc(mp, logdev);
+	if (!mp->m_logdev_targp)
+		goto out_fail;
+	if (rtdev) {
+		mp->m_rtdev_targp = xfs_buftarg_alloc(mp, rtdev);
+		if (!mp->m_rtdev_targp)
+			goto out_fail;
+	}
+	return;
+out_fail:
+	fprintf(stderr, _("%s: Failed to allocate buftarg\n"), progname);
+	exit(1);
 }
 
 /*
diff --git a/libxfs/libxfs_io.h b/libxfs/libxfs_io.h
index 3bb00af9bdba..eeca8895b1d3 100644
--- a/libxfs/libxfs_io.h
+++ b/libxfs/libxfs_io.h
@@ -14,25 +14,10 @@
 struct xfs_buf;
 struct xfs_mount;
 struct xfs_perag;
+struct xfs_buftarg;
 
-/*
- * IO verifier callbacks need the xfs_mount pointer, so we have to behave
- * somewhat like the kernel now for userspace IO in terms of having buftarg
- * based devices...
- */
-struct xfs_buftarg {
-	struct xfs_mount	*bt_mount;
-	dev_t			bt_bdev;
-	unsigned int		flags;
-};
-
-/* We purged a dirty buffer and lost a write. */
-#define XFS_BUFTARG_LOST_WRITE		(1 << 0)
-/* A dirty buffer failed the write verifier. */
-#define XFS_BUFTARG_CORRUPT_WRITE	(1 << 1)
-
-extern void	libxfs_buftarg_init(struct xfs_mount *mp, dev_t ddev,
-				    dev_t logdev, dev_t rtdev);
+void libxfs_buftarg_init(struct xfs_mount *mp, dev_t ddev,
+			dev_t logdev, dev_t rtdev);
 int libxfs_blkdev_issue_flush(struct xfs_buftarg *btp);
 
 #define LIBXFS_BBTOOFF64(bbs)	(((xfs_off_t)(bbs)) << BBSHIFT)
diff --git a/libxfs/libxfs_priv.h b/libxfs/libxfs_priv.h
index 7be3f7615fdd..72665f71098e 100644
--- a/libxfs/libxfs_priv.h
+++ b/libxfs/libxfs_priv.h
@@ -85,6 +85,7 @@ struct iomap;
  * This mirrors the kernel include for xfs_buf.h - it's implicitly included in
  * every files via a similar include in the kernel xfs_linux.h.
  */
+#include "xfs_buftarg.h"
 #include "libxfs_io.h"
 
 /* for all the support code that uses progname in error messages */
@@ -201,6 +202,8 @@ static inline bool WARN_ON(bool expr) {
 }
 
 #define WARN_ON_ONCE(e)			WARN_ON(e)
+#define percpu_counter_init(x,v,gfp)	(*x = v)
+#define percpu_counter_destroy(x)	((void) 0)
 #define percpu_counter_read(x)		(*x)
 #define percpu_counter_read_positive(x)	((*x) > 0 ? (*x) : 0)
 #define percpu_counter_sum(x)		(*x)
diff --git a/libxfs/xfs_buftarg.h b/libxfs/xfs_buftarg.h
new file mode 100644
index 000000000000..1bc3a4d0bc9c
--- /dev/null
+++ b/libxfs/xfs_buftarg.h
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.
+ * Copyright (c) 2019 Red Hat, Inc.
+ * All Rights Reserved.
+ */
+#ifndef __XFS_BUFTARG_H
+#define __XFS_BUFTARG_H
+
+struct xfs_mount;
+struct xfs_buf;
+struct xfs_buf_ops;
+
+/*
+ * The xfs_buftarg contains 2 notions of "sector size" -
+ *
+ * 1) The metadata sector size, which is the minimum unit and
+ *    alignment of IO which will be performed by metadata operations.
+ * 2) The device logical sector size
+ *
+ * The first is specified at mkfs time, and is stored on-disk in the
+ * superblock's sb_sectsize.
+ *
+ * The latter is derived from the underlying device, and controls direct IO
+ * alignment constraints.
+ */
+struct xfs_buftarg {
+	dev_t			bt_bdev;
+	int			bt_fd;		/* for read/write IO */
+	struct xfs_mount	*bt_mount;
+	unsigned int		bt_meta_sectorsize;
+	size_t			bt_meta_sectormask;
+	size_t			bt_logical_sectorsize;
+	size_t			bt_logical_sectormask;
+
+	uint32_t		bt_io_count;
+	unsigned int		flags;
+};
+
+/* We purged a dirty buffer and lost a write. */
+#define XFS_BUFTARG_LOST_WRITE		(1 << 0)
+/* A dirty buffer failed the write verifier. */
+#define XFS_BUFTARG_CORRUPT_WRITE	(1 << 1)
+
+/*
+ *	Handling of buftargs.
+ */
+struct xfs_buftarg *xfs_buftarg_alloc(struct xfs_mount *mp, dev_t bdev);
+void xfs_buftarg_free(struct xfs_buftarg *target);
+void xfs_buftarg_wait(struct xfs_buftarg *target);
+int xfs_buftarg_setsize(struct xfs_buftarg *target, unsigned int size);
+
+#define xfs_getsize_buftarg(buftarg)	block_size((buftarg)->bt_bdev)
+
+#endif /* __XFS_BUFTARG_H */
diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
index ba21b4accc97..e094c82f86b7 100644
--- a/mkfs/xfs_mkfs.c
+++ b/mkfs/xfs_mkfs.c
@@ -2660,6 +2660,16 @@ _("size %lld of data subvolume is too small, minimum %lld blocks\n"),
 reported by the device (%u).\n"),
 			cfg->sectorsize, xi->dbsize);
 	}
+
+	if (xi->disfile &&
+	    xi->dsize * xi->dbsize < cfg->dblocks * cfg->blocksize) {
+		if (ftruncate(xi->dfd, cfg->dblocks * cfg->blocksize) < 0) {
+			fprintf(stderr,
+				_("%s: Growing the data section failed\n"),
+				progname);
+			exit(1);
+		}
+	}
 }
 
 /*
@@ -3185,6 +3195,7 @@ calculate_log_size(
 	struct cli_params	*cli,
 	struct xfs_mount	*mp)
 {
+	struct libxfs_xinit	*xi = cli->xi;
 	struct xfs_sb		*sbp = &mp->m_sb;
 	int			min_logblocks;
 	struct xfs_mount	mount;
@@ -3192,7 +3203,7 @@ calculate_log_size(
 	/* we need a temporary mount to calculate the minimum log size. */
 	memset(&mount, 0, sizeof(mount));
 	mount.m_sb = *sbp;
-	libxfs_mount(&mount, &mp->m_sb, 0, 0, 0, 0);
+	libxfs_mount(&mount, &mp->m_sb, xi->ddev, xi->logdev, xi->rtdev, 0);
 	min_logblocks = libxfs_log_calc_minimum_size(&mount);
 	libxfs_umount(&mount);
 
@@ -3352,8 +3363,10 @@ start_superblock_setup(
 	} else
 		sbp->sb_logsunit = 0;
 
-	/* log reservation calculations depend on rt geometry */
+	/* log reservation calculations depends on geometry */
+	sbp->sb_dblocks = cfg->dblocks;
 	sbp->sb_rblocks = cfg->rtblocks;
+	sbp->sb_rextents = cfg->rtextents;
 	sbp->sb_rextsize = cfg->rtextblocks;
 }
 
@@ -3390,8 +3403,6 @@ finish_superblock_setup(
 		memcpy(sbp->sb_fname, cfg->label, label_len);
 	}
 
-	sbp->sb_dblocks = cfg->dblocks;
-	sbp->sb_rextents = cfg->rtextents;
 	platform_uuid_copy(&sbp->sb_uuid, &cfg->uuid);
 	/* Only in memory; libxfs expects this as if read from disk */
 	platform_uuid_copy(&sbp->sb_meta_uuid, &cfg->uuid);
@@ -3414,7 +3425,6 @@ finish_superblock_setup(
 	sbp->sb_qflags = 0;
 	sbp->sb_unit = cfg->dsunit;
 	sbp->sb_width = cfg->dswidth;
-
 }
 
 /* Prepare an uncached buffer, ready to write something out. */
@@ -3524,7 +3534,8 @@ prepare_devices(
 			 lsunit, XLOG_FMT, XLOG_INIT_CYCLE, false);
 
 	/* finally, check we can write the last block in the realtime area */
-	if (mp->m_rtdev_targp->bt_bdev && cfg->rtblocks > 0) {
+	if (mp->m_rtdev_targp && mp->m_rtdev_targp->bt_bdev &&
+	    cfg->rtblocks > 0) {
 		buf = alloc_write_buf(mp->m_rtdev_targp,
 				XFS_FSB_TO_BB(mp, cfg->rtblocks - 1LL),
 				BTOBB(cfg->blocksize));
