lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <003d7711c71a8f515687045f5b74fe045c0f01d1.1742800203.git.ojaswin@linux.ibm.com>
Date: Mon, 24 Mar 2025 13:07:07 +0530
From: Ojaswin Mujoo <ojaswin@...ux.ibm.com>
To: linux-ext4@...r.kernel.org, "Theodore Ts'o" <tytso@....edu>
Cc: John Garry <john.g.garry@...cle.com>, dchinner@...hat.com,
        "Darrick J . Wong" <djwong@...nel.org>,
        Ritesh Harjani <ritesh.list@...il.com>, linux-kernel@...r.kernel.org
Subject: [RFC v3 09/11] ext4: add forcealign support to ext4_map_blocks

Introduce EXT4_GET_BLOCKS_FORCEALIGN that works with EXT4_GET_BLOCKS_EXTSIZE
and guarantees that the extent returned by allocator is physically as
well as logically aligned to the extsize hint set on the inode.

This feature will be used to guranatee aligned allocations for HW accelerated
atomic writes

Signed-off-by: Ojaswin Mujoo <ojaswin@...ux.ibm.com>
---
 fs/ext4/ext4.h    |  1 +
 fs/ext4/extents.c | 24 ++++++++++++++++++++++--
 fs/ext4/inode.c   | 43 ++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 63 insertions(+), 5 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 9b9d7a354736..a7429797c1d2 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -730,6 +730,7 @@ enum {
 	/* Caller is in the atomic contex, find extent if it has been cached */
 #define EXT4_GET_BLOCKS_CACHED_NOWAIT		0x0800
 #define EXT4_GET_BLOCKS_EXTSIZE			0x1000
+#define EXT4_GET_BLOCKS_FORCEALIGN			0x2000
 
 /*
  * The bit position of these flags must not overlap with any of the
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index a86cc3e76f14..25c1368b49bb 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -4414,13 +4414,27 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
 		ar.flags |= EXT4_MB_USE_RESERVED;
 	if (flags & EXT4_GET_BLOCKS_EXTSIZE)
 		ar.flags |= EXT4_MB_HINT_ALIGNED;
+	if (flags & EXT4_GET_BLOCKS_FORCEALIGN) {
+		if (WARN_ON(ar.logical != map->m_lblk || ar.len != map->m_len ||
+			    !(flags & EXT4_GET_BLOCKS_EXTSIZE))) {
+			/*
+			 * This should ideally not happen but if does then error
+			 * out
+			 */
+			err = -ENOSPC;
+			goto out;
+		}
+		ar.flags |= EXT4_MB_FORCE_ALIGN;
+	}
 	newblock = ext4_mb_new_blocks(handle, &ar, &err);
 	if (!newblock)
 		goto out;
 	allocated_clusters = ar.len;
 	ar.len = EXT4_C2B(sbi, ar.len) - offset;
-	ext_debug(inode, "allocate new block: goal %llu, found %llu/%u, requested %u\n",
-		  ar.goal, newblock, ar.len, allocated);
+	ext_debug(
+		inode,
+		"allocate new block: goal %llu, found %llu/%u, requested %u\n",
+		ar.goal, newblock, ar.len, allocated);
 	if (ar.len > allocated)
 		ar.len = allocated;
 
@@ -4435,6 +4449,12 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
 		map->m_flags |= EXT4_MAP_UNWRITTEN;
 	}
 
+	if ((flags & EXT4_GET_BLOCKS_FORCEALIGN) &&
+	    (ar.len != map->m_len || pblk % map->m_len)) {
+		err = -ENOSPC;
+		goto insert_error;
+	}
+
 	if ((flags & EXT4_GET_BLOCKS_EXTSIZE) &&
 	    (flags & EXT4_GET_BLOCKS_PRE_IO)) {
 		/*
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index e41c97584f35..93ab76cb4818 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -753,6 +753,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
 
 	__u32 extsize = ext4_inode_get_extsize(EXT4_I(inode));
 	bool should_extsize = false;
+	bool should_forcealign = false;
 
 #ifdef ES_AGGRESSIVE_TEST
 	struct ext4_map_blocks test_map;
@@ -793,6 +794,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
 		 * ext4_extents.h here?
 		 */
 		int max_unwrit_len = ((1UL << 15) - 1);
+		should_forcealign = (flags & EXT4_GET_BLOCKS_FORCEALIGN);
 
 		align = orig_map->m_lblk % extsize;
 		len = orig_map->m_len + align;
@@ -802,7 +804,11 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
 			max_t(unsigned int, roundup_pow_of_two(len), extsize);
 
 		/* Fallback to normal allocation if we go beyond max len */
-		if (extsize_map.m_len >= max_unwrit_len) {
+		if (WARN_ON(extsize_map.m_len >= max_unwrit_len)) {
+			if (should_forcealign)
+				/* forcealign has no fallback */
+				return -EINVAL;
+
 			flags = orig_flags & ~EXT4_GET_BLOCKS_EXTSIZE;
 			goto set_map;
 		}
@@ -814,8 +820,10 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
 		 * dioread nolock to achieve this. Hence the caller has to pass
 		 * CREATE_UNWRIT with EXTSIZE
 		 */
-		if (!(flags | EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT)) {
-			WARN_ON(true);
+		if (WARN_ON(!(flags | EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT))) {
+			if (should_forcealign)
+				/* forcealign has no fallback */
+				return -EINVAL;
 
 			/* Fallback to non extsize allocation */
 			flags = orig_flags & ~EXT4_GET_BLOCKS_EXTSIZE;
@@ -905,6 +913,29 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
 	if (retval >= 0 && flags & EXT4_GET_BLOCKS_EXTSIZE) {
 		bool orig_in_range =
 			in_range(orig_mlblk, (__u64)map->m_lblk, map->m_len);
+
+		if (should_forcealign) {
+			/*
+			 * For forcealign, irrespective of it it's a hole or not,
+			 * the mapping we got should be exactly equal to the
+			 * extsize mapping we requested since allocation and
+			 * deallocation both respect extsize. If
+			 * not, something has gone terribly wrong.
+			 */
+			if (WARN_ON((map->m_lblk != extsize_mlblk) ||
+				    (map->m_len != extsize_mlen))) {
+				ext4_error_adjust_map(map, orig_map);
+				ext4_warning(
+					inode->i_sb,
+					"%s: Unaligned blocks found! Disable forcealign and try again."
+					"requested:(%u, %u) extsize:(%u, %u) got:(%u, %u)\n",
+					__func__, orig_mlblk, orig_mlen,
+					extsize_mlblk, extsize_mlen,
+					map->m_lblk, map->m_len);
+				return -EUCLEAN;
+			}
+		}
+
 		/*
 		 * Special case: if the extsize range is mapped already and
 		 * covers the original start, we return it.
@@ -925,6 +956,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
 
 		if (map->m_lblk != extsize_mlblk ||
 		    map->m_len != extsize_mlen) {
+			WARN_ON(should_forcealign);
 			flags = orig_flags & ~EXT4_GET_BLOCKS_EXTSIZE;
 			goto set_map;
 		}
@@ -1011,6 +1043,11 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
 		 * not used. Can we avoid that?
 		 */
 		if (!in_range(orig_mlblk, (__u64)map->m_lblk, map->m_len)) {
+			if (WARN_ON(should_forcealign)) {
+				/* this should never happen */
+				ext4_error_adjust_map(map, orig_map);
+				return -ENOSPC;
+			}
 			flags = orig_flags & ~EXT4_GET_BLOCKS_EXTSIZE;
 			goto set_map;
 		}
-- 
2.48.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ