]> git.saurik.com Git - apple/xnu.git/blame - bsd/net/classq/classq_subr.c
xnu-7195.81.3.tar.gz
[apple/xnu.git] / bsd / net / classq / classq_subr.c
CommitLineData
316670eb 1/*
f427ee49 2 * Copyright (c) 2011-2020 Apple Inc. All rights reserved.
316670eb
A
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <sys/cdefs.h>
30#include <sys/param.h>
31#include <sys/mbuf.h>
32#include <sys/errno.h>
33#include <sys/random.h>
34#include <sys/kernel_types.h>
35#include <sys/sysctl.h>
36
37#include <kern/zalloc.h>
38
39#include <net/if.h>
40#include <net/net_osdep.h>
41#include <net/classq/classq.h>
39037602 42#include <pexpert/pexpert.h>
316670eb 43#include <net/classq/classq_sfb.h>
a39ff7e2 44#include <net/classq/classq_fq_codel.h>
316670eb 45#include <net/pktsched/pktsched.h>
39037602 46#include <net/pktsched/pktsched_fq_codel.h>
f427ee49 47#include <net/flowadv.h>
316670eb
A
48
49#include <libkern/libkern.h>
50
316670eb
A
51
52static errno_t ifclassq_dequeue_common(struct ifclassq *, mbuf_svc_class_t,
cb323159
A
53 u_int32_t, u_int32_t, classq_pkt_t *, classq_pkt_t *, u_int32_t *,
54 u_int32_t *, boolean_t);
55static void ifclassq_tbr_dequeue_common(struct ifclassq *, mbuf_svc_class_t,
56 boolean_t, classq_pkt_t *);
316670eb 57
39037602 58static u_int64_t ifclassq_target_qdelay = 0;
0a7de745 59SYSCTL_QUAD(_net_classq, OID_AUTO, target_qdelay, CTLFLAG_RW | CTLFLAG_LOCKED,
39037602
A
60 &ifclassq_target_qdelay, "target queue delay in nanoseconds");
61
62static u_int64_t ifclassq_update_interval = 0;
63SYSCTL_QUAD(_net_classq, OID_AUTO, update_interval,
0a7de745 64 CTLFLAG_RW | CTLFLAG_LOCKED, &ifclassq_update_interval,
39037602
A
65 "update interval in nanoseconds");
66
316670eb
A
67void
68classq_init(void)
69{
70 _CASSERT(MBUF_TC_BE == 0);
71 _CASSERT(MBUF_SC_BE == 0);
72 _CASSERT(IFCQ_SC_MAX == MBUF_SC_MAX_CLASSES);
73
f427ee49 74 fq_codel_init();
316670eb
A
75}
76
77int
78ifclassq_setup(struct ifnet *ifp, u_int32_t sflags, boolean_t reuse)
79{
80#pragma unused(reuse)
81 struct ifclassq *ifq = &ifp->if_snd;
82 int err = 0;
83
84 IFCQ_LOCK(ifq);
85 VERIFY(IFCQ_IS_EMPTY(ifq));
86 ifq->ifcq_ifp = ifp;
87 IFCQ_LEN(ifq) = 0;
3e170ce0 88 IFCQ_BYTES(ifq) = 0;
0a7de745
A
89 bzero(&ifq->ifcq_xmitcnt, sizeof(ifq->ifcq_xmitcnt));
90 bzero(&ifq->ifcq_dropcnt, sizeof(ifq->ifcq_dropcnt));
316670eb
A
91
92 VERIFY(!IFCQ_TBR_IS_ENABLED(ifq));
93 VERIFY(ifq->ifcq_type == PKTSCHEDT_NONE);
94 VERIFY(ifq->ifcq_flags == 0);
95 VERIFY(ifq->ifcq_sflags == 0);
96 VERIFY(ifq->ifcq_disc == NULL);
316670eb
A
97
98 if (ifp->if_eflags & IFEF_TXSTART) {
99 u_int32_t maxlen = 0;
100
0a7de745 101 if ((maxlen = IFCQ_MAXLEN(ifq)) == 0) {
316670eb 102 maxlen = if_sndq_maxlen;
0a7de745 103 }
316670eb
A
104 IFCQ_SET_MAXLEN(ifq, maxlen);
105
fe8ab488
A
106 if (IFCQ_MAXLEN(ifq) != if_sndq_maxlen &&
107 IFCQ_TARGET_QDELAY(ifq) == 0) {
108 /*
109 * Choose static queues because the interface has
110 * maximum queue size set
111 */
112 sflags &= ~PKTSCHEDF_QALG_DELAYBASED;
113 }
316670eb
A
114 ifq->ifcq_sflags = sflags;
115 err = ifclassq_pktsched_setup(ifq);
0a7de745 116 if (err == 0) {
316670eb 117 ifq->ifcq_flags = (IFCQF_READY | IFCQF_ENABLED);
0a7de745 118 }
316670eb 119 }
316670eb 120 IFCQ_UNLOCK(ifq);
0a7de745 121 return err;
316670eb
A
122}
123
124void
125ifclassq_teardown(struct ifnet *ifp)
126{
127 struct ifclassq *ifq = &ifp->if_snd;
128
129 IFCQ_LOCK(ifq);
316670eb
A
130
131 if (IFCQ_IS_READY(ifq)) {
132 if (IFCQ_TBR_IS_ENABLED(ifq)) {
cb323159 133 struct tb_profile tb = { .rate = 0, .percent = 0, .depth = 0 };
316670eb
A
134 (void) ifclassq_tbr_set(ifq, &tb, FALSE);
135 }
f427ee49 136 pktsched_teardown(ifq);
316670eb
A
137 ifq->ifcq_flags = 0;
138 }
139 ifq->ifcq_sflags = 0;
140
141 VERIFY(IFCQ_IS_EMPTY(ifq));
142 VERIFY(!IFCQ_TBR_IS_ENABLED(ifq));
143 VERIFY(ifq->ifcq_type == PKTSCHEDT_NONE);
144 VERIFY(ifq->ifcq_flags == 0);
145 VERIFY(ifq->ifcq_sflags == 0);
146 VERIFY(ifq->ifcq_disc == NULL);
316670eb 147 IFCQ_LEN(ifq) = 0;
3e170ce0 148 IFCQ_BYTES(ifq) = 0;
316670eb 149 IFCQ_MAXLEN(ifq) = 0;
0a7de745
A
150 bzero(&ifq->ifcq_xmitcnt, sizeof(ifq->ifcq_xmitcnt));
151 bzero(&ifq->ifcq_dropcnt, sizeof(ifq->ifcq_dropcnt));
316670eb
A
152
153 IFCQ_UNLOCK(ifq);
154}
155
156int
157ifclassq_pktsched_setup(struct ifclassq *ifq)
158{
159 struct ifnet *ifp = ifq->ifcq_ifp;
5ba3f43e 160 classq_pkt_type_t ptype = QP_MBUF;
316670eb
A
161 int err = 0;
162
163 IFCQ_LOCK_ASSERT_HELD(ifq);
164 VERIFY(ifp->if_eflags & IFEF_TXSTART);
165
f427ee49 166 err = pktsched_setup(ifq, PKTSCHEDT_FQ_CODEL, ifq->ifcq_sflags, ptype);
316670eb 167
0a7de745 168 return err;
316670eb
A
169}
170
171void
172ifclassq_set_maxlen(struct ifclassq *ifq, u_int32_t maxqlen)
173{
174 IFCQ_LOCK(ifq);
0a7de745 175 if (maxqlen == 0) {
316670eb 176 maxqlen = if_sndq_maxlen;
0a7de745 177 }
316670eb
A
178 IFCQ_SET_MAXLEN(ifq, maxqlen);
179 IFCQ_UNLOCK(ifq);
180}
181
182u_int32_t
183ifclassq_get_maxlen(struct ifclassq *ifq)
184{
0a7de745 185 return IFCQ_MAXLEN(ifq);
316670eb
A
186}
187
39236c6e
A
188int
189ifclassq_get_len(struct ifclassq *ifq, mbuf_svc_class_t sc, u_int32_t *packets,
190 u_int32_t *bytes)
316670eb 191{
39236c6e
A
192 int err = 0;
193
194 IFCQ_LOCK(ifq);
195 if (sc == MBUF_SC_UNSPEC) {
196 VERIFY(packets != NULL);
197 *packets = IFCQ_LEN(ifq);
198 } else {
f427ee49
A
199 cqrq_stat_sc_t req = { sc, 0, 0 };
200
39236c6e
A
201 VERIFY(MBUF_VALID_SC(sc));
202 VERIFY(packets != NULL && bytes != NULL);
f427ee49
A
203
204 err = fq_if_request_classq(ifq, CLASSQRQ_STAT_SC, &req);
205 if (packets != NULL) {
206 *packets = req.packets;
207 }
208 if (bytes != NULL) {
209 *bytes = req.bytes;
210 }
39236c6e
A
211 }
212 IFCQ_UNLOCK(ifq);
213
cb323159 214
0a7de745 215 return err;
316670eb
A
216}
217
5ba3f43e
A
218inline void
219ifclassq_set_packet_metadata(struct ifclassq *ifq, struct ifnet *ifp,
cb323159 220 classq_pkt_t *p)
5ba3f43e 221{
0a7de745 222 if (!IFNET_IS_CELLULAR(ifp)) {
5ba3f43e 223 return;
0a7de745 224 }
5ba3f43e 225
cb323159 226 switch (p->cp_ptype) {
5ba3f43e 227 case QP_MBUF: {
cb323159 228 struct mbuf *m = p->cp_mbuf;
5ba3f43e
A
229 m->m_pkthdr.pkt_flags |= PKTF_VALID_UNSENT_DATA;
230 m->m_pkthdr.bufstatus_if = IFCQ_BYTES(ifq);
f427ee49 231 m->m_pkthdr.bufstatus_sndbuf = (uint32_t)ifp->if_sndbyte_unsent;
5ba3f43e
A
232 break;
233 }
234
235
236 default:
237 VERIFY(0);
238 /* NOTREACHED */
cb323159 239 __builtin_unreachable();
5ba3f43e
A
240 }
241}
242
316670eb 243errno_t
f427ee49
A
244ifclassq_enqueue(struct ifclassq *ifq, classq_pkt_t *head, classq_pkt_t *tail,
245 u_int32_t cnt, u_int32_t bytes, boolean_t *pdrop)
316670eb 246{
f427ee49 247 return fq_if_enqueue_classq(ifq, head, tail, cnt, bytes, pdrop);
316670eb
A
248}
249
250errno_t
39037602 251ifclassq_dequeue(struct ifclassq *ifq, u_int32_t pkt_limit,
cb323159
A
252 u_int32_t byte_limit, classq_pkt_t *head, classq_pkt_t *tail,
253 u_int32_t *cnt, u_int32_t *len)
316670eb 254{
0a7de745 255 return ifclassq_dequeue_common(ifq, MBUF_SC_UNSPEC, pkt_limit,
cb323159 256 byte_limit, head, tail, cnt, len, FALSE);
316670eb
A
257}
258
259errno_t
260ifclassq_dequeue_sc(struct ifclassq *ifq, mbuf_svc_class_t sc,
cb323159
A
261 u_int32_t pkt_limit, u_int32_t byte_limit, classq_pkt_t *head,
262 classq_pkt_t *tail, u_int32_t *cnt, u_int32_t *len)
316670eb 263{
0a7de745 264 return ifclassq_dequeue_common(ifq, sc, pkt_limit, byte_limit,
cb323159 265 head, tail, cnt, len, TRUE);
316670eb
A
266}
267
268static errno_t
cb323159
A
269ifclassq_dequeue_common_default(struct ifclassq *ifq, mbuf_svc_class_t sc,
270 u_int32_t pkt_limit, u_int32_t byte_limit, classq_pkt_t *head,
271 classq_pkt_t *tail, u_int32_t *cnt, u_int32_t *len, boolean_t drvmgt)
316670eb
A
272{
273 struct ifnet *ifp = ifq->ifcq_ifp;
f427ee49 274 u_int32_t i = 0, l = 0;
cb323159
A
275 classq_pkt_t first = CLASSQ_PKT_INITIALIZER(first);
276 classq_pkt_t last = CLASSQ_PKT_INITIALIZER(last);
316670eb
A
277
278 VERIFY(!drvmgt || MBUF_VALID_SC(sc));
279
0a7de745 280 if (IFCQ_TBR_IS_ENABLED(ifq)) {
5ba3f43e 281 goto dequeue_loop;
0a7de745 282 }
5ba3f43e 283
39037602
A
284 /*
285 * If the scheduler support dequeueing multiple packets at the
286 * same time, call that one instead.
287 */
f427ee49 288 if (drvmgt) {
5ba3f43e 289 int err;
39037602 290
f427ee49
A
291 IFCQ_LOCK_SPIN(ifq);
292 err = fq_if_dequeue_sc_classq_multi(ifq, sc, pkt_limit,
cb323159 293 byte_limit, head, tail, cnt, len);
5ba3f43e
A
294 IFCQ_UNLOCK(ifq);
295
cb323159 296 if (err == 0 && head->cp_mbuf == NULL) {
5ba3f43e 297 err = EAGAIN;
0a7de745
A
298 }
299 return err;
f427ee49 300 } else {
39037602 301 int err;
5ba3f43e 302
f427ee49
A
303 IFCQ_LOCK_SPIN(ifq);
304 err = fq_if_dequeue_classq_multi(ifq, pkt_limit, byte_limit,
cb323159 305 head, tail, cnt, len);
39037602
A
306 IFCQ_UNLOCK(ifq);
307
cb323159 308 if (err == 0 && head->cp_mbuf == NULL) {
39037602 309 err = EAGAIN;
0a7de745
A
310 }
311 return err;
39037602
A
312 }
313
5ba3f43e 314dequeue_loop:
316670eb 315
f427ee49 316 IFCQ_LOCK_SPIN(ifq);
316670eb 317
39037602 318 while (i < pkt_limit && l < byte_limit) {
316670eb 319 if (drvmgt) {
0a7de745 320 if (IFCQ_TBR_IS_ENABLED(ifq)) {
cb323159 321 IFCQ_TBR_DEQUEUE_SC(ifq, sc, head);
0a7de745 322 } else {
f427ee49 323 fq_if_dequeue_sc_classq(ifq, sc, head);
0a7de745 324 }
316670eb 325 } else {
0a7de745 326 if (IFCQ_TBR_IS_ENABLED(ifq)) {
cb323159 327 IFCQ_TBR_DEQUEUE(ifq, head);
0a7de745 328 } else {
f427ee49 329 fq_if_dequeue_classq(ifq, head);
0a7de745 330 }
316670eb 331 }
316670eb 332
cb323159 333 if (head->cp_mbuf == NULL) {
316670eb 334 break;
0a7de745 335 }
316670eb 336
cb323159
A
337 if (first.cp_mbuf == NULL) {
338 first = *head;
339 }
340
341 switch (head->cp_ptype) {
5ba3f43e 342 case QP_MBUF:
cb323159
A
343 head->cp_mbuf->m_nextpkt = NULL;
344 l += head->cp_mbuf->m_pkthdr.len;
345 ifclassq_set_packet_metadata(ifq, ifp, head);
346 if (last.cp_mbuf != NULL) {
347 last.cp_mbuf->m_nextpkt = head->cp_mbuf;
348 }
5ba3f43e 349 break;
316670eb 350
316670eb 351
5ba3f43e
A
352 default:
353 VERIFY(0);
354 /* NOTREACHED */
cb323159 355 __builtin_unreachable();
3e170ce0 356 }
5ba3f43e 357
cb323159 358 last = *head;
316670eb
A
359 i++;
360 }
361
362 IFCQ_UNLOCK(ifq);
363
0a7de745 364 if (tail != NULL) {
316670eb 365 *tail = last;
0a7de745
A
366 }
367 if (cnt != NULL) {
316670eb 368 *cnt = i;
0a7de745
A
369 }
370 if (len != NULL) {
316670eb 371 *len = l;
0a7de745 372 }
316670eb 373
cb323159
A
374 *head = first;
375 return (first.cp_mbuf != NULL) ? 0 : EAGAIN;
376}
377
378static errno_t
379ifclassq_dequeue_common(struct ifclassq *ifq, mbuf_svc_class_t sc,
380 u_int32_t pkt_limit, u_int32_t byte_limit, classq_pkt_t *head,
381 classq_pkt_t *tail, u_int32_t *cnt, u_int32_t *len, boolean_t drvmgt)
382{
383 return ifclassq_dequeue_common_default(ifq, sc,
384 pkt_limit, byte_limit, head, tail, cnt, len, drvmgt);
316670eb
A
385}
386
316670eb
A
387void
388ifclassq_update(struct ifclassq *ifq, cqev_t ev)
389{
390 IFCQ_LOCK_ASSERT_HELD(ifq);
391 VERIFY(IFCQ_IS_READY(ifq));
f427ee49 392 fq_if_request_classq(ifq, CLASSQRQ_EVENT, (void *)ev);
316670eb
A
393}
394
395int
f427ee49 396ifclassq_attach(struct ifclassq *ifq, u_int32_t type, void *discipline)
316670eb
A
397{
398 IFCQ_LOCK_ASSERT_HELD(ifq);
399
400 VERIFY(ifq->ifcq_disc == NULL);
316670eb
A
401
402 ifq->ifcq_type = type;
403 ifq->ifcq_disc = discipline;
316670eb 404
0a7de745 405 return 0;
316670eb
A
406}
407
f427ee49 408void
316670eb
A
409ifclassq_detach(struct ifclassq *ifq)
410{
411 IFCQ_LOCK_ASSERT_HELD(ifq);
412
413 VERIFY(ifq->ifcq_disc == NULL);
414
415 ifq->ifcq_type = PKTSCHEDT_NONE;
316670eb
A
416}
417
418int
419ifclassq_getqstats(struct ifclassq *ifq, u_int32_t qid, void *ubuf,
420 u_int32_t *nbytes)
421{
422 struct if_ifclassq_stats *ifqs;
423 int err;
424
0a7de745
A
425 if (*nbytes < sizeof(*ifqs)) {
426 return EINVAL;
427 }
316670eb 428
0a7de745
A
429 ifqs = _MALLOC(sizeof(*ifqs), M_TEMP, M_WAITOK | M_ZERO);
430 if (ifqs == NULL) {
431 return ENOMEM;
432 }
316670eb
A
433
434 IFCQ_LOCK(ifq);
435 if (!IFCQ_IS_READY(ifq)) {
436 IFCQ_UNLOCK(ifq);
437 _FREE(ifqs, M_TEMP);
0a7de745 438 return ENXIO;
316670eb
A
439 }
440
441 ifqs->ifqs_len = IFCQ_LEN(ifq);
442 ifqs->ifqs_maxlen = IFCQ_MAXLEN(ifq);
443 *(&ifqs->ifqs_xmitcnt) = *(&ifq->ifcq_xmitcnt);
444 *(&ifqs->ifqs_dropcnt) = *(&ifq->ifcq_dropcnt);
445 ifqs->ifqs_scheduler = ifq->ifcq_type;
446
447 err = pktsched_getqstats(ifq, qid, ifqs);
448 IFCQ_UNLOCK(ifq);
449
450 if (err == 0 && (err = copyout((caddr_t)ifqs,
0a7de745
A
451 (user_addr_t)(uintptr_t)ubuf, sizeof(*ifqs))) == 0) {
452 *nbytes = sizeof(*ifqs);
453 }
316670eb
A
454
455 _FREE(ifqs, M_TEMP);
456
0a7de745 457 return err;
316670eb
A
458}
459
460const char *
461ifclassq_ev2str(cqev_t ev)
462{
463 const char *c;
464
465 switch (ev) {
39236c6e
A
466 case CLASSQ_EV_LINK_BANDWIDTH:
467 c = "LINK_BANDWIDTH";
468 break;
469
470 case CLASSQ_EV_LINK_LATENCY:
471 c = "LINK_LATENCY";
316670eb
A
472 break;
473
474 case CLASSQ_EV_LINK_MTU:
475 c = "LINK_MTU";
476 break;
477
478 case CLASSQ_EV_LINK_UP:
479 c = "LINK_UP";
480 break;
481
482 case CLASSQ_EV_LINK_DOWN:
483 c = "LINK_DOWN";
484 break;
485
486 default:
487 c = "UNKNOWN";
488 break;
489 }
490
0a7de745 491 return c;
316670eb
A
492}
493
494/*
495 * internal representation of token bucket parameters
496 * rate: byte_per_unittime << 32
497 * (((bits_per_sec) / 8) << 32) / machclk_freq
498 * depth: byte << 32
499 *
500 */
0a7de745
A
501#define TBR_SHIFT 32
502#define TBR_SCALE(x) ((int64_t)(x) << TBR_SHIFT)
503#define TBR_UNSCALE(x) ((x) >> TBR_SHIFT)
316670eb 504
cb323159
A
505void
506ifclassq_tbr_dequeue(struct ifclassq *ifq, classq_pkt_t *pkt)
316670eb 507{
cb323159 508 ifclassq_tbr_dequeue_common(ifq, MBUF_SC_UNSPEC, FALSE, pkt);
316670eb
A
509}
510
cb323159 511void
5ba3f43e 512ifclassq_tbr_dequeue_sc(struct ifclassq *ifq, mbuf_svc_class_t sc,
cb323159 513 classq_pkt_t *pkt)
316670eb 514{
cb323159 515 ifclassq_tbr_dequeue_common(ifq, sc, TRUE, pkt);
316670eb
A
516}
517
cb323159 518static void
5ba3f43e 519ifclassq_tbr_dequeue_common(struct ifclassq *ifq, mbuf_svc_class_t sc,
cb323159 520 boolean_t drvmgt, classq_pkt_t *pkt)
316670eb
A
521{
522 struct tb_regulator *tbr;
316670eb
A
523 int64_t interval;
524 u_int64_t now;
525
526 IFCQ_LOCK_ASSERT_HELD(ifq);
527
528 VERIFY(!drvmgt || MBUF_VALID_SC(sc));
529 VERIFY(IFCQ_TBR_IS_ENABLED(ifq));
530
cb323159 531 *pkt = CLASSQ_PKT_INITIALIZER(*pkt);
316670eb 532 tbr = &ifq->ifcq_tbr;
5ba3f43e
A
533 /* update token only when it is negative */
534 if (tbr->tbr_token <= 0) {
535 now = read_machclk();
536 interval = now - tbr->tbr_last;
537 if (interval >= tbr->tbr_filluptime) {
538 tbr->tbr_token = tbr->tbr_depth;
539 } else {
540 tbr->tbr_token += interval * tbr->tbr_rate;
0a7de745 541 if (tbr->tbr_token > tbr->tbr_depth) {
316670eb 542 tbr->tbr_token = tbr->tbr_depth;
0a7de745 543 }
316670eb 544 }
5ba3f43e 545 tbr->tbr_last = now;
316670eb 546 }
5ba3f43e 547 /* if token is still negative, don't allow dequeue */
0a7de745 548 if (tbr->tbr_token <= 0) {
cb323159 549 return;
0a7de745 550 }
316670eb
A
551
552 /*
553 * ifclassq takes precedence over ALTQ queue;
554 * ifcq_drain count is adjusted by the caller.
555 */
0a7de745 556 if (drvmgt) {
f427ee49 557 fq_if_dequeue_sc_classq(ifq, sc, pkt);
0a7de745 558 } else {
f427ee49 559 fq_if_dequeue_classq(ifq, pkt);
0a7de745 560 }
5ba3f43e 561
cb323159
A
562 if (pkt->cp_mbuf != NULL) {
563 switch (pkt->cp_ptype) {
5ba3f43e 564 case QP_MBUF:
cb323159 565 tbr->tbr_token -= TBR_SCALE(m_pktlen(pkt->cp_mbuf));
5ba3f43e
A
566 break;
567
568
569 default:
570 VERIFY(0);
571 /* NOTREACHED */
316670eb
A
572 }
573 }
316670eb
A
574}
575
576/*
577 * set a token bucket regulator.
578 * if the specified rate is zero, the token bucket regulator is deleted.
579 */
580int
581ifclassq_tbr_set(struct ifclassq *ifq, struct tb_profile *profile,
582 boolean_t update)
583{
584 struct tb_regulator *tbr;
585 struct ifnet *ifp = ifq->ifcq_ifp;
586 u_int64_t rate, old_rate;
587
588 IFCQ_LOCK_ASSERT_HELD(ifq);
589 VERIFY(IFCQ_IS_READY(ifq));
590
591 VERIFY(machclk_freq != 0);
592
593 tbr = &ifq->ifcq_tbr;
594 old_rate = tbr->tbr_rate_raw;
595
596 rate = profile->rate;
597 if (profile->percent > 0) {
598 u_int64_t eff_rate;
599
0a7de745
A
600 if (profile->percent > 100) {
601 return EINVAL;
602 }
603 if ((eff_rate = ifp->if_output_bw.eff_bw) == 0) {
604 return ENODEV;
605 }
316670eb
A
606 rate = (eff_rate * profile->percent) / 100;
607 }
608
609 if (rate == 0) {
0a7de745 610 if (!IFCQ_TBR_IS_ENABLED(ifq)) {
cb323159 611 return 0;
0a7de745 612 }
316670eb 613
0a7de745 614 if (pktsched_verbose) {
316670eb 615 printf("%s: TBR disabled\n", if_name(ifp));
0a7de745 616 }
316670eb
A
617
618 /* disable this TBR */
619 ifq->ifcq_flags &= ~IFCQF_TBR;
0a7de745 620 bzero(tbr, sizeof(*tbr));
316670eb 621 ifnet_set_start_cycle(ifp, NULL);
0a7de745 622 if (update) {
39236c6e 623 ifclassq_update(ifq, CLASSQ_EV_LINK_BANDWIDTH);
0a7de745
A
624 }
625 return 0;
316670eb
A
626 }
627
628 if (pktsched_verbose) {
629 printf("%s: TBR %s (rate %llu bps depth %u)\n", if_name(ifp),
630 (ifq->ifcq_flags & IFCQF_TBR) ? "reconfigured" :
631 "enabled", rate, profile->depth);
632 }
633
634 /* set the new TBR */
0a7de745 635 bzero(tbr, sizeof(*tbr));
316670eb
A
636 tbr->tbr_rate_raw = rate;
637 tbr->tbr_percent = profile->percent;
638 ifq->ifcq_flags |= IFCQF_TBR;
639
640 /*
641 * Note that the TBR fill up time (hence the ifnet restart time)
642 * is directly related to the specified TBR depth. The ideal
643 * depth value should be computed such that the interval time
644 * between each successive wakeup is adequately spaced apart,
645 * in order to reduce scheduling overheads. A target interval
646 * of 10 ms seems to provide good performance balance. This can be
647 * overridden by specifying the depth profile. Values smaller than
648 * the ideal depth will reduce delay at the expense of CPU cycles.
649 */
650 tbr->tbr_rate = TBR_SCALE(rate / 8) / machclk_freq;
651 if (tbr->tbr_rate > 0) {
652 u_int32_t mtu = ifp->if_mtu;
653 int64_t ival, idepth = 0;
654 int i;
655
0a7de745 656 if (mtu < IF_MINMTU) {
316670eb 657 mtu = IF_MINMTU;
0a7de745 658 }
316670eb
A
659
660 ival = pktsched_nsecs_to_abstime(10 * NSEC_PER_MSEC); /* 10ms */
661
0a7de745 662 for (i = 1;; i++) {
316670eb 663 idepth = TBR_SCALE(i * mtu);
0a7de745 664 if ((idepth / tbr->tbr_rate) > ival) {
316670eb 665 break;
0a7de745 666 }
316670eb
A
667 }
668 VERIFY(idepth > 0);
669
670 tbr->tbr_depth = TBR_SCALE(profile->depth);
671 if (tbr->tbr_depth == 0) {
672 tbr->tbr_filluptime = idepth / tbr->tbr_rate;
673 /* a little fudge factor to get closer to rate */
674 tbr->tbr_depth = idepth + (idepth >> 3);
675 } else {
676 tbr->tbr_filluptime = tbr->tbr_depth / tbr->tbr_rate;
677 }
678 } else {
679 tbr->tbr_depth = TBR_SCALE(profile->depth);
680 tbr->tbr_filluptime = 0xffffffffffffffffLL;
681 }
682 tbr->tbr_token = tbr->tbr_depth;
683 tbr->tbr_last = read_machclk();
316670eb
A
684
685 if (tbr->tbr_rate > 0 && (ifp->if_flags & IFF_UP)) {
686 struct timespec ts =
f427ee49 687 { 0, (long)pktsched_abs_to_nsecs(tbr->tbr_filluptime) };
316670eb
A
688 if (pktsched_verbose) {
689 printf("%s: TBR calculated tokens %lld "
690 "filluptime %llu ns\n", if_name(ifp),
691 TBR_UNSCALE(tbr->tbr_token),
692 pktsched_abs_to_nsecs(tbr->tbr_filluptime));
693 }
694 ifnet_set_start_cycle(ifp, &ts);
695 } else {
696 if (pktsched_verbose) {
697 if (tbr->tbr_rate == 0) {
698 printf("%s: TBR calculated tokens %lld "
699 "infinite filluptime\n", if_name(ifp),
700 TBR_UNSCALE(tbr->tbr_token));
701 } else if (!(ifp->if_flags & IFF_UP)) {
702 printf("%s: TBR suspended (link is down)\n",
703 if_name(ifp));
704 }
705 }
706 ifnet_set_start_cycle(ifp, NULL);
707 }
0a7de745 708 if (update && tbr->tbr_rate_raw != old_rate) {
39236c6e 709 ifclassq_update(ifq, CLASSQ_EV_LINK_BANDWIDTH);
0a7de745 710 }
316670eb 711
0a7de745 712 return 0;
316670eb 713}
39037602
A
714
715void
716ifclassq_calc_target_qdelay(struct ifnet *ifp, u_int64_t *if_target_qdelay)
717{
5ba3f43e
A
718 u_int64_t qdelay = 0;
719 qdelay = IFCQ_TARGET_QDELAY(&ifp->if_snd);
39037602 720
0a7de745 721 if (ifclassq_target_qdelay != 0) {
5ba3f43e 722 qdelay = ifclassq_target_qdelay;
0a7de745 723 }
39037602
A
724
725 /*
726 * If we do not know the effective bandwidth, use the default
727 * target queue delay.
728 */
0a7de745 729 if (qdelay == 0) {
5ba3f43e 730 qdelay = IFQ_TARGET_DELAY;
0a7de745 731 }
39037602
A
732
733 /*
734 * If a delay has been added to ifnet start callback for
735 * coalescing, we have to add that to the pre-set target delay
736 * because the packets can be in the queue longer.
737 */
738 if ((ifp->if_eflags & IFEF_ENQUEUE_MULTI) &&
0a7de745 739 ifp->if_start_delay_timeout > 0) {
5ba3f43e 740 qdelay += ifp->if_start_delay_timeout;
0a7de745 741 }
39037602 742
5ba3f43e 743 *(if_target_qdelay) = qdelay;
39037602
A
744}
745
746void
747ifclassq_calc_update_interval(u_int64_t *update_interval)
748{
749 u_int64_t uint = 0;
750
751 /* If the system level override is set, use it */
0a7de745 752 if (ifclassq_update_interval != 0) {
39037602 753 uint = ifclassq_update_interval;
0a7de745 754 }
39037602
A
755
756 /* Otherwise use the default value */
0a7de745 757 if (uint == 0) {
39037602 758 uint = IFQ_UPDATE_INTERVAL;
0a7de745 759 }
39037602
A
760
761 *update_interval = uint;
762}
a39ff7e2
A
763
764void
765ifclassq_reap_caches(boolean_t purge)
766{
767 fq_codel_reap_caches(purge);
f427ee49 768 flowadv_reap_caches(purge);
a39ff7e2 769}