1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Copyright (c) 1982, 1986, 1988, 1991, 1993
29 * The Regents of the University of California. All rights reserved.
30 *
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
33 * are met:
34 * 1. Redistributions of source code must retain the above copyright
35 * notice, this list of conditions and the following disclaimer.
36 * 2. Redistributions in binary form must reproduce the above copyright
37 * notice, this list of conditions and the following disclaimer in the
38 * documentation and/or other materials provided with the distribution.
39 * 3. All advertising materials mentioning features or use of this software
40 * must display the following acknowledgement:
41 * This product includes software developed by the University of
42 * California, Berkeley and its contributors.
43 * 4. Neither the name of the University nor the names of its contributors
44 * may be used to endorse or promote products derived from this software
45 * without specific prior written permission.
46 *
47 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
48 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
51 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57 * SUCH DAMAGE.
58 *
59 */
60
61 #include <smbsrv/smb_kproto.h>
62 #include <smbsrv/smb_kstat.h>
63
64 static kmem_cache_t *smb_mbc_cache = NULL;
65 static kmem_cache_t *smb_mbuf_cache = NULL;
66 static kmem_cache_t *smb_mbufcl_cache = NULL;
67
68 void
smb_mbc_init(void)69 smb_mbc_init(void)
70 {
71 if (smb_mbc_cache != NULL)
72 return;
73 smb_mbc_cache = kmem_cache_create(SMBSRV_KSTAT_MBC_CACHE,
74 sizeof (mbuf_chain_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
75
76 smb_mbuf_cache = kmem_cache_create("smb_mbuf_cache",
77 sizeof (mbuf_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
78
79 smb_mbufcl_cache = kmem_cache_create("smb_mbufcl_cache",
80 MCLBYTES, 8, NULL, NULL, NULL, NULL, NULL, 0);
81 }
82
83 void
smb_mbc_fini(void)84 smb_mbc_fini(void)
85 {
86 if (smb_mbc_cache != NULL) {
87 kmem_cache_destroy(smb_mbc_cache);
88 smb_mbc_cache = NULL;
89 }
90 if (smb_mbuf_cache != NULL) {
91 kmem_cache_destroy(smb_mbuf_cache);
92 smb_mbuf_cache = NULL;
93 }
94 if (smb_mbufcl_cache != NULL) {
95 kmem_cache_destroy(smb_mbufcl_cache);
96 smb_mbufcl_cache = NULL;
97 }
98 }
99
100 mbuf_chain_t *
smb_mbc_alloc(uint32_t max_bytes)101 smb_mbc_alloc(uint32_t max_bytes)
102 {
103 mbuf_chain_t *mbc;
104 mbuf_t *m;
105
106 mbc = kmem_cache_alloc(smb_mbc_cache, KM_SLEEP);
107 bzero(mbc, sizeof (*mbc));
108 mbc->mbc_magic = SMB_MBC_MAGIC;
109
110 if (max_bytes != 0) {
111 MGET(m, M_WAIT, MT_DATA);
112 m->m_len = 0;
113 mbc->chain = m;
114 if (max_bytes > MINCLSIZE)
115 MCLGET(m, M_WAIT);
116 }
117 mbc->max_bytes = max_bytes;
118 return (mbc);
119 }
120
121 void
smb_mbc_free(mbuf_chain_t * mbc)122 smb_mbc_free(mbuf_chain_t *mbc)
123 {
124 SMB_MBC_VALID(mbc);
125
126 m_freem(mbc->chain);
127 mbc->chain = NULL;
128 mbc->mbc_magic = 0;
129 kmem_cache_free(smb_mbc_cache, mbc);
130 }
131
132 /*
133 * smb_mbuf_get
134 *
135 * Allocate mbufs to hold the amount of data specified.
136 * A pointer to the head of the mbuf list is returned.
137 */
138 struct mbuf *
smb_mbuf_get(uchar_t * buf,int nbytes)139 smb_mbuf_get(uchar_t *buf, int nbytes)
140 {
141 struct mbuf *mhead = 0;
142 struct mbuf *m = 0;
143 int count;
144 int offset = 0;
145
146 while (nbytes) {
147 count = (nbytes > MCLBYTES) ? MCLBYTES : nbytes;
148 nbytes -= count;
149
150 if (mhead == 0) {
151 MGET(mhead, M_WAIT, MT_DATA);
152 m = mhead;
153 } else {
154 MGET(m->m_next, M_WAIT, MT_DATA);
155 m = m->m_next;
156 }
157
158 if (count > MLEN) {
159 MCLGET(m, M_WAIT);
160 }
161
162 m->m_len = count;
163 bcopy(buf + offset, m->m_data, count);
164 offset += count;
165 }
166 return (mhead);
167 }
168
169 static int
smb_mbuf_kmem_ref(void * p,uint_t sz,int incr)170 smb_mbuf_kmem_ref(void *p, uint_t sz, int incr)
171 {
172 if (incr < 0)
173 kmem_free(p, sz);
174 return (0);
175 }
176
177 /*
178 * Allocate enough mbufs to accommodate the residual count in uio,
179 * and setup the uio_iov to point to them.
180 *
181 * This is used by the various SMB read code paths. That code is
182 * going to do a disk read into this buffer, so we'd like it to be
183 * large and contiguous. Use an external (M_EXT) buffer.
184 */
185 struct mbuf *
smb_mbuf_allocate(struct uio * uio)186 smb_mbuf_allocate(struct uio *uio)
187 {
188 mbuf_t *m = 0;
189 int len = uio->uio_resid;
190
191 MGET(m, M_WAIT, MT_DATA);
192 if (len > MCLBYTES) {
193 /* Like MCLGET(), but bigger buf. */
194 m->m_ext.ext_buf = kmem_zalloc(len, KM_SLEEP);
195 m->m_data = m->m_ext.ext_buf;
196 m->m_flags |= M_EXT;
197 m->m_ext.ext_size = len;
198 m->m_ext.ext_ref = smb_mbuf_kmem_ref;
199 } else if (len > MLEN) {
200 /* Use the kmem cache. */
201 MCLGET(m, M_WAIT);
202 }
203 m->m_len = len;
204
205 uio->uio_iov->iov_base = m->m_data;
206 uio->uio_iov->iov_len = m->m_len;
207 uio->uio_iovcnt = 1;
208
209 return (m);
210 }
211
212 /*
213 * Trim an mbuf chain to nbytes.
214 */
215 void
smb_mbuf_trim(struct mbuf * mhead,int nbytes)216 smb_mbuf_trim(struct mbuf *mhead, int nbytes)
217 {
218 struct mbuf *m = mhead;
219
220 while (m != 0) {
221 if (nbytes <= m->m_len) {
222 m->m_len = nbytes;
223 if (m->m_next != 0) {
224 m_freem(m->m_next);
225 m->m_next = 0;
226 }
227 break;
228 }
229 nbytes -= m->m_len;
230 m = m->m_next;
231 }
232 }
233
234 int
MBC_LENGTH(struct mbuf_chain * MBC)235 MBC_LENGTH(struct mbuf_chain *MBC)
236 {
237 struct mbuf *m = (MBC)->chain;
238 int used = 0;
239
240 while (m != 0) {
241 used += m->m_len;
242 m = m->m_next;
243 }
244 return (used);
245 }
246
247 int
MBC_MAXBYTES(struct mbuf_chain * MBC)248 MBC_MAXBYTES(struct mbuf_chain *MBC)
249 {
250 return (MBC->max_bytes);
251 }
252
253 void
MBC_SETUP(struct mbuf_chain * MBC,uint32_t max_bytes)254 MBC_SETUP(struct mbuf_chain *MBC, uint32_t max_bytes)
255 {
256 bzero((MBC), sizeof (struct mbuf_chain));
257 (MBC)->max_bytes = max_bytes;
258 }
259
260 void
MBC_INIT(struct mbuf_chain * MBC,uint32_t max_bytes)261 MBC_INIT(struct mbuf_chain *MBC, uint32_t max_bytes)
262 {
263 struct mbuf *m;
264
265 bzero((MBC), sizeof (struct mbuf_chain));
266
267 if (max_bytes != 0) {
268 MGET(m, M_WAIT, MT_DATA);
269 m->m_len = 0;
270 (MBC)->chain = m;
271 if (max_bytes > MINCLSIZE)
272 MCLGET(m, M_WAIT);
273 }
274 (MBC)->max_bytes = max_bytes;
275 }
276
277 void
MBC_FLUSH(struct mbuf_chain * MBC)278 MBC_FLUSH(struct mbuf_chain *MBC)
279 {
280 extern void m_freem(struct mbuf *);
281 struct mbuf *m;
282
283 while ((m = (MBC)->chain) != 0) {
284 (MBC)->chain = m->m_nextpkt;
285 m->m_nextpkt = 0;
286 m_freem(m);
287 }
288 MBC_SETUP(MBC, (MBC)->max_bytes);
289 }
290
291 void
MBC_ATTACH_MBUF(struct mbuf_chain * MBC,struct mbuf * MBUF)292 MBC_ATTACH_MBUF(struct mbuf_chain *MBC, struct mbuf *MBUF)
293 {
294 if (MBC->chain != 0)
295 MBC_FLUSH(MBC);
296
297 (MBC)->chain_offset = 0;
298 (MBC)->chain = (MBUF);
299 }
300
301 void
MBC_APPEND_MBUF(struct mbuf_chain * MBC,struct mbuf * MBUF)302 MBC_APPEND_MBUF(struct mbuf_chain *MBC, struct mbuf *MBUF)
303 {
304 struct mbuf *m;
305
306 if ((MBC)->chain == 0) {
307 (MBC)->chain = (MBUF);
308 } else {
309 m = (MBC)->chain;
310 while (m->m_next != 0)
311 m = m->m_next;
312 m->m_next = (MBUF);
313 }
314 }
315
316 static int /*ARGSUSED*/
mclrefnoop(caddr_t p,int size,int adj)317 mclrefnoop(caddr_t p, int size, int adj)
318 {
319 return (0);
320 }
321
322 void
MBC_ATTACH_BUF(struct mbuf_chain * MBC,unsigned char * BUF,int LEN)323 MBC_ATTACH_BUF(struct mbuf_chain *MBC, unsigned char *BUF, int LEN)
324 {
325 MGET((MBC)->chain, M_WAIT, MT_DATA);
326 (MBC)->chain_offset = 0;
327 (MBC)->chain->m_flags |= M_EXT;
328 (MBC)->chain->m_data = (caddr_t)(BUF);
329 (MBC)->chain->m_ext.ext_buf = (caddr_t)(BUF);
330 (MBC)->chain->m_len = (LEN);
331 (MBC)->chain->m_ext.ext_size = (LEN);
332 (MBC)->chain->m_ext.ext_ref = mclrefnoop;
333 (MBC)->max_bytes = (LEN);
334 }
335
336
337 int
MBC_SHADOW_CHAIN(struct mbuf_chain * submbc,struct mbuf_chain * mbc,int off,int len)338 MBC_SHADOW_CHAIN(struct mbuf_chain *submbc, struct mbuf_chain *mbc,
339 int off, int len)
340 {
341 int x = off + len;
342
343 if (off < 0 || len < 0 || x < 0 ||
344 off > mbc->max_bytes || x > mbc->max_bytes)
345 return (EMSGSIZE);
346
347 *submbc = *mbc;
348 submbc->chain_offset = off;
349 submbc->max_bytes = x;
350 submbc->shadow_of = mbc;
351 return (0);
352 }
353
354 /*
355 * Free a single mbuf structure. Calls m->m_ext.ext_ref() to free any
356 * associated external buffers if present (indicated by m->m_flags & M_EXT)
357 */
358 struct mbuf *
m_free(struct mbuf * m)359 m_free(struct mbuf *m)
360 {
361 struct mbuf *n;
362
363 MFREE(m, n);
364 return (n);
365 }
366
367 /*
368 * Free a list of mbufs. Each mbuf in the list is freed similarly to m_free.
369 */
370 void
m_freem(struct mbuf * m)371 m_freem(struct mbuf *m)
372 {
373 struct mbuf *n;
374
375 if (m == NULL)
376 return;
377 /*
378 * Lint doesn't like the m = n assignment at the close of the loop
379 * but it is correct. MFREE assigns n = (m)->m_next so the loop
380 * is effectively assigning m = (m)->m_next then exiting when
381 * m == NULL
382 */
383 do {
384 MFREE(m, n);
385 } while ((m = n) != 0);
386 }
387
388 /*
389 * Mbuffer utility routines.
390 */
391
392 mbuf_t *
smb_mbuf_alloc(void)393 smb_mbuf_alloc(void)
394 {
395 mbuf_t *m;
396
397 m = kmem_cache_alloc(smb_mbuf_cache, KM_SLEEP);
398 bzero(m, sizeof (*m));
399 return (m);
400 }
401
402 void
smb_mbuf_free(mbuf_t * m)403 smb_mbuf_free(mbuf_t *m)
404 {
405 kmem_cache_free(smb_mbuf_cache, m);
406 }
407
408 void *
smb_mbufcl_alloc(void)409 smb_mbufcl_alloc(void)
410 {
411 void *p;
412
413 p = kmem_cache_alloc(smb_mbufcl_cache, KM_SLEEP);
414 bzero(p, MCLBYTES);
415 return (p);
416 }
417
418 void
smb_mbufcl_free(void * p)419 smb_mbufcl_free(void *p)
420 {
421 kmem_cache_free(smb_mbufcl_cache, p);
422 }
423
424 int
smb_mbufcl_ref(void * p,uint_t sz,int incr)425 smb_mbufcl_ref(void *p, uint_t sz, int incr)
426 {
427 ASSERT3S(sz, ==, MCLBYTES);
428 if (incr < 0)
429 kmem_cache_free(smb_mbufcl_cache, p);
430 return (0);
431 }
432