| 1 | |
| 2 | |
| 3 | * Copyright (c) 2013
|
| 4 | * Minchan Kim <[email protected]>
|
| 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_obj(*stream);
|
| 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_obj(*decomp_strm);
|
| 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 |
|
| 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 |
|
| 151 | decomp_strm = kmalloc_obj(*decomp_strm);
|
| 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 | };
|