• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/net/sched/
1/*
2 * net/sched/sch_mq.c		Classful multiqueue dummy scheduler
3 *
4 * Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * version 2 as published by the Free Software Foundation.
9 */
10
11#include <linux/types.h>
12#include <linux/slab.h>
13#include <linux/kernel.h>
14#include <linux/string.h>
15#include <linux/errno.h>
16#include <linux/skbuff.h>
17#include <net/netlink.h>
18#include <net/pkt_sched.h>
19
20struct mq_sched {
21	struct Qdisc		**qdiscs;
22};
23
24static void mq_destroy(struct Qdisc *sch)
25{
26	struct net_device *dev = qdisc_dev(sch);
27	struct mq_sched *priv = qdisc_priv(sch);
28	unsigned int ntx;
29
30	if (!priv->qdiscs)
31		return;
32	for (ntx = 0; ntx < dev->num_tx_queues && priv->qdiscs[ntx]; ntx++)
33		qdisc_destroy(priv->qdiscs[ntx]);
34	kfree(priv->qdiscs);
35}
36
37static int mq_init(struct Qdisc *sch, struct nlattr *opt)
38{
39	struct net_device *dev = qdisc_dev(sch);
40	struct mq_sched *priv = qdisc_priv(sch);
41	struct netdev_queue *dev_queue;
42	struct Qdisc *qdisc;
43	unsigned int ntx;
44
45	if (sch->parent != TC_H_ROOT)
46		return -EOPNOTSUPP;
47
48	if (!netif_is_multiqueue(dev))
49		return -EOPNOTSUPP;
50
51	/* pre-allocate qdiscs, attachment can't fail */
52	priv->qdiscs = kcalloc(dev->num_tx_queues, sizeof(priv->qdiscs[0]),
53			       GFP_KERNEL);
54	if (priv->qdiscs == NULL)
55		return -ENOMEM;
56
57	for (ntx = 0; ntx < dev->num_tx_queues; ntx++) {
58		dev_queue = netdev_get_tx_queue(dev, ntx);
59		qdisc = qdisc_create_dflt(dev, dev_queue, &pfifo_fast_ops,
60					  TC_H_MAKE(TC_H_MAJ(sch->handle),
61						    TC_H_MIN(ntx + 1)));
62		if (qdisc == NULL)
63			goto err;
64		qdisc->flags |= TCQ_F_CAN_BYPASS;
65		priv->qdiscs[ntx] = qdisc;
66	}
67
68	sch->flags |= TCQ_F_MQROOT;
69	return 0;
70
71err:
72	mq_destroy(sch);
73	return -ENOMEM;
74}
75
76static void mq_attach(struct Qdisc *sch)
77{
78	struct net_device *dev = qdisc_dev(sch);
79	struct mq_sched *priv = qdisc_priv(sch);
80	struct Qdisc *qdisc;
81	unsigned int ntx;
82
83	for (ntx = 0; ntx < dev->num_tx_queues; ntx++) {
84		qdisc = priv->qdiscs[ntx];
85		qdisc = dev_graft_qdisc(qdisc->dev_queue, qdisc);
86		if (qdisc)
87			qdisc_destroy(qdisc);
88	}
89	kfree(priv->qdiscs);
90	priv->qdiscs = NULL;
91}
92
93static int mq_dump(struct Qdisc *sch, struct sk_buff *skb)
94{
95	struct net_device *dev = qdisc_dev(sch);
96	struct Qdisc *qdisc;
97	unsigned int ntx;
98
99	sch->q.qlen = 0;
100	memset(&sch->bstats, 0, sizeof(sch->bstats));
101	memset(&sch->qstats, 0, sizeof(sch->qstats));
102
103	for (ntx = 0; ntx < dev->num_tx_queues; ntx++) {
104		qdisc = netdev_get_tx_queue(dev, ntx)->qdisc_sleeping;
105		spin_lock_bh(qdisc_lock(qdisc));
106		sch->q.qlen		+= qdisc->q.qlen;
107		sch->bstats.bytes	+= qdisc->bstats.bytes;
108		sch->bstats.packets	+= qdisc->bstats.packets;
109		sch->qstats.qlen	+= qdisc->qstats.qlen;
110		sch->qstats.backlog	+= qdisc->qstats.backlog;
111		sch->qstats.drops	+= qdisc->qstats.drops;
112		sch->qstats.requeues	+= qdisc->qstats.requeues;
113		sch->qstats.overlimits	+= qdisc->qstats.overlimits;
114		spin_unlock_bh(qdisc_lock(qdisc));
115	}
116	return 0;
117}
118
119static struct netdev_queue *mq_queue_get(struct Qdisc *sch, unsigned long cl)
120{
121	struct net_device *dev = qdisc_dev(sch);
122	unsigned long ntx = cl - 1;
123
124	if (ntx >= dev->num_tx_queues)
125		return NULL;
126	return netdev_get_tx_queue(dev, ntx);
127}
128
129static struct netdev_queue *mq_select_queue(struct Qdisc *sch,
130					    struct tcmsg *tcm)
131{
132	unsigned int ntx = TC_H_MIN(tcm->tcm_parent);
133	struct netdev_queue *dev_queue = mq_queue_get(sch, ntx);
134
135	if (!dev_queue) {
136		struct net_device *dev = qdisc_dev(sch);
137
138		return netdev_get_tx_queue(dev, 0);
139	}
140	return dev_queue;
141}
142
143static int mq_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new,
144		    struct Qdisc **old)
145{
146	struct netdev_queue *dev_queue = mq_queue_get(sch, cl);
147	struct net_device *dev = qdisc_dev(sch);
148
149	if (dev->flags & IFF_UP)
150		dev_deactivate(dev);
151
152	*old = dev_graft_qdisc(dev_queue, new);
153
154	if (dev->flags & IFF_UP)
155		dev_activate(dev);
156	return 0;
157}
158
159static struct Qdisc *mq_leaf(struct Qdisc *sch, unsigned long cl)
160{
161	struct netdev_queue *dev_queue = mq_queue_get(sch, cl);
162
163	return dev_queue->qdisc_sleeping;
164}
165
166static unsigned long mq_get(struct Qdisc *sch, u32 classid)
167{
168	unsigned int ntx = TC_H_MIN(classid);
169
170	if (!mq_queue_get(sch, ntx))
171		return 0;
172	return ntx;
173}
174
175static void mq_put(struct Qdisc *sch, unsigned long cl)
176{
177}
178
179static int mq_dump_class(struct Qdisc *sch, unsigned long cl,
180			 struct sk_buff *skb, struct tcmsg *tcm)
181{
182	struct netdev_queue *dev_queue = mq_queue_get(sch, cl);
183
184	tcm->tcm_parent = TC_H_ROOT;
185	tcm->tcm_handle |= TC_H_MIN(cl);
186	tcm->tcm_info = dev_queue->qdisc_sleeping->handle;
187	return 0;
188}
189
190static int mq_dump_class_stats(struct Qdisc *sch, unsigned long cl,
191			       struct gnet_dump *d)
192{
193	struct netdev_queue *dev_queue = mq_queue_get(sch, cl);
194
195	sch = dev_queue->qdisc_sleeping;
196	sch->qstats.qlen = sch->q.qlen;
197	if (gnet_stats_copy_basic(d, &sch->bstats) < 0 ||
198	    gnet_stats_copy_queue(d, &sch->qstats) < 0)
199		return -1;
200	return 0;
201}
202
203static void mq_walk(struct Qdisc *sch, struct qdisc_walker *arg)
204{
205	struct net_device *dev = qdisc_dev(sch);
206	unsigned int ntx;
207
208	if (arg->stop)
209		return;
210
211	arg->count = arg->skip;
212	for (ntx = arg->skip; ntx < dev->num_tx_queues; ntx++) {
213		if (arg->fn(sch, ntx + 1, arg) < 0) {
214			arg->stop = 1;
215			break;
216		}
217		arg->count++;
218	}
219}
220
221static const struct Qdisc_class_ops mq_class_ops = {
222	.select_queue	= mq_select_queue,
223	.graft		= mq_graft,
224	.leaf		= mq_leaf,
225	.get		= mq_get,
226	.put		= mq_put,
227	.walk		= mq_walk,
228	.dump		= mq_dump_class,
229	.dump_stats	= mq_dump_class_stats,
230};
231
232struct Qdisc_ops mq_qdisc_ops __read_mostly = {
233	.cl_ops		= &mq_class_ops,
234	.id		= "mq",
235	.priv_size	= sizeof(struct mq_sched),
236	.init		= mq_init,
237	.destroy	= mq_destroy,
238	.attach		= mq_attach,
239	.dump		= mq_dump,
240	.owner		= THIS_MODULE,
241};
242