162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci#include <linux/module.h> 362306a36Sopenharmony_ci#include <linux/zlib.h> 462306a36Sopenharmony_ci#include "compress.h" 562306a36Sopenharmony_ci 662306a36Sopenharmony_cistruct z_erofs_deflate { 762306a36Sopenharmony_ci struct z_erofs_deflate *next; 862306a36Sopenharmony_ci struct z_stream_s z; 962306a36Sopenharmony_ci u8 bounce[PAGE_SIZE]; 1062306a36Sopenharmony_ci}; 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistatic DEFINE_SPINLOCK(z_erofs_deflate_lock); 1362306a36Sopenharmony_cistatic unsigned int z_erofs_deflate_nstrms, z_erofs_deflate_avail_strms; 1462306a36Sopenharmony_cistatic struct z_erofs_deflate *z_erofs_deflate_head; 1562306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(z_erofs_deflate_wq); 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cimodule_param_named(deflate_streams, z_erofs_deflate_nstrms, uint, 0444); 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_civoid z_erofs_deflate_exit(void) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci /* there should be no running fs instance */ 2262306a36Sopenharmony_ci while (z_erofs_deflate_avail_strms) { 2362306a36Sopenharmony_ci struct z_erofs_deflate *strm; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci spin_lock(&z_erofs_deflate_lock); 2662306a36Sopenharmony_ci strm = z_erofs_deflate_head; 2762306a36Sopenharmony_ci if (!strm) { 2862306a36Sopenharmony_ci spin_unlock(&z_erofs_deflate_lock); 2962306a36Sopenharmony_ci continue; 3062306a36Sopenharmony_ci } 3162306a36Sopenharmony_ci z_erofs_deflate_head = NULL; 3262306a36Sopenharmony_ci spin_unlock(&z_erofs_deflate_lock); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci while (strm) { 3562306a36Sopenharmony_ci struct z_erofs_deflate *n = strm->next; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci vfree(strm->z.workspace); 3862306a36Sopenharmony_ci kfree(strm); 3962306a36Sopenharmony_ci --z_erofs_deflate_avail_strms; 4062306a36Sopenharmony_ci strm = n; 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ciint __init z_erofs_deflate_init(void) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci /* by default, use # of possible CPUs instead */ 4862306a36Sopenharmony_ci if (!z_erofs_deflate_nstrms) 4962306a36Sopenharmony_ci z_erofs_deflate_nstrms = num_possible_cpus(); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci for (; z_erofs_deflate_avail_strms < z_erofs_deflate_nstrms; 5262306a36Sopenharmony_ci ++z_erofs_deflate_avail_strms) { 5362306a36Sopenharmony_ci struct z_erofs_deflate *strm; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci strm = kzalloc(sizeof(*strm), GFP_KERNEL); 5662306a36Sopenharmony_ci if (!strm) 5762306a36Sopenharmony_ci goto out_failed; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci /* XXX: in-kernel zlib cannot shrink windowbits currently */ 6062306a36Sopenharmony_ci strm->z.workspace = vmalloc(zlib_inflate_workspacesize()); 6162306a36Sopenharmony_ci if (!strm->z.workspace) { 6262306a36Sopenharmony_ci kfree(strm); 6362306a36Sopenharmony_ci goto out_failed; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci spin_lock(&z_erofs_deflate_lock); 6762306a36Sopenharmony_ci strm->next = z_erofs_deflate_head; 6862306a36Sopenharmony_ci z_erofs_deflate_head = strm; 6962306a36Sopenharmony_ci spin_unlock(&z_erofs_deflate_lock); 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci return 0; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ciout_failed: 7462306a36Sopenharmony_ci pr_err("failed to allocate zlib workspace\n"); 7562306a36Sopenharmony_ci z_erofs_deflate_exit(); 7662306a36Sopenharmony_ci return -ENOMEM; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ciint z_erofs_load_deflate_config(struct super_block *sb, 8062306a36Sopenharmony_ci struct erofs_super_block *dsb, void *data, int size) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci struct z_erofs_deflate_cfgs *dfl = data; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (!dfl || size < sizeof(struct z_erofs_deflate_cfgs)) { 8562306a36Sopenharmony_ci erofs_err(sb, "invalid deflate cfgs, size=%u", size); 8662306a36Sopenharmony_ci return -EINVAL; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (dfl->windowbits > MAX_WBITS) { 9062306a36Sopenharmony_ci erofs_err(sb, "unsupported windowbits %u", dfl->windowbits); 9162306a36Sopenharmony_ci return -EOPNOTSUPP; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci erofs_info(sb, "EXPERIMENTAL DEFLATE feature in use. Use at your own risk!"); 9562306a36Sopenharmony_ci return 0; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ciint z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq, 9962306a36Sopenharmony_ci struct page **pagepool) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci const unsigned int nrpages_out = 10262306a36Sopenharmony_ci PAGE_ALIGN(rq->pageofs_out + rq->outputsize) >> PAGE_SHIFT; 10362306a36Sopenharmony_ci const unsigned int nrpages_in = 10462306a36Sopenharmony_ci PAGE_ALIGN(rq->inputsize) >> PAGE_SHIFT; 10562306a36Sopenharmony_ci struct super_block *sb = rq->sb; 10662306a36Sopenharmony_ci unsigned int insz, outsz, pofs; 10762306a36Sopenharmony_ci struct z_erofs_deflate *strm; 10862306a36Sopenharmony_ci u8 *kin, *kout = NULL; 10962306a36Sopenharmony_ci bool bounced = false; 11062306a36Sopenharmony_ci int no = -1, ni = 0, j = 0, zerr, err; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* 1. get the exact DEFLATE compressed size */ 11362306a36Sopenharmony_ci kin = kmap_local_page(*rq->in); 11462306a36Sopenharmony_ci err = z_erofs_fixup_insize(rq, kin + rq->pageofs_in, 11562306a36Sopenharmony_ci min_t(unsigned int, rq->inputsize, 11662306a36Sopenharmony_ci sb->s_blocksize - rq->pageofs_in)); 11762306a36Sopenharmony_ci if (err) { 11862306a36Sopenharmony_ci kunmap_local(kin); 11962306a36Sopenharmony_ci return err; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* 2. get an available DEFLATE context */ 12362306a36Sopenharmony_ciagain: 12462306a36Sopenharmony_ci spin_lock(&z_erofs_deflate_lock); 12562306a36Sopenharmony_ci strm = z_erofs_deflate_head; 12662306a36Sopenharmony_ci if (!strm) { 12762306a36Sopenharmony_ci spin_unlock(&z_erofs_deflate_lock); 12862306a36Sopenharmony_ci wait_event(z_erofs_deflate_wq, READ_ONCE(z_erofs_deflate_head)); 12962306a36Sopenharmony_ci goto again; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci z_erofs_deflate_head = strm->next; 13262306a36Sopenharmony_ci spin_unlock(&z_erofs_deflate_lock); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* 3. multi-call decompress */ 13562306a36Sopenharmony_ci insz = rq->inputsize; 13662306a36Sopenharmony_ci outsz = rq->outputsize; 13762306a36Sopenharmony_ci zerr = zlib_inflateInit2(&strm->z, -MAX_WBITS); 13862306a36Sopenharmony_ci if (zerr != Z_OK) { 13962306a36Sopenharmony_ci err = -EIO; 14062306a36Sopenharmony_ci goto failed_zinit; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci pofs = rq->pageofs_out; 14462306a36Sopenharmony_ci strm->z.avail_in = min_t(u32, insz, PAGE_SIZE - rq->pageofs_in); 14562306a36Sopenharmony_ci insz -= strm->z.avail_in; 14662306a36Sopenharmony_ci strm->z.next_in = kin + rq->pageofs_in; 14762306a36Sopenharmony_ci strm->z.avail_out = 0; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci while (1) { 15062306a36Sopenharmony_ci if (!strm->z.avail_out) { 15162306a36Sopenharmony_ci if (++no >= nrpages_out || !outsz) { 15262306a36Sopenharmony_ci erofs_err(sb, "insufficient space for decompressed data"); 15362306a36Sopenharmony_ci err = -EFSCORRUPTED; 15462306a36Sopenharmony_ci break; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (kout) 15862306a36Sopenharmony_ci kunmap_local(kout); 15962306a36Sopenharmony_ci strm->z.avail_out = min_t(u32, outsz, PAGE_SIZE - pofs); 16062306a36Sopenharmony_ci outsz -= strm->z.avail_out; 16162306a36Sopenharmony_ci if (!rq->out[no]) { 16262306a36Sopenharmony_ci rq->out[no] = erofs_allocpage(pagepool, 16362306a36Sopenharmony_ci GFP_KERNEL | __GFP_NOFAIL); 16462306a36Sopenharmony_ci set_page_private(rq->out[no], 16562306a36Sopenharmony_ci Z_EROFS_SHORTLIVED_PAGE); 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci kout = kmap_local_page(rq->out[no]); 16862306a36Sopenharmony_ci strm->z.next_out = kout + pofs; 16962306a36Sopenharmony_ci pofs = 0; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (!strm->z.avail_in && insz) { 17362306a36Sopenharmony_ci if (++ni >= nrpages_in) { 17462306a36Sopenharmony_ci erofs_err(sb, "invalid compressed data"); 17562306a36Sopenharmony_ci err = -EFSCORRUPTED; 17662306a36Sopenharmony_ci break; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (kout) { /* unlike kmap(), take care of the orders */ 18062306a36Sopenharmony_ci j = strm->z.next_out - kout; 18162306a36Sopenharmony_ci kunmap_local(kout); 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci kunmap_local(kin); 18462306a36Sopenharmony_ci strm->z.avail_in = min_t(u32, insz, PAGE_SIZE); 18562306a36Sopenharmony_ci insz -= strm->z.avail_in; 18662306a36Sopenharmony_ci kin = kmap_local_page(rq->in[ni]); 18762306a36Sopenharmony_ci strm->z.next_in = kin; 18862306a36Sopenharmony_ci bounced = false; 18962306a36Sopenharmony_ci if (kout) { 19062306a36Sopenharmony_ci kout = kmap_local_page(rq->out[no]); 19162306a36Sopenharmony_ci strm->z.next_out = kout + j; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* 19662306a36Sopenharmony_ci * Handle overlapping: Use bounced buffer if the compressed 19762306a36Sopenharmony_ci * data is under processing; Or use short-lived pages from the 19862306a36Sopenharmony_ci * on-stack pagepool where pages share among the same request 19962306a36Sopenharmony_ci * and not _all_ inplace I/O pages are needed to be doubled. 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_ci if (!bounced && rq->out[no] == rq->in[ni]) { 20262306a36Sopenharmony_ci memcpy(strm->bounce, strm->z.next_in, strm->z.avail_in); 20362306a36Sopenharmony_ci strm->z.next_in = strm->bounce; 20462306a36Sopenharmony_ci bounced = true; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci for (j = ni + 1; j < nrpages_in; ++j) { 20862306a36Sopenharmony_ci struct page *tmppage; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (rq->out[no] != rq->in[j]) 21162306a36Sopenharmony_ci continue; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci DBG_BUGON(erofs_page_is_managed(EROFS_SB(sb), 21462306a36Sopenharmony_ci rq->in[j])); 21562306a36Sopenharmony_ci tmppage = erofs_allocpage(pagepool, 21662306a36Sopenharmony_ci GFP_KERNEL | __GFP_NOFAIL); 21762306a36Sopenharmony_ci set_page_private(tmppage, Z_EROFS_SHORTLIVED_PAGE); 21862306a36Sopenharmony_ci copy_highpage(tmppage, rq->in[j]); 21962306a36Sopenharmony_ci rq->in[j] = tmppage; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci zerr = zlib_inflate(&strm->z, Z_SYNC_FLUSH); 22362306a36Sopenharmony_ci if (zerr != Z_OK || !(outsz + strm->z.avail_out)) { 22462306a36Sopenharmony_ci if (zerr == Z_OK && rq->partial_decoding) 22562306a36Sopenharmony_ci break; 22662306a36Sopenharmony_ci if (zerr == Z_STREAM_END && !outsz) 22762306a36Sopenharmony_ci break; 22862306a36Sopenharmony_ci erofs_err(sb, "failed to decompress %d in[%u] out[%u]", 22962306a36Sopenharmony_ci zerr, rq->inputsize, rq->outputsize); 23062306a36Sopenharmony_ci err = -EFSCORRUPTED; 23162306a36Sopenharmony_ci break; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (zlib_inflateEnd(&strm->z) != Z_OK && !err) 23662306a36Sopenharmony_ci err = -EIO; 23762306a36Sopenharmony_ci if (kout) 23862306a36Sopenharmony_ci kunmap_local(kout); 23962306a36Sopenharmony_cifailed_zinit: 24062306a36Sopenharmony_ci kunmap_local(kin); 24162306a36Sopenharmony_ci /* 4. push back DEFLATE stream context to the global list */ 24262306a36Sopenharmony_ci spin_lock(&z_erofs_deflate_lock); 24362306a36Sopenharmony_ci strm->next = z_erofs_deflate_head; 24462306a36Sopenharmony_ci z_erofs_deflate_head = strm; 24562306a36Sopenharmony_ci spin_unlock(&z_erofs_deflate_lock); 24662306a36Sopenharmony_ci wake_up(&z_erofs_deflate_wq); 24762306a36Sopenharmony_ci return err; 24862306a36Sopenharmony_ci} 249