Merge tag 'linux_kselftest-next-6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / fs / squashfs / decompressor_multi.c
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
32 static int squashfs_max_decompressors(void)
33 {
34         return MAX_DECOMPRESSOR;
35 }
36
37 struct squashfs_stream {
38         void                    *comp_opts;
39         struct list_head        strm_list;
40         struct mutex            mutex;
41         int                     avail_decomp;
42         wait_queue_head_t       wait;
43 };
44
45
46 struct decomp_stream {
47         void *stream;
48         struct list_head list;
49 };
50
51
52 static void put_decomp_stream(struct decomp_stream *decomp_strm,
53                                 struct squashfs_stream *stream)
54 {
55         mutex_lock(&stream->mutex);
56         list_add(&decomp_strm->list, &stream->strm_list);
57         mutex_unlock(&stream->mutex);
58         wake_up(&stream->wait);
59 }
60
61 static void *squashfs_decompressor_create(struct squashfs_sb_info *msblk,
62                                 void *comp_opts)
63 {
64         struct squashfs_stream *stream;
65         struct decomp_stream *decomp_strm = NULL;
66         int err = -ENOMEM;
67
68         stream = kzalloc(sizeof(*stream), GFP_KERNEL);
69         if (!stream)
70                 goto out;
71
72         stream->comp_opts = comp_opts;
73         mutex_init(&stream->mutex);
74         INIT_LIST_HEAD(&stream->strm_list);
75         init_waitqueue_head(&stream->wait);
76
77         /*
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
81          * file system works.
82          */
83         decomp_strm = kmalloc(sizeof(*decomp_strm), GFP_KERNEL);
84         if (!decomp_strm)
85                 goto out;
86
87         decomp_strm->stream = msblk->decompressor->init(msblk,
88                                                 stream->comp_opts);
89         if (IS_ERR(decomp_strm->stream)) {
90                 err = PTR_ERR(decomp_strm->stream);
91                 goto out;
92         }
93
94         list_add(&decomp_strm->list, &stream->strm_list);
95         stream->avail_decomp = 1;
96         return stream;
97
98 out:
99         kfree(decomp_strm);
100         kfree(stream);
101         return ERR_PTR(err);
102 }
103
104
105 static void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk)
106 {
107         struct squashfs_stream *stream = msblk->stream;
108         if (stream) {
109                 struct decomp_stream *decomp_strm;
110
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);
116                         kfree(decomp_strm);
117                         stream->avail_decomp--;
118                 }
119                 WARN_ON(stream->avail_decomp);
120                 kfree(stream->comp_opts);
121                 kfree(stream);
122         }
123 }
124
125
126 static struct decomp_stream *get_decomp_stream(struct squashfs_sb_info *msblk,
127                                         struct squashfs_stream *stream)
128 {
129         struct decomp_stream *decomp_strm;
130
131         while (1) {
132                 mutex_lock(&stream->mutex);
133
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);
140                         break;
141                 }
142
143                 /*
144                  * If there is no available decomp and already full,
145                  * let's wait for releasing decomp from other users.
146                  */
147                 if (stream->avail_decomp >= msblk->max_thread_num)
148                         goto wait;
149
150                 /* Let's allocate new decomp */
151                 decomp_strm = kmalloc(sizeof(*decomp_strm), GFP_KERNEL);
152                 if (!decomp_strm)
153                         goto wait;
154
155                 decomp_strm->stream = msblk->decompressor->init(msblk,
156                                                 stream->comp_opts);
157                 if (IS_ERR(decomp_strm->stream)) {
158                         kfree(decomp_strm);
159                         goto wait;
160                 }
161
162                 stream->avail_decomp++;
163                 WARN_ON(stream->avail_decomp > msblk->max_thread_num);
164
165                 mutex_unlock(&stream->mutex);
166                 break;
167 wait:
168                 /*
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.
172                  */
173                 mutex_unlock(&stream->mutex);
174                 wait_event(stream->wait,
175                         !list_empty(&stream->strm_list));
176         }
177
178         return decomp_strm;
179 }
180
181
182 static int squashfs_decompress(struct squashfs_sb_info *msblk, struct bio *bio,
183                         int offset, int length,
184                         struct squashfs_page_actor *output)
185 {
186         int res;
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);
192         if (res < 0)
193                 ERROR("%s decompression failed, data probably corrupt\n",
194                         msblk->decompressor->name);
195         return res;
196 }
197
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,
203 };