]>
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. | |
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/>. | |
34ee731f AX |
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 | |
54 | \*****************************************************************************/ | |
55 | ||
56 | ||
57 | #include <sys/kmem.h> | |
10946b02 | 58 | #include <sys/kmem_cache.h> |
5c1967eb | 59 | #include <sys/zmod.h> |
10946b02 | 60 | #include <linux/zlib_compat.h> |
5c1967eb BB |
61 | |
62 | static spl_kmem_cache_t *zlib_workspace_cache; | |
63 | ||
64 | /* | |
65 | * A kmem_cache is used for the zlib workspaces to avoid having to vmalloc | |
66 | * and vfree for every call. Using a kmem_cache also has the advantage | |
67 | * that improves the odds that the memory used will be local to this cpu. | |
68 | * To further improve things it might be wise to create a dedicated per-cpu | |
69 | * workspace for use. This would take some additional care because we then | |
70 | * must disable preemption around the critical section, and verify that | |
71 | * zlib_deflate* and zlib_inflate* never internally call schedule(). | |
72 | */ | |
73 | static void * | |
74 | zlib_workspace_alloc(int flags) | |
75 | { | |
34ee731f | 76 | return (kmem_cache_alloc(zlib_workspace_cache, flags & ~(__GFP_FS))); |
5c1967eb BB |
77 | } |
78 | ||
79 | static void | |
80 | zlib_workspace_free(void *workspace) | |
81 | { | |
82 | kmem_cache_free(zlib_workspace_cache, workspace); | |
83 | } | |
84 | ||
85 | /* | |
86 | * Compresses the source buffer into the destination buffer. The level | |
87 | * parameter has the same meaning as in deflateInit. sourceLen is the byte | |
88 | * length of the source buffer. Upon entry, destLen is the total size of the | |
89 | * destination buffer, which must be at least 0.1% larger than sourceLen plus | |
90 | * 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. | |
91 | * | |
92 | * compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough | |
93 | * memory, Z_BUF_ERROR if there was not enough room in the output buffer, | |
94 | * Z_STREAM_ERROR if the level parameter is invalid. | |
95 | */ | |
96 | int | |
97 | z_compress_level(void *dest, size_t *destLen, const void *source, | |
34ee731f | 98 | size_t sourceLen, int level) |
5c1967eb BB |
99 | { |
100 | z_stream stream; | |
101 | int err; | |
102 | ||
103 | stream.next_in = (Byte *)source; | |
104 | stream.avail_in = (uInt)sourceLen; | |
105 | stream.next_out = dest; | |
106 | stream.avail_out = (uInt)*destLen; | |
107 | ||
108 | if ((size_t)stream.avail_out != *destLen) | |
34ee731f | 109 | return (Z_BUF_ERROR); |
5c1967eb BB |
110 | |
111 | stream.workspace = zlib_workspace_alloc(KM_SLEEP); | |
112 | if (!stream.workspace) | |
34ee731f | 113 | return (Z_MEM_ERROR); |
5c1967eb BB |
114 | |
115 | err = zlib_deflateInit(&stream, level); | |
116 | if (err != Z_OK) { | |
117 | zlib_workspace_free(stream.workspace); | |
34ee731f | 118 | return (err); |
5c1967eb BB |
119 | } |
120 | ||
121 | err = zlib_deflate(&stream, Z_FINISH); | |
122 | if (err != Z_STREAM_END) { | |
123 | zlib_deflateEnd(&stream); | |
124 | zlib_workspace_free(stream.workspace); | |
34ee731f | 125 | return (err == Z_OK ? Z_BUF_ERROR : err); |
5c1967eb BB |
126 | } |
127 | *destLen = stream.total_out; | |
128 | ||
129 | err = zlib_deflateEnd(&stream); | |
130 | zlib_workspace_free(stream.workspace); | |
131 | ||
34ee731f | 132 | return (err); |
5c1967eb BB |
133 | } |
134 | EXPORT_SYMBOL(z_compress_level); | |
135 | ||
136 | /* | |
137 | * Decompresses the source buffer into the destination buffer. sourceLen is | |
138 | * the byte length of the source buffer. Upon entry, destLen is the total | |
139 | * size of the destination buffer, which must be large enough to hold the | |
140 | * entire uncompressed data. (The size of the uncompressed data must have | |
141 | * been saved previously by the compressor and transmitted to the decompressor | |
142 | * by some mechanism outside the scope of this compression library.) | |
143 | * Upon exit, destLen is the actual size of the compressed buffer. | |
144 | * This function can be used to decompress a whole file at once if the | |
145 | * input file is mmap'ed. | |
146 | * | |
147 | * uncompress returns Z_OK if success, Z_MEM_ERROR if there was not | |
148 | * enough memory, Z_BUF_ERROR if there was not enough room in the output | |
149 | * buffer, or Z_DATA_ERROR if the input data was corrupted. | |
150 | */ | |
151 | int | |
152 | z_uncompress(void *dest, size_t *destLen, const void *source, size_t sourceLen) | |
153 | { | |
154 | z_stream stream; | |
155 | int err; | |
156 | ||
157 | stream.next_in = (Byte *)source; | |
158 | stream.avail_in = (uInt)sourceLen; | |
159 | stream.next_out = dest; | |
160 | stream.avail_out = (uInt)*destLen; | |
161 | ||
162 | if ((size_t)stream.avail_out != *destLen) | |
34ee731f | 163 | return (Z_BUF_ERROR); |
5c1967eb BB |
164 | |
165 | stream.workspace = zlib_workspace_alloc(KM_SLEEP); | |
166 | if (!stream.workspace) | |
34ee731f | 167 | return (Z_MEM_ERROR); |
5c1967eb BB |
168 | |
169 | err = zlib_inflateInit(&stream); | |
170 | if (err != Z_OK) { | |
171 | zlib_workspace_free(stream.workspace); | |
34ee731f | 172 | return (err); |
5c1967eb BB |
173 | } |
174 | ||
175 | err = zlib_inflate(&stream, Z_FINISH); | |
176 | if (err != Z_STREAM_END) { | |
177 | zlib_inflateEnd(&stream); | |
178 | zlib_workspace_free(stream.workspace); | |
179 | ||
180 | if (err == Z_NEED_DICT || | |
34ee731f AX |
181 | (err == Z_BUF_ERROR && stream.avail_in == 0)) |
182 | return (Z_DATA_ERROR); | |
5c1967eb | 183 | |
34ee731f | 184 | return (err); |
5c1967eb BB |
185 | } |
186 | *destLen = stream.total_out; | |
187 | ||
188 | err = zlib_inflateEnd(&stream); | |
189 | zlib_workspace_free(stream.workspace); | |
190 | ||
34ee731f | 191 | return (err); |
5c1967eb BB |
192 | } |
193 | EXPORT_SYMBOL(z_uncompress); | |
194 | ||
1114ae6a BB |
195 | int |
196 | spl_zlib_init(void) | |
5c1967eb | 197 | { |
3dfc591a | 198 | int size; |
3dfc591a BB |
199 | |
200 | size = MAX(spl_zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL), | |
201 | zlib_inflate_workspacesize()); | |
202 | ||
95331f44 BB |
203 | zlib_workspace_cache = kmem_cache_create( |
204 | "spl_zlib_workspace_cache", | |
205 | size, 0, NULL, NULL, NULL, NULL, NULL, | |
206 | KMC_VMEM | KMC_NOEMERGENCY); | |
34ee731f | 207 | if (!zlib_workspace_cache) |
10946b02 | 208 | return (1); |
5c1967eb | 209 | |
34ee731f | 210 | return (0); |
5c1967eb BB |
211 | } |
212 | ||
1114ae6a BB |
213 | void |
214 | spl_zlib_fini(void) | |
5c1967eb | 215 | { |
5c1967eb | 216 | kmem_cache_destroy(zlib_workspace_cache); |
34ee731f | 217 | zlib_workspace_cache = NULL; |
5c1967eb | 218 | } |