0001
0002
0003
0004
0005
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
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
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
0227 int fifo_set_limit(struct Qdisc *q, unsigned int limit)
0228 {
0229 struct nlattr *nla;
0230 int ret = -ENOMEM;
0231
0232
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);