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
64static kmem_cache_t	*smb_mbc_cache = NULL;
65static kmem_cache_t	*smb_mbuf_cache = NULL;
66static kmem_cache_t	*smb_mbufcl_cache = NULL;
67
68void
69smb_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
83void
84smb_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
100mbuf_chain_t *
101smb_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
121void
122smb_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 */
138struct mbuf *
139smb_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
169static int
170smb_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 */
185struct mbuf *
186smb_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 */
215void
216smb_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
234int
235MBC_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
247int
248MBC_MAXBYTES(struct mbuf_chain *MBC)
249{
250	return (MBC->max_bytes);
251}
252
253void
254MBC_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
260void
261MBC_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
277void
278MBC_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
291void
292MBC_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
301void
302MBC_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
316static int /*ARGSUSED*/
317mclrefnoop(caddr_t p, int size, int adj)
318{
319	return (0);
320}
321
322void
323MBC_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
337int
338MBC_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 */
358struct mbuf *
359m_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 */
370void
371m_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
392mbuf_t *
393smb_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
402void
403smb_mbuf_free(mbuf_t *m)
404{
405	kmem_cache_free(smb_mbuf_cache, m);
406}
407
408void *
409smb_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
418void
419smb_mbufcl_free(void *p)
420{
421	kmem_cache_free(smb_mbufcl_cache, p);
422}
423
424int
425smb_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