[PATCH] libfs: Fix DIO mode aligment

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

 



From: Alexey Lyashkov <alexey.lyashkov@xxxxxxx>

Bounce buffer must be extended to hold a whole disk block size.
Read/write operations with unaligned offsets is prohobined
in the DIO mode, so read/write blocks should be adjusted to
reflect it.

Change-Id: Ic573c9ff0d476028dd2293f8b814c6112705db0e
Signed-off-by: Alexey Lyashkov <alexey.lyashkov@xxxxxxx>
HPE-bug-id: LUS-9241
---
 lib/ext2fs/io_manager.c |   5 +-
 lib/ext2fs/unix_io.c    | 296 +++++++++++++++++++++++++---------------
 2 files changed, 190 insertions(+), 111 deletions(-)

diff --git a/lib/ext2fs/io_manager.c b/lib/ext2fs/io_manager.c
index c395d615..84399a12 100644
--- a/lib/ext2fs/io_manager.c
+++ b/lib/ext2fs/io_manager.c
@@ -20,6 +20,9 @@
 #include "ext2_fs.h"
 #include "ext2fs.h"
 
+#define max(a, b) ((a) > (b) ? (a) : (b))
+
+
 errcode_t io_channel_set_options(io_channel channel, const char *opts)
 {
 	errcode_t retval = 0;
@@ -128,7 +131,7 @@ errcode_t io_channel_alloc_buf(io_channel io, int count, void *ptr)
 	size_t	size;
 
 	if (count == 0)
-		size = io->block_size;
+		size = max(io->block_size, io->align);
 	else if (count > 0)
 		size = io->block_size * count;
 	else
diff --git a/lib/ext2fs/unix_io.c b/lib/ext2fs/unix_io.c
index 628e60c3..53647a22 100644
--- a/lib/ext2fs/unix_io.c
+++ b/lib/ext2fs/unix_io.c
@@ -154,46 +154,30 @@ static char *safe_getenv(const char *arg)
 /*
  * Here are the raw I/O functions
  */
-static errcode_t raw_read_blk(io_channel channel,
+static errcode_t raw_aligned_read_blk(io_channel channel,
 			      struct unix_private_data *data,
-			      unsigned long long block,
-			      int count, void *bufv)
+			      ext2_loff_t location,
+			      ssize_t size, void *bufv,
+			      int *asize)
 {
-	errcode_t	retval;
-	ssize_t		size;
-	ext2_loff_t	location;
 	int		actual = 0;
+	errcode_t	retval;
 	unsigned char	*buf = bufv;
-	ssize_t		really_read = 0;
-
-	size = (count < 0) ? -count : (ext2_loff_t) count * channel->block_size;
-	data->io_stats.bytes_read += size;
-	location = ((ext2_loff_t) block * channel->block_size) + data->offset;
 
-	if (data->flags & IO_FLAG_FORCE_BOUNCE) {
-		if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
-			retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
-			goto error_out;
-		}
-		goto bounce_read;
-	}
+#ifdef ALIGN_DEBUG
+	printf("raw_aligned_read_blk: %p %lu<>%lu\n", buf,
+		location, (unsigned long) size);
+#endif
 
 #ifdef HAVE_PREAD64
 	/* Try an aligned pread */
-	if ((channel->align == 0) ||
-	    (IS_ALIGNED(buf, channel->align) &&
-	     IS_ALIGNED(size, channel->align))) {
-		actual = pread64(data->dev, buf, size, location);
-		if (actual == size)
-			return 0;
-		actual = 0;
-	}
+	actual = pread64(data->dev, buf, size, location);
+	if (actual == size)
+		return 0;
+	actual = 0;
 #elif HAVE_PREAD
 	/* Try an aligned pread */
-	if ((sizeof(off_t) >= sizeof(ext2_loff_t)) &&
-	    ((channel->align == 0) ||
-	     (IS_ALIGNED(buf, channel->align) &&
-	      IS_ALIGNED(size, channel->align)))) {
+	if (sizeof(off_t) >= sizeof(ext2_loff_t)) {
 		actual = pread(data->dev, buf, size, location);
 		if (actual == size)
 			return 0;
@@ -205,47 +189,100 @@ static errcode_t raw_read_blk(io_channel channel,
 		retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
 		goto error_out;
 	}
+
+	actual = read(data->dev, buf, size);
+	if (actual != size) {
+		if (actual < 0) {
+			retval = errno;
+			actual = 0;
+		} else {
+			retval = EXT2_ET_SHORT_READ;
+		}
+	}
+
+error_out:
+	*asize = actual;
+	return retval;
+}
+
+
+/*
+ * Here are the raw I/O functions
+ */
+static errcode_t raw_read_blk(io_channel channel,
+			      struct unix_private_data *data,
+			      unsigned long long block,
+			      int count, void *bufv)
+{
+	errcode_t	retval;
+	ssize_t		size;
+	ext2_loff_t	location;
+	int		actual = 0;
+	unsigned char	*buf = bufv;
+	unsigned int	align = channel->align;
+	ssize_t		really_read = 0;
+	blk64_t		blk;
+	loff_t		offset;
+
+	size = (count < 0) ? -count : count * channel->block_size;
+	data->io_stats.bytes_read += size;
+	location = ((ext2_loff_t) block * channel->block_size) + data->offset;
+
+	if (data->flags & IO_FLAG_FORCE_BOUNCE) {
+		align = channel->block_size;
+		goto bounce_read;
+	}
+
 	if ((channel->align == 0) ||
-	    (IS_ALIGNED(buf, channel->align) &&
+	    (IS_ALIGNED(location, channel->align) &&
+	     IS_ALIGNED(buf, channel->align) &&
 	     IS_ALIGNED(size, channel->align))) {
-		actual = read(data->dev, buf, size);
-		if (actual != size) {
-		short_read:
-			if (actual < 0) {
-				retval = errno;
-				actual = 0;
-			} else
-				retval = EXT2_ET_SHORT_READ;
+		retval = raw_aligned_read_blk(channel, data, location,
+						size, bufv, &actual);
+		if (retval != 0)
 			goto error_out;
-		}
-		return 0;
+
+		return retval;
 	}
 
+bounce_read:
 #ifdef ALIGN_DEBUG
-	printf("raw_read_blk: O_DIRECT fallback: %p %lu\n", buf,
-	       (unsigned long) size);
+	printf("raw_read_blk: O_DIRECT fallback: %p %lu<>%lu\n", buf,
+		location, (unsigned long) size);
 #endif
-
 	/*
 	 * The buffer or size which we're trying to read isn't aligned
 	 * to the O_DIRECT rules, so we need to do this the hard way...
+	 * read / write must be aligned to the block device sector size
 	 */
-bounce_read:
+
+	blk = location / align;
+	offset = location % align;
+
+	if (lseek(data->dev, blk * align, SEEK_SET) < 0)
+		return errno;
+
 	while (size > 0) {
-		actual = read(data->dev, data->bounce, channel->block_size);
-		if (actual != channel->block_size) {
+		actual = read(data->dev, data->bounce, align);
+		if (actual != align) {
 			actual = really_read;
 			buf -= really_read;
 			size += really_read;
-			goto short_read;
+			retval = EXT2_ET_SHORT_READ;
+			goto error_out;
 		}
 		actual = size;
-		if (size > channel->block_size)
-			actual = channel->block_size;
-		memcpy(buf, data->bounce, actual);
+		if ((actual + offset) > align)
+			actual = align - offset;
+		if (actual > size)
+			actual = size;
+
+		memcpy(buf, data->bounce + offset, actual);
 		really_read += actual;
 		size -= actual;
 		buf += actual;
+		offset = 0;
+		blk++;
 	}
 	return 0;
 
@@ -258,6 +295,58 @@ error_out:
 	return retval;
 }
 
+static errcode_t raw_aligned_write_blk(io_channel channel,
+			       struct unix_private_data *data,
+			       ext2_loff_t  location,
+			       ssize_t size, const void *bufv,
+			       int *asize)
+
+{
+	int		actual = 0;
+	errcode_t	retval;
+	const unsigned char *buf = (void *)bufv;
+
+#ifdef ALIGN_DEBUG
+	printf("raw_aligned_write_blk: %p %lu %lu\n", buf,
+	       location, (unsigned long) size);
+#endif
+
+#ifdef HAVE_PWRITE64
+	/* Try an aligned pwrite */
+	actual = pwrite64(data->dev, buf, size, location);
+	if (actual == size)
+		return 0;
+#elif HAVE_PWRITE
+	/* Try an aligned pwrite */
+	if ((sizeof(off_t) >= sizeof(ext2_loff_t)) {
+		actual = pwrite(data->dev, buf, size, location);
+		if (actual == size)
+			return 0;
+	}
+#endif /* HAVE_PWRITE */
+
+	if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
+		retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
+		goto error_out;
+	}
+
+	actual = write(data->dev, buf, size);
+	if (actual < 0) {
+		retval = errno;
+		goto error_out;
+	}
+	if (actual != size) {
+		retval = EXT2_ET_SHORT_WRITE;
+		goto error_out;
+	}
+	retval = 0;
+error_out:
+	*asize = actual;
+	return retval;
+
+}
+
+
 static errcode_t raw_write_blk(io_channel channel,
 			       struct unix_private_data *data,
 			       unsigned long long block,
@@ -268,6 +357,9 @@ static errcode_t raw_write_blk(io_channel channel,
 	int		actual = 0;
 	errcode_t	retval;
 	const unsigned char *buf = bufv;
+	unsigned int	align = channel->align;
+	blk64_t		blk;
+	loff_t		offset;
 
 	if (count == 1)
 		size = channel->block_size;
@@ -282,95 +374,79 @@ static errcode_t raw_write_blk(io_channel channel,
 	location = ((ext2_loff_t) block * channel->block_size) + data->offset;
 
 	if (data->flags & IO_FLAG_FORCE_BOUNCE) {
-		if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
-			retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
-			goto error_out;
-		}
+		align = channel->block_size;
 		goto bounce_write;
 	}
 
-#ifdef HAVE_PWRITE64
-	/* Try an aligned pwrite */
 	if ((channel->align == 0) ||
-	    (IS_ALIGNED(buf, channel->align) &&
+	    (IS_ALIGNED(location, channel->align) &&
+	     IS_ALIGNED(buf, channel->align) &&
 	     IS_ALIGNED(size, channel->align))) {
-		actual = pwrite64(data->dev, buf, size, location);
-		if (actual == size)
-			return 0;
-	}
-#elif HAVE_PWRITE
-	/* Try an aligned pwrite */
-	if ((sizeof(off_t) >= sizeof(ext2_loff_t)) &&
-	    ((channel->align == 0) ||
-	     (IS_ALIGNED(buf, channel->align) &&
-	      IS_ALIGNED(size, channel->align)))) {
-		actual = pwrite(data->dev, buf, size, location);
-		if (actual == size)
-			return 0;
-	}
-#endif /* HAVE_PWRITE */
+		retval = raw_aligned_write_blk(channel, data, location,
+						size, bufv, &actual);
+		if (retval != 0)
+			goto error_out;
 
-	if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
-		retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
-		goto error_out;
+		return retval;
 	}
 
-	if ((channel->align == 0) ||
-	    (IS_ALIGNED(buf, channel->align) &&
-	     IS_ALIGNED(size, channel->align))) {
-		actual = write(data->dev, buf, size);
-		if (actual < 0) {
-			retval = errno;
-			goto error_out;
-		}
-		if (actual != size) {
-		short_write:
-			retval = EXT2_ET_SHORT_WRITE;
-			goto error_out;
-		}
-		return 0;
-	}
 
+bounce_write:
 #ifdef ALIGN_DEBUG
 	printf("raw_write_blk: O_DIRECT fallback: %p %lu\n", buf,
 	       (unsigned long) size);
 #endif
+
+	/* logical offset may don't aligned with block device block size */
+	blk = location / align;
+	offset = location % align;
+
+	if (lseek(data->dev, blk * align, SEEK_SET) != blk * align) {
+		retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
+		goto error_out;
+	}
+
 	/*
 	 * The buffer or size which we're trying to write isn't aligned
 	 * to the O_DIRECT rules, so we need to do this the hard way...
 	 */
-bounce_write:
 	while (size > 0) {
-		if (size < channel->block_size) {
-			actual = read(data->dev, data->bounce,
-				      channel->block_size);
-			if (actual != channel->block_size) {
-				if (actual < 0) {
-					retval = errno;
-					goto error_out;
-				}
-				memset((char *) data->bounce + actual, 0,
-				       channel->block_size - actual);
+		int actual_w;
+
+		memset((char *) data->bounce, 0, align);
+		if (offset || (size < align)) {
+			actual = read(data->dev, data->bounce, align);
+			if (actual < 0) {
+				retval = errno;
+				goto error_out;
 			}
 		}
 		actual = size;
-		if (size > channel->block_size)
-			actual = channel->block_size;
-		memcpy(data->bounce, buf, actual);
-		if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
+		if ((actual + offset) > align)
+			actual = align - offset;
+		if (actual > size)
+			actual = size;
+		memcpy(((char *)data->bounce) + offset, buf, actual);
+
+		if (lseek(data->dev, blk * align, SEEK_SET) != blk * align) {
 			retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
 			goto error_out;
 		}
-		actual = write(data->dev, data->bounce, channel->block_size);
-		if (actual < 0) {
+
+		actual_w = write(data->dev, data->bounce, align);
+		if (actual_w < 0) {
 			retval = errno;
 			goto error_out;
 		}
-		if (actual != channel->block_size)
-			goto short_write;
+		if (actual_w != align) {
+			retval = EXT2_ET_SHORT_WRITE;
+			goto error_out;
+		}
 		size -= actual;
 		buf += actual;
 		location += actual;
+		blk ++;
+		offset = 0;
 	}
 	return 0;
 
-- 
2.18.4




[Index of Archives]     [Reiser Filesystem Development]     [Ceph FS]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux FS]     [Yosemite National Park]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Device Mapper]     [Linux Media]

  Powered by Linux