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