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