Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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/buffer_head.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 buffer_head **bh,
184	int b, int offset, int length, 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		bh, b, 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}