OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [net/] [sched/] [sch_delay.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
2
 * net/sched/sch_delay.c        Simple constant delay
3
 *
4
 *              This program is free software; you can redistribute it and/or
5
 *              modify it under the terms of the GNU General Public License
6
 *              as published by the Free Software Foundation; either version
7
 *              2 of the License, or (at your option) any later version.
8
 *
9
 * Authors:     Stephen Hemminger <shemminger@osdl.org>
10
 */
11
 
12
#include <linux/config.h>
13
#include <linux/module.h>
14
#include <linux/types.h>
15
#include <linux/kernel.h>
16
 
17
#include <linux/string.h>
18
#include <linux/mm.h>
19
#include <linux/socket.h>
20
#include <linux/sockios.h>
21
#include <linux/in.h>
22
#include <linux/errno.h>
23
#include <linux/interrupt.h>
24
#include <linux/if_ether.h>
25
#include <linux/inet.h>
26
#include <linux/netdevice.h>
27
#include <linux/etherdevice.h>
28
#include <linux/notifier.h>
29
#include <net/ip.h>
30
#include <net/route.h>
31
#include <linux/skbuff.h>
32
#include <net/sock.h>
33
#include <net/pkt_sched.h>
34
 
35
/*      Network delay simulator
36
        This scheduler adds a fixed delay to all packets.
37
        Similar to NISTnet and BSD Dummynet.
38
 
39
        It uses byte fifo underneath similar to TBF */
40
struct dly_sched_data {
41
        u32     latency;
42
        u32     limit;
43
        struct timer_list timer;
44
        struct Qdisc *qdisc;
45
};
46
 
47
/* Time stamp put into socket buffer control block */
48
struct dly_skb_cb {
49
        psched_time_t   queuetime;
50
};
51
 
52
/* Enqueue packets with underlying discipline (fifo)
53
 * but mark them with current time first.
54
 */
55
static int dly_enqueue(struct sk_buff *skb, struct Qdisc *sch)
56
{
57
        struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
58
        struct dly_skb_cb *cb = (struct dly_skb_cb *)skb->cb;
59
        int ret;
60
 
61
        PSCHED_GET_TIME(cb->queuetime);
62
 
63
        /* Queue to underlying scheduler */
64
        ret = q->qdisc->enqueue(skb, q->qdisc);
65
        if (ret)
66
                sch->stats.drops++;
67
        else {
68
                sch->q.qlen++;
69
                sch->stats.bytes += skb->len;
70
                sch->stats.packets++;
71
        }
72
        return 0;
73
}
74
 
75
/* Requeue packets but don't change time stamp */
76
static int dly_requeue(struct sk_buff *skb, struct Qdisc *sch)
77
{
78
        struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
79
        int ret;
80
 
81
        ret = q->qdisc->ops->requeue(skb, q->qdisc);
82
        if (ret == 0)
83
                sch->q.qlen++;
84
        return ret;
85
}
86
 
87
static unsigned int dly_drop(struct Qdisc *sch)
88
{
89
        struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
90
        unsigned int len;
91
 
92
        len = q->qdisc->ops->drop(q->qdisc);
93
        if (len) {
94
                sch->q.qlen--;
95
                sch->stats.drops++;
96
        }
97
        return len;
98
}
99
 
100
/* Dequeue packet.
101
 * If packet needs to be held up, then stop the
102
 * queue and set timer to wakeup later.
103
 */
104
static struct sk_buff *dly_dequeue(struct Qdisc *sch)
105
{
106
        struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
107
        struct sk_buff *skb = q->qdisc->dequeue(q->qdisc);
108
 
109
        if (skb) {
110
                struct dly_skb_cb *cb = (struct dly_skb_cb *)skb->cb;
111
                psched_time_t now;
112
                long diff;
113
 
114
                PSCHED_GET_TIME(now);
115
                diff = q->latency - PSCHED_TDIFF(now, cb->queuetime);
116
 
117
                if (diff <= 0) {
118
                        sch->q.qlen--;
119
                        sch->flags &= ~TCQ_F_THROTTLED;
120
                        return skb;
121
                }
122
 
123
                if (!netif_queue_stopped(sch->dev)) {
124
                        long delay = PSCHED_US2JIFFIE(diff);
125
                        if (delay <= 0)
126
                                delay = 1;
127
                        mod_timer(&q->timer, jiffies+delay);
128
                }
129
 
130
                if (q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS) {
131
                        sch->q.qlen--;
132
                        sch->stats.drops++;
133
                }
134
                sch->flags |= TCQ_F_THROTTLED;
135
        }
136
        return NULL;
137
}
138
 
139
static void dly_reset(struct Qdisc *sch)
140
{
141
        struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
142
 
143
        qdisc_reset(q->qdisc);
144
        sch->q.qlen = 0;
145
        sch->flags &= ~TCQ_F_THROTTLED;
146
        del_timer(&q->timer);
147
}
148
 
149
static void dly_timer(unsigned long arg)
150
{
151
        struct Qdisc *sch = (struct Qdisc *)arg;
152
 
153
        sch->flags &= ~TCQ_F_THROTTLED;
154
        netif_schedule(sch->dev);
155
}
156
 
157
/* Tell Fifo the new limit. */
158
static int change_limit(struct Qdisc *q, u32 limit)
159
{
160
        struct rtattr *rta;
161
        int ret;
162
 
163
        rta = kmalloc(RTA_LENGTH(sizeof(struct tc_fifo_qopt)), GFP_KERNEL);
164
        if (!rta)
165
                return -ENOMEM;
166
 
167
        rta->rta_type = RTM_NEWQDISC;
168
        ((struct tc_fifo_qopt *)RTA_DATA(rta))->limit = limit;
169
        ret = q->ops->change(q, rta);
170
        kfree(rta);
171
 
172
        return ret;
173
}
174
 
175
/* Setup underlying FIFO discipline */
176
static int dly_change(struct Qdisc *sch, struct rtattr *opt)
177
{
178
        struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
179
        struct tc_dly_qopt *qopt = RTA_DATA(opt);
180
        int err;
181
 
182
        if (q->qdisc == &noop_qdisc) {
183
                struct Qdisc *child
184
                        = qdisc_create_dflt(sch->dev, &bfifo_qdisc_ops);
185
                if (!child)
186
                        return -EINVAL;
187
                q->qdisc = child;
188
        }
189
 
190
        err = change_limit(q->qdisc, qopt->limit);
191
        if (err) {
192
                qdisc_destroy(q->qdisc);
193
                q->qdisc = &noop_qdisc;
194
        } else {
195
                q->latency = qopt->latency;
196
                q->limit = qopt->limit;
197
        }
198
        return err;
199
}
200
 
201
static int dly_init(struct Qdisc *sch, struct rtattr *opt)
202
{
203
        struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
204
        int err;
205
 
206
        if (!opt)
207
                return -EINVAL;
208
 
209
        MOD_INC_USE_COUNT;
210
 
211
        init_timer(&q->timer);
212
        q->timer.function = dly_timer;
213
        q->timer.data = (unsigned long) sch;
214
        q->qdisc = &noop_qdisc;
215
 
216
        err = dly_change(sch, opt);
217
        if (err)
218
                MOD_DEC_USE_COUNT;
219
 
220
        return err;
221
}
222
 
223
static void dly_destroy(struct Qdisc *sch)
224
{
225
        struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
226
 
227
        del_timer(&q->timer);
228
        qdisc_destroy(q->qdisc);
229
        q->qdisc = &noop_qdisc;
230
 
231
        MOD_DEC_USE_COUNT;
232
}
233
 
234
static int dly_dump(struct Qdisc *sch, struct sk_buff *skb)
235
{
236
        struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
237
        unsigned char    *b = skb->tail;
238
        struct tc_dly_qopt qopt;
239
 
240
        qopt.latency = q->latency;
241
        qopt.limit = q->limit;
242
 
243
        RTA_PUT(skb, TCA_OPTIONS, sizeof(qopt), &qopt);
244
 
245
        return skb->len;
246
 
247
rtattr_failure:
248
        skb_trim(skb, b - skb->data);
249
        return -1;
250
}
251
 
252
struct Qdisc_ops dly_qdisc_ops = {
253
        .id             =       "delay",
254
        .priv_size      =       sizeof(struct dly_sched_data),
255
        .enqueue        =       dly_enqueue,
256
        .dequeue        =       dly_dequeue,
257
        .requeue        =       dly_requeue,
258
        .drop           =       dly_drop,
259
        .init           =       dly_init,
260
        .reset          =       dly_reset,
261
        .destroy        =       dly_destroy,
262
        .change         =       dly_change,
263
        .dump           =       dly_dump,
264
};
265
 
266
#ifdef MODULE
267
int init_module(void)
268
{
269
        return register_qdisc(&dly_qdisc_ops);
270
}
271
 
272
void cleanup_module(void)
273
{
274
        unregister_qdisc(&dly_qdisc_ops);
275
}
276
#endif
277
MODULE_LICENSE("GPL");

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.