├── .gitignore ├── Makefile ├── LICENSE ├── README.md └── sch_qjump.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | *.o 7 | *.log 8 | *.cmd 9 | *.so 10 | *.mod.c 11 | .settings 12 | Module.symvers 13 | .tmp* 14 | modules.order 15 | 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | obj-m += sch_qjump.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Matthew P. Grosvenor 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following 5 | conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following 8 | disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 11 | disclaimer in the documentation and/or other materials provided with the distribution. 12 | 13 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products 14 | derived from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 17 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 20 | USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 22 | OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # qjump-tc 2 | QJump Traffic Control Module - The heart of QJump 3 | 4 | To build, run "make" in the source directory. Make sure that you have kernel headers installed. 5 | 6 | To use, run with optional paramters decribed below. eg: 7 | sudo insmod sch_qjump.ko verbose=4 8 | 9 | The kernel module supports several paramters. 10 | - verbose (int): (in the range 0-4) which varies how verbose the module output is 11 | - bytesq (int): sets the number of bytes per epoch. This is the "P" parameter from the QJump equation 12 | - timeq (int): sets the time (in microseconds) per epoch. This is the result of the QJump equation. 13 | - pXrate (int): sets the the "f" value for QJump level X. Value is set as an integer multiple of the base rate given by bytesq/timeq. For example, setting p2rate to 5 indicates that the third highest priority level (p2) will receive 5x the number of bytes per epoch the highest prioirty (p0). 14 | - autolass (bool): turns on the "autoclassifier" which degrades an application until it reaches the right QJump level 15 | 16 | To check that the module has inserted properly use dmesg. eg: 17 | $ dmesg 18 | [ 2379.582475] sch_qjump: module license 'BSD' taints kernel. 19 | [ 2379.582479] Disabling lock debugging due to kernel taint 20 | [ 2379.583112] QJump[4]: Init module 21 | [ 2379.583126] QJump[4]: Calculating CPU speed 0 22 | [ 2380.583698] QJump[4]: CPU is running at 2799405818 cyles per second 23 | [ 2380.583701] QJump[4]: Calculating CPU speed 1 24 | [ 2381.584274] QJump[4]: CPU is running at 2799407672 cyles per second 25 | [ 2381.584277] QJump[4]: Calculating CPU speed 2 26 | [ 2382.584850] QJump[4]: CPU is running at 2799407705 cyles per second 27 | [ 2382.584853] QJump: Module parameteres: 28 | [ 2382.584853] ------------------------------- 29 | [ 2382.584854] QJump: timeq=100us 30 | [ 2382.584854] QJump: bytesq=100B 31 | [ 2382.584855] QJump: p7rate=10000 32 | [ 2382.584856] QJump: p6rate=0 33 | [ 2382.584856] QJump: p5rate=0 34 | [ 2382.584856] QJump: p4rate=1000 35 | [ 2382.584857] QJump: p3rate=100 36 | [ 2382.584857] QJump: p2rate=10 37 | [ 2382.584858] QJump: p1rate=5 38 | [ 2382.584858] QJump: p0rate=1 39 | [ 2382.584859] ------------------------------- 40 | [ 2382.584859] 41 | 42 | After inserting the module, you can bind qjump to an ethernet port using TC. eg: 43 | sudo tc qdisc add dev eth0 root qjump 44 | 45 | You can then check that the module is working e.g: 46 | $ dmesg 47 | [ 2519.047377] QJump[4]: Jump[4]: Delaying 279940 cycles per network tick (99us) 48 | [ 2519.047382] QJump[4]: Queue 0 = @ 10Mb/s 49 | [ 2519.047383] qjump[4]: Init fifo limit=128 50 | [ 2519.047384] Bands= 1 51 | 52 | To remove as a TC module: 53 | sudo tc qdisc del dev eth0 root qjump 54 | -------------------------------------------------------------------------------- /sch_qjump.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Matthew P. Grosvenor 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following 6 | * conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following 9 | * disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 12 | * disclaimer in the documentation and/or other materials provided with the distribution. 13 | * 14 | * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products 15 | * derived from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 18 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 21 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 23 | * OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #define SEC2NS (1000 * 1000 * 1000) 39 | #define SEC2US (1000 * 1000) 40 | #define USEC2NS (1000) 41 | 42 | static unsigned long bytesq = 128; 43 | module_param(bytesq, ulong, 0); 44 | 45 | static unsigned long timeq = 100; 46 | module_param(timeq, ulong, 0); 47 | 48 | #define P0RATE_DEFAULT 1 49 | #define P1RATE_DEFAULT 5 50 | #define P2RATE_DEFAULT 10 51 | #define P3RATE_DEFAULT 100 52 | #define P4RATE_DEFAULT 1000 53 | #define P5RATE_DEFAULT 0 54 | #define P6RATE_DEFAULT 0 55 | #define P7RATE_DEFAULT 10000 56 | 57 | static unsigned long p0rate = P0RATE_DEFAULT; 58 | static unsigned long p1rate = P1RATE_DEFAULT; 59 | static unsigned long p2rate = P2RATE_DEFAULT; 60 | static unsigned long p3rate = P3RATE_DEFAULT; 61 | static unsigned long p4rate = P4RATE_DEFAULT; 62 | static unsigned long p5rate = P5RATE_DEFAULT; 63 | static unsigned long p6rate = P6RATE_DEFAULT; 64 | static unsigned long p7rate = P7RATE_DEFAULT; 65 | 66 | module_param(p0rate, ulong, 0); 67 | module_param(p1rate, ulong, 0); 68 | module_param(p2rate, ulong, 0); 69 | module_param(p3rate, ulong, 0); 70 | module_param(p4rate, ulong, 0); 71 | //module_param(p5rate, ulong, 0); 72 | //module_param(p6rate, ulong, 0); 73 | module_param(p7rate, ulong, 0); 74 | 75 | 76 | static unsigned long autoclass = 0; 77 | module_param(autoclass, ulong, 0); 78 | 79 | 80 | static unsigned long verbose = 0; 81 | module_param(verbose, ulong, 0); 82 | 83 | static u64 frequency = 0; 84 | static u64 time_quant_cyles = 0; 85 | 86 | 87 | 88 | //Map of prioirty level to the packet rate multipler. 89 | //Assumes >= 8 queues. This should be generalized across nics. 90 | uint64_t prates_map[8] = { 0 }; 91 | 92 | struct qjump_fifo_priv{ 93 | u64 bytes_left; 94 | u64 next_timeout_cyles; 95 | int drop; 96 | #define CYCLE_STATS_LEN (128) 97 | u64 cycles_consumed[CYCLE_STATS_LEN]; 98 | u64 index; 99 | }; 100 | 101 | 102 | static int qfifo_enqueue(struct sk_buff *skb, struct Qdisc *sch) 103 | { 104 | struct qjump_fifo_priv* priv = qdisc_priv(sch); 105 | struct timespec ts; 106 | const u64 ts_now_cycles = get_cycles(); 107 | u64 ts_now_ns = 0; 108 | getnstimeofday(&ts); 109 | ts_now_ns = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec; 110 | 111 | if(ts_now_cycles >= priv->next_timeout_cyles){ 112 | priv->next_timeout_cyles = ts_now_cycles + time_quant_cyles; 113 | 114 | if(verbose>=3) printk("QJump[%lu]: (%u) Reset: %u,%u,%u,%llu,%llu,%llu,%lli,%llu\n", 115 | verbose, sch->limit, sch->qstats.backlog, qdisc_pkt_len(skb), sch->limit, priv->bytes_left, 116 | priv->next_timeout_cyles , ts_now_cycles, (priv->next_timeout_cyles - ts_now_cycles), ts_now_ns 117 | ); 118 | priv->bytes_left = sch->limit; 119 | priv->drop = 0; 120 | } 121 | 122 | if (likely(qdisc_pkt_len(skb) <= priv->bytes_left)){ 123 | int ret = 0; 124 | if(verbose>=3) printk("QJump[%lu]: (%u) Forwarded: %u,%u,%u,%llu,%llu,%llu,%lli,%llu\n", 125 | verbose, sch->limit, sch->qstats.backlog, qdisc_pkt_len(skb), sch->limit, priv->bytes_left, 126 | priv->next_timeout_cyles , ts_now_cycles, (priv->next_timeout_cyles - ts_now_cycles), ts_now_ns 127 | ); 128 | ret = qdisc_enqueue_tail(skb, sch); 129 | priv->bytes_left -= qdisc_pkt_len(skb); 130 | priv->cycles_consumed[priv->index]= get_cycles() - ts_now_cycles; 131 | priv->index = (priv->index + 1) % (CYCLE_STATS_LEN); 132 | return ret; 133 | } 134 | 135 | if((1 || (!priv->drop && verbose >=1)) || verbose >= 3){ 136 | if(verbose>=2) printk("QJump[%lu]: (%u) Dropped: %u,%u,%u,%llu,%llu,%llu,%lli,%llu\n", 137 | verbose, sch->limit, sch->qstats.backlog, qdisc_pkt_len(skb), sch->limit, priv->bytes_left, 138 | priv->next_timeout_cyles , ts_now_cycles, (priv->next_timeout_cyles - ts_now_cycles), 139 | ts_now_ns 140 | ); 141 | priv->drop = 1; 142 | } 143 | 144 | sch->qstats.drops++; 145 | priv->cycles_consumed[priv->index]= get_cycles() - ts_now_cycles; 146 | priv->index = (priv->index + 1) % (CYCLE_STATS_LEN); 147 | return NET_XMIT_DROP; 148 | 149 | } 150 | 151 | 152 | static int qfifo_init(struct Qdisc *sch, struct nlattr *opt) 153 | { 154 | sch->flags &= ~TCQ_F_CAN_BYPASS; 155 | return 0; 156 | } 157 | 158 | 159 | struct Qdisc_ops qjump_fifo_qdisc_ops __read_mostly = { 160 | .id = "qjump_fifo", 161 | .priv_size = sizeof(struct qjump_fifo_priv), 162 | .enqueue = qfifo_enqueue, 163 | .dequeue = qdisc_dequeue_head, 164 | .peek = qdisc_peek_head, 165 | .drop = qdisc_queue_drop, 166 | .init = qfifo_init, 167 | .reset = qdisc_reset_queue, 168 | .change = qfifo_init, 169 | }; 170 | 171 | 172 | struct Qdisc *qjump_fifo_create_dflt(struct netdev_queue *dev_queue, struct Qdisc *sch, struct Qdisc_ops *ops, unsigned int limit) 173 | { 174 | struct Qdisc *q; 175 | int err = -ENOMEM; 176 | //struct timespec ts; //Allow QJump to be run directly from getnstimeofday() 177 | 178 | 179 | if(verbose >= 1) printk("qjump[%lu]: Init fifo limit=%u\n", verbose, limit); 180 | 181 | q = qdisc_create_dflt(dev_queue, ops, TC_H_MAKE(sch->handle, 1)); 182 | if (q) { 183 | struct qjump_fifo_priv* priv = qdisc_priv(q); 184 | u64 ts_now_cycles = 0; 185 | q->limit = limit; 186 | priv->bytes_left = limit; 187 | priv->drop = 0; 188 | priv->index = 0; 189 | //ts_now_cycles = get_cycles(); 190 | //getnstimeofday(&ts); 191 | //ts_now_cycles = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec; //get_cycles(); 192 | ts_now_cycles = get_cycles(); 193 | 194 | priv->next_timeout_cyles = ts_now_cycles + time_quant_cyles; 195 | 196 | } 197 | 198 | return q ? : ERR_PTR(err); 199 | } 200 | 201 | 202 | struct qjump_sched_data { 203 | u16 bands; 204 | u16 max_bands; 205 | u16 curband; 206 | struct tcf_proto *filter_list; 207 | struct Qdisc **queues; 208 | }; 209 | 210 | 211 | static struct Qdisc * qjump_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) 212 | { 213 | struct qjump_sched_data *q = qdisc_priv(sch); 214 | 215 | skb->priority = skb->priority >= q->bands ? q->bands - 1 : skb->priority; 216 | skb->queue_mapping = skb->priority; 217 | if(verbose >= 5) printk("QJump[%lu]: Classify priority=%u queue_mapping=%i\n", verbose, skb->priority, skb->queue_mapping); 218 | 219 | return q->queues[skb->priority]; 220 | } 221 | 222 | 223 | static int qjump_enqueue(struct sk_buff *skb, struct Qdisc *sch) 224 | { 225 | struct Qdisc *qdisc; 226 | int ret; 227 | 228 | qdisc = qjump_classify(skb, sch, &ret); 229 | 230 | ret = qdisc->enqueue(skb, qdisc); 231 | if (ret == NET_XMIT_SUCCESS) { 232 | sch->q.qlen++; 233 | return NET_XMIT_SUCCESS; 234 | } 235 | 236 | 237 | //Auto classifer will keep trying to send you until a) you reach 0 or b) you send. 238 | while(autoclass && skb->priority){ 239 | int old_pri = skb->priority; 240 | 241 | //Deal with the discontinuity between 3 and 0 242 | if(skb->priority == 3){ 243 | skb->priority = 1; 244 | } 245 | 246 | skb->priority--; 247 | 248 | 249 | if(verbose>=2){ 250 | u64 ts_now_ns = 0; 251 | struct timespec ts = {0}; 252 | getnstimeofday(&ts); 253 | ts_now_ns = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec; 254 | printk("QJump[%lu]: (%u) Downgraded: %u,%u,%u,%llu", 255 | verbose, qdisc->limit, qdisc_pkt_len(skb), old_pri, skb->priority, ts_now_ns 256 | ); 257 | } 258 | 259 | 260 | qdisc = qjump_classify(skb, sch, &ret); 261 | ret = qdisc->enqueue(skb, qdisc); 262 | if (ret == NET_XMIT_SUCCESS) { 263 | skb->sk->sk_priority = skb->priority; //Make sure we come here next time. 264 | sch->q.qlen++; 265 | return NET_XMIT_SUCCESS; 266 | } 267 | } 268 | 269 | return qdisc_reshape_fail(skb, sch); 270 | } 271 | 272 | static struct sk_buff *qjump_dequeue(struct Qdisc *sch) 273 | { 274 | 275 | struct qjump_sched_data *q = qdisc_priv(sch); 276 | struct Qdisc *qdisc; 277 | struct sk_buff *skb; 278 | int band; 279 | 280 | for (band = 0; band < q->bands; band++) { 281 | 282 | if (!__netif_subqueue_stopped(qdisc_dev(sch), q->bands - 1 - band)) { 283 | qdisc = q->queues[q->bands - 1 - band]; 284 | skb = qdisc->dequeue(qdisc); 285 | if (skb) { 286 | qdisc_bstats_update(sch, skb); 287 | sch->q.qlen--; 288 | return skb; 289 | } 290 | } 291 | } 292 | return NULL; 293 | 294 | } 295 | 296 | static struct sk_buff *qjump_peek(struct Qdisc *sch) 297 | { 298 | struct qjump_sched_data *q = qdisc_priv(sch); 299 | struct Qdisc *qdisc; 300 | struct sk_buff *skb; 301 | int band; 302 | 303 | for (band = 0; band < q->bands; band++) { 304 | 305 | /* Check that target subqueue is available before 306 | * pulling an skb to avoid head-of-line blocking. 307 | */ 308 | if (!__netif_subqueue_stopped(qdisc_dev(sch), q->bands - 1 - band)) { 309 | qdisc = q->queues[q->bands - 1 - band]; 310 | skb = qdisc->ops->peek(qdisc); 311 | if (skb) 312 | return skb; 313 | } 314 | } 315 | return NULL; 316 | 317 | } 318 | 319 | static unsigned int qjump_drop(struct Qdisc *sch) 320 | { 321 | struct qjump_sched_data *q = qdisc_priv(sch); 322 | int band; 323 | unsigned int len; 324 | struct Qdisc *qdisc; 325 | 326 | for (band = q->bands - 1; band >= 0; band--) { 327 | qdisc = q->queues[band]; 328 | if (qdisc->ops->drop) { 329 | len = qdisc->ops->drop(qdisc); 330 | if (len != 0) { 331 | sch->q.qlen--; 332 | return len; 333 | } 334 | } 335 | } 336 | return 0; 337 | } 338 | 339 | 340 | static void qjump_reset(struct Qdisc *sch) 341 | { 342 | u16 band; 343 | struct qjump_sched_data *q = qdisc_priv(sch); 344 | 345 | for (band = 0; band < q->bands; band++) 346 | qdisc_reset(q->queues[band]); 347 | sch->q.qlen = 0; 348 | q->curband = 0; 349 | } 350 | 351 | static void qjump_destroy(struct Qdisc *sch) 352 | { 353 | int band = 0; 354 | struct qjump_sched_data *q = qdisc_priv(sch); 355 | 356 | if(verbose >= 1) printk("qjump[%lu]: Destroying queues\n", verbose); 357 | 358 | printk("QJump[STATS] --------------------------------------------\n"); 359 | printk("QJump[STATS] frequency %lluHz", frequency); 360 | if (q->queues) { 361 | for (band = 0; band < q->bands; band++){ 362 | struct Qdisc* sub = q->queues[band]; 363 | struct qjump_fifo_priv* priv = qdisc_priv(sub); 364 | int i = 0; 365 | for(i=0; i < CYCLE_STATS_LEN; i++ ){ 366 | printk("QJump[STATS] %i %i %llu\n", band, i, priv->cycles_consumed[i]); 367 | } 368 | 369 | if(verbose >= 2) printk("QJump[%lu]: Destroying queues %i\n", verbose, band); 370 | sub->ops->reset(sub); 371 | dev_put(qdisc_dev(sub)); 372 | kfree_skb(sub->gso_skb); 373 | 374 | } 375 | 376 | kfree(q->queues); 377 | } 378 | printk("QJump[STATS] --------------------------------------------\n"); 379 | 380 | //qdisc_dev(sch)->netdev_ops->ndo_setup_tc(qdisc_dev(sch), 0); 381 | netdev_set_num_tc(qdisc_dev(sch), 0); 382 | } 383 | 384 | 385 | 386 | static int qjump_init(struct Qdisc *sch, struct nlattr *opt) 387 | { 388 | struct qjump_sched_data *q = qdisc_priv(sch); 389 | int i; //, err; 390 | struct net_device *dev = qdisc_dev(sch); 391 | struct netdev_queue *dev_queue; 392 | 393 | if(verbose >= 1 ) printk("QJump[%lu]: Init QJump QDisc", verbose); 394 | 395 | q->queues = NULL; 396 | q->max_bands = qdisc_dev(sch)->real_num_tx_queues; 397 | q->max_bands = q->max_bands > 8 ? 8 : q->max_bands; 398 | 399 | //Assign the packet rate map 400 | prates_map[0] = p0rate; 401 | prates_map[1] = p1rate; 402 | prates_map[2] = p2rate; 403 | prates_map[3] = p3rate; 404 | prates_map[4] = p4rate; 405 | prates_map[5] = p5rate; 406 | prates_map[6] = p6rate; 407 | prates_map[7] = p7rate; 408 | 409 | time_quant_cyles = timeq * frequency / SEC2US; 410 | if(verbose >=0 ) printk("QJump[%lu]: Delaying %llu cycles per network tick (%lluus)\n", verbose, time_quant_cyles, (time_quant_cyles * 1000 * 1000) / frequency ); 411 | 412 | q->queues = kcalloc(q->max_bands, sizeof(struct Qdisc *), GFP_KERNEL); 413 | if (!q->queues){ 414 | return -ENOBUFS; 415 | } 416 | 417 | 418 | for (i = 0; i < q->max_bands; i++){ 419 | if(verbose >= 0) printk("QJump[%lu]: Queue %u = @ %lluMb/s \n", verbose, q->max_bands -1 - i, prates_map[i]*bytesq * 8 / timeq ); 420 | dev_queue = netdev_get_tx_queue(dev, q->max_bands -1 - i); 421 | q->queues[q->max_bands -1 - i] = qjump_fifo_create_dflt(dev_queue,sch,&qjump_fifo_qdisc_ops,prates_map[i]*bytesq); 422 | } 423 | 424 | 425 | q->bands = q->max_bands; 426 | printk("Bands= %u\n", q->bands); 427 | 428 | return 0; //err; 429 | } 430 | 431 | struct Qdisc_ops qjump_qdisc_ops __read_mostly = { 432 | .next = NULL, 433 | .id = "qjump", 434 | .priv_size = sizeof(struct qjump_sched_data), 435 | .enqueue = qjump_enqueue, 436 | .dequeue = qjump_dequeue, 437 | .peek = qjump_peek, 438 | .drop = qjump_drop, 439 | .init = qjump_init, 440 | .reset = qjump_reset, 441 | .destroy = qjump_destroy, 442 | .owner = THIS_MODULE, 443 | }; 444 | 445 | static int __init qjump_module_init(void) 446 | { 447 | struct timespec ts; 448 | u64 ts_start_ns = 0; 449 | u64 ts_end_ns = 0; 450 | u64 start_cycles = 0; 451 | u64 end_cycles = 0; 452 | int has_invariant_tsc = 0; 453 | unsigned int eax, ebx, ecx, edx; 454 | int i = 0; 455 | 456 | if(verbose >= 1) printk("QJump[%lu]: Init module\n", verbose); 457 | 458 | asm("cpuid" : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (0x80000007)); 459 | has_invariant_tsc = edx & (1 << 8); 460 | 461 | if(!has_invariant_tsc){ 462 | printk("QJump[%lu]: Cannot run qjump on machines without an invaraint TSC. Terminating\n", verbose); 463 | return -1; 464 | } 465 | 466 | for(i = 0; i <3; i++){ 467 | getnstimeofday(&ts); 468 | ts_start_ns = (ts.tv_nsec + ts.tv_sec * SEC2NS ); 469 | if(verbose >= 0 ) printk("QJump[%lu]: Calculating CPU speed %i\n", verbose, i); 470 | start_cycles = get_cycles(); 471 | while(1){ 472 | getnstimeofday(&ts); 473 | ts_end_ns = (ts.tv_nsec + ts.tv_sec * SEC2NS ); 474 | if((ts_end_ns - ts_start_ns) >= 1 * SEC2NS){ 475 | end_cycles = get_cycles(); 476 | break; 477 | } 478 | } 479 | frequency = end_cycles - start_cycles; 480 | if(verbose >= 0 ) printk("QJump[%lu]: CPU is running at %llu cyles per second\n", verbose, frequency); 481 | } 482 | 483 | if(verbose >= 1) printk("QJump: Module parameteres:\n"); 484 | if(verbose >= 1) printk("-------------------------------\n"); 485 | if(verbose >= 1) printk("QJump: timeq=%luus\n", timeq); 486 | if(verbose >= 1) printk("QJump: bytesq=%luB\n", timeq); 487 | if(verbose >= 1) printk("QJump: p7rate=%lu\n", p7rate); 488 | if(verbose >= 1) printk("QJump: p6rate=%lu\n", p6rate); 489 | if(verbose >= 1) printk("QJump: p5rate=%lu\n", p5rate); 490 | if(verbose >= 1) printk("QJump: p4rate=%lu\n", p4rate); 491 | if(verbose >= 1) printk("QJump: p3rate=%lu\n", p3rate); 492 | if(verbose >= 1) printk("QJump: p2rate=%lu\n", p2rate); 493 | if(verbose >= 1) printk("QJump: p1rate=%lu\n", p1rate); 494 | if(verbose >= 1) printk("QJump: p0rate=%lu\n", p0rate); 495 | if(verbose >= 1) printk("-------------------------------\n\n"); 496 | 497 | return register_qdisc(&qjump_qdisc_ops); 498 | } 499 | 500 | static void __exit qjump_module_exit(void) 501 | { 502 | printk("QJump[%lu]: QJump Module exit\n", verbose); 503 | unregister_qdisc(&qjump_qdisc_ops); 504 | } 505 | 506 | module_init(qjump_module_init) 507 | module_exit(qjump_module_exit) 508 | 509 | MODULE_LICENSE("BSD"); 510 | --------------------------------------------------------------------------------