xref: /linux/fs/squashfs/decompressor_multi.c (revision 97884ca8c2925d14c32188e865069f21378b4b4f)
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 int squashfs_max_decompressors(void)
33 {
34 	return MAX_DECOMPRESSOR;
35 }
36 
37 
38 struct 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 
47 struct decomp_stream {
48 	void *stream;
49 	struct list_head list;
50 };
51 
52 
53 static 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 
62 void *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 
99 out:
100 	kfree(decomp_strm);
101 	kfree(stream);
102 	return ERR_PTR(err);
103 }
104 
105 
106 void 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 
127 static 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;
168 wait:
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 
183 int 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 }
198