1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (c) 2016 Mellanox Technologies. All rights reserved. |
4 | * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> |
5 | */ |
6 | |
7 | #include "devl_internal.h" |
8 | |
9 | static struct devlink_dpipe_field devlink_dpipe_fields_ethernet[] = { |
10 | { |
11 | .name = "destination mac" , |
12 | .id = DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC, |
13 | .bitwidth = 48, |
14 | }, |
15 | }; |
16 | |
17 | struct devlink_dpipe_header = { |
18 | .name = "ethernet" , |
19 | .id = DEVLINK_DPIPE_HEADER_ETHERNET, |
20 | .fields = devlink_dpipe_fields_ethernet, |
21 | .fields_count = ARRAY_SIZE(devlink_dpipe_fields_ethernet), |
22 | .global = true, |
23 | }; |
24 | EXPORT_SYMBOL_GPL(devlink_dpipe_header_ethernet); |
25 | |
26 | static struct devlink_dpipe_field devlink_dpipe_fields_ipv4[] = { |
27 | { |
28 | .name = "destination ip" , |
29 | .id = DEVLINK_DPIPE_FIELD_IPV4_DST_IP, |
30 | .bitwidth = 32, |
31 | }, |
32 | }; |
33 | |
34 | struct devlink_dpipe_header = { |
35 | .name = "ipv4" , |
36 | .id = DEVLINK_DPIPE_HEADER_IPV4, |
37 | .fields = devlink_dpipe_fields_ipv4, |
38 | .fields_count = ARRAY_SIZE(devlink_dpipe_fields_ipv4), |
39 | .global = true, |
40 | }; |
41 | EXPORT_SYMBOL_GPL(devlink_dpipe_header_ipv4); |
42 | |
43 | static struct devlink_dpipe_field devlink_dpipe_fields_ipv6[] = { |
44 | { |
45 | .name = "destination ip" , |
46 | .id = DEVLINK_DPIPE_FIELD_IPV6_DST_IP, |
47 | .bitwidth = 128, |
48 | }, |
49 | }; |
50 | |
51 | struct devlink_dpipe_header = { |
52 | .name = "ipv6" , |
53 | .id = DEVLINK_DPIPE_HEADER_IPV6, |
54 | .fields = devlink_dpipe_fields_ipv6, |
55 | .fields_count = ARRAY_SIZE(devlink_dpipe_fields_ipv6), |
56 | .global = true, |
57 | }; |
58 | EXPORT_SYMBOL_GPL(devlink_dpipe_header_ipv6); |
59 | |
60 | int devlink_dpipe_match_put(struct sk_buff *skb, |
61 | struct devlink_dpipe_match *match) |
62 | { |
63 | struct devlink_dpipe_header * = match->header; |
64 | struct devlink_dpipe_field *field = &header->fields[match->field_id]; |
65 | struct nlattr *match_attr; |
66 | |
67 | match_attr = nla_nest_start_noflag(skb, attrtype: DEVLINK_ATTR_DPIPE_MATCH); |
68 | if (!match_attr) |
69 | return -EMSGSIZE; |
70 | |
71 | if (nla_put_u32(skb, attrtype: DEVLINK_ATTR_DPIPE_MATCH_TYPE, value: match->type) || |
72 | nla_put_u32(skb, attrtype: DEVLINK_ATTR_DPIPE_HEADER_INDEX, value: match->header_index) || |
73 | nla_put_u32(skb, attrtype: DEVLINK_ATTR_DPIPE_HEADER_ID, value: header->id) || |
74 | nla_put_u32(skb, attrtype: DEVLINK_ATTR_DPIPE_FIELD_ID, value: field->id) || |
75 | nla_put_u8(skb, attrtype: DEVLINK_ATTR_DPIPE_HEADER_GLOBAL, value: header->global)) |
76 | goto nla_put_failure; |
77 | |
78 | nla_nest_end(skb, start: match_attr); |
79 | return 0; |
80 | |
81 | nla_put_failure: |
82 | nla_nest_cancel(skb, start: match_attr); |
83 | return -EMSGSIZE; |
84 | } |
85 | EXPORT_SYMBOL_GPL(devlink_dpipe_match_put); |
86 | |
87 | static int devlink_dpipe_matches_put(struct devlink_dpipe_table *table, |
88 | struct sk_buff *skb) |
89 | { |
90 | struct nlattr *matches_attr; |
91 | |
92 | matches_attr = nla_nest_start_noflag(skb, |
93 | attrtype: DEVLINK_ATTR_DPIPE_TABLE_MATCHES); |
94 | if (!matches_attr) |
95 | return -EMSGSIZE; |
96 | |
97 | if (table->table_ops->matches_dump(table->priv, skb)) |
98 | goto nla_put_failure; |
99 | |
100 | nla_nest_end(skb, start: matches_attr); |
101 | return 0; |
102 | |
103 | nla_put_failure: |
104 | nla_nest_cancel(skb, start: matches_attr); |
105 | return -EMSGSIZE; |
106 | } |
107 | |
108 | int devlink_dpipe_action_put(struct sk_buff *skb, |
109 | struct devlink_dpipe_action *action) |
110 | { |
111 | struct devlink_dpipe_header * = action->header; |
112 | struct devlink_dpipe_field *field = &header->fields[action->field_id]; |
113 | struct nlattr *action_attr; |
114 | |
115 | action_attr = nla_nest_start_noflag(skb, attrtype: DEVLINK_ATTR_DPIPE_ACTION); |
116 | if (!action_attr) |
117 | return -EMSGSIZE; |
118 | |
119 | if (nla_put_u32(skb, attrtype: DEVLINK_ATTR_DPIPE_ACTION_TYPE, value: action->type) || |
120 | nla_put_u32(skb, attrtype: DEVLINK_ATTR_DPIPE_HEADER_INDEX, value: action->header_index) || |
121 | nla_put_u32(skb, attrtype: DEVLINK_ATTR_DPIPE_HEADER_ID, value: header->id) || |
122 | nla_put_u32(skb, attrtype: DEVLINK_ATTR_DPIPE_FIELD_ID, value: field->id) || |
123 | nla_put_u8(skb, attrtype: DEVLINK_ATTR_DPIPE_HEADER_GLOBAL, value: header->global)) |
124 | goto nla_put_failure; |
125 | |
126 | nla_nest_end(skb, start: action_attr); |
127 | return 0; |
128 | |
129 | nla_put_failure: |
130 | nla_nest_cancel(skb, start: action_attr); |
131 | return -EMSGSIZE; |
132 | } |
133 | EXPORT_SYMBOL_GPL(devlink_dpipe_action_put); |
134 | |
135 | static int devlink_dpipe_actions_put(struct devlink_dpipe_table *table, |
136 | struct sk_buff *skb) |
137 | { |
138 | struct nlattr *actions_attr; |
139 | |
140 | actions_attr = nla_nest_start_noflag(skb, |
141 | attrtype: DEVLINK_ATTR_DPIPE_TABLE_ACTIONS); |
142 | if (!actions_attr) |
143 | return -EMSGSIZE; |
144 | |
145 | if (table->table_ops->actions_dump(table->priv, skb)) |
146 | goto nla_put_failure; |
147 | |
148 | nla_nest_end(skb, start: actions_attr); |
149 | return 0; |
150 | |
151 | nla_put_failure: |
152 | nla_nest_cancel(skb, start: actions_attr); |
153 | return -EMSGSIZE; |
154 | } |
155 | |
156 | static int devlink_dpipe_table_put(struct sk_buff *skb, |
157 | struct devlink_dpipe_table *table) |
158 | { |
159 | struct nlattr *table_attr; |
160 | u64 table_size; |
161 | |
162 | table_size = table->table_ops->size_get(table->priv); |
163 | table_attr = nla_nest_start_noflag(skb, attrtype: DEVLINK_ATTR_DPIPE_TABLE); |
164 | if (!table_attr) |
165 | return -EMSGSIZE; |
166 | |
167 | if (nla_put_string(skb, attrtype: DEVLINK_ATTR_DPIPE_TABLE_NAME, str: table->name) || |
168 | nla_put_u64_64bit(skb, attrtype: DEVLINK_ATTR_DPIPE_TABLE_SIZE, value: table_size, |
169 | padattr: DEVLINK_ATTR_PAD)) |
170 | goto nla_put_failure; |
171 | if (nla_put_u8(skb, attrtype: DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED, |
172 | value: table->counters_enabled)) |
173 | goto nla_put_failure; |
174 | |
175 | if (table->resource_valid) { |
176 | if (nla_put_u64_64bit(skb, attrtype: DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID, |
177 | value: table->resource_id, padattr: DEVLINK_ATTR_PAD) || |
178 | nla_put_u64_64bit(skb, attrtype: DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_UNITS, |
179 | value: table->resource_units, padattr: DEVLINK_ATTR_PAD)) |
180 | goto nla_put_failure; |
181 | } |
182 | if (devlink_dpipe_matches_put(table, skb)) |
183 | goto nla_put_failure; |
184 | |
185 | if (devlink_dpipe_actions_put(table, skb)) |
186 | goto nla_put_failure; |
187 | |
188 | nla_nest_end(skb, start: table_attr); |
189 | return 0; |
190 | |
191 | nla_put_failure: |
192 | nla_nest_cancel(skb, start: table_attr); |
193 | return -EMSGSIZE; |
194 | } |
195 | |
196 | static int devlink_dpipe_send_and_alloc_skb(struct sk_buff **pskb, |
197 | struct genl_info *info) |
198 | { |
199 | int err; |
200 | |
201 | if (*pskb) { |
202 | err = genlmsg_reply(skb: *pskb, info); |
203 | if (err) |
204 | return err; |
205 | } |
206 | *pskb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); |
207 | if (!*pskb) |
208 | return -ENOMEM; |
209 | return 0; |
210 | } |
211 | |
212 | static int devlink_dpipe_tables_fill(struct genl_info *info, |
213 | enum devlink_command cmd, int flags, |
214 | struct list_head *dpipe_tables, |
215 | const char *table_name) |
216 | { |
217 | struct devlink *devlink = info->user_ptr[0]; |
218 | struct devlink_dpipe_table *table; |
219 | struct nlattr *tables_attr; |
220 | struct sk_buff *skb = NULL; |
221 | struct nlmsghdr *nlh; |
222 | bool incomplete; |
223 | void *hdr; |
224 | int i; |
225 | int err; |
226 | |
227 | table = list_first_entry(dpipe_tables, |
228 | struct devlink_dpipe_table, list); |
229 | start_again: |
230 | err = devlink_dpipe_send_and_alloc_skb(pskb: &skb, info); |
231 | if (err) |
232 | return err; |
233 | |
234 | hdr = genlmsg_put(skb, portid: info->snd_portid, seq: info->snd_seq, |
235 | family: &devlink_nl_family, NLM_F_MULTI, cmd); |
236 | if (!hdr) { |
237 | nlmsg_free(skb); |
238 | return -EMSGSIZE; |
239 | } |
240 | |
241 | if (devlink_nl_put_handle(msg: skb, devlink)) |
242 | goto nla_put_failure; |
243 | tables_attr = nla_nest_start_noflag(skb, attrtype: DEVLINK_ATTR_DPIPE_TABLES); |
244 | if (!tables_attr) |
245 | goto nla_put_failure; |
246 | |
247 | i = 0; |
248 | incomplete = false; |
249 | list_for_each_entry_from(table, dpipe_tables, list) { |
250 | if (!table_name) { |
251 | err = devlink_dpipe_table_put(skb, table); |
252 | if (err) { |
253 | if (!i) |
254 | goto err_table_put; |
255 | incomplete = true; |
256 | break; |
257 | } |
258 | } else { |
259 | if (!strcmp(table->name, table_name)) { |
260 | err = devlink_dpipe_table_put(skb, table); |
261 | if (err) |
262 | break; |
263 | } |
264 | } |
265 | i++; |
266 | } |
267 | |
268 | nla_nest_end(skb, start: tables_attr); |
269 | genlmsg_end(skb, hdr); |
270 | if (incomplete) |
271 | goto start_again; |
272 | |
273 | send_done: |
274 | nlh = nlmsg_put(skb, portid: info->snd_portid, seq: info->snd_seq, |
275 | NLMSG_DONE, payload: 0, flags: flags | NLM_F_MULTI); |
276 | if (!nlh) { |
277 | err = devlink_dpipe_send_and_alloc_skb(pskb: &skb, info); |
278 | if (err) |
279 | return err; |
280 | goto send_done; |
281 | } |
282 | |
283 | return genlmsg_reply(skb, info); |
284 | |
285 | nla_put_failure: |
286 | err = -EMSGSIZE; |
287 | err_table_put: |
288 | nlmsg_free(skb); |
289 | return err; |
290 | } |
291 | |
292 | int devlink_nl_dpipe_table_get_doit(struct sk_buff *skb, struct genl_info *info) |
293 | { |
294 | struct devlink *devlink = info->user_ptr[0]; |
295 | const char *table_name = NULL; |
296 | |
297 | if (info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]) |
298 | table_name = nla_data(nla: info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]); |
299 | |
300 | return devlink_dpipe_tables_fill(info, cmd: DEVLINK_CMD_DPIPE_TABLE_GET, flags: 0, |
301 | dpipe_tables: &devlink->dpipe_table_list, |
302 | table_name); |
303 | } |
304 | |
305 | static int devlink_dpipe_value_put(struct sk_buff *skb, |
306 | struct devlink_dpipe_value *value) |
307 | { |
308 | if (nla_put(skb, attrtype: DEVLINK_ATTR_DPIPE_VALUE, |
309 | attrlen: value->value_size, data: value->value)) |
310 | return -EMSGSIZE; |
311 | if (value->mask) |
312 | if (nla_put(skb, attrtype: DEVLINK_ATTR_DPIPE_VALUE_MASK, |
313 | attrlen: value->value_size, data: value->mask)) |
314 | return -EMSGSIZE; |
315 | if (value->mapping_valid) |
316 | if (nla_put_u32(skb, attrtype: DEVLINK_ATTR_DPIPE_VALUE_MAPPING, |
317 | value: value->mapping_value)) |
318 | return -EMSGSIZE; |
319 | return 0; |
320 | } |
321 | |
322 | static int devlink_dpipe_action_value_put(struct sk_buff *skb, |
323 | struct devlink_dpipe_value *value) |
324 | { |
325 | if (!value->action) |
326 | return -EINVAL; |
327 | if (devlink_dpipe_action_put(skb, value->action)) |
328 | return -EMSGSIZE; |
329 | if (devlink_dpipe_value_put(skb, value)) |
330 | return -EMSGSIZE; |
331 | return 0; |
332 | } |
333 | |
334 | static int devlink_dpipe_action_values_put(struct sk_buff *skb, |
335 | struct devlink_dpipe_value *values, |
336 | unsigned int values_count) |
337 | { |
338 | struct nlattr *action_attr; |
339 | int i; |
340 | int err; |
341 | |
342 | for (i = 0; i < values_count; i++) { |
343 | action_attr = nla_nest_start_noflag(skb, |
344 | attrtype: DEVLINK_ATTR_DPIPE_ACTION_VALUE); |
345 | if (!action_attr) |
346 | return -EMSGSIZE; |
347 | err = devlink_dpipe_action_value_put(skb, value: &values[i]); |
348 | if (err) |
349 | goto err_action_value_put; |
350 | nla_nest_end(skb, start: action_attr); |
351 | } |
352 | return 0; |
353 | |
354 | err_action_value_put: |
355 | nla_nest_cancel(skb, start: action_attr); |
356 | return err; |
357 | } |
358 | |
359 | static int devlink_dpipe_match_value_put(struct sk_buff *skb, |
360 | struct devlink_dpipe_value *value) |
361 | { |
362 | if (!value->match) |
363 | return -EINVAL; |
364 | if (devlink_dpipe_match_put(skb, value->match)) |
365 | return -EMSGSIZE; |
366 | if (devlink_dpipe_value_put(skb, value)) |
367 | return -EMSGSIZE; |
368 | return 0; |
369 | } |
370 | |
371 | static int devlink_dpipe_match_values_put(struct sk_buff *skb, |
372 | struct devlink_dpipe_value *values, |
373 | unsigned int values_count) |
374 | { |
375 | struct nlattr *match_attr; |
376 | int i; |
377 | int err; |
378 | |
379 | for (i = 0; i < values_count; i++) { |
380 | match_attr = nla_nest_start_noflag(skb, |
381 | attrtype: DEVLINK_ATTR_DPIPE_MATCH_VALUE); |
382 | if (!match_attr) |
383 | return -EMSGSIZE; |
384 | err = devlink_dpipe_match_value_put(skb, value: &values[i]); |
385 | if (err) |
386 | goto err_match_value_put; |
387 | nla_nest_end(skb, start: match_attr); |
388 | } |
389 | return 0; |
390 | |
391 | err_match_value_put: |
392 | nla_nest_cancel(skb, start: match_attr); |
393 | return err; |
394 | } |
395 | |
396 | static int devlink_dpipe_entry_put(struct sk_buff *skb, |
397 | struct devlink_dpipe_entry *entry) |
398 | { |
399 | struct nlattr *entry_attr, *matches_attr, *actions_attr; |
400 | int err; |
401 | |
402 | entry_attr = nla_nest_start_noflag(skb, attrtype: DEVLINK_ATTR_DPIPE_ENTRY); |
403 | if (!entry_attr) |
404 | return -EMSGSIZE; |
405 | |
406 | if (nla_put_u64_64bit(skb, attrtype: DEVLINK_ATTR_DPIPE_ENTRY_INDEX, value: entry->index, |
407 | padattr: DEVLINK_ATTR_PAD)) |
408 | goto nla_put_failure; |
409 | if (entry->counter_valid) |
410 | if (nla_put_u64_64bit(skb, attrtype: DEVLINK_ATTR_DPIPE_ENTRY_COUNTER, |
411 | value: entry->counter, padattr: DEVLINK_ATTR_PAD)) |
412 | goto nla_put_failure; |
413 | |
414 | matches_attr = nla_nest_start_noflag(skb, |
415 | attrtype: DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES); |
416 | if (!matches_attr) |
417 | goto nla_put_failure; |
418 | |
419 | err = devlink_dpipe_match_values_put(skb, values: entry->match_values, |
420 | values_count: entry->match_values_count); |
421 | if (err) { |
422 | nla_nest_cancel(skb, start: matches_attr); |
423 | goto err_match_values_put; |
424 | } |
425 | nla_nest_end(skb, start: matches_attr); |
426 | |
427 | actions_attr = nla_nest_start_noflag(skb, |
428 | attrtype: DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES); |
429 | if (!actions_attr) |
430 | goto nla_put_failure; |
431 | |
432 | err = devlink_dpipe_action_values_put(skb, values: entry->action_values, |
433 | values_count: entry->action_values_count); |
434 | if (err) { |
435 | nla_nest_cancel(skb, start: actions_attr); |
436 | goto err_action_values_put; |
437 | } |
438 | nla_nest_end(skb, start: actions_attr); |
439 | |
440 | nla_nest_end(skb, start: entry_attr); |
441 | return 0; |
442 | |
443 | nla_put_failure: |
444 | err = -EMSGSIZE; |
445 | err_match_values_put: |
446 | err_action_values_put: |
447 | nla_nest_cancel(skb, start: entry_attr); |
448 | return err; |
449 | } |
450 | |
451 | static struct devlink_dpipe_table * |
452 | devlink_dpipe_table_find(struct list_head *dpipe_tables, |
453 | const char *table_name, struct devlink *devlink) |
454 | { |
455 | struct devlink_dpipe_table *table; |
456 | |
457 | list_for_each_entry_rcu(table, dpipe_tables, list, |
458 | lockdep_is_held(&devlink->lock)) { |
459 | if (!strcmp(table->name, table_name)) |
460 | return table; |
461 | } |
462 | return NULL; |
463 | } |
464 | |
465 | int devlink_dpipe_entry_ctx_prepare(struct devlink_dpipe_dump_ctx *dump_ctx) |
466 | { |
467 | struct devlink *devlink; |
468 | int err; |
469 | |
470 | err = devlink_dpipe_send_and_alloc_skb(pskb: &dump_ctx->skb, |
471 | info: dump_ctx->info); |
472 | if (err) |
473 | return err; |
474 | |
475 | dump_ctx->hdr = genlmsg_put(skb: dump_ctx->skb, |
476 | portid: dump_ctx->info->snd_portid, |
477 | seq: dump_ctx->info->snd_seq, |
478 | family: &devlink_nl_family, NLM_F_MULTI, |
479 | cmd: dump_ctx->cmd); |
480 | if (!dump_ctx->hdr) |
481 | goto nla_put_failure; |
482 | |
483 | devlink = dump_ctx->info->user_ptr[0]; |
484 | if (devlink_nl_put_handle(msg: dump_ctx->skb, devlink)) |
485 | goto nla_put_failure; |
486 | dump_ctx->nest = nla_nest_start_noflag(skb: dump_ctx->skb, |
487 | attrtype: DEVLINK_ATTR_DPIPE_ENTRIES); |
488 | if (!dump_ctx->nest) |
489 | goto nla_put_failure; |
490 | return 0; |
491 | |
492 | nla_put_failure: |
493 | nlmsg_free(skb: dump_ctx->skb); |
494 | return -EMSGSIZE; |
495 | } |
496 | EXPORT_SYMBOL_GPL(devlink_dpipe_entry_ctx_prepare); |
497 | |
498 | int devlink_dpipe_entry_ctx_append(struct devlink_dpipe_dump_ctx *dump_ctx, |
499 | struct devlink_dpipe_entry *entry) |
500 | { |
501 | return devlink_dpipe_entry_put(skb: dump_ctx->skb, entry); |
502 | } |
503 | EXPORT_SYMBOL_GPL(devlink_dpipe_entry_ctx_append); |
504 | |
505 | int devlink_dpipe_entry_ctx_close(struct devlink_dpipe_dump_ctx *dump_ctx) |
506 | { |
507 | nla_nest_end(skb: dump_ctx->skb, start: dump_ctx->nest); |
508 | genlmsg_end(skb: dump_ctx->skb, hdr: dump_ctx->hdr); |
509 | return 0; |
510 | } |
511 | EXPORT_SYMBOL_GPL(devlink_dpipe_entry_ctx_close); |
512 | |
513 | void devlink_dpipe_entry_clear(struct devlink_dpipe_entry *entry) |
514 | |
515 | { |
516 | unsigned int value_count, value_index; |
517 | struct devlink_dpipe_value *value; |
518 | |
519 | value = entry->action_values; |
520 | value_count = entry->action_values_count; |
521 | for (value_index = 0; value_index < value_count; value_index++) { |
522 | kfree(objp: value[value_index].value); |
523 | kfree(objp: value[value_index].mask); |
524 | } |
525 | |
526 | value = entry->match_values; |
527 | value_count = entry->match_values_count; |
528 | for (value_index = 0; value_index < value_count; value_index++) { |
529 | kfree(objp: value[value_index].value); |
530 | kfree(objp: value[value_index].mask); |
531 | } |
532 | } |
533 | EXPORT_SYMBOL_GPL(devlink_dpipe_entry_clear); |
534 | |
535 | static int devlink_dpipe_entries_fill(struct genl_info *info, |
536 | enum devlink_command cmd, int flags, |
537 | struct devlink_dpipe_table *table) |
538 | { |
539 | struct devlink_dpipe_dump_ctx dump_ctx; |
540 | struct nlmsghdr *nlh; |
541 | int err; |
542 | |
543 | dump_ctx.skb = NULL; |
544 | dump_ctx.cmd = cmd; |
545 | dump_ctx.info = info; |
546 | |
547 | err = table->table_ops->entries_dump(table->priv, |
548 | table->counters_enabled, |
549 | &dump_ctx); |
550 | if (err) |
551 | return err; |
552 | |
553 | send_done: |
554 | nlh = nlmsg_put(skb: dump_ctx.skb, portid: info->snd_portid, seq: info->snd_seq, |
555 | NLMSG_DONE, payload: 0, flags: flags | NLM_F_MULTI); |
556 | if (!nlh) { |
557 | err = devlink_dpipe_send_and_alloc_skb(pskb: &dump_ctx.skb, info); |
558 | if (err) |
559 | return err; |
560 | goto send_done; |
561 | } |
562 | return genlmsg_reply(skb: dump_ctx.skb, info); |
563 | } |
564 | |
565 | int devlink_nl_dpipe_entries_get_doit(struct sk_buff *skb, |
566 | struct genl_info *info) |
567 | { |
568 | struct devlink *devlink = info->user_ptr[0]; |
569 | struct devlink_dpipe_table *table; |
570 | const char *table_name; |
571 | |
572 | if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_DPIPE_TABLE_NAME)) |
573 | return -EINVAL; |
574 | |
575 | table_name = nla_data(nla: info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]); |
576 | table = devlink_dpipe_table_find(dpipe_tables: &devlink->dpipe_table_list, |
577 | table_name, devlink); |
578 | if (!table) |
579 | return -EINVAL; |
580 | |
581 | if (!table->table_ops->entries_dump) |
582 | return -EINVAL; |
583 | |
584 | return devlink_dpipe_entries_fill(info, cmd: DEVLINK_CMD_DPIPE_ENTRIES_GET, |
585 | flags: 0, table); |
586 | } |
587 | |
588 | static int devlink_dpipe_fields_put(struct sk_buff *skb, |
589 | const struct devlink_dpipe_header *) |
590 | { |
591 | struct devlink_dpipe_field *field; |
592 | struct nlattr *field_attr; |
593 | int i; |
594 | |
595 | for (i = 0; i < header->fields_count; i++) { |
596 | field = &header->fields[i]; |
597 | field_attr = nla_nest_start_noflag(skb, |
598 | attrtype: DEVLINK_ATTR_DPIPE_FIELD); |
599 | if (!field_attr) |
600 | return -EMSGSIZE; |
601 | if (nla_put_string(skb, attrtype: DEVLINK_ATTR_DPIPE_FIELD_NAME, str: field->name) || |
602 | nla_put_u32(skb, attrtype: DEVLINK_ATTR_DPIPE_FIELD_ID, value: field->id) || |
603 | nla_put_u32(skb, attrtype: DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH, value: field->bitwidth) || |
604 | nla_put_u32(skb, attrtype: DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE, value: field->mapping_type)) |
605 | goto nla_put_failure; |
606 | nla_nest_end(skb, start: field_attr); |
607 | } |
608 | return 0; |
609 | |
610 | nla_put_failure: |
611 | nla_nest_cancel(skb, start: field_attr); |
612 | return -EMSGSIZE; |
613 | } |
614 | |
615 | static int (struct sk_buff *skb, |
616 | struct devlink_dpipe_header *) |
617 | { |
618 | struct nlattr *fields_attr, *; |
619 | int err; |
620 | |
621 | header_attr = nla_nest_start_noflag(skb, attrtype: DEVLINK_ATTR_DPIPE_HEADER); |
622 | if (!header_attr) |
623 | return -EMSGSIZE; |
624 | |
625 | if (nla_put_string(skb, attrtype: DEVLINK_ATTR_DPIPE_HEADER_NAME, str: header->name) || |
626 | nla_put_u32(skb, attrtype: DEVLINK_ATTR_DPIPE_HEADER_ID, value: header->id) || |
627 | nla_put_u8(skb, attrtype: DEVLINK_ATTR_DPIPE_HEADER_GLOBAL, value: header->global)) |
628 | goto nla_put_failure; |
629 | |
630 | fields_attr = nla_nest_start_noflag(skb, |
631 | attrtype: DEVLINK_ATTR_DPIPE_HEADER_FIELDS); |
632 | if (!fields_attr) |
633 | goto nla_put_failure; |
634 | |
635 | err = devlink_dpipe_fields_put(skb, header); |
636 | if (err) { |
637 | nla_nest_cancel(skb, start: fields_attr); |
638 | goto nla_put_failure; |
639 | } |
640 | nla_nest_end(skb, start: fields_attr); |
641 | nla_nest_end(skb, start: header_attr); |
642 | return 0; |
643 | |
644 | nla_put_failure: |
645 | err = -EMSGSIZE; |
646 | nla_nest_cancel(skb, start: header_attr); |
647 | return err; |
648 | } |
649 | |
650 | static int (struct genl_info *info, |
651 | enum devlink_command cmd, int flags, |
652 | struct devlink_dpipe_headers * |
653 | ) |
654 | { |
655 | struct devlink *devlink = info->user_ptr[0]; |
656 | struct nlattr *; |
657 | struct sk_buff *skb = NULL; |
658 | struct nlmsghdr *nlh; |
659 | void *hdr; |
660 | int i, j; |
661 | int err; |
662 | |
663 | i = 0; |
664 | start_again: |
665 | err = devlink_dpipe_send_and_alloc_skb(pskb: &skb, info); |
666 | if (err) |
667 | return err; |
668 | |
669 | hdr = genlmsg_put(skb, portid: info->snd_portid, seq: info->snd_seq, |
670 | family: &devlink_nl_family, NLM_F_MULTI, cmd); |
671 | if (!hdr) { |
672 | nlmsg_free(skb); |
673 | return -EMSGSIZE; |
674 | } |
675 | |
676 | if (devlink_nl_put_handle(msg: skb, devlink)) |
677 | goto nla_put_failure; |
678 | headers_attr = nla_nest_start_noflag(skb, attrtype: DEVLINK_ATTR_DPIPE_HEADERS); |
679 | if (!headers_attr) |
680 | goto nla_put_failure; |
681 | |
682 | j = 0; |
683 | for (; i < dpipe_headers->headers_count; i++) { |
684 | err = devlink_dpipe_header_put(skb, header: dpipe_headers->headers[i]); |
685 | if (err) { |
686 | if (!j) |
687 | goto err_table_put; |
688 | break; |
689 | } |
690 | j++; |
691 | } |
692 | nla_nest_end(skb, start: headers_attr); |
693 | genlmsg_end(skb, hdr); |
694 | if (i != dpipe_headers->headers_count) |
695 | goto start_again; |
696 | |
697 | send_done: |
698 | nlh = nlmsg_put(skb, portid: info->snd_portid, seq: info->snd_seq, |
699 | NLMSG_DONE, payload: 0, flags: flags | NLM_F_MULTI); |
700 | if (!nlh) { |
701 | err = devlink_dpipe_send_and_alloc_skb(pskb: &skb, info); |
702 | if (err) |
703 | return err; |
704 | goto send_done; |
705 | } |
706 | return genlmsg_reply(skb, info); |
707 | |
708 | nla_put_failure: |
709 | err = -EMSGSIZE; |
710 | err_table_put: |
711 | nlmsg_free(skb); |
712 | return err; |
713 | } |
714 | |
715 | int (struct sk_buff *skb, |
716 | struct genl_info *info) |
717 | { |
718 | struct devlink *devlink = info->user_ptr[0]; |
719 | |
720 | if (!devlink->dpipe_headers) |
721 | return -EOPNOTSUPP; |
722 | return devlink_dpipe_headers_fill(info, cmd: DEVLINK_CMD_DPIPE_HEADERS_GET, |
723 | flags: 0, dpipe_headers: devlink->dpipe_headers); |
724 | } |
725 | |
726 | static int devlink_dpipe_table_counters_set(struct devlink *devlink, |
727 | const char *table_name, |
728 | bool enable) |
729 | { |
730 | struct devlink_dpipe_table *table; |
731 | |
732 | table = devlink_dpipe_table_find(dpipe_tables: &devlink->dpipe_table_list, |
733 | table_name, devlink); |
734 | if (!table) |
735 | return -EINVAL; |
736 | |
737 | if (table->counter_control_extern) |
738 | return -EOPNOTSUPP; |
739 | |
740 | if (!(table->counters_enabled ^ enable)) |
741 | return 0; |
742 | |
743 | table->counters_enabled = enable; |
744 | if (table->table_ops->counters_set_update) |
745 | table->table_ops->counters_set_update(table->priv, enable); |
746 | return 0; |
747 | } |
748 | |
749 | int devlink_nl_dpipe_table_counters_set_doit(struct sk_buff *skb, |
750 | struct genl_info *info) |
751 | { |
752 | struct devlink *devlink = info->user_ptr[0]; |
753 | const char *table_name; |
754 | bool counters_enable; |
755 | |
756 | if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_DPIPE_TABLE_NAME) || |
757 | GENL_REQ_ATTR_CHECK(info, |
758 | DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED)) |
759 | return -EINVAL; |
760 | |
761 | table_name = nla_data(nla: info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]); |
762 | counters_enable = !!nla_get_u8(nla: info->attrs[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED]); |
763 | |
764 | return devlink_dpipe_table_counters_set(devlink, table_name, |
765 | enable: counters_enable); |
766 | } |
767 | |
768 | /** |
769 | * devl_dpipe_headers_register - register dpipe headers |
770 | * |
771 | * @devlink: devlink |
772 | * @dpipe_headers: dpipe header array |
773 | * |
774 | * Register the headers supported by hardware. |
775 | */ |
776 | void (struct devlink *devlink, |
777 | struct devlink_dpipe_headers *) |
778 | { |
779 | lockdep_assert_held(&devlink->lock); |
780 | |
781 | devlink->dpipe_headers = dpipe_headers; |
782 | } |
783 | EXPORT_SYMBOL_GPL(devl_dpipe_headers_register); |
784 | |
785 | /** |
786 | * devl_dpipe_headers_unregister - unregister dpipe headers |
787 | * |
788 | * @devlink: devlink |
789 | * |
790 | * Unregister the headers supported by hardware. |
791 | */ |
792 | void (struct devlink *devlink) |
793 | { |
794 | lockdep_assert_held(&devlink->lock); |
795 | |
796 | devlink->dpipe_headers = NULL; |
797 | } |
798 | EXPORT_SYMBOL_GPL(devl_dpipe_headers_unregister); |
799 | |
800 | /** |
801 | * devlink_dpipe_table_counter_enabled - check if counter allocation |
802 | * required |
803 | * @devlink: devlink |
804 | * @table_name: tables name |
805 | * |
806 | * Used by driver to check if counter allocation is required. |
807 | * After counter allocation is turned on the table entries |
808 | * are updated to include counter statistics. |
809 | * |
810 | * After that point on the driver must respect the counter |
811 | * state so that each entry added to the table is added |
812 | * with a counter. |
813 | */ |
814 | bool devlink_dpipe_table_counter_enabled(struct devlink *devlink, |
815 | const char *table_name) |
816 | { |
817 | struct devlink_dpipe_table *table; |
818 | bool enabled; |
819 | |
820 | rcu_read_lock(); |
821 | table = devlink_dpipe_table_find(dpipe_tables: &devlink->dpipe_table_list, |
822 | table_name, devlink); |
823 | enabled = false; |
824 | if (table) |
825 | enabled = table->counters_enabled; |
826 | rcu_read_unlock(); |
827 | return enabled; |
828 | } |
829 | EXPORT_SYMBOL_GPL(devlink_dpipe_table_counter_enabled); |
830 | |
831 | /** |
832 | * devl_dpipe_table_register - register dpipe table |
833 | * |
834 | * @devlink: devlink |
835 | * @table_name: table name |
836 | * @table_ops: table ops |
837 | * @priv: priv |
838 | * @counter_control_extern: external control for counters |
839 | */ |
840 | int devl_dpipe_table_register(struct devlink *devlink, |
841 | const char *table_name, |
842 | struct devlink_dpipe_table_ops *table_ops, |
843 | void *priv, bool counter_control_extern) |
844 | { |
845 | struct devlink_dpipe_table *table; |
846 | |
847 | lockdep_assert_held(&devlink->lock); |
848 | |
849 | if (WARN_ON(!table_ops->size_get)) |
850 | return -EINVAL; |
851 | |
852 | if (devlink_dpipe_table_find(dpipe_tables: &devlink->dpipe_table_list, table_name, |
853 | devlink)) |
854 | return -EEXIST; |
855 | |
856 | table = kzalloc(size: sizeof(*table), GFP_KERNEL); |
857 | if (!table) |
858 | return -ENOMEM; |
859 | |
860 | table->name = table_name; |
861 | table->table_ops = table_ops; |
862 | table->priv = priv; |
863 | table->counter_control_extern = counter_control_extern; |
864 | |
865 | list_add_tail_rcu(new: &table->list, head: &devlink->dpipe_table_list); |
866 | |
867 | return 0; |
868 | } |
869 | EXPORT_SYMBOL_GPL(devl_dpipe_table_register); |
870 | |
871 | /** |
872 | * devl_dpipe_table_unregister - unregister dpipe table |
873 | * |
874 | * @devlink: devlink |
875 | * @table_name: table name |
876 | */ |
877 | void devl_dpipe_table_unregister(struct devlink *devlink, |
878 | const char *table_name) |
879 | { |
880 | struct devlink_dpipe_table *table; |
881 | |
882 | lockdep_assert_held(&devlink->lock); |
883 | |
884 | table = devlink_dpipe_table_find(dpipe_tables: &devlink->dpipe_table_list, |
885 | table_name, devlink); |
886 | if (!table) |
887 | return; |
888 | list_del_rcu(entry: &table->list); |
889 | kfree_rcu(table, rcu); |
890 | } |
891 | EXPORT_SYMBOL_GPL(devl_dpipe_table_unregister); |
892 | |
893 | /** |
894 | * devl_dpipe_table_resource_set - set the resource id |
895 | * |
896 | * @devlink: devlink |
897 | * @table_name: table name |
898 | * @resource_id: resource id |
899 | * @resource_units: number of resource's units consumed per table's entry |
900 | */ |
901 | int devl_dpipe_table_resource_set(struct devlink *devlink, |
902 | const char *table_name, u64 resource_id, |
903 | u64 resource_units) |
904 | { |
905 | struct devlink_dpipe_table *table; |
906 | |
907 | table = devlink_dpipe_table_find(dpipe_tables: &devlink->dpipe_table_list, |
908 | table_name, devlink); |
909 | if (!table) |
910 | return -EINVAL; |
911 | |
912 | table->resource_id = resource_id; |
913 | table->resource_units = resource_units; |
914 | table->resource_valid = true; |
915 | return 0; |
916 | } |
917 | EXPORT_SYMBOL_GPL(devl_dpipe_table_resource_set); |
918 | |