1 /* $NetBSD: interrupt.c,v 1.100 2021/11/10 16:53:28 msaitoh Exp $ */
2 
3 /*-
4  * Copyright (c) 2000, 2001 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University.
34  * All rights reserved.
35  *
36  * Authors: Keith Bostic, Chris G. Demetriou
37  *
38  * Permission to use, copy, modify and distribute this software and
39  * its documentation is hereby granted, provided that both the copyright
40  * notice and this permission notice appear in all copies of the
41  * software, derivative works or modified versions, and any portions
42  * thereof, and that both notices appear in supporting documentation.
43  *
44  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
45  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
46  * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
47  *
48  * Carnegie Mellon requests users of this software to return to
49  *
50  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
51  *  School of Computer Science
52  *  Carnegie Mellon University
53  *  Pittsburgh PA 15213-3890
54  *
55  * any improvements or extensions that they make and grant Carnegie the
56  * rights to redistribute these changes.
57  */
58 /*
59  * Additional Copyright (c) 1997 by Matthew Jacob for NASA/Ames Research Center.
60  * Redistribute and modify at will, leaving only this additional copyright
61  * notice.
62  */
63 
64 #include "opt_multiprocessor.h"
65 
66 #include <sys/cdefs.h>                            /* RCS ID & Copyright macro defns */
67 
68 __KERNEL_RCSID(0, "$NetBSD: interrupt.c,v 1.100 2021/11/10 16:53:28 msaitoh Exp $");
69 
70 #include <sys/param.h>
71 #include <sys/systm.h>
72 #include <sys/proc.h>
73 #include <sys/vmmeter.h>
74 #include <sys/sched.h>
75 #include <sys/kernel.h>
76 #include <sys/time.h>
77 #include <sys/intr.h>
78 #include <sys/device.h>
79 #include <sys/cpu.h>
80 #include <sys/atomic.h>
81 
82 #include <machine/cpuvar.h>
83 #include <machine/autoconf.h>
84 #include <machine/reg.h>
85 #include <machine/rpb.h>
86 #include <machine/frame.h>
87 #include <machine/cpuconf.h>
88 #include <machine/alpha.h>
89 
90 /* Protected by cpu_lock */
91 struct scbvec scb_iovectab[SCB_VECTOIDX(SCB_SIZE - SCB_IOVECBASE)]
92                                                                       __read_mostly;
93 
94 static void         scb_stray(void *, u_long);
95 
96 void
scb_init(void)97 scb_init(void)
98 {
99           u_long i;
100 
101           for (i = 0; i < SCB_NIOVECS; i++) {
102                     scb_iovectab[i].scb_func = scb_stray;
103                     scb_iovectab[i].scb_arg = NULL;
104           }
105 }
106 
107 static void
scb_stray(void * arg,u_long vec)108 scb_stray(void *arg, u_long vec)
109 {
110 
111           printf("WARNING: stray interrupt, vector 0x%lx\n", vec);
112 }
113 
114 void
scb_set(u_long vec,void (* func)(void *,u_long),void * arg)115 scb_set(u_long vec, void (*func)(void *, u_long), void *arg)
116 {
117           u_long idx;
118 
119           KASSERT(mutex_owned(&cpu_lock));
120 
121           if (vec < SCB_IOVECBASE || vec >= SCB_SIZE ||
122               (vec & (SCB_VECSIZE - 1)) != 0)
123                     panic("scb_set: bad vector 0x%lx", vec);
124 
125           idx = SCB_VECTOIDX(vec - SCB_IOVECBASE);
126 
127           if (scb_iovectab[idx].scb_func != scb_stray)
128                     panic("scb_set: vector 0x%lx already occupied", vec);
129 
130           scb_iovectab[idx].scb_arg = arg;
131           alpha_mb();
132           scb_iovectab[idx].scb_func = func;
133           alpha_mb();
134 }
135 
136 u_long
scb_alloc(void (* func)(void *,u_long),void * arg)137 scb_alloc(void (*func)(void *, u_long), void *arg)
138 {
139           u_long vec, idx;
140 
141           KASSERT(mutex_owned(&cpu_lock));
142 
143           /*
144            * Allocate "downwards", to avoid bumping into
145            * interrupts which are likely to be at the lower
146            * vector numbers.
147            */
148           for (vec = SCB_SIZE - SCB_VECSIZE;
149                vec >= SCB_IOVECBASE; vec -= SCB_VECSIZE) {
150                     idx = SCB_VECTOIDX(vec - SCB_IOVECBASE);
151                     if (scb_iovectab[idx].scb_func == scb_stray) {
152                               scb_iovectab[idx].scb_arg = arg;
153                               alpha_mb();
154                               scb_iovectab[idx].scb_func = func;
155                               alpha_mb();
156                               return (vec);
157                     }
158           }
159 
160           return (SCB_ALLOC_FAILED);
161 }
162 
163 void
scb_free(u_long vec)164 scb_free(u_long vec)
165 {
166           u_long idx;
167 
168           KASSERT(mutex_owned(&cpu_lock));
169 
170           if (vec < SCB_IOVECBASE || vec >= SCB_SIZE ||
171               (vec & (SCB_VECSIZE - 1)) != 0)
172                     panic("scb_free: bad vector 0x%lx", vec);
173 
174           idx = SCB_VECTOIDX(vec - SCB_IOVECBASE);
175 
176           if (scb_iovectab[idx].scb_func == scb_stray)
177                     panic("scb_free: vector 0x%lx is empty", vec);
178 
179           scb_iovectab[idx].scb_func = scb_stray;
180           alpha_mb();
181           scb_iovectab[idx].scb_arg = (void *) vec;
182           alpha_mb();
183 }
184 
185 void
interrupt(unsigned long a0,unsigned long a1,unsigned long a2,struct trapframe * framep)186 interrupt(unsigned long a0, unsigned long a1, unsigned long a2,
187     struct trapframe *framep)
188 {
189           struct cpu_info *ci = curcpu();
190           struct cpu_softc *sc = ci->ci_softc;
191 
192           switch (a0) {
193           case ALPHA_INTR_XPROC:        /* interprocessor interrupt */
194 #if defined(MULTIPROCESSOR)
195                     ci->ci_intrdepth++;
196 
197                     alpha_ipi_process(ci, framep);
198 
199                     /*
200                      * Handle inter-console messages if we're the primary
201                      * CPU.
202                      */
203                     if (ci->ci_cpuid == hwrpb->rpb_primary_cpu_id &&
204                         hwrpb->rpb_txrdy != 0)
205                               cpu_iccb_receive();
206 
207                     ci->ci_intrdepth--;
208 #else
209                     printf("WARNING: received interprocessor interrupt!\n");
210 #endif /* MULTIPROCESSOR */
211                     break;
212 
213           case ALPHA_INTR_CLOCK:        /* clock interrupt */
214                     /*
215                      * Rather than simply increment the interrupt depth
216                      * for the clock interrupt, we add 0x10.  Why?  Because
217                      * while we only call out a single device interrupt
218                      * level, technically the architecture specification
219                      * supports two, meaning we could have intrdepth > 1
220                      * just for device interrupts.
221                      *
222                      * Adding 0x10 here means that cpu_intr_p() can check
223                      * for "intrdepth != 0" for "in interrupt context" and
224                      * CLKF_INTR() can check "(intrdepth & 0xf) != 0" for
225                      * "was processing interrupts when the clock interrupt
226                      * happened".
227                      */
228                     ci->ci_intrdepth += 0x10;
229                     sc->sc_evcnt_clock.ev_count++;
230                     ci->ci_data.cpu_nintr++;
231                     if (platform.clockintr) {
232                               /*
233                                * Call hardclock().  This will also call
234                                * statclock(). On the primary CPU, it
235                                * will also deal with time-of-day stuff.
236                                */
237                               (*platform.clockintr)((struct clockframe *)framep);
238 
239 #if defined(MULTIPROCESSOR)
240                               if (alpha_use_cctr) {
241                                         cc_hardclock(ci);
242                               }
243 #endif /* MULTIPROCESSOR */
244 
245                               /*
246                                * If it's time to call the scheduler clock,
247                                * do so.
248                                */
249                               if ((++ci->ci_schedstate.spc_schedticks & 0x3f) == 0 &&
250                                   schedhz != 0)
251                                         schedclock(ci->ci_curlwp);
252                     }
253                     ci->ci_intrdepth -= 0x10;
254                     break;
255 
256           case ALPHA_INTR_ERROR:        /* Machine Check or Correctable Error */
257                     ci->ci_intrdepth++;
258                     a0 = alpha_pal_rdmces();
259                     if (platform.mcheck_handler != NULL &&
260                         (void *)framep->tf_regs[FRAME_PC] != XentArith)
261                               (*platform.mcheck_handler)(a0, framep, a1, a2);
262                     else
263                               machine_check(a0, framep, a1, a2);
264                     ci->ci_intrdepth--;
265                     break;
266 
267           case ALPHA_INTR_DEVICE:       /* I/O device interrupt */
268               {
269                     const int idx = SCB_VECTOIDX(a1 - SCB_IOVECBASE);
270 
271                     KDASSERT(a1 >= SCB_IOVECBASE && a1 < SCB_SIZE);
272 
273                     atomic_inc_ulong(&sc->sc_evcnt_device.ev_count);
274                     ci->ci_intrdepth++;
275 
276                     ci->ci_data.cpu_nintr++;
277 
278                     struct scbvec * const scb = &scb_iovectab[idx];
279                     (*scb->scb_func)(scb->scb_arg, a1);
280 
281                     ci->ci_intrdepth--;
282                     break;
283               }
284 
285           case ALPHA_INTR_PERF:         /* performance counter interrupt */
286                     printf("WARNING: received performance counter interrupt!\n");
287                     break;
288 
289           case ALPHA_INTR_PASSIVE:
290 #if 0
291                     printf("WARNING: received passive release interrupt vec "
292                         "0x%lx\n", a1);
293 #endif
294                     break;
295 
296           default:
297                     printf("unexpected interrupt: type 0x%lx vec 0x%lx "
298                         "a2 0x%lx"
299 #if defined(MULTIPROCESSOR)
300                         " cpu %lu"
301 #endif
302                         "\n", a0, a1, a2
303 #if defined(MULTIPROCESSOR)
304                         , ci->ci_cpuid
305 #endif
306                         );
307                     panic("interrupt");
308                     /* NOTREACHED */
309           }
310 }
311 
312 void
machine_check(unsigned long mces,struct trapframe * framep,unsigned long vector,unsigned long param)313 machine_check(unsigned long mces, struct trapframe *framep,
314     unsigned long vector, unsigned long param)
315 {
316           const char *type;
317           struct mchkinfo *mcp;
318           static struct timeval ratelimit[1];
319 
320           mcp = &curcpu()->ci_mcinfo;
321           /* Make sure it's an error we know about. */
322           if ((mces & (ALPHA_MCES_MIP|ALPHA_MCES_SCE|ALPHA_MCES_PCE)) == 0) {
323                     type = "fatal machine check or error (unknown type)";
324                     goto fatal;
325           }
326 
327           /* Machine checks. */
328           if (mces & ALPHA_MCES_MIP) {
329                     /* If we weren't expecting it, then we punt. */
330                     if (!mcp->mc_expected) {
331                               type = "unexpected machine check";
332                               goto fatal;
333                     }
334                     mcp->mc_expected = 0;
335                     mcp->mc_received = 1;
336           }
337 
338           /* System correctable errors. */
339           if (mces & ALPHA_MCES_SCE)
340                     printf("Warning: received system correctable error.\n");
341 
342           /* Processor correctable errors. */
343           if (mces & ALPHA_MCES_PCE)
344                     printf("Warning: received processor correctable error.\n");
345 
346           /* Clear pending machine checks and correctable errors */
347           alpha_pal_wrmces(mces);
348           return;
349 
350 fatal:
351           alpha_pal_wrmces(mces);
352           if ((void *)framep->tf_regs[FRAME_PC] == XentArith) {
353                     rlprintf(ratelimit, "Stray machine check\n");
354                     return;
355           }
356 
357           printf("\n");
358           printf("%s:\n", type);
359           printf("\n");
360           printf("    mces    = 0x%lx\n", mces);
361           printf("    vector  = 0x%lx\n", vector);
362           printf("    param   = 0x%lx\n", param);
363           printf("    pc      = 0x%lx\n", framep->tf_regs[FRAME_PC]);
364           printf("    ra      = 0x%lx\n", framep->tf_regs[FRAME_RA]);
365           printf("    code    = 0x%lx\n", *(unsigned long *)(param + 0x10));
366           printf("    curlwp = %p\n", curlwp);
367           if (curlwp != NULL)
368                     printf("        pid = %d.%d, comm = %s\n",
369                         curproc->p_pid, curlwp->l_lid,
370                         curproc->p_comm);
371           printf("\n");
372           panic("machine check");
373 }
374 
375 int
badaddr(void * addr,size_t size)376 badaddr(void *addr, size_t size)
377 {
378 
379           return (badaddr_read(addr, size, NULL));
380 }
381 
382 int
badaddr_read(void * addr,size_t size,void * rptr)383 badaddr_read(void *addr, size_t size, void *rptr)
384 {
385           lwp_t * const l = curlwp;
386           KPREEMPT_DISABLE(l);
387 
388           struct mchkinfo *mcp = &curcpu()->ci_mcinfo;
389           long rcpt;
390           int rv;
391 
392           /* Get rid of any stale machine checks that have been waiting.  */
393           alpha_pal_draina();
394 
395           /* Tell the trap code to expect a machine check. */
396           mcp->mc_received = 0;
397           mcp->mc_expected = 1;
398 
399           /* Read from the test address, and make sure the read happens. */
400           alpha_mb();
401           switch (size) {
402           case sizeof (uint8_t):
403                     rcpt = *(volatile uint8_t *)addr;
404                     break;
405 
406           case sizeof (uint16_t):
407                     rcpt = *(volatile uint16_t *)addr;
408                     break;
409 
410           case sizeof (uint32_t):
411                     rcpt = *(volatile uint32_t *)addr;
412                     break;
413 
414           case sizeof (uint64_t):
415                     rcpt = *(volatile uint64_t *)addr;
416                     break;
417 
418           default:
419                     panic("badaddr: invalid size (%ld)", size);
420           }
421           alpha_mb();
422           alpha_mb();         /* MAGIC ON SOME SYSTEMS */
423 
424           /* Make sure we took the machine check, if we caused one. */
425           alpha_pal_draina();
426 
427           /* disallow further machine checks */
428           mcp->mc_expected = 0;
429 
430           rv = mcp->mc_received;
431           mcp->mc_received = 0;
432 
433           /*
434            * And copy back read results (if no fault occurred).
435            */
436           if (rptr && rv == 0) {
437                     switch (size) {
438                     case sizeof (uint8_t):
439                               *(volatile uint8_t *)rptr = rcpt;
440                               break;
441 
442                     case sizeof (uint16_t):
443                               *(volatile uint16_t *)rptr = rcpt;
444                               break;
445 
446                     case sizeof (uint32_t):
447                               *(volatile uint32_t *)rptr = rcpt;
448                               break;
449 
450                     case sizeof (uint64_t):
451                               *(volatile uint64_t *)rptr = rcpt;
452                               break;
453                     }
454           }
455 
456           KPREEMPT_ENABLE(l);
457 
458           /* Return non-zero (i.e. true) if it's a bad address. */
459           return (rv);
460 }
461 
462 /*
463  * Fast soft interrupt support.
464  */
465 
466 #define   SOFTINT_TO_IPL(si)                                                    \
467           (ALPHA_PSL_IPL_SOFT_LO + ((ALPHA_IPL2_SOFTINTS >> (si)) & 1))
468 
469 #define   SOFTINTS_ELIGIBLE(ipl)                                                          \
470           ((ALPHA_ALL_SOFTINTS << ((ipl) << 1)) & ALPHA_ALL_SOFTINTS)
471 
472 /* Validate some assumptions the code makes. */
473 __CTASSERT(SOFTINT_TO_IPL(SOFTINT_CLOCK) == ALPHA_PSL_IPL_SOFT_LO);
474 __CTASSERT(SOFTINT_TO_IPL(SOFTINT_BIO) == ALPHA_PSL_IPL_SOFT_LO);
475 __CTASSERT(SOFTINT_TO_IPL(SOFTINT_NET) == ALPHA_PSL_IPL_SOFT_HI);
476 __CTASSERT(SOFTINT_TO_IPL(SOFTINT_SERIAL) == ALPHA_PSL_IPL_SOFT_HI);
477 
478 __CTASSERT(IPL_SOFTCLOCK == ALPHA_PSL_IPL_SOFT_LO);
479 __CTASSERT(IPL_SOFTBIO == ALPHA_PSL_IPL_SOFT_LO);
480 __CTASSERT(IPL_SOFTNET == ALPHA_PSL_IPL_SOFT_HI);
481 __CTASSERT(IPL_SOFTSERIAL == ALPHA_PSL_IPL_SOFT_HI);
482 
483 __CTASSERT(SOFTINT_CLOCK_MASK & 0x3);
484 __CTASSERT(SOFTINT_BIO_MASK & 0x3);
485 __CTASSERT(SOFTINT_NET_MASK & 0xc);
486 __CTASSERT(SOFTINT_SERIAL_MASK & 0xc);
487 __CTASSERT(SOFTINT_COUNT == 4);
488 
489 __CTASSERT((ALPHA_ALL_SOFTINTS & ~0xfUL) == 0);
490 __CTASSERT(SOFTINTS_ELIGIBLE(IPL_NONE) == ALPHA_ALL_SOFTINTS);
491 __CTASSERT(SOFTINTS_ELIGIBLE(IPL_SOFTCLOCK) == ALPHA_IPL2_SOFTINTS);
492 __CTASSERT(SOFTINTS_ELIGIBLE(IPL_SOFTBIO) == ALPHA_IPL2_SOFTINTS);
493 __CTASSERT(SOFTINTS_ELIGIBLE(IPL_SOFTNET) == 0);
494 __CTASSERT(SOFTINTS_ELIGIBLE(IPL_SOFTSERIAL) == 0);
495 
496 /*
497  * softint_trigger:
498  *
499  *        Trigger a soft interrupt.
500  */
501 void
softint_trigger(uintptr_t const machdep)502 softint_trigger(uintptr_t const machdep)
503 {
504           /* No need for an atomic; called at splhigh(). */
505           KASSERT(alpha_pal_rdps() == ALPHA_PSL_IPL_HIGH);
506           curcpu()->ci_ssir |= machdep;
507 }
508 
509 /*
510  * softint_init_md:
511  *
512  *        Machine-dependent initialization for a fast soft interrupt thread.
513  */
514 void
softint_init_md(lwp_t * const l,u_int const level,uintptr_t * const machdep)515 softint_init_md(lwp_t * const l, u_int const level, uintptr_t * const machdep)
516 {
517           lwp_t ** lp = &l->l_cpu->ci_silwps[level];
518           KASSERT(*lp == NULL || *lp == l);
519           *lp = l;
520 
521           const uintptr_t si_bit = __BIT(level);
522           KASSERT(si_bit & ALPHA_ALL_SOFTINTS);
523           *machdep = si_bit;
524 }
525 
526 /*
527  * Helper macro.
528  *
529  * Dispatch a softint and then restart the loop so that higher
530  * priority softints are always done first.
531  */
532 #define   DOSOFTINT(level)                                                      \
533           if (ssir & SOFTINT_##level##_MASK) {                                  \
534                     ci->ci_ssir &= ~SOFTINT_##level##_MASK;                     \
535                     alpha_softint_switchto(l, IPL_SOFT##level,                  \
536                         ci->ci_silwps[SOFTINT_##level]);                        \
537                     KASSERT(alpha_pal_rdps() == ALPHA_PSL_IPL_HIGH);  \
538                     continue;                                                   \
539           }                                                                               \
540 
541 /*
542  * alpha_softint_dispatch:
543  *
544  *        Process pending soft interrupts that are eligible to run
545  *        at the specified new IPL.  Must be called at splhigh().
546  */
547 void
alpha_softint_dispatch(int const ipl)548 alpha_softint_dispatch(int const ipl)
549 {
550           struct lwp * const l = curlwp;
551           struct cpu_info * const ci = l->l_cpu;
552           unsigned long ssir;
553           const unsigned long eligible = SOFTINTS_ELIGIBLE(ipl);
554 
555           KASSERT(alpha_pal_rdps() == ALPHA_PSL_IPL_HIGH);
556 
557           for (;;) {
558                     ssir = ci->ci_ssir & eligible;
559                     if (ssir == 0)
560                               break;
561 
562                     DOSOFTINT(SERIAL);
563                     DOSOFTINT(NET);
564                     DOSOFTINT(BIO);
565                     DOSOFTINT(CLOCK);
566           }
567 }
568 
569 /*
570  * spllower:
571  *
572  *        Lower interrupt priority.  May need to check for software
573  *        interrupts.
574  */
575 void
spllower(int const ipl)576 spllower(int const ipl)
577 {
578 
579           if (ipl < ALPHA_PSL_IPL_SOFT_HI && curcpu()->ci_ssir) {
580                     (void) alpha_pal_swpipl(ALPHA_PSL_IPL_HIGH);
581                     alpha_softint_dispatch(ipl);
582           }
583           (void) alpha_pal_swpipl(ipl);
584 }
585 
586 /*
587  * cpu_intr_p:
588  *
589  *        Return non-zero if executing in interrupt context.
590  */
591 bool
cpu_intr_p(void)592 cpu_intr_p(void)
593 {
594 
595           return curcpu()->ci_intrdepth != 0;
596 }
597 
598 void      (*alpha_intr_redistribute)(void);
599 
600 /*
601  * cpu_intr_redistribute:
602  *
603  *        Redistribute interrupts amongst CPUs eligible to handle them.
604  */
605 void
cpu_intr_redistribute(void)606 cpu_intr_redistribute(void)
607 {
608           if (alpha_intr_redistribute != NULL)
609                     (*alpha_intr_redistribute)();
610 }
611 
612 /*
613  * cpu_intr_count:
614  *
615  *        Return the number of device interrupts this CPU handles.
616  */
617 unsigned int
cpu_intr_count(struct cpu_info * const ci)618 cpu_intr_count(struct cpu_info * const ci)
619 {
620           return ci->ci_nintrhand;
621 }
622 
623 /*
624  * Security sensitive rate limiting printf
625  */
626 void
rlprintf(struct timeval * t,const char * fmt,...)627 rlprintf(struct timeval *t, const char *fmt, ...)
628 {
629           va_list ap;
630           static const struct timeval msgperiod[1] = {{ 5, 0 }};
631 
632           if (!ratecheck(t, msgperiod))
633                     return;
634 
635           va_start(ap, fmt);
636           vprintf(fmt, ap);
637           va_end(ap);
638 }
639