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 }; 204
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.