1/*
2 * Copyright (c) 2011 Apple Inc. All rights reserved.
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
31#include <sys/param.h>
32#include <sys/malloc.h>
33#include <sys/mbuf.h>
34#include <sys/systm.h>
35#include <sys/kernel.h>
36#include <sys/errno.h>
37#include <sys/mcache.h>
38#include <sys/sysctl.h>
39
40#include <net/if.h>
41#include <net/if_var.h>
42#include <net/if_dl.h>
43#include <net/if_types.h>
44#include <net/net_osdep.h>
45#include <net/pktsched/pktsched.h>
46#include <net/pktsched/pktsched_tcq.h>
47#include <net/pktsched/pktsched_qfq.h>
48#if PKTSCHED_PRIQ
49#include <net/pktsched/pktsched_priq.h>
50#endif /* PKTSCHED_PRIQ */
51#if PKTSCHED_FAIRQ
52#include <net/pktsched/pktsched_fairq.h>
53#endif /* PKTSCHED_FAIRQ */
54#if PKTSCHED_CBQ
55#include <net/pktsched/pktsched_cbq.h>
56#endif /* PKTSCHED_CBQ */
57#if PKTSCHED_HFSC
58#include <net/pktsched/pktsched_hfsc.h>
59#endif /* PKTSCHED_HFSC */
60
61#include <pexpert/pexpert.h>
62
63u_int32_t machclk_freq = 0;
64u_int64_t machclk_per_sec = 0;
65u_int32_t pktsched_verbose;	/* more noise if greater than 1 */
66
67static void init_machclk(void);
68
69SYSCTL_NODE(_net, OID_AUTO, pktsched, CTLFLAG_RW|CTLFLAG_LOCKED, 0, "pktsched");
70
71SYSCTL_UINT(_net_pktsched, OID_AUTO, verbose, CTLFLAG_RW|CTLFLAG_LOCKED,
72	&pktsched_verbose, 0, "Packet scheduler verbosity level");
73
74void
75pktsched_init(void)
76{
77	init_machclk();
78	if (machclk_freq == 0) {
79		panic("%s: no CPU clock available!\n", __func__);
80		/* NOTREACHED */
81	}
82
83	tcq_init();
84	qfq_init();
85#if PKTSCHED_PRIQ
86	priq_init();
87#endif /* PKTSCHED_PRIQ */
88#if PKTSCHED_FAIRQ
89	fairq_init();
90#endif /* PKTSCHED_FAIRQ */
91#if PKTSCHED_CBQ
92	cbq_init();
93#endif /* PKTSCHED_CBQ */
94#if PKTSCHED_HFSC
95	hfsc_init();
96#endif /* PKTSCHED_HFSC */
97}
98
99static void
100init_machclk(void)
101{
102	/*
103	 * Initialize machclk_freq using the timerbase frequency
104	 * value from device specific info.
105	 */
106	machclk_freq = gPEClockFrequencyInfo.timebase_frequency_hz;
107
108	clock_interval_to_absolutetime_interval(1, NSEC_PER_SEC,
109	    &machclk_per_sec);
110}
111
112u_int64_t
113pktsched_abs_to_nsecs(u_int64_t abstime)
114{
115	u_int64_t nsecs;
116
117	absolutetime_to_nanoseconds(abstime, &nsecs);
118	return (nsecs);
119}
120
121u_int64_t
122pktsched_nsecs_to_abstime(u_int64_t nsecs)
123{
124	u_int64_t abstime;
125
126	nanoseconds_to_absolutetime(nsecs, &abstime);
127	return (abstime);
128}
129
130int
131pktsched_setup(struct ifclassq *ifq, u_int32_t scheduler, u_int32_t sflags)
132{
133	int error = 0;
134	u_int32_t qflags = sflags;
135	u_int32_t rflags;
136
137	IFCQ_LOCK_ASSERT_HELD(ifq);
138
139	VERIFY(machclk_freq != 0);
140
141	/* Nothing to do unless the scheduler type changes */
142	if (ifq->ifcq_type == scheduler)
143		return (0);
144
145	qflags &= (PKTSCHEDF_QALG_RED | PKTSCHEDF_QALG_RIO |
146	    PKTSCHEDF_QALG_BLUE | PKTSCHEDF_QALG_SFB);
147
148	/* These are mutually exclusive */
149	if (qflags != 0 &&
150	    qflags != PKTSCHEDF_QALG_RED && qflags != PKTSCHEDF_QALG_RIO &&
151	    qflags != PKTSCHEDF_QALG_BLUE && qflags != PKTSCHEDF_QALG_SFB) {
152		panic("%s: RED|RIO|BLUE|SFB mutually exclusive\n", __func__);
153		/* NOTREACHED */
154	}
155
156	/*
157	 * Remember the flags that need to be restored upon success, as
158	 * they may be cleared when we tear down existing scheduler.
159	 */
160	rflags = (ifq->ifcq_flags & IFCQF_ENABLED);
161
162	if (ifq->ifcq_type != PKTSCHEDT_NONE) {
163		(void) pktsched_teardown(ifq);
164
165		/* Teardown should have succeeded */
166		VERIFY(ifq->ifcq_type == PKTSCHEDT_NONE);
167		VERIFY(ifq->ifcq_disc == NULL);
168		VERIFY(ifq->ifcq_enqueue == NULL);
169		VERIFY(ifq->ifcq_dequeue == NULL);
170		VERIFY(ifq->ifcq_dequeue_sc == NULL);
171		VERIFY(ifq->ifcq_request == NULL);
172	}
173
174	switch (scheduler) {
175#if PKTSCHED_PRIQ
176	case PKTSCHEDT_PRIQ:
177		error = priq_setup_ifclassq(ifq, sflags);
178		break;
179#endif /* PKTSCHED_PRIQ */
180
181	case PKTSCHEDT_TCQ:
182		error = tcq_setup_ifclassq(ifq, sflags);
183		break;
184
185	case PKTSCHEDT_QFQ:
186		error = qfq_setup_ifclassq(ifq, sflags);
187		break;
188
189	default:
190		error = ENXIO;
191		break;
192	}
193
194	if (error == 0)
195		ifq->ifcq_flags |= rflags;
196
197	return (error);
198}
199
200int
201pktsched_teardown(struct ifclassq *ifq)
202{
203	int error = 0;
204
205	IFCQ_LOCK_ASSERT_HELD(ifq);
206
207	if_qflush(ifq->ifcq_ifp, 1);
208	VERIFY(IFCQ_IS_EMPTY(ifq));
209
210	ifq->ifcq_flags &= ~IFCQF_ENABLED;
211
212	switch (ifq->ifcq_type) {
213	case PKTSCHEDT_NONE:
214		break;
215
216#if PKTSCHED_PRIQ
217	case PKTSCHEDT_PRIQ:
218		error = priq_teardown_ifclassq(ifq);
219		break;
220#endif /* PKTSCHED_PRIQ */
221
222	case PKTSCHEDT_TCQ:
223		error = tcq_teardown_ifclassq(ifq);
224		break;
225
226	case PKTSCHEDT_QFQ:
227		error = qfq_teardown_ifclassq(ifq);
228		break;
229
230	default:
231		error = ENXIO;
232		break;
233	}
234
235	return (error);
236}
237
238int
239pktsched_getqstats(struct ifclassq *ifq, u_int32_t qid,
240    struct if_ifclassq_stats *ifqs)
241{
242	int error;
243
244	IFCQ_LOCK_ASSERT_HELD(ifq);
245
246	switch (ifq->ifcq_type) {
247#if PKTSCHED_PRIQ
248	case PKTSCHEDT_PRIQ:
249		error = priq_getqstats_ifclassq(ifq, qid, ifqs);
250		break;
251#endif /* PKTSCHED_PRIQ */
252
253	case PKTSCHEDT_TCQ:
254		error = tcq_getqstats_ifclassq(ifq, qid, ifqs);
255		break;
256
257	case PKTSCHEDT_QFQ:
258		error = qfq_getqstats_ifclassq(ifq, qid, ifqs);
259		break;
260
261	default:
262		error = ENXIO;
263		break;
264	}
265
266	return (error);
267}
268