Merge branch 'vendor/MPFR'
[dragonfly.git] / sys / net / altq / altq_cbq.c
1 /*      $KAME: altq_cbq.c,v 1.20 2004/04/17 10:54:48 kjc Exp $  */
2 /*      $DragonFly: src/sys/net/altq/altq_cbq.c,v 1.7 2008/05/14 11:59:23 sephe Exp $ */
3
4 /*
5  * Copyright (c) Sun Microsystems, Inc. 1993-1998 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  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *      This product includes software developed by the SMCC Technology
21  *      Development Group at Sun Microsystems, Inc.
22  *
23  * 4. The name of the Sun Microsystems, Inc nor may not be used to endorse or
24  *      promote products derived from this software without specific prior
25  *      written permission.
26  *
27  * SUN MICROSYSTEMS DOES NOT CLAIM MERCHANTABILITY OF THIS SOFTWARE OR THE
28  * SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE.  The software is
29  * provided "as is" without express or implied warranty of any kind.
30  *
31  * These notices must be retained in any copies of any part of this software.
32  */
33
34 #include "opt_altq.h"
35 #include "opt_inet.h"
36 #include "opt_inet6.h"
37
38 #ifdef ALTQ_CBQ /* cbq is enabled by ALTQ_CBQ option in opt_altq.h */
39
40 #include <sys/param.h>
41 #include <sys/malloc.h>
42 #include <sys/mbuf.h>
43 #include <sys/socket.h>
44 #include <sys/systm.h>
45 #include <sys/proc.h>
46 #include <sys/callout.h>
47 #include <sys/errno.h>
48 #include <sys/time.h>
49 #include <sys/thread.h>
50
51 #include <net/if.h>
52 #include <net/ifq_var.h>
53 #include <netinet/in.h>
54
55 #include <net/pf/pfvar.h>
56 #include <net/altq/altq.h>
57 #include <net/altq/altq_cbq.h>
58
59 #include <sys/thread2.h>
60
61 /*
62  * Forward Declarations.
63  */
64 static int               cbq_class_destroy(cbq_state_t *, struct rm_class *);
65 static struct rm_class  *clh_to_clp(cbq_state_t *, uint32_t);
66 static int               cbq_clear_interface(cbq_state_t *);
67 static int               cbq_request(struct ifaltq *, int, void *);
68 static int               cbq_enqueue(struct ifaltq *, struct mbuf *,
69                              struct altq_pktattr *);
70 static struct mbuf      *cbq_dequeue(struct ifaltq *, struct mbuf *, int);
71 static void              cbqrestart(struct ifaltq *);
72 static void              get_class_stats(class_stats_t *, struct rm_class *);
73 static void              cbq_purge(cbq_state_t *);
74
75 /*
76  * int
77  * cbq_class_destroy(cbq_mod_state_t *, struct rm_class *) - This
78  *      function destroys a given traffic class.  Before destroying
79  *      the class, all traffic for that class is released.
80  */
81 static int
82 cbq_class_destroy(cbq_state_t *cbqp, struct rm_class *cl)
83 {
84         int     i;
85
86         /* delete the class */
87         rmc_delete_class(&cbqp->ifnp, cl);
88
89         /*
90          * free the class handle
91          */
92         for (i = 0; i < CBQ_MAX_CLASSES; i++)
93                 if (cbqp->cbq_class_tbl[i] == cl)
94                         cbqp->cbq_class_tbl[i] = NULL;
95
96         if (cl == cbqp->ifnp.root_)
97                 cbqp->ifnp.root_ = NULL;
98         if (cl == cbqp->ifnp.default_)
99                 cbqp->ifnp.default_ = NULL;
100         return (0);
101 }
102
103 /* convert class handle to class pointer */
104 static struct rm_class *
105 clh_to_clp(cbq_state_t *cbqp, uint32_t chandle)
106 {
107         int i;
108         struct rm_class *cl;
109
110         if (chandle == 0)
111                 return (NULL);
112         /*
113          * first, try optimistically the slot matching the lower bits of
114          * the handle.  if it fails, do the linear table search.
115          */
116         i = chandle % CBQ_MAX_CLASSES;
117         if ((cl = cbqp->cbq_class_tbl[i]) != NULL &&
118             cl->stats_.handle == chandle)
119                 return (cl);
120         for (i = 0; i < CBQ_MAX_CLASSES; i++)
121                 if ((cl = cbqp->cbq_class_tbl[i]) != NULL &&
122                     cl->stats_.handle == chandle)
123                         return (cl);
124         return (NULL);
125 }
126
127 static int
128 cbq_clear_interface(cbq_state_t *cbqp)
129 {
130         int              again, i;
131         struct rm_class *cl;
132
133         /* clear out the classes now */
134         do {
135                 again = 0;
136                 for (i = 0; i < CBQ_MAX_CLASSES; i++) {
137                         if ((cl = cbqp->cbq_class_tbl[i]) != NULL) {
138                                 if (is_a_parent_class(cl))
139                                         again++;
140                                 else {
141                                         cbq_class_destroy(cbqp, cl);
142                                         cbqp->cbq_class_tbl[i] = NULL;
143                                         if (cl == cbqp->ifnp.root_)
144                                                 cbqp->ifnp.root_ = NULL;
145                                         if (cl == cbqp->ifnp.default_)
146                                                 cbqp->ifnp.default_ = NULL;
147                                 }
148                         }
149                 }
150         } while (again);
151
152         return (0);
153 }
154
155 static int
156 cbq_request(struct ifaltq *ifq, int req, void *arg)
157 {
158         cbq_state_t     *cbqp = (cbq_state_t *)ifq->altq_disc;
159
160         crit_enter();
161         switch (req) {
162         case ALTRQ_PURGE:
163                 cbq_purge(cbqp);
164                 break;
165         }
166         crit_exit();
167         return (0);
168 }
169
170 /* copy the stats info in rm_class to class_states_t */
171 static void
172 get_class_stats(class_stats_t *statsp, struct rm_class *cl)
173 {
174         statsp->xmit_cnt        = cl->stats_.xmit_cnt;
175         statsp->drop_cnt        = cl->stats_.drop_cnt;
176         statsp->over            = cl->stats_.over;
177         statsp->borrows         = cl->stats_.borrows;
178         statsp->overactions     = cl->stats_.overactions;
179         statsp->delays          = cl->stats_.delays;
180
181         statsp->depth           = cl->depth_;
182         statsp->priority        = cl->pri_;
183         statsp->maxidle         = cl->maxidle_;
184         statsp->minidle         = cl->minidle_;
185         statsp->offtime         = cl->offtime_;
186         statsp->qmax            = qlimit(cl->q_);
187         statsp->ns_per_byte     = cl->ns_per_byte_;
188         statsp->wrr_allot       = cl->w_allotment_;
189         statsp->qcnt            = qlen(cl->q_);
190         statsp->avgidle         = cl->avgidle_;
191
192         statsp->qtype           = qtype(cl->q_);
193 #ifdef ALTQ_RED
194         if (q_is_red(cl->q_))
195                 red_getstats(cl->red_, &statsp->red[0]);
196 #endif
197 #ifdef ALTQ_RIO
198         if (q_is_rio(cl->q_))
199                 rio_getstats((rio_t *)cl->red_, &statsp->red[0]);
200 #endif
201 }
202
203 int
204 cbq_pfattach(struct pf_altq *a, struct ifaltq *ifq)
205 {
206         return altq_attach(ifq, ALTQT_CBQ, a->altq_disc,
207             cbq_enqueue, cbq_dequeue, cbq_request, NULL, NULL);
208 }
209
210 int
211 cbq_add_altq(struct pf_altq *a)
212 {
213         cbq_state_t     *cbqp;
214         struct ifnet    *ifp;
215
216         if ((ifp = ifunit(a->ifname)) == NULL)
217                 return (EINVAL);
218         if (!ifq_is_ready(&ifp->if_snd))
219                 return (ENODEV);
220
221         /* allocate and initialize cbq_state_t */
222         cbqp = kmalloc(sizeof(*cbqp), M_ALTQ, M_WAITOK | M_ZERO);
223         callout_init(&cbqp->cbq_callout);
224         cbqp->cbq_qlen = 0;
225         cbqp->ifnp.ifq_ = &ifp->if_snd;     /* keep the ifq */
226         ifq_purge(&ifp->if_snd);
227
228         /* keep the state in pf_altq */
229         a->altq_disc = cbqp;
230
231         return (0);
232 }
233
234 int
235 cbq_remove_altq(struct pf_altq *a)
236 {
237         cbq_state_t     *cbqp;
238
239         if ((cbqp = a->altq_disc) == NULL)
240                 return (EINVAL);
241         a->altq_disc = NULL;
242
243         cbq_clear_interface(cbqp);
244
245         if (cbqp->ifnp.default_)
246                 cbq_class_destroy(cbqp, cbqp->ifnp.default_);
247         if (cbqp->ifnp.root_)
248                 cbq_class_destroy(cbqp, cbqp->ifnp.root_);
249
250         /* deallocate cbq_state_t */
251         kfree(cbqp, M_ALTQ);
252
253         return (0);
254 }
255
256 static int
257 cbq_add_queue_locked(struct pf_altq *a, cbq_state_t *cbqp)
258 {
259         struct rm_class *borrow, *parent;
260         struct rm_class *cl;
261         struct cbq_opts *opts;
262         int             i;
263
264         KKASSERT(a->qid != 0);
265
266         /*
267          * find a free slot in the class table.  if the slot matching
268          * the lower bits of qid is free, use this slot.  otherwise,
269          * use the first free slot.
270          */
271         i = a->qid % CBQ_MAX_CLASSES;
272         if (cbqp->cbq_class_tbl[i] != NULL) {
273                 for (i = 0; i < CBQ_MAX_CLASSES; i++)
274                         if (cbqp->cbq_class_tbl[i] == NULL)
275                                 break;
276                 if (i == CBQ_MAX_CLASSES)
277                         return (EINVAL);
278         }
279
280         opts = &a->pq_u.cbq_opts;
281         /* check parameters */
282         if (a->priority >= CBQ_MAXPRI)
283                 return (EINVAL);
284
285         /* Get pointers to parent and borrow classes.  */
286         parent = clh_to_clp(cbqp, a->parent_qid);
287         if (opts->flags & CBQCLF_BORROW)
288                 borrow = parent;
289         else
290                 borrow = NULL;
291
292         /*
293          * A class must borrow from it's parent or it can not
294          * borrow at all.  Hence, borrow can be null.
295          */
296         if (parent == NULL && (opts->flags & CBQCLF_ROOTCLASS) == 0) {
297                 kprintf("cbq_add_queue: no parent class!\n");
298                 return (EINVAL);
299         }
300
301         if ((borrow != parent)  && (borrow != NULL)) {
302                 kprintf("cbq_add_class: borrow class != parent\n");
303                 return (EINVAL);
304         }
305
306         /*
307          * check parameters
308          */
309         switch (opts->flags & CBQCLF_CLASSMASK) {
310         case CBQCLF_ROOTCLASS:
311                 if (parent != NULL)
312                         return (EINVAL);
313                 if (cbqp->ifnp.root_)
314                         return (EINVAL);
315                 break;
316         case CBQCLF_DEFCLASS:
317                 if (cbqp->ifnp.default_)
318                         return (EINVAL);
319                 break;
320         case 0:
321                 if (a->qid == 0)
322                         return (EINVAL);
323                 break;
324         default:
325                 /* more than two flags bits set */
326                 return (EINVAL);
327         }
328
329         /*
330          * create a class.  if this is a root class, initialize the
331          * interface.
332          */
333         if ((opts->flags & CBQCLF_CLASSMASK) == CBQCLF_ROOTCLASS) {
334                 rmc_init(cbqp->ifnp.ifq_, &cbqp->ifnp, opts->ns_per_byte,
335                     cbqrestart, a->qlimit, RM_MAXQUEUED,
336                     opts->maxidle, opts->minidle, opts->offtime,
337                     opts->flags);
338                 cl = cbqp->ifnp.root_;
339         } else {
340                 cl = rmc_newclass(a->priority,
341                                   &cbqp->ifnp, opts->ns_per_byte,
342                                   rmc_delay_action, a->qlimit, parent, borrow,
343                                   opts->maxidle, opts->minidle, opts->offtime,
344                                   opts->pktsize, opts->flags);
345         }
346         if (cl == NULL)
347                 return (ENOMEM);
348
349         /* return handle to user space. */
350         cl->stats_.handle = a->qid;
351         cl->stats_.depth = cl->depth_;
352
353         /* save the allocated class */
354         cbqp->cbq_class_tbl[i] = cl;
355
356         if ((opts->flags & CBQCLF_CLASSMASK) == CBQCLF_DEFCLASS)
357                 cbqp->ifnp.default_ = cl;
358
359         return (0);
360 }
361
362 int
363 cbq_add_queue(struct pf_altq *a)
364 {
365         cbq_state_t *cbqp;
366         struct ifaltq *ifq;
367         int error;
368
369         if (a->qid == 0)
370                 return (EINVAL);
371
372         /* XXX not MP safe */
373         if ((cbqp = a->altq_disc) == NULL)
374                 return (EINVAL);
375         ifq = cbqp->ifnp.ifq_;
376
377         ALTQ_LOCK(ifq);
378         error = cbq_add_queue_locked(a, cbqp);
379         ALTQ_UNLOCK(ifq);
380
381         return error;
382 }
383
384 static int
385 cbq_remove_queue_locked(struct pf_altq *a, cbq_state_t *cbqp)
386 {
387         struct rm_class *cl;
388         int             i;
389
390         if ((cl = clh_to_clp(cbqp, a->qid)) == NULL)
391                 return (EINVAL);
392
393         /* if we are a parent class, then return an error. */
394         if (is_a_parent_class(cl))
395                 return (EINVAL);
396
397         /* delete the class */
398         rmc_delete_class(&cbqp->ifnp, cl);
399
400         /*
401          * free the class handle
402          */
403         for (i = 0; i < CBQ_MAX_CLASSES; i++)
404                 if (cbqp->cbq_class_tbl[i] == cl) {
405                         cbqp->cbq_class_tbl[i] = NULL;
406                         if (cl == cbqp->ifnp.root_)
407                                 cbqp->ifnp.root_ = NULL;
408                         if (cl == cbqp->ifnp.default_)
409                                 cbqp->ifnp.default_ = NULL;
410                         break;
411                 }
412
413         return (0);
414 }
415
416 int
417 cbq_remove_queue(struct pf_altq *a)
418 {
419         cbq_state_t *cbqp;
420         struct ifaltq *ifq;
421         int error;
422
423         /* XXX not MP safe */
424         if ((cbqp = a->altq_disc) == NULL)
425                 return (EINVAL);
426         ifq = cbqp->ifnp.ifq_;
427
428         ALTQ_LOCK(ifq);
429         error = cbq_remove_queue_locked(a, cbqp);
430         ALTQ_UNLOCK(ifq);
431
432         return error;
433 }
434
435 int
436 cbq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes)
437 {
438         cbq_state_t     *cbqp;
439         struct rm_class *cl;
440         class_stats_t    stats;
441         int              error = 0;
442         struct ifaltq   *ifq;
443
444         if (*nbytes < sizeof(stats))
445                 return (EINVAL);
446
447         /* XXX not MP safe */
448         if ((cbqp = altq_lookup(a->ifname, ALTQT_CBQ)) == NULL)
449                 return (EBADF);
450         ifq = cbqp->ifnp.ifq_;
451
452         ALTQ_LOCK(ifq);
453
454         if ((cl = clh_to_clp(cbqp, a->qid)) == NULL) {
455                 ALTQ_UNLOCK(ifq);
456                 return (EINVAL);
457         }
458
459         get_class_stats(&stats, cl);
460
461         ALTQ_UNLOCK(ifq);
462
463         if ((error = copyout((caddr_t)&stats, ubuf, sizeof(stats))) != 0)
464                 return (error);
465         *nbytes = sizeof(stats);
466         return (0);
467 }
468
469 /*
470  * int
471  * cbq_enqueue(struct ifaltq *ifq, struct mbuf *m, struct altq_pktattr *pattr)
472  *              - Queue data packets.
473  *
474  *      cbq_enqueue is set to ifp->if_altqenqueue and called by an upper
475  *      layer (e.g. ether_output).  cbq_enqueue queues the given packet
476  *      to the cbq, then invokes the driver's start routine.
477  *
478  *      Returns:        0 if the queueing is successful.
479  *                      ENOBUFS if a packet dropping occurred as a result of
480  *                      the queueing.
481  */
482
483 static int
484 cbq_enqueue(struct ifaltq *ifq, struct mbuf *m, struct altq_pktattr *pktattr)
485 {
486         cbq_state_t     *cbqp = (cbq_state_t *)ifq->altq_disc;
487         struct rm_class *cl;
488         int len;
489
490         /* grab class set by classifier */
491         if ((m->m_flags & M_PKTHDR) == 0) {
492                 /* should not happen */
493                 if_printf(ifq->altq_ifp, "altq: packet does not have pkthdr\n");
494                 m_freem(m);
495                 return (ENOBUFS);
496         }
497         if (m->m_pkthdr.fw_flags & PF_MBUF_STRUCTURE)
498                 cl = clh_to_clp(cbqp, m->m_pkthdr.pf.qid);
499         else
500                 cl = NULL;
501         if (cl == NULL) {
502                 cl = cbqp->ifnp.default_;
503                 if (cl == NULL) {
504                         m_freem(m);
505                         return (ENOBUFS);
506                 }
507         }
508         crit_enter();
509         cl->pktattr_ = NULL;
510         len = m_pktlen(m);
511         if (rmc_queue_packet(cl, m) != 0) {
512                 /* drop occurred.  some mbuf was freed in rmc_queue_packet. */
513                 PKTCNTR_ADD(&cl->stats_.drop_cnt, len);
514                 crit_exit();
515                 return (ENOBUFS);
516         }
517
518         /* successfully queued. */
519         ++cbqp->cbq_qlen;
520         ++ifq->ifq_len;
521         crit_exit();
522         return (0);
523 }
524
525 static struct mbuf *
526 cbq_dequeue(struct ifaltq *ifq, struct mbuf *mpolled, int op)
527 {
528         cbq_state_t     *cbqp = (cbq_state_t *)ifq->altq_disc;
529         struct mbuf     *m;
530
531         crit_enter();
532         m = rmc_dequeue_next(&cbqp->ifnp, op);
533
534         if (m && op == ALTDQ_REMOVE) {
535                 --cbqp->cbq_qlen;  /* decrement # of packets in cbq */
536                 --ifq->ifq_len;
537
538                 /* Update the class. */
539                 rmc_update_class_util(&cbqp->ifnp);
540         }
541         crit_exit();
542         KKASSERT(mpolled == NULL || mpolled == m);
543         return (m);
544 }
545
546 /*
547  * void
548  * cbqrestart(queue_t *) - Restart sending of data.
549  * called from rmc_restart in a critical section via timeout after waking up
550  * a suspended class.
551  *      Returns:        NONE
552  */
553
554 static void
555 cbqrestart(struct ifaltq *ifq)
556 {
557         cbq_state_t     *cbqp;
558
559         ALTQ_ASSERT_LOCKED(ifq);
560
561         if (!ifq_is_enabled(ifq))
562                 /* cbq must have been detached */
563                 return;
564
565         if ((cbqp = (cbq_state_t *)ifq->altq_disc) == NULL)
566                 /* should not happen */
567                 return;
568
569         if (cbqp->cbq_qlen > 0) {
570                 struct ifnet *ifp = ifq->altq_ifp;
571
572                 /* Release the altq lock to avoid deadlock */
573                 ALTQ_UNLOCK(ifq);
574
575                 ifnet_serialize_tx(ifp);
576                 if (ifp->if_start && (ifp->if_flags & IFF_OACTIVE) == 0)
577                         (*ifp->if_start)(ifp);
578                 ifnet_deserialize_tx(ifp);
579
580                 ALTQ_LOCK(ifq);
581         }
582 }
583
584 static void
585 cbq_purge(cbq_state_t *cbqp)
586 {
587         struct rm_class *cl;
588         int i;
589         for (i = 0; i < CBQ_MAX_CLASSES; i++) {
590                 if ((cl = cbqp->cbq_class_tbl[i]) != NULL)
591                         rmc_dropall(cl);
592         }
593         if (ifq_is_enabled(cbqp->ifnp.ifq_))
594                 cbqp->ifnp.ifq_->ifq_len = 0;
595 }
596
597 #endif /* ALTQ_CBQ */