[PATCH 4/4] fs/erofs: allocate data buffers on heap with alignment (3/3)

Michael Walle mwalle at kernel.org
Tue Mar 24 00:42:20 AEDT 2026


The data buffers are used to transfer from or to hardware peripherals.
Often, there are restrictions on addresses, i.e. they have to be aligned
at a certain size. Thus, allocate the data on the heap instead of the
stack (at a random address alignment).

This will also have the benefit, that large data (4k) isn't eating up
the stack.

The actual change is split across multiple patches. This one handles the
"struct erofs_map_blocks" which itself contains a data buffer. Add some
helpers to alloc and free the struct because the data buffer will now be
malloc'ed separately.

Signed-off-by: Michael Walle <mwalle at kernel.org>
---
 fs/erofs/data.c     | 103 +++++++++++++++++++++++++++++---------------
 fs/erofs/internal.h |   4 +-
 fs/erofs/zmap.c     |  29 ++++++++++---
 3 files changed, 94 insertions(+), 42 deletions(-)

diff --git a/fs/erofs/data.c b/fs/erofs/data.c
index 2fe345d80ee..d10c00fe9f3 100644
--- a/fs/erofs/data.c
+++ b/fs/erofs/data.c
@@ -174,32 +174,60 @@ int erofs_read_one_data(struct erofs_map_blocks *map, char *buffer, u64 offset,
 	return 0;
 }
 
+struct erofs_map_blocks *erofs_alloc_map_blocks(void)
+{
+	struct erofs_map_blocks *map;
+
+	map = malloc(sizeof(struct erofs_map_blocks));
+	if (!map)
+		return NULL;
+
+	memset(map, 0, sizeof(struct erofs_map_blocks));
+	map->index = UINT_MAX;
+
+	map->mpage = malloc_cache_aligned(EROFS_MAX_BLOCK_SIZE);
+	if (!map->mpage) {
+		free(map);
+		return NULL;
+	}
+
+	return map;
+}
+
+void erofs_free_map_blocks(struct erofs_map_blocks *map)
+{
+	free(map->mpage);
+	free(map);
+}
+
 static int erofs_read_raw_data(struct erofs_inode *inode, char *buffer,
 			       erofs_off_t size, erofs_off_t offset)
 {
-	struct erofs_map_blocks map = {
-		.index = UINT_MAX,
-	};
+	struct erofs_map_blocks *map;
 	int ret;
 	erofs_off_t ptr = offset;
 
+	map = erofs_alloc_map_blocks();
+	if (!map)
+		return -ENOMEM;
+
 	while (ptr < offset + size) {
 		char *const estart = buffer + ptr - offset;
 		erofs_off_t eend, moff = 0;
 
-		map.m_la = ptr;
-		ret = erofs_map_blocks(inode, &map, 0);
+		map->m_la = ptr;
+		ret = erofs_map_blocks(inode, map, 0);
 		if (ret)
-			return ret;
+			goto out;
 
-		DBG_BUGON(map.m_plen != map.m_llen);
+		DBG_BUGON(map->m_plen != map->m_llen);
 
 		/* trim extent */
-		eend = min(offset + size, map.m_la + map.m_llen);
-		DBG_BUGON(ptr < map.m_la);
+		eend = min(offset + size, map->m_la + map->m_llen);
+		DBG_BUGON(ptr < map->m_la);
 
-		if (!(map.m_flags & EROFS_MAP_MAPPED)) {
-			if (!map.m_llen) {
+		if (!(map->m_flags & EROFS_MAP_MAPPED)) {
+			if (!map->m_llen) {
 				/* reached EOF */
 				memset(estart, 0, offset + size - ptr);
 				ptr = offset + size;
@@ -210,17 +238,21 @@ static int erofs_read_raw_data(struct erofs_inode *inode, char *buffer,
 			continue;
 		}
 
-		if (ptr > map.m_la) {
-			moff = ptr - map.m_la;
-			map.m_la = ptr;
+		if (ptr > map->m_la) {
+			moff = ptr - map->m_la;
+			map->m_la = ptr;
 		}
 
-		ret = erofs_read_one_data(&map, estart, moff, eend - map.m_la);
+		ret = erofs_read_one_data(map, estart, moff, eend - map->m_la);
 		if (ret)
-			return ret;
+			goto out;
 		ptr = eend;
 	}
-	return 0;
+
+	ret = 0;
+out:
+	erofs_free_map_blocks(map);
+	return ret;
 }
 
 int z_erofs_read_one_data(struct erofs_inode *inode,
@@ -282,19 +314,21 @@ static int z_erofs_read_data(struct erofs_inode *inode, char *buffer,
 			     erofs_off_t size, erofs_off_t offset)
 {
 	erofs_off_t end, length, skip;
-	struct erofs_map_blocks map = {
-		.index = UINT_MAX,
-	};
+	struct erofs_map_blocks *map;
 	bool trimmed;
 	unsigned int bufsize = 0;
 	char *raw = NULL;
 	int ret = 0;
 
+	map = erofs_alloc_map_blocks();
+	if (!map)
+		return -ENOMEM;
+
 	end = offset + size;
 	while (end > offset) {
-		map.m_la = end - 1;
+		map->m_la = end - 1;
 
-		ret = z_erofs_map_blocks_iter(inode, &map, 0);
+		ret = z_erofs_map_blocks_iter(inode, map, 0);
 		if (ret)
 			break;
 
@@ -302,31 +336,31 @@ static int z_erofs_read_data(struct erofs_inode *inode, char *buffer,
 		 * trim to the needed size if the returned extent is quite
 		 * larger than requested, and set up partial flag as well.
 		 */
-		if (end < map.m_la + map.m_llen) {
-			length = end - map.m_la;
+		if (end < map->m_la + map->m_llen) {
+			length = end - map->m_la;
 			trimmed = true;
 		} else {
-			DBG_BUGON(end != map.m_la + map.m_llen);
-			length = map.m_llen;
+			DBG_BUGON(end != map->m_la + map->m_llen);
+			length = map->m_llen;
 			trimmed = false;
 		}
 
-		if (map.m_la < offset) {
-			skip = offset - map.m_la;
+		if (map->m_la < offset) {
+			skip = offset - map->m_la;
 			end = offset;
 		} else {
 			skip = 0;
-			end = map.m_la;
+			end = map->m_la;
 		}
 
-		if (!(map.m_flags & EROFS_MAP_MAPPED)) {
+		if (!(map->m_flags & EROFS_MAP_MAPPED)) {
 			memset(buffer + end - offset, 0, length - skip);
-			end = map.m_la;
+			end = map->m_la;
 			continue;
 		}
 
-		if (map.m_plen > bufsize) {
-			bufsize = map.m_plen;
+		if (map->m_plen > bufsize) {
+			bufsize = map->m_plen;
 			free(raw);
 			raw = malloc_cache_aligned(bufsize);
 			if (!raw) {
@@ -335,13 +369,14 @@ static int z_erofs_read_data(struct erofs_inode *inode, char *buffer,
 			}
 		}
 
-		ret = z_erofs_read_one_data(inode, &map, raw,
+		ret = z_erofs_read_one_data(inode, map, raw,
 					    buffer + end - offset, skip, length,
 					    trimmed);
 		if (ret < 0)
 			break;
 	}
 	free(raw);
+	erofs_free_map_blocks(map);
 	return ret < 0 ? ret : 0;
 }
 
diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
index 13c862325a6..2e471d66c7d 100644
--- a/fs/erofs/internal.h
+++ b/fs/erofs/internal.h
@@ -291,7 +291,7 @@ enum {
 #define EROFS_MAP_PARTIAL_REF	(1 << BH_Partialref)
 
 struct erofs_map_blocks {
-	char mpage[EROFS_MAX_BLOCK_SIZE];
+	char *mpage;
 
 	erofs_off_t m_pa, m_la;
 	u64 m_plen, m_llen;
@@ -367,6 +367,8 @@ static inline int erofs_get_occupied_size(const struct erofs_inode *inode,
 }
 
 /* data.c */
+struct erofs_map_blocks *erofs_alloc_map_blocks(void);
+void erofs_free_map_blocks(struct erofs_map_blocks *map);
 int erofs_getxattr(struct erofs_inode *vi, const char *name, char *buffer,
 		   size_t buffer_size);
 int erofs_listxattr(struct erofs_inode *vi, char *buffer, size_t buffer_size);
diff --git a/fs/erofs/zmap.c b/fs/erofs/zmap.c
index 1ded934a5d7..3060b2e9ec6 100644
--- a/fs/erofs/zmap.c
+++ b/fs/erofs/zmap.c
@@ -75,28 +75,43 @@ static int z_erofs_fill_inode_lazy(struct erofs_inode *vi)
 	}
 
 	if (vi->z_advise & Z_EROFS_ADVISE_INLINE_PCLUSTER) {
-		struct erofs_map_blocks map = { .index = UINT_MAX };
+		struct erofs_map_blocks *map;
+
+		map = erofs_alloc_map_blocks();
+		if (!map) {
+			ret = -ENOMEM;
+			goto err_out;
+		}
 
 		vi->idata_size = le16_to_cpu(h->h_idata_size);
-		ret = z_erofs_do_map_blocks(vi, &map,
+		ret = z_erofs_do_map_blocks(vi, map,
 					    EROFS_GET_BLOCKS_FINDTAIL);
-		if (!map.m_plen ||
-		    erofs_blkoff(map.m_pa) + map.m_plen > erofs_blksiz()) {
+		if (!map->m_plen ||
+		    erofs_blkoff(map->m_pa) + map->m_plen > erofs_blksiz()) {
 			erofs_err("invalid tail-packing pclustersize %llu",
-				  map.m_plen | 0ULL);
+				  map->m_plen | 0ULL);
 			ret = -EFSCORRUPTED;
+			erofs_free_map_blocks(map);
 			goto err_out;
 		}
+		erofs_free_map_blocks(map);
 		if (ret < 0)
 			goto err_out;
 	}
 	if (vi->z_advise & Z_EROFS_ADVISE_FRAGMENT_PCLUSTER &&
 	    !(h->h_clusterbits >> Z_EROFS_FRAGMENT_INODE_BIT)) {
-		struct erofs_map_blocks map = { .index = UINT_MAX };
+		struct erofs_map_blocks *map;
+
+		map = erofs_alloc_map_blocks();
+		if (!map) {
+			ret = -ENOMEM;
+			goto err_out;
+		}
 
 		vi->fragmentoff = le32_to_cpu(h->h_fragmentoff);
-		ret = z_erofs_do_map_blocks(vi, &map,
+		ret = z_erofs_do_map_blocks(vi, map,
 					    EROFS_GET_BLOCKS_FINDTAIL);
+		erofs_free_map_blocks(map);
 		if (ret < 0)
 			goto err_out;
 	}
-- 
2.47.3



More information about the Linux-erofs mailing list