diff options
| author | Paul E. McKenney <paulmck@kernel.org> | 2026-08-14 15:43:51 -0700 |
|---|---|---|
| committer | Paul E. McKenney <paulmck@kernel.org> | 2026-08-14 15:43:51 -0700 |
| commit | 9cc63f8bcd560c760d0b12e15bba8f81c86237cf (patch) | |
| tree | 3b5269b1bcf6714171754946367c51e0590f64da /fs/jffs2/compr_zlib.c | |
| download | linux-stable-9cc63f8bcd560c760d0b12e15bba8f81c86237cf.tar.gz linux-stable-9cc63f8bcd560c760d0b12e15bba8f81c86237cf.zip | |
Merge branches 'expcb.2026.07.24a', 'misc.2026.07.30a', 'rcu-tasks.2026.07.30a', 'srcu.2026.08.11a' and 'torture.2026.08.14a' into HEADgrafted
Changes:
Make expedited grace periods expedite normal RCU callbacks
Miscellaneous fixes:
* Improve diagnostic output with character task states.
* Mark accesses to inform KCSAN of concurrency design.
* Move from kmalloc() to kmalloc_obj().
* Documentation updates.
* Improve handling of RCU deferred quiescent states.
* Clean up unused function arguments and structure fields.
* Reduce show_rcu_gp_kthreads() stack space.
Tasks RCU updates:
* Clean up after SRCU re-implementation of Tasks Trace RCU.
* Mark accesses to inform KCSAN of concurrency design.
* Add ->lazy_timer status to diagnostic output.
* Remove an unnecessary memory barrier.
* Fix a data race, courtesy of KCSAN.
* Documentation updates.
* Convert cond_resched_tasks_rcu_qs() from macro to static inline
function.
SRCU updates:
* Add Rust helpers for SRCU.
* Avoid losing queued work at cleanup_srcu_struct() time.
Torture-test updates:
* Preparation work for immediate RCU priority deboosting.
* Test RCU readers from real interrupt handlers (as opposed to softirq).
* Simplify code through use of cpumask_next_wrap().
* Improve diagnostic output with character task states.
* Add rcutorture.nwriters parameter to allow lightweight stall testing,
and rcutorture.stall_only to make doing so easier.
* Test an RCU Tasks Trace grace period implying an RCU grace period.
* Make RCU Tasks Trace torturing track reader batches.
* Fix a data race, courtesy of KCSAN.
* Plug a shuffle_tmp_mask memory leak on kthread spawn failure.
Diffstat (limited to 'fs/jffs2/compr_zlib.c')
| -rw-r--r-- | fs/jffs2/compr_zlib.c | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/fs/jffs2/compr_zlib.c b/fs/jffs2/compr_zlib.c new file mode 100644 index 000000000..5698dae5d --- /dev/null +++ b/fs/jffs2/compr_zlib.c @@ -0,0 +1,222 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright © 2001-2007 Red Hat, Inc. + * Copyright © 2004-2010 David Woodhouse <dwmw2@infradead.org> + * + * Created by David Woodhouse <dwmw2@infradead.org> + * + * For licensing information, see the file 'LICENCE' in this directory. + * + */ + +#if !defined(__KERNEL__) && !defined(__ECOS) +#error "The userspace support got too messy and was removed. Update your mkfs.jffs2" +#endif + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/zlib.h> +#include <linux/zutil.h> +#include "nodelist.h" +#include "compr.h" + + /* Plan: call deflate() with avail_in == *sourcelen, + avail_out = *dstlen - 12 and flush == Z_FINISH. + If it doesn't manage to finish, call it again with + avail_in == 0 and avail_out set to the remaining 12 + bytes for it to clean up. + Q: Is 12 bytes sufficient? + */ +#define STREAM_END_SPACE 12 + +static DEFINE_MUTEX(deflate_mutex); +static DEFINE_MUTEX(inflate_mutex); +static z_stream inf_strm, def_strm; + +#ifdef __KERNEL__ /* Linux-only */ +#include <linux/vmalloc.h> +#include <linux/init.h> +#include <linux/mutex.h> + +static int __init alloc_workspaces(void) +{ + def_strm.workspace = vmalloc(zlib_deflate_workspacesize(MAX_WBITS, + MAX_MEM_LEVEL)); + if (!def_strm.workspace) + return -ENOMEM; + + jffs2_dbg(1, "Allocated %d bytes for deflate workspace\n", + zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL)); + inf_strm.workspace = vmalloc(zlib_inflate_workspacesize()); + if (!inf_strm.workspace) { + vfree(def_strm.workspace); + return -ENOMEM; + } + jffs2_dbg(1, "Allocated %d bytes for inflate workspace\n", + zlib_inflate_workspacesize()); + return 0; +} + +static void free_workspaces(void) +{ + vfree(def_strm.workspace); + vfree(inf_strm.workspace); +} +#else +#define alloc_workspaces() (0) +#define free_workspaces() do { } while(0) +#endif /* __KERNEL__ */ + +static int jffs2_zlib_compress(unsigned char *data_in, + unsigned char *cpage_out, + uint32_t *sourcelen, uint32_t *dstlen) +{ + int ret; + + if (*dstlen <= STREAM_END_SPACE) + return -1; + + mutex_lock(&deflate_mutex); + + if (Z_OK != zlib_deflateInit(&def_strm, 3)) { + pr_warn("deflateInit failed\n"); + mutex_unlock(&deflate_mutex); + return -1; + } + + def_strm.next_in = data_in; + def_strm.total_in = 0; + + def_strm.next_out = cpage_out; + def_strm.total_out = 0; + + while (def_strm.total_out < *dstlen - STREAM_END_SPACE && def_strm.total_in < *sourcelen) { + def_strm.avail_out = *dstlen - (def_strm.total_out + STREAM_END_SPACE); + def_strm.avail_in = min_t(unsigned long, + (*sourcelen-def_strm.total_in), def_strm.avail_out); + jffs2_dbg(1, "calling deflate with avail_in %ld, avail_out %ld\n", + def_strm.avail_in, def_strm.avail_out); + ret = zlib_deflate(&def_strm, Z_PARTIAL_FLUSH); + jffs2_dbg(1, "deflate returned with avail_in %ld, avail_out %ld, total_in %ld, total_out %ld\n", + def_strm.avail_in, def_strm.avail_out, + def_strm.total_in, def_strm.total_out); + if (ret != Z_OK) { + jffs2_dbg(1, "deflate in loop returned %d\n", ret); + zlib_deflateEnd(&def_strm); + mutex_unlock(&deflate_mutex); + return -1; + } + } + def_strm.avail_out += STREAM_END_SPACE; + def_strm.avail_in = 0; + ret = zlib_deflate(&def_strm, Z_FINISH); + zlib_deflateEnd(&def_strm); + + if (ret != Z_STREAM_END) { + jffs2_dbg(1, "final deflate returned %d\n", ret); + ret = -1; + goto out; + } + + if (def_strm.total_out >= def_strm.total_in) { + jffs2_dbg(1, "zlib compressed %ld bytes into %ld; failing\n", + def_strm.total_in, def_strm.total_out); + ret = -1; + goto out; + } + + jffs2_dbg(1, "zlib compressed %ld bytes into %ld\n", + def_strm.total_in, def_strm.total_out); + + *dstlen = def_strm.total_out; + *sourcelen = def_strm.total_in; + ret = 0; + out: + mutex_unlock(&deflate_mutex); + return ret; +} + +static int jffs2_zlib_decompress(unsigned char *data_in, + unsigned char *cpage_out, + uint32_t srclen, uint32_t destlen) +{ + int ret; + int wbits = MAX_WBITS; + + mutex_lock(&inflate_mutex); + + inf_strm.next_in = data_in; + inf_strm.avail_in = srclen; + inf_strm.total_in = 0; + + inf_strm.next_out = cpage_out; + inf_strm.avail_out = destlen; + inf_strm.total_out = 0; + + /* If it's deflate, and it's got no preset dictionary, then + we can tell zlib to skip the adler32 check. */ + if (srclen > 2 && !(data_in[1] & PRESET_DICT) && + ((data_in[0] & 0x0f) == Z_DEFLATED) && + !(((data_in[0]<<8) + data_in[1]) % 31)) { + + jffs2_dbg(2, "inflate skipping adler32\n"); + wbits = -((data_in[0] >> 4) + 8); + inf_strm.next_in += 2; + inf_strm.avail_in -= 2; + } else { + /* Let this remain D1 for now -- it should never happen */ + jffs2_dbg(1, "inflate not skipping adler32\n"); + } + + + if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) { + pr_warn("inflateInit failed\n"); + mutex_unlock(&inflate_mutex); + return 1; + } + + while((ret = zlib_inflate(&inf_strm, Z_FINISH)) == Z_OK) + ; + if (ret != Z_STREAM_END) { + pr_notice("inflate returned %d\n", ret); + } + zlib_inflateEnd(&inf_strm); + mutex_unlock(&inflate_mutex); + return 0; +} + +static struct jffs2_compressor jffs2_zlib_comp = { + .priority = JFFS2_ZLIB_PRIORITY, + .name = "zlib", + .compr = JFFS2_COMPR_ZLIB, + .compress = &jffs2_zlib_compress, + .decompress = &jffs2_zlib_decompress, +#ifdef JFFS2_ZLIB_DISABLED + .disabled = 1, +#else + .disabled = 0, +#endif +}; + +int __init jffs2_zlib_init(void) +{ + int ret; + + ret = alloc_workspaces(); + if (ret) + return ret; + + ret = jffs2_register_compressor(&jffs2_zlib_comp); + if (ret) + free_workspaces(); + + return ret; +} + +void jffs2_zlib_exit(void) +{ + jffs2_unregister_compressor(&jffs2_zlib_comp); + free_workspaces(); +} |
