Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * net/sched/sch_fifo.c The simplest FIFO queue.
0004  *
0005  * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
0006  */
0007 
0008 #include <linux/module.h>
0009 #include <linux/slab.h>
0010 #include <linux/types.h>
0011 #include <linux/kernel.h>
0012 #include <linux/errno.h>
0013 #include <linux/skbuff.h>
0014 #include <net/pkt_sched.h>
0015 #include <net/pkt_cls.h>
0016 
0017 /* 1 band FIFO pseudo-"scheduler" */
0018 
0019 static int bfifo_enqueue(struct sk_buff *skb, struct Qdisc *sch,
0020              struct sk_buff **to_free)
0021 {
0022     if (likely(sch->qstats.backlog + qdisc_pkt_len(skb) <= sch->limit))
0023         return qdisc_enqueue_tail(skb, sch);
0024 
0025     return qdisc_drop(skb, sch, to_free);
0026 }
0027 
0028 static int pfifo_enqueue(struct sk_buff *skb, struct Qdisc *sch,
0029              struct sk_buff **to_free)
0030 {
0031     if (likely(sch->q.qlen < sch->limit))
0032         return qdisc_enqueue_tail(skb, sch);
0033 
0034     return qdisc_drop(skb, sch, to_free);
0035 }
0036 
0037 static int pfifo_tail_enqueue(struct sk_buff *skb, struct Qdisc *sch,
0038                   struct sk_buff **to_free)
0039 {
0040     unsigned int prev_backlog;
0041 
0042     if (likely(sch->q.qlen < sch->limit))
0043         return qdisc_enqueue_tail(skb, sch);
0044 
0045     prev_backlog = sch->qstats.backlog;
0046     /* queue full, remove one skb to fulfill the limit */
0047     __qdisc_queue_drop_head(sch, &sch->q, to_free);
0048     qdisc_qstats_drop(sch);
0049     qdisc_enqueue_tail(skb, sch);
0050 
0051     qdisc_tree_reduce_backlog(sch, 0, prev_backlog - sch->qstats.backlog);
0052     return NET_XMIT_CN;
0053 }
0054 
0055 static void fifo_offload_init(struct Qdisc *sch)
0056 {
0057     struct net_device *dev = qdisc_dev(sch);
0058     struct tc_fifo_qopt_offload qopt;
0059 
0060     if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
0061         return;
0062 
0063     qopt.command = TC_FIFO_REPLACE;
0064     qopt.handle = sch->handle;
0065     qopt.parent = sch->parent;
0066     dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_FIFO, &qopt);
0067 }
0068 
0069 static void fifo_offload_destroy(struct Qdisc *sch)
0070 {
0071     struct net_device *dev = qdisc_dev(sch);
0072     struct tc_fifo_qopt_offload qopt;
0073 
0074     if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
0075         return;
0076 
0077     qopt.command = TC_FIFO_DESTROY;
0078     qopt.handle = sch->handle;
0079     qopt.parent = sch->parent;
0080     dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_FIFO, &qopt);
0081 }
0082 
0083 static int fifo_offload_dump(struct Qdisc *sch)
0084 {
0085     struct tc_fifo_qopt_offload qopt;
0086 
0087     qopt.command = TC_FIFO_STATS;
0088     qopt.handle = sch->handle;
0089     qopt.parent = sch->parent;
0090     qopt.stats.bstats = &sch->bstats;
0091     qopt.stats.qstats = &sch->qstats;
0092 
0093     return qdisc_offload_dump_helper(sch, TC_SETUP_QDISC_FIFO, &qopt);
0094 }
0095 
0096 static int __fifo_init(struct Qdisc *sch, struct nlattr *opt,
0097                struct netlink_ext_ack *extack)
0098 {
0099     bool bypass;
0100     bool is_bfifo = sch->ops == &bfifo_qdisc_ops;
0101 
0102     if (opt == NULL) {
0103         u32 limit = qdisc_dev(sch)->tx_queue_len;
0104 
0105         if (is_bfifo)
0106             limit *= psched_mtu(qdisc_dev(sch));
0107 
0108         sch->limit = limit;
0109     } else {
0110         struct tc_fifo_qopt *ctl = nla_data(opt);
0111 
0112         if (nla_len(opt) < sizeof(*ctl))
0113             return -EINVAL;
0114 
0115         sch->limit = ctl->limit;
0116     }
0117 
0118     if (is_bfifo)
0119         bypass = sch->limit >= psched_mtu(qdisc_dev(sch));
0120     else
0121         bypass = sch->limit >= 1;
0122 
0123     if (bypass)
0124         sch->flags |= TCQ_F_CAN_BYPASS;
0125     else
0126         sch->flags &= ~TCQ_F_CAN_BYPASS;
0127 
0128     return 0;
0129 }
0130 
0131 static int fifo_init(struct Qdisc *sch, struct nlattr *opt,
0132              struct netlink_ext_ack *extack)
0133 {
0134     int err;
0135 
0136     err = __fifo_init(sch, opt, extack);
0137     if (err)
0138         return err;
0139 
0140     fifo_offload_init(sch);
0141     return 0;
0142 }
0143 
0144 static int fifo_hd_init(struct Qdisc *sch, struct nlattr *opt,
0145             struct netlink_ext_ack *extack)
0146 {
0147     return __fifo_init(sch, opt, extack);
0148 }
0149 
0150 static void fifo_destroy(struct Qdisc *sch)
0151 {
0152     fifo_offload_destroy(sch);
0153 }
0154 
0155 static int __fifo_dump(struct Qdisc *sch, struct sk_buff *skb)
0156 {
0157     struct tc_fifo_qopt opt = { .limit = sch->limit };
0158 
0159     if (nla_put(skb, TCA_OPTIONS, sizeof(opt), &opt))
0160         goto nla_put_failure;
0161     return skb->len;
0162 
0163 nla_put_failure:
0164     return -1;
0165 }
0166 
0167 static int fifo_dump(struct Qdisc *sch, struct sk_buff *skb)
0168 {
0169     int err;
0170 
0171     err = fifo_offload_dump(sch);
0172     if (err)
0173         return err;
0174 
0175     return __fifo_dump(sch, skb);
0176 }
0177 
0178 static int fifo_hd_dump(struct Qdisc *sch, struct sk_buff *skb)
0179 {
0180     return __fifo_dump(sch, skb);
0181 }
0182 
0183 struct Qdisc_ops pfifo_qdisc_ops __read_mostly = {
0184     .id     =   "pfifo",
0185     .priv_size  =   0,
0186     .enqueue    =   pfifo_enqueue,
0187     .dequeue    =   qdisc_dequeue_head,
0188     .peek       =   qdisc_peek_head,
0189     .init       =   fifo_init,
0190     .destroy    =   fifo_destroy,
0191     .reset      =   qdisc_reset_queue,
0192     .change     =   fifo_init,
0193     .dump       =   fifo_dump,
0194     .owner      =   THIS_MODULE,
0195 };
0196 EXPORT_SYMBOL(pfifo_qdisc_ops);
0197 
0198 struct Qdisc_ops bfifo_qdisc_ops __read_mostly = {
0199     .id     =   "bfifo",
0200     .priv_size  =   0,
0201     .enqueue    =   bfifo_enqueue,
0202     .dequeue    =   qdisc_dequeue_head,
0203     .peek       =   qdisc_peek_head,
0204     .init       =   fifo_init,
0205     .destroy    =   fifo_destroy,
0206     .reset      =   qdisc_reset_queue,
0207     .change     =   fifo_init,
0208     .dump       =   fifo_dump,
0209     .owner      =   THIS_MODULE,
0210 };
0211 EXPORT_SYMBOL(bfifo_qdisc_ops);
0212 
0213 struct Qdisc_ops pfifo_head_drop_qdisc_ops __read_mostly = {
0214     .id     =   "pfifo_head_drop",
0215     .priv_size  =   0,
0216     .enqueue    =   pfifo_tail_enqueue,
0217     .dequeue    =   qdisc_dequeue_head,
0218     .peek       =   qdisc_peek_head,
0219     .init       =   fifo_hd_init,
0220     .reset      =   qdisc_reset_queue,
0221     .change     =   fifo_hd_init,
0222     .dump       =   fifo_hd_dump,
0223     .owner      =   THIS_MODULE,
0224 };
0225 
0226 /* Pass size change message down to embedded FIFO */
0227 int fifo_set_limit(struct Qdisc *q, unsigned int limit)
0228 {
0229     struct nlattr *nla;
0230     int ret = -ENOMEM;
0231 
0232     /* Hack to avoid sending change message to non-FIFO */
0233     if (strncmp(q->ops->id + 1, "fifo", 4) != 0)
0234         return 0;
0235 
0236     if (!q->ops->change)
0237         return 0;
0238 
0239     nla = kmalloc(nla_attr_size(sizeof(struct tc_fifo_qopt)), GFP_KERNEL);
0240     if (nla) {
0241         nla->nla_type = RTM_NEWQDISC;
0242         nla->nla_len = nla_attr_size(sizeof(struct tc_fifo_qopt));
0243         ((struct tc_fifo_qopt *)nla_data(nla))->limit = limit;
0244 
0245         ret = q->ops->change(q, nla, NULL);
0246         kfree(nla);
0247     }
0248     return ret;
0249 }
0250 EXPORT_SYMBOL(fifo_set_limit);
0251 
0252 struct Qdisc *fifo_create_dflt(struct Qdisc *sch, struct Qdisc_ops *ops,
0253                    unsigned int limit,
0254                    struct netlink_ext_ack *extack)
0255 {
0256     struct Qdisc *q;
0257     int err = -ENOMEM;
0258 
0259     q = qdisc_create_dflt(sch->dev_queue, ops, TC_H_MAKE(sch->handle, 1),
0260                   extack);
0261     if (q) {
0262         err = fifo_set_limit(q, limit);
0263         if (err < 0) {
0264             qdisc_put(q);
0265             q = NULL;
0266         }
0267     }
0268 
0269     return q ? : ERR_PTR(err);
0270 }
0271 EXPORT_SYMBOL(fifo_create_dflt);