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