static struct sk_buff *netem_dequeue(struct Qdisc *sch) { struct netem_sched_data *q = qdisc_priv(sch); struct sk_buff *skb; 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; /* if more time remaining? */ PSCHED_GET_TIME(now); if (PSCHED_TLESS(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; } } return NULL; }
static void netem_watchdog(unsigned long arg) { struct Qdisc *sch = (struct Qdisc *)arg; struct netem_sched_data *q = qdisc_priv(sch); struct net_device *dev = sch->dev; struct sk_buff *skb; psched_time_t now; pr_debug("netem_watchdog: fired @%lu\n", jiffies); spin_lock_bh(&dev->queue_lock); PSCHED_GET_TIME(now); while ((skb = skb_peek(&q->delayed)) != NULL) { const struct netem_skb_cb *cb = (const struct netem_skb_cb *)skb->cb; long delay = PSCHED_US2JIFFIE(PSCHED_TDIFF(cb->time_to_send, now)); pr_debug("netem_watchdog: skb %p@%lu %ld\n", skb, jiffies, delay); /* if more time remaining? */ if (delay > 0) { mod_timer(&q->timer, jiffies + delay); break; } __skb_unlink(skb, &q->delayed); if (q->qdisc->enqueue(skb, q->qdisc)) { sch->q.qlen--; sch->qstats.drops++; } } qdisc_run(dev); spin_unlock_bh(&dev->queue_lock); }