Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

squashfs: add the mount parameter theads=<single|multi|percpu>

Patch series 'squashfs: Add the mount parameter "threads="'.

Currently, Squashfs supports multiple decompressor parallel modes.
However, this mode can be configured only during kernel building and does
not support flexible selection during runtime.

In the current patch set, the mount parameter "threads=" is added to allow
users to select the parallel decompressor mode and configure the number of
decompressors when mounting a file system.

"threads=<single|multi|percpu|1|2|3|...>"
The upper limit is num_online_cpus() * 2.


This patch (of 2):

Squashfs supports three decompression concurrency modes:
Single-thread mode: concurrent reads are blocked and the memory
overhead is small.
Multi-thread mode/percpu mode: reduces concurrent read blocking but
increases memory overhead.

The corresponding schema must be fixed at compile time. During mounting,
the concurrent decompression mode cannot be adjusted based on file read
blocking.

The mount parameter theads=<single|multi|percpu> is added to select
the concurrent decompression mode of a single SquashFS file system
image.

Link: https://lkml.kernel.org/r/20221019030930.130456-1-nixiaoming@huawei.com
Link: https://lkml.kernel.org/r/20221019030930.130456-2-nixiaoming@huawei.com
Signed-off-by: Xiaoming Ni <nixiaoming@huawei.com>
Reviewed-by: Phillip Lougher <phillip@squashfs.org.uk>
Cc: Jianguo Chen <chenjianguo3@huawei.com>
Cc: Jubin Zhong <zhongjubin@huawei.com>
Cc: Zhang Yi <yi.zhang@huawei.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Xiaoming Ni and committed by
Andrew Morton
80f78409 4197530b

+148 -33
+35 -6
fs/squashfs/Kconfig
··· 54 54 55 55 endchoice 56 56 57 - choice 58 - prompt "Decompressor parallelisation options" 57 + config SQUASHFS_DECOMP_SINGLE 59 58 depends on SQUASHFS 59 + def_bool n 60 + 61 + config SQUASHFS_DECOMP_MULTI 62 + depends on SQUASHFS 63 + def_bool n 64 + 65 + config SQUASHFS_DECOMP_MULTI_PERCPU 66 + depends on SQUASHFS 67 + def_bool n 68 + 69 + config SQUASHFS_CHOICE_DECOMP_BY_MOUNT 70 + bool "Select the parallel decompression mode during mount" 71 + depends on SQUASHFS 72 + default n 73 + select SQUASHFS_DECOMP_SINGLE 74 + select SQUASHFS_DECOMP_MULTI 75 + select SQUASHFS_DECOMP_MULTI_PERCPU 76 + help 77 + Compile all parallel decompression modes and specify the 78 + decompression mode by setting "threads=" during mount. 79 + threads=<single|multi|percpu> 80 + 81 + default Decompressor parallelisation is SQUASHFS_DECOMP_SINGLE 82 + 83 + choice 84 + prompt "Select decompression parallel mode at compile time" 85 + depends on SQUASHFS 86 + depends on !SQUASHFS_CHOICE_DECOMP_BY_MOUNT 60 87 help 61 88 Squashfs now supports three parallelisation options for 62 89 decompression. Each one exhibits various trade-offs between ··· 91 64 92 65 If in doubt, select "Single threaded compression" 93 66 94 - config SQUASHFS_DECOMP_SINGLE 67 + config SQUASHFS_COMPILE_DECOMP_SINGLE 95 68 bool "Single threaded compression" 69 + select SQUASHFS_DECOMP_SINGLE 96 70 help 97 71 Traditionally Squashfs has used single-threaded decompression. 98 72 Only one block (data or metadata) can be decompressed at any 99 73 one time. This limits CPU and memory usage to a minimum. 100 74 101 - config SQUASHFS_DECOMP_MULTI 75 + config SQUASHFS_COMPILE_DECOMP_MULTI 102 76 bool "Use multiple decompressors for parallel I/O" 77 + select SQUASHFS_DECOMP_MULTI 103 78 help 104 79 By default Squashfs uses a single decompressor but it gives 105 80 poor performance on parallel I/O workloads when using multiple CPU ··· 114 85 decompressors per core. It dynamically allocates decompressors 115 86 on a demand basis. 116 87 117 - config SQUASHFS_DECOMP_MULTI_PERCPU 88 + config SQUASHFS_COMPILE_DECOMP_MULTI_PERCPU 118 89 bool "Use percpu multiple decompressors for parallel I/O" 90 + select SQUASHFS_DECOMP_MULTI_PERCPU 119 91 help 120 92 By default Squashfs uses a single decompressor but it gives 121 93 poor performance on parallel I/O workloads when using multiple CPU ··· 125 95 This decompressor implementation uses a maximum of one 126 96 decompressor per core. It uses percpu variables to ensure 127 97 decompression is load-balanced across the cores. 128 - 129 98 endchoice 130 99 131 100 config SQUASHFS_XATTR
+1 -1
fs/squashfs/block.c
··· 216 216 res = -EIO; 217 217 goto out_free_bio; 218 218 } 219 - res = squashfs_decompress(msblk, bio, offset, length, output); 219 + res = msblk->thread_ops->decompress(msblk, bio, offset, length, output); 220 220 } else { 221 221 res = copy_bio_to_actor(bio, output, offset, length); 222 222 }
+1 -1
fs/squashfs/decompressor.c
··· 134 134 if (IS_ERR(comp_opts)) 135 135 return comp_opts; 136 136 137 - stream = squashfs_decompressor_create(msblk, comp_opts); 137 + stream = msblk->thread_ops->create(msblk, comp_opts); 138 138 if (IS_ERR(stream)) 139 139 kfree(comp_opts); 140 140
+11 -5
fs/squashfs/decompressor_multi.c
··· 29 29 #define MAX_DECOMPRESSOR (num_online_cpus() * 2) 30 30 31 31 32 - int squashfs_max_decompressors(void) 32 + static int squashfs_max_decompressors(void) 33 33 { 34 34 return MAX_DECOMPRESSOR; 35 35 } 36 - 37 36 38 37 struct squashfs_stream { 39 38 void *comp_opts; ··· 58 59 wake_up(&stream->wait); 59 60 } 60 61 61 - void *squashfs_decompressor_create(struct squashfs_sb_info *msblk, 62 + static void *squashfs_decompressor_create(struct squashfs_sb_info *msblk, 62 63 void *comp_opts) 63 64 { 64 65 struct squashfs_stream *stream; ··· 102 103 } 103 104 104 105 105 - void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk) 106 + static void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk) 106 107 { 107 108 struct squashfs_stream *stream = msblk->stream; 108 109 if (stream) { ··· 179 180 } 180 181 181 182 182 - int squashfs_decompress(struct squashfs_sb_info *msblk, struct bio *bio, 183 + static int squashfs_decompress(struct squashfs_sb_info *msblk, struct bio *bio, 183 184 int offset, int length, 184 185 struct squashfs_page_actor *output) 185 186 { ··· 194 195 msblk->decompressor->name); 195 196 return res; 196 197 } 198 + 199 + const struct squashfs_decompressor_thread_ops squashfs_decompressor_multi = { 200 + .create = squashfs_decompressor_create, 201 + .destroy = squashfs_decompressor_destroy, 202 + .decompress = squashfs_decompress, 203 + .max_decompressors = squashfs_max_decompressors, 204 + };
+16 -7
fs/squashfs/decompressor_multi_percpu.c
··· 25 25 local_lock_t lock; 26 26 }; 27 27 28 - void *squashfs_decompressor_create(struct squashfs_sb_info *msblk, 28 + static void *squashfs_decompressor_create(struct squashfs_sb_info *msblk, 29 29 void *comp_opts) 30 30 { 31 31 struct squashfs_stream *stream; ··· 59 59 return ERR_PTR(err); 60 60 } 61 61 62 - void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk) 62 + static void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk) 63 63 { 64 64 struct squashfs_stream __percpu *percpu = 65 65 (struct squashfs_stream __percpu *) msblk->stream; ··· 75 75 } 76 76 } 77 77 78 - int squashfs_decompress(struct squashfs_sb_info *msblk, struct bio *bio, 78 + static int squashfs_decompress(struct squashfs_sb_info *msblk, struct bio *bio, 79 79 int offset, int length, struct squashfs_page_actor *output) 80 80 { 81 81 struct squashfs_stream *stream; 82 + struct squashfs_stream __percpu *percpu = 83 + (struct squashfs_stream __percpu *) msblk->stream; 82 84 int res; 83 85 84 - local_lock(&msblk->stream->lock); 85 - stream = this_cpu_ptr(msblk->stream); 86 + local_lock(&percpu->lock); 87 + stream = this_cpu_ptr(percpu); 86 88 87 89 res = msblk->decompressor->decompress(msblk, stream->stream, bio, 88 90 offset, length, output); 89 91 90 - local_unlock(&msblk->stream->lock); 92 + local_unlock(&percpu->lock); 91 93 92 94 if (res < 0) 93 95 ERROR("%s decompression failed, data probably corrupt\n", ··· 98 96 return res; 99 97 } 100 98 101 - int squashfs_max_decompressors(void) 99 + static int squashfs_max_decompressors(void) 102 100 { 103 101 return num_possible_cpus(); 104 102 } 103 + 104 + const struct squashfs_decompressor_thread_ops squashfs_decompressor_percpu = { 105 + .create = squashfs_decompressor_create, 106 + .destroy = squashfs_decompressor_destroy, 107 + .decompress = squashfs_decompress, 108 + .max_decompressors = squashfs_max_decompressors, 109 + };
+11 -4
fs/squashfs/decompressor_single.c
··· 24 24 struct mutex mutex; 25 25 }; 26 26 27 - void *squashfs_decompressor_create(struct squashfs_sb_info *msblk, 27 + static void *squashfs_decompressor_create(struct squashfs_sb_info *msblk, 28 28 void *comp_opts) 29 29 { 30 30 struct squashfs_stream *stream; ··· 49 49 return ERR_PTR(err); 50 50 } 51 51 52 - void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk) 52 + static void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk) 53 53 { 54 54 struct squashfs_stream *stream = msblk->stream; 55 55 ··· 59 59 } 60 60 } 61 61 62 - int squashfs_decompress(struct squashfs_sb_info *msblk, struct bio *bio, 62 + static int squashfs_decompress(struct squashfs_sb_info *msblk, struct bio *bio, 63 63 int offset, int length, 64 64 struct squashfs_page_actor *output) 65 65 { ··· 78 78 return res; 79 79 } 80 80 81 - int squashfs_max_decompressors(void) 81 + static int squashfs_max_decompressors(void) 82 82 { 83 83 return 1; 84 84 } 85 + 86 + const struct squashfs_decompressor_thread_ops squashfs_decompressor_single = { 87 + .create = squashfs_decompressor_create, 88 + .destroy = squashfs_decompressor_destroy, 89 + .decompress = squashfs_decompress, 90 + .max_decompressors = squashfs_max_decompressors, 91 + };
+18 -5
fs/squashfs/squashfs.h
··· 38 38 extern void *squashfs_decompressor_setup(struct super_block *, unsigned short); 39 39 40 40 /* decompressor_xxx.c */ 41 - extern void *squashfs_decompressor_create(struct squashfs_sb_info *, void *); 42 - extern void squashfs_decompressor_destroy(struct squashfs_sb_info *); 43 - extern int squashfs_decompress(struct squashfs_sb_info *, struct bio *, 44 - int, int, struct squashfs_page_actor *); 45 - extern int squashfs_max_decompressors(void); 41 + 42 + struct squashfs_decompressor_thread_ops { 43 + void * (*create)(struct squashfs_sb_info *msblk, void *comp_opts); 44 + void (*destroy)(struct squashfs_sb_info *msblk); 45 + int (*decompress)(struct squashfs_sb_info *msblk, struct bio *bio, 46 + int offset, int length, struct squashfs_page_actor *output); 47 + int (*max_decompressors)(void); 48 + }; 49 + 50 + #ifdef CONFIG_SQUASHFS_DECOMP_SINGLE 51 + extern const struct squashfs_decompressor_thread_ops squashfs_decompressor_single; 52 + #endif 53 + #ifdef CONFIG_SQUASHFS_DECOMP_MULTI 54 + extern const struct squashfs_decompressor_thread_ops squashfs_decompressor_multi; 55 + #endif 56 + #ifdef CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU 57 + extern const struct squashfs_decompressor_thread_ops squashfs_decompressor_percpu; 58 + #endif 46 59 47 60 /* export.c */ 48 61 extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64, u64,
+2 -1
fs/squashfs/squashfs_fs_sb.h
··· 53 53 __le64 *xattr_id_table; 54 54 struct mutex meta_index_mutex; 55 55 struct meta_index *meta_index; 56 - struct squashfs_stream *stream; 56 + void *stream; 57 57 __le64 *inode_lookup_table; 58 58 u64 inode_table; 59 59 u64 directory_table; ··· 66 66 int xattr_ids; 67 67 unsigned int ids; 68 68 bool panic_on_errors; 69 + const struct squashfs_decompressor_thread_ops *thread_ops; 69 70 }; 70 71 #endif
+53 -3
fs/squashfs/super.c
··· 47 47 48 48 enum squashfs_param { 49 49 Opt_errors, 50 + Opt_threads, 50 51 }; 51 52 52 53 struct squashfs_mount_opts { 53 54 enum Opt_errors errors; 55 + const struct squashfs_decompressor_thread_ops *thread_ops; 54 56 }; 55 57 56 58 static const struct constant_table squashfs_param_errors[] = { ··· 63 61 64 62 static const struct fs_parameter_spec squashfs_fs_parameters[] = { 65 63 fsparam_enum("errors", Opt_errors, squashfs_param_errors), 64 + fsparam_string("threads", Opt_threads), 66 65 {} 67 66 }; 67 + 68 + static int squashfs_parse_param_threads(const char *str, struct squashfs_mount_opts *opts) 69 + { 70 + #ifdef CONFIG_SQUASHFS_CHOICE_DECOMP_BY_MOUNT 71 + if (strcmp(str, "single") == 0) { 72 + opts->thread_ops = &squashfs_decompressor_single; 73 + return 0; 74 + } 75 + if (strcmp(str, "multi") == 0) { 76 + opts->thread_ops = &squashfs_decompressor_multi; 77 + return 0; 78 + } 79 + if (strcmp(str, "percpu") == 0) { 80 + opts->thread_ops = &squashfs_decompressor_percpu; 81 + return 0; 82 + } 83 + #endif 84 + return -EINVAL; 85 + } 68 86 69 87 static int squashfs_parse_param(struct fs_context *fc, struct fs_parameter *param) 70 88 { ··· 99 77 switch (opt) { 100 78 case Opt_errors: 101 79 opts->errors = result.uint_32; 80 + break; 81 + case Opt_threads: 82 + if (squashfs_parse_param_threads(param->string, opts) != 0) 83 + return -EINVAL; 102 84 break; 103 85 default: 104 86 return -EINVAL; ··· 193 167 sb->s_bdev); 194 168 goto failed_mount; 195 169 } 170 + msblk->thread_ops = opts->thread_ops; 196 171 197 172 /* Check the MAJOR & MINOR versions and lookup compression type */ 198 173 msblk->decompressor = supported_squashfs_filesystem( ··· 279 252 280 253 /* Allocate read_page block */ 281 254 msblk->read_page = squashfs_cache_init("data", 282 - squashfs_max_decompressors(), msblk->block_size); 255 + msblk->thread_ops->max_decompressors(), msblk->block_size); 283 256 if (msblk->read_page == NULL) { 284 257 errorf(fc, "Failed to allocate read_page block"); 285 258 goto failed_mount; ··· 410 383 squashfs_cache_delete(msblk->block_cache); 411 384 squashfs_cache_delete(msblk->fragment_cache); 412 385 squashfs_cache_delete(msblk->read_page); 413 - squashfs_decompressor_destroy(msblk); 386 + msblk->thread_ops->destroy(msblk); 414 387 kfree(msblk->inode_lookup_table); 415 388 kfree(msblk->fragment_index); 416 389 kfree(msblk->id_table); ··· 462 435 else 463 436 seq_puts(s, ",errors=continue"); 464 437 438 + #ifdef CONFIG_SQUASHFS_CHOICE_DECOMP_BY_MOUNT 439 + if (msblk->thread_ops == &squashfs_decompressor_single) { 440 + seq_puts(s, ",threads=single"); 441 + return 0; 442 + } 443 + if (msblk->thread_ops == &squashfs_decompressor_multi) { 444 + seq_puts(s, ",threads=multi"); 445 + return 0; 446 + } 447 + if (msblk->thread_ops == &squashfs_decompressor_percpu) { 448 + seq_puts(s, ",threads=percpu"); 449 + return 0; 450 + } 451 + #endif 465 452 return 0; 466 453 } 467 454 ··· 487 446 if (!opts) 488 447 return -ENOMEM; 489 448 449 + #ifdef CONFIG_SQUASHFS_DECOMP_SINGLE 450 + opts->thread_ops = &squashfs_decompressor_single; 451 + #elif defined(CONFIG_SQUASHFS_DECOMP_MULTI) 452 + opts->thread_ops = &squashfs_decompressor_multi; 453 + #elif defined(CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU) 454 + opts->thread_ops = &squashfs_decompressor_percpu; 455 + #else 456 + #error "fail: unknown squashfs decompression thread mode?" 457 + #endif 490 458 fc->fs_private = opts; 491 459 fc->ops = &squashfs_context_ops; 492 460 return 0; ··· 528 478 squashfs_cache_delete(sbi->block_cache); 529 479 squashfs_cache_delete(sbi->fragment_cache); 530 480 squashfs_cache_delete(sbi->read_page); 531 - squashfs_decompressor_destroy(sbi); 481 + sbi->thread_ops->destroy(sbi); 532 482 kfree(sbi->id_table); 533 483 kfree(sbi->fragment_index); 534 484 kfree(sbi->meta_index);