192651940SAlexander Duyck /* 292651940SAlexander Duyck * Copyright (c) 2008, Intel Corporation. 392651940SAlexander Duyck * 492651940SAlexander Duyck * This program is free software; you can redistribute it and/or modify it 592651940SAlexander Duyck * under the terms and conditions of the GNU General Public License, 692651940SAlexander Duyck * version 2, as published by the Free Software Foundation. 792651940SAlexander Duyck * 892651940SAlexander Duyck * This program is distributed in the hope it will be useful, but WITHOUT 992651940SAlexander Duyck * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1092651940SAlexander Duyck * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 1192651940SAlexander Duyck * more details. 1292651940SAlexander Duyck * 1392651940SAlexander Duyck * You should have received a copy of the GNU General Public License along with 14c057b190SJeff Kirsher * this program; if not, see <http://www.gnu.org/licenses/>. 1592651940SAlexander Duyck * 1692651940SAlexander Duyck * Author: Alexander Duyck <alexander.h.duyck@intel.com> 1792651940SAlexander Duyck */ 1892651940SAlexander Duyck 1992651940SAlexander Duyck #include <linux/module.h> 205a0e3ad6STejun Heo #include <linux/slab.h> 2192651940SAlexander Duyck #include <linux/types.h> 2292651940SAlexander Duyck #include <linux/kernel.h> 2392651940SAlexander Duyck #include <linux/string.h> 2492651940SAlexander Duyck #include <linux/errno.h> 2592651940SAlexander Duyck #include <linux/skbuff.h> 2692651940SAlexander Duyck #include <net/netlink.h> 2792651940SAlexander Duyck #include <net/pkt_sched.h> 28cf1facdaSJiri Pirko #include <net/pkt_cls.h> 2992651940SAlexander Duyck 3092651940SAlexander Duyck struct multiq_sched_data { 3192651940SAlexander Duyck u16 bands; 3292651940SAlexander Duyck u16 max_bands; 3392651940SAlexander Duyck u16 curband; 3425d8c0d5SJohn Fastabend struct tcf_proto __rcu *filter_list; 3592651940SAlexander Duyck struct Qdisc **queues; 3692651940SAlexander Duyck }; 3792651940SAlexander Duyck 3892651940SAlexander Duyck 3992651940SAlexander Duyck static struct Qdisc * 4092651940SAlexander Duyck multiq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) 4192651940SAlexander Duyck { 4292651940SAlexander Duyck struct multiq_sched_data *q = qdisc_priv(sch); 4392651940SAlexander Duyck u32 band; 4492651940SAlexander Duyck struct tcf_result res; 4525d8c0d5SJohn Fastabend struct tcf_proto *fl = rcu_dereference_bh(q->filter_list); 4692651940SAlexander Duyck int err; 4792651940SAlexander Duyck 4892651940SAlexander Duyck *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; 493b3ae880SDaniel Borkmann err = tc_classify(skb, fl, &res, false); 5092651940SAlexander Duyck #ifdef CONFIG_NET_CLS_ACT 5192651940SAlexander Duyck switch (err) { 5292651940SAlexander Duyck case TC_ACT_STOLEN: 5392651940SAlexander Duyck case TC_ACT_QUEUED: 5492651940SAlexander Duyck *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; 5592651940SAlexander Duyck case TC_ACT_SHOT: 5692651940SAlexander Duyck return NULL; 5792651940SAlexander Duyck } 5892651940SAlexander Duyck #endif 5992651940SAlexander Duyck band = skb_get_queue_mapping(skb); 6092651940SAlexander Duyck 6192651940SAlexander Duyck if (band >= q->bands) 6292651940SAlexander Duyck return q->queues[0]; 6392651940SAlexander Duyck 6492651940SAlexander Duyck return q->queues[band]; 6592651940SAlexander Duyck } 6692651940SAlexander Duyck 6792651940SAlexander Duyck static int 68520ac30fSEric Dumazet multiq_enqueue(struct sk_buff *skb, struct Qdisc *sch, 69520ac30fSEric Dumazet struct sk_buff **to_free) 7092651940SAlexander Duyck { 7192651940SAlexander Duyck struct Qdisc *qdisc; 7292651940SAlexander Duyck int ret; 7392651940SAlexander Duyck 7492651940SAlexander Duyck qdisc = multiq_classify(skb, sch, &ret); 7592651940SAlexander Duyck #ifdef CONFIG_NET_CLS_ACT 7692651940SAlexander Duyck if (qdisc == NULL) { 7792651940SAlexander Duyck 7892651940SAlexander Duyck if (ret & __NET_XMIT_BYPASS) 7925331d6cSJohn Fastabend qdisc_qstats_drop(sch); 80520ac30fSEric Dumazet __qdisc_drop(skb, to_free); 8192651940SAlexander Duyck return ret; 8292651940SAlexander Duyck } 8392651940SAlexander Duyck #endif 8492651940SAlexander Duyck 85520ac30fSEric Dumazet ret = qdisc_enqueue(skb, qdisc, to_free); 8692651940SAlexander Duyck if (ret == NET_XMIT_SUCCESS) { 8792651940SAlexander Duyck sch->q.qlen++; 8892651940SAlexander Duyck return NET_XMIT_SUCCESS; 8992651940SAlexander Duyck } 9092651940SAlexander Duyck if (net_xmit_drop_count(ret)) 9125331d6cSJohn Fastabend qdisc_qstats_drop(sch); 9292651940SAlexander Duyck return ret; 9392651940SAlexander Duyck } 9492651940SAlexander Duyck 9592651940SAlexander Duyck static struct sk_buff *multiq_dequeue(struct Qdisc *sch) 9692651940SAlexander Duyck { 9792651940SAlexander Duyck struct multiq_sched_data *q = qdisc_priv(sch); 9892651940SAlexander Duyck struct Qdisc *qdisc; 9992651940SAlexander Duyck struct sk_buff *skb; 10092651940SAlexander Duyck int band; 10192651940SAlexander Duyck 10292651940SAlexander Duyck for (band = 0; band < q->bands; band++) { 10392651940SAlexander Duyck /* cycle through bands to ensure fairness */ 10492651940SAlexander Duyck q->curband++; 10592651940SAlexander Duyck if (q->curband >= q->bands) 10692651940SAlexander Duyck q->curband = 0; 10792651940SAlexander Duyck 10892651940SAlexander Duyck /* Check that target subqueue is available before 109f30ab418SJarek Poplawski * pulling an skb to avoid head-of-line blocking. 11092651940SAlexander Duyck */ 11173466498STom Herbert if (!netif_xmit_stopped( 11273466498STom Herbert netdev_get_tx_queue(qdisc_dev(sch), q->curband))) { 11392651940SAlexander Duyck qdisc = q->queues[q->curband]; 11492651940SAlexander Duyck skb = qdisc->dequeue(qdisc); 11592651940SAlexander Duyck if (skb) { 1169190b3b3SEric Dumazet qdisc_bstats_update(sch, skb); 11792651940SAlexander Duyck sch->q.qlen--; 11892651940SAlexander Duyck return skb; 11992651940SAlexander Duyck } 12092651940SAlexander Duyck } 12192651940SAlexander Duyck } 12292651940SAlexander Duyck return NULL; 12392651940SAlexander Duyck 12492651940SAlexander Duyck } 12592651940SAlexander Duyck 1268e3af978SJarek Poplawski static struct sk_buff *multiq_peek(struct Qdisc *sch) 1278e3af978SJarek Poplawski { 1288e3af978SJarek Poplawski struct multiq_sched_data *q = qdisc_priv(sch); 1298e3af978SJarek Poplawski unsigned int curband = q->curband; 1308e3af978SJarek Poplawski struct Qdisc *qdisc; 1318e3af978SJarek Poplawski struct sk_buff *skb; 1328e3af978SJarek Poplawski int band; 1338e3af978SJarek Poplawski 1348e3af978SJarek Poplawski for (band = 0; band < q->bands; band++) { 1358e3af978SJarek Poplawski /* cycle through bands to ensure fairness */ 1368e3af978SJarek Poplawski curband++; 1378e3af978SJarek Poplawski if (curband >= q->bands) 1388e3af978SJarek Poplawski curband = 0; 1398e3af978SJarek Poplawski 1408e3af978SJarek Poplawski /* Check that target subqueue is available before 141f30ab418SJarek Poplawski * pulling an skb to avoid head-of-line blocking. 1428e3af978SJarek Poplawski */ 14373466498STom Herbert if (!netif_xmit_stopped( 14473466498STom Herbert netdev_get_tx_queue(qdisc_dev(sch), curband))) { 1458e3af978SJarek Poplawski qdisc = q->queues[curband]; 1468e3af978SJarek Poplawski skb = qdisc->ops->peek(qdisc); 1478e3af978SJarek Poplawski if (skb) 1488e3af978SJarek Poplawski return skb; 1498e3af978SJarek Poplawski } 1508e3af978SJarek Poplawski } 1518e3af978SJarek Poplawski return NULL; 1528e3af978SJarek Poplawski 1538e3af978SJarek Poplawski } 1548e3af978SJarek Poplawski 15592651940SAlexander Duyck static void 15692651940SAlexander Duyck multiq_reset(struct Qdisc *sch) 15792651940SAlexander Duyck { 15892651940SAlexander Duyck u16 band; 15992651940SAlexander Duyck struct multiq_sched_data *q = qdisc_priv(sch); 16092651940SAlexander Duyck 16192651940SAlexander Duyck for (band = 0; band < q->bands; band++) 16292651940SAlexander Duyck qdisc_reset(q->queues[band]); 16392651940SAlexander Duyck sch->q.qlen = 0; 16492651940SAlexander Duyck q->curband = 0; 16592651940SAlexander Duyck } 16692651940SAlexander Duyck 16792651940SAlexander Duyck static void 16892651940SAlexander Duyck multiq_destroy(struct Qdisc *sch) 16992651940SAlexander Duyck { 17092651940SAlexander Duyck int band; 17192651940SAlexander Duyck struct multiq_sched_data *q = qdisc_priv(sch); 17292651940SAlexander Duyck 17392651940SAlexander Duyck tcf_destroy_chain(&q->filter_list); 17492651940SAlexander Duyck for (band = 0; band < q->bands; band++) 17592651940SAlexander Duyck qdisc_destroy(q->queues[band]); 17692651940SAlexander Duyck 17792651940SAlexander Duyck kfree(q->queues); 17892651940SAlexander Duyck } 17992651940SAlexander Duyck 18092651940SAlexander Duyck static int multiq_tune(struct Qdisc *sch, struct nlattr *opt) 18192651940SAlexander Duyck { 18292651940SAlexander Duyck struct multiq_sched_data *q = qdisc_priv(sch); 18392651940SAlexander Duyck struct tc_multiq_qopt *qopt; 18492651940SAlexander Duyck int i; 18592651940SAlexander Duyck 18692651940SAlexander Duyck if (!netif_is_multiqueue(qdisc_dev(sch))) 187149490f1SJarek Poplawski return -EOPNOTSUPP; 18892651940SAlexander Duyck if (nla_len(opt) < sizeof(*qopt)) 18992651940SAlexander Duyck return -EINVAL; 19092651940SAlexander Duyck 19192651940SAlexander Duyck qopt = nla_data(opt); 19292651940SAlexander Duyck 19392651940SAlexander Duyck qopt->bands = qdisc_dev(sch)->real_num_tx_queues; 19492651940SAlexander Duyck 19592651940SAlexander Duyck sch_tree_lock(sch); 19692651940SAlexander Duyck q->bands = qopt->bands; 19792651940SAlexander Duyck for (i = q->bands; i < q->max_bands; i++) { 198f07d1501SAlexander Duyck if (q->queues[i] != &noop_qdisc) { 199b94c8afcSPatrick McHardy struct Qdisc *child = q->queues[i]; 200b94c8afcSPatrick McHardy q->queues[i] = &noop_qdisc; 2012ccccf5fSWANG Cong qdisc_tree_reduce_backlog(child, child->q.qlen, 2022ccccf5fSWANG Cong child->qstats.backlog); 20392651940SAlexander Duyck qdisc_destroy(child); 20492651940SAlexander Duyck } 20592651940SAlexander Duyck } 20692651940SAlexander Duyck 20792651940SAlexander Duyck sch_tree_unlock(sch); 20892651940SAlexander Duyck 20992651940SAlexander Duyck for (i = 0; i < q->bands; i++) { 21092651940SAlexander Duyck if (q->queues[i] == &noop_qdisc) { 211b94c8afcSPatrick McHardy struct Qdisc *child, *old; 2123511c913SChangli Gao child = qdisc_create_dflt(sch->dev_queue, 21392651940SAlexander Duyck &pfifo_qdisc_ops, 21492651940SAlexander Duyck TC_H_MAKE(sch->handle, 21592651940SAlexander Duyck i + 1)); 21692651940SAlexander Duyck if (child) { 21792651940SAlexander Duyck sch_tree_lock(sch); 218b94c8afcSPatrick McHardy old = q->queues[i]; 219b94c8afcSPatrick McHardy q->queues[i] = child; 220*49b49971SJiri Kosina if (child != &noop_qdisc) 221*49b49971SJiri Kosina qdisc_hash_add(child, true); 22292651940SAlexander Duyck 223b94c8afcSPatrick McHardy if (old != &noop_qdisc) { 2242ccccf5fSWANG Cong qdisc_tree_reduce_backlog(old, 2252ccccf5fSWANG Cong old->q.qlen, 2262ccccf5fSWANG Cong old->qstats.backlog); 227b94c8afcSPatrick McHardy qdisc_destroy(old); 22892651940SAlexander Duyck } 22992651940SAlexander Duyck sch_tree_unlock(sch); 23092651940SAlexander Duyck } 23192651940SAlexander Duyck } 23292651940SAlexander Duyck } 23392651940SAlexander Duyck return 0; 23492651940SAlexander Duyck } 23592651940SAlexander Duyck 23692651940SAlexander Duyck static int multiq_init(struct Qdisc *sch, struct nlattr *opt) 23792651940SAlexander Duyck { 23892651940SAlexander Duyck struct multiq_sched_data *q = qdisc_priv(sch); 239f07d1501SAlexander Duyck int i, err; 24092651940SAlexander Duyck 24192651940SAlexander Duyck q->queues = NULL; 24292651940SAlexander Duyck 24392651940SAlexander Duyck if (opt == NULL) 24492651940SAlexander Duyck return -EINVAL; 24592651940SAlexander Duyck 24692651940SAlexander Duyck q->max_bands = qdisc_dev(sch)->num_tx_queues; 24792651940SAlexander Duyck 24892651940SAlexander Duyck q->queues = kcalloc(q->max_bands, sizeof(struct Qdisc *), GFP_KERNEL); 24992651940SAlexander Duyck if (!q->queues) 25092651940SAlexander Duyck return -ENOBUFS; 25192651940SAlexander Duyck for (i = 0; i < q->max_bands; i++) 25292651940SAlexander Duyck q->queues[i] = &noop_qdisc; 25392651940SAlexander Duyck 254f07d1501SAlexander Duyck err = multiq_tune(sch, opt); 255f07d1501SAlexander Duyck 256f07d1501SAlexander Duyck if (err) 257f07d1501SAlexander Duyck kfree(q->queues); 258f07d1501SAlexander Duyck 259f07d1501SAlexander Duyck return err; 26092651940SAlexander Duyck } 26192651940SAlexander Duyck 26292651940SAlexander Duyck static int multiq_dump(struct Qdisc *sch, struct sk_buff *skb) 26392651940SAlexander Duyck { 26492651940SAlexander Duyck struct multiq_sched_data *q = qdisc_priv(sch); 26592651940SAlexander Duyck unsigned char *b = skb_tail_pointer(skb); 26692651940SAlexander Duyck struct tc_multiq_qopt opt; 26792651940SAlexander Duyck 26892651940SAlexander Duyck opt.bands = q->bands; 26992651940SAlexander Duyck opt.max_bands = q->max_bands; 27092651940SAlexander Duyck 2711b34ec43SDavid S. Miller if (nla_put(skb, TCA_OPTIONS, sizeof(opt), &opt)) 2721b34ec43SDavid S. Miller goto nla_put_failure; 27392651940SAlexander Duyck 27492651940SAlexander Duyck return skb->len; 27592651940SAlexander Duyck 27692651940SAlexander Duyck nla_put_failure: 27792651940SAlexander Duyck nlmsg_trim(skb, b); 27892651940SAlexander Duyck return -1; 27992651940SAlexander Duyck } 28092651940SAlexander Duyck 28192651940SAlexander Duyck static int multiq_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, 28292651940SAlexander Duyck struct Qdisc **old) 28392651940SAlexander Duyck { 28492651940SAlexander Duyck struct multiq_sched_data *q = qdisc_priv(sch); 28592651940SAlexander Duyck unsigned long band = arg - 1; 28692651940SAlexander Duyck 28792651940SAlexander Duyck if (new == NULL) 28892651940SAlexander Duyck new = &noop_qdisc; 28992651940SAlexander Duyck 29086a7996cSWANG Cong *old = qdisc_replace(sch, new, &q->queues[band]); 29192651940SAlexander Duyck return 0; 29292651940SAlexander Duyck } 29392651940SAlexander Duyck 29492651940SAlexander Duyck static struct Qdisc * 29592651940SAlexander Duyck multiq_leaf(struct Qdisc *sch, unsigned long arg) 29692651940SAlexander Duyck { 29792651940SAlexander Duyck struct multiq_sched_data *q = qdisc_priv(sch); 29892651940SAlexander Duyck unsigned long band = arg - 1; 29992651940SAlexander Duyck 30092651940SAlexander Duyck return q->queues[band]; 30192651940SAlexander Duyck } 30292651940SAlexander Duyck 30392651940SAlexander Duyck static unsigned long multiq_get(struct Qdisc *sch, u32 classid) 30492651940SAlexander Duyck { 30592651940SAlexander Duyck struct multiq_sched_data *q = qdisc_priv(sch); 30692651940SAlexander Duyck unsigned long band = TC_H_MIN(classid); 30792651940SAlexander Duyck 30892651940SAlexander Duyck if (band - 1 >= q->bands) 30992651940SAlexander Duyck return 0; 31092651940SAlexander Duyck return band; 31192651940SAlexander Duyck } 31292651940SAlexander Duyck 31392651940SAlexander Duyck static unsigned long multiq_bind(struct Qdisc *sch, unsigned long parent, 31492651940SAlexander Duyck u32 classid) 31592651940SAlexander Duyck { 31692651940SAlexander Duyck return multiq_get(sch, classid); 31792651940SAlexander Duyck } 31892651940SAlexander Duyck 31992651940SAlexander Duyck 32092651940SAlexander Duyck static void multiq_put(struct Qdisc *q, unsigned long cl) 32192651940SAlexander Duyck { 32292651940SAlexander Duyck } 32392651940SAlexander Duyck 32492651940SAlexander Duyck static int multiq_dump_class(struct Qdisc *sch, unsigned long cl, 32592651940SAlexander Duyck struct sk_buff *skb, struct tcmsg *tcm) 32692651940SAlexander Duyck { 32792651940SAlexander Duyck struct multiq_sched_data *q = qdisc_priv(sch); 32892651940SAlexander Duyck 32992651940SAlexander Duyck tcm->tcm_handle |= TC_H_MIN(cl); 33092651940SAlexander Duyck tcm->tcm_info = q->queues[cl - 1]->handle; 33192651940SAlexander Duyck return 0; 33292651940SAlexander Duyck } 33392651940SAlexander Duyck 33492651940SAlexander Duyck static int multiq_dump_class_stats(struct Qdisc *sch, unsigned long cl, 33592651940SAlexander Duyck struct gnet_dump *d) 33692651940SAlexander Duyck { 33792651940SAlexander Duyck struct multiq_sched_data *q = qdisc_priv(sch); 33892651940SAlexander Duyck struct Qdisc *cl_q; 33992651940SAlexander Duyck 34092651940SAlexander Duyck cl_q = q->queues[cl - 1]; 341edb09eb1SEric Dumazet if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch), 342edb09eb1SEric Dumazet d, NULL, &cl_q->bstats) < 0 || 343b0ab6f92SJohn Fastabend gnet_stats_copy_queue(d, NULL, &cl_q->qstats, cl_q->q.qlen) < 0) 34492651940SAlexander Duyck return -1; 34592651940SAlexander Duyck 34692651940SAlexander Duyck return 0; 34792651940SAlexander Duyck } 34892651940SAlexander Duyck 34992651940SAlexander Duyck static void multiq_walk(struct Qdisc *sch, struct qdisc_walker *arg) 35092651940SAlexander Duyck { 35192651940SAlexander Duyck struct multiq_sched_data *q = qdisc_priv(sch); 35292651940SAlexander Duyck int band; 35392651940SAlexander Duyck 35492651940SAlexander Duyck if (arg->stop) 35592651940SAlexander Duyck return; 35692651940SAlexander Duyck 35792651940SAlexander Duyck for (band = 0; band < q->bands; band++) { 35892651940SAlexander Duyck if (arg->count < arg->skip) { 35992651940SAlexander Duyck arg->count++; 36092651940SAlexander Duyck continue; 36192651940SAlexander Duyck } 36292651940SAlexander Duyck if (arg->fn(sch, band + 1, arg) < 0) { 36392651940SAlexander Duyck arg->stop = 1; 36492651940SAlexander Duyck break; 36592651940SAlexander Duyck } 36692651940SAlexander Duyck arg->count++; 36792651940SAlexander Duyck } 36892651940SAlexander Duyck } 36992651940SAlexander Duyck 37025d8c0d5SJohn Fastabend static struct tcf_proto __rcu **multiq_find_tcf(struct Qdisc *sch, 37125d8c0d5SJohn Fastabend unsigned long cl) 37292651940SAlexander Duyck { 37392651940SAlexander Duyck struct multiq_sched_data *q = qdisc_priv(sch); 37492651940SAlexander Duyck 37592651940SAlexander Duyck if (cl) 37692651940SAlexander Duyck return NULL; 37792651940SAlexander Duyck return &q->filter_list; 37892651940SAlexander Duyck } 37992651940SAlexander Duyck 38092651940SAlexander Duyck static const struct Qdisc_class_ops multiq_class_ops = { 38192651940SAlexander Duyck .graft = multiq_graft, 38292651940SAlexander Duyck .leaf = multiq_leaf, 38392651940SAlexander Duyck .get = multiq_get, 38492651940SAlexander Duyck .put = multiq_put, 38592651940SAlexander Duyck .walk = multiq_walk, 38692651940SAlexander Duyck .tcf_chain = multiq_find_tcf, 38792651940SAlexander Duyck .bind_tcf = multiq_bind, 38892651940SAlexander Duyck .unbind_tcf = multiq_put, 38992651940SAlexander Duyck .dump = multiq_dump_class, 39092651940SAlexander Duyck .dump_stats = multiq_dump_class_stats, 39192651940SAlexander Duyck }; 39292651940SAlexander Duyck 39392651940SAlexander Duyck static struct Qdisc_ops multiq_qdisc_ops __read_mostly = { 39492651940SAlexander Duyck .next = NULL, 39592651940SAlexander Duyck .cl_ops = &multiq_class_ops, 39692651940SAlexander Duyck .id = "multiq", 39792651940SAlexander Duyck .priv_size = sizeof(struct multiq_sched_data), 39892651940SAlexander Duyck .enqueue = multiq_enqueue, 39992651940SAlexander Duyck .dequeue = multiq_dequeue, 4008e3af978SJarek Poplawski .peek = multiq_peek, 40192651940SAlexander Duyck .init = multiq_init, 40292651940SAlexander Duyck .reset = multiq_reset, 40392651940SAlexander Duyck .destroy = multiq_destroy, 40492651940SAlexander Duyck .change = multiq_tune, 40592651940SAlexander Duyck .dump = multiq_dump, 40692651940SAlexander Duyck .owner = THIS_MODULE, 40792651940SAlexander Duyck }; 40892651940SAlexander Duyck 40992651940SAlexander Duyck static int __init multiq_module_init(void) 41092651940SAlexander Duyck { 41192651940SAlexander Duyck return register_qdisc(&multiq_qdisc_ops); 41292651940SAlexander Duyck } 41392651940SAlexander Duyck 41492651940SAlexander Duyck static void __exit multiq_module_exit(void) 41592651940SAlexander Duyck { 41692651940SAlexander Duyck unregister_qdisc(&multiq_qdisc_ops); 41792651940SAlexander Duyck } 41892651940SAlexander Duyck 41992651940SAlexander Duyck module_init(multiq_module_init) 42092651940SAlexander Duyck module_exit(multiq_module_exit) 42192651940SAlexander Duyck 42292651940SAlexander Duyck MODULE_LICENSE("GPL"); 423