162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2013 462306a36Sopenharmony_ci * Minchan Kim <minchan@kernel.org> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include <linux/types.h> 762306a36Sopenharmony_ci#include <linux/mutex.h> 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/bio.h> 1062306a36Sopenharmony_ci#include <linux/sched.h> 1162306a36Sopenharmony_ci#include <linux/wait.h> 1262306a36Sopenharmony_ci#include <linux/cpumask.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "squashfs_fs.h" 1562306a36Sopenharmony_ci#include "squashfs_fs_sb.h" 1662306a36Sopenharmony_ci#include "decompressor.h" 1762306a36Sopenharmony_ci#include "squashfs.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * This file implements multi-threaded decompression in the 2162306a36Sopenharmony_ci * decompressor framework 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* 2662306a36Sopenharmony_ci * The reason that multiply two is that a CPU can request new I/O 2762306a36Sopenharmony_ci * while it is waiting previous request. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci#define MAX_DECOMPRESSOR (num_online_cpus() * 2) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic int squashfs_max_decompressors(void) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci return MAX_DECOMPRESSOR; 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct squashfs_stream { 3862306a36Sopenharmony_ci void *comp_opts; 3962306a36Sopenharmony_ci struct list_head strm_list; 4062306a36Sopenharmony_ci struct mutex mutex; 4162306a36Sopenharmony_ci int avail_decomp; 4262306a36Sopenharmony_ci wait_queue_head_t wait; 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistruct decomp_stream { 4762306a36Sopenharmony_ci void *stream; 4862306a36Sopenharmony_ci struct list_head list; 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic void put_decomp_stream(struct decomp_stream *decomp_strm, 5362306a36Sopenharmony_ci struct squashfs_stream *stream) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci mutex_lock(&stream->mutex); 5662306a36Sopenharmony_ci list_add(&decomp_strm->list, &stream->strm_list); 5762306a36Sopenharmony_ci mutex_unlock(&stream->mutex); 5862306a36Sopenharmony_ci wake_up(&stream->wait); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic void *squashfs_decompressor_create(struct squashfs_sb_info *msblk, 6262306a36Sopenharmony_ci void *comp_opts) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct squashfs_stream *stream; 6562306a36Sopenharmony_ci struct decomp_stream *decomp_strm = NULL; 6662306a36Sopenharmony_ci int err = -ENOMEM; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci stream = kzalloc(sizeof(*stream), GFP_KERNEL); 6962306a36Sopenharmony_ci if (!stream) 7062306a36Sopenharmony_ci goto out; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci stream->comp_opts = comp_opts; 7362306a36Sopenharmony_ci mutex_init(&stream->mutex); 7462306a36Sopenharmony_ci INIT_LIST_HEAD(&stream->strm_list); 7562306a36Sopenharmony_ci init_waitqueue_head(&stream->wait); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* 7862306a36Sopenharmony_ci * We should have a decompressor at least as default 7962306a36Sopenharmony_ci * so if we fail to allocate new decompressor dynamically, 8062306a36Sopenharmony_ci * we could always fall back to default decompressor and 8162306a36Sopenharmony_ci * file system works. 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci decomp_strm = kmalloc(sizeof(*decomp_strm), GFP_KERNEL); 8462306a36Sopenharmony_ci if (!decomp_strm) 8562306a36Sopenharmony_ci goto out; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci decomp_strm->stream = msblk->decompressor->init(msblk, 8862306a36Sopenharmony_ci stream->comp_opts); 8962306a36Sopenharmony_ci if (IS_ERR(decomp_strm->stream)) { 9062306a36Sopenharmony_ci err = PTR_ERR(decomp_strm->stream); 9162306a36Sopenharmony_ci goto out; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci list_add(&decomp_strm->list, &stream->strm_list); 9562306a36Sopenharmony_ci stream->avail_decomp = 1; 9662306a36Sopenharmony_ci return stream; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ciout: 9962306a36Sopenharmony_ci kfree(decomp_strm); 10062306a36Sopenharmony_ci kfree(stream); 10162306a36Sopenharmony_ci return ERR_PTR(err); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct squashfs_stream *stream = msblk->stream; 10862306a36Sopenharmony_ci if (stream) { 10962306a36Sopenharmony_ci struct decomp_stream *decomp_strm; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci while (!list_empty(&stream->strm_list)) { 11262306a36Sopenharmony_ci decomp_strm = list_entry(stream->strm_list.prev, 11362306a36Sopenharmony_ci struct decomp_stream, list); 11462306a36Sopenharmony_ci list_del(&decomp_strm->list); 11562306a36Sopenharmony_ci msblk->decompressor->free(decomp_strm->stream); 11662306a36Sopenharmony_ci kfree(decomp_strm); 11762306a36Sopenharmony_ci stream->avail_decomp--; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci WARN_ON(stream->avail_decomp); 12062306a36Sopenharmony_ci kfree(stream->comp_opts); 12162306a36Sopenharmony_ci kfree(stream); 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic struct decomp_stream *get_decomp_stream(struct squashfs_sb_info *msblk, 12762306a36Sopenharmony_ci struct squashfs_stream *stream) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct decomp_stream *decomp_strm; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci while (1) { 13262306a36Sopenharmony_ci mutex_lock(&stream->mutex); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* There is available decomp_stream */ 13562306a36Sopenharmony_ci if (!list_empty(&stream->strm_list)) { 13662306a36Sopenharmony_ci decomp_strm = list_entry(stream->strm_list.prev, 13762306a36Sopenharmony_ci struct decomp_stream, list); 13862306a36Sopenharmony_ci list_del(&decomp_strm->list); 13962306a36Sopenharmony_ci mutex_unlock(&stream->mutex); 14062306a36Sopenharmony_ci break; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* 14462306a36Sopenharmony_ci * If there is no available decomp and already full, 14562306a36Sopenharmony_ci * let's wait for releasing decomp from other users. 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ci if (stream->avail_decomp >= msblk->max_thread_num) 14862306a36Sopenharmony_ci goto wait; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* Let's allocate new decomp */ 15162306a36Sopenharmony_ci decomp_strm = kmalloc(sizeof(*decomp_strm), GFP_KERNEL); 15262306a36Sopenharmony_ci if (!decomp_strm) 15362306a36Sopenharmony_ci goto wait; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci decomp_strm->stream = msblk->decompressor->init(msblk, 15662306a36Sopenharmony_ci stream->comp_opts); 15762306a36Sopenharmony_ci if (IS_ERR(decomp_strm->stream)) { 15862306a36Sopenharmony_ci kfree(decomp_strm); 15962306a36Sopenharmony_ci goto wait; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci stream->avail_decomp++; 16362306a36Sopenharmony_ci WARN_ON(stream->avail_decomp > msblk->max_thread_num); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci mutex_unlock(&stream->mutex); 16662306a36Sopenharmony_ci break; 16762306a36Sopenharmony_ciwait: 16862306a36Sopenharmony_ci /* 16962306a36Sopenharmony_ci * If system memory is tough, let's for other's 17062306a36Sopenharmony_ci * releasing instead of hurting VM because it could 17162306a36Sopenharmony_ci * make page cache thrashing. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_ci mutex_unlock(&stream->mutex); 17462306a36Sopenharmony_ci wait_event(stream->wait, 17562306a36Sopenharmony_ci !list_empty(&stream->strm_list)); 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return decomp_strm; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int squashfs_decompress(struct squashfs_sb_info *msblk, struct bio *bio, 18362306a36Sopenharmony_ci int offset, int length, 18462306a36Sopenharmony_ci struct squashfs_page_actor *output) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci int res; 18762306a36Sopenharmony_ci struct squashfs_stream *stream = msblk->stream; 18862306a36Sopenharmony_ci struct decomp_stream *decomp_stream = get_decomp_stream(msblk, stream); 18962306a36Sopenharmony_ci res = msblk->decompressor->decompress(msblk, decomp_stream->stream, 19062306a36Sopenharmony_ci bio, offset, length, output); 19162306a36Sopenharmony_ci put_decomp_stream(decomp_stream, stream); 19262306a36Sopenharmony_ci if (res < 0) 19362306a36Sopenharmony_ci ERROR("%s decompression failed, data probably corrupt\n", 19462306a36Sopenharmony_ci msblk->decompressor->name); 19562306a36Sopenharmony_ci return res; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ciconst struct squashfs_decompressor_thread_ops squashfs_decompressor_multi = { 19962306a36Sopenharmony_ci .create = squashfs_decompressor_create, 20062306a36Sopenharmony_ci .destroy = squashfs_decompressor_destroy, 20162306a36Sopenharmony_ci .decompress = squashfs_decompress, 20262306a36Sopenharmony_ci .max_decompressors = squashfs_max_decompressors, 20362306a36Sopenharmony_ci}; 204