575 lines
16 KiB
Diff
Executable File
575 lines
16 KiB
Diff
Executable File
diff --git a/include/Makefile.am b/include/Makefile.am
|
|
index 1e07fdb..3f96690 100644
|
|
--- a/include/Makefile.am
|
|
+++ b/include/Makefile.am
|
|
@@ -54,6 +54,7 @@ nobase_libnlinclude_HEADERS = \
|
|
netlink/route/qdisc/red.h \
|
|
netlink/route/qdisc/sfq.h \
|
|
netlink/route/qdisc/tbf.h \
|
|
+ netlink/route/qdisc/fq_codel.h \
|
|
netlink/route/qdisc/plug.h \
|
|
netlink/route/addr.h \
|
|
netlink/route/class.h \
|
|
diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h
|
|
index 7ccc1fd..1c0727c 100644
|
|
--- a/include/linux/pkt_sched.h
|
|
+++ b/include/linux/pkt_sched.h
|
|
@@ -624,4 +624,57 @@ struct tc_qfq_stats {
|
|
__u32 lmax;
|
|
};
|
|
|
|
+/* FQ_CODEL */
|
|
+
|
|
+enum {
|
|
+ TCA_FQ_CODEL_UNSPEC,
|
|
+ TCA_FQ_CODEL_TARGET,
|
|
+ TCA_FQ_CODEL_LIMIT,
|
|
+ TCA_FQ_CODEL_INTERVAL,
|
|
+ TCA_FQ_CODEL_ECN,
|
|
+ TCA_FQ_CODEL_FLOWS,
|
|
+ TCA_FQ_CODEL_QUANTUM,
|
|
+ __TCA_FQ_CODEL_MAX
|
|
+};
|
|
+
|
|
+#define TCA_FQ_CODEL_MAX (__TCA_FQ_CODEL_MAX - 1)
|
|
+
|
|
+enum {
|
|
+ TCA_FQ_CODEL_XSTATS_QDISC,
|
|
+ TCA_FQ_CODEL_XSTATS_CLASS,
|
|
+};
|
|
+
|
|
+struct tc_fq_codel_qd_stats {
|
|
+ __u32 maxpacket; /* largest packet we've seen so far */
|
|
+ __u32 drop_overlimit; /* number of time max qdisc
|
|
+ * packet limit was hit
|
|
+ */
|
|
+ __u32 ecn_mark; /* number of packets we ECN marked
|
|
+ * instead of being dropped
|
|
+ */
|
|
+ __u32 new_flow_count; /* number of time packets
|
|
+ * created a 'new flow'
|
|
+ */
|
|
+ __u32 new_flows_len; /* count of flows in new list */
|
|
+ __u32 old_flows_len; /* count of flows in old list */
|
|
+};
|
|
+
|
|
+struct tc_fq_codel_cl_stats {
|
|
+ __s32 deficit;
|
|
+ __u32 ldelay; /* in-queue delay seen by most recently
|
|
+ * dequeued packet
|
|
+ */
|
|
+ __u32 count;
|
|
+ __u32 lastcount;
|
|
+ __u32 dropping;
|
|
+ __s32 drop_next;
|
|
+};
|
|
+
|
|
+struct tc_fq_codel_xstats {
|
|
+ __u32 type;
|
|
+ union {
|
|
+ struct tc_fq_codel_qd_stats qdisc_stats;
|
|
+ struct tc_fq_codel_cl_stats class_stats;
|
|
+ };
|
|
+};
|
|
#endif
|
|
diff --git a/include/netlink-private/types.h b/include/netlink-private/types.h
|
|
index 60a3bce..d6d056d 100644
|
|
--- a/include/netlink-private/types.h
|
|
+++ b/include/netlink-private/types.h
|
|
@@ -590,6 +590,17 @@ struct rtnl_tbf
|
|
uint32_t qt_mask;
|
|
};
|
|
|
|
+struct rtnl_fq_codel_qdisc
|
|
+{
|
|
+ uint32_t qcq_target;
|
|
+ uint32_t qcq_limit;
|
|
+ uint32_t qcq_interval;
|
|
+ uint32_t qcq_ecn;
|
|
+ uint32_t qcq_flows;
|
|
+ uint32_t qcq_quantum;
|
|
+ uint32_t qcq_mask;
|
|
+};
|
|
+
|
|
struct rtnl_sfq
|
|
{
|
|
uint32_t qs_quantum;
|
|
diff --git a/include/netlink/route/qdisc/fq_codel.h b/include/netlink/route/qdisc/fq_codel.h
|
|
new file mode 100644
|
|
index 0000000..da7c637
|
|
--- /dev/null
|
|
+++ b/include/netlink/route/qdisc/fq_codel.h
|
|
@@ -0,0 +1,41 @@
|
|
+/*
|
|
+ * netlink/route/sch/fq_codel.h Fair Queue CODEL Qdisc
|
|
+ */
|
|
+
|
|
+#ifndef NETLINK_FQ_CODEL_H_
|
|
+#define NETLINK_FQ_CODEL_H_
|
|
+
|
|
+#include <netlink/netlink.h>
|
|
+#include <netlink/route/tc.h>
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+/* Target Delay in usecs - Default 5 * 1000us */
|
|
+extern int rtnl_fq_codel_qdisc_get_target_usecs(struct rtnl_qdisc *);
|
|
+extern int rtnl_fq_codel_qdisc_set_target_usecs(struct rtnl_qdisc *,
|
|
+ uint32_t);
|
|
+/* Maximum number of enqueued packets before tail drop begins */
|
|
+extern int rtnl_fq_codel_qdisc_get_packet_limit(struct rtnl_qdisc *);
|
|
+extern int rtnl_fq_codel_qdisc_set_packet_limit(struct rtnl_qdisc *,
|
|
+ uint32_t);
|
|
+/* Moving window for calculating target delay in usecs - Default 100 * 1000us */
|
|
+extern int rtnl_fq_codel_qdisc_get_interval(struct rtnl_qdisc *);
|
|
+extern int rtnl_fq_codel_qdisc_set_interval(struct rtnl_qdisc *, uint32_t);
|
|
+/* Determines whether or not drops can be emulated via ECN - Default False */
|
|
+extern int rtnl_fq_codel_qdisc_get_ecn(struct rtnl_qdisc *);
|
|
+extern int rtnl_fq_codel_qdisc_set_ecn(struct rtnl_qdisc *, uint32_t);
|
|
+/* The maximum number of flows into which connections may be classified */
|
|
+extern int rtnl_fq_codel_qdisc_get_max_flow_count(struct rtnl_qdisc *);
|
|
+extern int rtnl_fq_codel_qdisc_set_max_flow_count(struct rtnl_qdisc *,
|
|
+ uint32_t);
|
|
+/* The quantum value for byte round robin between flows */
|
|
+extern int rtnl_fq_codel_qdisc_get_quantum(struct rtnl_qdisc *);
|
|
+extern int rtnl_fq_codel_qdisc_set_quantum(struct rtnl_qdisc *, uint32_t);
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+
|
|
+#endif
|
|
diff --git a/lib/Makefile.am b/lib/Makefile.am
|
|
index 677a89c..1dce098 100644
|
|
--- a/lib/Makefile.am
|
|
+++ b/lib/Makefile.am
|
|
@@ -75,6 +75,7 @@ libnl_route_3_la_SOURCES = \
|
|
route/qdisc/fifo.c route/qdisc/htb.c route/qdisc/netem.c \
|
|
route/qdisc/prio.c route/qdisc/red.c route/qdisc/sfq.c \
|
|
route/qdisc/tbf.c route/qdisc/plug.c \
|
|
+ route/qdisc/fq_codel.c \
|
|
\
|
|
fib_lookup/lookup.c fib_lookup/request.c \
|
|
\
|
|
diff --git a/lib/route/qdisc/fq_codel.c b/lib/route/qdisc/fq_codel.c
|
|
new file mode 100644
|
|
index 0000000..3c99887
|
|
--- /dev/null
|
|
+++ b/lib/route/qdisc/fq_codel.c
|
|
@@ -0,0 +1,413 @@
|
|
+/*
|
|
+ * lib/route/qdisc/fq_codel.c Fair Queue CODEL Qdisc
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * @ingroup qdisc
|
|
+ * @defgroup qdisc_fq_codel Fair Queue with Per-Flow Controlled Delay AQM
|
|
+ * @{
|
|
+ */
|
|
+
|
|
+#include <netlink-private/netlink.h>
|
|
+#include <netlink-private/tc.h>
|
|
+#include <netlink/netlink.h>
|
|
+#include <netlink/cache.h>
|
|
+#include <netlink/utils.h>
|
|
+#include <netlink-private/route/tc-api.h>
|
|
+#include <netlink/route/qdisc.h>
|
|
+#include <netlink/route/link.h>
|
|
+#include <netlink/route/qdisc/fq_codel.h>
|
|
+
|
|
+/** @cond SKIP */
|
|
+#define SCH_FQ_CODEL_ATTR_TARGET 0x01
|
|
+#define SCH_FQ_CODEL_ATTR_LIMIT 0x02
|
|
+#define SCH_FQ_CODEL_ATTR_INTERVAL 0x04
|
|
+#define SCH_FQ_CODEL_ATTR_ECN 0x08
|
|
+#define SCH_FQ_CODEL_ATTR_FLOWS 0x10
|
|
+#define SCH_FQ_CODEL_ATTR_QUANTUM 0x20
|
|
+/** @endcond */
|
|
+
|
|
+static struct nla_policy fq_codel_policy[TCA_FQ_CODEL_MAX + 1] = {
|
|
+ [TCA_FQ_CODEL_TARGET] = { .type = NLA_U32 },
|
|
+ [TCA_FQ_CODEL_LIMIT] = { .type = NLA_U32 },
|
|
+ [TCA_FQ_CODEL_INTERVAL] = { .type = NLA_U32 },
|
|
+ [TCA_FQ_CODEL_ECN] = { .type = NLA_U32 },
|
|
+ [TCA_FQ_CODEL_FLOWS] = { .type = NLA_U32 },
|
|
+ [TCA_FQ_CODEL_QUANTUM] = { .type = NLA_U32 },
|
|
+};
|
|
+
|
|
+static int fq_codel_qdisc_msg_parser(struct rtnl_tc *tc, void *data)
|
|
+{
|
|
+ struct rtnl_fq_codel_qdisc *fq_codel = data;
|
|
+ struct nlattr *tb[TCA_FQ_CODEL_MAX + 1];
|
|
+ int err;
|
|
+
|
|
+ err = tca_parse(tb, TCA_FQ_CODEL_MAX, tc, fq_codel_policy);
|
|
+ if (err < 0)
|
|
+ return err;
|
|
+
|
|
+ if (tb[TCA_FQ_CODEL_TARGET]) {
|
|
+ fq_codel->qcq_target = nla_get_u32(tb[TCA_FQ_CODEL_TARGET]);
|
|
+ fq_codel->qcq_mask |= SCH_FQ_CODEL_ATTR_TARGET;
|
|
+ }
|
|
+
|
|
+ if (tb[TCA_FQ_CODEL_LIMIT]) {
|
|
+ fq_codel->qcq_limit = nla_get_u32(tb[TCA_FQ_CODEL_LIMIT]);
|
|
+ fq_codel->qcq_mask |= SCH_FQ_CODEL_ATTR_LIMIT;
|
|
+ }
|
|
+
|
|
+ if (tb[TCA_FQ_CODEL_INTERVAL]) {
|
|
+ fq_codel->qcq_interval = nla_get_u32(tb[TCA_FQ_CODEL_INTERVAL]);
|
|
+ fq_codel->qcq_mask |= SCH_FQ_CODEL_ATTR_INTERVAL;
|
|
+ }
|
|
+
|
|
+ if (tb[TCA_FQ_CODEL_ECN]) {
|
|
+ fq_codel->qcq_ecn = !!nla_get_u32(tb[TCA_FQ_CODEL_ECN]);
|
|
+ fq_codel->qcq_mask |= SCH_FQ_CODEL_ATTR_ECN;
|
|
+ }
|
|
+
|
|
+ if (tb[TCA_FQ_CODEL_FLOWS]) {
|
|
+ fq_codel->qcq_flows = nla_get_u32(tb[TCA_FQ_CODEL_FLOWS]);
|
|
+ fq_codel->qcq_mask |= SCH_FQ_CODEL_ATTR_FLOWS;
|
|
+ }
|
|
+
|
|
+ if (tb[TCA_FQ_CODEL_QUANTUM]) {
|
|
+ fq_codel->qcq_quantum = nla_get_u32(tb[TCA_FQ_CODEL_QUANTUM]);
|
|
+ fq_codel->qcq_mask |= SCH_FQ_CODEL_ATTR_QUANTUM;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void fq_codel_qdisc_dump_line(struct rtnl_tc *tc, void *data,
|
|
+ struct nl_dump_params *p)
|
|
+{
|
|
+ struct rtnl_fq_codel_qdisc *fq_codel = data;
|
|
+
|
|
+ if (fq_codel && (fq_codel->qcq_mask & SCH_FQ_CODEL_ATTR_TARGET))
|
|
+ nl_dump(p, " target %u usecs", fq_codel->qcq_target);
|
|
+
|
|
+ if (fq_codel && (fq_codel->qcq_mask & SCH_FQ_CODEL_ATTR_FLOWS))
|
|
+ nl_dump(p, " max_flows %u", fq_codel->qcq_flows);
|
|
+}
|
|
+
|
|
+static void fq_codel_qdisc_dump_details(struct rtnl_tc *tc, void *data,
|
|
+ struct nl_dump_params *p)
|
|
+{
|
|
+ struct rtnl_fq_codel_qdisc *fq_codel = data;
|
|
+
|
|
+ if (!fq_codel)
|
|
+ return;
|
|
+
|
|
+ if (fq_codel && (fq_codel->qcq_mask & SCH_FQ_CODEL_ATTR_LIMIT))
|
|
+ nl_dump(p, " limit %u packets", fq_codel->qcq_limit);
|
|
+
|
|
+ if (fq_codel && (fq_codel->qcq_mask & SCH_FQ_CODEL_ATTR_INTERVAL))
|
|
+ nl_dump(p, " interval %u usecs", fq_codel->qcq_limit);
|
|
+
|
|
+ if (fq_codel && (fq_codel->qcq_mask & SCH_FQ_CODEL_ATTR_ECN))
|
|
+ nl_dump(p, " ecn %s", fq_codel->qcq_ecn ? "enable" : "disable");
|
|
+
|
|
+ if (fq_codel && (fq_codel->qcq_mask & SCH_FQ_CODEL_ATTR_QUANTUM))
|
|
+ nl_dump(p, " quantum %u bytes", fq_codel->qcq_quantum);
|
|
+}
|
|
+
|
|
+static int fq_codel_qdisc_msg_fill(struct rtnl_tc *tc, void *data,
|
|
+ struct nl_msg *msg)
|
|
+{
|
|
+ struct rtnl_fq_codel_qdisc *fq_codel = data;
|
|
+
|
|
+ if (!fq_codel)
|
|
+ return 0;
|
|
+
|
|
+ if (fq_codel && (fq_codel->qcq_mask & SCH_FQ_CODEL_ATTR_TARGET))
|
|
+ NLA_PUT_U32(msg, TCA_FQ_CODEL_TARGET, fq_codel->qcq_target);
|
|
+
|
|
+ if (fq_codel && (fq_codel->qcq_mask & SCH_FQ_CODEL_ATTR_LIMIT))
|
|
+ NLA_PUT_U32(msg, TCA_FQ_CODEL_LIMIT, fq_codel->qcq_limit);
|
|
+
|
|
+ if (fq_codel && (fq_codel->qcq_mask & SCH_FQ_CODEL_ATTR_INTERVAL))
|
|
+ NLA_PUT_U32(msg, TCA_FQ_CODEL_INTERVAL, fq_codel->qcq_interval);
|
|
+
|
|
+ if (fq_codel && (fq_codel->qcq_mask & SCH_FQ_CODEL_ATTR_ECN))
|
|
+ NLA_PUT_U32(msg, TCA_FQ_CODEL_ECN, fq_codel->qcq_ecn);
|
|
+
|
|
+ if (fq_codel && (fq_codel->qcq_mask & SCH_FQ_CODEL_ATTR_FLOWS))
|
|
+ NLA_PUT_U32(msg, TCA_FQ_CODEL_FLOWS, fq_codel->qcq_flows);
|
|
+
|
|
+ if (fq_codel && (fq_codel->qcq_mask & SCH_FQ_CODEL_ATTR_QUANTUM))
|
|
+ NLA_PUT_U32(msg, TCA_FQ_CODEL_QUANTUM, fq_codel->qcq_quantum);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+nla_put_failure:
|
|
+ return -NLE_MSGSIZE;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @name Qdisc Attribute Access
|
|
+ * @{
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * Get target delay for FQ_CODEL qdisc.
|
|
+ * @arg qdisc FQ_CODEL qdisc.
|
|
+ * @return Target delay in microseconds, or a negative error code.
|
|
+ */
|
|
+int rtnl_fq_codel_qdisc_get_target_usecs(struct rtnl_qdisc *qdisc)
|
|
+{
|
|
+ struct rtnl_fq_codel_qdisc *fq_codel;
|
|
+
|
|
+ if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
|
|
+ return -NLE_NOMEM;
|
|
+
|
|
+ if (fq_codel->qcq_mask & SCH_FQ_CODEL_ATTR_TARGET)
|
|
+ return fq_codel->qcq_target;
|
|
+ else
|
|
+ return -NLE_NOATTR;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Sets the target delay for FQ_CODEL qdisc.
|
|
+ * @arg qdisc FQ_CODEL qdisc to be modified.
|
|
+ * @arg target_usecs Target delay in miscroseconds.
|
|
+ * @return 0 on success, or a negative error code.
|
|
+ */
|
|
+int rtnl_fq_codel_qdisc_set_target_usecs(struct rtnl_qdisc *qdisc,
|
|
+ uint32_t target_usecs)
|
|
+{
|
|
+ struct rtnl_fq_codel_qdisc *fq_codel;
|
|
+
|
|
+ if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
|
|
+ return -NLE_NOMEM;
|
|
+
|
|
+ fq_codel->qcq_target = target_usecs;
|
|
+ fq_codel->qcq_mask |= SCH_FQ_CODEL_ATTR_TARGET;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Get maximum number of enqueued packets before tail drop occurs.
|
|
+ * @arg qdisc FQ_CODEL qdisc.
|
|
+ * @return Maximum number of enqueued packets, or a negative error code.
|
|
+ */
|
|
+int rtnl_fq_codel_qdisc_get_packet_limit(struct rtnl_qdisc *qdisc)
|
|
+{
|
|
+ struct rtnl_fq_codel_qdisc *fq_codel;
|
|
+
|
|
+ if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
|
|
+ return -NLE_NOMEM;
|
|
+
|
|
+ if (fq_codel->qcq_mask & SCH_FQ_CODEL_ATTR_LIMIT)
|
|
+ return fq_codel->qcq_limit;
|
|
+ else
|
|
+ return -NLE_NOATTR;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Sets the maximum number of queued packets for FQ_CODEL qdisc.
|
|
+ * @arg qdisc FQ_CODEL qdisc to be modified.
|
|
+ * @arg limit Maximum number of queued packets before tail drop starts.
|
|
+ * @return 0 on success, or a negative error code.
|
|
+ */
|
|
+int rtnl_fq_codel_qdisc_set_packet_limit(struct rtnl_qdisc *qdisc,
|
|
+ uint32_t max_packets)
|
|
+{
|
|
+ struct rtnl_fq_codel_qdisc *fq_codel;
|
|
+
|
|
+ if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
|
|
+ return -NLE_NOMEM;
|
|
+
|
|
+ fq_codel->qcq_limit = max_packets;
|
|
+ fq_codel->qcq_mask |= SCH_FQ_CODEL_ATTR_LIMIT;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * Get width of the moving time window for calculating delay, in microseconds.
|
|
+ * @arg qdisc FQ_CODEL qdisc.
|
|
+ * @return Width of the moving window in microseconds, or a negative error code.
|
|
+ */
|
|
+int rtnl_fq_codel_qdisc_get_interval(struct rtnl_qdisc *qdisc)
|
|
+{
|
|
+ struct rtnl_fq_codel_qdisc *fq_codel;
|
|
+
|
|
+ if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
|
|
+ return -NLE_NOMEM;
|
|
+
|
|
+ if (fq_codel->qcq_mask & SCH_FQ_CODEL_ATTR_INTERVAL)
|
|
+ return fq_codel->qcq_interval;
|
|
+ else
|
|
+ return -NLE_NOATTR;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Sets the width of the moving window over which the delay is calculated.
|
|
+ * @arg qdisc FQ_CODEL qdisc to be modified.
|
|
+ * @arg window_usecs Moving window width in microseconds
|
|
+ * @return 0 on success, or a negative error code.
|
|
+ */
|
|
+int rtnl_fq_codel_qdisc_set_interval(struct rtnl_qdisc *qdisc,
|
|
+ uint32_t window_usecs)
|
|
+{
|
|
+ struct rtnl_fq_codel_qdisc *fq_codel;
|
|
+
|
|
+ if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
|
|
+ return -NLE_NOMEM;
|
|
+
|
|
+ fq_codel->qcq_interval = window_usecs;
|
|
+ fq_codel->qcq_mask |= SCH_FQ_CODEL_ATTR_INTERVAL;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * Get ECN for FQ_CODEL qdisc. If this is set, FQ_CODEL will mark packets with
|
|
+ * ECN rather than dropping them, where it is possible.
|
|
+ * @arg qdisc FQ_CODEL qdisc.
|
|
+ * @return 1 if ECN marking instead of dropping is enabled, 0 if it is disabled,
|
|
+ * or a negative error code.
|
|
+ */
|
|
+int rtnl_fq_codel_qdisc_get_ecn(struct rtnl_qdisc *qdisc)
|
|
+{
|
|
+ struct rtnl_fq_codel_qdisc *fq_codel;
|
|
+
|
|
+ if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
|
|
+ return -NLE_NOMEM;
|
|
+
|
|
+ if (fq_codel->qcq_mask & SCH_FQ_CODEL_ATTR_ECN)
|
|
+ return !!(fq_codel->qcq_ecn);
|
|
+ else
|
|
+ return -NLE_NOATTR;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Sets the target delay for FQ_CODEL qdisc.
|
|
+ * @arg qdisc FQ_CODEL qdisc to be modified.
|
|
+ * @arg ecn 0 indicates ECN marking instead of dropping is disabled,
|
|
+ * and 1 indicates that ECN marking instead of dropping is enabled.
|
|
+ * @return 0 on success, or a negative error code.
|
|
+ */
|
|
+int rtnl_fq_codel_qdisc_set_ecn(struct rtnl_qdisc *qdisc, uint32_t ecn)
|
|
+{
|
|
+ struct rtnl_fq_codel_qdisc *fq_codel;
|
|
+
|
|
+ if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
|
|
+ return -NLE_NOMEM;
|
|
+
|
|
+ fq_codel->qcq_ecn = !!(ecn);
|
|
+ fq_codel->qcq_mask |= SCH_FQ_CODEL_ATTR_ECN;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Get maximum number of flows for FQ_CODEL qdisc. This is the number of
|
|
+ * flows into which connections may be classified.
|
|
+ * @arg qdisc FQ_CODEL qdisc.
|
|
+ * @return Maximum number of flows for qdisc, or a negative error code.
|
|
+ */
|
|
+int rtnl_fq_codel_qdisc_get_max_flow_count(struct rtnl_qdisc *qdisc)
|
|
+{
|
|
+ struct rtnl_fq_codel_qdisc *fq_codel;
|
|
+
|
|
+ if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
|
|
+ return -NLE_NOMEM;
|
|
+
|
|
+ if (fq_codel->qcq_mask & SCH_FQ_CODEL_ATTR_FLOWS)
|
|
+ return fq_codel->qcq_flows;
|
|
+ else
|
|
+ return -NLE_NOATTR;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Set the maximum number of flows for FQ_CODEL qdisc. This is the number of
|
|
+ * flows into which connections may be classified.
|
|
+ * @arg qdisc FQ_CODEL qdisc to be modified.
|
|
+ * @arg ecn New maximum number of flows. This must be greater than
|
|
+ * 0, and less than 65536.
|
|
+ * @return 0 on success, or a negative error code.
|
|
+ */
|
|
+int rtnl_fq_codel_qdisc_set_max_flow_count(struct rtnl_qdisc *qdisc,
|
|
+ uint32_t max_flows)
|
|
+{
|
|
+ struct rtnl_fq_codel_qdisc *fq_codel;
|
|
+
|
|
+ if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
|
|
+ return -NLE_NOMEM;
|
|
+
|
|
+ fq_codel->qcq_flows = max_flows;
|
|
+ fq_codel->qcq_mask |= SCH_FQ_CODEL_ATTR_FLOWS;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Get the quantum for round robin between the active flows.
|
|
+ * @arg qdisc FQ_CODEL qdisc.
|
|
+ * @return The current quantum in bytes each flow receives during round robin.
|
|
+ */
|
|
+int rtnl_fq_codel_qdisc_get_quantum(struct rtnl_qdisc *qdisc)
|
|
+{
|
|
+ struct rtnl_fq_codel_qdisc *fq_codel;
|
|
+
|
|
+ if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
|
|
+ return -NLE_NOMEM;
|
|
+
|
|
+ if (fq_codel->qcq_mask & SCH_FQ_CODEL_ATTR_QUANTUM)
|
|
+ return fq_codel->qcq_quantum;
|
|
+ else
|
|
+ return -NLE_NOATTR;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Set the quantum for round robin between the active flows.
|
|
+ * @arg qdisc FQ_CODEL qdisc to be modified.
|
|
+ * @arg quantum Set the number of bytes each flow may transmit before
|
|
+ * allowing the next flow to transmit during round robin between active flows.
|
|
+ * If this number is < 256, it will be set to 256.
|
|
+ * @return 0 on success, or a negative error code.
|
|
+ */
|
|
+int rtnl_fq_codel_qdisc_set_quantum(struct rtnl_qdisc *qdisc,
|
|
+ uint32_t quantum)
|
|
+{
|
|
+ struct rtnl_fq_codel_qdisc *fq_codel;
|
|
+
|
|
+ if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
|
|
+ return -NLE_NOMEM;
|
|
+
|
|
+ fq_codel->qcq_quantum = quantum;
|
|
+ fq_codel->qcq_mask |= SCH_FQ_CODEL_ATTR_QUANTUM;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/** @} */
|
|
+
|
|
+static struct rtnl_tc_ops fq_codel_qdisc_ops = {
|
|
+ .to_kind = "fq_codel",
|
|
+ .to_type = RTNL_TC_TYPE_QDISC,
|
|
+ .to_size = sizeof(struct rtnl_fq_codel_qdisc),
|
|
+ .to_msg_parser = fq_codel_qdisc_msg_parser,
|
|
+ .to_dump = {
|
|
+ [NL_DUMP_LINE] = fq_codel_qdisc_dump_line,
|
|
+ [NL_DUMP_DETAILS] = fq_codel_qdisc_dump_details,
|
|
+ },
|
|
+ .to_msg_fill = fq_codel_qdisc_msg_fill,
|
|
+};
|
|
+
|
|
+static void __init fq_codel_init(void)
|
|
+{
|
|
+ rtnl_tc_register(&fq_codel_qdisc_ops);
|
|
+}
|
|
+
|
|
+static void __exit fq_codel_exit(void)
|
|
+{
|
|
+ rtnl_tc_unregister(&fq_codel_qdisc_ops);
|
|
+}
|
|
+
|
|
+/** @} */
|