1 /* 2 * Copyright (c) 1994, David Greenman 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice unmodified, this list of conditions, and the following 10 * disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD: src/sys/kern/tty_subr.c,v 1.32 1999/08/28 00:46:21 peter Exp $ 28 * $DragonFly: src/sys/kern/tty_subr.c,v 1.2 2003/06/17 04:28:41 dillon Exp $ 29 */ 30 31 /* 32 * clist support routines 33 */ 34 35 #include <sys/param.h> 36 #include <sys/kernel.h> 37 #include <sys/systm.h> 38 #include <sys/malloc.h> 39 #include <sys/tty.h> 40 #include <sys/clist.h> 41 42 static void clist_init __P((void *)); 43 SYSINIT(clist, SI_SUB_CLIST, SI_ORDER_FIRST, clist_init, NULL) 44 45 static struct cblock *cfreelist = 0; 46 int cfreecount = 0; 47 static int cslushcount; 48 static int ctotcount; 49 50 #ifndef INITIAL_CBLOCKS 51 #define INITIAL_CBLOCKS 50 52 #endif 53 54 static struct cblock *cblock_alloc __P((void)); 55 static void cblock_alloc_cblocks __P((int number)); 56 static void cblock_free __P((struct cblock *cblockp)); 57 static void cblock_free_cblocks __P((int number)); 58 59 #include "opt_ddb.h" 60 #ifdef DDB 61 #include <ddb/ddb.h> 62 63 DB_SHOW_COMMAND(cbstat, cbstat) 64 { 65 int cbsize = CBSIZE; 66 67 printf( 68 "tot = %d (active = %d, free = %d (reserved = %d, slush = %d))\n", 69 ctotcount * cbsize, ctotcount * cbsize - cfreecount, cfreecount, 70 cfreecount - cslushcount * cbsize, cslushcount * cbsize); 71 } 72 #endif /* DDB */ 73 74 /* 75 * Called from init_main.c 76 */ 77 /* ARGSUSED*/ 78 static void 79 clist_init(dummy) 80 void *dummy; 81 { 82 /* 83 * Allocate an initial base set of cblocks as a 'slush'. 84 * We allocate non-slush cblocks with each initial ttyopen() and 85 * deallocate them with each ttyclose(). 86 * We should adjust the slush allocation. This can't be done in 87 * the i/o routines because they are sometimes called from 88 * interrupt handlers when it may be unsafe to call malloc(). 89 */ 90 cblock_alloc_cblocks(cslushcount = INITIAL_CBLOCKS); 91 } 92 93 /* 94 * Remove a cblock from the cfreelist queue and return a pointer 95 * to it. 96 */ 97 static __inline struct cblock * 98 cblock_alloc() 99 { 100 struct cblock *cblockp; 101 102 cblockp = cfreelist; 103 if (cblockp == NULL) 104 panic("clist reservation botch"); 105 cfreelist = cblockp->c_next; 106 cblockp->c_next = NULL; 107 cfreecount -= CBSIZE; 108 return (cblockp); 109 } 110 111 /* 112 * Add a cblock to the cfreelist queue. 113 */ 114 static __inline void 115 cblock_free(cblockp) 116 struct cblock *cblockp; 117 { 118 if (isset(cblockp->c_quote, CBQSIZE * NBBY - 1)) 119 bzero(cblockp->c_quote, sizeof cblockp->c_quote); 120 cblockp->c_next = cfreelist; 121 cfreelist = cblockp; 122 cfreecount += CBSIZE; 123 } 124 125 /* 126 * Allocate some cblocks for the cfreelist queue. 127 */ 128 static void 129 cblock_alloc_cblocks(number) 130 int number; 131 { 132 int i; 133 struct cblock *cbp; 134 135 for (i = 0; i < number; ++i) { 136 cbp = malloc(sizeof *cbp, M_TTYS, M_NOWAIT); 137 if (cbp == NULL) { 138 printf( 139 "clist_alloc_cblocks: M_NOWAIT malloc failed, trying M_WAITOK\n"); 140 cbp = malloc(sizeof *cbp, M_TTYS, M_WAITOK); 141 } 142 /* 143 * Freed cblocks have zero quotes and garbage elsewhere. 144 * Set the may-have-quote bit to force zeroing the quotes. 145 */ 146 setbit(cbp->c_quote, CBQSIZE * NBBY - 1); 147 cblock_free(cbp); 148 } 149 ctotcount += number; 150 } 151 152 /* 153 * Set the cblock allocation policy for a a clist. 154 * Must be called in process context at spltty(). 155 */ 156 void 157 clist_alloc_cblocks(clistp, ccmax, ccreserved) 158 struct clist *clistp; 159 int ccmax; 160 int ccreserved; 161 { 162 int dcbr; 163 164 /* 165 * Allow for wasted space at the head. 166 */ 167 if (ccmax != 0) 168 ccmax += CBSIZE - 1; 169 if (ccreserved != 0) 170 ccreserved += CBSIZE - 1; 171 172 clistp->c_cbmax = roundup(ccmax, CBSIZE) / CBSIZE; 173 dcbr = roundup(ccreserved, CBSIZE) / CBSIZE - clistp->c_cbreserved; 174 if (dcbr >= 0) 175 cblock_alloc_cblocks(dcbr); 176 else { 177 if (clistp->c_cbreserved + dcbr < clistp->c_cbcount) 178 dcbr = clistp->c_cbcount - clistp->c_cbreserved; 179 cblock_free_cblocks(-dcbr); 180 } 181 clistp->c_cbreserved += dcbr; 182 } 183 184 /* 185 * Free some cblocks from the cfreelist queue back to the 186 * system malloc pool. 187 */ 188 static void 189 cblock_free_cblocks(number) 190 int number; 191 { 192 int i; 193 194 for (i = 0; i < number; ++i) 195 free(cblock_alloc(), M_TTYS); 196 ctotcount -= number; 197 } 198 199 /* 200 * Free the cblocks reserved for a clist. 201 * Must be called at spltty(). 202 */ 203 void 204 clist_free_cblocks(clistp) 205 struct clist *clistp; 206 { 207 if (clistp->c_cbcount != 0) 208 panic("freeing active clist cblocks"); 209 cblock_free_cblocks(clistp->c_cbreserved); 210 clistp->c_cbmax = 0; 211 clistp->c_cbreserved = 0; 212 } 213 214 /* 215 * Get a character from the head of a clist. 216 */ 217 int 218 getc(clistp) 219 struct clist *clistp; 220 { 221 int chr = -1; 222 int s; 223 struct cblock *cblockp; 224 225 s = spltty(); 226 227 /* If there are characters in the list, get one */ 228 if (clistp->c_cc) { 229 cblockp = (struct cblock *)((intptr_t)clistp->c_cf & ~CROUND); 230 chr = (u_char)*clistp->c_cf; 231 232 /* 233 * If this char is quoted, set the flag. 234 */ 235 if (isset(cblockp->c_quote, clistp->c_cf - (char *)cblockp->c_info)) 236 chr |= TTY_QUOTE; 237 238 /* 239 * Advance to next character. 240 */ 241 clistp->c_cf++; 242 clistp->c_cc--; 243 /* 244 * If we have advanced the 'first' character pointer 245 * past the end of this cblock, advance to the next one. 246 * If there are no more characters, set the first and 247 * last pointers to NULL. In either case, free the 248 * current cblock. 249 */ 250 if ((clistp->c_cf >= (char *)(cblockp+1)) || (clistp->c_cc == 0)) { 251 if (clistp->c_cc > 0) { 252 clistp->c_cf = cblockp->c_next->c_info; 253 } else { 254 clistp->c_cf = clistp->c_cl = NULL; 255 } 256 cblock_free(cblockp); 257 if (--clistp->c_cbcount >= clistp->c_cbreserved) 258 ++cslushcount; 259 } 260 } 261 262 splx(s); 263 return (chr); 264 } 265 266 /* 267 * Copy 'amount' of chars, beginning at head of clist 'clistp' to 268 * destination linear buffer 'dest'. Return number of characters 269 * actually copied. 270 */ 271 int 272 q_to_b(clistp, dest, amount) 273 struct clist *clistp; 274 char *dest; 275 int amount; 276 { 277 struct cblock *cblockp; 278 struct cblock *cblockn; 279 char *dest_orig = dest; 280 int numc; 281 int s; 282 283 s = spltty(); 284 285 while (clistp && amount && (clistp->c_cc > 0)) { 286 cblockp = (struct cblock *)((intptr_t)clistp->c_cf & ~CROUND); 287 cblockn = cblockp + 1; /* pointer arithmetic! */ 288 numc = min(amount, (char *)cblockn - clistp->c_cf); 289 numc = min(numc, clistp->c_cc); 290 bcopy(clistp->c_cf, dest, numc); 291 amount -= numc; 292 clistp->c_cf += numc; 293 clistp->c_cc -= numc; 294 dest += numc; 295 /* 296 * If this cblock has been emptied, advance to the next 297 * one. If there are no more characters, set the first 298 * and last pointer to NULL. In either case, free the 299 * current cblock. 300 */ 301 if ((clistp->c_cf >= (char *)cblockn) || (clistp->c_cc == 0)) { 302 if (clistp->c_cc > 0) { 303 clistp->c_cf = cblockp->c_next->c_info; 304 } else { 305 clistp->c_cf = clistp->c_cl = NULL; 306 } 307 cblock_free(cblockp); 308 if (--clistp->c_cbcount >= clistp->c_cbreserved) 309 ++cslushcount; 310 } 311 } 312 313 splx(s); 314 return (dest - dest_orig); 315 } 316 317 /* 318 * Flush 'amount' of chars, beginning at head of clist 'clistp'. 319 */ 320 void 321 ndflush(clistp, amount) 322 struct clist *clistp; 323 int amount; 324 { 325 struct cblock *cblockp; 326 struct cblock *cblockn; 327 int numc; 328 int s; 329 330 s = spltty(); 331 332 while (amount && (clistp->c_cc > 0)) { 333 cblockp = (struct cblock *)((intptr_t)clistp->c_cf & ~CROUND); 334 cblockn = cblockp + 1; /* pointer arithmetic! */ 335 numc = min(amount, (char *)cblockn - clistp->c_cf); 336 numc = min(numc, clistp->c_cc); 337 amount -= numc; 338 clistp->c_cf += numc; 339 clistp->c_cc -= numc; 340 /* 341 * If this cblock has been emptied, advance to the next 342 * one. If there are no more characters, set the first 343 * and last pointer to NULL. In either case, free the 344 * current cblock. 345 */ 346 if ((clistp->c_cf >= (char *)cblockn) || (clistp->c_cc == 0)) { 347 if (clistp->c_cc > 0) { 348 clistp->c_cf = cblockp->c_next->c_info; 349 } else { 350 clistp->c_cf = clistp->c_cl = NULL; 351 } 352 cblock_free(cblockp); 353 if (--clistp->c_cbcount >= clistp->c_cbreserved) 354 ++cslushcount; 355 } 356 } 357 358 splx(s); 359 } 360 361 /* 362 * Add a character to the end of a clist. Return -1 is no 363 * more clists, or 0 for success. 364 */ 365 int 366 putc(chr, clistp) 367 int chr; 368 struct clist *clistp; 369 { 370 struct cblock *cblockp; 371 int s; 372 373 s = spltty(); 374 375 if (clistp->c_cl == NULL) { 376 if (clistp->c_cbreserved < 1) { 377 splx(s); 378 printf("putc to a clist with no reserved cblocks\n"); 379 return (-1); /* nothing done */ 380 } 381 cblockp = cblock_alloc(); 382 clistp->c_cbcount = 1; 383 clistp->c_cf = clistp->c_cl = cblockp->c_info; 384 clistp->c_cc = 0; 385 } else { 386 cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND); 387 if (((intptr_t)clistp->c_cl & CROUND) == 0) { 388 struct cblock *prev = (cblockp - 1); 389 390 if (clistp->c_cbcount >= clistp->c_cbreserved) { 391 if (clistp->c_cbcount >= clistp->c_cbmax 392 || cslushcount <= 0) { 393 splx(s); 394 return (-1); 395 } 396 --cslushcount; 397 } 398 cblockp = cblock_alloc(); 399 clistp->c_cbcount++; 400 prev->c_next = cblockp; 401 clistp->c_cl = cblockp->c_info; 402 } 403 } 404 405 /* 406 * If this character is quoted, set the quote bit, if not, clear it. 407 */ 408 if (chr & TTY_QUOTE) { 409 setbit(cblockp->c_quote, clistp->c_cl - (char *)cblockp->c_info); 410 /* 411 * Use one of the spare quote bits to record that something 412 * may be quoted. 413 */ 414 setbit(cblockp->c_quote, CBQSIZE * NBBY - 1); 415 } else 416 clrbit(cblockp->c_quote, clistp->c_cl - (char *)cblockp->c_info); 417 418 *clistp->c_cl++ = chr; 419 clistp->c_cc++; 420 421 splx(s); 422 return (0); 423 } 424 425 /* 426 * Copy data from linear buffer to clist chain. Return the 427 * number of characters not copied. 428 */ 429 int 430 b_to_q(src, amount, clistp) 431 char *src; 432 int amount; 433 struct clist *clistp; 434 { 435 struct cblock *cblockp; 436 char *firstbyte, *lastbyte; 437 u_char startmask, endmask; 438 int startbit, endbit, num_between, numc; 439 int s; 440 441 /* 442 * Avoid allocating an initial cblock and then not using it. 443 * c_cc == 0 must imply c_cbount == 0. 444 */ 445 if (amount <= 0) 446 return (amount); 447 448 s = spltty(); 449 450 /* 451 * If there are no cblocks assigned to this clist yet, 452 * then get one. 453 */ 454 if (clistp->c_cl == NULL) { 455 if (clistp->c_cbreserved < 1) { 456 splx(s); 457 printf("b_to_q to a clist with no reserved cblocks.\n"); 458 return (amount); /* nothing done */ 459 } 460 cblockp = cblock_alloc(); 461 clistp->c_cbcount = 1; 462 clistp->c_cf = clistp->c_cl = cblockp->c_info; 463 clistp->c_cc = 0; 464 } else { 465 cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND); 466 } 467 468 while (amount) { 469 /* 470 * Get another cblock if needed. 471 */ 472 if (((intptr_t)clistp->c_cl & CROUND) == 0) { 473 struct cblock *prev = cblockp - 1; 474 475 if (clistp->c_cbcount >= clistp->c_cbreserved) { 476 if (clistp->c_cbcount >= clistp->c_cbmax 477 || cslushcount <= 0) { 478 splx(s); 479 return (amount); 480 } 481 --cslushcount; 482 } 483 cblockp = cblock_alloc(); 484 clistp->c_cbcount++; 485 prev->c_next = cblockp; 486 clistp->c_cl = cblockp->c_info; 487 } 488 489 /* 490 * Copy a chunk of the linear buffer up to the end 491 * of this cblock. 492 */ 493 numc = min(amount, (char *)(cblockp + 1) - clistp->c_cl); 494 bcopy(src, clistp->c_cl, numc); 495 496 /* 497 * Clear quote bits if they aren't known to be clear. 498 * The following could probably be made into a seperate 499 * "bitzero()" routine, but why bother? 500 */ 501 if (isset(cblockp->c_quote, CBQSIZE * NBBY - 1)) { 502 startbit = clistp->c_cl - (char *)cblockp->c_info; 503 endbit = startbit + numc - 1; 504 505 firstbyte = (u_char *)cblockp->c_quote + (startbit / NBBY); 506 lastbyte = (u_char *)cblockp->c_quote + (endbit / NBBY); 507 508 /* 509 * Calculate mask of bits to preserve in first and 510 * last bytes. 511 */ 512 startmask = NBBY - (startbit % NBBY); 513 startmask = 0xff >> startmask; 514 endmask = (endbit % NBBY); 515 endmask = 0xff << (endmask + 1); 516 517 if (firstbyte != lastbyte) { 518 *firstbyte &= startmask; 519 *lastbyte &= endmask; 520 521 num_between = lastbyte - firstbyte - 1; 522 if (num_between) 523 bzero(firstbyte + 1, num_between); 524 } else { 525 *firstbyte &= (startmask | endmask); 526 } 527 } 528 529 /* 530 * ...and update pointer for the next chunk. 531 */ 532 src += numc; 533 clistp->c_cl += numc; 534 clistp->c_cc += numc; 535 amount -= numc; 536 /* 537 * If we go through the loop again, it's always 538 * for data in the next cblock, so by adding one (cblock), 539 * (which makes the pointer 1 beyond the end of this 540 * cblock) we prepare for the assignment of 'prev' 541 * above. 542 */ 543 cblockp += 1; 544 545 } 546 547 splx(s); 548 return (amount); 549 } 550 551 /* 552 * Get the next character in the clist. Store it at dst. Don't 553 * advance any clist pointers, but return a pointer to the next 554 * character position. 555 */ 556 char * 557 nextc(clistp, cp, dst) 558 struct clist *clistp; 559 char *cp; 560 int *dst; 561 { 562 struct cblock *cblockp; 563 564 ++cp; 565 /* 566 * See if the next character is beyond the end of 567 * the clist. 568 */ 569 if (clistp->c_cc && (cp != clistp->c_cl)) { 570 /* 571 * If the next character is beyond the end of this 572 * cblock, advance to the next cblock. 573 */ 574 if (((intptr_t)cp & CROUND) == 0) 575 cp = ((struct cblock *)cp - 1)->c_next->c_info; 576 cblockp = (struct cblock *)((intptr_t)cp & ~CROUND); 577 578 /* 579 * Get the character. Set the quote flag if this character 580 * is quoted. 581 */ 582 *dst = (u_char)*cp | (isset(cblockp->c_quote, cp - (char *)cblockp->c_info) ? TTY_QUOTE : 0); 583 584 return (cp); 585 } 586 587 return (NULL); 588 } 589 590 /* 591 * "Unput" a character from a clist. 592 */ 593 int 594 unputc(clistp) 595 struct clist *clistp; 596 { 597 struct cblock *cblockp = 0, *cbp = 0; 598 int s; 599 int chr = -1; 600 601 602 s = spltty(); 603 604 if (clistp->c_cc) { 605 --clistp->c_cc; 606 --clistp->c_cl; 607 608 chr = (u_char)*clistp->c_cl; 609 610 cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND); 611 612 /* 613 * Set quote flag if this character was quoted. 614 */ 615 if (isset(cblockp->c_quote, (u_char *)clistp->c_cl - cblockp->c_info)) 616 chr |= TTY_QUOTE; 617 618 /* 619 * If all of the characters have been unput in this 620 * cblock, then find the previous one and free this 621 * one. 622 */ 623 if (clistp->c_cc && (clistp->c_cl <= (char *)cblockp->c_info)) { 624 cbp = (struct cblock *)((intptr_t)clistp->c_cf & ~CROUND); 625 626 while (cbp->c_next != cblockp) 627 cbp = cbp->c_next; 628 629 /* 630 * When the previous cblock is at the end, the 'last' 631 * pointer always points (invalidly) one past. 632 */ 633 clistp->c_cl = (char *)(cbp+1); 634 cblock_free(cblockp); 635 if (--clistp->c_cbcount >= clistp->c_cbreserved) 636 ++cslushcount; 637 cbp->c_next = NULL; 638 } 639 } 640 641 /* 642 * If there are no more characters on the list, then 643 * free the last cblock. 644 */ 645 if ((clistp->c_cc == 0) && clistp->c_cl) { 646 cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND); 647 cblock_free(cblockp); 648 if (--clistp->c_cbcount >= clistp->c_cbreserved) 649 ++cslushcount; 650 clistp->c_cf = clistp->c_cl = NULL; 651 } 652 653 splx(s); 654 return (chr); 655 } 656 657 /* 658 * Move characters in source clist to destination clist, 659 * preserving quote bits. 660 */ 661 void 662 catq(src_clistp, dest_clistp) 663 struct clist *src_clistp, *dest_clistp; 664 { 665 int chr, s; 666 667 s = spltty(); 668 /* 669 * If the destination clist is empty (has no cblocks atttached), 670 * and there are no possible complications with the resource counters, 671 * then we simply assign the current clist to the destination. 672 */ 673 if (!dest_clistp->c_cf 674 && src_clistp->c_cbcount <= src_clistp->c_cbmax 675 && src_clistp->c_cbcount <= dest_clistp->c_cbmax) { 676 dest_clistp->c_cf = src_clistp->c_cf; 677 dest_clistp->c_cl = src_clistp->c_cl; 678 src_clistp->c_cf = src_clistp->c_cl = NULL; 679 680 dest_clistp->c_cc = src_clistp->c_cc; 681 src_clistp->c_cc = 0; 682 dest_clistp->c_cbcount = src_clistp->c_cbcount; 683 src_clistp->c_cbcount = 0; 684 685 splx(s); 686 return; 687 } 688 689 splx(s); 690 691 /* 692 * XXX This should probably be optimized to more than one 693 * character at a time. 694 */ 695 while ((chr = getc(src_clistp)) != -1) 696 putc(chr, dest_clistp); 697 } 698