This commit was generated by cvs2svn to compensate for changes in r786,
[libnl.git] / lib / route / sch / prio.c
diff --git a/lib/route/sch/prio.c b/lib/route/sch/prio.c
new file mode 100644 (file)
index 0000000..aa06f9c
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ * lib/route/sch/prio.c                PRIO Qdisc/Class
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup qdisc
+ * @defgroup prio (Fast) Prio
+ * @brief
+ *
+ * @par 1) Typical PRIO configuration
+ * @code
+ * // Specify the maximal number of bands to be used for this PRIO qdisc.
+ * rtnl_qdisc_prio_set_bands(qdisc, QDISC_PRIO_DEFAULT_BANDS);
+ *
+ * // Provide a map assigning each priority to a band number.
+ * uint8_t map[] = QDISC_PRIO_DEFAULT_PRIOMAP;
+ * rtnl_qdisc_prio_set_priomap(qdisc, map, sizeof(map));
+ * @endcode
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink-tc.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/route/qdisc.h>
+#include <netlink/route/qdisc-modules.h>
+#include <netlink/route/sch/prio.h>
+
+/** @cond SKIP */
+#define SCH_PRIO_ATTR_BANDS    1
+#define SCH_PRIO_ATTR_PRIOMAP  2
+/** @endcond */
+
+static inline struct rtnl_prio *prio_qdisc(struct rtnl_qdisc *qdisc)
+{
+       return (struct rtnl_prio *) qdisc->q_subdata;
+}
+
+static inline struct rtnl_prio *prio_alloc(struct rtnl_qdisc *qdisc)
+{
+       if (!qdisc->q_subdata)
+               qdisc->q_subdata = calloc(1, sizeof(struct rtnl_prio));
+
+       return prio_qdisc(qdisc);
+}
+
+static int prio_msg_parser(struct rtnl_qdisc *qdisc)
+{
+       struct rtnl_prio *prio;
+       struct tc_prio_qopt *opt;
+
+       if (qdisc->q_opts->d_size < sizeof(*opt))
+               return nl_error(EINVAL, "prio specific option size mismatch");
+
+       prio = prio_alloc(qdisc);
+       if (!prio)
+               return nl_errno(ENOMEM);
+
+       opt = (struct tc_prio_qopt *) qdisc->q_opts->d_data;
+       prio->qp_bands = opt->bands;
+       memcpy(prio->qp_priomap, opt->priomap, sizeof(prio->qp_priomap));
+       prio->qp_mask = (SCH_PRIO_ATTR_BANDS | SCH_PRIO_ATTR_PRIOMAP);
+       
+       return 0;
+}
+
+static void prio_free_data(struct rtnl_qdisc *qdisc)
+{
+       free(qdisc->q_subdata);
+}
+
+static int prio_dump_brief(struct rtnl_qdisc *qdisc,
+                          struct nl_dump_params *p, int line)
+{
+       struct rtnl_prio *prio = prio_qdisc(qdisc);
+
+       if (prio)
+               dp_dump(p, " bands %u", prio->qp_bands);
+
+       return line;
+}
+
+static int prio_dump_full(struct rtnl_qdisc *qdisc,
+                         struct nl_dump_params *p, int line)
+{
+       struct rtnl_prio *prio = prio_qdisc(qdisc);
+       int i, hp;
+
+       if (!prio)
+               goto ignore;
+
+       dp_dump(p, "priomap [");
+       
+       for (i = 0; i <= TC_PRIO_MAX; i++)
+               dp_dump(p, "%u%s", prio->qp_priomap[i],
+                       i < TC_PRIO_MAX ? " " : "");
+
+       dp_dump(p, "]\n");
+       dp_new_line(p, line++);
+
+       hp = (((TC_PRIO_MAX/2) + 1) & ~1);
+
+       for (i = 0; i < hp; i++) {
+               char a[32];
+               dp_dump(p, "    %18s => %u",
+                       rtnl_prio2str(i, a, sizeof(a)),
+                       prio->qp_priomap[i]);
+               if (hp+i <= TC_PRIO_MAX) {
+                       dp_dump(p, " %18s => %u",
+                               rtnl_prio2str(hp+i, a, sizeof(a)),
+                               prio->qp_priomap[hp+i]);
+                       if (i < (hp - 1)) {
+                               dp_dump(p, "\n");
+                               dp_new_line(p, line++);
+                       }
+               }
+       }
+
+ignore:
+       return line;
+}
+
+static struct nl_msg *prio_get_opts(struct rtnl_qdisc *qdisc)
+{
+       struct rtnl_prio *prio;
+       struct tc_prio_qopt opts;
+       struct nl_msg *msg;
+
+       prio = prio_qdisc(qdisc);
+       if (!prio ||
+           !(prio->qp_mask & SCH_PRIO_ATTR_PRIOMAP))
+               goto errout;
+
+       opts.bands = prio->qp_bands;
+       memcpy(opts.priomap, prio->qp_priomap, sizeof(opts.priomap));
+
+       msg = nlmsg_build_no_hdr();
+       if (!msg)
+               goto errout;
+
+       if (nlmsg_append(msg, &opts, sizeof(opts), 0) < 0) {
+               nlmsg_free(msg);
+               goto errout;
+       }
+
+       return msg;
+errout:
+       return NULL;
+}
+
+/**
+ * @name Attribute Modification
+ * @{
+ */
+
+/**
+ * Set number of bands of PRIO qdisc.
+ * @arg qdisc          PRIO qdisc to be modified.
+ * @arg bands          New number of bands.
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_prio_set_bands(struct rtnl_qdisc *qdisc, int bands)
+{
+       struct rtnl_prio *prio;
+       
+       prio = prio_alloc(qdisc);
+       if (!prio)
+               return nl_errno(ENOMEM);
+
+       prio->qp_bands = bands;
+       prio->qp_mask |= SCH_PRIO_ATTR_BANDS;
+
+       return 0;
+}
+
+/**
+ * Get number of bands of PRIO qdisc.
+ * @arg qdisc          PRIO qdisc.
+ * @return Number of bands or a negative error code.
+ */
+int rtnl_qdisc_prio_get_bands(struct rtnl_qdisc *qdisc)
+{
+       struct rtnl_prio *prio;
+
+       prio = prio_qdisc(qdisc);
+       if (prio && prio->qp_mask & SCH_PRIO_ATTR_BANDS)
+               return prio->qp_bands;
+       else
+               return nl_errno(ENOMEM);
+}
+
+/**
+ * Set priomap of the PRIO qdisc.
+ * @arg qdisc          PRIO qdisc to be modified.
+ * @arg priomap                New priority mapping.
+ * @arg len            Length of priomap (# of elements).
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_prio_set_priomap(struct rtnl_qdisc *qdisc, uint8_t priomap[],
+                               int len)
+{
+       struct rtnl_prio *prio;
+       int i;
+
+       prio = prio_alloc(qdisc);
+       if (!prio)
+               return nl_errno(ENOMEM);
+
+       if (!(prio->qp_mask & SCH_PRIO_ATTR_BANDS))
+               return nl_error(EINVAL, "Set number of bands first");
+
+       if ((len / sizeof(uint8_t)) > (TC_PRIO_MAX+1))
+               return nl_error(ERANGE, "priomap length out of bounds");
+
+       for (i = 0; i <= TC_PRIO_MAX; i++) {
+               if (priomap[i] > prio->qp_bands)
+                       return nl_error(ERANGE, "priomap element %d " \
+                           "out of bounds, increase bands number");
+       }
+
+       memcpy(prio->qp_priomap, priomap, len);
+       prio->qp_mask |= SCH_PRIO_ATTR_PRIOMAP;
+
+       return 0;
+}
+
+/**
+ * Get priomap of a PRIO qdisc.
+ * @arg qdisc          PRIO qdisc.
+ * @return Priority mapping as array of size TC_PRIO_MAX+1
+ *         or NULL if an error occured.
+ */
+uint8_t *rtnl_qdisc_prio_get_priomap(struct rtnl_qdisc *qdisc)
+{
+       struct rtnl_prio *prio;
+
+       prio = prio_qdisc(qdisc);
+       if (prio && prio->qp_mask & SCH_PRIO_ATTR_PRIOMAP)
+               return prio->qp_priomap;
+       else {
+               nl_errno(ENOENT);
+               return NULL;
+       }
+}
+
+/** @} */
+
+/**
+ * @name Priority Band Translations
+ * @{
+ */
+
+static struct trans_tbl prios[] = {
+       __ADD(TC_PRIO_BESTEFFORT,besteffort)
+       __ADD(TC_PRIO_FILLER,filler)
+       __ADD(TC_PRIO_BULK,bulk)
+       __ADD(TC_PRIO_INTERACTIVE_BULK,interactive_bulk)
+       __ADD(TC_PRIO_INTERACTIVE,interactive)
+       __ADD(TC_PRIO_CONTROL,control)
+};
+
+/**
+ * Convert priority to character string.
+ * @arg prio           Priority.
+ * @arg buf            Destination buffer
+ * @arg size           Size of destination buffer.
+ *
+ * Converts a priority to a character string and stores the result in
+ * the specified destination buffer.
+ *
+ * @return Name of priority as character string.
+ */
+char * rtnl_prio2str(int prio, char *buf, size_t size)
+{
+       return __type2str(prio, buf, size, prios, ARRAY_SIZE(prios));
+}
+
+/**
+ * Convert character string to priority.
+ * @arg name           Name of priority.
+ *
+ * Converts the provided character string specifying a priority
+ * to the corresponding numeric value.
+ *
+ * @return Numeric priority or a negative value if no match was found.
+ */
+int rtnl_str2prio(const char *name)
+{
+       return __str2type(name, prios, ARRAY_SIZE(prios));
+}
+
+/** @} */
+
+static struct rtnl_qdisc_ops prio_ops = {
+       .qo_kind                = "prio",
+       .qo_msg_parser          = prio_msg_parser,
+       .qo_free_data           = prio_free_data,
+       .qo_dump[NL_DUMP_BRIEF] = prio_dump_brief,
+       .qo_dump[NL_DUMP_FULL]  = prio_dump_full,
+       .qo_get_opts            = prio_get_opts,
+};
+
+static struct rtnl_qdisc_ops pfifo_fast_ops = {
+       .qo_kind                = "pfifo_fast",
+       .qo_msg_parser          = prio_msg_parser,
+       .qo_free_data           = prio_free_data,
+       .qo_dump[NL_DUMP_BRIEF] = prio_dump_brief,
+       .qo_dump[NL_DUMP_FULL]  = prio_dump_full,
+       .qo_get_opts            = prio_get_opts,
+};
+
+static void __init prio_init(void)
+{
+       rtnl_qdisc_register(&prio_ops);
+       rtnl_qdisc_register(&pfifo_fast_ops);
+}
+
+static void __exit prio_exit(void)
+{
+       rtnl_qdisc_unregister(&prio_ops);
+       rtnl_qdisc_unregister(&pfifo_fast_ops);
+}
+
+/** @} */