kernel - Reduce lwp_signotify() latency
[dragonfly.git] / usr.sbin / pfctl / pfctl_qstats.c
1 /*      $OpenBSD: pfctl_qstats.c,v 1.31 2007/10/15 02:16:35 deraadt Exp $ */
2
3 /*
4  * Copyright (c) Henning Brauer <henning@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include <sys/types.h>
20 #include <sys/ioctl.h>
21 #include <sys/socket.h>
22
23 #include <net/if.h>
24 #include <netinet/in.h>
25 #include <net/pf/pfvar.h>
26 #include <arpa/inet.h>
27
28 #include <err.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33
34 #include <net/altq/altq.h>
35 #include <net/altq/altq_cbq.h>
36 #include <net/altq/altq_priq.h>
37 #include <net/altq/altq_hfsc.h>
38 #include <net/altq/altq_fairq.h>
39
40 #include "pfctl.h"
41 #include "pfctl_parser.h"
42
43 union class_stats {
44         class_stats_t           cbq_stats;
45         struct priq_classstats  priq_stats;
46         struct hfsc_classstats  hfsc_stats;
47         struct fairq_classstats fairq_stats;
48 };
49
50 #define AVGN_MAX        8
51 #define STAT_INTERVAL   5
52
53 struct queue_stats {
54         union class_stats        data;
55         int                      avgn;
56         double                   avg_bytes;
57         double                   avg_packets;
58         u_int64_t                prev_bytes;
59         u_int64_t                prev_packets;
60 };
61
62 struct pf_altq_node {
63         struct pf_altq           altq;
64         struct pf_altq_node     *next;
65         struct pf_altq_node     *children;
66         struct queue_stats       qstats;
67 };
68
69 int                      pfctl_update_qstats(int, struct pf_altq_node **);
70 void                     pfctl_insert_altq_node(struct pf_altq_node **,
71                             const struct pf_altq, const struct queue_stats);
72 struct pf_altq_node     *pfctl_find_altq_node(struct pf_altq_node *,
73                             const char *, const char *);
74 void                     pfctl_print_altq_node(int, const struct pf_altq_node *,
75                              unsigned, int);
76 void                     print_cbqstats(struct queue_stats);
77 void                     print_priqstats(struct queue_stats);
78 void                     print_hfscstats(struct queue_stats);
79 void                     print_fairqstats(struct queue_stats);
80 void                     pfctl_free_altq_node(struct pf_altq_node *);
81 void                     pfctl_print_altq_nodestat(int,
82                             const struct pf_altq_node *);
83
84 void                     update_avg(struct pf_altq_node *);
85
86 int
87 pfctl_show_altq(int dev, const char *iface, int opts, int verbose2)
88 {
89         struct pf_altq_node     *root = NULL, *node;
90         int                      nodes, dotitle = (opts & PF_OPT_SHOWALL);
91
92
93         if ((nodes = pfctl_update_qstats(dev, &root)) < 0)
94                 return (-1);
95
96         for (node = root; node != NULL; node = node->next) {
97                 if (iface != NULL && strcmp(node->altq.ifname, iface))
98                         continue;
99                 if (dotitle) {
100                         pfctl_print_title("ALTQ:");
101                         dotitle = 0;
102                 }
103                 pfctl_print_altq_node(dev, node, 0, opts);
104         }
105
106         while (verbose2) {
107                 printf("\n");
108                 fflush(stdout);
109                 sleep(STAT_INTERVAL);
110                 if (pfctl_update_qstats(dev, &root) == -1)
111                         return (-1);
112                 for (node = root; node != NULL; node = node->next) {
113                         if (iface != NULL && strcmp(node->altq.ifname, iface))
114                                 continue;
115                         pfctl_print_altq_node(dev, node, 0, opts);
116                 }
117         }
118         pfctl_free_altq_node(root);
119         return (0);
120 }
121
122 int
123 pfctl_update_qstats(int dev, struct pf_altq_node **root)
124 {
125         struct pf_altq_node     *node;
126         struct pfioc_altq        pa;
127         struct pfioc_qstats      pq;
128         u_int32_t                mnr, nr;
129         struct queue_stats       qstats;
130         static  u_int32_t        last_ticket;
131
132         memset(&pa, 0, sizeof(pa));
133         memset(&pq, 0, sizeof(pq));
134         memset(&qstats, 0, sizeof(qstats));
135         if (ioctl(dev, DIOCGETALTQS, &pa)) {
136                 warn("DIOCGETALTQS");
137                 return (-1);
138         }
139
140         /* if a new set is found, start over */
141         if (pa.ticket != last_ticket && *root != NULL) {
142                 pfctl_free_altq_node(*root);
143                 *root = NULL;
144         }
145         last_ticket = pa.ticket;
146
147         mnr = pa.nr;
148         for (nr = 0; nr < mnr; ++nr) {
149                 pa.nr = nr;
150                 if (ioctl(dev, DIOCGETALTQ, &pa)) {
151                         warn("DIOCGETALTQ");
152                         return (-1);
153                 }
154                 if (pa.altq.qid > 0) {
155                         pq.nr = nr;
156                         pq.ticket = pa.ticket;
157                         pq.buf = &qstats.data;
158                         pq.nbytes = sizeof(qstats.data);
159                         if (ioctl(dev, DIOCGETQSTATS, &pq)) {
160                                 warn("DIOCGETQSTATS");
161                                 return (-1);
162                         }
163                         if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
164                             pa.altq.ifname)) != NULL) {
165                                 memcpy(&node->qstats.data, &qstats.data,
166                                     sizeof(qstats.data));
167                                 update_avg(node);
168                         } else {
169                                 pfctl_insert_altq_node(root, pa.altq, qstats);
170                         }
171                 }
172         }
173         return (mnr);
174 }
175
176 void
177 pfctl_insert_altq_node(struct pf_altq_node **root,
178     const struct pf_altq altq, const struct queue_stats qstats)
179 {
180         struct pf_altq_node     *node;
181
182         node = calloc(1, sizeof(struct pf_altq_node));
183         if (node == NULL)
184                 err(1, "pfctl_insert_altq_node: calloc");
185         memcpy(&node->altq, &altq, sizeof(struct pf_altq));
186         memcpy(&node->qstats, &qstats, sizeof(qstats));
187         node->next = node->children = NULL;
188
189         if (*root == NULL)
190                 *root = node;
191         else if (!altq.parent[0]) {
192                 struct pf_altq_node     *prev = *root;
193
194                 while (prev->next != NULL)
195                         prev = prev->next;
196                 prev->next = node;
197         } else {
198                 struct pf_altq_node     *parent;
199
200                 parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname);
201                 if (parent == NULL)
202                         errx(1, "parent %s not found", altq.parent);
203                 if (parent->children == NULL)
204                         parent->children = node;
205                 else {
206                         struct pf_altq_node *prev = parent->children;
207
208                         while (prev->next != NULL)
209                                 prev = prev->next;
210                         prev->next = node;
211                 }
212         }
213         update_avg(node);
214 }
215
216 struct pf_altq_node *
217 pfctl_find_altq_node(struct pf_altq_node *root, const char *qname,
218     const char *ifname)
219 {
220         struct pf_altq_node     *node, *child;
221
222         for (node = root; node != NULL; node = node->next) {
223                 if (!strcmp(node->altq.qname, qname)
224                     && !(strcmp(node->altq.ifname, ifname)))
225                         return (node);
226                 if (node->children != NULL) {
227                         child = pfctl_find_altq_node(node->children, qname,
228                             ifname);
229                         if (child != NULL)
230                                 return (child);
231                 }
232         }
233         return (NULL);
234 }
235
236 void
237 pfctl_print_altq_node(int dev, const struct pf_altq_node *node,
238     unsigned int level, int opts)
239 {
240         const struct pf_altq_node       *child;
241
242         if (node == NULL)
243                 return;
244
245         print_altq(&node->altq, level, NULL, NULL);
246
247         if (node->children != NULL) {
248                 printf("{");
249                 for (child = node->children; child != NULL;
250                     child = child->next) {
251                         printf("%s", child->altq.qname);
252                         if (child->next != NULL)
253                                 printf(", ");
254                 }
255                 printf("}");
256         }
257         printf("\n");
258
259         if (opts & PF_OPT_VERBOSE)
260                 pfctl_print_altq_nodestat(dev, node);
261
262         if (opts & PF_OPT_DEBUG)
263                 printf("  [ qid=%u ifname=%s ifbandwidth=%s ]\n",
264                     node->altq.qid, node->altq.ifname,
265                     rate2str((double)(node->altq.ifbandwidth)));
266
267         for (child = node->children; child != NULL;
268             child = child->next)
269                 pfctl_print_altq_node(dev, child, level + 1, opts);
270 }
271
272 void
273 pfctl_print_altq_nodestat(int dev __unused, const struct pf_altq_node *a)
274 {
275         if (a->altq.qid == 0)
276                 return;
277
278         switch (a->altq.scheduler) {
279         case ALTQT_CBQ:
280                 print_cbqstats(a->qstats);
281                 break;
282         case ALTQT_PRIQ:
283                 print_priqstats(a->qstats);
284                 break;
285         case ALTQT_HFSC:
286                 print_hfscstats(a->qstats);
287                 break;
288         case ALTQT_FAIRQ:
289                 print_fairqstats(a->qstats);
290                 break;
291         }
292 }
293
294 void
295 print_cbqstats(struct queue_stats cur)
296 {
297         printf("  [ pkts: %10llu  bytes: %10llu  "
298             "dropped pkts: %6llu bytes: %6llu ]\n",
299             (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets,
300             (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes,
301             (unsigned long long)cur.data.cbq_stats.drop_cnt.packets,
302             (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes);
303         printf("  [ qlength: %3d/%3d  borrows: %6u  suspends: %6u ]\n",
304             cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax,
305             cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays);
306
307         if (cur.avgn < 2)
308                 return;
309
310         printf("  [ measured: %7.1f packets/s, %s/s ]\n",
311             cur.avg_packets / STAT_INTERVAL,
312             rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
313 }
314
315 void
316 print_priqstats(struct queue_stats cur)
317 {
318         printf("  [ pkts: %10llu  bytes: %10llu  "
319             "dropped pkts: %6llu bytes: %6llu ]\n",
320             (unsigned long long)cur.data.priq_stats.xmitcnt.packets,
321             (unsigned long long)cur.data.priq_stats.xmitcnt.bytes,
322             (unsigned long long)cur.data.priq_stats.dropcnt.packets,
323             (unsigned long long)cur.data.priq_stats.dropcnt.bytes);
324         printf("  [ qlength: %3d/%3d ]\n",
325             cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit);
326
327         if (cur.avgn < 2)
328                 return;
329
330         printf("  [ measured: %7.1f packets/s, %s/s ]\n",
331             cur.avg_packets / STAT_INTERVAL,
332             rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
333 }
334
335 void
336 print_hfscstats(struct queue_stats cur)
337 {
338         printf("  [ pkts: %10llu  bytes: %10llu  "
339             "dropped pkts: %6llu bytes: %6llu ]\n",
340             (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets,
341             (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes,
342             (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets,
343             (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes);
344         printf("  [ qlength: %3d/%3d ]\n",
345             cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit);
346
347         if (cur.avgn < 2)
348                 return;
349
350         printf("  [ measured: %7.1f packets/s, %s/s ]\n",
351             cur.avg_packets / STAT_INTERVAL,
352             rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
353 }
354
355 void
356 print_fairqstats(struct queue_stats cur)
357 {
358         printf("  [ pkts: %10llu  bytes: %10llu  "
359             "dropped pkts: %6llu bytes: %6llu ]\n",
360             (unsigned long long)cur.data.fairq_stats.xmit_cnt.packets,
361             (unsigned long long)cur.data.fairq_stats.xmit_cnt.bytes,
362             (unsigned long long)cur.data.fairq_stats.drop_cnt.packets,
363             (unsigned long long)cur.data.fairq_stats.drop_cnt.bytes);
364         printf("  [ qlength: %3d/%3d ]\n",
365             cur.data.fairq_stats.qlength, cur.data.fairq_stats.qlimit);
366
367         if (cur.avgn < 2)
368                 return;
369
370         printf("  [ measured: %7.1f packets/s, %s/s ]\n",
371             cur.avg_packets / STAT_INTERVAL,
372             rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
373 }
374
375
376 void
377 pfctl_free_altq_node(struct pf_altq_node *node)
378 {
379         while (node != NULL) {
380                 struct pf_altq_node     *prev;
381
382                 if (node->children != NULL)
383                         pfctl_free_altq_node(node->children);
384                 prev = node;
385                 node = node->next;
386                 free(prev);
387         }
388 }
389
390 void
391 update_avg(struct pf_altq_node *a)
392 {
393         struct queue_stats      *qs;
394         u_int64_t                b, p;
395         int                      n;
396
397         if (a->altq.qid == 0)
398                 return;
399
400         qs = &a->qstats;
401         n = qs->avgn;
402
403         switch (a->altq.scheduler) {
404         case ALTQT_CBQ:
405                 b = qs->data.cbq_stats.xmit_cnt.bytes;
406                 p = qs->data.cbq_stats.xmit_cnt.packets;
407                 break;
408         case ALTQT_PRIQ:
409                 b = qs->data.priq_stats.xmitcnt.bytes;
410                 p = qs->data.priq_stats.xmitcnt.packets;
411                 break;
412         case ALTQT_HFSC:
413                 b = qs->data.hfsc_stats.xmit_cnt.bytes;
414                 p = qs->data.hfsc_stats.xmit_cnt.packets;
415                 break;
416         case ALTQT_FAIRQ:
417                 b = qs->data.fairq_stats.xmit_cnt.bytes;
418                 p = qs->data.fairq_stats.xmit_cnt.packets;
419                 break;
420         default:
421                 b = 0;
422                 p = 0;
423                 break;
424         }
425
426         if (n == 0) {
427                 qs->prev_bytes = b;
428                 qs->prev_packets = p;
429                 qs->avgn++;
430                 return;
431         }
432
433         if (b >= qs->prev_bytes)
434                 qs->avg_bytes = ((qs->avg_bytes * (n - 1)) +
435                     (b - qs->prev_bytes)) / n;
436
437         if (p >= qs->prev_packets)
438                 qs->avg_packets = ((qs->avg_packets * (n - 1)) +
439                     (p - qs->prev_packets)) / n;
440
441         qs->prev_bytes = b;
442         qs->prev_packets = p;
443         if (n < AVGN_MAX)
444                 qs->avgn++;
445 }