120c8ccb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2d208383dSPhillip Lougher /*
3d208383dSPhillip Lougher  * Copyright (c) 2013
4d208383dSPhillip Lougher  * Phillip Lougher <phillip@squashfs.org.uk>
5d208383dSPhillip Lougher  */
6d208383dSPhillip Lougher 
7d208383dSPhillip Lougher #include <linux/types.h>
8d208383dSPhillip Lougher #include <linux/slab.h>
9d208383dSPhillip Lougher #include <linux/percpu.h>
10fd56200aSJulia Cartwright #include <linux/local_lock.h>
11d208383dSPhillip Lougher 
12d208383dSPhillip Lougher #include "squashfs_fs.h"
13d208383dSPhillip Lougher #include "squashfs_fs_sb.h"
14d208383dSPhillip Lougher #include "decompressor.h"
15d208383dSPhillip Lougher #include "squashfs.h"
16d208383dSPhillip Lougher 
17d208383dSPhillip Lougher /*
18d208383dSPhillip Lougher  * This file implements multi-threaded decompression using percpu
19d208383dSPhillip Lougher  * variables, one thread per cpu core.
20d208383dSPhillip Lougher  */
21d208383dSPhillip Lougher 
22d208383dSPhillip Lougher struct squashfs_stream {
23d208383dSPhillip Lougher 	void			*stream;
24fd56200aSJulia Cartwright 	local_lock_t	lock;
25d208383dSPhillip Lougher };
26d208383dSPhillip Lougher 
squashfs_decompressor_create(struct squashfs_sb_info * msblk,void * comp_opts)27*80f78409SXiaoming Ni static void *squashfs_decompressor_create(struct squashfs_sb_info *msblk,
28d208383dSPhillip Lougher 						void *comp_opts)
29d208383dSPhillip Lougher {
30d208383dSPhillip Lougher 	struct squashfs_stream *stream;
31d208383dSPhillip Lougher 	struct squashfs_stream __percpu *percpu;
32d208383dSPhillip Lougher 	int err, cpu;
33d208383dSPhillip Lougher 
34d208383dSPhillip Lougher 	percpu = alloc_percpu(struct squashfs_stream);
35d208383dSPhillip Lougher 	if (percpu == NULL)
36d208383dSPhillip Lougher 		return ERR_PTR(-ENOMEM);
37d208383dSPhillip Lougher 
38d208383dSPhillip Lougher 	for_each_possible_cpu(cpu) {
39d208383dSPhillip Lougher 		stream = per_cpu_ptr(percpu, cpu);
40d208383dSPhillip Lougher 		stream->stream = msblk->decompressor->init(msblk, comp_opts);
41d208383dSPhillip Lougher 		if (IS_ERR(stream->stream)) {
42d208383dSPhillip Lougher 			err = PTR_ERR(stream->stream);
43d208383dSPhillip Lougher 			goto out;
44d208383dSPhillip Lougher 		}
45fd56200aSJulia Cartwright 		local_lock_init(&stream->lock);
46d208383dSPhillip Lougher 	}
47d208383dSPhillip Lougher 
48d208383dSPhillip Lougher 	kfree(comp_opts);
49d208383dSPhillip Lougher 	return (__force void *) percpu;
50d208383dSPhillip Lougher 
51d208383dSPhillip Lougher out:
52d208383dSPhillip Lougher 	for_each_possible_cpu(cpu) {
53d208383dSPhillip Lougher 		stream = per_cpu_ptr(percpu, cpu);
54d208383dSPhillip Lougher 		if (!IS_ERR_OR_NULL(stream->stream))
55d208383dSPhillip Lougher 			msblk->decompressor->free(stream->stream);
56d208383dSPhillip Lougher 	}
57d208383dSPhillip Lougher 	free_percpu(percpu);
58d208383dSPhillip Lougher 	return ERR_PTR(err);
59d208383dSPhillip Lougher }
60d208383dSPhillip Lougher 
squashfs_decompressor_destroy(struct squashfs_sb_info * msblk)61*80f78409SXiaoming Ni static void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk)
62d208383dSPhillip Lougher {
63d208383dSPhillip Lougher 	struct squashfs_stream __percpu *percpu =
64d208383dSPhillip Lougher 			(struct squashfs_stream __percpu *) msblk->stream;
65d208383dSPhillip Lougher 	struct squashfs_stream *stream;
66d208383dSPhillip Lougher 	int cpu;
67d208383dSPhillip Lougher 
68d208383dSPhillip Lougher 	if (msblk->stream) {
69d208383dSPhillip Lougher 		for_each_possible_cpu(cpu) {
70d208383dSPhillip Lougher 			stream = per_cpu_ptr(percpu, cpu);
71d208383dSPhillip Lougher 			msblk->decompressor->free(stream->stream);
72d208383dSPhillip Lougher 		}
73d208383dSPhillip Lougher 		free_percpu(percpu);
74d208383dSPhillip Lougher 	}
75d208383dSPhillip Lougher }
76d208383dSPhillip Lougher 
squashfs_decompress(struct squashfs_sb_info * msblk,struct bio * bio,int offset,int length,struct squashfs_page_actor * output)77*80f78409SXiaoming Ni static int squashfs_decompress(struct squashfs_sb_info *msblk, struct bio *bio,
7893e72b3cSPhilippe Liard 	int offset, int length, struct squashfs_page_actor *output)
79d208383dSPhillip Lougher {
80fd56200aSJulia Cartwright 	struct squashfs_stream *stream;
81*80f78409SXiaoming Ni 	struct squashfs_stream __percpu *percpu =
82*80f78409SXiaoming Ni 			(struct squashfs_stream __percpu *) msblk->stream;
83fd56200aSJulia Cartwright 	int res;
84fd56200aSJulia Cartwright 
85*80f78409SXiaoming Ni 	local_lock(&percpu->lock);
86*80f78409SXiaoming Ni 	stream = this_cpu_ptr(percpu);
87fd56200aSJulia Cartwright 
8893e72b3cSPhilippe Liard 	res = msblk->decompressor->decompress(msblk, stream->stream, bio,
89846b730eSPhillip Lougher 					      offset, length, output);
90fd56200aSJulia Cartwright 
91*80f78409SXiaoming Ni 	local_unlock(&percpu->lock);
92d208383dSPhillip Lougher 
93d208383dSPhillip Lougher 	if (res < 0)
94d208383dSPhillip Lougher 		ERROR("%s decompression failed, data probably corrupt\n",
95d208383dSPhillip Lougher 			msblk->decompressor->name);
96d208383dSPhillip Lougher 
97d208383dSPhillip Lougher 	return res;
98d208383dSPhillip Lougher }
99d208383dSPhillip Lougher 
squashfs_max_decompressors(void)100*80f78409SXiaoming Ni static int squashfs_max_decompressors(void)
101d208383dSPhillip Lougher {
102d208383dSPhillip Lougher 	return num_possible_cpus();
103d208383dSPhillip Lougher }
104*80f78409SXiaoming Ni 
105*80f78409SXiaoming Ni const struct squashfs_decompressor_thread_ops squashfs_decompressor_percpu = {
106*80f78409SXiaoming Ni 	.create = squashfs_decompressor_create,
107*80f78409SXiaoming Ni 	.destroy = squashfs_decompressor_destroy,
108*80f78409SXiaoming Ni 	.decompress = squashfs_decompress,
109*80f78409SXiaoming Ni 	.max_decompressors = squashfs_max_decompressors,
110*80f78409SXiaoming Ni };
111