mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-22 00:40:03 +00:00
![Thomas Gleixner](/assets/img/avatar_default.png)
Based on 1 normalized pattern(s): this program is free software you can redistribute it and or modify it under the terms of the gnu general public license as published by the free software foundation either version 2 of the license or at your option any later version extracted by the scancode license scanner the SPDX license identifier GPL-2.0-or-later has been chosen to replace the boilerplate/reference in 3029 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Allison Randal <allison@lohutok.net> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190527070032.746973796@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
183 lines
4.2 KiB
C
183 lines
4.2 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* net/sched/sch_fifo.c The simplest FIFO queue.
|
|
*
|
|
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/types.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/skbuff.h>
|
|
#include <net/pkt_sched.h>
|
|
|
|
/* 1 band FIFO pseudo-"scheduler" */
|
|
|
|
static int bfifo_enqueue(struct sk_buff *skb, struct Qdisc *sch,
|
|
struct sk_buff **to_free)
|
|
{
|
|
if (likely(sch->qstats.backlog + qdisc_pkt_len(skb) <= sch->limit))
|
|
return qdisc_enqueue_tail(skb, sch);
|
|
|
|
return qdisc_drop(skb, sch, to_free);
|
|
}
|
|
|
|
static int pfifo_enqueue(struct sk_buff *skb, struct Qdisc *sch,
|
|
struct sk_buff **to_free)
|
|
{
|
|
if (likely(sch->q.qlen < sch->limit))
|
|
return qdisc_enqueue_tail(skb, sch);
|
|
|
|
return qdisc_drop(skb, sch, to_free);
|
|
}
|
|
|
|
static int pfifo_tail_enqueue(struct sk_buff *skb, struct Qdisc *sch,
|
|
struct sk_buff **to_free)
|
|
{
|
|
unsigned int prev_backlog;
|
|
|
|
if (likely(sch->q.qlen < sch->limit))
|
|
return qdisc_enqueue_tail(skb, sch);
|
|
|
|
prev_backlog = sch->qstats.backlog;
|
|
/* queue full, remove one skb to fulfill the limit */
|
|
__qdisc_queue_drop_head(sch, &sch->q, to_free);
|
|
qdisc_qstats_drop(sch);
|
|
qdisc_enqueue_tail(skb, sch);
|
|
|
|
qdisc_tree_reduce_backlog(sch, 0, prev_backlog - sch->qstats.backlog);
|
|
return NET_XMIT_CN;
|
|
}
|
|
|
|
static int fifo_init(struct Qdisc *sch, struct nlattr *opt,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
bool bypass;
|
|
bool is_bfifo = sch->ops == &bfifo_qdisc_ops;
|
|
|
|
if (opt == NULL) {
|
|
u32 limit = qdisc_dev(sch)->tx_queue_len;
|
|
|
|
if (is_bfifo)
|
|
limit *= psched_mtu(qdisc_dev(sch));
|
|
|
|
sch->limit = limit;
|
|
} else {
|
|
struct tc_fifo_qopt *ctl = nla_data(opt);
|
|
|
|
if (nla_len(opt) < sizeof(*ctl))
|
|
return -EINVAL;
|
|
|
|
sch->limit = ctl->limit;
|
|
}
|
|
|
|
if (is_bfifo)
|
|
bypass = sch->limit >= psched_mtu(qdisc_dev(sch));
|
|
else
|
|
bypass = sch->limit >= 1;
|
|
|
|
if (bypass)
|
|
sch->flags |= TCQ_F_CAN_BYPASS;
|
|
else
|
|
sch->flags &= ~TCQ_F_CAN_BYPASS;
|
|
return 0;
|
|
}
|
|
|
|
static int fifo_dump(struct Qdisc *sch, struct sk_buff *skb)
|
|
{
|
|
struct tc_fifo_qopt opt = { .limit = sch->limit };
|
|
|
|
if (nla_put(skb, TCA_OPTIONS, sizeof(opt), &opt))
|
|
goto nla_put_failure;
|
|
return skb->len;
|
|
|
|
nla_put_failure:
|
|
return -1;
|
|
}
|
|
|
|
struct Qdisc_ops pfifo_qdisc_ops __read_mostly = {
|
|
.id = "pfifo",
|
|
.priv_size = 0,
|
|
.enqueue = pfifo_enqueue,
|
|
.dequeue = qdisc_dequeue_head,
|
|
.peek = qdisc_peek_head,
|
|
.init = fifo_init,
|
|
.reset = qdisc_reset_queue,
|
|
.change = fifo_init,
|
|
.dump = fifo_dump,
|
|
.owner = THIS_MODULE,
|
|
};
|
|
EXPORT_SYMBOL(pfifo_qdisc_ops);
|
|
|
|
struct Qdisc_ops bfifo_qdisc_ops __read_mostly = {
|
|
.id = "bfifo",
|
|
.priv_size = 0,
|
|
.enqueue = bfifo_enqueue,
|
|
.dequeue = qdisc_dequeue_head,
|
|
.peek = qdisc_peek_head,
|
|
.init = fifo_init,
|
|
.reset = qdisc_reset_queue,
|
|
.change = fifo_init,
|
|
.dump = fifo_dump,
|
|
.owner = THIS_MODULE,
|
|
};
|
|
EXPORT_SYMBOL(bfifo_qdisc_ops);
|
|
|
|
struct Qdisc_ops pfifo_head_drop_qdisc_ops __read_mostly = {
|
|
.id = "pfifo_head_drop",
|
|
.priv_size = 0,
|
|
.enqueue = pfifo_tail_enqueue,
|
|
.dequeue = qdisc_dequeue_head,
|
|
.peek = qdisc_peek_head,
|
|
.init = fifo_init,
|
|
.reset = qdisc_reset_queue,
|
|
.change = fifo_init,
|
|
.dump = fifo_dump,
|
|
.owner = THIS_MODULE,
|
|
};
|
|
|
|
/* Pass size change message down to embedded FIFO */
|
|
int fifo_set_limit(struct Qdisc *q, unsigned int limit)
|
|
{
|
|
struct nlattr *nla;
|
|
int ret = -ENOMEM;
|
|
|
|
/* Hack to avoid sending change message to non-FIFO */
|
|
if (strncmp(q->ops->id + 1, "fifo", 4) != 0)
|
|
return 0;
|
|
|
|
nla = kmalloc(nla_attr_size(sizeof(struct tc_fifo_qopt)), GFP_KERNEL);
|
|
if (nla) {
|
|
nla->nla_type = RTM_NEWQDISC;
|
|
nla->nla_len = nla_attr_size(sizeof(struct tc_fifo_qopt));
|
|
((struct tc_fifo_qopt *)nla_data(nla))->limit = limit;
|
|
|
|
ret = q->ops->change(q, nla, NULL);
|
|
kfree(nla);
|
|
}
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(fifo_set_limit);
|
|
|
|
struct Qdisc *fifo_create_dflt(struct Qdisc *sch, struct Qdisc_ops *ops,
|
|
unsigned int limit,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
struct Qdisc *q;
|
|
int err = -ENOMEM;
|
|
|
|
q = qdisc_create_dflt(sch->dev_queue, ops, TC_H_MAKE(sch->handle, 1),
|
|
extack);
|
|
if (q) {
|
|
err = fifo_set_limit(q, limit);
|
|
if (err < 0) {
|
|
qdisc_put(q);
|
|
q = NULL;
|
|
}
|
|
}
|
|
|
|
return q ? : ERR_PTR(err);
|
|
}
|
|
EXPORT_SYMBOL(fifo_create_dflt);
|