summaryrefslogtreecommitdiffstats
path: root/net/sched/sch_netem.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/sched/sch_netem.c')
-rw-r--r--net/sched/sch_netem.c108
1 files changed, 53 insertions, 55 deletions
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 1ccbfb55b0b8..5d9d8bc9cc3a 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -22,6 +22,7 @@
#include <linux/skbuff.h>
#include <linux/rtnetlink.h>
+#include <net/netlink.h>
#include <net/pkt_sched.h>
#define VERSION "1.2"
@@ -54,21 +55,22 @@
struct netem_sched_data {
struct Qdisc *qdisc;
- struct timer_list timer;
+ struct qdisc_watchdog watchdog;
+
+ psched_tdiff_t latency;
+ psched_tdiff_t jitter;
- u32 latency;
u32 loss;
u32 limit;
u32 counter;
u32 gap;
- u32 jitter;
u32 duplicate;
u32 reorder;
u32 corrupt;
struct crndstate {
- unsigned long last;
- unsigned long rho;
+ u32 last;
+ u32 rho;
} delay_cor, loss_cor, dup_cor, reorder_cor, corrupt_cor;
struct disttable {
@@ -95,12 +97,12 @@ static void init_crandom(struct crndstate *state, unsigned long rho)
* Next number depends on last value.
* rho is scaled to avoid floating point.
*/
-static unsigned long get_crandom(struct crndstate *state)
+static u32 get_crandom(struct crndstate *state)
{
u64 value, rho;
unsigned long answer;
- if (state->rho == 0) /* no correllation */
+ if (state->rho == 0) /* no correlation */
return net_random();
value = net_random();
@@ -114,11 +116,13 @@ static unsigned long get_crandom(struct crndstate *state)
* std deviation sigma. Uses table lookup to approximate the desired
* distribution, and a uniformly-distributed pseudo-random source.
*/
-static long tabledist(unsigned long mu, long sigma,
- struct crndstate *state, const struct disttable *dist)
+static psched_tdiff_t tabledist(psched_tdiff_t mu, psched_tdiff_t sigma,
+ struct crndstate *state,
+ const struct disttable *dist)
{
- long t, x;
- unsigned long rnd;
+ psched_tdiff_t x;
+ long t;
+ u32 rnd;
if (sigma == 0)
return mu;
@@ -213,8 +217,8 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
delay = tabledist(q->latency, q->jitter,
&q->delay_cor, q->delay_dist);
- PSCHED_GET_TIME(now);
- PSCHED_TADD2(now, delay, cb->time_to_send);
+ now = psched_get_time();
+ cb->time_to_send = now + delay;
++q->counter;
ret = q->qdisc->enqueue(skb, q->qdisc);
} else {
@@ -222,7 +226,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
* Do re-ordering by putting one out of N packets at the front
* of the queue.
*/
- PSCHED_GET_TIME(cb->time_to_send);
+ cb->time_to_send = psched_get_time();
q->counter = 0;
ret = q->qdisc->ops->requeue(skb, q->qdisc);
}
@@ -269,55 +273,43 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch)
struct netem_sched_data *q = qdisc_priv(sch);
struct sk_buff *skb;
+ smp_mb();
+ if (sch->flags & TCQ_F_THROTTLED)
+ return NULL;
+
skb = q->qdisc->dequeue(q->qdisc);
if (skb) {
const struct netem_skb_cb *cb
= (const struct netem_skb_cb *)skb->cb;
- psched_time_t now;
+ psched_time_t now = psched_get_time();
/* if more time remaining? */
- PSCHED_GET_TIME(now);
-
- if (PSCHED_TLESS(cb->time_to_send, now)) {
+ if (cb->time_to_send <= now) {
pr_debug("netem_dequeue: return skb=%p\n", skb);
sch->q.qlen--;
- sch->flags &= ~TCQ_F_THROTTLED;
return skb;
- } else {
- psched_tdiff_t delay = PSCHED_TDIFF(cb->time_to_send, now);
-
- if (q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS) {
- qdisc_tree_decrease_qlen(q->qdisc, 1);
- sch->qstats.drops++;
- printk(KERN_ERR "netem: queue discpline %s could not requeue\n",
- q->qdisc->ops->id);
- }
+ }
- mod_timer(&q->timer, jiffies + PSCHED_US2JIFFIE(delay));
- sch->flags |= TCQ_F_THROTTLED;
+ if (unlikely(q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS)) {
+ qdisc_tree_decrease_qlen(q->qdisc, 1);
+ sch->qstats.drops++;
+ printk(KERN_ERR "netem: %s could not requeue\n",
+ q->qdisc->ops->id);
}
+
+ qdisc_watchdog_schedule(&q->watchdog, cb->time_to_send);
}
return NULL;
}
-static void netem_watchdog(unsigned long arg)
-{
- struct Qdisc *sch = (struct Qdisc *)arg;
-
- pr_debug("netem_watchdog qlen=%d\n", sch->q.qlen);
- sch->flags &= ~TCQ_F_THROTTLED;
- netif_schedule(sch->dev);
-}
-
static void netem_reset(struct Qdisc *sch)
{
struct netem_sched_data *q = qdisc_priv(sch);
qdisc_reset(q->qdisc);
sch->q.qlen = 0;
- sch->flags &= ~TCQ_F_THROTTLED;
- del_timer_sync(&q->timer);
+ qdisc_watchdog_cancel(&q->watchdog);
}
/* Pass size change message down to embedded FIFO */
@@ -438,10 +430,11 @@ static int netem_change(struct Qdisc *sch, struct rtattr *opt)
q->loss = qopt->loss;
q->duplicate = qopt->duplicate;
- /* for compatiablity with earlier versions.
- * if gap is set, need to assume 100% probablity
+ /* for compatibility with earlier versions.
+ * if gap is set, need to assume 100% probability
*/
- q->reorder = ~0;
+ if (q->gap)
+ q->reorder = ~0;
/* Handle nested options after initial queue options.
* Should have put all options in nested format but too late now.
@@ -487,22 +480,28 @@ static int netem_change(struct Qdisc *sch, struct rtattr *opt)
*/
struct fifo_sched_data {
u32 limit;
+ psched_time_t oldest;
};
static int tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
{
struct fifo_sched_data *q = qdisc_priv(sch);
struct sk_buff_head *list = &sch->q;
- const struct netem_skb_cb *ncb
- = (const struct netem_skb_cb *)nskb->cb;
+ psched_time_t tnext = ((struct netem_skb_cb *)nskb->cb)->time_to_send;
struct sk_buff *skb;
if (likely(skb_queue_len(list) < q->limit)) {
+ /* Optimize for add at tail */
+ if (likely(skb_queue_empty(list) || tnext >= q->oldest)) {
+ q->oldest = tnext;
+ return qdisc_enqueue_tail(nskb, sch);
+ }
+
skb_queue_reverse_walk(list, skb) {
const struct netem_skb_cb *cb
= (const struct netem_skb_cb *)skb->cb;
- if (!PSCHED_TLESS(ncb->time_to_send, cb->time_to_send))
+ if (tnext >= cb->time_to_send)
break;
}
@@ -515,7 +514,7 @@ static int tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
return NET_XMIT_SUCCESS;
}
- return qdisc_drop(nskb, sch);
+ return qdisc_reshape_fail(nskb, sch);
}
static int tfifo_init(struct Qdisc *sch, struct rtattr *opt)
@@ -531,6 +530,7 @@ static int tfifo_init(struct Qdisc *sch, struct rtattr *opt)
} else
q->limit = max_t(u32, sch->dev->tx_queue_len, 1);
+ q->oldest = PSCHED_PASTPERFECT;
return 0;
}
@@ -567,9 +567,7 @@ static int netem_init(struct Qdisc *sch, struct rtattr *opt)
if (!opt)
return -EINVAL;
- init_timer(&q->timer);
- q->timer.function = netem_watchdog;
- q->timer.data = (unsigned long) sch;
+ qdisc_watchdog_init(&q->watchdog, sch);
q->qdisc = qdisc_create_dflt(sch->dev, &tfifo_qdisc_ops,
TC_H_MAKE(sch->handle, 1));
@@ -590,7 +588,7 @@ static void netem_destroy(struct Qdisc *sch)
{
struct netem_sched_data *q = qdisc_priv(sch);
- del_timer_sync(&q->timer);
+ qdisc_watchdog_cancel(&q->watchdog);
qdisc_destroy(q->qdisc);
kfree(q->delay_dist);
}
@@ -598,7 +596,7 @@ static void netem_destroy(struct Qdisc *sch)
static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
{
const struct netem_sched_data *q = qdisc_priv(sch);
- unsigned char *b = skb->tail;
+ unsigned char *b = skb_tail_pointer(skb);
struct rtattr *rta = (struct rtattr *) b;
struct tc_netem_qopt qopt;
struct tc_netem_corr cor;
@@ -626,12 +624,12 @@ static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
corrupt.correlation = q->corrupt_cor.rho;
RTA_PUT(skb, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt);
- rta->rta_len = skb->tail - b;
+ rta->rta_len = skb_tail_pointer(skb) - b;
return skb->len;
rtattr_failure:
- skb_trim(skb, b - skb->data);
+ nlmsg_trim(skb, b);
return -1;
}