decompressor_multi.c (4577B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (c) 2013 4 * Minchan Kim <minchan@kernel.org> 5 */ 6#include <linux/types.h> 7#include <linux/mutex.h> 8#include <linux/slab.h> 9#include <linux/bio.h> 10#include <linux/sched.h> 11#include <linux/wait.h> 12#include <linux/cpumask.h> 13 14#include "squashfs_fs.h" 15#include "squashfs_fs_sb.h" 16#include "decompressor.h" 17#include "squashfs.h" 18 19/* 20 * This file implements multi-threaded decompression in the 21 * decompressor framework 22 */ 23 24 25/* 26 * The reason that multiply two is that a CPU can request new I/O 27 * while it is waiting previous request. 28 */ 29#define MAX_DECOMPRESSOR (num_online_cpus() * 2) 30 31 32int squashfs_max_decompressors(void) 33{ 34 return MAX_DECOMPRESSOR; 35} 36 37 38struct squashfs_stream { 39 void *comp_opts; 40 struct list_head strm_list; 41 struct mutex mutex; 42 int avail_decomp; 43 wait_queue_head_t wait; 44}; 45 46 47struct decomp_stream { 48 void *stream; 49 struct list_head list; 50}; 51 52 53static void put_decomp_stream(struct decomp_stream *decomp_strm, 54 struct squashfs_stream *stream) 55{ 56 mutex_lock(&stream->mutex); 57 list_add(&decomp_strm->list, &stream->strm_list); 58 mutex_unlock(&stream->mutex); 59 wake_up(&stream->wait); 60} 61 62void *squashfs_decompressor_create(struct squashfs_sb_info *msblk, 63 void *comp_opts) 64{ 65 struct squashfs_stream *stream; 66 struct decomp_stream *decomp_strm = NULL; 67 int err = -ENOMEM; 68 69 stream = kzalloc(sizeof(*stream), GFP_KERNEL); 70 if (!stream) 71 goto out; 72 73 stream->comp_opts = comp_opts; 74 mutex_init(&stream->mutex); 75 INIT_LIST_HEAD(&stream->strm_list); 76 init_waitqueue_head(&stream->wait); 77 78 /* 79 * We should have a decompressor at least as default 80 * so if we fail to allocate new decompressor dynamically, 81 * we could always fall back to default decompressor and 82 * file system works. 83 */ 84 decomp_strm = kmalloc(sizeof(*decomp_strm), GFP_KERNEL); 85 if (!decomp_strm) 86 goto out; 87 88 decomp_strm->stream = msblk->decompressor->init(msblk, 89 stream->comp_opts); 90 if (IS_ERR(decomp_strm->stream)) { 91 err = PTR_ERR(decomp_strm->stream); 92 goto out; 93 } 94 95 list_add(&decomp_strm->list, &stream->strm_list); 96 stream->avail_decomp = 1; 97 return stream; 98 99out: 100 kfree(decomp_strm); 101 kfree(stream); 102 return ERR_PTR(err); 103} 104 105 106void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk) 107{ 108 struct squashfs_stream *stream = msblk->stream; 109 if (stream) { 110 struct decomp_stream *decomp_strm; 111 112 while (!list_empty(&stream->strm_list)) { 113 decomp_strm = list_entry(stream->strm_list.prev, 114 struct decomp_stream, list); 115 list_del(&decomp_strm->list); 116 msblk->decompressor->free(decomp_strm->stream); 117 kfree(decomp_strm); 118 stream->avail_decomp--; 119 } 120 WARN_ON(stream->avail_decomp); 121 kfree(stream->comp_opts); 122 kfree(stream); 123 } 124} 125 126 127static struct decomp_stream *get_decomp_stream(struct squashfs_sb_info *msblk, 128 struct squashfs_stream *stream) 129{ 130 struct decomp_stream *decomp_strm; 131 132 while (1) { 133 mutex_lock(&stream->mutex); 134 135 /* There is available decomp_stream */ 136 if (!list_empty(&stream->strm_list)) { 137 decomp_strm = list_entry(stream->strm_list.prev, 138 struct decomp_stream, list); 139 list_del(&decomp_strm->list); 140 mutex_unlock(&stream->mutex); 141 break; 142 } 143 144 /* 145 * If there is no available decomp and already full, 146 * let's wait for releasing decomp from other users. 147 */ 148 if (stream->avail_decomp >= MAX_DECOMPRESSOR) 149 goto wait; 150 151 /* Let's allocate new decomp */ 152 decomp_strm = kmalloc(sizeof(*decomp_strm), GFP_KERNEL); 153 if (!decomp_strm) 154 goto wait; 155 156 decomp_strm->stream = msblk->decompressor->init(msblk, 157 stream->comp_opts); 158 if (IS_ERR(decomp_strm->stream)) { 159 kfree(decomp_strm); 160 goto wait; 161 } 162 163 stream->avail_decomp++; 164 WARN_ON(stream->avail_decomp > MAX_DECOMPRESSOR); 165 166 mutex_unlock(&stream->mutex); 167 break; 168wait: 169 /* 170 * If system memory is tough, let's for other's 171 * releasing instead of hurting VM because it could 172 * make page cache thrashing. 173 */ 174 mutex_unlock(&stream->mutex); 175 wait_event(stream->wait, 176 !list_empty(&stream->strm_list)); 177 } 178 179 return decomp_strm; 180} 181 182 183int squashfs_decompress(struct squashfs_sb_info *msblk, struct bio *bio, 184 int offset, int length, 185 struct squashfs_page_actor *output) 186{ 187 int res; 188 struct squashfs_stream *stream = msblk->stream; 189 struct decomp_stream *decomp_stream = get_decomp_stream(msblk, stream); 190 res = msblk->decompressor->decompress(msblk, decomp_stream->stream, 191 bio, offset, length, output); 192 put_decomp_stream(decomp_stream, stream); 193 if (res < 0) 194 ERROR("%s decompression failed, data probably corrupt\n", 195 msblk->decompressor->name); 196 return res; 197}