libnl 1.1
lib/route/sch/prio.c
00001 /*
00002  * lib/route/sch/prio.c         PRIO Qdisc/Class
00003  *
00004  *      This library is free software; you can redistribute it and/or
00005  *      modify it under the terms of the GNU Lesser General Public
00006  *      License as published by the Free Software Foundation version 2.1
00007  *      of the License.
00008  *
00009  * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
00010  */
00011 
00012 /**
00013  * @ingroup qdisc_api
00014  * @defgroup prio (Fast) Prio
00015  * @brief
00016  *
00017  * @par 1) Typical PRIO configuration
00018  * @code
00019  * // Specify the maximal number of bands to be used for this PRIO qdisc.
00020  * rtnl_qdisc_prio_set_bands(qdisc, QDISC_PRIO_DEFAULT_BANDS);
00021  *
00022  * // Provide a map assigning each priority to a band number.
00023  * uint8_t map[] = QDISC_PRIO_DEFAULT_PRIOMAP;
00024  * rtnl_qdisc_prio_set_priomap(qdisc, map, sizeof(map));
00025  * @endcode
00026  * @{
00027  */
00028 
00029 #include <netlink-local.h>
00030 #include <netlink-tc.h>
00031 #include <netlink/netlink.h>
00032 #include <netlink/utils.h>
00033 #include <netlink/route/qdisc.h>
00034 #include <netlink/route/qdisc-modules.h>
00035 #include <netlink/route/sch/prio.h>
00036 
00037 /** @cond SKIP */
00038 #define SCH_PRIO_ATTR_BANDS     1
00039 #define SCH_PRIO_ATTR_PRIOMAP   2
00040 /** @endcond */
00041 
00042 static inline struct rtnl_prio *prio_qdisc(struct rtnl_qdisc *qdisc)
00043 {
00044         return (struct rtnl_prio *) qdisc->q_subdata;
00045 }
00046 
00047 static inline struct rtnl_prio *prio_alloc(struct rtnl_qdisc *qdisc)
00048 {
00049         if (!qdisc->q_subdata)
00050                 qdisc->q_subdata = calloc(1, sizeof(struct rtnl_prio));
00051 
00052         return prio_qdisc(qdisc);
00053 }
00054 
00055 static int prio_msg_parser(struct rtnl_qdisc *qdisc)
00056 {
00057         struct rtnl_prio *prio;
00058         struct tc_prio_qopt *opt;
00059 
00060         if (qdisc->q_opts->d_size < sizeof(*opt))
00061                 return nl_error(EINVAL, "prio specific option size mismatch");
00062 
00063         prio = prio_alloc(qdisc);
00064         if (!prio)
00065                 return nl_errno(ENOMEM);
00066 
00067         opt = (struct tc_prio_qopt *) qdisc->q_opts->d_data;
00068         prio->qp_bands = opt->bands;
00069         memcpy(prio->qp_priomap, opt->priomap, sizeof(prio->qp_priomap));
00070         prio->qp_mask = (SCH_PRIO_ATTR_BANDS | SCH_PRIO_ATTR_PRIOMAP);
00071         
00072         return 0;
00073 }
00074 
00075 static void prio_free_data(struct rtnl_qdisc *qdisc)
00076 {
00077         free(qdisc->q_subdata);
00078 }
00079 
00080 static int prio_dump_brief(struct rtnl_qdisc *qdisc,
00081                            struct nl_dump_params *p, int line)
00082 {
00083         struct rtnl_prio *prio = prio_qdisc(qdisc);
00084 
00085         if (prio)
00086                 dp_dump(p, " bands %u", prio->qp_bands);
00087 
00088         return line;
00089 }
00090 
00091 static int prio_dump_full(struct rtnl_qdisc *qdisc,
00092                           struct nl_dump_params *p, int line)
00093 {
00094         struct rtnl_prio *prio = prio_qdisc(qdisc);
00095         int i, hp;
00096 
00097         if (!prio)
00098                 goto ignore;
00099 
00100         dp_dump(p, "priomap [");
00101         
00102         for (i = 0; i <= TC_PRIO_MAX; i++)
00103                 dp_dump(p, "%u%s", prio->qp_priomap[i],
00104                         i < TC_PRIO_MAX ? " " : "");
00105 
00106         dp_dump(p, "]\n");
00107         dp_new_line(p, line++);
00108 
00109         hp = (((TC_PRIO_MAX/2) + 1) & ~1);
00110 
00111         for (i = 0; i < hp; i++) {
00112                 char a[32];
00113                 dp_dump(p, "    %18s => %u",
00114                         rtnl_prio2str(i, a, sizeof(a)),
00115                         prio->qp_priomap[i]);
00116                 if (hp+i <= TC_PRIO_MAX) {
00117                         dp_dump(p, " %18s => %u",
00118                                 rtnl_prio2str(hp+i, a, sizeof(a)),
00119                                 prio->qp_priomap[hp+i]);
00120                         if (i < (hp - 1)) {
00121                                 dp_dump(p, "\n");
00122                                 dp_new_line(p, line++);
00123                         }
00124                 }
00125         }
00126 
00127 ignore:
00128         return line;
00129 }
00130 
00131 static struct nl_msg *prio_get_opts(struct rtnl_qdisc *qdisc)
00132 {
00133         struct rtnl_prio *prio;
00134         struct tc_prio_qopt opts;
00135         struct nl_msg *msg;
00136 
00137         prio = prio_qdisc(qdisc);
00138         if (!prio ||
00139             !(prio->qp_mask & SCH_PRIO_ATTR_PRIOMAP))
00140                 goto errout;
00141 
00142         opts.bands = prio->qp_bands;
00143         memcpy(opts.priomap, prio->qp_priomap, sizeof(opts.priomap));
00144 
00145         msg = nlmsg_alloc();
00146         if (!msg)
00147                 goto errout;
00148 
00149         if (nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD) < 0) {
00150                 nlmsg_free(msg);
00151                 goto errout;
00152         }
00153 
00154         return msg;
00155 errout:
00156         return NULL;
00157 }
00158 
00159 /**
00160  * @name Attribute Modification
00161  * @{
00162  */
00163 
00164 /**
00165  * Set number of bands of PRIO qdisc.
00166  * @arg qdisc           PRIO qdisc to be modified.
00167  * @arg bands           New number of bands.
00168  * @return 0 on success or a negative error code.
00169  */
00170 int rtnl_qdisc_prio_set_bands(struct rtnl_qdisc *qdisc, int bands)
00171 {
00172         struct rtnl_prio *prio;
00173         
00174         prio = prio_alloc(qdisc);
00175         if (!prio)
00176                 return nl_errno(ENOMEM);
00177 
00178         prio->qp_bands = bands;
00179         prio->qp_mask |= SCH_PRIO_ATTR_BANDS;
00180 
00181         return 0;
00182 }
00183 
00184 /**
00185  * Get number of bands of PRIO qdisc.
00186  * @arg qdisc           PRIO qdisc.
00187  * @return Number of bands or a negative error code.
00188  */
00189 int rtnl_qdisc_prio_get_bands(struct rtnl_qdisc *qdisc)
00190 {
00191         struct rtnl_prio *prio;
00192 
00193         prio = prio_qdisc(qdisc);
00194         if (prio && prio->qp_mask & SCH_PRIO_ATTR_BANDS)
00195                 return prio->qp_bands;
00196         else
00197                 return nl_errno(ENOMEM);
00198 }
00199 
00200 /**
00201  * Set priomap of the PRIO qdisc.
00202  * @arg qdisc           PRIO qdisc to be modified.
00203  * @arg priomap         New priority mapping.
00204  * @arg len             Length of priomap (# of elements).
00205  * @return 0 on success or a negative error code.
00206  */
00207 int rtnl_qdisc_prio_set_priomap(struct rtnl_qdisc *qdisc, uint8_t priomap[],
00208                                 int len)
00209 {
00210         struct rtnl_prio *prio;
00211         int i;
00212 
00213         prio = prio_alloc(qdisc);
00214         if (!prio)
00215                 return nl_errno(ENOMEM);
00216 
00217         if (!(prio->qp_mask & SCH_PRIO_ATTR_BANDS))
00218                 return nl_error(EINVAL, "Set number of bands first");
00219 
00220         if ((len / sizeof(uint8_t)) > (TC_PRIO_MAX+1))
00221                 return nl_error(ERANGE, "priomap length out of bounds");
00222 
00223         for (i = 0; i <= TC_PRIO_MAX; i++) {
00224                 if (priomap[i] > prio->qp_bands)
00225                         return nl_error(ERANGE, "priomap element %d " \
00226                             "out of bounds, increase bands number");
00227         }
00228 
00229         memcpy(prio->qp_priomap, priomap, len);
00230         prio->qp_mask |= SCH_PRIO_ATTR_PRIOMAP;
00231 
00232         return 0;
00233 }
00234 
00235 /**
00236  * Get priomap of a PRIO qdisc.
00237  * @arg qdisc           PRIO qdisc.
00238  * @return Priority mapping as array of size TC_PRIO_MAX+1
00239  *         or NULL if an error occured.
00240  */
00241 uint8_t *rtnl_qdisc_prio_get_priomap(struct rtnl_qdisc *qdisc)
00242 {
00243         struct rtnl_prio *prio;
00244 
00245         prio = prio_qdisc(qdisc);
00246         if (prio && prio->qp_mask & SCH_PRIO_ATTR_PRIOMAP)
00247                 return prio->qp_priomap;
00248         else {
00249                 nl_errno(ENOENT);
00250                 return NULL;
00251         }
00252 }
00253 
00254 /** @} */
00255 
00256 /**
00257  * @name Priority Band Translations
00258  * @{
00259  */
00260 
00261 static struct trans_tbl prios[] = {
00262         __ADD(TC_PRIO_BESTEFFORT,besteffort)
00263         __ADD(TC_PRIO_FILLER,filler)
00264         __ADD(TC_PRIO_BULK,bulk)
00265         __ADD(TC_PRIO_INTERACTIVE_BULK,interactive_bulk)
00266         __ADD(TC_PRIO_INTERACTIVE,interactive)
00267         __ADD(TC_PRIO_CONTROL,control)
00268 };
00269 
00270 /**
00271  * Convert priority to character string.
00272  * @arg prio            Priority.
00273  * @arg buf             Destination buffer
00274  * @arg size            Size of destination buffer.
00275  *
00276  * Converts a priority to a character string and stores the result in
00277  * the specified destination buffer.
00278  *
00279  * @return Name of priority as character string.
00280  */
00281 char * rtnl_prio2str(int prio, char *buf, size_t size)
00282 {
00283         return __type2str(prio, buf, size, prios, ARRAY_SIZE(prios));
00284 }
00285 
00286 /**
00287  * Convert character string to priority.
00288  * @arg name            Name of priority.
00289  *
00290  * Converts the provided character string specifying a priority
00291  * to the corresponding numeric value.
00292  *
00293  * @return Numeric priority or a negative value if no match was found.
00294  */
00295 int rtnl_str2prio(const char *name)
00296 {
00297         return __str2type(name, prios, ARRAY_SIZE(prios));
00298 }
00299 
00300 /** @} */
00301 
00302 static struct rtnl_qdisc_ops prio_ops = {
00303         .qo_kind                = "prio",
00304         .qo_msg_parser          = prio_msg_parser,
00305         .qo_free_data           = prio_free_data,
00306         .qo_dump[NL_DUMP_BRIEF] = prio_dump_brief,
00307         .qo_dump[NL_DUMP_FULL]  = prio_dump_full,
00308         .qo_get_opts            = prio_get_opts,
00309 };
00310 
00311 static struct rtnl_qdisc_ops pfifo_fast_ops = {
00312         .qo_kind                = "pfifo_fast",
00313         .qo_msg_parser          = prio_msg_parser,
00314         .qo_free_data           = prio_free_data,
00315         .qo_dump[NL_DUMP_BRIEF] = prio_dump_brief,
00316         .qo_dump[NL_DUMP_FULL]  = prio_dump_full,
00317         .qo_get_opts            = prio_get_opts,
00318 };
00319 
00320 static void __init prio_init(void)
00321 {
00322         rtnl_qdisc_register(&prio_ops);
00323         rtnl_qdisc_register(&pfifo_fast_ops);
00324 }
00325 
00326 static void __exit prio_exit(void)
00327 {
00328         rtnl_qdisc_unregister(&prio_ops);
00329         rtnl_qdisc_unregister(&pfifo_fast_ops);
00330 }
00331 
00332 /** @} */