linux/net/sched/sch_fifo.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * net/sched/sch_fifo.c The simplest FIFO queue.
   4 *
   5 * Authors:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   6 */
   7
   8#include <linux/module.h>
   9#include <linux/slab.h>
  10#include <linux/types.h>
  11#include <linux/kernel.h>
  12#include <linux/errno.h>
  13#include <linux/skbuff.h>
  14#include <net/pkt_sched.h>
  15
  16/* 1 band FIFO pseudo-"scheduler" */
  17
  18static int bfifo_enqueue(struct sk_buff *skb, struct Qdisc *sch,
  19                         struct sk_buff **to_free)
  20{
  21        if (likely(sch->qstats.backlog + qdisc_pkt_len(skb) <= sch->limit))
  22                return qdisc_enqueue_tail(skb, sch);
  23
  24        return qdisc_drop(skb, sch, to_free);
  25}
  26
  27static int pfifo_enqueue(struct sk_buff *skb, struct Qdisc *sch,
  28                         struct sk_buff **to_free)
  29{
  30        if (likely(sch->q.qlen < sch->limit))
  31                return qdisc_enqueue_tail(skb, sch);
  32
  33        return qdisc_drop(skb, sch, to_free);
  34}
  35
  36static int pfifo_tail_enqueue(struct sk_buff *skb, struct Qdisc *sch,
  37                              struct sk_buff **to_free)
  38{
  39        unsigned int prev_backlog;
  40
  41        if (likely(sch->q.qlen < sch->limit))
  42                return qdisc_enqueue_tail(skb, sch);
  43
  44        prev_backlog = sch->qstats.backlog;
  45        /* queue full, remove one skb to fulfill the limit */
  46        __qdisc_queue_drop_head(sch, &sch->q, to_free);
  47        qdisc_qstats_drop(sch);
  48        qdisc_enqueue_tail(skb, sch);
  49
  50        qdisc_tree_reduce_backlog(sch, 0, prev_backlog - sch->qstats.backlog);
  51        return NET_XMIT_CN;
  52}
  53
  54static int fifo_init(struct Qdisc *sch, struct nlattr *opt,
  55                     struct netlink_ext_ack *extack)
  56{
  57        bool bypass;
  58        bool is_bfifo = sch->ops == &bfifo_qdisc_ops;
  59
  60        if (opt == NULL) {
  61                u32 limit = qdisc_dev(sch)->tx_queue_len;
  62
  63                if (is_bfifo)
  64                        limit *= psched_mtu(qdisc_dev(sch));
  65
  66                sch->limit = limit;
  67        } else {
  68                struct tc_fifo_qopt *ctl = nla_data(opt);
  69
  70                if (nla_len(opt) < sizeof(*ctl))
  71                        return -EINVAL;
  72
  73                sch->limit = ctl->limit;
  74        }
  75
  76        if (is_bfifo)
  77                bypass = sch->limit >= psched_mtu(qdisc_dev(sch));
  78        else
  79                bypass = sch->limit >= 1;
  80
  81        if (bypass)
  82                sch->flags |= TCQ_F_CAN_BYPASS;
  83        else
  84                sch->flags &= ~TCQ_F_CAN_BYPASS;
  85        return 0;
  86}
  87
  88static int fifo_dump(struct Qdisc *sch, struct sk_buff *skb)
  89{
  90        struct tc_fifo_qopt opt = { .limit = sch->limit };
  91
  92        if (nla_put(skb, TCA_OPTIONS, sizeof(opt), &opt))
  93                goto nla_put_failure;
  94        return skb->len;
  95
  96nla_put_failure:
  97        return -1;
  98}
  99
 100struct Qdisc_ops pfifo_qdisc_ops __read_mostly = {
 101        .id             =       "pfifo",
 102        .priv_size      =       0,
 103        .enqueue        =       pfifo_enqueue,
 104        .dequeue        =       qdisc_dequeue_head,
 105        .peek           =       qdisc_peek_head,
 106        .init           =       fifo_init,
 107        .reset          =       qdisc_reset_queue,
 108        .change         =       fifo_init,
 109        .dump           =       fifo_dump,
 110        .owner          =       THIS_MODULE,
 111};
 112EXPORT_SYMBOL(pfifo_qdisc_ops);
 113
 114struct Qdisc_ops bfifo_qdisc_ops __read_mostly = {
 115        .id             =       "bfifo",
 116        .priv_size      =       0,
 117        .enqueue        =       bfifo_enqueue,
 118        .dequeue        =       qdisc_dequeue_head,
 119        .peek           =       qdisc_peek_head,
 120        .init           =       fifo_init,
 121        .reset          =       qdisc_reset_queue,
 122        .change         =       fifo_init,
 123        .dump           =       fifo_dump,
 124        .owner          =       THIS_MODULE,
 125};
 126EXPORT_SYMBOL(bfifo_qdisc_ops);
 127
 128struct Qdisc_ops pfifo_head_drop_qdisc_ops __read_mostly = {
 129        .id             =       "pfifo_head_drop",
 130        .priv_size      =       0,
 131        .enqueue        =       pfifo_tail_enqueue,
 132        .dequeue        =       qdisc_dequeue_head,
 133        .peek           =       qdisc_peek_head,
 134        .init           =       fifo_init,
 135        .reset          =       qdisc_reset_queue,
 136        .change         =       fifo_init,
 137        .dump           =       fifo_dump,
 138        .owner          =       THIS_MODULE,
 139};
 140
 141/* Pass size change message down to embedded FIFO */
 142int fifo_set_limit(struct Qdisc *q, unsigned int limit)
 143{
 144        struct nlattr *nla;
 145        int ret = -ENOMEM;
 146
 147        /* Hack to avoid sending change message to non-FIFO */
 148        if (strncmp(q->ops->id + 1, "fifo", 4) != 0)
 149                return 0;
 150
 151        nla = kmalloc(nla_attr_size(sizeof(struct tc_fifo_qopt)), GFP_KERNEL);
 152        if (nla) {
 153                nla->nla_type = RTM_NEWQDISC;
 154                nla->nla_len = nla_attr_size(sizeof(struct tc_fifo_qopt));
 155                ((struct tc_fifo_qopt *)nla_data(nla))->limit = limit;
 156
 157                ret = q->ops->change(q, nla, NULL);
 158                kfree(nla);
 159        }
 160        return ret;
 161}
 162EXPORT_SYMBOL(fifo_set_limit);
 163
 164struct Qdisc *fifo_create_dflt(struct Qdisc *sch, struct Qdisc_ops *ops,
 165                               unsigned int limit,
 166                               struct netlink_ext_ack *extack)
 167{
 168        struct Qdisc *q;
 169        int err = -ENOMEM;
 170
 171        q = qdisc_create_dflt(sch->dev_queue, ops, TC_H_MAKE(sch->handle, 1),
 172                              extack);
 173        if (q) {
 174                err = fifo_set_limit(q, limit);
 175                if (err < 0) {
 176                        qdisc_put(q);
 177                        q = NULL;
 178                }
 179        }
 180
 181        return q ? : ERR_PTR(err);
 182}
 183EXPORT_SYMBOL(fifo_create_dflt);
 184