[RFC v2 8/8] shmem: add support to customize block size order

Luis Chamberlain
2023-05-26T07:56:41Z

This allows tmpfs mounts to use a custom block size order. We
only allow block sizes greater than PAGE_SIZE, and these must
also be a multiple of the PAGE_SIZE too. To simplify these
requirements and the math we just use power of 2 order, so
block order.

Only simple tests have been run so far:

mkdir -p /data-tmpfs/
time for i in $(seq 1 1000000); do echo $i >> /root/ordered.txt; done

real 0m21.392s
user 0m8.077s
sys 0m13.098s

du -h /root/ordered.txt
6.6M /root/ordered.txt

sha1sum /root/ordered.txt
2dcc06b7ca3b7dd8b5626af83c1be3cb08ddc76c /root/ordered.txt

stat /root/ordered.txt
File: /root/ordered.txt
Size: 6888896 Blocks: 13456 IO Block: 4096 regular file
Device: 254,1 Inode: 655717 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2023-04-21 19:34:20.709869093 +0000
Modify: 2023-04-21 19:34:43.833900042 +0000
Change: 2023-04-21 19:34:43.833900042 +0000
Birth: 2023-04-21 19:34:20.709869093 +0000

8 KiB block size:

sha1sum /root/ordered.txt
mount -t tmpfs -o size=10M,border=13 -o noswap tmpfs /data-tmpfs/
cp /root/ordered.txt
sha1sum /data-tmpfs/ordered.txt
stat /data-tmpfs/ordered.txt
2dcc06b7ca3b7dd8b5626af83c1be3cb08ddc76c /root/ordered.txt
2dcc06b7ca3b7dd8b5626af83c1be3cb08ddc76c /data-tmpfs/ordered.txt
File: /data-tmpfs/ordered.txt
Size: 6888896 Blocks: 13456 IO Block: 8192 regular file
Device: 0,42 Inode: 2 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2023-04-21 19:31:16.078390405 +0000
Modify: 2023-04-21 19:31:16.070391363 +0000
Change: 2023-04-21 19:31:16.070391363 +0000
Birth: 2023-04-21 19:31:16.034395676 +0000

64 KiB block size:

sha1sum /root/ordered.txt
mount -t tmpfs -o size=10M,border=16 -o noswap tmpfs /data-tmpfs/
cp /root/ordered.txt /data-tmpfs/; sha1sum /data-tmpfs/ordered.txt
stat /data-tmpfs/ordered.txt
2dcc06b7ca3b7dd8b5626af83c1be3cb08ddc76c /root/ordered.txt
2dcc06b7ca3b7dd8b5626af83c1be3cb08ddc76c /data-tmpfs/ordered.txt
File: /data-tmpfs/ordered.txt
Size: 6888896 Blocks: 13568 IO Block: 65536 regular file
Device: 0,42 Inode: 2 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2023-04-21 19:32:14.669796970 +0000
Modify: 2023-04-21 19:32:14.661796959 +0000
Change: 2023-04-21 19:32:14.661796959 +0000
Birth: 2023-04-21 19:32:14.649796944 +0000

4 MiB works too.

Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
---
mm/shmem.c | 44 +++++++++++++++++++++++++++++++++++++++-----
1 file changed, 39 insertions(+), 5 deletions(-)

diff --git a/mm/shmem.c b/mm/shmem.c
index 080864949fe5..777e953df62e 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -115,11 +115,13 @@ struct shmem_options {
int huge;
int seen;
bool noswap;
+ unsigned char block_order;
#define SHMEM_SEEN_BLOCKS 1
#define SHMEM_SEEN_INODES 2
#define SHMEM_SEEN_HUGE 4
#define SHMEM_SEEN_INUMS 8
#define SHMEM_SEEN_NOSWAP 16
+#define SHMEM_SEEN_BLOCKORDER 32
};

static u64 shmem_default_block_order(void)
@@ -3661,6 +3663,7 @@ enum shmem_param {
Opt_inode32,
Opt_inode64,
Opt_noswap,
+ Opt_border,
};

static const struct constant_table shmem_param_enums_huge[] = {
@@ -3683,6 +3686,7 @@ const struct fs_parameter_spec shmem_fs_parameters[] = {
fsparam_flag ("inode32", Opt_inode32),
fsparam_flag ("inode64", Opt_inode64),
fsparam_flag ("noswap", Opt_noswap),
+ fsparam_u32 ("border", Opt_border),
{}
};

@@ -3709,7 +3713,15 @@ static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param)
}
if (*rest)
goto bad_value;
- ctx->blocks = DIV_ROUND_UP(size, PAGE_SIZE);
+ if (!(ctx->seen & SHMEM_SEEN_BLOCKORDER) ||
+ ctx->block_order == shmem_default_block_order())
+ ctx->blocks = DIV_ROUND_UP(size, PAGE_SIZE);
+ else {
+ if (size < (1UL << ctx->block_order) ||
+ size % (1UL << ctx->block_order) != 0)
+ goto bad_value;
+ ctx->blocks = size >> ctx->block_order;
+ }
ctx->seen |= SHMEM_SEEN_BLOCKS;
break;
case Opt_nr_blocks:
@@ -3774,6 +3786,19 @@ static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param)
ctx->noswap = true;
ctx->seen |= SHMEM_SEEN_NOSWAP;
break;
+ case Opt_border:
+ ctx->block_order = result.uint_32;
+ ctx->seen |= SHMEM_SEEN_BLOCKORDER;
+ if (ctx->block_order < PAGE_SHIFT)
+ goto bad_value;
+ /*
+ * We cap this to allow a block to be at least allowed to
+ * be allocated using the buddy allocator. That's MAX_ORDER
+ * pages. So 8 MiB on x86_64.
+ */
+ if (ctx->block_order > (MAX_ORDER + PAGE_SHIFT))
+ goto bad_value;
+ break;
}
return 0;

@@ -3845,6 +3870,12 @@ static int shmem_reconfigure(struct fs_context *fc)
raw_spin_lock(&sbinfo->stat_lock);
inodes = sbinfo->max_inodes - sbinfo->free_inodes;

+ if (ctx->seen & SHMEM_SEEN_BLOCKORDER) {
+ if (ctx->block_order != shmem_block_order(sbinfo)) {
+ err = "Cannot modify block order on remount";
+ goto out;
+ }
+ }
if ((ctx->seen & SHMEM_SEEN_BLOCKS) && ctx->blocks) {
if (!sbinfo->max_blocks) {
err = "Cannot retroactively limit size";
@@ -3960,6 +3991,8 @@ static int shmem_show_options(struct seq_file *seq, struct dentry *root)
shmem_show_mpol(seq, sbinfo->mpol);
if (sbinfo->noswap)
seq_printf(seq, ",noswap");
+ if (shmem_block_order(sbinfo) != shmem_default_block_order())
+ seq_printf(seq, ",border=%llu", shmem_block_order(sbinfo));
return 0;
}

@@ -3997,10 +4030,12 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc)
* but the internal instance is left unlimited.
*/
if (!(sb->s_flags & SB_KERNMOUNT)) {
+ if (!(ctx->seen & SHMEM_SEEN_BLOCKORDER))
+ ctx->block_order = shmem_default_block_order();
if (!(ctx->seen & SHMEM_SEEN_BLOCKS))
- ctx->blocks = shmem_default_max_blocks(shmem_default_block_order());
+ ctx->blocks = shmem_default_max_blocks(ctx->block_order);
if (!(ctx->seen & SHMEM_SEEN_INODES))
- ctx->inodes = shmem_default_max_inodes(shmem_default_block_order());
+ ctx->inodes = shmem_default_max_inodes(ctx->block_order);
if (!(ctx->seen & SHMEM_SEEN_INUMS))
ctx->full_inums = IS_ENABLED(CONFIG_TMPFS_INODE64);
sbinfo->noswap = ctx->noswap;
@@ -4009,7 +4044,7 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc)
}
sb->s_export_op = &shmem_export_ops;
sb->s_flags |= SB_NOSEC | SB_I_VERSION;
- sbinfo->block_order = shmem_default_block_order();
+ sbinfo->block_order = ctx->block_order;
#else
sb->s_flags |= SB_NOUSER;
#endif
@@ -4037,7 +4072,6 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_blocksize = shmem_sb_blocksize(sbinfo);
sb->s_blocksize_bits = shmem_block_order(sbinfo);
- WARN_ON_ONCE(sb->s_blocksize_bits != PAGE_SHIFT);
sb->s_magic = TMPFS_MAGIC;
sb->s_op = &shmem_ops;
sb->s_time_gran = 1;
--
2.39.2