blob: fb737dffd0623950d822b9dcc623a0ec267ea1d2 [file] [log] [blame]
/*
**************************************************************************
* Copyright (c) 2014, 2016-2017, 2020, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
#include "nss_qdisc.h"
/*
* nss_blackhole private qdisc structure
*/
struct nss_blackhole_sched_data {
struct nss_qdisc nq; /* Common base class for all nss qdiscs */
u8 set_default; /* Flag to set qdisc as default qdisc for enqueue */
};
/*
* nss_blackhole policy structure
*/
static struct nla_policy nss_blackhole_policy[TCA_NSSBLACKHOLE_MAX + 1] = {
[TCA_NSSBLACKHOLE_PARMS] = { .len = sizeof(struct tc_nssblackhole_qopt) },
};
/*
* nss_blackhole_enqueue()
* Enqueue API for nss blackhole qdisc.
*/
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0))
static int nss_blackhole_enqueue(struct sk_buff *skb, struct Qdisc *sch)
#else
static int nss_blackhole_enqueue(struct sk_buff *skb, struct Qdisc *sch,
struct sk_buff **to_free)
#endif
{
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0))
return nss_qdisc_enqueue(skb, sch);
#else
return nss_qdisc_enqueue(skb, sch, to_free);
#endif
}
/*
* nss_blackhole_dequeue()
* Dequeue API for nss blackhole qdisc.
*/
static struct sk_buff *nss_blackhole_dequeue(struct Qdisc *sch)
{
return nss_qdisc_dequeue(sch);
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0))
/*
* nss_blackhole_drop()
* The following function drops a packet from HLOS queue.
*
* Note, this does not drop packets from queues in the NSS. We do not support that.
*/
static unsigned int nss_blackhole_drop(struct Qdisc *sch)
{
nss_qdisc_info("qdisc %x dropping\n", sch->handle);
return nss_qdisc_drop(sch);
}
#endif
/*
* nss_blackhole_reset()
* Resets the nss blackhole qdisc.
*/
static void nss_blackhole_reset(struct Qdisc *sch)
{
nss_qdisc_info("qdisc %x resetting\n", sch->handle);
nss_qdisc_reset(sch);
}
/*
* nss_blackhole_destroy()
* Destroys the nss blackhole qdisc.
*/
static void nss_blackhole_destroy(struct Qdisc *sch)
{
struct nss_qdisc *nq = (struct nss_qdisc *)qdisc_priv(sch);
/*
* Stop the polling of basic stats
*/
nss_qdisc_stop_basic_stats_polling(nq);
nss_qdisc_info("destroying qdisc %x\n", sch->handle);
nss_qdisc_destroy(nq);
}
/*
* nss_blackhole_change()
* Function call used to configure the parameters of the nss blackhole qdisc.
*/
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0))
static int nss_blackhole_change(struct Qdisc *sch, struct nlattr *opt)
#else
static int nss_blackhole_change(struct Qdisc *sch, struct nlattr *opt,
struct netlink_ext_ack *extack)
#endif
{
struct nss_blackhole_sched_data *q;
struct nlattr *tb[TCA_NSSBLACKHOLE_MAX + 1];
struct tc_nssblackhole_qopt *qopt;
struct nss_if_msg nim;
if (!opt) {
return 0;
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0))
qopt = nss_qdisc_qopt_get(opt, nss_blackhole_policy, tb, TCA_NSSBLACKHOLE_MAX, TCA_NSSBLACKHOLE_PARMS);
#else
qopt = nss_qdisc_qopt_get(opt, nss_blackhole_policy, tb, TCA_NSSBLACKHOLE_MAX, TCA_NSSBLACKHOLE_PARMS, extack);
#endif
if (!qopt) {
return -EINVAL;
}
/*
* Required for basic stats display
*/
sch->limit = 0;
q = qdisc_priv(sch);
q->set_default = qopt->set_default;
nss_qdisc_info("qdisc set_default = %u\n", qopt->set_default);
/*
* Underneath nss_bloackhole uses a fifo in the NSS. This is why we are sending down a configuration
* message to a fifo node. There are no blackhole shaper in the NSS as yet.
*
* Note: We simply set the limit of fifo to zero to get the blackhole behavior.
*/
nim.msg.shaper_configure.config.msg.shaper_node_config.qos_tag = q->nq.qos_tag;
nim.msg.shaper_configure.config.msg.shaper_node_config.snc.fifo_param.limit = 0;
nim.msg.shaper_configure.config.msg.shaper_node_config.snc.fifo_param.drop_mode = NSS_SHAPER_FIFO_DROP_MODE_TAIL;
if (nss_qdisc_configure(&q->nq, &nim, NSS_SHAPER_CONFIG_TYPE_SHAPER_NODE_CHANGE_PARAM) < 0) {
nss_qdisc_error("qdisc %x configuration failed\n", sch->handle);
return -EINVAL;
}
/*
* There is nothing we need to do if the qdisc is not
* set as default qdisc.
*/
if (q->set_default == 0) {
return 0;
}
/*
* Set this qdisc to be the default qdisc for enqueuing packets.
*/
if (nss_qdisc_set_default(&q->nq) < 0) {
nss_qdisc_error("qdisc %x set_default failed\n", sch->handle);
return -EINVAL;
}
nss_qdisc_info("qdisc %x set as default\n", q->nq.qos_tag);
return 0;
}
/*
* nss_blackhole_init()
* Initializes a nss blackhole qdisc.
*/
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0))
static int nss_blackhole_init(struct Qdisc *sch, struct nlattr *opt)
{
struct netlink_ext_ack *extack = NULL;
#else
static int nss_blackhole_init(struct Qdisc *sch, struct nlattr *opt,
struct netlink_ext_ack *extack)
{
#endif
struct nss_qdisc *nq = qdisc_priv(sch);
struct nlattr *tb[TCA_NSSBLACKHOLE_MAX + 1];
struct tc_nssblackhole_qopt *qopt;
unsigned int accel_mode;
/*
* opt is NULL when no parameter is passed to TC.
*/
if (!opt) {
accel_mode = TCA_NSS_ACCEL_MODE_PPE;
} else {
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0))
qopt = nss_qdisc_qopt_get(opt, nss_blackhole_policy, tb, TCA_NSSBLACKHOLE_MAX, TCA_NSSBLACKHOLE_PARMS);
#else
qopt = nss_qdisc_qopt_get(opt, nss_blackhole_policy, tb, TCA_NSSBLACKHOLE_MAX, TCA_NSSBLACKHOLE_PARMS, extack);
#endif
if (!qopt) {
return -EINVAL;
}
accel_mode = qopt->accel_mode;
}
nss_qdisc_info("qdisc %x initializing\n", sch->handle);
nss_blackhole_reset(sch);
if (nss_qdisc_init(sch, nq, NSS_SHAPER_NODE_TYPE_FIFO, 0, accel_mode, extack) < 0)
{
return -EINVAL;
}
nss_qdisc_info("qdisc %x initialized with parent %x\n", sch->handle, sch->parent);
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0))
if (nss_blackhole_change(sch, opt) < 0) {
#else
if (nss_blackhole_change(sch, opt, extack) < 0) {
#endif
nss_qdisc_destroy(nq);
return -EINVAL;
}
/*
* Start the stats polling timer
*/
nss_qdisc_start_basic_stats_polling(nq);
return 0;
}
/*
* nss_blackhole_dump()
* Dumps qdisc parameters for nss blackhole.
*/
static int nss_blackhole_dump(struct Qdisc *sch, struct sk_buff *skb)
{
struct nss_blackhole_sched_data *q;
struct nlattr *opts = NULL;
struct tc_nssblackhole_qopt opt;
nss_qdisc_info("qdisc %x dumping!\n", sch->handle);
q = qdisc_priv(sch);
if (q == NULL) {
return -1;
}
opt.set_default = q->set_default;
opt.accel_mode = nss_qdisc_accel_mode_get(&q->nq);
opts = nss_qdisc_nla_nest_start(skb, TCA_OPTIONS);
if (opts == NULL) {
goto nla_put_failure;
}
if (nla_put(skb, TCA_NSSBLACKHOLE_PARMS, sizeof(opt), &opt))
goto nla_put_failure;
return nla_nest_end(skb, opts);
nla_put_failure:
nla_nest_cancel(skb, opts);
return -EMSGSIZE;
}
/*
* nss_blackhole_peek()
* Peeks the first packet in queue for this qdisc.
*
* Note: This just peeks at the first packet in what is present in HLOS. This does not
* perform an actual peak into the queue in the NSS. Given the async delay between
* the processors, there is less use in implementing this function.
*/
static struct sk_buff *nss_blackhole_peek(struct Qdisc *sch)
{
nss_qdisc_info("qdisc %x peeked\n", sch->handle);
return nss_qdisc_peek(sch);
}
/*
* Registration structure for nss blackhole qdisc
*/
struct Qdisc_ops nss_blackhole_qdisc_ops __read_mostly = {
.id = "nssblackhole",
.priv_size = sizeof(struct nss_blackhole_sched_data),
.enqueue = nss_blackhole_enqueue,
.dequeue = nss_blackhole_dequeue,
.peek = nss_blackhole_peek,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0))
.drop = nss_blackhole_drop,
#endif
.init = nss_blackhole_init,
.reset = nss_blackhole_reset,
.destroy = nss_blackhole_destroy,
.change = nss_blackhole_change,
.dump = nss_blackhole_dump,
.owner = THIS_MODULE,
};