1 // SPDX-License-Identifier: GPL-2.0-only
4 * Minchan Kim <minchan@kernel.org>
6 #include <linux/types.h>
7 #include <linux/mutex.h>
8 #include <linux/slab.h>
10 #include <linux/sched.h>
11 #include <linux/wait.h>
12 #include <linux/cpumask.h>
14 #include "squashfs_fs.h"
15 #include "squashfs_fs_sb.h"
16 #include "decompressor.h"
20 * This file implements multi-threaded decompression in the
21 * decompressor framework
26 * The reason that multiply two is that a CPU can request new I/O
27 * while it is waiting previous request.
29 #define MAX_DECOMPRESSOR (num_online_cpus() * 2)
32 static int squashfs_max_decompressors(void)
34 return MAX_DECOMPRESSOR
;
37 struct squashfs_stream
{
39 struct list_head strm_list
;
42 wait_queue_head_t wait
;
46 struct decomp_stream
{
48 struct list_head list
;
52 static void put_decomp_stream(struct decomp_stream
*decomp_strm
,
53 struct squashfs_stream
*stream
)
55 mutex_lock(&stream
->mutex
);
56 list_add(&decomp_strm
->list
, &stream
->strm_list
);
57 mutex_unlock(&stream
->mutex
);
58 wake_up(&stream
->wait
);
61 static void *squashfs_decompressor_create(struct squashfs_sb_info
*msblk
,
64 struct squashfs_stream
*stream
;
65 struct decomp_stream
*decomp_strm
= NULL
;
68 stream
= kzalloc(sizeof(*stream
), GFP_KERNEL
);
72 stream
->comp_opts
= comp_opts
;
73 mutex_init(&stream
->mutex
);
74 INIT_LIST_HEAD(&stream
->strm_list
);
75 init_waitqueue_head(&stream
->wait
);
78 * We should have a decompressor at least as default
79 * so if we fail to allocate new decompressor dynamically,
80 * we could always fall back to default decompressor and
83 decomp_strm
= kmalloc(sizeof(*decomp_strm
), GFP_KERNEL
);
87 decomp_strm
->stream
= msblk
->decompressor
->init(msblk
,
89 if (IS_ERR(decomp_strm
->stream
)) {
90 err
= PTR_ERR(decomp_strm
->stream
);
94 list_add(&decomp_strm
->list
, &stream
->strm_list
);
95 stream
->avail_decomp
= 1;
105 static void squashfs_decompressor_destroy(struct squashfs_sb_info
*msblk
)
107 struct squashfs_stream
*stream
= msblk
->stream
;
109 struct decomp_stream
*decomp_strm
;
111 while (!list_empty(&stream
->strm_list
)) {
112 decomp_strm
= list_entry(stream
->strm_list
.prev
,
113 struct decomp_stream
, list
);
114 list_del(&decomp_strm
->list
);
115 msblk
->decompressor
->free(decomp_strm
->stream
);
117 stream
->avail_decomp
--;
119 WARN_ON(stream
->avail_decomp
);
120 kfree(stream
->comp_opts
);
126 static struct decomp_stream
*get_decomp_stream(struct squashfs_sb_info
*msblk
,
127 struct squashfs_stream
*stream
)
129 struct decomp_stream
*decomp_strm
;
132 mutex_lock(&stream
->mutex
);
134 /* There is available decomp_stream */
135 if (!list_empty(&stream
->strm_list
)) {
136 decomp_strm
= list_entry(stream
->strm_list
.prev
,
137 struct decomp_stream
, list
);
138 list_del(&decomp_strm
->list
);
139 mutex_unlock(&stream
->mutex
);
144 * If there is no available decomp and already full,
145 * let's wait for releasing decomp from other users.
147 if (stream
->avail_decomp
>= msblk
->max_thread_num
)
150 /* Let's allocate new decomp */
151 decomp_strm
= kmalloc(sizeof(*decomp_strm
), GFP_KERNEL
);
155 decomp_strm
->stream
= msblk
->decompressor
->init(msblk
,
157 if (IS_ERR(decomp_strm
->stream
)) {
162 stream
->avail_decomp
++;
163 WARN_ON(stream
->avail_decomp
> msblk
->max_thread_num
);
165 mutex_unlock(&stream
->mutex
);
169 * If system memory is tough, let's for other's
170 * releasing instead of hurting VM because it could
171 * make page cache thrashing.
173 mutex_unlock(&stream
->mutex
);
174 wait_event(stream
->wait
,
175 !list_empty(&stream
->strm_list
));
182 static int squashfs_decompress(struct squashfs_sb_info
*msblk
, struct bio
*bio
,
183 int offset
, int length
,
184 struct squashfs_page_actor
*output
)
187 struct squashfs_stream
*stream
= msblk
->stream
;
188 struct decomp_stream
*decomp_stream
= get_decomp_stream(msblk
, stream
);
189 res
= msblk
->decompressor
->decompress(msblk
, decomp_stream
->stream
,
190 bio
, offset
, length
, output
);
191 put_decomp_stream(decomp_stream
, stream
);
193 ERROR("%s decompression failed, data probably corrupt\n",
194 msblk
->decompressor
->name
);
198 const struct squashfs_decompressor_thread_ops squashfs_decompressor_multi
= {
199 .create
= squashfs_decompressor_create
,
200 .destroy
= squashfs_decompressor_destroy
,
201 .decompress
= squashfs_decompress
,
202 .max_decompressors
= squashfs_max_decompressors
,