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 return (-1); /* nothing done */ 438 } 439 cblockp = cblock_alloc(); 440 clistp->c_cbcount = 1; 441 clistp->c_cf = clistp->c_cl = cblockp->c_info; 442 clistp->c_cc = 0; 443 } else { 444 cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND); 445 if (((intptr_t)clistp->c_cl & CROUND) == 0) { 446 struct cblock *prev = (cblockp - 1); 447 448 if (clistp->c_cbcount >= clistp->c_cbreserved) { 449 if (clistp->c_cbcount >= clistp->c_cbmax 450 || cslushcount <= 0) { 451 lwkt_reltoken(&tty_token); 452 crit_exit(); 453 return (-1); 454 } 455 --cslushcount; 456 } 457 cblockp = cblock_alloc(); 458 clistp->c_cbcount++; 459 prev->c_head.ch_next = cblockp; 460 clistp->c_cl = cblockp->c_info; 461 } 462 } 463 464 /* 465 * If this character is quoted, set the quote bit, if not, clear it. 466 */ 467 if (chr & TTY_QUOTE) { 468 setbit(cblockp->c_quote, clistp->c_cl - (char *)cblockp->c_info); 469 /* 470 * Use one of the spare quote bits to record that something 471 * may be quoted. 472 */ 473 setbit(cblockp->c_quote, CBQSIZE * NBBY - 1); 474 } else { 475 clrbit(cblockp->c_quote, clistp->c_cl - (char *)cblockp->c_info); 476 } 477 478 *clistp->c_cl++ = chr; 479 clistp->c_cc++; 480 481 lwkt_reltoken(&tty_token); 482 crit_exit(); 483 return (0); 484 } 485 486 /* 487 * Copy data from linear buffer to clist chain. Return the 488 * number of characters not copied. 489 */ 490 int 491 b_to_q(char *src, int amount, struct clist *clistp) 492 { 493 struct cblock *cblockp; 494 char *firstbyte, *lastbyte; 495 u_char startmask, endmask; 496 int startbit, endbit, num_between, numc; 497 498 /* 499 * Avoid allocating an initial cblock and then not using it. 500 * c_cc == 0 must imply c_cbount == 0. 501 */ 502 if (amount <= 0) 503 return (amount); 504 505 crit_enter(); 506 lwkt_gettoken(&tty_token); 507 508 /* 509 * Note: this section may point c_cl at the base of a cblock. This 510 * is a temporary violation of the requirements for c_cl. Since 511 * amount is non-zero we will not return with it in that state. 512 */ 513 if (clistp->c_cl == NULL) { 514 if (clistp->c_cbreserved < 1) { 515 lwkt_reltoken(&tty_token); 516 crit_exit(); 517 kprintf("b_to_q to a clist with no reserved cblocks.\n"); 518 return (amount); /* nothing done */ 519 } 520 cblockp = cblock_alloc(); 521 clistp->c_cbcount = 1; 522 clistp->c_cf = clistp->c_cl = cblockp->c_info; 523 clistp->c_cc = 0; 524 } else { 525 /* 526 * c_cl may legally point past the end of the block, which 527 * falls through to the 'get another cblock' code below. 528 */ 529 cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND); 530 } 531 532 while (amount) { 533 /* 534 * Get another cblock if needed. 535 */ 536 if (((intptr_t)clistp->c_cl & CROUND) == 0) { 537 struct cblock *prev = cblockp - 1; 538 539 if (clistp->c_cbcount >= clistp->c_cbreserved) { 540 if (clistp->c_cbcount >= clistp->c_cbmax 541 || cslushcount <= 0) { 542 lwkt_reltoken(&tty_token); 543 crit_exit(); 544 return (amount); 545 } 546 --cslushcount; 547 } 548 cblockp = cblock_alloc(); 549 clistp->c_cbcount++; 550 prev->c_head.ch_next = cblockp; 551 clistp->c_cl = cblockp->c_info; 552 } 553 554 /* 555 * Copy a chunk of the linear buffer up to the end 556 * of this cblock. 557 */ 558 numc = min(amount, (char *)(cblockp + 1) - clistp->c_cl); 559 bcopy(src, clistp->c_cl, numc); 560 561 /* 562 * Clear quote bits if they aren't known to be clear. 563 * The following could probably be made into a separate 564 * "bitzero()" routine, but why bother? 565 */ 566 if (isset(cblockp->c_quote, CBQSIZE * NBBY - 1)) { 567 startbit = clistp->c_cl - (char *)cblockp->c_info; 568 endbit = startbit + numc - 1; 569 570 firstbyte = (u_char *)cblockp->c_quote + (startbit / NBBY); 571 lastbyte = (u_char *)cblockp->c_quote + (endbit / NBBY); 572 573 /* 574 * Calculate mask of bits to preserve in first and 575 * last bytes. 576 */ 577 startmask = NBBY - (startbit % NBBY); 578 startmask = 0xff >> startmask; 579 endmask = (endbit % NBBY); 580 endmask = 0xff << (endmask + 1); 581 582 if (firstbyte != lastbyte) { 583 *firstbyte &= startmask; 584 *lastbyte &= endmask; 585 586 num_between = lastbyte - firstbyte - 1; 587 if (num_between) 588 bzero(firstbyte + 1, num_between); 589 } else { 590 *firstbyte &= (startmask | endmask); 591 } 592 } 593 594 /* 595 * ...and update pointer for the next chunk. 596 */ 597 src += numc; 598 clistp->c_cl += numc; 599 clistp->c_cc += numc; 600 amount -= numc; 601 /* 602 * If we go through the loop again, it's always 603 * for data in the next cblock, so by adding one (cblock), 604 * (which makes the pointer 1 beyond the end of this 605 * cblock) we prepare for the assignment of 'prev' 606 * above. 607 */ 608 ++cblockp; 609 } 610 lwkt_reltoken(&tty_token); 611 crit_exit(); 612 return (amount); 613 } 614 615 /* 616 * Get the next character in the clist. Store it at dst. Don't 617 * advance any clist pointers, but return a pointer to the next 618 * character position. 619 * 620 * Must be called at spltty(). This routine may not run in a critical 621 * section and so may not call the cblock allocator/deallocator. 622 */ 623 char * 624 nextc(struct clist *clistp, char *cp, int *dst) 625 { 626 struct cblock *cblockp; 627 628 ++cp; 629 /* 630 * See if the next character is beyond the end of 631 * the clist. 632 */ 633 lwkt_gettoken(&tty_token); 634 if (clistp->c_cc && (cp != clistp->c_cl)) { 635 /* 636 * If the next character is beyond the end of this 637 * cblock, advance to the next cblock. 638 */ 639 if (((intptr_t)cp & CROUND) == 0) 640 cp = ((struct cblock *)cp - 1)->c_head.ch_next->c_info; 641 cblockp = (struct cblock *)((intptr_t)cp & ~CROUND); 642 643 /* 644 * Get the character. Set the quote flag if this character 645 * is quoted. 646 */ 647 *dst = (u_char)*cp | (isset(cblockp->c_quote, cp - (char *)cblockp->c_info) ? TTY_QUOTE : 0); 648 649 lwkt_reltoken(&tty_token); 650 return (cp); 651 } 652 653 lwkt_reltoken(&tty_token); 654 return (NULL); 655 } 656 657 /* 658 * "Unput" a character from a clist. 659 */ 660 int 661 clist_unputc(struct clist *clistp) 662 { 663 struct cblock *cblockp = 0, *cbp = 0; 664 int chr = -1; 665 666 crit_enter(); 667 lwkt_gettoken(&tty_token); 668 669 if (clistp->c_cc) { 670 /* 671 * note that clistp->c_cl will never point at the base 672 * of a cblock (cblock->c_info) (see assert this later on), 673 * but it may point past the end of one. We temporarily 674 * violate this in the decrement below but then we fix it up. 675 */ 676 --clistp->c_cc; 677 --clistp->c_cl; 678 679 chr = (u_char)*clistp->c_cl; 680 681 cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND); 682 683 /* 684 * Set quote flag if this character was quoted. 685 */ 686 if (isset(cblockp->c_quote, (u_char *)clistp->c_cl - cblockp->c_info)) 687 chr |= TTY_QUOTE; 688 689 /* 690 * If all of the characters have been unput in this 691 * cblock, then find the previous one and free this 692 * one. 693 * 694 * if c_cc is 0 clistp->c_cl may end up pointing at 695 * cblockp->c_info, which is illegal, but the case will be 696 * taken care of near the end of the routine. Otherwise 697 * there *MUST* be another cblock, find it. 698 */ 699 KKASSERT(clistp->c_cl >= (char *)cblockp->c_info); 700 if (clistp->c_cc && (clistp->c_cl == (char *)cblockp->c_info)) { 701 cbp = (struct cblock *)((intptr_t)clistp->c_cf & ~CROUND); 702 703 while (cbp->c_head.ch_next != cblockp) 704 cbp = cbp->c_head.ch_next; 705 cbp->c_head.ch_next = NULL; 706 707 /* 708 * When the previous cblock is at the end, the 'last' 709 * pointer always points (invalidly) one past. 710 */ 711 clistp->c_cl = (char *)(cbp + 1); 712 cblock_free(cblockp); 713 if (--clistp->c_cbcount >= clistp->c_cbreserved) 714 ++cslushcount; 715 } 716 } 717 718 /* 719 * If there are no more characters on the list, then 720 * free the last cblock. It should not be possible for c->cl 721 * to be pointing past the end of a block due to our decrement 722 * of it way above. 723 */ 724 if (clistp->c_cc == 0 && clistp->c_cl) { 725 KKASSERT(((intptr_t)clistp->c_cl & CROUND) != 0); 726 cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND); 727 cblock_free(cblockp); 728 if (--clistp->c_cbcount >= clistp->c_cbreserved) 729 ++cslushcount; 730 clistp->c_cf = clistp->c_cl = NULL; 731 } 732 733 lwkt_reltoken(&tty_token); 734 crit_exit(); 735 return (chr); 736 } 737 738 /* 739 * Move characters in source clist to destination clist, 740 * preserving quote bits. 741 */ 742 void 743 catq(struct clist *src_clistp, struct clist *dest_clistp) 744 { 745 int chr; 746 747 lwkt_gettoken(&tty_token); 748 crit_enter(); 749 /* 750 * If the destination clist is empty (has no cblocks atttached), 751 * and there are no possible complications with the resource counters, 752 * then we simply assign the current clist to the destination. 753 */ 754 if (!dest_clistp->c_cf 755 && src_clistp->c_cbcount <= src_clistp->c_cbmax 756 && src_clistp->c_cbcount <= dest_clistp->c_cbmax) { 757 dest_clistp->c_cf = src_clistp->c_cf; 758 dest_clistp->c_cl = src_clistp->c_cl; 759 src_clistp->c_cf = src_clistp->c_cl = NULL; 760 761 dest_clistp->c_cc = src_clistp->c_cc; 762 src_clistp->c_cc = 0; 763 dest_clistp->c_cbcount = src_clistp->c_cbcount; 764 src_clistp->c_cbcount = 0; 765 766 crit_exit(); 767 lwkt_reltoken(&tty_token); 768 return; 769 } 770 crit_exit(); 771 772 /* 773 * XXX This should probably be optimized to more than one 774 * character at a time. 775 */ 776 while ((chr = clist_getc(src_clistp)) != -1) 777 clist_putc(chr, dest_clistp); 778 lwkt_reltoken(&tty_token); 779 } 780