Add qdisc->ops->peek() implementation for work-conserving qdiscs.
With feedback from Patrick McHardy.
Signed-off-by: Jarek Poplawski <jarkao2@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
        return skb;
 }
 
+static struct sk_buff *atm_tc_peek(struct Qdisc *sch)
+{
+       struct atm_qdisc_data *p = qdisc_priv(sch);
+
+       pr_debug("atm_tc_peek(sch %p,[qdisc %p])\n", sch, p);
+
+       return p->link.q->ops->peek(p->link.q);
+}
+
 static int atm_tc_requeue(struct sk_buff *skb, struct Qdisc *sch)
 {
        struct atm_qdisc_data *p = qdisc_priv(sch);
        .priv_size      = sizeof(struct atm_qdisc_data),
        .enqueue        = atm_tc_enqueue,
        .dequeue        = atm_tc_dequeue,
+       .peek           = atm_tc_peek,
        .requeue        = atm_tc_requeue,
        .drop           = atm_tc_drop,
        .init           = atm_tc_init,
 
        .priv_size      = 0,
        .enqueue        = blackhole_enqueue,
        .dequeue        = blackhole_dequeue,
+       .peek           = blackhole_dequeue,
        .owner          = THIS_MODULE,
 };
 
 
        return skb;
 }
 
+static struct sk_buff *dsmark_peek(struct Qdisc *sch)
+{
+       struct dsmark_qdisc_data *p = qdisc_priv(sch);
+
+       pr_debug("dsmark_peek(sch %p,[qdisc %p])\n", sch, p);
+
+       return p->q->ops->peek(p->q);
+}
+
 static int dsmark_requeue(struct sk_buff *skb, struct Qdisc *sch)
 {
        struct dsmark_qdisc_data *p = qdisc_priv(sch);
        .priv_size      =       sizeof(struct dsmark_qdisc_data),
        .enqueue        =       dsmark_enqueue,
        .dequeue        =       dsmark_dequeue,
+       .peek           =       dsmark_peek,
        .requeue        =       dsmark_requeue,
        .drop           =       dsmark_drop,
        .init           =       dsmark_init,
 
        .priv_size      =       sizeof(struct gred_sched),
        .enqueue        =       gred_enqueue,
        .dequeue        =       gred_dequeue,
+       .peek           =       qdisc_peek_head,
        .requeue        =       gred_requeue,
        .drop           =       gred_drop,
        .init           =       gred_init,
 
 
 }
 
+static struct sk_buff *multiq_peek(struct Qdisc *sch)
+{
+       struct multiq_sched_data *q = qdisc_priv(sch);
+       unsigned int curband = q->curband;
+       struct Qdisc *qdisc;
+       struct sk_buff *skb;
+       int band;
+
+       for (band = 0; band < q->bands; band++) {
+               /* cycle through bands to ensure fairness */
+               curband++;
+               if (curband >= q->bands)
+                       curband = 0;
+
+               /* Check that target subqueue is available before
+                * pulling an skb to avoid excessive requeues
+                */
+               if (!__netif_subqueue_stopped(qdisc_dev(sch), curband)) {
+                       qdisc = q->queues[curband];
+                       skb = qdisc->ops->peek(qdisc);
+                       if (skb)
+                               return skb;
+               }
+       }
+       return NULL;
+
+}
+
 static unsigned int multiq_drop(struct Qdisc *sch)
 {
        struct multiq_sched_data *q = qdisc_priv(sch);
        .priv_size      =       sizeof(struct multiq_sched_data),
        .enqueue        =       multiq_enqueue,
        .dequeue        =       multiq_dequeue,
+       .peek           =       multiq_peek,
        .requeue        =       multiq_requeue,
        .drop           =       multiq_drop,
        .init           =       multiq_init,
 
        .priv_size      =       sizeof(struct fifo_sched_data),
        .enqueue        =       tfifo_enqueue,
        .dequeue        =       qdisc_dequeue_head,
+       .peek           =       qdisc_peek_head,
        .requeue        =       qdisc_requeue,
        .drop           =       qdisc_queue_drop,
        .init           =       tfifo_init,
 
        return skb;
 }
 
+static struct sk_buff * red_peek(struct Qdisc* sch)
+{
+       struct red_sched_data *q = qdisc_priv(sch);
+       struct Qdisc *child = q->qdisc;
+
+       return child->ops->peek(child);
+}
+
 static unsigned int red_drop(struct Qdisc* sch)
 {
        struct red_sched_data *q = qdisc_priv(sch);
        .cl_ops         =       &red_class_ops,
        .enqueue        =       red_enqueue,
        .dequeue        =       red_dequeue,
+       .peek           =       red_peek,
        .requeue        =       red_requeue,
        .drop           =       red_drop,
        .init           =       red_init,
 
        return skb;
 }
 
+static struct sk_buff *
+teql_peek(struct Qdisc* sch)
+{
+       /* teql is meant to be used as root qdisc */
+       return NULL;
+}
+
 static __inline__ void
 teql_neigh_release(struct neighbour *n)
 {
 
        ops->enqueue    =       teql_enqueue;
        ops->dequeue    =       teql_dequeue;
+       ops->peek       =       teql_peek;
        ops->requeue    =       teql_requeue;
        ops->init       =       teql_qdisc_init;
        ops->reset      =       teql_reset;