1204591Sluigi/*- 2204591Sluigi * Copyright (c) 2010 Luigi Rizzo, Riccardo Panicucci, Universita` di Pisa 3204591Sluigi * All rights reserved 4204591Sluigi * 5204591Sluigi * Redistribution and use in source and binary forms, with or without 6204591Sluigi * modification, are permitted provided that the following conditions 7204591Sluigi * are met: 8204591Sluigi * 1. Redistributions of source code must retain the above copyright 9204591Sluigi * notice, this list of conditions and the following disclaimer. 10204591Sluigi * 2. Redistributions in binary form must reproduce the above copyright 11204591Sluigi * notice, this list of conditions and the following disclaimer in the 12204591Sluigi * documentation and/or other materials provided with the distribution. 13204591Sluigi * 14204591Sluigi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15204591Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16204591Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17204591Sluigi * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18204591Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19204591Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20204591Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21204591Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22204591Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23204591Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24204591Sluigi * SUCH DAMAGE. 25204591Sluigi */ 26204591Sluigi 27204591Sluigi/* 28204591Sluigi * internal dummynet APIs. 29204591Sluigi * 30204591Sluigi * $FreeBSD: stable/10/sys/netpfil/ipfw/ip_dn_private.h 325731 2017-11-12 01:28:20Z truckman $ 31204591Sluigi */ 32204591Sluigi 33204591Sluigi#ifndef _IP_DN_PRIVATE_H 34204591Sluigi#define _IP_DN_PRIVATE_H 35204591Sluigi 36204591Sluigi/* debugging support 37204591Sluigi * use ND() to remove debugging, D() to print a line, 38204591Sluigi * DX(level, ...) to print above a certain level 39204591Sluigi * If you redefine D() you are expected to redefine all. 40204591Sluigi */ 41204591Sluigi#ifndef D 42204591Sluigi#define ND(fmt, ...) do {} while (0) 43204591Sluigi#define D1(fmt, ...) do {} while (0) 44204591Sluigi#define D(fmt, ...) printf("%-10s " fmt "\n", \ 45204591Sluigi __FUNCTION__, ## __VA_ARGS__) 46204591Sluigi#define DX(lev, fmt, ...) do { \ 47204591Sluigi if (dn_cfg.debug > lev) D(fmt, ## __VA_ARGS__); } while (0) 48204591Sluigi#endif 49204591Sluigi 50204591SluigiMALLOC_DECLARE(M_DUMMYNET); 51204591Sluigi 52204591Sluigi#ifndef __linux__ 53204591Sluigi#define div64(a, b) ((int64_t)(a) / (int64_t)(b)) 54204591Sluigi#endif 55204591Sluigi 56204591Sluigi#define DN_LOCK_INIT() do { \ 57204591Sluigi mtx_init(&dn_cfg.uh_mtx, "dn_uh", NULL, MTX_DEF); \ 58204591Sluigi mtx_init(&dn_cfg.bh_mtx, "dn_bh", NULL, MTX_DEF); \ 59204591Sluigi } while (0) 60204591Sluigi#define DN_LOCK_DESTROY() do { \ 61204591Sluigi mtx_destroy(&dn_cfg.uh_mtx); \ 62204591Sluigi mtx_destroy(&dn_cfg.bh_mtx); \ 63204591Sluigi } while (0) 64204591Sluigi#if 0 /* not used yet */ 65204591Sluigi#define DN_UH_RLOCK() mtx_lock(&dn_cfg.uh_mtx) 66204591Sluigi#define DN_UH_RUNLOCK() mtx_unlock(&dn_cfg.uh_mtx) 67204591Sluigi#define DN_UH_WLOCK() mtx_lock(&dn_cfg.uh_mtx) 68204591Sluigi#define DN_UH_WUNLOCK() mtx_unlock(&dn_cfg.uh_mtx) 69204591Sluigi#define DN_UH_LOCK_ASSERT() mtx_assert(&dn_cfg.uh_mtx, MA_OWNED) 70204591Sluigi#endif 71204591Sluigi 72204591Sluigi#define DN_BH_RLOCK() mtx_lock(&dn_cfg.uh_mtx) 73204591Sluigi#define DN_BH_RUNLOCK() mtx_unlock(&dn_cfg.uh_mtx) 74204591Sluigi#define DN_BH_WLOCK() mtx_lock(&dn_cfg.uh_mtx) 75204591Sluigi#define DN_BH_WUNLOCK() mtx_unlock(&dn_cfg.uh_mtx) 76204591Sluigi#define DN_BH_LOCK_ASSERT() mtx_assert(&dn_cfg.uh_mtx, MA_OWNED) 77204591Sluigi 78204591SluigiSLIST_HEAD(dn_schk_head, dn_schk); 79204591SluigiSLIST_HEAD(dn_sch_inst_head, dn_sch_inst); 80204591SluigiSLIST_HEAD(dn_fsk_head, dn_fsk); 81204591SluigiSLIST_HEAD(dn_queue_head, dn_queue); 82204591SluigiSLIST_HEAD(dn_alg_head, dn_alg); 83204591Sluigi 84301772Struckman#ifdef NEW_AQM 85301772StruckmanSLIST_HEAD(dn_aqm_head, dn_aqm); /* for new AQMs */ 86301772Struckman#endif 87301772Struckman 88204591Sluigistruct mq { /* a basic queue of packets*/ 89204591Sluigi struct mbuf *head, *tail; 90204591Sluigi}; 91204591Sluigi 92204591Sluigistatic inline void 93204591Sluigiset_oid(struct dn_id *o, int type, int len) 94204591Sluigi{ 95204591Sluigi o->type = type; 96204591Sluigi o->len = len; 97204591Sluigi o->subtype = 0; 98204591Sluigi}; 99204591Sluigi 100204591Sluigi/* 101204591Sluigi * configuration and global data for a dummynet instance 102204591Sluigi * 103204591Sluigi * When a configuration is modified from userland, 'id' is incremented 104204591Sluigi * so we can use the value to check for stale pointers. 105204591Sluigi */ 106204591Sluigistruct dn_parms { 107204591Sluigi uint32_t id; /* configuration version */ 108204591Sluigi 109204591Sluigi /* defaults (sysctl-accessible) */ 110204591Sluigi int red_lookup_depth; 111204591Sluigi int red_avg_pkt_size; 112204591Sluigi int red_max_pkt_size; 113204591Sluigi int hash_size; 114204591Sluigi int max_hash_size; 115204591Sluigi long byte_limit; /* max queue sizes */ 116204591Sluigi long slot_limit; 117204591Sluigi 118204591Sluigi int io_fast; 119204591Sluigi int debug; 120204591Sluigi 121204591Sluigi /* timekeeping */ 122204591Sluigi struct timeval prev_t; /* last time dummynet_tick ran */ 123204591Sluigi struct dn_heap evheap; /* scheduled events */ 124204591Sluigi 125204591Sluigi /* counters of objects -- used for reporting space */ 126204591Sluigi int schk_count; 127204591Sluigi int si_count; 128204591Sluigi int fsk_count; 129204591Sluigi int queue_count; 130204591Sluigi 131204591Sluigi /* ticks and other stuff */ 132204591Sluigi uint64_t curr_time; 133204591Sluigi /* flowsets and schedulers are in hash tables, with 'hash_size' 134204591Sluigi * buckets. fshash is looked up at every packet arrival 135204591Sluigi * so better be generous if we expect many entries. 136204591Sluigi */ 137204591Sluigi struct dn_ht *fshash; 138204591Sluigi struct dn_ht *schedhash; 139204591Sluigi /* list of flowsets without a scheduler -- use sch_chain */ 140204591Sluigi struct dn_fsk_head fsu; /* list of unlinked flowsets */ 141204591Sluigi struct dn_alg_head schedlist; /* list of algorithms */ 142301772Struckman#ifdef NEW_AQM 143301772Struckman struct dn_aqm_head aqmlist; /* list of AQMs */ 144301772Struckman#endif 145204591Sluigi 146204591Sluigi /* Store the fs/sch to scan when draining. The value is the 147205173Sluigi * bucket number of the hash table. Expire can be disabled 148205173Sluigi * with net.inet.ip.dummynet.expire=0, or it happens every 149205173Sluigi * expire ticks. 150204591Sluigi **/ 151204591Sluigi int drain_fs; 152204591Sluigi int drain_sch; 153205173Sluigi uint32_t expire; 154205173Sluigi uint32_t expire_cycle; /* tick count */ 155206845Sluigi 156206428Sluigi int init_done; 157206428Sluigi 158204591Sluigi /* if the upper half is busy doing something long, 159204591Sluigi * can set the busy flag and we will enqueue packets in 160204591Sluigi * a queue for later processing. 161204591Sluigi */ 162204591Sluigi int busy; 163204591Sluigi struct mq pending; 164204591Sluigi 165204591Sluigi#ifdef _KERNEL 166204591Sluigi /* 167204591Sluigi * This file is normally used in the kernel, unless we do 168204591Sluigi * some userland tests, in which case we do not need a mtx. 169204591Sluigi * uh_mtx arbitrates between system calls and also 170204591Sluigi * protects fshash, schedhash and fsunlinked. 171204591Sluigi * These structures are readonly for the lower half. 172204591Sluigi * bh_mtx protects all other structures which may be 173204591Sluigi * modified upon packet arrivals 174204591Sluigi */ 175204591Sluigi#if defined( __linux__ ) || defined( _WIN32 ) 176204591Sluigi spinlock_t uh_mtx; 177204591Sluigi spinlock_t bh_mtx; 178204591Sluigi#else 179204591Sluigi struct mtx uh_mtx; 180204591Sluigi struct mtx bh_mtx; 181204591Sluigi#endif 182204591Sluigi 183204591Sluigi#endif /* _KERNEL */ 184204591Sluigi}; 185204591Sluigi 186204591Sluigi/* 187204591Sluigi * Delay line, contains all packets on output from a link. 188204591Sluigi * Every scheduler instance has one. 189204591Sluigi */ 190204591Sluigistruct delay_line { 191204591Sluigi struct dn_id oid; 192204591Sluigi struct dn_sch_inst *si; 193204591Sluigi struct mq mq; 194204591Sluigi}; 195204591Sluigi 196204591Sluigi/* 197204591Sluigi * The kernel side of a flowset. It is linked in a hash table 198204591Sluigi * of flowsets, and in a list of children of their parent scheduler. 199204591Sluigi * qht is either the queue or (if HAVE_MASK) a hash table queues. 200204591Sluigi * Note that the mask to use is the (flow_mask|sched_mask), which 201204591Sluigi * changes as we attach/detach schedulers. So we store it here. 202204591Sluigi * 203204591Sluigi * XXX If we want to add scheduler-specific parameters, we need to 204204591Sluigi * put them in external storage because the scheduler may not be 205204591Sluigi * available when the fsk is created. 206204591Sluigi */ 207204591Sluigistruct dn_fsk { /* kernel side of a flowset */ 208204591Sluigi struct dn_fs fs; 209204591Sluigi SLIST_ENTRY(dn_fsk) fsk_next; /* hash chain for fshash */ 210204591Sluigi 211204591Sluigi struct ipfw_flow_id fsk_mask; 212204591Sluigi 213204591Sluigi /* qht is a hash table of queues, or just a single queue 214204591Sluigi * a bit in fs.flags tells us which one 215204591Sluigi */ 216204591Sluigi struct dn_ht *qht; 217204591Sluigi struct dn_schk *sched; /* Sched we are linked to */ 218204591Sluigi SLIST_ENTRY(dn_fsk) sch_chain; /* list of fsk attached to sched */ 219204591Sluigi 220204591Sluigi /* bucket index used by drain routine to drain queues for this 221204591Sluigi * flowset 222204591Sluigi */ 223204591Sluigi int drain_bucket; 224204591Sluigi /* Parameter realted to RED / GRED */ 225204591Sluigi /* original values are in dn_fs*/ 226204591Sluigi int w_q ; /* queue weight (scaled) */ 227204591Sluigi int max_th ; /* maximum threshold for queue (scaled) */ 228204591Sluigi int min_th ; /* minimum threshold for queue (scaled) */ 229204591Sluigi int max_p ; /* maximum value for p_b (scaled) */ 230204591Sluigi 231204591Sluigi u_int c_1 ; /* max_p/(max_th-min_th) (scaled) */ 232204591Sluigi u_int c_2 ; /* max_p*min_th/(max_th-min_th) (scaled) */ 233204591Sluigi u_int c_3 ; /* for GRED, (1-max_p)/max_th (scaled) */ 234204591Sluigi u_int c_4 ; /* for GRED, 1 - 2*max_p (scaled) */ 235204591Sluigi u_int * w_q_lookup ; /* lookup table for computing (1-w_q)^t */ 236204591Sluigi u_int lookup_depth ; /* depth of lookup table */ 237204591Sluigi int lookup_step ; /* granularity inside the lookup table */ 238204591Sluigi int lookup_weight ; /* equal to (1-w_q)^t / (1-w_q)^(t+1) */ 239204591Sluigi int avg_pkt_size ; /* medium packet size */ 240204591Sluigi int max_pkt_size ; /* max packet size */ 241301772Struckman#ifdef NEW_AQM 242301772Struckman struct dn_aqm *aqmfp; /* Pointer to AQM functions */ 243301772Struckman void *aqmcfg; /* configuration parameters for AQM */ 244301772Struckman#endif 245204591Sluigi}; 246204591Sluigi 247204591Sluigi/* 248204591Sluigi * A queue is created as a child of a flowset unless it belongs to 249204591Sluigi * a !MULTIQUEUE scheduler. It is normally in a hash table in the 250204591Sluigi * flowset. fs always points to the parent flowset. 251204591Sluigi * si normally points to the sch_inst, unless the flowset has been 252204591Sluigi * detached from the scheduler -- in this case si == NULL and we 253204591Sluigi * should not enqueue. 254204591Sluigi */ 255204591Sluigistruct dn_queue { 256204591Sluigi struct dn_flow ni; /* oid, flow_id, stats */ 257204591Sluigi struct mq mq; /* packets queue */ 258204591Sluigi struct dn_sch_inst *_si; /* owner scheduler instance */ 259204591Sluigi SLIST_ENTRY(dn_queue) q_next; /* hash chain list for qht */ 260204591Sluigi struct dn_fsk *fs; /* parent flowset. */ 261204591Sluigi 262204591Sluigi /* RED parameters */ 263204591Sluigi int avg; /* average queue length est. (scaled) */ 264204591Sluigi int count; /* arrivals since last RED drop */ 265204591Sluigi int random; /* random value (scaled) */ 266204591Sluigi uint64_t q_time; /* start of queue idle time */ 267301772Struckman#ifdef NEW_AQM 268301772Struckman void *aqm_status; /* per-queue status variables*/ 269301772Struckman#endif 270204591Sluigi 271204591Sluigi}; 272204591Sluigi 273204591Sluigi/* 274204591Sluigi * The kernel side of a scheduler. Contains the userland config, 275204591Sluigi * a link, pointer to extra config arguments from command line, 276204591Sluigi * kernel flags, and a pointer to the scheduler methods. 277204591Sluigi * It is stored in a hash table, and holds a list of all 278204591Sluigi * flowsets and scheduler instances. 279204591Sluigi * XXX sch must be at the beginning, see schk_hash(). 280204591Sluigi */ 281204591Sluigistruct dn_schk { 282204591Sluigi struct dn_sch sch; 283204591Sluigi struct dn_alg *fp; /* Pointer to scheduler functions */ 284204591Sluigi struct dn_link link; /* The link, embedded */ 285204591Sluigi struct dn_profile *profile; /* delay profile, if any */ 286204591Sluigi struct dn_id *cfg; /* extra config arguments */ 287204591Sluigi 288204591Sluigi SLIST_ENTRY(dn_schk) schk_next; /* hash chain for schedhash */ 289204591Sluigi 290204591Sluigi struct dn_fsk_head fsk_list; /* all fsk linked to me */ 291204591Sluigi struct dn_fsk *fs; /* Flowset for !MULTIQUEUE */ 292204591Sluigi 293204591Sluigi /* bucket index used by the drain routine to drain the scheduler 294204591Sluigi * instance for this flowset. 295204591Sluigi */ 296204591Sluigi int drain_bucket; 297204591Sluigi 298204591Sluigi /* Hash table of all instances (through sch.sched_mask) 299204591Sluigi * or single instance if no mask. Always valid. 300204591Sluigi */ 301204591Sluigi struct dn_ht *siht; 302204591Sluigi}; 303204591Sluigi 304204591Sluigi 305204591Sluigi/* 306204591Sluigi * Scheduler instance. 307204591Sluigi * Contains variables and all queues relative to a this instance. 308204591Sluigi * This struct is created a runtime. 309204591Sluigi */ 310204591Sluigistruct dn_sch_inst { 311204591Sluigi struct dn_flow ni; /* oid, flowid and stats */ 312204591Sluigi SLIST_ENTRY(dn_sch_inst) si_next; /* hash chain for siht */ 313204591Sluigi struct delay_line dline; 314204591Sluigi struct dn_schk *sched; /* the template */ 315204591Sluigi int kflags; /* DN_ACTIVE */ 316204591Sluigi 317204591Sluigi int64_t credit; /* bits I can transmit (more or less). */ 318204591Sluigi uint64_t sched_time; /* time link was scheduled in ready_heap */ 319204591Sluigi uint64_t idle_time; /* start of scheduler instance idle time */ 320204591Sluigi 321204591Sluigi /* q_count is the number of queues that this instance is using. 322204591Sluigi * The counter is incremented or decremented when 323204591Sluigi * a reference from the queue is created or deleted. 324204591Sluigi * It is used to make sure that a scheduler instance can be safely 325204591Sluigi * deleted by the drain routine. See notes below. 326204591Sluigi */ 327204591Sluigi int q_count; 328204591Sluigi 329204591Sluigi}; 330204591Sluigi 331204591Sluigi/* 332204591Sluigi * NOTE about object drain. 333204591Sluigi * The system will automatically (XXX check when) drain queues and 334204591Sluigi * scheduler instances when they are idle. 335204591Sluigi * A queue is idle when it has no packets; an instance is idle when 336204591Sluigi * it is not in the evheap heap, and the corresponding delay line is empty. 337204591Sluigi * A queue can be safely deleted when it is idle because of the scheduler 338204591Sluigi * function xxx_free_queue() will remove any references to it. 339204591Sluigi * An instance can be only deleted when no queues reference it. To be sure 340204591Sluigi * of that, a counter (q_count) stores the number of queues that are pointing 341204591Sluigi * to the instance. 342204591Sluigi * 343204591Sluigi * XXX 344204591Sluigi * Order of scan: 345204591Sluigi * - take all flowset in a bucket for the flowset hash table 346204591Sluigi * - take all queues in a bucket for the flowset 347204591Sluigi * - increment the queue bucket 348204591Sluigi * - scan next flowset bucket 349204591Sluigi * Nothing is done if a bucket contains no entries. 350204591Sluigi * 351204591Sluigi * The same schema is used for sceduler instances 352204591Sluigi */ 353204591Sluigi 354204591Sluigi 355204591Sluigi/* kernel-side flags. Linux has DN_DELETE in fcntl.h 356204591Sluigi */ 357204591Sluigienum { 358204591Sluigi /* 1 and 2 are reserved for the SCAN flags */ 359204591Sluigi DN_DESTROY = 0x0004, /* destroy */ 360204591Sluigi DN_DELETE_FS = 0x0008, /* destroy flowset */ 361204591Sluigi DN_DETACH = 0x0010, 362204591Sluigi DN_ACTIVE = 0x0020, /* object is in evheap */ 363204591Sluigi DN_F_DLINE = 0x0040, /* object is a delay line */ 364213253Sluigi DN_DEL_SAFE = 0x0080, /* delete a queue only if no longer needed 365213253Sluigi * by scheduler */ 366204591Sluigi DN_QHT_IS_Q = 0x0100, /* in flowset, qht is a single queue */ 367204591Sluigi}; 368204591Sluigi 369325731Struckman/* 370325731Struckman * Packets processed by dummynet have an mbuf tag associated with 371325731Struckman * them that carries their dummynet state. 372325731Struckman * Outside dummynet, only the 'rule' field is relevant, and it must 373325731Struckman * be at the beginning of the structure. 374325731Struckman */ 375325731Struckmanstruct dn_pkt_tag { 376325731Struckman struct ipfw_rule_ref rule; /* matching rule */ 377325731Struckman 378325731Struckman /* second part, dummynet specific */ 379325731Struckman int dn_dir; /* action when packet comes out.*/ 380325731Struckman /* see ip_fw_private.h */ 381325731Struckman uint64_t output_time; /* when the pkt is due for delivery*/ 382325731Struckman struct ifnet *ifp; /* interface, for ip_output */ 383325731Struckman struct _ip6dn_args ip6opt; /* XXX ipv6 options */ 384325731Struckman uint16_t iphdr_off; /* IP header offset for mtodo() */ 385325731Struckman}; 386325731Struckman 387204591Sluigiextern struct dn_parms dn_cfg; 388206428Sluigi//VNET_DECLARE(struct dn_parms, _base_dn_cfg); 389213253Sluigi//#define dn_cfg VNET(_base_dn_cfg) 390204591Sluigi 391204591Sluigiint dummynet_io(struct mbuf **, int , struct ip_fw_args *); 392204591Sluigivoid dummynet_task(void *context, int pending); 393204591Sluigivoid dn_reschedule(void); 394325731Struckmanstruct dn_pkt_tag * dn_tag_get(struct mbuf *m); 395204591Sluigi 396204591Sluigistruct dn_queue *ipdn_q_find(struct dn_fsk *, struct dn_sch_inst *, 397204591Sluigi struct ipfw_flow_id *); 398204591Sluigistruct dn_sch_inst *ipdn_si_find(struct dn_schk *, struct ipfw_flow_id *); 399204591Sluigi 400205050Sluigi/* 401205050Sluigi * copy_range is a template for requests for ranges of pipes/queues/scheds. 402205050Sluigi * The number of ranges is variable and can be derived by o.len. 403205050Sluigi * As a default, we use a small number of entries so that the struct 404205050Sluigi * fits easily on the stack and is sufficient for most common requests. 405205050Sluigi */ 406205050Sluigi#define DEFAULT_RANGES 5 407205050Sluigistruct copy_range { 408205050Sluigi struct dn_id o; 409205050Sluigi uint32_t r[ 2 * DEFAULT_RANGES ]; 410205050Sluigi}; 411205050Sluigi 412204591Sluigistruct copy_args { 413204591Sluigi char **start; 414204591Sluigi char *end; 415204591Sluigi int flags; 416204591Sluigi int type; 417205050Sluigi struct copy_range *extra; /* extra filtering */ 418204591Sluigi}; 419204591Sluigi 420204591Sluigistruct sockopt; 421204591Sluigiint ip_dummynet_compat(struct sockopt *sopt); 422204591Sluigiint dummynet_get(struct sockopt *sopt, void **compat); 423204591Sluigiint dn_c_copy_q (void *_ni, void *arg); 424204591Sluigiint dn_c_copy_pipe(struct dn_schk *s, struct copy_args *a, int nq); 425204591Sluigiint dn_c_copy_fs(struct dn_fsk *f, struct copy_args *a, int nq); 426204591Sluigiint dn_compat_copy_queue(struct copy_args *a, void *_o); 427204591Sluigiint dn_compat_copy_pipe(struct copy_args *a, void *_o); 428204591Sluigiint copy_data_helper_compat(void *_o, void *_arg); 429206425Sluigiint dn_compat_calc_size(void); 430204591Sluigiint do_config(void *p, int l); 431204591Sluigi 432204591Sluigi/* function to drain idle object */ 433204591Sluigivoid dn_drain_scheduler(void); 434204591Sluigivoid dn_drain_queue(void); 435204591Sluigi 436301772Struckman#ifdef NEW_AQM 437301772Struckmanint ecn_mark(struct mbuf* m); 438301772Struckman 439301772Struckman/* moved from ip_dn_io.c to here to be available for AQMs modules*/ 440301772Struckmanstatic inline void 441301772Struckmanmq_append(struct mq *q, struct mbuf *m) 442301772Struckman{ 443301772Struckman if (q->head == NULL) 444301772Struckman q->head = m; 445301772Struckman else 446301772Struckman q->tail->m_nextpkt = m; 447301772Struckman q->tail = m; 448301772Struckman m->m_nextpkt = NULL; 449301772Struckman} 450301772Struckman#endif /* NEW_AQM */ 451301772Struckman 452204591Sluigi#endif /* _IP_DN_PRIVATE_H */ 453