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