HAMMER 61E/Many: Stabilization, Performance
[dragonfly.git] / sys / kern / tty_subr.c
blob4558d442abfe40fa2255ff2c59dd2e4fdb07e02c
1 /*
2 * Copyright (c) 1994, David Greenman
3 * All rights reserved.
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.
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.
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.10 2006/12/23 00:35:04 swildner Exp $
32 * clist support routines
34 * NOTE on cblock->c_cf: This pointer may point at the base of a cblock,
35 * which is &cblock->c_info[0], but will never
36 * point at the end of a cblock (char *)(cblk + 1)
38 * NOTE on cblock->c_cl: This pointer will never point at the base of
39 * a block but may point at the end of one.
41 * These routines may be used by more then just ttys, so a critical section
42 * must be used to access the free list, and for general safety.
45 #include <sys/param.h>
46 #include <sys/kernel.h>
47 #include <sys/systm.h>
48 #include <sys/malloc.h>
49 #include <sys/tty.h>
50 #include <sys/clist.h>
51 #include <sys/thread2.h>
53 static void clist_init (void *);
54 SYSINIT(clist, SI_SUB_CLIST, SI_ORDER_FIRST, clist_init, NULL)
56 static struct cblock *cfreelist = 0;
57 int cfreecount = 0;
58 static int cslushcount;
59 static int ctotcount;
61 #ifndef INITIAL_CBLOCKS
62 #define INITIAL_CBLOCKS 50
63 #endif
65 static struct cblock *cblock_alloc (void);
66 static void cblock_alloc_cblocks (int number);
67 static void cblock_free (struct cblock *cblockp);
68 static void cblock_free_cblocks (int number);
70 #include "opt_ddb.h"
71 #ifdef DDB
72 #include <ddb/ddb.h>
74 DB_SHOW_COMMAND(cbstat, cbstat)
76 int cbsize = CBSIZE;
78 kprintf(
79 "tot = %d (active = %d, free = %d (reserved = %d, slush = %d))\n",
80 ctotcount * cbsize, ctotcount * cbsize - cfreecount, cfreecount,
81 cfreecount - cslushcount * cbsize, cslushcount * cbsize);
83 #endif /* DDB */
86 * Called from init_main.c
88 /* ARGSUSED*/
89 static void
90 clist_init(void *dummy)
93 * Allocate an initial base set of cblocks as a 'slush'.
94 * We allocate non-slush cblocks with each initial ttyopen() and
95 * deallocate them with each ttyclose().
96 * We should adjust the slush allocation. This can't be done in
97 * the i/o routines because they are sometimes called from
98 * interrupt handlers when it may be unsafe to call kmalloc().
100 cblock_alloc_cblocks(cslushcount = INITIAL_CBLOCKS);
101 KKASSERT(sizeof(struct cblock) == CBLOCK);
105 * Remove a cblock from the cfreelist queue and return a pointer
106 * to it.
108 * May not block.
110 static struct cblock *
111 cblock_alloc(void)
113 struct cblock *cblockp;
115 cblockp = cfreelist;
116 if (cblockp == NULL)
117 panic("clist reservation botch");
118 KKASSERT(cblockp->c_head.ch_magic == CLIST_MAGIC_FREE);
119 cfreelist = cblockp->c_head.ch_next;
120 cblockp->c_head.ch_next = NULL;
121 cblockp->c_head.ch_magic = CLIST_MAGIC_USED;
122 cfreecount -= CBSIZE;
123 return (cblockp);
127 * Add a cblock to the cfreelist queue.
129 * May not block, must be called in a critical section
131 static void
132 cblock_free(struct cblock *cblockp)
134 if (isset(cblockp->c_quote, CBQSIZE * NBBY - 1))
135 bzero(cblockp->c_quote, sizeof cblockp->c_quote);
136 KKASSERT(cblockp->c_head.ch_magic == CLIST_MAGIC_USED);
137 cblockp->c_head.ch_next = cfreelist;
138 cblockp->c_head.ch_magic = CLIST_MAGIC_FREE;
139 cfreelist = cblockp;
140 cfreecount += CBSIZE;
144 * Allocate some cblocks for the cfreelist queue.
146 * This routine may block, but still must be called in a critical section
148 static void
149 cblock_alloc_cblocks(int number)
151 int i;
152 struct cblock *cbp;
154 for (i = 0; i < number; ++i) {
155 cbp = kmalloc(sizeof *cbp, M_TTYS, M_NOWAIT);
156 if (cbp == NULL) {
157 kprintf(
158 "clist_alloc_cblocks: M_NOWAIT kmalloc failed, trying M_WAITOK\n");
159 cbp = kmalloc(sizeof *cbp, M_TTYS, M_WAITOK);
161 KKASSERT(((intptr_t)cbp & CROUND) == 0);
163 * Freed cblocks have zero quotes and garbage elsewhere.
164 * Set the may-have-quote bit to force zeroing the quotes.
166 setbit(cbp->c_quote, CBQSIZE * NBBY - 1);
167 cbp->c_head.ch_magic = CLIST_MAGIC_USED;
168 cblock_free(cbp);
170 ctotcount += number;
174 * Set the cblock allocation policy for a clist.
176 void
177 clist_alloc_cblocks(struct clist *clistp, int ccmax, int ccreserved)
179 int dcbr;
182 * Allow for wasted space at the head.
184 if (ccmax != 0)
185 ccmax += CBSIZE - 1;
186 if (ccreserved != 0)
187 ccreserved += CBSIZE - 1;
189 crit_enter();
190 clistp->c_cbmax = roundup(ccmax, CBSIZE) / CBSIZE;
191 dcbr = roundup(ccreserved, CBSIZE) / CBSIZE - clistp->c_cbreserved;
192 if (dcbr >= 0) {
193 clistp->c_cbreserved += dcbr; /* atomic w/c_cbmax */
194 cblock_alloc_cblocks(dcbr); /* may block */
195 } else {
196 KKASSERT(clistp->c_cbcount <= clistp->c_cbreserved);
197 if (clistp->c_cbreserved + dcbr < clistp->c_cbcount)
198 dcbr = clistp->c_cbcount - clistp->c_cbreserved;
199 clistp->c_cbreserved += dcbr; /* atomic w/c_cbmax */
200 cblock_free_cblocks(-dcbr); /* may block */
202 KKASSERT(clistp->c_cbreserved >= 0);
203 crit_exit();
207 * Free some cblocks from the cfreelist queue back to the
208 * system malloc pool.
210 * Must be called from within a critical section. May block.
212 static void
213 cblock_free_cblocks(int number)
215 int i;
217 for (i = 0; i < number; ++i)
218 kfree(cblock_alloc(), M_TTYS);
219 ctotcount -= number;
223 * Free the cblocks reserved for a clist.
225 void
226 clist_free_cblocks(struct clist *clistp)
228 int cbreserved;
230 crit_enter();
231 if (clistp->c_cbcount != 0)
232 panic("freeing active clist cblocks");
233 cbreserved = clistp->c_cbreserved;
234 clistp->c_cbmax = 0;
235 clistp->c_cbreserved = 0;
236 cblock_free_cblocks(cbreserved); /* may block */
237 crit_exit();
241 * Get a character from the head of a clist.
244 clist_getc(struct clist *clistp)
246 int chr = -1;
247 struct cblock *cblockp;
249 crit_enter();
250 if (clistp->c_cc) {
251 KKASSERT(((intptr_t)clistp->c_cf & CROUND) != 0);
252 cblockp = (struct cblock *)((intptr_t)clistp->c_cf & ~CROUND);
253 chr = (u_char)*clistp->c_cf;
256 * If this char is quoted, set the flag.
258 if (isset(cblockp->c_quote, clistp->c_cf - (char *)cblockp->c_info))
259 chr |= TTY_QUOTE;
262 * Advance to next character.
264 clistp->c_cf++;
265 clistp->c_cc--;
267 * If we have advanced the 'first' character pointer
268 * past the end of this cblock, advance to the next one.
269 * If there are no more characters, set the first and
270 * last pointers to NULL. In either case, free the
271 * current cblock.
273 KKASSERT(clistp->c_cf <= (char *)(cblockp + 1));
274 if ((clistp->c_cf == (char *)(cblockp + 1)) ||
275 (clistp->c_cc == 0)) {
276 if (clistp->c_cc > 0) {
277 clistp->c_cf = cblockp->c_head.ch_next->c_info;
278 } else {
279 clistp->c_cf = clistp->c_cl = NULL;
281 cblock_free(cblockp);
282 if (--clistp->c_cbcount >= clistp->c_cbreserved)
283 ++cslushcount;
286 crit_exit();
287 return (chr);
291 * Copy 'amount' of chars, beginning at head of clist 'clistp' to
292 * destination linear buffer 'dest'. Return number of characters
293 * actually copied.
296 q_to_b(struct clist *clistp, char *dest, int amount)
298 struct cblock *cblockp;
299 struct cblock *cblockn;
300 char *dest_orig = dest;
301 int numc;
303 crit_enter();
304 while (clistp && amount && (clistp->c_cc > 0)) {
305 KKASSERT(((intptr_t)clistp->c_cf & CROUND) != 0);
306 cblockp = (struct cblock *)((intptr_t)clistp->c_cf & ~CROUND);
307 cblockn = cblockp + 1; /* pointer arithmetic! */
308 numc = min(amount, (char *)cblockn - clistp->c_cf);
309 numc = min(numc, clistp->c_cc);
310 bcopy(clistp->c_cf, dest, numc);
311 amount -= numc;
312 clistp->c_cf += numc;
313 clistp->c_cc -= numc;
314 dest += numc;
316 * If this cblock has been emptied, advance to the next
317 * one. If there are no more characters, set the first
318 * and last pointer to NULL. In either case, free the
319 * current cblock.
321 KKASSERT(clistp->c_cf <= (char *)cblockn);
322 if ((clistp->c_cf == (char *)cblockn) || (clistp->c_cc == 0)) {
323 if (clistp->c_cc > 0) {
324 KKASSERT(cblockp->c_head.ch_next != NULL);
325 clistp->c_cf = cblockp->c_head.ch_next->c_info;
326 } else {
327 clistp->c_cf = clistp->c_cl = NULL;
329 cblock_free(cblockp);
330 if (--clistp->c_cbcount >= clistp->c_cbreserved)
331 ++cslushcount;
334 crit_exit();
335 return (dest - dest_orig);
339 * Flush 'amount' of chars, beginning at head of clist 'clistp'.
341 void
342 ndflush(struct clist *clistp, int amount)
344 struct cblock *cblockp;
345 struct cblock *cblockn;
346 int numc;
348 crit_enter();
349 while (amount && (clistp->c_cc > 0)) {
350 KKASSERT(((intptr_t)clistp->c_cf & CROUND) != 0);
351 cblockp = (struct cblock *)((intptr_t)clistp->c_cf & ~CROUND);
352 cblockn = cblockp + 1; /* pointer arithmetic! */
353 numc = min(amount, (char *)cblockn - clistp->c_cf);
354 numc = min(numc, clistp->c_cc);
355 amount -= numc;
356 clistp->c_cf += numc;
357 clistp->c_cc -= numc;
359 * If this cblock has been emptied, advance to the next
360 * one. If there are no more characters, set the first
361 * and last pointer to NULL. In either case, free the
362 * current cblock.
364 KKASSERT(clistp->c_cf <= (char *)cblockn);
365 if (clistp->c_cf == (char *)cblockn || clistp->c_cc == 0) {
366 if (clistp->c_cc > 0) {
367 KKASSERT(cblockp->c_head.ch_next != NULL);
368 clistp->c_cf = cblockp->c_head.ch_next->c_info;
369 } else {
370 clistp->c_cf = clistp->c_cl = NULL;
372 cblock_free(cblockp);
373 if (--clistp->c_cbcount >= clistp->c_cbreserved)
374 ++cslushcount;
377 crit_exit();
381 * Add a character to the end of a clist. Return -1 is no
382 * more clists, or 0 for success.
385 clist_putc(int chr, struct clist *clistp)
387 struct cblock *cblockp;
389 crit_enter();
392 * Note: this section may point c_cl at the base of a cblock. This
393 * is a temporary violation of the requirements for c_cl, we
394 * increment it before returning.
396 if (clistp->c_cl == NULL) {
397 if (clistp->c_cbreserved < 1) {
398 crit_exit();
399 kprintf("putc to a clist with no reserved cblocks\n");
400 return (-1); /* nothing done */
402 cblockp = cblock_alloc();
403 clistp->c_cbcount = 1;
404 clistp->c_cf = clistp->c_cl = cblockp->c_info;
405 clistp->c_cc = 0;
406 } else {
407 cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND);
408 if (((intptr_t)clistp->c_cl & CROUND) == 0) {
409 struct cblock *prev = (cblockp - 1);
411 if (clistp->c_cbcount >= clistp->c_cbreserved) {
412 if (clistp->c_cbcount >= clistp->c_cbmax
413 || cslushcount <= 0) {
414 crit_exit();
415 return (-1);
417 --cslushcount;
419 cblockp = cblock_alloc();
420 clistp->c_cbcount++;
421 prev->c_head.ch_next = cblockp;
422 clistp->c_cl = cblockp->c_info;
427 * If this character is quoted, set the quote bit, if not, clear it.
429 if (chr & TTY_QUOTE) {
430 setbit(cblockp->c_quote, clistp->c_cl - (char *)cblockp->c_info);
432 * Use one of the spare quote bits to record that something
433 * may be quoted.
435 setbit(cblockp->c_quote, CBQSIZE * NBBY - 1);
436 } else {
437 clrbit(cblockp->c_quote, clistp->c_cl - (char *)cblockp->c_info);
440 *clistp->c_cl++ = chr;
441 clistp->c_cc++;
443 crit_exit();
444 return (0);
448 * Copy data from linear buffer to clist chain. Return the
449 * number of characters not copied.
452 b_to_q(char *src, int amount, struct clist *clistp)
454 struct cblock *cblockp;
455 char *firstbyte, *lastbyte;
456 u_char startmask, endmask;
457 int startbit, endbit, num_between, numc;
460 * Avoid allocating an initial cblock and then not using it.
461 * c_cc == 0 must imply c_cbount == 0.
463 if (amount <= 0)
464 return (amount);
466 crit_enter();
469 * Note: this section may point c_cl at the base of a cblock. This
470 * is a temporary violation of the requirements for c_cl. Since
471 * amount is non-zero we will not return with it in that state.
473 if (clistp->c_cl == NULL) {
474 if (clistp->c_cbreserved < 1) {
475 crit_exit();
476 kprintf("b_to_q to a clist with no reserved cblocks.\n");
477 return (amount); /* nothing done */
479 cblockp = cblock_alloc();
480 clistp->c_cbcount = 1;
481 clistp->c_cf = clistp->c_cl = cblockp->c_info;
482 clistp->c_cc = 0;
483 } else {
485 * c_cl may legally point past the end of the block, which
486 * falls through to the 'get another cblock' code below.
488 cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND);
491 while (amount) {
493 * Get another cblock if needed.
495 if (((intptr_t)clistp->c_cl & CROUND) == 0) {
496 struct cblock *prev = cblockp - 1;
498 if (clistp->c_cbcount >= clistp->c_cbreserved) {
499 if (clistp->c_cbcount >= clistp->c_cbmax
500 || cslushcount <= 0) {
501 crit_exit();
502 return (amount);
504 --cslushcount;
506 cblockp = cblock_alloc();
507 clistp->c_cbcount++;
508 prev->c_head.ch_next = cblockp;
509 clistp->c_cl = cblockp->c_info;
513 * Copy a chunk of the linear buffer up to the end
514 * of this cblock.
516 numc = min(amount, (char *)(cblockp + 1) - clistp->c_cl);
517 bcopy(src, clistp->c_cl, numc);
520 * Clear quote bits if they aren't known to be clear.
521 * The following could probably be made into a separate
522 * "bitzero()" routine, but why bother?
524 if (isset(cblockp->c_quote, CBQSIZE * NBBY - 1)) {
525 startbit = clistp->c_cl - (char *)cblockp->c_info;
526 endbit = startbit + numc - 1;
528 firstbyte = (u_char *)cblockp->c_quote + (startbit / NBBY);
529 lastbyte = (u_char *)cblockp->c_quote + (endbit / NBBY);
532 * Calculate mask of bits to preserve in first and
533 * last bytes.
535 startmask = NBBY - (startbit % NBBY);
536 startmask = 0xff >> startmask;
537 endmask = (endbit % NBBY);
538 endmask = 0xff << (endmask + 1);
540 if (firstbyte != lastbyte) {
541 *firstbyte &= startmask;
542 *lastbyte &= endmask;
544 num_between = lastbyte - firstbyte - 1;
545 if (num_between)
546 bzero(firstbyte + 1, num_between);
547 } else {
548 *firstbyte &= (startmask | endmask);
553 * ...and update pointer for the next chunk.
555 src += numc;
556 clistp->c_cl += numc;
557 clistp->c_cc += numc;
558 amount -= numc;
560 * If we go through the loop again, it's always
561 * for data in the next cblock, so by adding one (cblock),
562 * (which makes the pointer 1 beyond the end of this
563 * cblock) we prepare for the assignment of 'prev'
564 * above.
566 ++cblockp;
568 crit_exit();
569 return (amount);
573 * Get the next character in the clist. Store it at dst. Don't
574 * advance any clist pointers, but return a pointer to the next
575 * character position.
577 * Must be called at spltty(). This routine may not run in a critical
578 * section and so may not call the cblock allocator/deallocator.
580 char *
581 nextc(struct clist *clistp, char *cp, int *dst)
583 struct cblock *cblockp;
585 ++cp;
587 * See if the next character is beyond the end of
588 * the clist.
590 if (clistp->c_cc && (cp != clistp->c_cl)) {
592 * If the next character is beyond the end of this
593 * cblock, advance to the next cblock.
595 if (((intptr_t)cp & CROUND) == 0)
596 cp = ((struct cblock *)cp - 1)->c_head.ch_next->c_info;
597 cblockp = (struct cblock *)((intptr_t)cp & ~CROUND);
600 * Get the character. Set the quote flag if this character
601 * is quoted.
603 *dst = (u_char)*cp | (isset(cblockp->c_quote, cp - (char *)cblockp->c_info) ? TTY_QUOTE : 0);
605 return (cp);
608 return (NULL);
612 * "Unput" a character from a clist.
615 clist_unputc(struct clist *clistp)
617 struct cblock *cblockp = 0, *cbp = 0;
618 int chr = -1;
620 crit_enter();
622 if (clistp->c_cc) {
624 * note that clistp->c_cl will never point at the base
625 * of a cblock (cblock->c_info) (see assert this later on),
626 * but it may point past the end of one. We temporarily
627 * violate this in the decrement below but then we fix it up.
629 --clistp->c_cc;
630 --clistp->c_cl;
632 chr = (u_char)*clistp->c_cl;
634 cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND);
637 * Set quote flag if this character was quoted.
639 if (isset(cblockp->c_quote, (u_char *)clistp->c_cl - cblockp->c_info))
640 chr |= TTY_QUOTE;
643 * If all of the characters have been unput in this
644 * cblock, then find the previous one and free this
645 * one.
647 * if c_cc is 0 clistp->c_cl may end up pointing at
648 * cblockp->c_info, which is illegal, but the case will be
649 * taken care of near the end of the routine. Otherwise
650 * there *MUST* be another cblock, find it.
652 KKASSERT(clistp->c_cl >= (char *)cblockp->c_info);
653 if (clistp->c_cc && (clistp->c_cl == (char *)cblockp->c_info)) {
654 cbp = (struct cblock *)((intptr_t)clistp->c_cf & ~CROUND);
656 while (cbp->c_head.ch_next != cblockp)
657 cbp = cbp->c_head.ch_next;
658 cbp->c_head.ch_next = NULL;
661 * When the previous cblock is at the end, the 'last'
662 * pointer always points (invalidly) one past.
664 clistp->c_cl = (char *)(cbp + 1);
665 cblock_free(cblockp);
666 if (--clistp->c_cbcount >= clistp->c_cbreserved)
667 ++cslushcount;
672 * If there are no more characters on the list, then
673 * free the last cblock. It should not be possible for c->cl
674 * to be pointing past the end of a block due to our decrement
675 * of it way above.
677 if (clistp->c_cc == 0 && clistp->c_cl) {
678 KKASSERT(((intptr_t)clistp->c_cl & CROUND) != 0);
679 cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND);
680 cblock_free(cblockp);
681 if (--clistp->c_cbcount >= clistp->c_cbreserved)
682 ++cslushcount;
683 clistp->c_cf = clistp->c_cl = NULL;
686 crit_exit();
687 return (chr);
691 * Move characters in source clist to destination clist,
692 * preserving quote bits.
694 void
695 catq(struct clist *src_clistp, struct clist *dest_clistp)
697 int chr;
699 crit_enter();
701 * If the destination clist is empty (has no cblocks atttached),
702 * and there are no possible complications with the resource counters,
703 * then we simply assign the current clist to the destination.
705 if (!dest_clistp->c_cf
706 && src_clistp->c_cbcount <= src_clistp->c_cbmax
707 && src_clistp->c_cbcount <= dest_clistp->c_cbmax) {
708 dest_clistp->c_cf = src_clistp->c_cf;
709 dest_clistp->c_cl = src_clistp->c_cl;
710 src_clistp->c_cf = src_clistp->c_cl = NULL;
712 dest_clistp->c_cc = src_clistp->c_cc;
713 src_clistp->c_cc = 0;
714 dest_clistp->c_cbcount = src_clistp->c_cbcount;
715 src_clistp->c_cbcount = 0;
717 crit_exit();
718 return;
720 crit_exit();
723 * XXX This should probably be optimized to more than one
724 * character at a time.
726 while ((chr = clist_getc(src_clistp)) != -1)
727 clist_putc(chr, dest_clistp);