xref: /freebsd/sys/netpfil/ipfw/dn_sched_rr.c (revision a64729f5077d77e13b9497cb33ecb3c82e606ee8)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2010 Riccardo Panicucci, Universita` di Pisa
5  * All rights reserved
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 /*
30  */
31 
32 #ifdef _KERNEL
33 #include <sys/malloc.h>
34 #include <sys/socket.h>
35 #include <sys/socketvar.h>
36 #include <sys/kernel.h>
37 #include <sys/lock.h>
38 #include <sys/mbuf.h>
39 #include <sys/module.h>
40 #include <sys/rwlock.h>
41 #include <net/if.h>	/* IFNAMSIZ */
42 #include <netinet/in.h>
43 #include <netinet/ip_var.h>		/* ipfw_rule_ref */
44 #include <netinet/ip_fw.h>	/* flow_id */
45 #include <netinet/ip_dummynet.h>
46 #include <netpfil/ipfw/ip_fw_private.h>
47 #include <netpfil/ipfw/dn_heap.h>
48 #include <netpfil/ipfw/ip_dn_private.h>
49 #ifdef NEW_AQM
50 #include <netpfil/ipfw/dn_aqm.h>
51 #endif
52 #include <netpfil/ipfw/dn_sched.h>
53 #else
54 #include <dn_test.h>
55 #endif
56 
57 #define DN_SCHED_RR	3 // XXX Where?
58 
59 struct rr_queue {
60 	struct dn_queue q;		/* Standard queue */
61 	int status;			/* 1: queue is in the list */
62 	uint32_t credit;		/* max bytes we can transmit */
63 	uint32_t quantum;		/* quantum * weight */
64 	struct rr_queue *qnext;		/* */
65 };
66 
67 /* struct rr_schk contains global config parameters
68  * and is right after dn_schk
69  */
70 struct rr_schk {
71 	uint32_t min_q;		/* Min quantum */
72 	uint32_t max_q;		/* Max quantum */
73 	uint32_t q_bytes;	/* default quantum in bytes */
74 };
75 
76 /* per-instance round robin list, right after dn_sch_inst */
77 struct rr_si {
78 	struct rr_queue *head, *tail;	/* Pointer to current queue */
79 };
80 
81 /* Append a queue to the rr list */
82 static inline void
83 rr_append(struct rr_queue *q, struct rr_si *si)
84 {
85 	q->status = 1;		/* mark as in-rr_list */
86 	q->credit = q->quantum;	/* initialize credit */
87 
88 	/* append to the tail */
89 	if (si->head == NULL)
90 		si->head = q;
91 	else
92 		si->tail->qnext = q;
93 	si->tail = q;		/* advance the tail pointer */
94 	q->qnext = si->head;	/* make it circular */
95 }
96 
97 /* Remove the head queue from circular list. */
98 static inline void
99 rr_remove_head(struct rr_si *si)
100 {
101 	if (si->head == NULL)
102 		return; /* empty queue */
103 	si->head->status = 0;
104 
105 	if (si->head == si->tail) {
106 		si->head = si->tail = NULL;
107 		return;
108 	}
109 
110 	si->head = si->head->qnext;
111 	si->tail->qnext = si->head;
112 }
113 
114 /* Remove a queue from circular list.
115  * XXX see if ti can be merge with remove_queue()
116  */
117 static inline void
118 remove_queue_q(struct rr_queue *q, struct rr_si *si)
119 {
120 	struct rr_queue *prev;
121 
122 	if (q->status != 1)
123 		return;
124 	if (q == si->head) {
125 		rr_remove_head(si);
126 		return;
127 	}
128 
129 	for (prev = si->head; prev; prev = prev->qnext) {
130 		if (prev->qnext != q)
131 			continue;
132 		prev->qnext = q->qnext;
133 		if (q == si->tail)
134 			si->tail = prev;
135 		q->status = 0;
136 		break;
137 	}
138 }
139 
140 static inline void
141 next_pointer(struct rr_si *si)
142 {
143 	if (si->head == NULL)
144 		return; /* empty queue */
145 
146 	si->head = si->head->qnext;
147 	si->tail = si->tail->qnext;
148 }
149 
150 static int
151 rr_enqueue(struct dn_sch_inst *_si, struct dn_queue *q, struct mbuf *m)
152 {
153 	struct rr_si *si;
154 	struct rr_queue *rrq;
155 
156 	if (m != q->mq.head) {
157 		if (dn_enqueue(q, m, 0)) /* packet was dropped */
158 			return 1;
159 		if (m != q->mq.head)
160 			return 0;
161 	}
162 
163 	/* If reach this point, queue q was idle */
164 	si = (struct rr_si *)(_si + 1);
165 	rrq = (struct rr_queue *)q;
166 
167 	if (rrq->status == 1) /* Queue is already in the queue list */
168 		return 0;
169 
170 	/* Insert the queue in the queue list */
171 	rr_append(rrq, si);
172 
173 	return 0;
174 }
175 
176 static struct mbuf *
177 rr_dequeue(struct dn_sch_inst *_si)
178 {
179 	/* Access scheduler instance private data */
180 	struct rr_si *si = (struct rr_si *)(_si + 1);
181 	struct rr_queue *rrq;
182 	uint64_t len;
183 
184 	while ( (rrq = si->head) ) {
185 		struct mbuf *m = rrq->q.mq.head;
186 		if ( m == NULL) {
187 			/* empty queue, remove from list */
188 			rr_remove_head(si);
189 			continue;
190 		}
191 		len = m->m_pkthdr.len;
192 
193 		if (len > rrq->credit) {
194 			/* Packet too big */
195 			rrq->credit += rrq->quantum;
196 			/* Try next queue */
197 			next_pointer(si);
198 		} else {
199 			rrq->credit -= len;
200 			return dn_dequeue(&rrq->q);
201 		}
202 	}
203 
204 	/* no packet to dequeue*/
205 	return NULL;
206 }
207 
208 static int
209 rr_config(struct dn_schk *_schk)
210 {
211 	struct rr_schk *schk = (struct rr_schk *)(_schk + 1);
212 	ND("called");
213 
214 	/* use reasonable quantums (64..2k bytes, default 1500) */
215 	schk->min_q = 64;
216 	schk->max_q = 2048;
217 	schk->q_bytes = 1500;	/* quantum */
218 
219 	return 0;
220 }
221 
222 static int
223 rr_new_sched(struct dn_sch_inst *_si)
224 {
225 	struct rr_si *si = (struct rr_si *)(_si + 1);
226 
227 	ND("called");
228 	si->head = si->tail = NULL;
229 
230 	return 0;
231 }
232 
233 static int
234 rr_free_sched(struct dn_sch_inst *_si)
235 {
236 	(void)_si;
237 	ND("called");
238 	/* Nothing to do? */
239 	return 0;
240 }
241 
242 static int
243 rr_new_fsk(struct dn_fsk *fs)
244 {
245 	struct rr_schk *schk = (struct rr_schk *)(fs->sched + 1);
246 	/* par[0] is the weight, par[1] is the quantum step */
247 	/* make sure the product fits an uint32_t */
248 	ipdn_bound_var(&fs->fs.par[0], 1,
249 		1, 65536, "RR weight");
250 	ipdn_bound_var(&fs->fs.par[1], schk->q_bytes,
251 		schk->min_q, schk->max_q, "RR quantum");
252 	return 0;
253 }
254 
255 static int
256 rr_new_queue(struct dn_queue *_q)
257 {
258 	struct rr_queue *q = (struct rr_queue *)_q;
259 	uint64_t quantum;
260 
261 	_q->ni.oid.subtype = DN_SCHED_RR;
262 
263 	quantum = (uint64_t)_q->fs->fs.par[0] * _q->fs->fs.par[1];
264 	if (quantum >= (1ULL<< 32)) {
265 		D("quantum too large, truncating to 4G - 1");
266 		quantum = (1ULL<< 32) - 1;
267 	}
268 	q->quantum = quantum;
269 	ND("called, q->quantum %d", q->quantum);
270 	q->credit = q->quantum;
271 	q->status = 0;
272 
273 	if (_q->mq.head != NULL) {
274 		/* Queue NOT empty, insert in the queue list */
275 		rr_append(q, (struct rr_si *)(_q->_si + 1));
276 	}
277 	return 0;
278 }
279 
280 static int
281 rr_free_queue(struct dn_queue *_q)
282 {
283 	struct rr_queue *q = (struct rr_queue *)_q;
284 
285 	ND("called");
286 	if (q->status == 1) {
287 		struct rr_si *si = (struct rr_si *)(_q->_si + 1);
288 		remove_queue_q(q, si);
289 	}
290 	return 0;
291 }
292 
293 /*
294  * RR scheduler descriptor
295  * contains the type of the scheduler, the name, the size of the
296  * structures and function pointers.
297  */
298 static struct dn_alg rr_desc = {
299 	_SI( .type = ) DN_SCHED_RR,
300 	_SI( .name = ) "RR",
301 	_SI( .flags = ) DN_MULTIQUEUE,
302 
303 	_SI( .schk_datalen = ) sizeof(struct rr_schk),
304 	_SI( .si_datalen = ) sizeof(struct rr_si),
305 	_SI( .q_datalen = ) sizeof(struct rr_queue) - sizeof(struct dn_queue),
306 
307 	_SI( .enqueue = ) rr_enqueue,
308 	_SI( .dequeue = ) rr_dequeue,
309 
310 	_SI( .config = ) rr_config,
311 	_SI( .destroy = ) NULL,
312 	_SI( .new_sched = ) rr_new_sched,
313 	_SI( .free_sched = ) rr_free_sched,
314 	_SI( .new_fsk = ) rr_new_fsk,
315 	_SI( .free_fsk = ) NULL,
316 	_SI( .new_queue = ) rr_new_queue,
317 	_SI( .free_queue = ) rr_free_queue,
318 #ifdef NEW_AQM
319 	_SI( .getconfig = )  NULL,
320 #endif
321 };
322 
323 DECLARE_DNSCHED_MODULE(dn_rr, &rr_desc);
324