This commit was generated by cvs2svn to compensate for changes in r786,
[libnl.git] / lib / route / sch / prio.c
1 /*
2  * lib/route/sch/prio.c         PRIO Qdisc/Class
3  *
4  *      This library is free software; you can redistribute it and/or
5  *      modify it under the terms of the GNU Lesser General Public
6  *      License as published by the Free Software Foundation version 2.1
7  *      of the License.
8  *
9  * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
10  */
11
12 /**
13  * @ingroup qdisc
14  * @defgroup prio (Fast) Prio
15  * @brief
16  *
17  * @par 1) Typical PRIO configuration
18  * @code
19  * // Specify the maximal number of bands to be used for this PRIO qdisc.
20  * rtnl_qdisc_prio_set_bands(qdisc, QDISC_PRIO_DEFAULT_BANDS);
21  *
22  * // Provide a map assigning each priority to a band number.
23  * uint8_t map[] = QDISC_PRIO_DEFAULT_PRIOMAP;
24  * rtnl_qdisc_prio_set_priomap(qdisc, map, sizeof(map));
25  * @endcode
26  * @{
27  */
28
29 #include <netlink-local.h>
30 #include <netlink-tc.h>
31 #include <netlink/netlink.h>
32 #include <netlink/utils.h>
33 #include <netlink/route/qdisc.h>
34 #include <netlink/route/qdisc-modules.h>
35 #include <netlink/route/sch/prio.h>
36
37 /** @cond SKIP */
38 #define SCH_PRIO_ATTR_BANDS     1
39 #define SCH_PRIO_ATTR_PRIOMAP   2
40 /** @endcond */
41
42 static inline struct rtnl_prio *prio_qdisc(struct rtnl_qdisc *qdisc)
43 {
44         return (struct rtnl_prio *) qdisc->q_subdata;
45 }
46
47 static inline struct rtnl_prio *prio_alloc(struct rtnl_qdisc *qdisc)
48 {
49         if (!qdisc->q_subdata)
50                 qdisc->q_subdata = calloc(1, sizeof(struct rtnl_prio));
51
52         return prio_qdisc(qdisc);
53 }
54
55 static int prio_msg_parser(struct rtnl_qdisc *qdisc)
56 {
57         struct rtnl_prio *prio;
58         struct tc_prio_qopt *opt;
59
60         if (qdisc->q_opts->d_size < sizeof(*opt))
61                 return nl_error(EINVAL, "prio specific option size mismatch");
62
63         prio = prio_alloc(qdisc);
64         if (!prio)
65                 return nl_errno(ENOMEM);
66
67         opt = (struct tc_prio_qopt *) qdisc->q_opts->d_data;
68         prio->qp_bands = opt->bands;
69         memcpy(prio->qp_priomap, opt->priomap, sizeof(prio->qp_priomap));
70         prio->qp_mask = (SCH_PRIO_ATTR_BANDS | SCH_PRIO_ATTR_PRIOMAP);
71         
72         return 0;
73 }
74
75 static void prio_free_data(struct rtnl_qdisc *qdisc)
76 {
77         free(qdisc->q_subdata);
78 }
79
80 static int prio_dump_brief(struct rtnl_qdisc *qdisc,
81                            struct nl_dump_params *p, int line)
82 {
83         struct rtnl_prio *prio = prio_qdisc(qdisc);
84
85         if (prio)
86                 dp_dump(p, " bands %u", prio->qp_bands);
87
88         return line;
89 }
90
91 static int prio_dump_full(struct rtnl_qdisc *qdisc,
92                           struct nl_dump_params *p, int line)
93 {
94         struct rtnl_prio *prio = prio_qdisc(qdisc);
95         int i, hp;
96
97         if (!prio)
98                 goto ignore;
99
100         dp_dump(p, "priomap [");
101         
102         for (i = 0; i <= TC_PRIO_MAX; i++)
103                 dp_dump(p, "%u%s", prio->qp_priomap[i],
104                         i < TC_PRIO_MAX ? " " : "");
105
106         dp_dump(p, "]\n");
107         dp_new_line(p, line++);
108
109         hp = (((TC_PRIO_MAX/2) + 1) & ~1);
110
111         for (i = 0; i < hp; i++) {
112                 char a[32];
113                 dp_dump(p, "    %18s => %u",
114                         rtnl_prio2str(i, a, sizeof(a)),
115                         prio->qp_priomap[i]);
116                 if (hp+i <= TC_PRIO_MAX) {
117                         dp_dump(p, " %18s => %u",
118                                 rtnl_prio2str(hp+i, a, sizeof(a)),
119                                 prio->qp_priomap[hp+i]);
120                         if (i < (hp - 1)) {
121                                 dp_dump(p, "\n");
122                                 dp_new_line(p, line++);
123                         }
124                 }
125         }
126
127 ignore:
128         return line;
129 }
130
131 static struct nl_msg *prio_get_opts(struct rtnl_qdisc *qdisc)
132 {
133         struct rtnl_prio *prio;
134         struct tc_prio_qopt opts;
135         struct nl_msg *msg;
136
137         prio = prio_qdisc(qdisc);
138         if (!prio ||
139             !(prio->qp_mask & SCH_PRIO_ATTR_PRIOMAP))
140                 goto errout;
141
142         opts.bands = prio->qp_bands;
143         memcpy(opts.priomap, prio->qp_priomap, sizeof(opts.priomap));
144
145         msg = nlmsg_build_no_hdr();
146         if (!msg)
147                 goto errout;
148
149         if (nlmsg_append(msg, &opts, sizeof(opts), 0) < 0) {
150                 nlmsg_free(msg);
151                 goto errout;
152         }
153
154         return msg;
155 errout:
156         return NULL;
157 }
158
159 /**
160  * @name Attribute Modification
161  * @{
162  */
163
164 /**
165  * Set number of bands of PRIO qdisc.
166  * @arg qdisc           PRIO qdisc to be modified.
167  * @arg bands           New number of bands.
168  * @return 0 on success or a negative error code.
169  */
170 int rtnl_qdisc_prio_set_bands(struct rtnl_qdisc *qdisc, int bands)
171 {
172         struct rtnl_prio *prio;
173         
174         prio = prio_alloc(qdisc);
175         if (!prio)
176                 return nl_errno(ENOMEM);
177
178         prio->qp_bands = bands;
179         prio->qp_mask |= SCH_PRIO_ATTR_BANDS;
180
181         return 0;
182 }
183
184 /**
185  * Get number of bands of PRIO qdisc.
186  * @arg qdisc           PRIO qdisc.
187  * @return Number of bands or a negative error code.
188  */
189 int rtnl_qdisc_prio_get_bands(struct rtnl_qdisc *qdisc)
190 {
191         struct rtnl_prio *prio;
192
193         prio = prio_qdisc(qdisc);
194         if (prio && prio->qp_mask & SCH_PRIO_ATTR_BANDS)
195                 return prio->qp_bands;
196         else
197                 return nl_errno(ENOMEM);
198 }
199
200 /**
201  * Set priomap of the PRIO qdisc.
202  * @arg qdisc           PRIO qdisc to be modified.
203  * @arg priomap         New priority mapping.
204  * @arg len             Length of priomap (# of elements).
205  * @return 0 on success or a negative error code.
206  */
207 int rtnl_qdisc_prio_set_priomap(struct rtnl_qdisc *qdisc, uint8_t priomap[],
208                                 int len)
209 {
210         struct rtnl_prio *prio;
211         int i;
212
213         prio = prio_alloc(qdisc);
214         if (!prio)
215                 return nl_errno(ENOMEM);
216
217         if (!(prio->qp_mask & SCH_PRIO_ATTR_BANDS))
218                 return nl_error(EINVAL, "Set number of bands first");
219
220         if ((len / sizeof(uint8_t)) > (TC_PRIO_MAX+1))
221                 return nl_error(ERANGE, "priomap length out of bounds");
222
223         for (i = 0; i <= TC_PRIO_MAX; i++) {
224                 if (priomap[i] > prio->qp_bands)
225                         return nl_error(ERANGE, "priomap element %d " \
226                             "out of bounds, increase bands number");
227         }
228
229         memcpy(prio->qp_priomap, priomap, len);
230         prio->qp_mask |= SCH_PRIO_ATTR_PRIOMAP;
231
232         return 0;
233 }
234
235 /**
236  * Get priomap of a PRIO qdisc.
237  * @arg qdisc           PRIO qdisc.
238  * @return Priority mapping as array of size TC_PRIO_MAX+1
239  *         or NULL if an error occured.
240  */
241 uint8_t *rtnl_qdisc_prio_get_priomap(struct rtnl_qdisc *qdisc)
242 {
243         struct rtnl_prio *prio;
244
245         prio = prio_qdisc(qdisc);
246         if (prio && prio->qp_mask & SCH_PRIO_ATTR_PRIOMAP)
247                 return prio->qp_priomap;
248         else {
249                 nl_errno(ENOENT);
250                 return NULL;
251         }
252 }
253
254 /** @} */
255
256 /**
257  * @name Priority Band Translations
258  * @{
259  */
260
261 static struct trans_tbl prios[] = {
262         __ADD(TC_PRIO_BESTEFFORT,besteffort)
263         __ADD(TC_PRIO_FILLER,filler)
264         __ADD(TC_PRIO_BULK,bulk)
265         __ADD(TC_PRIO_INTERACTIVE_BULK,interactive_bulk)
266         __ADD(TC_PRIO_INTERACTIVE,interactive)
267         __ADD(TC_PRIO_CONTROL,control)
268 };
269
270 /**
271  * Convert priority to character string.
272  * @arg prio            Priority.
273  * @arg buf             Destination buffer
274  * @arg size            Size of destination buffer.
275  *
276  * Converts a priority to a character string and stores the result in
277  * the specified destination buffer.
278  *
279  * @return Name of priority as character string.
280  */
281 char * rtnl_prio2str(int prio, char *buf, size_t size)
282 {
283         return __type2str(prio, buf, size, prios, ARRAY_SIZE(prios));
284 }
285
286 /**
287  * Convert character string to priority.
288  * @arg name            Name of priority.
289  *
290  * Converts the provided character string specifying a priority
291  * to the corresponding numeric value.
292  *
293  * @return Numeric priority or a negative value if no match was found.
294  */
295 int rtnl_str2prio(const char *name)
296 {
297         return __str2type(name, prios, ARRAY_SIZE(prios));
298 }
299
300 /** @} */
301
302 static struct rtnl_qdisc_ops prio_ops = {
303         .qo_kind                = "prio",
304         .qo_msg_parser          = prio_msg_parser,
305         .qo_free_data           = prio_free_data,
306         .qo_dump[NL_DUMP_BRIEF] = prio_dump_brief,
307         .qo_dump[NL_DUMP_FULL]  = prio_dump_full,
308         .qo_get_opts            = prio_get_opts,
309 };
310
311 static struct rtnl_qdisc_ops pfifo_fast_ops = {
312         .qo_kind                = "pfifo_fast",
313         .qo_msg_parser          = prio_msg_parser,
314         .qo_free_data           = prio_free_data,
315         .qo_dump[NL_DUMP_BRIEF] = prio_dump_brief,
316         .qo_dump[NL_DUMP_FULL]  = prio_dump_full,
317         .qo_get_opts            = prio_get_opts,
318 };
319
320 static void __init prio_init(void)
321 {
322         rtnl_qdisc_register(&prio_ops);
323         rtnl_qdisc_register(&pfifo_fast_ops);
324 }
325
326 static void __exit prio_exit(void)
327 {
328         rtnl_qdisc_unregister(&prio_ops);
329         rtnl_qdisc_unregister(&pfifo_fast_ops);
330 }
331
332 /** @} */