Annotation of sys/altq/altq_subr.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: altq_subr.c,v 1.21 2006/12/20 17:50:40 gwk Exp $ */
2: /* $KAME: altq_subr.c,v 1.11 2002/01/11 08:11:49 kjc Exp $ */
3:
4: /*
5: * Copyright (C) 1997-2002
6: * Sony Computer Science Laboratories Inc. All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following 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 SONY CSL 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 SONY CSL 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:
30: #include <sys/param.h>
31: #include <sys/malloc.h>
32: #include <sys/mbuf.h>
33: #include <sys/systm.h>
34: #include <sys/proc.h>
35: #include <sys/socket.h>
36: #include <sys/socketvar.h>
37: #include <sys/kernel.h>
38: #include <sys/errno.h>
39: #include <sys/syslog.h>
40: #include <sys/sysctl.h>
41: #include <sys/queue.h>
42:
43: #include <net/if.h>
44: #include <net/if_dl.h>
45: #include <net/if_types.h>
46:
47: #include <netinet/in.h>
48: #include <netinet/in_systm.h>
49: #include <netinet/ip.h>
50: #ifdef INET6
51: #include <netinet/ip6.h>
52: #endif
53: #include <netinet/tcp.h>
54: #include <netinet/udp.h>
55:
56: #include <net/pfvar.h>
57: #include <altq/altq.h>
58:
59: /* machine dependent clock related includes */
60: #if defined(__i386__)
61: #include <machine/cpufunc.h> /* for pentium tsc */
62: #include <machine/specialreg.h> /* for CPUID_TSC */
63: #endif /* __i386__ */
64:
65: /*
66: * internal function prototypes
67: */
68: static void tbr_timeout(void *);
69: int (*altq_input)(struct mbuf *, int) = NULL;
70: static int tbr_timer = 0; /* token bucket regulator timer */
71: static struct callout tbr_callout = CALLOUT_INITIALIZER;
72:
73: /*
74: * alternate queueing support routines
75: */
76:
77: /* look up the queue state by the interface name and the queueing type. */
78: void *
79: altq_lookup(name, type)
80: char *name;
81: int type;
82: {
83: struct ifnet *ifp;
84:
85: if ((ifp = ifunit(name)) != NULL) {
86: if (type != ALTQT_NONE && ifp->if_snd.altq_type == type)
87: return (ifp->if_snd.altq_disc);
88: }
89:
90: return NULL;
91: }
92:
93: int
94: altq_attach(ifq, type, discipline, enqueue, dequeue, request, clfier, classify)
95: struct ifaltq *ifq;
96: int type;
97: void *discipline;
98: int (*enqueue)(struct ifaltq *, struct mbuf *, struct altq_pktattr *);
99: struct mbuf *(*dequeue)(struct ifaltq *, int);
100: int (*request)(struct ifaltq *, int, void *);
101: void *clfier;
102: void *(*classify)(void *, struct mbuf *, int);
103: {
104: if (!ALTQ_IS_READY(ifq))
105: return ENXIO;
106:
107: #if 0 /* pfaltq can override the existing discipline */
108: if (ALTQ_IS_ENABLED(ifq))
109: return EBUSY;
110: if (ALTQ_IS_ATTACHED(ifq))
111: return EEXIST;
112: #endif
113: ifq->altq_type = type;
114: ifq->altq_disc = discipline;
115: ifq->altq_enqueue = enqueue;
116: ifq->altq_dequeue = dequeue;
117: ifq->altq_request = request;
118: ifq->altq_clfier = clfier;
119: ifq->altq_classify = classify;
120: ifq->altq_flags &= (ALTQF_CANTCHANGE|ALTQF_ENABLED);
121:
122: return 0;
123: }
124:
125: int
126: altq_detach(ifq)
127: struct ifaltq *ifq;
128: {
129: if (!ALTQ_IS_READY(ifq))
130: return ENXIO;
131: if (ALTQ_IS_ENABLED(ifq))
132: return EBUSY;
133: if (!ALTQ_IS_ATTACHED(ifq))
134: return (0);
135:
136: ifq->altq_type = ALTQT_NONE;
137: ifq->altq_disc = NULL;
138: ifq->altq_enqueue = NULL;
139: ifq->altq_dequeue = NULL;
140: ifq->altq_request = NULL;
141: ifq->altq_clfier = NULL;
142: ifq->altq_classify = NULL;
143: ifq->altq_flags &= ALTQF_CANTCHANGE;
144: return 0;
145: }
146:
147: int
148: altq_enable(ifq)
149: struct ifaltq *ifq;
150: {
151: int s;
152:
153: if (!ALTQ_IS_READY(ifq))
154: return ENXIO;
155: if (ALTQ_IS_ENABLED(ifq))
156: return 0;
157:
158: s = splnet();
159: IFQ_PURGE(ifq);
160: ASSERT(ifq->ifq_len == 0);
161: ifq->altq_flags |= ALTQF_ENABLED;
162: if (ifq->altq_clfier != NULL)
163: ifq->altq_flags |= ALTQF_CLASSIFY;
164: splx(s);
165:
166: return 0;
167: }
168:
169: int
170: altq_disable(ifq)
171: struct ifaltq *ifq;
172: {
173: int s;
174:
175: if (!ALTQ_IS_ENABLED(ifq))
176: return 0;
177:
178: s = splnet();
179: IFQ_PURGE(ifq);
180: ASSERT(ifq->ifq_len == 0);
181: ifq->altq_flags &= ~(ALTQF_ENABLED|ALTQF_CLASSIFY);
182: splx(s);
183: return 0;
184: }
185:
186: void
187: altq_assert(file, line, failedexpr)
188: const char *file, *failedexpr;
189: int line;
190: {
191: (void)printf("altq assertion \"%s\" failed: file \"%s\", line %d\n",
192: failedexpr, file, line);
193: panic("altq assertion");
194: /* NOTREACHED */
195: }
196:
197: /*
198: * internal representation of token bucket parameters
199: * rate: byte_per_unittime << 32
200: * (((bits_per_sec) / 8) << 32) / machclk_freq
201: * depth: byte << 32
202: *
203: */
204: #define TBR_SHIFT 32
205: #define TBR_SCALE(x) ((int64_t)(x) << TBR_SHIFT)
206: #define TBR_UNSCALE(x) ((x) >> TBR_SHIFT)
207:
208: struct mbuf *
209: tbr_dequeue(ifq, op)
210: struct ifaltq *ifq;
211: int op;
212: {
213: struct tb_regulator *tbr;
214: struct mbuf *m;
215: int64_t interval;
216: u_int64_t now;
217:
218: tbr = ifq->altq_tbr;
219: if (op == ALTDQ_REMOVE && tbr->tbr_lastop == ALTDQ_POLL) {
220: /* if this is a remove after poll, bypass tbr check */
221: } else {
222: /* update token only when it is negative */
223: if (tbr->tbr_token <= 0) {
224: now = read_machclk();
225: interval = now - tbr->tbr_last;
226: if (interval >= tbr->tbr_filluptime)
227: tbr->tbr_token = tbr->tbr_depth;
228: else {
229: tbr->tbr_token += interval * tbr->tbr_rate;
230: if (tbr->tbr_token > tbr->tbr_depth)
231: tbr->tbr_token = tbr->tbr_depth;
232: }
233: tbr->tbr_last = now;
234: }
235: /* if token is still negative, don't allow dequeue */
236: if (tbr->tbr_token <= 0)
237: return (NULL);
238: }
239:
240: if (ALTQ_IS_ENABLED(ifq))
241: m = (*ifq->altq_dequeue)(ifq, op);
242: else {
243: if (op == ALTDQ_POLL)
244: IF_POLL(ifq, m);
245: else
246: IF_DEQUEUE(ifq, m);
247: }
248:
249: if (m != NULL && op == ALTDQ_REMOVE)
250: tbr->tbr_token -= TBR_SCALE(m_pktlen(m));
251: tbr->tbr_lastop = op;
252: return (m);
253: }
254:
255: /*
256: * set a token bucket regulator.
257: * if the specified rate is zero, the token bucket regulator is deleted.
258: */
259: int
260: tbr_set(ifq, profile)
261: struct ifaltq *ifq;
262: struct tb_profile *profile;
263: {
264: struct tb_regulator *tbr, *otbr;
265:
266: if (machclk_freq == 0)
267: init_machclk();
268: if (machclk_freq == 0) {
269: printf("tbr_set: no cpu clock available!\n");
270: return (ENXIO);
271: }
272:
273: if (profile->rate == 0) {
274: /* delete this tbr */
275: if ((tbr = ifq->altq_tbr) == NULL)
276: return (ENOENT);
277: ifq->altq_tbr = NULL;
278: FREE(tbr, M_DEVBUF);
279: return (0);
280: }
281:
282: MALLOC(tbr, struct tb_regulator *, sizeof(struct tb_regulator),
283: M_DEVBUF, M_WAITOK);
284: if (tbr == NULL)
285: return (ENOMEM);
286: bzero(tbr, sizeof(struct tb_regulator));
287:
288: tbr->tbr_rate = TBR_SCALE(profile->rate / 8) / machclk_freq;
289: tbr->tbr_depth = TBR_SCALE(profile->depth);
290: if (tbr->tbr_rate > 0)
291: tbr->tbr_filluptime = tbr->tbr_depth / tbr->tbr_rate;
292: else
293: tbr->tbr_filluptime = 0xffffffffffffffffLL;
294: tbr->tbr_token = tbr->tbr_depth;
295: tbr->tbr_last = read_machclk();
296: tbr->tbr_lastop = ALTDQ_REMOVE;
297:
298: otbr = ifq->altq_tbr;
299: ifq->altq_tbr = tbr; /* set the new tbr */
300:
301: if (otbr != NULL)
302: FREE(otbr, M_DEVBUF);
303: else {
304: if (tbr_timer == 0) {
305: CALLOUT_RESET(&tbr_callout, 1, tbr_timeout, (void *)0);
306: tbr_timer = 1;
307: }
308: }
309: return (0);
310: }
311:
312: /*
313: * tbr_timeout goes through the interface list, and kicks the drivers
314: * if necessary.
315: */
316: static void
317: tbr_timeout(arg)
318: void *arg;
319: {
320: struct ifnet *ifp;
321: int active, s;
322:
323: active = 0;
324: s = splnet();
325: for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list)) {
326: if (!TBR_IS_ENABLED(&ifp->if_snd))
327: continue;
328: active++;
329: if (!IFQ_IS_EMPTY(&ifp->if_snd) && ifp->if_start != NULL)
330: (*ifp->if_start)(ifp);
331: }
332: splx(s);
333: if (active > 0)
334: CALLOUT_RESET(&tbr_callout, 1, tbr_timeout, (void *)0);
335: else
336: tbr_timer = 0; /* don't need tbr_timer anymore */
337: #if defined(__alpha__) && !defined(ALTQ_NOPCC)
338: {
339: /*
340: * XXX read out the machine dependent clock once a second
341: * to detect counter wrap-around.
342: */
343: static u_int cnt;
344:
345: if (++cnt >= hz) {
346: (void)read_machclk();
347: cnt = 0;
348: }
349: }
350: #endif /* __alpha__ && !ALTQ_NOPCC */
351: }
352:
353: /*
354: * get token bucket regulator profile
355: */
356: int
357: tbr_get(ifq, profile)
358: struct ifaltq *ifq;
359: struct tb_profile *profile;
360: {
361: struct tb_regulator *tbr;
362:
363: if ((tbr = ifq->altq_tbr) == NULL) {
364: profile->rate = 0;
365: profile->depth = 0;
366: } else {
367: profile->rate =
368: (u_int)TBR_UNSCALE(tbr->tbr_rate * 8 * machclk_freq);
369: profile->depth = (u_int)TBR_UNSCALE(tbr->tbr_depth);
370: }
371: return (0);
372: }
373:
374: /*
375: * attach a discipline to the interface. if one already exists, it is
376: * overridden.
377: */
378: int
379: altq_pfattach(struct pf_altq *a)
380: {
381: int error = 0;
382:
383: switch (a->scheduler) {
384: case ALTQT_NONE:
385: break;
386: #ifdef ALTQ_CBQ
387: case ALTQT_CBQ:
388: error = cbq_pfattach(a);
389: break;
390: #endif
391: #ifdef ALTQ_PRIQ
392: case ALTQT_PRIQ:
393: error = priq_pfattach(a);
394: break;
395: #endif
396: #ifdef ALTQ_HFSC
397: case ALTQT_HFSC:
398: error = hfsc_pfattach(a);
399: break;
400: #endif
401: default:
402: error = ENXIO;
403: }
404:
405: return (error);
406: }
407:
408: /*
409: * detach a discipline from the interface.
410: * it is possible that the discipline was already overridden by another
411: * discipline.
412: */
413: int
414: altq_pfdetach(struct pf_altq *a)
415: {
416: struct ifnet *ifp;
417: int s, error = 0;
418:
419: if ((ifp = ifunit(a->ifname)) == NULL)
420: return (EINVAL);
421:
422: /* if this discipline is no longer referenced, just return */
423: if (a->altq_disc == NULL || a->altq_disc != ifp->if_snd.altq_disc)
424: return (0);
425:
426: s = splnet();
427: if (ALTQ_IS_ENABLED(&ifp->if_snd))
428: error = altq_disable(&ifp->if_snd);
429: if (error == 0)
430: error = altq_detach(&ifp->if_snd);
431: splx(s);
432:
433: return (error);
434: }
435:
436: /*
437: * add a discipline or a queue
438: */
439: int
440: altq_add(struct pf_altq *a)
441: {
442: int error = 0;
443:
444: if (a->qname[0] != 0)
445: return (altq_add_queue(a));
446:
447: if (machclk_freq == 0)
448: init_machclk();
449: if (machclk_freq == 0)
450: panic("altq_add: no cpu clock");
451:
452: switch (a->scheduler) {
453: #ifdef ALTQ_CBQ
454: case ALTQT_CBQ:
455: error = cbq_add_altq(a);
456: break;
457: #endif
458: #ifdef ALTQ_PRIQ
459: case ALTQT_PRIQ:
460: error = priq_add_altq(a);
461: break;
462: #endif
463: #ifdef ALTQ_HFSC
464: case ALTQT_HFSC:
465: error = hfsc_add_altq(a);
466: break;
467: #endif
468: default:
469: error = ENXIO;
470: }
471:
472: return (error);
473: }
474:
475: /*
476: * remove a discipline or a queue
477: */
478: int
479: altq_remove(struct pf_altq *a)
480: {
481: int error = 0;
482:
483: if (a->qname[0] != 0)
484: return (altq_remove_queue(a));
485:
486: switch (a->scheduler) {
487: #ifdef ALTQ_CBQ
488: case ALTQT_CBQ:
489: error = cbq_remove_altq(a);
490: break;
491: #endif
492: #ifdef ALTQ_PRIQ
493: case ALTQT_PRIQ:
494: error = priq_remove_altq(a);
495: break;
496: #endif
497: #ifdef ALTQ_HFSC
498: case ALTQT_HFSC:
499: error = hfsc_remove_altq(a);
500: break;
501: #endif
502: default:
503: error = ENXIO;
504: }
505:
506: return (error);
507: }
508:
509: /*
510: * add a queue to the discipline
511: */
512: int
513: altq_add_queue(struct pf_altq *a)
514: {
515: int error = 0;
516:
517: switch (a->scheduler) {
518: #ifdef ALTQ_CBQ
519: case ALTQT_CBQ:
520: error = cbq_add_queue(a);
521: break;
522: #endif
523: #ifdef ALTQ_PRIQ
524: case ALTQT_PRIQ:
525: error = priq_add_queue(a);
526: break;
527: #endif
528: #ifdef ALTQ_HFSC
529: case ALTQT_HFSC:
530: error = hfsc_add_queue(a);
531: break;
532: #endif
533: default:
534: error = ENXIO;
535: }
536:
537: return (error);
538: }
539:
540: /*
541: * remove a queue from the discipline
542: */
543: int
544: altq_remove_queue(struct pf_altq *a)
545: {
546: int error = 0;
547:
548: switch (a->scheduler) {
549: #ifdef ALTQ_CBQ
550: case ALTQT_CBQ:
551: error = cbq_remove_queue(a);
552: break;
553: #endif
554: #ifdef ALTQ_PRIQ
555: case ALTQT_PRIQ:
556: error = priq_remove_queue(a);
557: break;
558: #endif
559: #ifdef ALTQ_HFSC
560: case ALTQT_HFSC:
561: error = hfsc_remove_queue(a);
562: break;
563: #endif
564: default:
565: error = ENXIO;
566: }
567:
568: return (error);
569: }
570:
571: /*
572: * get queue statistics
573: */
574: int
575: altq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes)
576: {
577: int error = 0;
578:
579: switch (a->scheduler) {
580: #ifdef ALTQ_CBQ
581: case ALTQT_CBQ:
582: error = cbq_getqstats(a, ubuf, nbytes);
583: break;
584: #endif
585: #ifdef ALTQ_PRIQ
586: case ALTQT_PRIQ:
587: error = priq_getqstats(a, ubuf, nbytes);
588: break;
589: #endif
590: #ifdef ALTQ_HFSC
591: case ALTQT_HFSC:
592: error = hfsc_getqstats(a, ubuf, nbytes);
593: break;
594: #endif
595: default:
596: error = ENXIO;
597: }
598:
599: return (error);
600: }
601:
602: /*
603: * read and write diffserv field in IPv4 or IPv6 header
604: */
605: u_int8_t
606: read_dsfield(m, pktattr)
607: struct mbuf *m;
608: struct altq_pktattr *pktattr;
609: {
610: struct mbuf *m0;
611: u_int8_t ds_field = 0;
612:
613: if (pktattr == NULL ||
614: (pktattr->pattr_af != AF_INET && pktattr->pattr_af != AF_INET6))
615: return ((u_int8_t)0);
616:
617: /* verify that pattr_hdr is within the mbuf data */
618: for (m0 = m; m0 != NULL; m0 = m0->m_next)
619: if ((pktattr->pattr_hdr >= m0->m_data) &&
620: (pktattr->pattr_hdr < m0->m_data + m0->m_len))
621: break;
622: if (m0 == NULL) {
623: /* ick, pattr_hdr is stale */
624: pktattr->pattr_af = AF_UNSPEC;
625: #ifdef ALTQ_DEBUG
626: printf("read_dsfield: can't locate header!\n");
627: #endif
628: return ((u_int8_t)0);
629: }
630:
631: if (pktattr->pattr_af == AF_INET) {
632: struct ip *ip = (struct ip *)pktattr->pattr_hdr;
633:
634: if (ip->ip_v != 4)
635: return ((u_int8_t)0); /* version mismatch! */
636: ds_field = ip->ip_tos;
637: }
638: #ifdef INET6
639: else if (pktattr->pattr_af == AF_INET6) {
640: struct ip6_hdr *ip6 = (struct ip6_hdr *)pktattr->pattr_hdr;
641: u_int32_t flowlabel;
642:
643: flowlabel = ntohl(ip6->ip6_flow);
644: if ((flowlabel >> 28) != 6)
645: return ((u_int8_t)0); /* version mismatch! */
646: ds_field = (flowlabel >> 20) & 0xff;
647: }
648: #endif
649: return (ds_field);
650: }
651:
652: void
653: write_dsfield(m, pktattr, dsfield)
654: struct mbuf *m;
655: struct altq_pktattr *pktattr;
656: u_int8_t dsfield;
657: {
658: struct mbuf *m0;
659:
660: if (pktattr == NULL ||
661: (pktattr->pattr_af != AF_INET && pktattr->pattr_af != AF_INET6))
662: return;
663:
664: /* verify that pattr_hdr is within the mbuf data */
665: for (m0 = m; m0 != NULL; m0 = m0->m_next)
666: if ((pktattr->pattr_hdr >= m0->m_data) &&
667: (pktattr->pattr_hdr < m0->m_data + m0->m_len))
668: break;
669: if (m0 == NULL) {
670: /* ick, pattr_hdr is stale */
671: pktattr->pattr_af = AF_UNSPEC;
672: #ifdef ALTQ_DEBUG
673: printf("write_dsfield: can't locate header!\n");
674: #endif
675: return;
676: }
677:
678: if (pktattr->pattr_af == AF_INET) {
679: struct ip *ip = (struct ip *)pktattr->pattr_hdr;
680: u_int8_t old;
681: int32_t sum;
682:
683: if (ip->ip_v != 4)
684: return; /* version mismatch! */
685: old = ip->ip_tos;
686: dsfield |= old & 3; /* leave CU bits */
687: if (old == dsfield)
688: return;
689: ip->ip_tos = dsfield;
690: /*
691: * update checksum (from RFC1624)
692: * HC' = ~(~HC + ~m + m')
693: */
694: sum = ~ntohs(ip->ip_sum) & 0xffff;
695: sum += 0xff00 + (~old & 0xff) + dsfield;
696: sum = (sum >> 16) + (sum & 0xffff);
697: sum += (sum >> 16); /* add carry */
698:
699: ip->ip_sum = htons(~sum & 0xffff);
700: }
701: #ifdef INET6
702: else if (pktattr->pattr_af == AF_INET6) {
703: struct ip6_hdr *ip6 = (struct ip6_hdr *)pktattr->pattr_hdr;
704: u_int32_t flowlabel;
705:
706: flowlabel = ntohl(ip6->ip6_flow);
707: if ((flowlabel >> 28) != 6)
708: return; /* version mismatch! */
709: flowlabel = (flowlabel & 0xf03fffff) | (dsfield << 20);
710: ip6->ip6_flow = htonl(flowlabel);
711: }
712: #endif
713: return;
714: }
715:
716:
717: /*
718: * high resolution clock support taking advantage of a machine dependent
719: * high resolution time counter (e.g., timestamp counter of intel pentium).
720: * we assume
721: * - 64-bit-long monotonically-increasing counter
722: * - frequency range is 100M-4GHz (CPU speed)
723: */
724: /* if pcc is not available or disabled, emulate 256MHz using microtime() */
725: #define MACHCLK_SHIFT 8
726:
727: int machclk_usepcc;
728: u_int32_t machclk_freq = 0;
729: u_int32_t machclk_per_tick = 0;
730:
731: #ifdef __alpha__
732: extern u_int64_t cycles_per_usec; /* alpha cpu clock frequency */
733: #endif /* __alpha__ */
734:
735: void
736: init_machclk(void)
737: {
738: machclk_usepcc = 1;
739:
740: #if (!defined(__i386__) && !defined(__alpha__)) || defined(ALTQ_NOPCC)
741: machclk_usepcc = 0;
742: #endif
743: #if defined(__FreeBSD__) && defined(SMP)
744: machclk_usepcc = 0;
745: #endif
746: #if defined(__NetBSD__) && defined(MULTIPROCESSOR)
747: machclk_usepcc = 0;
748: #endif
749: #if defined(__OpenBSD__) && defined(__HAVE_TIMECOUNTER)
750: /*
751: * If we have timecounters, microtime is good enough and we can
752: * avoid problems on machines with variable cycle counter
753: * frequencies.
754: */
755: machclk_usepcc = 0;
756: #endif
757: #ifdef __i386__
758: /* check if TSC is available */
759: if (machclk_usepcc == 1 && (cpu_feature & CPUID_TSC) == 0)
760: machclk_usepcc = 0;
761: #endif
762:
763: if (machclk_usepcc == 0) {
764: /* emulate 256MHz using microtime() */
765: machclk_freq = 1000000 << MACHCLK_SHIFT;
766: machclk_per_tick = machclk_freq / hz;
767: #ifdef ALTQ_DEBUG
768: printf("altq: emulate %uHz cpu clock\n", machclk_freq);
769: #endif
770: return;
771: }
772:
773: /*
774: * if the clock frequency (of Pentium TSC or Alpha PCC) is
775: * accessible, just use it.
776: */
777: #if defined(__i386__) && (defined(I586_CPU) || defined(I686_CPU))
778: /* XXX - this will break down with variable cpu frequency. */
779: machclk_freq = cpuspeed * 1000000;
780: #endif
781: #if defined(__alpha__)
782: machclk_freq = (u_int32_t)(cycles_per_usec * 1000000);
783: #endif /* __alpha__ */
784:
785: /*
786: * if we don't know the clock frequency, measure it.
787: */
788: if (machclk_freq == 0) {
789: static int wait;
790: struct timeval tv_start, tv_end;
791: u_int64_t start, end, diff;
792: int timo;
793:
794: microtime(&tv_start);
795: start = read_machclk();
796: timo = hz; /* 1 sec */
797: (void)tsleep(&wait, PWAIT | PCATCH, "init_machclk", timo);
798: microtime(&tv_end);
799: end = read_machclk();
800: diff = (u_int64_t)(tv_end.tv_sec - tv_start.tv_sec) * 1000000
801: + tv_end.tv_usec - tv_start.tv_usec;
802: if (diff != 0)
803: machclk_freq = (u_int)((end - start) * 1000000 / diff);
804: }
805:
806: machclk_per_tick = machclk_freq / hz;
807:
808: #ifdef ALTQ_DEBUG
809: printf("altq: CPU clock: %uHz\n", machclk_freq);
810: #endif
811: }
812:
813: #if defined(__OpenBSD__) && defined(__i386__)
814: static __inline u_int64_t
815: rdtsc(void)
816: {
817: u_int64_t rv;
818: __asm __volatile(".byte 0x0f, 0x31" : "=A" (rv));
819: return (rv);
820: }
821: #endif /* __OpenBSD__ && __i386__ */
822:
823: u_int64_t
824: read_machclk(void)
825: {
826: u_int64_t val;
827:
828: if (machclk_usepcc) {
829: #if defined(__i386__)
830: val = rdtsc();
831: #elif defined(__alpha__)
832: static u_int32_t last_pcc, upper;
833: u_int32_t pcc;
834:
835: /*
836: * for alpha, make a 64bit counter value out of the 32bit
837: * alpha processor cycle counter.
838: * read_machclk must be called within a half of its
839: * wrap-around cycle (about 5 sec for 400MHz cpu) to properly
840: * detect a counter wrap-around.
841: * tbr_timeout calls read_machclk once a second.
842: */
843: pcc = (u_int32_t)alpha_rpcc();
844: if (pcc <= last_pcc)
845: upper++;
846: last_pcc = pcc;
847: val = ((u_int64_t)upper << 32) + pcc;
848: #else
849: panic("read_machclk");
850: #endif
851: } else {
852: struct timeval tv;
853:
854: microuptime(&tv);
855: val = (((u_int64_t)(tv.tv_sec) * 1000000
856: + tv.tv_usec) << MACHCLK_SHIFT);
857: }
858: return (val);
859: }
CVSweb