net/mvpp2: support forwarding bad packets
[dpdk.git] / drivers / net / mvpp2 / mrvl_qos.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2017 Marvell International Ltd.
3  * Copyright(c) 2017 Semihalf.
4  * All rights reserved.
5  */
6
7 #include <stdint.h>
8 #include <stdlib.h>
9 #include <string.h>
10
11 #include <rte_common.h>
12 #include <rte_cfgfile.h>
13 #include <rte_log.h>
14 #include <rte_lcore.h>
15 #include <rte_malloc.h>
16 #include <rte_string_fns.h>
17
18 #include "mrvl_qos.h"
19
20 /* Parsing tokens. Defined conveniently, so that any correction is easy. */
21 #define MRVL_TOK_DEFAULT "default"
22 #define MRVL_TOK_DSA_MODE "dsa_mode"
23 #define MRVL_TOK_START_HDR "start_hdr"
24 #define MRVL_TOK_START_HDR_NONE "none"
25 #define MRVL_TOK_START_HDR_DSA "dsa"
26 #define MRVL_TOK_START_HDR_CUSTOM "custom"
27 #define MRVL_TOK_START_HDR_EXT_DSA "ext_dsa"
28 #define MRVL_TOK_DEFAULT_TC "default_tc"
29 #define MRVL_TOK_DSCP "dscp"
30 #define MRVL_TOK_MAPPING_PRIORITY "mapping_priority"
31 #define MRVL_TOK_IP "ip"
32 #define MRVL_TOK_IP_VLAN "ip/vlan"
33 #define MRVL_TOK_PCP "pcp"
34 #define MRVL_TOK_PORT "port"
35 #define MRVL_TOK_RXQ "rxq"
36 #define MRVL_TOK_TC "tc"
37 #define MRVL_TOK_TXQ "txq"
38 #define MRVL_TOK_VLAN "vlan"
39 #define MRVL_TOK_VLAN_IP "vlan/ip"
40 #define MRVL_TOK_PARSER_UDF "parser udf"
41
42 /* egress specific configuration tokens */
43 #define MRVL_TOK_BURST_SIZE "burst_size"
44 #define MRVL_TOK_RATE_LIMIT "rate_limit"
45 #define MRVL_TOK_RATE_LIMIT_ENABLE "rate_limit_enable"
46 #define MRVL_TOK_SCHED_MODE "sched_mode"
47 #define MRVL_TOK_SCHED_MODE_SP "sp"
48 #define MRVL_TOK_SCHED_MODE_WRR "wrr"
49 #define MRVL_TOK_WRR_WEIGHT "wrr_weight"
50
51 /* policer specific configuration tokens */
52 #define MRVL_TOK_PLCR "policer"
53 #define MRVL_TOK_PLCR_DEFAULT "default_policer"
54 #define MRVL_TOK_PLCR_UNIT "token_unit"
55 #define MRVL_TOK_PLCR_UNIT_BYTES "bytes"
56 #define MRVL_TOK_PLCR_UNIT_PACKETS "packets"
57 #define MRVL_TOK_PLCR_COLOR "color_mode"
58 #define MRVL_TOK_PLCR_COLOR_BLIND "blind"
59 #define MRVL_TOK_PLCR_COLOR_AWARE "aware"
60 #define MRVL_TOK_PLCR_CIR "cir"
61 #define MRVL_TOK_PLCR_CBS "cbs"
62 #define MRVL_TOK_PLCR_EBS "ebs"
63 #define MRVL_TOK_PLCR_DEFAULT_COLOR "default_color"
64 #define MRVL_TOK_PLCR_DEFAULT_COLOR_GREEN "green"
65 #define MRVL_TOK_PLCR_DEFAULT_COLOR_YELLOW "yellow"
66 #define MRVL_TOK_PLCR_DEFAULT_COLOR_RED "red"
67
68 /* parser udf specific configuration tokens */
69 #define MRVL_TOK_PARSER_UDF_PROTO "proto"
70 #define MRVL_TOK_PARSER_UDF_FIELD "field"
71 #define MRVL_TOK_PARSER_UDF_KEY "key"
72 #define MRVL_TOK_PARSER_UDF_MASK "mask"
73 #define MRVL_TOK_PARSER_UDF_OFFSET "offset"
74 #define MRVL_TOK_PARSER_UDF_PROTO_ETH "eth"
75 #define MRVL_TOK_PARSER_UDF_FIELD_ETH_TYPE "type"
76 #define MRVL_TOK_PARSER_UDF_PROTO_UDP "udp"
77 #define MRVL_TOK_PARSER_UDF_FIELD_UDP_DPORT "dport"
78
79 /* parser forward bad frames tokens */
80 #define MRVL_TOK_FWD_BAD_FRAMES "forward_bad_frames"
81
82 /** Number of tokens in range a-b = 2. */
83 #define MAX_RNG_TOKENS 2
84
85 /** Maximum possible value of PCP. */
86 #define MAX_PCP 7
87
88 /** Maximum possible value of DSCP. */
89 #define MAX_DSCP 63
90
91 /** Global configuration. */
92 struct mrvl_cfg *mrvl_cfg;
93
94 /**
95  * Read out-queue configuration from file.
96  *
97  * @param file Path to the configuration file.
98  * @param port Port number.
99  * @param outq Out queue number.
100  * @param cfg Pointer to the Marvell configuration structure.
101  * @returns 0 in case of success, negative value otherwise.
102  */
103 static int
104 get_outq_cfg(struct rte_cfgfile *file, int port, int outq,
105                 struct mrvl_cfg *cfg)
106 {
107         char sec_name[32];
108         const char *entry;
109         uint32_t val;
110
111         snprintf(sec_name, sizeof(sec_name), "%s %d %s %d",
112                 MRVL_TOK_PORT, port, MRVL_TOK_TXQ, outq);
113
114         /* Skip non-existing */
115         if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0)
116                 return 0;
117
118         /* Read scheduling mode */
119         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_SCHED_MODE);
120         if (entry) {
121                 if (!strncmp(entry, MRVL_TOK_SCHED_MODE_SP,
122                                         strlen(MRVL_TOK_SCHED_MODE_SP))) {
123                         cfg->port[port].outq[outq].sched_mode =
124                                 PP2_PPIO_SCHED_M_SP;
125                 } else if (!strncmp(entry, MRVL_TOK_SCHED_MODE_WRR,
126                                         strlen(MRVL_TOK_SCHED_MODE_WRR))) {
127                         cfg->port[port].outq[outq].sched_mode =
128                                 PP2_PPIO_SCHED_M_WRR;
129                 } else {
130                         MRVL_LOG(ERR, "Unknown token: %s", entry);
131                         return -1;
132                 }
133         }
134
135         /* Read wrr weight */
136         if (cfg->port[port].outq[outq].sched_mode == PP2_PPIO_SCHED_M_WRR) {
137                 entry = rte_cfgfile_get_entry(file, sec_name,
138                                 MRVL_TOK_WRR_WEIGHT);
139                 if (entry) {
140                         if (get_val_securely(entry, &val) < 0)
141                                 return -1;
142                         cfg->port[port].outq[outq].weight = val;
143                 }
144         }
145
146         /*
147          * There's no point in setting rate limiting for specific outq as
148          * global port rate limiting has priority.
149          */
150         if (cfg->port[port].rate_limit_enable) {
151                 MRVL_LOG(WARNING, "Port %d rate limiting already enabled",
152                         port);
153                 return 0;
154         }
155
156         entry = rte_cfgfile_get_entry(file, sec_name,
157                         MRVL_TOK_RATE_LIMIT_ENABLE);
158         if (entry) {
159                 if (get_val_securely(entry, &val) < 0)
160                         return -1;
161                 cfg->port[port].outq[outq].rate_limit_enable = val;
162         }
163
164         if (!cfg->port[port].outq[outq].rate_limit_enable)
165                 return 0;
166
167         /* Read CBS (in kB) */
168         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_BURST_SIZE);
169         if (entry) {
170                 if (get_val_securely(entry, &val) < 0)
171                         return -1;
172                 cfg->port[port].outq[outq].rate_limit_params.cbs = val;
173         }
174
175         /* Read CIR (in kbps) */
176         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_RATE_LIMIT);
177         if (entry) {
178                 if (get_val_securely(entry, &val) < 0)
179                         return -1;
180                 cfg->port[port].outq[outq].rate_limit_params.cir = val;
181         }
182
183         return 0;
184 }
185
186 /**
187  * Gets multiple-entry values and places them in table.
188  *
189  * Entry can be anything, e.g. "1 2-3 5 6 7-9". This needs to be converted to
190  * table entries, respectively: {1, 2, 3, 5, 6, 7, 8, 9}.
191  * As all result table's elements are always 1-byte long, we
192  * won't overcomplicate the function, but we'll keep API generic,
193  * check if someone hasn't changed element size and make it simple
194  * to extend to other sizes.
195  *
196  * This function is purely utilitary, it does not print any error, only returns
197  * different error numbers.
198  *
199  * @param entry[in] Values string to parse.
200  * @param tab[out] Results table.
201  * @param elem_sz[in] Element size (in bytes).
202  * @param max_elems[in] Number of results table elements available.
203  * @param max val[in] Maximum value allowed.
204  * @returns Number of correctly parsed elements in case of success.
205  * @retval -1 Wrong element size.
206  * @retval -2 More tokens than result table allows.
207  * @retval -3 Wrong range syntax.
208  * @retval -4 Wrong range values.
209  * @retval -5 Maximum value exceeded.
210  */
211 static int
212 get_entry_values(const char *entry, uint8_t *tab,
213         size_t elem_sz, uint8_t max_elems, uint8_t max_val)
214 {
215         /* There should not be more tokens than max elements.
216          * Add 1 for error trap.
217          */
218         char *tokens[max_elems + 1];
219
220         /* Begin, End + error trap = 3. */
221         char *rng_tokens[MAX_RNG_TOKENS + 1];
222         long beg, end;
223         uint32_t token_val;
224         int nb_tokens, nb_rng_tokens;
225         int i;
226         int values = 0;
227         char val;
228         char entry_cpy[CFG_VALUE_LEN];
229
230         if (elem_sz != 1)
231                 return -1;
232
233         /* Copy the entry to safely use rte_strsplit(). */
234         strlcpy(entry_cpy, entry, RTE_DIM(entry_cpy));
235
236         /*
237          * If there are more tokens than array size, rte_strsplit will
238          * not return error, just array size.
239          */
240         nb_tokens = rte_strsplit(entry_cpy, strlen(entry_cpy),
241                 tokens, max_elems + 1, ' ');
242
243         /* Quick check, will be refined later. */
244         if (nb_tokens > max_elems)
245                 return -2;
246
247         for (i = 0; i < nb_tokens; ++i) {
248                 if (strchr(tokens[i], '-') != NULL) {
249                         /*
250                          * Split to begin and end tokens.
251                          * We want to catch error cases too, thus we leave
252                          * option for number of tokens to be more than 2.
253                          */
254                         nb_rng_tokens = rte_strsplit(tokens[i],
255                                         strlen(tokens[i]), rng_tokens,
256                                         RTE_DIM(rng_tokens), '-');
257                         if (nb_rng_tokens != 2)
258                                 return -3;
259
260                         /* Range and sanity checks. */
261                         if (get_val_securely(rng_tokens[0], &token_val) < 0)
262                                 return -4;
263                         beg = (char)token_val;
264                         if (get_val_securely(rng_tokens[1], &token_val) < 0)
265                                 return -4;
266                         end = (char)token_val;
267                         if (beg < 0 || beg > UCHAR_MAX ||
268                                 end < 0 || end > UCHAR_MAX || end < beg)
269                                 return -4;
270
271                         for (val = beg; val <= end; ++val) {
272                                 if (val > max_val)
273                                         return -5;
274
275                                 *tab = val;
276                                 tab = RTE_PTR_ADD(tab, elem_sz);
277                                 ++values;
278                                 if (values >= max_elems)
279                                         return -2;
280                         }
281                 } else {
282                         /* Single values. */
283                         if (get_val_securely(tokens[i], &token_val) < 0)
284                                 return -5;
285                         val = (char)token_val;
286                         if (val > max_val)
287                                 return -5;
288
289                         *tab = val;
290                         tab = RTE_PTR_ADD(tab, elem_sz);
291                         ++values;
292                         if (values >= max_elems)
293                                 return -2;
294                 }
295         }
296
297         return values;
298 }
299
300 /**
301  * Parse Traffic Class'es mapping configuration.
302  *
303  * @param file Config file handle.
304  * @param port Which port to look for.
305  * @param tc Which Traffic Class to look for.
306  * @param cfg[out] Parsing results.
307  * @returns 0 in case of success, negative value otherwise.
308  */
309 static int
310 parse_tc_cfg(struct rte_cfgfile *file, int port, int tc,
311                 struct mrvl_cfg *cfg)
312 {
313         char sec_name[32];
314         const char *entry;
315         int n;
316
317         snprintf(sec_name, sizeof(sec_name), "%s %d %s %d",
318                 MRVL_TOK_PORT, port, MRVL_TOK_TC, tc);
319
320         /* Skip non-existing */
321         if (rte_cfgfile_num_sections(file, sec_name, strlen(sec_name)) <= 0)
322                 return 0;
323
324         cfg->port[port].use_global_defaults = 0;
325         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_RXQ);
326         if (entry) {
327                 n = get_entry_values(entry,
328                         cfg->port[port].tc[tc].inq,
329                         sizeof(cfg->port[port].tc[tc].inq[0]),
330                         RTE_DIM(cfg->port[port].tc[tc].inq),
331                         MRVL_PP2_RXQ_MAX);
332                 if (n < 0) {
333                         MRVL_LOG(ERR, "Error %d while parsing: %s",
334                                 n, entry);
335                         return n;
336                 }
337                 cfg->port[port].tc[tc].inqs = n;
338         }
339
340         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PCP);
341         if (entry) {
342                 n = get_entry_values(entry,
343                         cfg->port[port].tc[tc].pcp,
344                         sizeof(cfg->port[port].tc[tc].pcp[0]),
345                         RTE_DIM(cfg->port[port].tc[tc].pcp),
346                         MAX_PCP);
347                 if (n < 0) {
348                         MRVL_LOG(ERR, "Error %d while parsing: %s",
349                                 n, entry);
350                         return n;
351                 }
352                 cfg->port[port].tc[tc].pcps = n;
353         }
354
355         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_DSCP);
356         if (entry) {
357                 n = get_entry_values(entry,
358                         cfg->port[port].tc[tc].dscp,
359                         sizeof(cfg->port[port].tc[tc].dscp[0]),
360                         RTE_DIM(cfg->port[port].tc[tc].dscp),
361                         MAX_DSCP);
362                 if (n < 0) {
363                         MRVL_LOG(ERR, "Error %d while parsing: %s",
364                                 n, entry);
365                         return n;
366                 }
367                 cfg->port[port].tc[tc].dscps = n;
368         }
369
370         if (!cfg->port[port].setup_policer)
371                 return 0;
372
373         entry = rte_cfgfile_get_entry(file, sec_name,
374                         MRVL_TOK_PLCR_DEFAULT_COLOR);
375         if (entry) {
376                 if (!strncmp(entry, MRVL_TOK_PLCR_DEFAULT_COLOR_GREEN,
377                                 sizeof(MRVL_TOK_PLCR_DEFAULT_COLOR_GREEN))) {
378                         cfg->port[port].tc[tc].color = PP2_PPIO_COLOR_GREEN;
379                 } else if (!strncmp(entry, MRVL_TOK_PLCR_DEFAULT_COLOR_YELLOW,
380                                 sizeof(MRVL_TOK_PLCR_DEFAULT_COLOR_YELLOW))) {
381                         cfg->port[port].tc[tc].color = PP2_PPIO_COLOR_YELLOW;
382                 } else if (!strncmp(entry, MRVL_TOK_PLCR_DEFAULT_COLOR_RED,
383                                 sizeof(MRVL_TOK_PLCR_DEFAULT_COLOR_RED))) {
384                         cfg->port[port].tc[tc].color = PP2_PPIO_COLOR_RED;
385                 } else {
386                         MRVL_LOG(ERR, "Error while parsing: %s", entry);
387                         return -1;
388                 }
389         }
390
391         return 0;
392 }
393
394 /**
395  * Parse default port policer.
396  *
397  * @param file Config file handle.
398  * @param sec_name Section name with policer configuration
399  * @param port Port number.
400  * @param cfg[out] Parsing results.
401  * @returns 0 in case of success, negative value otherwise.
402  */
403 static int
404 parse_policer(struct rte_cfgfile *file, int port, const char *sec_name,
405                 struct mrvl_cfg *cfg)
406 {
407         const char *entry;
408         uint32_t val;
409
410         /* Read policer token unit */
411         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_UNIT);
412         if (entry) {
413                 if (!strncmp(entry, MRVL_TOK_PLCR_UNIT_BYTES,
414                                         sizeof(MRVL_TOK_PLCR_UNIT_BYTES))) {
415                         cfg->port[port].policer_params.token_unit =
416                                 PP2_CLS_PLCR_BYTES_TOKEN_UNIT;
417                 } else if (!strncmp(entry, MRVL_TOK_PLCR_UNIT_PACKETS,
418                                         sizeof(MRVL_TOK_PLCR_UNIT_PACKETS))) {
419                         cfg->port[port].policer_params.token_unit =
420                                 PP2_CLS_PLCR_PACKETS_TOKEN_UNIT;
421                 } else {
422                         MRVL_LOG(ERR, "Unknown token: %s", entry);
423                         return -1;
424                 }
425         }
426
427         /* Read policer color mode */
428         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_COLOR);
429         if (entry) {
430                 if (!strncmp(entry, MRVL_TOK_PLCR_COLOR_BLIND,
431                                         sizeof(MRVL_TOK_PLCR_COLOR_BLIND))) {
432                         cfg->port[port].policer_params.color_mode =
433                                 PP2_CLS_PLCR_COLOR_BLIND_MODE;
434                 } else if (!strncmp(entry, MRVL_TOK_PLCR_COLOR_AWARE,
435                                         sizeof(MRVL_TOK_PLCR_COLOR_AWARE))) {
436                         cfg->port[port].policer_params.color_mode =
437                                 PP2_CLS_PLCR_COLOR_AWARE_MODE;
438                 } else {
439                         MRVL_LOG(ERR, "Error in parsing: %s", entry);
440                         return -1;
441                 }
442         }
443
444         /* Read policer cir */
445         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_CIR);
446         if (entry) {
447                 if (get_val_securely(entry, &val) < 0)
448                         return -1;
449                 cfg->port[port].policer_params.cir = val;
450         }
451
452         /* Read policer cbs */
453         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_CBS);
454         if (entry) {
455                 if (get_val_securely(entry, &val) < 0)
456                         return -1;
457                 cfg->port[port].policer_params.cbs = val;
458         }
459
460         /* Read policer ebs */
461         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PLCR_EBS);
462         if (entry) {
463                 if (get_val_securely(entry, &val) < 0)
464                         return -1;
465                 cfg->port[port].policer_params.ebs = val;
466         }
467
468         cfg->port[port].setup_policer = 1;
469
470         return 0;
471 }
472
473 /**
474  * Parse parser udf.
475  *
476  * @param file Config file handle.
477  * @param sec_name section name
478  * @param udf udf index
479  * @param cfg[out] Parsing results.
480  * @returns 0 in case of success, negative value otherwise.
481  */
482 static int
483 parse_udf(struct rte_cfgfile *file, const char *sec_name, int udf,
484           struct mrvl_cfg *cfg)
485 {
486         struct pp2_parse_udf_params *udf_params;
487         const char *entry, *entry_field;
488         uint32_t val, i;
489         uint8_t field_size;
490         char malloc_name[32], tmp_arr[3];
491         /* field len in chars equal to '0x' + rest of data */
492 #define FIELD_LEN_IN_CHARS(field_size)  (uint32_t)(2 + (field_size) * 2)
493
494         udf_params = &cfg->pp2_cfg.prs_udfs.udfs[udf];
495
496         /* Read 'proto' field */
497         entry = rte_cfgfile_get_entry(file, sec_name,
498                                       MRVL_TOK_PARSER_UDF_PROTO);
499         if (!entry) {
500                 MRVL_LOG(ERR, "UDF[%d]: '%s' field must be set\n", udf,
501                          MRVL_TOK_PARSER_UDF_PROTO);
502                 return -1;
503         }
504
505         /* Read 'field' field */
506         entry_field = rte_cfgfile_get_entry(file, sec_name,
507                                        MRVL_TOK_PARSER_UDF_FIELD);
508         if (!entry_field) {
509                 MRVL_LOG(ERR, "UDF[%d]: '%s' field must be set\n", udf,
510                          MRVL_TOK_PARSER_UDF_FIELD);
511                 return -1;
512         }
513
514         if (!strncmp(entry, MRVL_TOK_PARSER_UDF_PROTO_ETH,
515                                 sizeof(MRVL_TOK_PARSER_UDF_PROTO_ETH))) {
516                 udf_params->match_proto = MV_NET_PROTO_ETH;
517                 if (!strncmp(entry_field, MRVL_TOK_PARSER_UDF_FIELD_ETH_TYPE,
518                              sizeof(MRVL_TOK_PARSER_UDF_FIELD_ETH_TYPE))) {
519                         udf_params->match_field.eth = MV_NET_ETH_F_TYPE;
520                         field_size = 2;
521                 } else {
522                         MRVL_LOG(ERR, "UDF[%d]: mismatch between '%s' proto "
523                                  "and '%s' field\n", udf,
524                                  MRVL_TOK_PARSER_UDF_PROTO_ETH,
525                                  entry_field);
526                         return -1;
527                 }
528         } else if (!strncmp(entry, MRVL_TOK_PARSER_UDF_PROTO_UDP,
529                                 sizeof(MRVL_TOK_PARSER_UDF_PROTO_UDP))) {
530                 udf_params->match_proto = MV_NET_PROTO_UDP;
531                 if (!strncmp(entry_field, MRVL_TOK_PARSER_UDF_FIELD_UDP_DPORT,
532                              sizeof(MRVL_TOK_PARSER_UDF_FIELD_UDP_DPORT))) {
533                         udf_params->match_field.udp = MV_NET_UDP_F_DP;
534                         field_size = 2;
535                 } else {
536                         MRVL_LOG(ERR, "UDF[%d]: mismatch between '%s' proto "
537                                  "and '%s' field\n", udf,
538                                  MRVL_TOK_PARSER_UDF_PROTO_UDP,
539                                  entry_field);
540                         return -1;
541                 }
542         } else {
543                 MRVL_LOG(ERR, "UDF[%d]: Unsupported '%s' proto\n", udf, entry);
544                 return -1;
545         }
546
547         snprintf(malloc_name, sizeof(malloc_name), "mrvl_udf_%d_key", udf);
548         udf_params->match_key = rte_zmalloc(malloc_name, field_size, 0);
549         if (udf_params->match_key == NULL) {
550                 MRVL_LOG(ERR, "Cannot allocate udf %d key\n", udf);
551                 return -1;
552         }
553         snprintf(malloc_name, sizeof(malloc_name), "mrvl_udf_%d_mask", udf);
554         udf_params->match_mask = rte_zmalloc(malloc_name, field_size, 0);
555         if (udf_params->match_mask == NULL) {
556                 MRVL_LOG(ERR, "Cannot allocate udf %d mask\n", udf);
557                 return -1;
558         }
559
560         /* Read 'key' field */
561         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PARSER_UDF_KEY);
562         if (!entry) {
563                 MRVL_LOG(ERR, "UDF[%d]: '%s' field must be set\n", udf,
564                          MRVL_TOK_PARSER_UDF_KEY);
565                 return -1;
566         }
567
568         if (strncmp(entry, "0x", 2) != 0)  {
569                 MRVL_LOG(ERR, "UDF[%d]: '%s' field must start with '0x'\n",
570                          udf, MRVL_TOK_PARSER_UDF_KEY);
571                 return -EINVAL;
572         }
573
574         if (strlen(entry) != FIELD_LEN_IN_CHARS(field_size)) {
575                 MRVL_LOG(ERR, "UDF[%d]: '%s' field's len must be %d\n", udf,
576                          MRVL_TOK_PARSER_UDF_KEY,
577                          FIELD_LEN_IN_CHARS(field_size));
578                 return -EINVAL;
579         }
580
581         entry += 2; /* skip the '0x' */
582         for (i = 0; i < field_size; i++) {
583                 strncpy(tmp_arr, entry, 2);
584                 tmp_arr[2] = '\0';
585                 if (get_val_securely8(tmp_arr, 16,
586                                       &udf_params->match_key[i]) < 0) {
587                         MRVL_LOG(ERR, "UDF[%d]: '%s' field's value is not in "
588                                 "hex format\n", udf, MRVL_TOK_PARSER_UDF_KEY);
589                         return -EINVAL;
590                 }
591                 entry += 2;
592         }
593
594         /* Read 'mask' field */
595         entry = rte_cfgfile_get_entry(file, sec_name, MRVL_TOK_PARSER_UDF_MASK);
596         if (!entry) {
597                 MRVL_LOG(ERR, "UDF[%d]: '%s' field must be set\n", udf,
598                          MRVL_TOK_PARSER_UDF_MASK);
599                 return -1;
600         }
601         if (strncmp(entry, "0x", 2) != 0) {
602                 MRVL_LOG(ERR, "UDF[%d]: '%s' field must start with '0x'\n",
603                          udf, MRVL_TOK_PARSER_UDF_MASK);
604                 return -EINVAL;
605         }
606
607         if (strlen(entry) != FIELD_LEN_IN_CHARS(field_size)) {
608                 MRVL_LOG(ERR, "UDF[%d]: '%s' field's len must be %d\n", udf,
609                          MRVL_TOK_PARSER_UDF_MASK,
610                          FIELD_LEN_IN_CHARS(field_size));
611                 return -EINVAL;
612         }
613
614         entry += 2; /* skip the '0x' */
615         for (i = 0; i < field_size; i++) {
616                 strncpy(tmp_arr, entry, 2);
617                 tmp_arr[2] = '\0';
618                 if (get_val_securely8(tmp_arr, 16,
619                                       &udf_params->match_mask[i]) < 0) {
620                         MRVL_LOG(ERR, "UDF[%d]: '%s' field's value is not in "
621                                 "hex format\n", udf, MRVL_TOK_PARSER_UDF_MASK);
622                         return -EINVAL;
623                 }
624                 entry += 2;
625         }
626
627         /* Read offset */
628         entry = rte_cfgfile_get_entry(file, sec_name,
629                                       MRVL_TOK_PARSER_UDF_OFFSET);
630         if (!entry) {
631                 MRVL_LOG(ERR, "UDF[%d]: '%s' field must be set\n", udf,
632                          MRVL_TOK_PARSER_UDF_OFFSET);
633                 return -1;
634         }
635         if (get_val_securely(entry, &val) < 0)
636                 return -1;
637         udf_params->offset = val;
638
639         return 0;
640 }
641
642 /**
643  * Parse configuration - rte_kvargs_process handler.
644  *
645  * Opens configuration file and parses its content.
646  *
647  * @param key Unused.
648  * @param path Path to config file.
649  * @param extra_args Pointer to configuration structure.
650  * @returns 0 in case of success, exits otherwise.
651  */
652 int
653 mrvl_get_cfg(const char *key __rte_unused, const char *path, void *extra_args)
654 {
655         struct mrvl_cfg **cfg = extra_args;
656         struct rte_cfgfile *file = rte_cfgfile_load(path, 0);
657         uint32_t val;
658         int n, i, ret;
659         const char *entry;
660         char sec_name[32];
661
662         if (file == NULL) {
663                 MRVL_LOG(ERR, "Cannot load configuration %s\n", path);
664                 return -1;
665         }
666
667         /* Create configuration. This is never accessed on the fast path,
668          * so we can ignore socket.
669          */
670         *cfg = rte_zmalloc("mrvl_cfg", sizeof(struct mrvl_cfg), 0);
671         if (*cfg == NULL) {
672                 MRVL_LOG(ERR, "Cannot allocate configuration %s\n", path);
673                 return -1;
674         }
675
676         /* PP2 configuration */
677         n = rte_cfgfile_num_sections(file, MRVL_TOK_PARSER_UDF,
678                 sizeof(MRVL_TOK_PARSER_UDF) - 1);
679
680         if (n && n > PP2_MAX_UDFS_SUPPORTED) {
681                 MRVL_LOG(ERR, "found %d udf sections, but only %d are supported\n",
682                          n, PP2_MAX_UDFS_SUPPORTED);
683                 return -1;
684         }
685         (*cfg)->pp2_cfg.prs_udfs.num_udfs = n;
686         for (i = 0; i < n; i++) {
687                 snprintf(sec_name, sizeof(sec_name), "%s %d",
688                                 MRVL_TOK_PARSER_UDF, i);
689
690                 /* udf sections must be sequential. */
691                 if (rte_cfgfile_num_sections(file, sec_name,
692                                 strlen(sec_name)) <= 0) {
693                         MRVL_LOG(ERR, "udf sections must be sequential (0 - %d)\n",
694                                  PP2_MAX_UDFS_SUPPORTED - 1);
695                         return -1;
696                 }
697
698                 ret = parse_udf(file, sec_name, i, *cfg);
699                 if (ret) {
700                         MRVL_LOG(ERR, "Error in parsing %s!\n", sec_name);
701                         return -1;
702                 }
703         }
704
705         /* PP2 Ports configuration */
706         n = rte_cfgfile_num_sections(file, MRVL_TOK_PORT,
707                 sizeof(MRVL_TOK_PORT) - 1);
708
709         if (n == 0) {
710                 /* This is weird, but not bad. */
711                 MRVL_LOG(WARNING, "Empty configuration file?");
712                 return 0;
713         }
714
715         /* Use the number of ports given as vdev parameters. */
716         for (n = 0; n < (PP2_NUM_ETH_PPIO * PP2_NUM_PKT_PROC); ++n) {
717                 snprintf(sec_name, sizeof(sec_name), "%s %d %s",
718                         MRVL_TOK_PORT, n, MRVL_TOK_DEFAULT);
719
720                 /* Use global defaults, unless an override occurs */
721                 (*cfg)->port[n].use_global_defaults = 1;
722
723                 /* Skip ports non-existing in configuration. */
724                 if (rte_cfgfile_num_sections(file, sec_name,
725                                 strlen(sec_name)) <= 0) {
726                         continue;
727                 }
728
729                 /* MRVL_TOK_START_HDR replaces MRVL_TOK_DSA_MODE parameter.
730                  * MRVL_TOK_DSA_MODE will be supported for backward
731                  * compatibillity.
732                  */
733                 entry = rte_cfgfile_get_entry(file, sec_name,
734                                 MRVL_TOK_START_HDR);
735                 /* if start_hsr is missing, check if dsa_mode exist instead */
736                 if (entry == NULL)
737                         entry = rte_cfgfile_get_entry(file, sec_name,
738                                 MRVL_TOK_DSA_MODE);
739                 if (entry) {
740                         if (!strncmp(entry, MRVL_TOK_START_HDR_NONE,
741                                 sizeof(MRVL_TOK_START_HDR_NONE)))
742                                 (*cfg)->port[n].eth_start_hdr =
743                                 PP2_PPIO_HDR_ETH;
744                         else if (!strncmp(entry, MRVL_TOK_START_HDR_DSA,
745                                 sizeof(MRVL_TOK_START_HDR_DSA)))
746                                 (*cfg)->port[n].eth_start_hdr =
747                                 PP2_PPIO_HDR_ETH_DSA;
748                         else if (!strncmp(entry, MRVL_TOK_START_HDR_CUSTOM,
749                                 sizeof(MRVL_TOK_START_HDR_CUSTOM)))
750                                 (*cfg)->port[n].eth_start_hdr =
751                                 PP2_PPIO_HDR_ETH_CUSTOM;
752                         else if (!strncmp(entry, MRVL_TOK_START_HDR_EXT_DSA,
753                                 sizeof(MRVL_TOK_START_HDR_EXT_DSA))) {
754                                 (*cfg)->port[n].eth_start_hdr =
755                                 PP2_PPIO_HDR_ETH_EXT_DSA;
756                         } else {
757                                 MRVL_LOG(ERR,
758                                         "Error in parsing %s value (%s)!\n",
759                                         MRVL_TOK_START_HDR, entry);
760                                 return -1;
761                         }
762                 } else {
763                         (*cfg)->port[n].eth_start_hdr = PP2_PPIO_HDR_ETH;
764                 }
765
766                 /*
767                  * Read per-port rate limiting. Setting that will
768                  * disable per-queue rate limiting.
769                  */
770                 entry = rte_cfgfile_get_entry(file, sec_name,
771                                 MRVL_TOK_RATE_LIMIT_ENABLE);
772                 if (entry) {
773                         if (get_val_securely(entry, &val) < 0)
774                                 return -1;
775                         (*cfg)->port[n].rate_limit_enable = val;
776                 }
777
778                 if ((*cfg)->port[n].rate_limit_enable) {
779                         entry = rte_cfgfile_get_entry(file, sec_name,
780                                         MRVL_TOK_BURST_SIZE);
781                         if (entry) {
782                                 if (get_val_securely(entry, &val) < 0)
783                                         return -1;
784                                 (*cfg)->port[n].rate_limit_params.cbs = val;
785                         }
786
787                         entry = rte_cfgfile_get_entry(file, sec_name,
788                                         MRVL_TOK_RATE_LIMIT);
789                         if (entry) {
790                                 if (get_val_securely(entry, &val) < 0)
791                                         return -1;
792                                 (*cfg)->port[n].rate_limit_params.cir = val;
793                         }
794                 }
795
796                 entry = rte_cfgfile_get_entry(file, sec_name,
797                                 MRVL_TOK_MAPPING_PRIORITY);
798                 if (entry) {
799                         (*cfg)->port[n].use_global_defaults = 0;
800                         if (!strncmp(entry, MRVL_TOK_VLAN_IP,
801                                 sizeof(MRVL_TOK_VLAN_IP)))
802                                 (*cfg)->port[n].mapping_priority =
803                                         PP2_CLS_QOS_TBL_VLAN_IP_PRI;
804                         else if (!strncmp(entry, MRVL_TOK_IP_VLAN,
805                                 sizeof(MRVL_TOK_IP_VLAN)))
806                                 (*cfg)->port[n].mapping_priority =
807                                         PP2_CLS_QOS_TBL_IP_VLAN_PRI;
808                         else if (!strncmp(entry, MRVL_TOK_IP,
809                                 sizeof(MRVL_TOK_IP)))
810                                 (*cfg)->port[n].mapping_priority =
811                                         PP2_CLS_QOS_TBL_IP_PRI;
812                         else if (!strncmp(entry, MRVL_TOK_VLAN,
813                                 sizeof(MRVL_TOK_VLAN))) {
814                                 (*cfg)->port[n].mapping_priority =
815                                         PP2_CLS_QOS_TBL_VLAN_PRI;
816                         } else {
817                                 MRVL_LOG(ERR,
818                                         "Error in parsing %s value (%s)!\n",
819                                         MRVL_TOK_MAPPING_PRIORITY, entry);
820                                 return -1;
821                         }
822                 } else {
823                         (*cfg)->port[n].mapping_priority =
824                                 PP2_CLS_QOS_TBL_NONE;
825                 }
826
827                 /* Parse policer configuration (if any) */
828                 entry = rte_cfgfile_get_entry(file, sec_name,
829                                 MRVL_TOK_PLCR_DEFAULT);
830                 if (entry) {
831                         (*cfg)->port[n].use_global_defaults = 0;
832                         if (get_val_securely(entry, &val) < 0)
833                                 return -1;
834
835                         snprintf(sec_name, sizeof(sec_name), "%s %d",
836                                         MRVL_TOK_PLCR, val);
837                         ret = parse_policer(file, n, sec_name, *cfg);
838                         if (ret)
839                                 return -1;
840                 }
841
842                 for (i = 0; i < MRVL_PP2_RXQ_MAX; ++i) {
843                         ret = get_outq_cfg(file, n, i, *cfg);
844                         if (ret < 0) {
845                                 MRVL_LOG(ERR,
846                                         "Error %d parsing port %d outq %d!\n",
847                                         ret, n, i);
848                                 return -1;
849                         }
850                 }
851
852                 for (i = 0; i < MRVL_PP2_TC_MAX; ++i) {
853                         ret = parse_tc_cfg(file, n, i, *cfg);
854                         if (ret < 0) {
855                                 MRVL_LOG(ERR,
856                                         "Error %d parsing port %d tc %d!\n",
857                                         ret, n, i);
858                                 return -1;
859                         }
860                 }
861
862                 entry = rte_cfgfile_get_entry(file, sec_name,
863                                               MRVL_TOK_DEFAULT_TC);
864                 if (entry) {
865                         if (get_val_securely(entry, &val) < 0 ||
866                             val > USHRT_MAX)
867                                 return -1;
868                         (*cfg)->port[n].default_tc = (uint8_t)val;
869                 } else {
870                         if ((*cfg)->port[n].use_global_defaults == 0) {
871                                 MRVL_LOG(ERR,
872                                          "Default Traffic Class required in "
873                                          "custom configuration!");
874                                 return -1;
875                         }
876                 }
877
878                 /* Parse forward bad frames option */
879                 entry = rte_cfgfile_get_entry(file, sec_name,
880                                 MRVL_TOK_FWD_BAD_FRAMES);
881                 if (entry) {
882                         if (get_val_securely(entry, &val) < 0) {
883                                 MRVL_LOG(ERR,
884                                         "Error in parsing %s value (%s)!\n",
885                                         MRVL_TOK_FWD_BAD_FRAMES, entry);
886                                 return -1;
887                         }
888                         (*cfg)->port[n].forward_bad_frames = (uint8_t)val;
889                 } else {
890                         (*cfg)->port[n].forward_bad_frames = 0;
891                 }
892         }
893
894         return 0;
895 }
896
897 /**
898  * Setup Traffic Class.
899  *
900  * Fill in TC parameters in single MUSDK TC config entry.
901  * @param param TC parameters entry.
902  * @param inqs Number of MUSDK in-queues in this TC.
903  * @param bpool Bpool for this TC.
904  * @param color Default color for this TC.
905  * @returns 0 in case of success, exits otherwise.
906  */
907 static int
908 setup_tc(struct pp2_ppio_tc_params *param, uint8_t inqs,
909         struct pp2_bpool *bpool, enum pp2_ppio_color color)
910 {
911         struct pp2_ppio_inq_params *inq_params;
912
913         param->pkt_offset = MRVL_PKT_OFFS;
914         param->pools[0][0] = bpool;
915         param->pools[0][1] = dummy_pool[bpool->pp2_id];
916         param->default_color = color;
917
918         inq_params = rte_zmalloc_socket("inq_params",
919                 inqs * sizeof(*inq_params),
920                 0, rte_socket_id());
921         if (!inq_params)
922                 return -ENOMEM;
923
924         param->num_in_qs = inqs;
925
926         /* Release old config if necessary. */
927         if (param->inqs_params)
928                 rte_free(param->inqs_params);
929
930         param->inqs_params = inq_params;
931
932         return 0;
933 }
934
935 /**
936  * Setup ingress policer.
937  *
938  * @param priv Port's private data.
939  * @param params Pointer to the policer's configuration.
940  * @param plcr_id Policer id.
941  * @returns 0 in case of success, negative values otherwise.
942  */
943 static int
944 setup_policer(struct mrvl_priv *priv, struct pp2_cls_plcr_params *params)
945 {
946         char match[16];
947         int ret;
948
949         /*
950          * At this point no other policers are used which means
951          * any policer can be picked up and used as a default one.
952          *
953          * Lets use 0th then.
954          */
955         sprintf(match, "policer-%d:%d\n", priv->pp_id, 0);
956         params->match = match;
957
958         ret = pp2_cls_plcr_init(params, &priv->default_policer);
959         if (ret) {
960                 MRVL_LOG(ERR, "Failed to setup %s", match);
961                 return -1;
962         }
963
964         priv->ppio_params.inqs_params.plcr = priv->default_policer;
965         priv->used_plcrs = BIT(0);
966
967         return 0;
968 }
969
970 /**
971  * Configure RX Queues in a given port.
972  *
973  * Sets up RX queues, their Traffic Classes and DPDK rxq->(TC,inq) mapping.
974  *
975  * @param priv Port's private data
976  * @param portid DPDK port ID
977  * @param max_queues Maximum number of queues to configure.
978  * @returns 0 in case of success, negative value otherwise.
979  */
980 int
981 mrvl_configure_rxqs(struct mrvl_priv *priv, uint16_t portid,
982         uint16_t max_queues)
983 {
984         size_t i, tc;
985
986         if (mrvl_cfg == NULL ||
987                 mrvl_cfg->port[portid].use_global_defaults) {
988                 /*
989                  * No port configuration, use default: 1 TC, no QoS,
990                  * TC color set to green.
991                  */
992                 priv->ppio_params.inqs_params.num_tcs = 1;
993                 setup_tc(&priv->ppio_params.inqs_params.tcs_params[0],
994                         max_queues, priv->bpool, PP2_PPIO_COLOR_GREEN);
995
996                 /* Direct mapping of queues i.e. 0->0, 1->1 etc. */
997                 for (i = 0; i < max_queues; ++i) {
998                         priv->rxq_map[i].tc = 0;
999                         priv->rxq_map[i].inq = i;
1000                 }
1001                 return 0;
1002         }
1003
1004         /* We need only a subset of configuration. */
1005         struct port_cfg *port_cfg = &mrvl_cfg->port[portid];
1006
1007         priv->qos_tbl_params.type = port_cfg->mapping_priority;
1008
1009         /*
1010          * We need to reverse mapping, from tc->pcp (better from usability
1011          * point of view) to pcp->tc (configurable in MUSDK).
1012          * First, set all map elements to "default".
1013          */
1014         for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i)
1015                 priv->qos_tbl_params.pcp_cos_map[i].tc = port_cfg->default_tc;
1016
1017         /* Then, fill in all known values. */
1018         for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
1019                 if (port_cfg->tc[tc].pcps > RTE_DIM(port_cfg->tc[0].pcp)) {
1020                         /* Better safe than sorry. */
1021                         MRVL_LOG(ERR,
1022                                 "Too many PCPs configured in TC %zu!", tc);
1023                         return -1;
1024                 }
1025                 for (i = 0; i < port_cfg->tc[tc].pcps; ++i) {
1026                         priv->qos_tbl_params.pcp_cos_map[
1027                           port_cfg->tc[tc].pcp[i]].tc = tc;
1028                 }
1029         }
1030
1031         /*
1032          * The same logic goes with DSCP.
1033          * First, set all map elements to "default".
1034          */
1035         for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i)
1036                 priv->qos_tbl_params.dscp_cos_map[i].tc =
1037                         port_cfg->default_tc;
1038
1039         /* Fill in all known values. */
1040         for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
1041                 if (port_cfg->tc[tc].dscps > RTE_DIM(port_cfg->tc[0].dscp)) {
1042                         /* Better safe than sorry. */
1043                         MRVL_LOG(ERR,
1044                                 "Too many DSCPs configured in TC %zu!", tc);
1045                         return -1;
1046                 }
1047                 for (i = 0; i < port_cfg->tc[tc].dscps; ++i) {
1048                         priv->qos_tbl_params.dscp_cos_map[
1049                           port_cfg->tc[tc].dscp[i]].tc = tc;
1050                 }
1051         }
1052
1053         /*
1054          * Surprisingly, similar logic goes with queue mapping.
1055          * We need only to store qid->tc mapping,
1056          * to know TC when queue is read.
1057          */
1058         for (i = 0; i < RTE_DIM(priv->rxq_map); ++i)
1059                 priv->rxq_map[i].tc = MRVL_UNKNOWN_TC;
1060
1061         /* Set up DPDKq->(TC,inq) mapping. */
1062         for (tc = 0; tc < RTE_DIM(port_cfg->tc); ++tc) {
1063                 if (port_cfg->tc[tc].inqs > RTE_DIM(port_cfg->tc[0].inq)) {
1064                         /* Overflow. */
1065                         MRVL_LOG(ERR,
1066                                 "Too many RX queues configured per TC %zu!",
1067                                 tc);
1068                         return -1;
1069                 }
1070                 for (i = 0; i < port_cfg->tc[tc].inqs; ++i) {
1071                         uint8_t idx = port_cfg->tc[tc].inq[i];
1072
1073                         if (idx > RTE_DIM(priv->rxq_map)) {
1074                                 MRVL_LOG(ERR, "Bad queue index %d!", idx);
1075                                 return -1;
1076                         }
1077
1078                         priv->rxq_map[idx].tc = tc;
1079                         priv->rxq_map[idx].inq = i;
1080                 }
1081         }
1082
1083         /*
1084          * Set up TC configuration. TCs need to be sequenced: 0, 1, 2
1085          * with no gaps. Empty TC means end of processing.
1086          */
1087         for (i = 0; i < MRVL_PP2_TC_MAX; ++i) {
1088                 if (port_cfg->tc[i].inqs == 0)
1089                         break;
1090                 setup_tc(&priv->ppio_params.inqs_params.tcs_params[i],
1091                                 port_cfg->tc[i].inqs,
1092                                 priv->bpool, port_cfg->tc[i].color);
1093         }
1094
1095         priv->ppio_params.inqs_params.num_tcs = i;
1096
1097         if (port_cfg->setup_policer)
1098                 return setup_policer(priv, &port_cfg->policer_params);
1099
1100         return 0;
1101 }
1102
1103 /**
1104  * Configure TX Queues in a given port.
1105  *
1106  * Sets up TX queues egress scheduler and limiter.
1107  *
1108  * @param priv Port's private data
1109  * @param portid DPDK port ID
1110  * @param max_queues Maximum number of queues to configure.
1111  * @returns 0 in case of success, negative value otherwise.
1112  */
1113 int
1114 mrvl_configure_txqs(struct mrvl_priv *priv, uint16_t portid,
1115                 uint16_t max_queues)
1116 {
1117         /* We need only a subset of configuration. */
1118         struct port_cfg *port_cfg = &mrvl_cfg->port[portid];
1119         int i;
1120
1121         if (mrvl_cfg == NULL)
1122                 return 0;
1123
1124         priv->ppio_params.rate_limit_enable = port_cfg->rate_limit_enable;
1125         if (port_cfg->rate_limit_enable)
1126                 priv->ppio_params.rate_limit_params =
1127                         port_cfg->rate_limit_params;
1128
1129         for (i = 0; i < max_queues; i++) {
1130                 struct pp2_ppio_outq_params *params =
1131                         &priv->ppio_params.outqs_params.outqs_params[i];
1132
1133                 params->sched_mode = port_cfg->outq[i].sched_mode;
1134                 params->weight = port_cfg->outq[i].weight;
1135                 params->rate_limit_enable = port_cfg->outq[i].rate_limit_enable;
1136                 params->rate_limit_params = port_cfg->outq[i].rate_limit_params;
1137         }
1138
1139         return 0;
1140 }
1141
1142 /**
1143  * Start QoS mapping.
1144  *
1145  * Finalize QoS table configuration and initialize it in SDK. It can be done
1146  * only after port is started, so we have a valid ppio reference.
1147  *
1148  * @param priv Port's private (configuration) data.
1149  * @returns 0 in case of success, exits otherwise.
1150  */
1151 int
1152 mrvl_start_qos_mapping(struct mrvl_priv *priv)
1153 {
1154         size_t i;
1155
1156         if (priv->qos_tbl_params.type == PP2_CLS_QOS_TBL_NONE)
1157                 return 0;
1158
1159         if (priv->ppio == NULL) {
1160                 MRVL_LOG(ERR, "ppio must not be NULL here!");
1161                 return -1;
1162         }
1163
1164         for (i = 0; i < RTE_DIM(priv->qos_tbl_params.pcp_cos_map); ++i)
1165                 priv->qos_tbl_params.pcp_cos_map[i].ppio = priv->ppio;
1166
1167         for (i = 0; i < RTE_DIM(priv->qos_tbl_params.dscp_cos_map); ++i)
1168                 priv->qos_tbl_params.dscp_cos_map[i].ppio = priv->ppio;
1169
1170         /* Initialize Classifier QoS table. */
1171
1172         return pp2_cls_qos_tbl_init(&priv->qos_tbl_params, &priv->qos_tbl);
1173 }