summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@kernel.org>2021-10-07 17:54:45 -0700
committerDarrick J. Wong <djwong@kernel.org>2021-10-22 16:41:17 -0700
commit014cdddffcddd5b13402721de744b9ea76961d74 (patch)
tree9c0641a8cfad09d0e02c9b9e57772a046d0f1a42
parent04e01af9318f50df5e1974d8e2ce7d54b6cfbe55 (diff)
xfs: use b_offset to support direct-mapping pages when blocksize < pagesizedirect-xfile-mapped-buffers_2021-10-22
Support using directly-mapped pages in the buffer cache when the fs blocksize is less than the page size. Signed-off-by: Darrick J. Wong <djwong@kernel.org>
-rw-r--r--fs/xfs/scrub/xfbtree.c2
-rw-r--r--fs/xfs/xfs_buf.c32
2 files changed, 28 insertions, 6 deletions
diff --git a/fs/xfs/scrub/xfbtree.c b/fs/xfs/scrub/xfbtree.c
index 1098234eec3b..bde856391c29 100644
--- a/fs/xfs/scrub/xfbtree.c
+++ b/fs/xfs/scrub/xfbtree.c
@@ -240,7 +240,7 @@ xfbtree_create(
goto err_xfile;
}
- if (mp->m_bsize == PAGE_SIZE && (flags & XFBTREE_DIRECT_MAP))
+ if (flags & XFBTREE_DIRECT_MAP)
xfbt->target->bt_flags |= XFS_BUFTARG_DIRECT_MAP;
xfbt->freespace = kmem_alloc(sizeof(struct xbitmap),
diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
index 5d1eebee2555..15259ccf212f 100644
--- a/fs/xfs/xfs_buf.c
+++ b/fs/xfs/xfs_buf.c
@@ -318,7 +318,7 @@ xfs_buf_free_pages(
ASSERT(bp->b_flags & _XBF_PAGES);
if (xfs_buf_is_vmapped(bp))
- vm_unmap_ram(bp->b_addr, bp->b_page_count);
+ vm_unmap_ram(bp->b_addr - bp->b_offset, bp->b_page_count);
for (i = 0; i < bp->b_page_count; i++) {
if (bp->b_pages[i])
@@ -438,6 +438,8 @@ xfs_buf_alloc_pages(
XFS_STATS_INC(bp->b_mount, xb_page_retries);
congestion_wait(BLK_RW_ASYNC, HZ / 50);
}
+
+ bp->b_offset = 0;
return 0;
}
@@ -453,16 +455,32 @@ xfs_buf_alloc_direct_pages(
struct xfs_buf_map *map;
gfp_t gfp_mask = __GFP_NOWARN;
const unsigned int page_align_mask = PAGE_SIZE - 1;
+ unsigned int first_page_offset;
unsigned int m, p, n;
int error;
ASSERT(bp->b_target->bt_flags & XFS_BUFTARG_IN_MEMORY);
- /* For direct-map buffers, each map has to be page aligned. */
- for (m = 0, map = bp->b_maps; m < bp->b_map_count; m++, map++)
- if (BBTOB(map->bm_bn | map->bm_len) & page_align_mask)
+ /*
+ * For direct-map buffer targets with multiple mappings, the first map
+ * must end on a page boundary; the last map must start at a page
+ * boundary; and the maps in between must start and end on a page
+ * boundary. For single-mapping buffers, we don't care.
+ */
+ if (bp->b_map_count > 1) {
+ map = &bp->b_maps[bp->b_map_count - 1];
+ if (BBTOB(map->bm_bn) & page_align_mask)
return -ENOTBLK;
+ map = &bp->b_maps[0];
+ if (BBTOB(map->bm_bn + map->bm_len) & page_align_mask)
+ return -ENOTBLK;
+
+ for (m = 1, map++; m < bp->b_map_count - 1; m++, map++)
+ if (BBTOB(map->bm_bn | map->bm_len) & page_align_mask)
+ return -ENOTBLK;
+ }
+
if (flags & XBF_READ_AHEAD)
gfp_mask |= __GFP_NORETRY;
else
@@ -480,6 +498,7 @@ xfs_buf_alloc_direct_pages(
}
/* Map in the xfile pages. */
+ first_page_offset = offset_in_page(BBTOB(xfs_buf_daddr(bp)));
for (m = 0, p = 0, map = bp->b_maps; m < bp->b_map_count; m++, map++) {
for (n = 0; n < map->bm_len; n += BTOBB(PAGE_SIZE)) {
unsigned int len;
@@ -502,6 +521,7 @@ xfs_buf_alloc_direct_pages(
}
bp->b_flags |= _XBF_DIRECT_MAP;
+ bp->b_offset = first_page_offset;
return 0;
fail:
@@ -540,7 +560,7 @@ _xfs_buf_map_pages(
if (bp->b_page_count == 1) {
/* A single page buffer is always mappable */
- bp->b_addr = page_address(bp->b_pages[0]);
+ bp->b_addr = page_address(bp->b_pages[0]) + bp->b_offset;
} else if (flags & XBF_UNMAPPED) {
bp->b_addr = NULL;
} else {
@@ -567,6 +587,8 @@ _xfs_buf_map_pages(
if (!bp->b_addr)
return -ENOMEM;
+
+ bp->b_addr += bp->b_offset;
}
return 0;