libnl  1.1.4
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_api
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_alloc();
146  if (!msg)
147  goto errout;
148 
149  if (nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD) < 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 /** @} */