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 2011-2021 Tintri by DDN, 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 /* Perhaps move this to libfakekernel? */ 69 #ifdef _FAKE_KERNEL 70 size_t kmem_max_cached = 0x20000; // UMEM_MAXBUF 71 #endif 72 73 void 74 smb_mbc_init(void) 75 { 76 if (smb_mbc_cache != NULL) 77 return; 78 smb_mbc_cache = kmem_cache_create(SMBSRV_KSTAT_MBC_CACHE, 79 sizeof (mbuf_chain_t), 8, NULL, NULL, NULL, NULL, NULL, 0); 80 81 smb_mbuf_cache = kmem_cache_create("smb_mbuf_cache", 82 sizeof (mbuf_t), 8, NULL, NULL, NULL, NULL, NULL, 0); 83 84 smb_mbufcl_cache = kmem_cache_create("smb_mbufcl_cache", 85 MCLBYTES, 8, NULL, NULL, NULL, NULL, NULL, 0); 86 } 87 88 void 89 smb_mbc_fini(void) 90 { 91 if (smb_mbc_cache != NULL) { 92 kmem_cache_destroy(smb_mbc_cache); 93 smb_mbc_cache = NULL; 94 } 95 if (smb_mbuf_cache != NULL) { 96 kmem_cache_destroy(smb_mbuf_cache); 97 smb_mbuf_cache = NULL; 98 } 99 if (smb_mbufcl_cache != NULL) { 100 kmem_cache_destroy(smb_mbufcl_cache); 101 smb_mbufcl_cache = NULL; 102 } 103 } 104 105 mbuf_chain_t * 106 smb_mbc_alloc(uint32_t max_bytes) 107 { 108 mbuf_chain_t *mbc; 109 mbuf_t *m; 110 111 mbc = kmem_cache_alloc(smb_mbc_cache, KM_SLEEP); 112 bzero(mbc, sizeof (*mbc)); 113 mbc->mbc_magic = SMB_MBC_MAGIC; 114 115 if (max_bytes != 0) { 116 MGET(m, M_WAIT, MT_DATA); 117 m->m_len = 0; 118 mbc->chain = m; 119 if (max_bytes > MINCLSIZE) 120 MCLGET(m, M_WAIT); 121 } 122 mbc->max_bytes = max_bytes; 123 return (mbc); 124 } 125 126 void 127 smb_mbc_free(mbuf_chain_t *mbc) 128 { 129 SMB_MBC_VALID(mbc); 130 131 m_freem(mbc->chain); 132 mbc->chain = NULL; 133 mbc->mbc_magic = 0; 134 kmem_cache_free(smb_mbc_cache, mbc); 135 } 136 137 /* 138 * smb_mbuf_get 139 * 140 * Allocate mbufs to hold the amount of data specified. 141 * A pointer to the head of the mbuf list is returned. 142 */ 143 struct mbuf * 144 smb_mbuf_get(uchar_t *buf, int nbytes) 145 { 146 struct mbuf *mhead = 0; 147 struct mbuf *m = 0; 148 int count; 149 int offset = 0; 150 151 while (nbytes) { 152 count = (nbytes > MCLBYTES) ? MCLBYTES : nbytes; 153 nbytes -= count; 154 155 if (mhead == 0) { 156 MGET(mhead, M_WAIT, MT_DATA); 157 m = mhead; 158 } else { 159 MGET(m->m_next, M_WAIT, MT_DATA); 160 m = m->m_next; 161 } 162 163 if (count > MLEN) { 164 MCLGET(m, M_WAIT); 165 } 166 167 m->m_len = count; 168 bcopy(buf + offset, m->m_data, count); 169 offset += count; 170 } 171 return (mhead); 172 } 173 174 /* 175 * Build an mbuf with external storage 176 * Like esballoca() 177 */ 178 mbuf_t * 179 smb_mbuf_alloc_ext(caddr_t buf, int len, m_ext_free_t ff, void *arg) 180 { 181 mbuf_t *m = 0; 182 183 MGET(m, M_WAIT, MT_DATA); 184 185 /* Like MCLGET(), but external buf. */ 186 m->m_ext.ext_buf = buf; 187 m->m_data = m->m_ext.ext_buf; 188 m->m_flags |= M_EXT; 189 m->m_ext.ext_size = len; 190 m->m_ext.ext_free = ff; 191 m->m_ext.ext_arg1 = arg; 192 193 m->m_len = len; 194 195 return (m); 196 } 197 198 static void 199 smb_mbuf_kmem_free(mbuf_t *m) 200 { 201 ASSERT((m->m_flags & M_EXT) != 0); 202 203 kmem_free(m->m_ext.ext_buf, m->m_ext.ext_size); 204 /* Caller sets m->m_ext.ext_buf = NULL */ 205 } 206 207 /* 208 * Allocate one mbuf with contiguous (external) storage 209 * obtained via kmem_alloc. Most places should be using 210 * smb_mbuf_alloc_chain below. 211 */ 212 mbuf_t * 213 smb_mbuf_alloc_kmem(int len) 214 { 215 mbuf_t *m; 216 caddr_t buf; 217 218 VERIFY(len > 0); 219 #ifdef DEBUG 220 if (len > kmem_max_cached) { 221 debug_enter("fix caller"); 222 } 223 #endif 224 225 buf = kmem_alloc(len, KM_SLEEP); 226 m = smb_mbuf_alloc_ext(buf, len, smb_mbuf_kmem_free, NULL); 227 228 return (m); 229 } 230 231 /* 232 * smb_mbuf_alloc_chain 233 * 234 * Allocate mbufs to hold the amount of data specified. 235 * A pointer to the head of the mbuf chain is returned. 236 * 237 * For large messages, put the large mbufs at the tail, starting with 238 * kmem_max_cached messages (128K) and then a shorter (eg MCLBYTES) 239 * message at the front where we're more likely to need to copy. 240 * 241 * Must limit this to kmem_max_cached, to avoid contention with 242 * allocating from kmem_oversize_arena. 243 */ 244 struct mbuf * 245 smb_mbuf_alloc_chain(int nbytes) 246 { 247 struct mbuf *mhead = NULL; 248 struct mbuf *m; 249 int len; /* m_len for current mbuf */ 250 251 ASSERT(nbytes > 0); 252 253 while (nbytes >= kmem_max_cached) { 254 len = kmem_max_cached; 255 m = smb_mbuf_alloc_kmem(len); 256 257 /* prepend to chain at mhead */ 258 m->m_next = mhead; 259 mhead = m; 260 261 nbytes -= len; 262 } 263 264 if (nbytes > MCLBYTES) { 265 len = nbytes; 266 m = smb_mbuf_alloc_kmem(len); 267 268 /* prepend to chain at mhead */ 269 m->m_next = mhead; 270 mhead = m; 271 272 nbytes -= len; 273 } else if (nbytes > 0) { 274 ASSERT(nbytes <= MCLBYTES); 275 len = nbytes; 276 MGET(m, M_WAIT, MT_DATA); 277 if (len > MLEN) { 278 MCLGET(m, M_WAIT); 279 } 280 m->m_len = len; 281 282 /* prepend to chain at mhead */ 283 m->m_next = mhead; 284 mhead = m; 285 286 nbytes -= len; 287 } 288 289 return (mhead); 290 } 291 292 /* 293 * Allocate enough mbufs to accommodate the residual count in uio, 294 * and setup the uio_iov to point to them. Note that uio->uio_iov 295 * is allocated by the call and has MAX_IOVEC elements. Currently 296 * the uio passed is always part of an smb_vdb_t and starts with 297 * uio->uio_iovcnt = MAX_IOVEC; 298 * 299 * This is used by the various SMB read code paths. That code is 300 * going to do a disk read into this buffer, so we'd like the 301 * segments to be large. See smb_mbuf_alloc_chain(). 302 */ 303 struct mbuf * 304 smb_mbuf_allocate(struct uio *uio) 305 { 306 mbuf_t *mhead; 307 int rc; 308 309 ASSERT(uio->uio_resid > 0); 310 ASSERT(uio->uio_iovcnt == MAX_IOVEC); 311 312 mhead = smb_mbuf_alloc_chain(uio->uio_resid); 313 314 rc = smb_mbuf_mkuio(mhead, uio); 315 VERIFY(rc == 0); 316 317 return (mhead); 318 } 319 320 /* 321 * Build an iovec for an mbuf chain. 322 * 323 * The resulting iovec covers uio_resid length in the chain, 324 * which could be shorter than the mbuf chain total length. 325 * 326 * uio->uio_iovcnt is allocated size on entry, used size on return. 327 * Errors if iovec too small or mbuf chain too short. 328 */ 329 int 330 smb_mbuf_mkuio(mbuf_t *m, uio_t *uio) 331 { 332 iovec_t *iov; 333 ssize_t off = 0; 334 int iovcnt = 0; 335 int tlen; 336 337 iov = uio->uio_iov; 338 while (off < uio->uio_resid) { 339 if (m == NULL) 340 return (EFAULT); 341 if (iovcnt >= uio->uio_iovcnt) 342 return (E2BIG); 343 tlen = m->m_len; 344 if ((off + tlen) > uio->uio_resid) 345 tlen = (int)(uio->uio_resid - off); 346 iov->iov_base = m->m_data; 347 iov->iov_len = tlen; 348 off += tlen; 349 m = m->m_next; 350 iovcnt++; 351 iov++; 352 } 353 uio->uio_iovcnt = iovcnt; 354 355 return (0); 356 } 357 358 /* 359 * Trim an mbuf chain to nbytes. 360 */ 361 void 362 smb_mbuf_trim(struct mbuf *mhead, int nbytes) 363 { 364 struct mbuf *m = mhead; 365 366 while (m != 0) { 367 if (nbytes <= m->m_len) { 368 m->m_len = nbytes; 369 if (m->m_next != 0) { 370 m_freem(m->m_next); 371 m->m_next = 0; 372 } 373 break; 374 } 375 nbytes -= m->m_len; 376 m = m->m_next; 377 } 378 } 379 380 int 381 MBC_LENGTH(struct mbuf_chain *MBC) 382 { 383 struct mbuf *m = (MBC)->chain; 384 int used = 0; 385 386 while (m != 0) { 387 used += m->m_len; 388 m = m->m_next; 389 } 390 return (used); 391 } 392 393 int 394 MBC_MAXBYTES(struct mbuf_chain *MBC) 395 { 396 return (MBC->max_bytes); 397 } 398 399 void 400 MBC_SETUP(struct mbuf_chain *MBC, uint32_t max_bytes) 401 { 402 bzero((MBC), sizeof (struct mbuf_chain)); 403 (MBC)->max_bytes = max_bytes; 404 } 405 406 /* 407 * Initialize an mbuf chain and allocate the first message (if max_bytes 408 * is specified). Leave some prepend space at the head. 409 */ 410 void 411 MBC_INIT(struct mbuf_chain *MBC, uint32_t max_bytes) 412 { 413 struct mbuf *m; 414 415 bzero((MBC), sizeof (struct mbuf_chain)); 416 417 if (max_bytes != 0) { 418 MGET(m, M_WAIT, MT_DATA); 419 m->m_len = 0; 420 (MBC)->chain = m; 421 if (max_bytes > MINCLSIZE) 422 MCLGET(m, M_WAIT); 423 m->m_data += MH_PREPEND_SPACE; 424 } 425 (MBC)->max_bytes = max_bytes; 426 } 427 428 void 429 MBC_FLUSH(struct mbuf_chain *MBC) 430 { 431 extern void m_freem(struct mbuf *); 432 struct mbuf *m; 433 434 while ((m = (MBC)->chain) != 0) { 435 (MBC)->chain = m->m_nextpkt; 436 m->m_nextpkt = 0; 437 m_freem(m); 438 } 439 MBC_SETUP(MBC, (MBC)->max_bytes); 440 } 441 442 void 443 MBC_ATTACH_MBUF(struct mbuf_chain *MBC, struct mbuf *MBUF) 444 { 445 if (MBC->chain != 0) 446 MBC_FLUSH(MBC); 447 448 (MBC)->chain_offset = 0; 449 (MBC)->chain = (MBUF); 450 } 451 452 void 453 MBC_APPEND_MBUF(struct mbuf_chain *MBC, struct mbuf *MBUF) 454 { 455 struct mbuf *m; 456 457 if ((MBC)->chain == 0) { 458 (MBC)->chain = (MBUF); 459 } else { 460 m = (MBC)->chain; 461 while (m->m_next != 0) 462 m = m->m_next; 463 m->m_next = (MBUF); 464 } 465 } 466 467 static void /*ARGSUSED*/ 468 mclrefnoop(mbuf_t *m) 469 { 470 } 471 472 void 473 MBC_ATTACH_BUF(struct mbuf_chain *MBC, unsigned char *BUF, int LEN) 474 { 475 MGET((MBC)->chain, M_WAIT, MT_DATA); 476 (MBC)->chain_offset = 0; 477 (MBC)->chain->m_flags |= M_EXT; 478 (MBC)->chain->m_data = (caddr_t)(BUF); 479 (MBC)->chain->m_ext.ext_buf = (caddr_t)(BUF); 480 (MBC)->chain->m_len = (LEN); 481 (MBC)->chain->m_ext.ext_size = (LEN); 482 (MBC)->chain->m_ext.ext_free = mclrefnoop; 483 (MBC)->max_bytes = (LEN); 484 } 485 486 487 int 488 MBC_SHADOW_CHAIN(struct mbuf_chain *submbc, struct mbuf_chain *mbc, 489 int off, int len) 490 { 491 int x = off + len; 492 493 if (off < 0 || len < 0 || x < 0 || 494 off > mbc->max_bytes || x > mbc->max_bytes) 495 return (EMSGSIZE); 496 497 *submbc = *mbc; 498 submbc->chain_offset = off; 499 submbc->max_bytes = x; 500 submbc->shadow_of = mbc; 501 return (0); 502 } 503 504 /* 505 * Trim req_len bytes from the message, 506 * from head if > 0, else from tail. 507 */ 508 void 509 m_adjust(struct mbuf *mp, int req_len) 510 { 511 int len = req_len; 512 struct mbuf *m; 513 514 if ((m = mp) == NULL) 515 return; 516 517 /* 518 * We don't use the trim-from-tail case. 519 * Using smb_mbuf_trim() for that. 520 */ 521 VERIFY(len >= 0); 522 523 /* 524 * Trim from head. 525 */ 526 while (m != NULL && len > 0) { 527 if (m->m_len <= len) { 528 len -= m->m_len; 529 m->m_len = 0; 530 m = m->m_next; 531 } else { 532 m->m_len -= len; 533 m->m_data += len; 534 len = 0; 535 } 536 } 537 } 538 539 /* 540 * Arrange to prepend space of size plen to mbuf m. If a new mbuf must be 541 * allocated, how specifies whether to wait. If the allocation fails, the 542 * original mbuf chain is freed and m is set to NULL. 543 * BSD had a macro: M_PREPEND(m, plen, how) 544 */ 545 546 struct mbuf * 547 m_prepend(struct mbuf *m, int plen, int how) 548 { 549 struct mbuf *mn; 550 551 if (M_LEADINGSPACE(m) >= plen) { 552 m->m_data -= plen; 553 m->m_len += plen; 554 return (m); 555 } 556 if (m->m_flags & M_PKTHDR) { 557 MGETHDR(mn, how, m->m_type); 558 } else { 559 MGET(mn, how, m->m_type); 560 } 561 VERIFY(mn != NULL); 562 if (m->m_flags & M_PKTHDR) { 563 // BSD: m_move_pkthdr(mn, m); 564 // We don't use any pkthdr stuff. 565 mn->m_pkthdr.len += plen; 566 } 567 mn->m_next = m; 568 m = mn; 569 if (plen < M_SIZE(m)) 570 M_ALIGN(m, plen); 571 m->m_len = plen; 572 DTRACE_PROBE1(prepend_allocated, struct mbuf *, m); 573 return (m); 574 } 575 576 /* 577 * Free a single mbuf structure. Calls m->m_ext.ext_ref() to free any 578 * associated external buffers if present (indicated by m->m_flags & M_EXT) 579 */ 580 struct mbuf * 581 m_free(struct mbuf *m) 582 { 583 struct mbuf *n; 584 585 MFREE(m, n); 586 return (n); 587 } 588 589 /* 590 * Free a list of mbufs. Each mbuf in the list is freed similarly to m_free. 591 */ 592 void 593 m_freem(struct mbuf *m) 594 { 595 struct mbuf *n; 596 597 if (m == NULL) 598 return; 599 /* 600 * Lint doesn't like the m = n assignment at the close of the loop 601 * but it is correct. MFREE assigns n = (m)->m_next so the loop 602 * is effectively assigning m = (m)->m_next then exiting when 603 * m == NULL 604 */ 605 do { 606 MFREE(m, n); 607 } while ((m = n) != 0); 608 } 609 610 /* 611 * Mbuffer utility routines. 612 */ 613 614 mbuf_t * 615 smb_mbuf_alloc(void) 616 { 617 mbuf_t *m; 618 619 m = kmem_cache_alloc(smb_mbuf_cache, KM_SLEEP); 620 bzero(m, sizeof (*m)); 621 return (m); 622 } 623 624 void 625 smb_mbuf_free(mbuf_t *m) 626 { 627 kmem_cache_free(smb_mbuf_cache, m); 628 } 629 630 void * 631 smb_mbufcl_alloc(void) 632 { 633 void *p; 634 635 p = kmem_cache_alloc(smb_mbufcl_cache, KM_SLEEP); 636 bzero(p, MCLBYTES); 637 return (p); 638 } 639 640 void 641 smb_mbufcl_free(mbuf_t *m) 642 { 643 ASSERT((m->m_flags & M_EXT) != 0); 644 ASSERT(m->m_ext.ext_size == MCLBYTES); 645 646 kmem_cache_free(smb_mbufcl_cache, m->m_ext.ext_buf); 647 /* Caller sets m->m_ext.ext_buf = NULL */ 648 } 649 650 int 651 smb_mbufcl_ref(void *p, uint_t sz, int incr) 652 { 653 ASSERT3S(sz, ==, MCLBYTES); 654 if (incr < 0) 655 kmem_cache_free(smb_mbufcl_cache, p); 656 return (0); 657 } 658