]>
Commit | Line | Data |
---|---|---|
4b393c50 | 1 | /* |
5c1967eb BB |
2 | * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. |
3 | * Copyright (C) 2007 The Regents of the University of California. | |
4 | * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). | |
5 | * Written by Brian Behlendorf <behlendorf1@llnl.gov>. | |
6 | * UCRL-CODE-235197 | |
7 | * | |
8 | * This file is part of the SPL, Solaris Porting Layer. | |
3d6af2dd | 9 | * For details, see <http://zfsonlinux.org/>. |
5c1967eb BB |
10 | * |
11 | * The SPL is free software; you can redistribute it and/or modify it | |
12 | * under the terms of the GNU General Public License as published by the | |
13 | * Free Software Foundation; either version 2 of the License, or (at your | |
14 | * option) any later version. | |
15 | * | |
16 | * The SPL is distributed in the hope that it will be useful, but WITHOUT | |
17 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
18 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
19 | * for more details. | |
20 | * | |
21 | * You should have received a copy of the GNU General Public License along | |
22 | * with the SPL. If not, see <http://www.gnu.org/licenses/>. | |
5461eefe BB |
23 | * |
24 | * | |
5c1967eb BB |
25 | * z_compress_level/z_uncompress are nearly identical copies of the |
26 | * compress2/uncompress functions provided by the official zlib package | |
27 | * available at http://zlib.net/. The only changes made we to slightly | |
28 | * adapt the functions called to match the linux kernel implementation | |
29 | * of zlib. The full zlib license follows: | |
30 | * | |
31 | * zlib.h -- interface of the 'zlib' general purpose compression library | |
32 | * version 1.2.5, April 19th, 2010 | |
33 | * | |
34 | * Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler | |
35 | * | |
36 | * This software is provided 'as-is', without any express or implied | |
37 | * warranty. In no event will the authors be held liable for any damages | |
38 | * arising from the use of this software. | |
39 | * | |
40 | * Permission is granted to anyone to use this software for any purpose, | |
41 | * including commercial applications, and to alter it and redistribute it | |
42 | * freely, subject to the following restrictions: | |
43 | * | |
44 | * 1. The origin of this software must not be misrepresented; you must not | |
45 | * claim that you wrote the original software. If you use this software | |
46 | * in a product, an acknowledgment in the product documentation would be | |
47 | * appreciated but is not required. | |
48 | * 2. Altered source versions must be plainly marked as such, and must not be | |
49 | * misrepresented as being the original software. | |
50 | * 3. This notice may not be removed or altered from any source distribution. | |
51 | * | |
52 | * Jean-loup Gailly | |
53 | * Mark Adler | |
4b393c50 | 54 | */ |
5c1967eb BB |
55 | |
56 | ||
57 | #include <sys/kmem.h> | |
e5b9b344 | 58 | #include <sys/kmem_cache.h> |
5c1967eb | 59 | #include <sys/zmod.h> |
5c1967eb BB |
60 | |
61 | static spl_kmem_cache_t *zlib_workspace_cache; | |
62 | ||
63 | /* | |
64 | * A kmem_cache is used for the zlib workspaces to avoid having to vmalloc | |
65 | * and vfree for every call. Using a kmem_cache also has the advantage | |
66 | * that improves the odds that the memory used will be local to this cpu. | |
67 | * To further improve things it might be wise to create a dedicated per-cpu | |
68 | * workspace for use. This would take some additional care because we then | |
69 | * must disable preemption around the critical section, and verify that | |
70 | * zlib_deflate* and zlib_inflate* never internally call schedule(). | |
71 | */ | |
72 | static void * | |
73 | zlib_workspace_alloc(int flags) | |
74 | { | |
5461eefe | 75 | return (kmem_cache_alloc(zlib_workspace_cache, flags & ~(__GFP_FS))); |
5c1967eb BB |
76 | } |
77 | ||
78 | static void | |
79 | zlib_workspace_free(void *workspace) | |
80 | { | |
81 | kmem_cache_free(zlib_workspace_cache, workspace); | |
82 | } | |
83 | ||
84 | /* | |
85 | * Compresses the source buffer into the destination buffer. The level | |
86 | * parameter has the same meaning as in deflateInit. sourceLen is the byte | |
87 | * length of the source buffer. Upon entry, destLen is the total size of the | |
88 | * destination buffer, which must be at least 0.1% larger than sourceLen plus | |
89 | * 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. | |
90 | * | |
91 | * compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough | |
92 | * memory, Z_BUF_ERROR if there was not enough room in the output buffer, | |
93 | * Z_STREAM_ERROR if the level parameter is invalid. | |
94 | */ | |
95 | int | |
96 | z_compress_level(void *dest, size_t *destLen, const void *source, | |
5461eefe | 97 | size_t sourceLen, int level) |
5c1967eb BB |
98 | { |
99 | z_stream stream; | |
100 | int err; | |
101 | ||
102 | stream.next_in = (Byte *)source; | |
103 | stream.avail_in = (uInt)sourceLen; | |
104 | stream.next_out = dest; | |
105 | stream.avail_out = (uInt)*destLen; | |
106 | ||
107 | if ((size_t)stream.avail_out != *destLen) | |
5461eefe | 108 | return (Z_BUF_ERROR); |
5c1967eb BB |
109 | |
110 | stream.workspace = zlib_workspace_alloc(KM_SLEEP); | |
111 | if (!stream.workspace) | |
5461eefe | 112 | return (Z_MEM_ERROR); |
5c1967eb BB |
113 | |
114 | err = zlib_deflateInit(&stream, level); | |
115 | if (err != Z_OK) { | |
116 | zlib_workspace_free(stream.workspace); | |
5461eefe | 117 | return (err); |
5c1967eb BB |
118 | } |
119 | ||
120 | err = zlib_deflate(&stream, Z_FINISH); | |
121 | if (err != Z_STREAM_END) { | |
122 | zlib_deflateEnd(&stream); | |
123 | zlib_workspace_free(stream.workspace); | |
5461eefe | 124 | return (err == Z_OK ? Z_BUF_ERROR : err); |
5c1967eb BB |
125 | } |
126 | *destLen = stream.total_out; | |
127 | ||
128 | err = zlib_deflateEnd(&stream); | |
129 | zlib_workspace_free(stream.workspace); | |
130 | ||
5461eefe | 131 | return (err); |
5c1967eb BB |
132 | } |
133 | EXPORT_SYMBOL(z_compress_level); | |
134 | ||
135 | /* | |
136 | * Decompresses the source buffer into the destination buffer. sourceLen is | |
137 | * the byte length of the source buffer. Upon entry, destLen is the total | |
138 | * size of the destination buffer, which must be large enough to hold the | |
139 | * entire uncompressed data. (The size of the uncompressed data must have | |
140 | * been saved previously by the compressor and transmitted to the decompressor | |
141 | * by some mechanism outside the scope of this compression library.) | |
142 | * Upon exit, destLen is the actual size of the compressed buffer. | |
143 | * This function can be used to decompress a whole file at once if the | |
144 | * input file is mmap'ed. | |
145 | * | |
146 | * uncompress returns Z_OK if success, Z_MEM_ERROR if there was not | |
147 | * enough memory, Z_BUF_ERROR if there was not enough room in the output | |
148 | * buffer, or Z_DATA_ERROR if the input data was corrupted. | |
149 | */ | |
150 | int | |
151 | z_uncompress(void *dest, size_t *destLen, const void *source, size_t sourceLen) | |
152 | { | |
153 | z_stream stream; | |
154 | int err; | |
155 | ||
156 | stream.next_in = (Byte *)source; | |
157 | stream.avail_in = (uInt)sourceLen; | |
158 | stream.next_out = dest; | |
159 | stream.avail_out = (uInt)*destLen; | |
160 | ||
161 | if ((size_t)stream.avail_out != *destLen) | |
5461eefe | 162 | return (Z_BUF_ERROR); |
5c1967eb BB |
163 | |
164 | stream.workspace = zlib_workspace_alloc(KM_SLEEP); | |
165 | if (!stream.workspace) | |
5461eefe | 166 | return (Z_MEM_ERROR); |
5c1967eb BB |
167 | |
168 | err = zlib_inflateInit(&stream); | |
169 | if (err != Z_OK) { | |
170 | zlib_workspace_free(stream.workspace); | |
5461eefe | 171 | return (err); |
5c1967eb BB |
172 | } |
173 | ||
174 | err = zlib_inflate(&stream, Z_FINISH); | |
175 | if (err != Z_STREAM_END) { | |
176 | zlib_inflateEnd(&stream); | |
177 | zlib_workspace_free(stream.workspace); | |
178 | ||
179 | if (err == Z_NEED_DICT || | |
5461eefe BB |
180 | (err == Z_BUF_ERROR && stream.avail_in == 0)) |
181 | return (Z_DATA_ERROR); | |
5c1967eb | 182 | |
5461eefe | 183 | return (err); |
5c1967eb BB |
184 | } |
185 | *destLen = stream.total_out; | |
186 | ||
187 | err = zlib_inflateEnd(&stream); | |
188 | zlib_workspace_free(stream.workspace); | |
189 | ||
5461eefe | 190 | return (err); |
5c1967eb BB |
191 | } |
192 | EXPORT_SYMBOL(z_uncompress); | |
193 | ||
1114ae6a BB |
194 | int |
195 | spl_zlib_init(void) | |
5c1967eb | 196 | { |
3dfc591a | 197 | int size; |
3dfc591a BB |
198 | |
199 | size = MAX(spl_zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL), | |
200 | zlib_inflate_workspacesize()); | |
201 | ||
95331f44 BB |
202 | zlib_workspace_cache = kmem_cache_create( |
203 | "spl_zlib_workspace_cache", | |
204 | size, 0, NULL, NULL, NULL, NULL, NULL, | |
205 | KMC_VMEM | KMC_NOEMERGENCY); | |
5461eefe | 206 | if (!zlib_workspace_cache) |
8d9a23e8 | 207 | return (1); |
5c1967eb | 208 | |
5461eefe | 209 | return (0); |
5c1967eb BB |
210 | } |
211 | ||
1114ae6a BB |
212 | void |
213 | spl_zlib_fini(void) | |
5c1967eb | 214 | { |
5c1967eb | 215 | kmem_cache_destroy(zlib_workspace_cache); |
5461eefe | 216 | zlib_workspace_cache = NULL; |
5c1967eb | 217 | } |