xref: /openbmc/linux/net/sched/sch_multiq.c (revision 6c71a0574249f5e5a45fe055ab5f837023d5eeca)
19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
292651940SAlexander Duyck /*
392651940SAlexander Duyck  * Copyright (c) 2008, Intel Corporation.
492651940SAlexander Duyck  *
592651940SAlexander Duyck  * Author: Alexander Duyck <alexander.h.duyck@intel.com>
692651940SAlexander Duyck  */
792651940SAlexander Duyck 
892651940SAlexander Duyck #include <linux/module.h>
95a0e3ad6STejun Heo #include <linux/slab.h>
1092651940SAlexander Duyck #include <linux/types.h>
1192651940SAlexander Duyck #include <linux/kernel.h>
1292651940SAlexander Duyck #include <linux/string.h>
1392651940SAlexander Duyck #include <linux/errno.h>
1492651940SAlexander Duyck #include <linux/skbuff.h>
1592651940SAlexander Duyck #include <net/netlink.h>
1692651940SAlexander Duyck #include <net/pkt_sched.h>
17cf1facdaSJiri Pirko #include <net/pkt_cls.h>
1892651940SAlexander Duyck 
1992651940SAlexander Duyck struct multiq_sched_data {
2092651940SAlexander Duyck 	u16 bands;
2192651940SAlexander Duyck 	u16 max_bands;
2292651940SAlexander Duyck 	u16 curband;
2325d8c0d5SJohn Fastabend 	struct tcf_proto __rcu *filter_list;
246529eabaSJiri Pirko 	struct tcf_block *block;
2592651940SAlexander Duyck 	struct Qdisc **queues;
2692651940SAlexander Duyck };
2792651940SAlexander Duyck 
2892651940SAlexander Duyck 
2992651940SAlexander Duyck static struct Qdisc *
multiq_classify(struct sk_buff * skb,struct Qdisc * sch,int * qerr)3092651940SAlexander Duyck multiq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
3192651940SAlexander Duyck {
3292651940SAlexander Duyck 	struct multiq_sched_data *q = qdisc_priv(sch);
3392651940SAlexander Duyck 	u32 band;
3492651940SAlexander Duyck 	struct tcf_result res;
3525d8c0d5SJohn Fastabend 	struct tcf_proto *fl = rcu_dereference_bh(q->filter_list);
3692651940SAlexander Duyck 	int err;
3792651940SAlexander Duyck 
3892651940SAlexander Duyck 	*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
393aa26055SDavide Caratti 	err = tcf_classify(skb, NULL, fl, &res, false);
4092651940SAlexander Duyck #ifdef CONFIG_NET_CLS_ACT
4192651940SAlexander Duyck 	switch (err) {
4292651940SAlexander Duyck 	case TC_ACT_STOLEN:
4392651940SAlexander Duyck 	case TC_ACT_QUEUED:
44e25ea21fSJiri Pirko 	case TC_ACT_TRAP:
4592651940SAlexander Duyck 		*qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
46964201deSGustavo A. R. Silva 		fallthrough;
4792651940SAlexander Duyck 	case TC_ACT_SHOT:
4892651940SAlexander Duyck 		return NULL;
4992651940SAlexander Duyck 	}
5092651940SAlexander Duyck #endif
5192651940SAlexander Duyck 	band = skb_get_queue_mapping(skb);
5292651940SAlexander Duyck 
5392651940SAlexander Duyck 	if (band >= q->bands)
5492651940SAlexander Duyck 		return q->queues[0];
5592651940SAlexander Duyck 
5692651940SAlexander Duyck 	return q->queues[band];
5792651940SAlexander Duyck }
5892651940SAlexander Duyck 
5992651940SAlexander Duyck static int
multiq_enqueue(struct sk_buff * skb,struct Qdisc * sch,struct sk_buff ** to_free)60ac5c66f2SPetr Machata multiq_enqueue(struct sk_buff *skb, struct Qdisc *sch,
61520ac30fSEric Dumazet 	       struct sk_buff **to_free)
6292651940SAlexander Duyck {
6392651940SAlexander Duyck 	struct Qdisc *qdisc;
6492651940SAlexander Duyck 	int ret;
6592651940SAlexander Duyck 
6692651940SAlexander Duyck 	qdisc = multiq_classify(skb, sch, &ret);
6792651940SAlexander Duyck #ifdef CONFIG_NET_CLS_ACT
6892651940SAlexander Duyck 	if (qdisc == NULL) {
6992651940SAlexander Duyck 
7092651940SAlexander Duyck 		if (ret & __NET_XMIT_BYPASS)
7125331d6cSJohn Fastabend 			qdisc_qstats_drop(sch);
72520ac30fSEric Dumazet 		__qdisc_drop(skb, to_free);
7392651940SAlexander Duyck 		return ret;
7492651940SAlexander Duyck 	}
7592651940SAlexander Duyck #endif
7692651940SAlexander Duyck 
77ac5c66f2SPetr Machata 	ret = qdisc_enqueue(skb, qdisc, to_free);
7892651940SAlexander Duyck 	if (ret == NET_XMIT_SUCCESS) {
7992651940SAlexander Duyck 		sch->q.qlen++;
8092651940SAlexander Duyck 		return NET_XMIT_SUCCESS;
8192651940SAlexander Duyck 	}
8292651940SAlexander Duyck 	if (net_xmit_drop_count(ret))
8325331d6cSJohn Fastabend 		qdisc_qstats_drop(sch);
8492651940SAlexander Duyck 	return ret;
8592651940SAlexander Duyck }
8692651940SAlexander Duyck 
multiq_dequeue(struct Qdisc * sch)8792651940SAlexander Duyck static struct sk_buff *multiq_dequeue(struct Qdisc *sch)
8892651940SAlexander Duyck {
8992651940SAlexander Duyck 	struct multiq_sched_data *q = qdisc_priv(sch);
9092651940SAlexander Duyck 	struct Qdisc *qdisc;
9192651940SAlexander Duyck 	struct sk_buff *skb;
9292651940SAlexander Duyck 	int band;
9392651940SAlexander Duyck 
9492651940SAlexander Duyck 	for (band = 0; band < q->bands; band++) {
9592651940SAlexander Duyck 		/* cycle through bands to ensure fairness */
9692651940SAlexander Duyck 		q->curband++;
9792651940SAlexander Duyck 		if (q->curband >= q->bands)
9892651940SAlexander Duyck 			q->curband = 0;
9992651940SAlexander Duyck 
10092651940SAlexander Duyck 		/* Check that target subqueue is available before
101f30ab418SJarek Poplawski 		 * pulling an skb to avoid head-of-line blocking.
10292651940SAlexander Duyck 		 */
10373466498STom Herbert 		if (!netif_xmit_stopped(
10473466498STom Herbert 		    netdev_get_tx_queue(qdisc_dev(sch), q->curband))) {
10592651940SAlexander Duyck 			qdisc = q->queues[q->curband];
10692651940SAlexander Duyck 			skb = qdisc->dequeue(qdisc);
10792651940SAlexander Duyck 			if (skb) {
1089190b3b3SEric Dumazet 				qdisc_bstats_update(sch, skb);
10992651940SAlexander Duyck 				sch->q.qlen--;
11092651940SAlexander Duyck 				return skb;
11192651940SAlexander Duyck 			}
11292651940SAlexander Duyck 		}
11392651940SAlexander Duyck 	}
11492651940SAlexander Duyck 	return NULL;
11592651940SAlexander Duyck 
11692651940SAlexander Duyck }
11792651940SAlexander Duyck 
multiq_peek(struct Qdisc * sch)1188e3af978SJarek Poplawski static struct sk_buff *multiq_peek(struct Qdisc *sch)
1198e3af978SJarek Poplawski {
1208e3af978SJarek Poplawski 	struct multiq_sched_data *q = qdisc_priv(sch);
1218e3af978SJarek Poplawski 	unsigned int curband = q->curband;
1228e3af978SJarek Poplawski 	struct Qdisc *qdisc;
1238e3af978SJarek Poplawski 	struct sk_buff *skb;
1248e3af978SJarek Poplawski 	int band;
1258e3af978SJarek Poplawski 
1268e3af978SJarek Poplawski 	for (band = 0; band < q->bands; band++) {
1278e3af978SJarek Poplawski 		/* cycle through bands to ensure fairness */
1288e3af978SJarek Poplawski 		curband++;
1298e3af978SJarek Poplawski 		if (curband >= q->bands)
1308e3af978SJarek Poplawski 			curband = 0;
1318e3af978SJarek Poplawski 
1328e3af978SJarek Poplawski 		/* Check that target subqueue is available before
133f30ab418SJarek Poplawski 		 * pulling an skb to avoid head-of-line blocking.
1348e3af978SJarek Poplawski 		 */
13573466498STom Herbert 		if (!netif_xmit_stopped(
13673466498STom Herbert 		    netdev_get_tx_queue(qdisc_dev(sch), curband))) {
1378e3af978SJarek Poplawski 			qdisc = q->queues[curband];
1388e3af978SJarek Poplawski 			skb = qdisc->ops->peek(qdisc);
1398e3af978SJarek Poplawski 			if (skb)
1408e3af978SJarek Poplawski 				return skb;
1418e3af978SJarek Poplawski 		}
1428e3af978SJarek Poplawski 	}
1438e3af978SJarek Poplawski 	return NULL;
1448e3af978SJarek Poplawski 
1458e3af978SJarek Poplawski }
1468e3af978SJarek Poplawski 
14792651940SAlexander Duyck static void
multiq_reset(struct Qdisc * sch)14892651940SAlexander Duyck multiq_reset(struct Qdisc *sch)
14992651940SAlexander Duyck {
15092651940SAlexander Duyck 	u16 band;
15192651940SAlexander Duyck 	struct multiq_sched_data *q = qdisc_priv(sch);
15292651940SAlexander Duyck 
15392651940SAlexander Duyck 	for (band = 0; band < q->bands; band++)
15492651940SAlexander Duyck 		qdisc_reset(q->queues[band]);
15592651940SAlexander Duyck 	q->curband = 0;
15692651940SAlexander Duyck }
15792651940SAlexander Duyck 
15892651940SAlexander Duyck static void
multiq_destroy(struct Qdisc * sch)15992651940SAlexander Duyck multiq_destroy(struct Qdisc *sch)
16092651940SAlexander Duyck {
16192651940SAlexander Duyck 	int band;
16292651940SAlexander Duyck 	struct multiq_sched_data *q = qdisc_priv(sch);
16392651940SAlexander Duyck 
1646529eabaSJiri Pirko 	tcf_block_put(q->block);
16592651940SAlexander Duyck 	for (band = 0; band < q->bands; band++)
16686bd446bSVlad Buslov 		qdisc_put(q->queues[band]);
16792651940SAlexander Duyck 
16892651940SAlexander Duyck 	kfree(q->queues);
16992651940SAlexander Duyck }
17092651940SAlexander Duyck 
multiq_tune(struct Qdisc * sch,struct nlattr * opt,struct netlink_ext_ack * extack)1712030721cSAlexander Aring static int multiq_tune(struct Qdisc *sch, struct nlattr *opt,
1722030721cSAlexander Aring 		       struct netlink_ext_ack *extack)
17392651940SAlexander Duyck {
17492651940SAlexander Duyck 	struct multiq_sched_data *q = qdisc_priv(sch);
17592651940SAlexander Duyck 	struct tc_multiq_qopt *qopt;
176c2999f7fSVlad Buslov 	struct Qdisc **removed;
177c2999f7fSVlad Buslov 	int i, n_removed = 0;
17892651940SAlexander Duyck 
17992651940SAlexander Duyck 	if (!netif_is_multiqueue(qdisc_dev(sch)))
180149490f1SJarek Poplawski 		return -EOPNOTSUPP;
18192651940SAlexander Duyck 	if (nla_len(opt) < sizeof(*qopt))
18292651940SAlexander Duyck 		return -EINVAL;
18392651940SAlexander Duyck 
18492651940SAlexander Duyck 	qopt = nla_data(opt);
18592651940SAlexander Duyck 
18692651940SAlexander Duyck 	qopt->bands = qdisc_dev(sch)->real_num_tx_queues;
18792651940SAlexander Duyck 
188*54c2c171SHangyu Hua 	removed = kmalloc(sizeof(*removed) * (q->max_bands - qopt->bands),
189c2999f7fSVlad Buslov 			  GFP_KERNEL);
190c2999f7fSVlad Buslov 	if (!removed)
191c2999f7fSVlad Buslov 		return -ENOMEM;
192c2999f7fSVlad Buslov 
19392651940SAlexander Duyck 	sch_tree_lock(sch);
19492651940SAlexander Duyck 	q->bands = qopt->bands;
19592651940SAlexander Duyck 	for (i = q->bands; i < q->max_bands; i++) {
196f07d1501SAlexander Duyck 		if (q->queues[i] != &noop_qdisc) {
197b94c8afcSPatrick McHardy 			struct Qdisc *child = q->queues[i];
198e5f0e8f8SPaolo Abeni 
199b94c8afcSPatrick McHardy 			q->queues[i] = &noop_qdisc;
200c2999f7fSVlad Buslov 			qdisc_purge_queue(child);
201c2999f7fSVlad Buslov 			removed[n_removed++] = child;
20292651940SAlexander Duyck 		}
20392651940SAlexander Duyck 	}
20492651940SAlexander Duyck 
20592651940SAlexander Duyck 	sch_tree_unlock(sch);
20692651940SAlexander Duyck 
207c2999f7fSVlad Buslov 	for (i = 0; i < n_removed; i++)
208c2999f7fSVlad Buslov 		qdisc_put(removed[i]);
209c2999f7fSVlad Buslov 	kfree(removed);
210c2999f7fSVlad Buslov 
21192651940SAlexander Duyck 	for (i = 0; i < q->bands; i++) {
21292651940SAlexander Duyck 		if (q->queues[i] == &noop_qdisc) {
213b94c8afcSPatrick McHardy 			struct Qdisc *child, *old;
2143511c913SChangli Gao 			child = qdisc_create_dflt(sch->dev_queue,
21592651940SAlexander Duyck 						  &pfifo_qdisc_ops,
21692651940SAlexander Duyck 						  TC_H_MAKE(sch->handle,
217a38a9882SAlexander Aring 							    i + 1), extack);
21892651940SAlexander Duyck 			if (child) {
21992651940SAlexander Duyck 				sch_tree_lock(sch);
220b94c8afcSPatrick McHardy 				old = q->queues[i];
221b94c8afcSPatrick McHardy 				q->queues[i] = child;
22249b49971SJiri Kosina 				if (child != &noop_qdisc)
22349b49971SJiri Kosina 					qdisc_hash_add(child, true);
22492651940SAlexander Duyck 
225c2999f7fSVlad Buslov 				if (old != &noop_qdisc)
226c2999f7fSVlad Buslov 					qdisc_purge_queue(old);
22792651940SAlexander Duyck 				sch_tree_unlock(sch);
228c2999f7fSVlad Buslov 				qdisc_put(old);
22992651940SAlexander Duyck 			}
23092651940SAlexander Duyck 		}
23192651940SAlexander Duyck 	}
23292651940SAlexander Duyck 	return 0;
23392651940SAlexander Duyck }
23492651940SAlexander Duyck 
multiq_init(struct Qdisc * sch,struct nlattr * opt,struct netlink_ext_ack * extack)235e63d7dfdSAlexander Aring static int multiq_init(struct Qdisc *sch, struct nlattr *opt,
236e63d7dfdSAlexander Aring 		       struct netlink_ext_ack *extack)
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 
243ac8ef4abSAlexander Aring 	if (!opt)
24492651940SAlexander Duyck 		return -EINVAL;
24592651940SAlexander Duyck 
2468d1a77f9SAlexander Aring 	err = tcf_block_get(&q->block, &q->filter_list, sch, extack);
2476529eabaSJiri Pirko 	if (err)
2486529eabaSJiri Pirko 		return err;
2496529eabaSJiri Pirko 
25092651940SAlexander Duyck 	q->max_bands = qdisc_dev(sch)->num_tx_queues;
25192651940SAlexander Duyck 
25292651940SAlexander Duyck 	q->queues = kcalloc(q->max_bands, sizeof(struct Qdisc *), GFP_KERNEL);
25392651940SAlexander Duyck 	if (!q->queues)
25492651940SAlexander Duyck 		return -ENOBUFS;
25592651940SAlexander Duyck 	for (i = 0; i < q->max_bands; i++)
25692651940SAlexander Duyck 		q->queues[i] = &noop_qdisc;
25792651940SAlexander Duyck 
2582030721cSAlexander Aring 	return multiq_tune(sch, opt, extack);
25992651940SAlexander Duyck }
26092651940SAlexander Duyck 
multiq_dump(struct Qdisc * sch,struct sk_buff * skb)26192651940SAlexander Duyck static int multiq_dump(struct Qdisc *sch, struct sk_buff *skb)
26292651940SAlexander Duyck {
26392651940SAlexander Duyck 	struct multiq_sched_data *q = qdisc_priv(sch);
26492651940SAlexander Duyck 	unsigned char *b = skb_tail_pointer(skb);
26592651940SAlexander Duyck 	struct tc_multiq_qopt opt;
26692651940SAlexander Duyck 
26792651940SAlexander Duyck 	opt.bands = q->bands;
26892651940SAlexander Duyck 	opt.max_bands = q->max_bands;
26992651940SAlexander Duyck 
2701b34ec43SDavid S. Miller 	if (nla_put(skb, TCA_OPTIONS, sizeof(opt), &opt))
2711b34ec43SDavid S. Miller 		goto nla_put_failure;
27292651940SAlexander Duyck 
27392651940SAlexander Duyck 	return skb->len;
27492651940SAlexander Duyck 
27592651940SAlexander Duyck nla_put_failure:
27692651940SAlexander Duyck 	nlmsg_trim(skb, b);
27792651940SAlexander Duyck 	return -1;
27892651940SAlexander Duyck }
27992651940SAlexander Duyck 
multiq_graft(struct Qdisc * sch,unsigned long arg,struct Qdisc * new,struct Qdisc ** old,struct netlink_ext_ack * extack)28092651940SAlexander Duyck static int multiq_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
281653d6fd6SAlexander Aring 			struct Qdisc **old, struct netlink_ext_ack *extack)
28292651940SAlexander Duyck {
28392651940SAlexander Duyck 	struct multiq_sched_data *q = qdisc_priv(sch);
28492651940SAlexander Duyck 	unsigned long band = arg - 1;
28592651940SAlexander Duyck 
28692651940SAlexander Duyck 	if (new == NULL)
28792651940SAlexander Duyck 		new = &noop_qdisc;
28892651940SAlexander Duyck 
28986a7996cSWANG Cong 	*old = qdisc_replace(sch, new, &q->queues[band]);
29092651940SAlexander Duyck 	return 0;
29192651940SAlexander Duyck }
29292651940SAlexander Duyck 
29392651940SAlexander Duyck static struct Qdisc *
multiq_leaf(struct Qdisc * sch,unsigned long arg)29492651940SAlexander Duyck multiq_leaf(struct Qdisc *sch, unsigned long arg)
29592651940SAlexander Duyck {
29692651940SAlexander Duyck 	struct multiq_sched_data *q = qdisc_priv(sch);
29792651940SAlexander Duyck 	unsigned long band = arg - 1;
29892651940SAlexander Duyck 
29992651940SAlexander Duyck 	return q->queues[band];
30092651940SAlexander Duyck }
30192651940SAlexander Duyck 
multiq_find(struct Qdisc * sch,u32 classid)302143976ceSWANG Cong static unsigned long multiq_find(struct Qdisc *sch, u32 classid)
30392651940SAlexander Duyck {
30492651940SAlexander Duyck 	struct multiq_sched_data *q = qdisc_priv(sch);
30592651940SAlexander Duyck 	unsigned long band = TC_H_MIN(classid);
30692651940SAlexander Duyck 
30792651940SAlexander Duyck 	if (band - 1 >= q->bands)
30892651940SAlexander Duyck 		return 0;
30992651940SAlexander Duyck 	return band;
31092651940SAlexander Duyck }
31192651940SAlexander Duyck 
multiq_bind(struct Qdisc * sch,unsigned long parent,u32 classid)31292651940SAlexander Duyck static unsigned long multiq_bind(struct Qdisc *sch, unsigned long parent,
31392651940SAlexander Duyck 				 u32 classid)
31492651940SAlexander Duyck {
315143976ceSWANG Cong 	return multiq_find(sch, classid);
31692651940SAlexander Duyck }
31792651940SAlexander Duyck 
31892651940SAlexander Duyck 
multiq_unbind(struct Qdisc * q,unsigned long cl)319143976ceSWANG Cong static void multiq_unbind(struct Qdisc *q, unsigned long cl)
32092651940SAlexander Duyck {
32192651940SAlexander Duyck }
32292651940SAlexander Duyck 
multiq_dump_class(struct Qdisc * sch,unsigned long cl,struct sk_buff * skb,struct tcmsg * tcm)32392651940SAlexander Duyck static int multiq_dump_class(struct Qdisc *sch, unsigned long cl,
32492651940SAlexander Duyck 			     struct sk_buff *skb, struct tcmsg *tcm)
32592651940SAlexander Duyck {
32692651940SAlexander Duyck 	struct multiq_sched_data *q = qdisc_priv(sch);
32792651940SAlexander Duyck 
32892651940SAlexander Duyck 	tcm->tcm_handle |= TC_H_MIN(cl);
32992651940SAlexander Duyck 	tcm->tcm_info = q->queues[cl - 1]->handle;
33092651940SAlexander Duyck 	return 0;
33192651940SAlexander Duyck }
33292651940SAlexander Duyck 
multiq_dump_class_stats(struct Qdisc * sch,unsigned long cl,struct gnet_dump * d)33392651940SAlexander Duyck static int multiq_dump_class_stats(struct Qdisc *sch, unsigned long cl,
33492651940SAlexander Duyck 				 struct gnet_dump *d)
33592651940SAlexander Duyck {
33692651940SAlexander Duyck 	struct multiq_sched_data *q = qdisc_priv(sch);
33792651940SAlexander Duyck 	struct Qdisc *cl_q;
33892651940SAlexander Duyck 
33992651940SAlexander Duyck 	cl_q = q->queues[cl - 1];
34029cbcd85SAhmed S. Darwish 	if (gnet_stats_copy_basic(d, cl_q->cpu_bstats, &cl_q->bstats, true) < 0 ||
3415dd431b6SPaolo Abeni 	    qdisc_qstats_copy(d, cl_q) < 0)
34292651940SAlexander Duyck 		return -1;
34392651940SAlexander Duyck 
34492651940SAlexander Duyck 	return 0;
34592651940SAlexander Duyck }
34692651940SAlexander Duyck 
multiq_walk(struct Qdisc * sch,struct qdisc_walker * arg)34792651940SAlexander Duyck static void multiq_walk(struct Qdisc *sch, struct qdisc_walker *arg)
34892651940SAlexander Duyck {
34992651940SAlexander Duyck 	struct multiq_sched_data *q = qdisc_priv(sch);
35092651940SAlexander Duyck 	int band;
35192651940SAlexander Duyck 
35292651940SAlexander Duyck 	if (arg->stop)
35392651940SAlexander Duyck 		return;
35492651940SAlexander Duyck 
35592651940SAlexander Duyck 	for (band = 0; band < q->bands; band++) {
356e046fa89SZhengchao Shao 		if (!tc_qdisc_stats_dump(sch, band + 1, arg))
35792651940SAlexander Duyck 			break;
35892651940SAlexander Duyck 	}
35992651940SAlexander Duyck }
36092651940SAlexander Duyck 
multiq_tcf_block(struct Qdisc * sch,unsigned long cl,struct netlink_ext_ack * extack)361cbaacc4eSAlexander Aring static struct tcf_block *multiq_tcf_block(struct Qdisc *sch, unsigned long cl,
362cbaacc4eSAlexander Aring 					  struct netlink_ext_ack *extack)
36392651940SAlexander Duyck {
36492651940SAlexander Duyck 	struct multiq_sched_data *q = qdisc_priv(sch);
36592651940SAlexander Duyck 
36692651940SAlexander Duyck 	if (cl)
36792651940SAlexander Duyck 		return NULL;
3686529eabaSJiri Pirko 	return q->block;
36992651940SAlexander Duyck }
37092651940SAlexander Duyck 
37192651940SAlexander Duyck static const struct Qdisc_class_ops multiq_class_ops = {
37292651940SAlexander Duyck 	.graft		=	multiq_graft,
37392651940SAlexander Duyck 	.leaf		=	multiq_leaf,
374143976ceSWANG Cong 	.find		=	multiq_find,
37592651940SAlexander Duyck 	.walk		=	multiq_walk,
3766529eabaSJiri Pirko 	.tcf_block	=	multiq_tcf_block,
37792651940SAlexander Duyck 	.bind_tcf	=	multiq_bind,
378143976ceSWANG Cong 	.unbind_tcf	=	multiq_unbind,
37992651940SAlexander Duyck 	.dump		=	multiq_dump_class,
38092651940SAlexander Duyck 	.dump_stats	=	multiq_dump_class_stats,
38192651940SAlexander Duyck };
38292651940SAlexander Duyck 
38392651940SAlexander Duyck static struct Qdisc_ops multiq_qdisc_ops __read_mostly = {
38492651940SAlexander Duyck 	.next		=	NULL,
38592651940SAlexander Duyck 	.cl_ops		=	&multiq_class_ops,
38692651940SAlexander Duyck 	.id		=	"multiq",
38792651940SAlexander Duyck 	.priv_size	=	sizeof(struct multiq_sched_data),
38892651940SAlexander Duyck 	.enqueue	=	multiq_enqueue,
38992651940SAlexander Duyck 	.dequeue	=	multiq_dequeue,
3908e3af978SJarek Poplawski 	.peek		=	multiq_peek,
39192651940SAlexander Duyck 	.init		=	multiq_init,
39292651940SAlexander Duyck 	.reset		=	multiq_reset,
39392651940SAlexander Duyck 	.destroy	=	multiq_destroy,
39492651940SAlexander Duyck 	.change		=	multiq_tune,
39592651940SAlexander Duyck 	.dump		=	multiq_dump,
39692651940SAlexander Duyck 	.owner		=	THIS_MODULE,
39792651940SAlexander Duyck };
39892651940SAlexander Duyck 
multiq_module_init(void)39992651940SAlexander Duyck static int __init multiq_module_init(void)
40092651940SAlexander Duyck {
40192651940SAlexander Duyck 	return register_qdisc(&multiq_qdisc_ops);
40292651940SAlexander Duyck }
40392651940SAlexander Duyck 
multiq_module_exit(void)40492651940SAlexander Duyck static void __exit multiq_module_exit(void)
40592651940SAlexander Duyck {
40692651940SAlexander Duyck 	unregister_qdisc(&multiq_qdisc_ops);
40792651940SAlexander Duyck }
40892651940SAlexander Duyck 
40992651940SAlexander Duyck module_init(multiq_module_init)
41092651940SAlexander Duyck module_exit(multiq_module_exit)
41192651940SAlexander Duyck 
41292651940SAlexander Duyck MODULE_LICENSE("GPL");
413