162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2016 Mellanox Technologies. All rights reserved. 462306a36Sopenharmony_ci * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "devl_internal.h" 862306a36Sopenharmony_ci 962306a36Sopenharmony_cistatic struct devlink_dpipe_field devlink_dpipe_fields_ethernet[] = { 1062306a36Sopenharmony_ci { 1162306a36Sopenharmony_ci .name = "destination mac", 1262306a36Sopenharmony_ci .id = DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC, 1362306a36Sopenharmony_ci .bitwidth = 48, 1462306a36Sopenharmony_ci }, 1562306a36Sopenharmony_ci}; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct devlink_dpipe_header devlink_dpipe_header_ethernet = { 1862306a36Sopenharmony_ci .name = "ethernet", 1962306a36Sopenharmony_ci .id = DEVLINK_DPIPE_HEADER_ETHERNET, 2062306a36Sopenharmony_ci .fields = devlink_dpipe_fields_ethernet, 2162306a36Sopenharmony_ci .fields_count = ARRAY_SIZE(devlink_dpipe_fields_ethernet), 2262306a36Sopenharmony_ci .global = true, 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_dpipe_header_ethernet); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic struct devlink_dpipe_field devlink_dpipe_fields_ipv4[] = { 2762306a36Sopenharmony_ci { 2862306a36Sopenharmony_ci .name = "destination ip", 2962306a36Sopenharmony_ci .id = DEVLINK_DPIPE_FIELD_IPV4_DST_IP, 3062306a36Sopenharmony_ci .bitwidth = 32, 3162306a36Sopenharmony_ci }, 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistruct devlink_dpipe_header devlink_dpipe_header_ipv4 = { 3562306a36Sopenharmony_ci .name = "ipv4", 3662306a36Sopenharmony_ci .id = DEVLINK_DPIPE_HEADER_IPV4, 3762306a36Sopenharmony_ci .fields = devlink_dpipe_fields_ipv4, 3862306a36Sopenharmony_ci .fields_count = ARRAY_SIZE(devlink_dpipe_fields_ipv4), 3962306a36Sopenharmony_ci .global = true, 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_dpipe_header_ipv4); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic struct devlink_dpipe_field devlink_dpipe_fields_ipv6[] = { 4462306a36Sopenharmony_ci { 4562306a36Sopenharmony_ci .name = "destination ip", 4662306a36Sopenharmony_ci .id = DEVLINK_DPIPE_FIELD_IPV6_DST_IP, 4762306a36Sopenharmony_ci .bitwidth = 128, 4862306a36Sopenharmony_ci }, 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistruct devlink_dpipe_header devlink_dpipe_header_ipv6 = { 5262306a36Sopenharmony_ci .name = "ipv6", 5362306a36Sopenharmony_ci .id = DEVLINK_DPIPE_HEADER_IPV6, 5462306a36Sopenharmony_ci .fields = devlink_dpipe_fields_ipv6, 5562306a36Sopenharmony_ci .fields_count = ARRAY_SIZE(devlink_dpipe_fields_ipv6), 5662306a36Sopenharmony_ci .global = true, 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_dpipe_header_ipv6); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ciint devlink_dpipe_match_put(struct sk_buff *skb, 6162306a36Sopenharmony_ci struct devlink_dpipe_match *match) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci struct devlink_dpipe_header *header = match->header; 6462306a36Sopenharmony_ci struct devlink_dpipe_field *field = &header->fields[match->field_id]; 6562306a36Sopenharmony_ci struct nlattr *match_attr; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci match_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_DPIPE_MATCH); 6862306a36Sopenharmony_ci if (!match_attr) 6962306a36Sopenharmony_ci return -EMSGSIZE; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (nla_put_u32(skb, DEVLINK_ATTR_DPIPE_MATCH_TYPE, match->type) || 7262306a36Sopenharmony_ci nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_INDEX, match->header_index) || 7362306a36Sopenharmony_ci nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_ID, header->id) || 7462306a36Sopenharmony_ci nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_ID, field->id) || 7562306a36Sopenharmony_ci nla_put_u8(skb, DEVLINK_ATTR_DPIPE_HEADER_GLOBAL, header->global)) 7662306a36Sopenharmony_ci goto nla_put_failure; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci nla_nest_end(skb, match_attr); 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cinla_put_failure: 8262306a36Sopenharmony_ci nla_nest_cancel(skb, match_attr); 8362306a36Sopenharmony_ci return -EMSGSIZE; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_dpipe_match_put); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int devlink_dpipe_matches_put(struct devlink_dpipe_table *table, 8862306a36Sopenharmony_ci struct sk_buff *skb) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct nlattr *matches_attr; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci matches_attr = nla_nest_start_noflag(skb, 9362306a36Sopenharmony_ci DEVLINK_ATTR_DPIPE_TABLE_MATCHES); 9462306a36Sopenharmony_ci if (!matches_attr) 9562306a36Sopenharmony_ci return -EMSGSIZE; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (table->table_ops->matches_dump(table->priv, skb)) 9862306a36Sopenharmony_ci goto nla_put_failure; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci nla_nest_end(skb, matches_attr); 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cinla_put_failure: 10462306a36Sopenharmony_ci nla_nest_cancel(skb, matches_attr); 10562306a36Sopenharmony_ci return -EMSGSIZE; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ciint devlink_dpipe_action_put(struct sk_buff *skb, 10962306a36Sopenharmony_ci struct devlink_dpipe_action *action) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct devlink_dpipe_header *header = action->header; 11262306a36Sopenharmony_ci struct devlink_dpipe_field *field = &header->fields[action->field_id]; 11362306a36Sopenharmony_ci struct nlattr *action_attr; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci action_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_DPIPE_ACTION); 11662306a36Sopenharmony_ci if (!action_attr) 11762306a36Sopenharmony_ci return -EMSGSIZE; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (nla_put_u32(skb, DEVLINK_ATTR_DPIPE_ACTION_TYPE, action->type) || 12062306a36Sopenharmony_ci nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_INDEX, action->header_index) || 12162306a36Sopenharmony_ci nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_ID, header->id) || 12262306a36Sopenharmony_ci nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_ID, field->id) || 12362306a36Sopenharmony_ci nla_put_u8(skb, DEVLINK_ATTR_DPIPE_HEADER_GLOBAL, header->global)) 12462306a36Sopenharmony_ci goto nla_put_failure; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci nla_nest_end(skb, action_attr); 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cinla_put_failure: 13062306a36Sopenharmony_ci nla_nest_cancel(skb, action_attr); 13162306a36Sopenharmony_ci return -EMSGSIZE; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_dpipe_action_put); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic int devlink_dpipe_actions_put(struct devlink_dpipe_table *table, 13662306a36Sopenharmony_ci struct sk_buff *skb) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct nlattr *actions_attr; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci actions_attr = nla_nest_start_noflag(skb, 14162306a36Sopenharmony_ci DEVLINK_ATTR_DPIPE_TABLE_ACTIONS); 14262306a36Sopenharmony_ci if (!actions_attr) 14362306a36Sopenharmony_ci return -EMSGSIZE; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (table->table_ops->actions_dump(table->priv, skb)) 14662306a36Sopenharmony_ci goto nla_put_failure; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci nla_nest_end(skb, actions_attr); 14962306a36Sopenharmony_ci return 0; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cinla_put_failure: 15262306a36Sopenharmony_ci nla_nest_cancel(skb, actions_attr); 15362306a36Sopenharmony_ci return -EMSGSIZE; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic int devlink_dpipe_table_put(struct sk_buff *skb, 15762306a36Sopenharmony_ci struct devlink_dpipe_table *table) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct nlattr *table_attr; 16062306a36Sopenharmony_ci u64 table_size; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci table_size = table->table_ops->size_get(table->priv); 16362306a36Sopenharmony_ci table_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_DPIPE_TABLE); 16462306a36Sopenharmony_ci if (!table_attr) 16562306a36Sopenharmony_ci return -EMSGSIZE; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (nla_put_string(skb, DEVLINK_ATTR_DPIPE_TABLE_NAME, table->name) || 16862306a36Sopenharmony_ci nla_put_u64_64bit(skb, DEVLINK_ATTR_DPIPE_TABLE_SIZE, table_size, 16962306a36Sopenharmony_ci DEVLINK_ATTR_PAD)) 17062306a36Sopenharmony_ci goto nla_put_failure; 17162306a36Sopenharmony_ci if (nla_put_u8(skb, DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED, 17262306a36Sopenharmony_ci table->counters_enabled)) 17362306a36Sopenharmony_ci goto nla_put_failure; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (table->resource_valid) { 17662306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID, 17762306a36Sopenharmony_ci table->resource_id, DEVLINK_ATTR_PAD) || 17862306a36Sopenharmony_ci nla_put_u64_64bit(skb, DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_UNITS, 17962306a36Sopenharmony_ci table->resource_units, DEVLINK_ATTR_PAD)) 18062306a36Sopenharmony_ci goto nla_put_failure; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci if (devlink_dpipe_matches_put(table, skb)) 18362306a36Sopenharmony_ci goto nla_put_failure; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (devlink_dpipe_actions_put(table, skb)) 18662306a36Sopenharmony_ci goto nla_put_failure; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci nla_nest_end(skb, table_attr); 18962306a36Sopenharmony_ci return 0; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cinla_put_failure: 19262306a36Sopenharmony_ci nla_nest_cancel(skb, table_attr); 19362306a36Sopenharmony_ci return -EMSGSIZE; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic int devlink_dpipe_send_and_alloc_skb(struct sk_buff **pskb, 19762306a36Sopenharmony_ci struct genl_info *info) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci int err; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (*pskb) { 20262306a36Sopenharmony_ci err = genlmsg_reply(*pskb, info); 20362306a36Sopenharmony_ci if (err) 20462306a36Sopenharmony_ci return err; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci *pskb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); 20762306a36Sopenharmony_ci if (!*pskb) 20862306a36Sopenharmony_ci return -ENOMEM; 20962306a36Sopenharmony_ci return 0; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic int devlink_dpipe_tables_fill(struct genl_info *info, 21362306a36Sopenharmony_ci enum devlink_command cmd, int flags, 21462306a36Sopenharmony_ci struct list_head *dpipe_tables, 21562306a36Sopenharmony_ci const char *table_name) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct devlink *devlink = info->user_ptr[0]; 21862306a36Sopenharmony_ci struct devlink_dpipe_table *table; 21962306a36Sopenharmony_ci struct nlattr *tables_attr; 22062306a36Sopenharmony_ci struct sk_buff *skb = NULL; 22162306a36Sopenharmony_ci struct nlmsghdr *nlh; 22262306a36Sopenharmony_ci bool incomplete; 22362306a36Sopenharmony_ci void *hdr; 22462306a36Sopenharmony_ci int i; 22562306a36Sopenharmony_ci int err; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci table = list_first_entry(dpipe_tables, 22862306a36Sopenharmony_ci struct devlink_dpipe_table, list); 22962306a36Sopenharmony_cistart_again: 23062306a36Sopenharmony_ci err = devlink_dpipe_send_and_alloc_skb(&skb, info); 23162306a36Sopenharmony_ci if (err) 23262306a36Sopenharmony_ci return err; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq, 23562306a36Sopenharmony_ci &devlink_nl_family, NLM_F_MULTI, cmd); 23662306a36Sopenharmony_ci if (!hdr) { 23762306a36Sopenharmony_ci nlmsg_free(skb); 23862306a36Sopenharmony_ci return -EMSGSIZE; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (devlink_nl_put_handle(skb, devlink)) 24262306a36Sopenharmony_ci goto nla_put_failure; 24362306a36Sopenharmony_ci tables_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_DPIPE_TABLES); 24462306a36Sopenharmony_ci if (!tables_attr) 24562306a36Sopenharmony_ci goto nla_put_failure; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci i = 0; 24862306a36Sopenharmony_ci incomplete = false; 24962306a36Sopenharmony_ci list_for_each_entry_from(table, dpipe_tables, list) { 25062306a36Sopenharmony_ci if (!table_name) { 25162306a36Sopenharmony_ci err = devlink_dpipe_table_put(skb, table); 25262306a36Sopenharmony_ci if (err) { 25362306a36Sopenharmony_ci if (!i) 25462306a36Sopenharmony_ci goto err_table_put; 25562306a36Sopenharmony_ci incomplete = true; 25662306a36Sopenharmony_ci break; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci } else { 25962306a36Sopenharmony_ci if (!strcmp(table->name, table_name)) { 26062306a36Sopenharmony_ci err = devlink_dpipe_table_put(skb, table); 26162306a36Sopenharmony_ci if (err) 26262306a36Sopenharmony_ci break; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci i++; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci nla_nest_end(skb, tables_attr); 26962306a36Sopenharmony_ci genlmsg_end(skb, hdr); 27062306a36Sopenharmony_ci if (incomplete) 27162306a36Sopenharmony_ci goto start_again; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cisend_done: 27462306a36Sopenharmony_ci nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq, 27562306a36Sopenharmony_ci NLMSG_DONE, 0, flags | NLM_F_MULTI); 27662306a36Sopenharmony_ci if (!nlh) { 27762306a36Sopenharmony_ci err = devlink_dpipe_send_and_alloc_skb(&skb, info); 27862306a36Sopenharmony_ci if (err) 27962306a36Sopenharmony_ci return err; 28062306a36Sopenharmony_ci goto send_done; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci return genlmsg_reply(skb, info); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cinla_put_failure: 28662306a36Sopenharmony_ci err = -EMSGSIZE; 28762306a36Sopenharmony_cierr_table_put: 28862306a36Sopenharmony_ci nlmsg_free(skb); 28962306a36Sopenharmony_ci return err; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ciint devlink_nl_cmd_dpipe_table_get(struct sk_buff *skb, struct genl_info *info) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct devlink *devlink = info->user_ptr[0]; 29562306a36Sopenharmony_ci const char *table_name = NULL; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]) 29862306a36Sopenharmony_ci table_name = nla_data(info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci return devlink_dpipe_tables_fill(info, DEVLINK_CMD_DPIPE_TABLE_GET, 0, 30162306a36Sopenharmony_ci &devlink->dpipe_table_list, 30262306a36Sopenharmony_ci table_name); 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic int devlink_dpipe_value_put(struct sk_buff *skb, 30662306a36Sopenharmony_ci struct devlink_dpipe_value *value) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci if (nla_put(skb, DEVLINK_ATTR_DPIPE_VALUE, 30962306a36Sopenharmony_ci value->value_size, value->value)) 31062306a36Sopenharmony_ci return -EMSGSIZE; 31162306a36Sopenharmony_ci if (value->mask) 31262306a36Sopenharmony_ci if (nla_put(skb, DEVLINK_ATTR_DPIPE_VALUE_MASK, 31362306a36Sopenharmony_ci value->value_size, value->mask)) 31462306a36Sopenharmony_ci return -EMSGSIZE; 31562306a36Sopenharmony_ci if (value->mapping_valid) 31662306a36Sopenharmony_ci if (nla_put_u32(skb, DEVLINK_ATTR_DPIPE_VALUE_MAPPING, 31762306a36Sopenharmony_ci value->mapping_value)) 31862306a36Sopenharmony_ci return -EMSGSIZE; 31962306a36Sopenharmony_ci return 0; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic int devlink_dpipe_action_value_put(struct sk_buff *skb, 32362306a36Sopenharmony_ci struct devlink_dpipe_value *value) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci if (!value->action) 32662306a36Sopenharmony_ci return -EINVAL; 32762306a36Sopenharmony_ci if (devlink_dpipe_action_put(skb, value->action)) 32862306a36Sopenharmony_ci return -EMSGSIZE; 32962306a36Sopenharmony_ci if (devlink_dpipe_value_put(skb, value)) 33062306a36Sopenharmony_ci return -EMSGSIZE; 33162306a36Sopenharmony_ci return 0; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic int devlink_dpipe_action_values_put(struct sk_buff *skb, 33562306a36Sopenharmony_ci struct devlink_dpipe_value *values, 33662306a36Sopenharmony_ci unsigned int values_count) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct nlattr *action_attr; 33962306a36Sopenharmony_ci int i; 34062306a36Sopenharmony_ci int err; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci for (i = 0; i < values_count; i++) { 34362306a36Sopenharmony_ci action_attr = nla_nest_start_noflag(skb, 34462306a36Sopenharmony_ci DEVLINK_ATTR_DPIPE_ACTION_VALUE); 34562306a36Sopenharmony_ci if (!action_attr) 34662306a36Sopenharmony_ci return -EMSGSIZE; 34762306a36Sopenharmony_ci err = devlink_dpipe_action_value_put(skb, &values[i]); 34862306a36Sopenharmony_ci if (err) 34962306a36Sopenharmony_ci goto err_action_value_put; 35062306a36Sopenharmony_ci nla_nest_end(skb, action_attr); 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci return 0; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cierr_action_value_put: 35562306a36Sopenharmony_ci nla_nest_cancel(skb, action_attr); 35662306a36Sopenharmony_ci return err; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic int devlink_dpipe_match_value_put(struct sk_buff *skb, 36062306a36Sopenharmony_ci struct devlink_dpipe_value *value) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci if (!value->match) 36362306a36Sopenharmony_ci return -EINVAL; 36462306a36Sopenharmony_ci if (devlink_dpipe_match_put(skb, value->match)) 36562306a36Sopenharmony_ci return -EMSGSIZE; 36662306a36Sopenharmony_ci if (devlink_dpipe_value_put(skb, value)) 36762306a36Sopenharmony_ci return -EMSGSIZE; 36862306a36Sopenharmony_ci return 0; 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cistatic int devlink_dpipe_match_values_put(struct sk_buff *skb, 37262306a36Sopenharmony_ci struct devlink_dpipe_value *values, 37362306a36Sopenharmony_ci unsigned int values_count) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct nlattr *match_attr; 37662306a36Sopenharmony_ci int i; 37762306a36Sopenharmony_ci int err; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci for (i = 0; i < values_count; i++) { 38062306a36Sopenharmony_ci match_attr = nla_nest_start_noflag(skb, 38162306a36Sopenharmony_ci DEVLINK_ATTR_DPIPE_MATCH_VALUE); 38262306a36Sopenharmony_ci if (!match_attr) 38362306a36Sopenharmony_ci return -EMSGSIZE; 38462306a36Sopenharmony_ci err = devlink_dpipe_match_value_put(skb, &values[i]); 38562306a36Sopenharmony_ci if (err) 38662306a36Sopenharmony_ci goto err_match_value_put; 38762306a36Sopenharmony_ci nla_nest_end(skb, match_attr); 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci return 0; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cierr_match_value_put: 39262306a36Sopenharmony_ci nla_nest_cancel(skb, match_attr); 39362306a36Sopenharmony_ci return err; 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic int devlink_dpipe_entry_put(struct sk_buff *skb, 39762306a36Sopenharmony_ci struct devlink_dpipe_entry *entry) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci struct nlattr *entry_attr, *matches_attr, *actions_attr; 40062306a36Sopenharmony_ci int err; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci entry_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_DPIPE_ENTRY); 40362306a36Sopenharmony_ci if (!entry_attr) 40462306a36Sopenharmony_ci return -EMSGSIZE; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, DEVLINK_ATTR_DPIPE_ENTRY_INDEX, entry->index, 40762306a36Sopenharmony_ci DEVLINK_ATTR_PAD)) 40862306a36Sopenharmony_ci goto nla_put_failure; 40962306a36Sopenharmony_ci if (entry->counter_valid) 41062306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, DEVLINK_ATTR_DPIPE_ENTRY_COUNTER, 41162306a36Sopenharmony_ci entry->counter, DEVLINK_ATTR_PAD)) 41262306a36Sopenharmony_ci goto nla_put_failure; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci matches_attr = nla_nest_start_noflag(skb, 41562306a36Sopenharmony_ci DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES); 41662306a36Sopenharmony_ci if (!matches_attr) 41762306a36Sopenharmony_ci goto nla_put_failure; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci err = devlink_dpipe_match_values_put(skb, entry->match_values, 42062306a36Sopenharmony_ci entry->match_values_count); 42162306a36Sopenharmony_ci if (err) { 42262306a36Sopenharmony_ci nla_nest_cancel(skb, matches_attr); 42362306a36Sopenharmony_ci goto err_match_values_put; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci nla_nest_end(skb, matches_attr); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci actions_attr = nla_nest_start_noflag(skb, 42862306a36Sopenharmony_ci DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES); 42962306a36Sopenharmony_ci if (!actions_attr) 43062306a36Sopenharmony_ci goto nla_put_failure; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci err = devlink_dpipe_action_values_put(skb, entry->action_values, 43362306a36Sopenharmony_ci entry->action_values_count); 43462306a36Sopenharmony_ci if (err) { 43562306a36Sopenharmony_ci nla_nest_cancel(skb, actions_attr); 43662306a36Sopenharmony_ci goto err_action_values_put; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci nla_nest_end(skb, actions_attr); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci nla_nest_end(skb, entry_attr); 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cinla_put_failure: 44462306a36Sopenharmony_ci err = -EMSGSIZE; 44562306a36Sopenharmony_cierr_match_values_put: 44662306a36Sopenharmony_cierr_action_values_put: 44762306a36Sopenharmony_ci nla_nest_cancel(skb, entry_attr); 44862306a36Sopenharmony_ci return err; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic struct devlink_dpipe_table * 45262306a36Sopenharmony_cidevlink_dpipe_table_find(struct list_head *dpipe_tables, 45362306a36Sopenharmony_ci const char *table_name, struct devlink *devlink) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci struct devlink_dpipe_table *table; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci list_for_each_entry_rcu(table, dpipe_tables, list, 45862306a36Sopenharmony_ci lockdep_is_held(&devlink->lock)) { 45962306a36Sopenharmony_ci if (!strcmp(table->name, table_name)) 46062306a36Sopenharmony_ci return table; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci return NULL; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ciint devlink_dpipe_entry_ctx_prepare(struct devlink_dpipe_dump_ctx *dump_ctx) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci struct devlink *devlink; 46862306a36Sopenharmony_ci int err; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci err = devlink_dpipe_send_and_alloc_skb(&dump_ctx->skb, 47162306a36Sopenharmony_ci dump_ctx->info); 47262306a36Sopenharmony_ci if (err) 47362306a36Sopenharmony_ci return err; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci dump_ctx->hdr = genlmsg_put(dump_ctx->skb, 47662306a36Sopenharmony_ci dump_ctx->info->snd_portid, 47762306a36Sopenharmony_ci dump_ctx->info->snd_seq, 47862306a36Sopenharmony_ci &devlink_nl_family, NLM_F_MULTI, 47962306a36Sopenharmony_ci dump_ctx->cmd); 48062306a36Sopenharmony_ci if (!dump_ctx->hdr) 48162306a36Sopenharmony_ci goto nla_put_failure; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci devlink = dump_ctx->info->user_ptr[0]; 48462306a36Sopenharmony_ci if (devlink_nl_put_handle(dump_ctx->skb, devlink)) 48562306a36Sopenharmony_ci goto nla_put_failure; 48662306a36Sopenharmony_ci dump_ctx->nest = nla_nest_start_noflag(dump_ctx->skb, 48762306a36Sopenharmony_ci DEVLINK_ATTR_DPIPE_ENTRIES); 48862306a36Sopenharmony_ci if (!dump_ctx->nest) 48962306a36Sopenharmony_ci goto nla_put_failure; 49062306a36Sopenharmony_ci return 0; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cinla_put_failure: 49362306a36Sopenharmony_ci nlmsg_free(dump_ctx->skb); 49462306a36Sopenharmony_ci return -EMSGSIZE; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_dpipe_entry_ctx_prepare); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ciint devlink_dpipe_entry_ctx_append(struct devlink_dpipe_dump_ctx *dump_ctx, 49962306a36Sopenharmony_ci struct devlink_dpipe_entry *entry) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci return devlink_dpipe_entry_put(dump_ctx->skb, entry); 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_dpipe_entry_ctx_append); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ciint devlink_dpipe_entry_ctx_close(struct devlink_dpipe_dump_ctx *dump_ctx) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci nla_nest_end(dump_ctx->skb, dump_ctx->nest); 50862306a36Sopenharmony_ci genlmsg_end(dump_ctx->skb, dump_ctx->hdr); 50962306a36Sopenharmony_ci return 0; 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_dpipe_entry_ctx_close); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_civoid devlink_dpipe_entry_clear(struct devlink_dpipe_entry *entry) 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci unsigned int value_count, value_index; 51762306a36Sopenharmony_ci struct devlink_dpipe_value *value; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci value = entry->action_values; 52062306a36Sopenharmony_ci value_count = entry->action_values_count; 52162306a36Sopenharmony_ci for (value_index = 0; value_index < value_count; value_index++) { 52262306a36Sopenharmony_ci kfree(value[value_index].value); 52362306a36Sopenharmony_ci kfree(value[value_index].mask); 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci value = entry->match_values; 52762306a36Sopenharmony_ci value_count = entry->match_values_count; 52862306a36Sopenharmony_ci for (value_index = 0; value_index < value_count; value_index++) { 52962306a36Sopenharmony_ci kfree(value[value_index].value); 53062306a36Sopenharmony_ci kfree(value[value_index].mask); 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_dpipe_entry_clear); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic int devlink_dpipe_entries_fill(struct genl_info *info, 53662306a36Sopenharmony_ci enum devlink_command cmd, int flags, 53762306a36Sopenharmony_ci struct devlink_dpipe_table *table) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci struct devlink_dpipe_dump_ctx dump_ctx; 54062306a36Sopenharmony_ci struct nlmsghdr *nlh; 54162306a36Sopenharmony_ci int err; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci dump_ctx.skb = NULL; 54462306a36Sopenharmony_ci dump_ctx.cmd = cmd; 54562306a36Sopenharmony_ci dump_ctx.info = info; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci err = table->table_ops->entries_dump(table->priv, 54862306a36Sopenharmony_ci table->counters_enabled, 54962306a36Sopenharmony_ci &dump_ctx); 55062306a36Sopenharmony_ci if (err) 55162306a36Sopenharmony_ci return err; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cisend_done: 55462306a36Sopenharmony_ci nlh = nlmsg_put(dump_ctx.skb, info->snd_portid, info->snd_seq, 55562306a36Sopenharmony_ci NLMSG_DONE, 0, flags | NLM_F_MULTI); 55662306a36Sopenharmony_ci if (!nlh) { 55762306a36Sopenharmony_ci err = devlink_dpipe_send_and_alloc_skb(&dump_ctx.skb, info); 55862306a36Sopenharmony_ci if (err) 55962306a36Sopenharmony_ci return err; 56062306a36Sopenharmony_ci goto send_done; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci return genlmsg_reply(dump_ctx.skb, info); 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ciint devlink_nl_cmd_dpipe_entries_get(struct sk_buff *skb, 56662306a36Sopenharmony_ci struct genl_info *info) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci struct devlink *devlink = info->user_ptr[0]; 56962306a36Sopenharmony_ci struct devlink_dpipe_table *table; 57062306a36Sopenharmony_ci const char *table_name; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_DPIPE_TABLE_NAME)) 57362306a36Sopenharmony_ci return -EINVAL; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci table_name = nla_data(info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]); 57662306a36Sopenharmony_ci table = devlink_dpipe_table_find(&devlink->dpipe_table_list, 57762306a36Sopenharmony_ci table_name, devlink); 57862306a36Sopenharmony_ci if (!table) 57962306a36Sopenharmony_ci return -EINVAL; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (!table->table_ops->entries_dump) 58262306a36Sopenharmony_ci return -EINVAL; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci return devlink_dpipe_entries_fill(info, DEVLINK_CMD_DPIPE_ENTRIES_GET, 58562306a36Sopenharmony_ci 0, table); 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic int devlink_dpipe_fields_put(struct sk_buff *skb, 58962306a36Sopenharmony_ci const struct devlink_dpipe_header *header) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci struct devlink_dpipe_field *field; 59262306a36Sopenharmony_ci struct nlattr *field_attr; 59362306a36Sopenharmony_ci int i; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci for (i = 0; i < header->fields_count; i++) { 59662306a36Sopenharmony_ci field = &header->fields[i]; 59762306a36Sopenharmony_ci field_attr = nla_nest_start_noflag(skb, 59862306a36Sopenharmony_ci DEVLINK_ATTR_DPIPE_FIELD); 59962306a36Sopenharmony_ci if (!field_attr) 60062306a36Sopenharmony_ci return -EMSGSIZE; 60162306a36Sopenharmony_ci if (nla_put_string(skb, DEVLINK_ATTR_DPIPE_FIELD_NAME, field->name) || 60262306a36Sopenharmony_ci nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_ID, field->id) || 60362306a36Sopenharmony_ci nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH, field->bitwidth) || 60462306a36Sopenharmony_ci nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE, field->mapping_type)) 60562306a36Sopenharmony_ci goto nla_put_failure; 60662306a36Sopenharmony_ci nla_nest_end(skb, field_attr); 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci return 0; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cinla_put_failure: 61162306a36Sopenharmony_ci nla_nest_cancel(skb, field_attr); 61262306a36Sopenharmony_ci return -EMSGSIZE; 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic int devlink_dpipe_header_put(struct sk_buff *skb, 61662306a36Sopenharmony_ci struct devlink_dpipe_header *header) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci struct nlattr *fields_attr, *header_attr; 61962306a36Sopenharmony_ci int err; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci header_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_DPIPE_HEADER); 62262306a36Sopenharmony_ci if (!header_attr) 62362306a36Sopenharmony_ci return -EMSGSIZE; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (nla_put_string(skb, DEVLINK_ATTR_DPIPE_HEADER_NAME, header->name) || 62662306a36Sopenharmony_ci nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_ID, header->id) || 62762306a36Sopenharmony_ci nla_put_u8(skb, DEVLINK_ATTR_DPIPE_HEADER_GLOBAL, header->global)) 62862306a36Sopenharmony_ci goto nla_put_failure; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci fields_attr = nla_nest_start_noflag(skb, 63162306a36Sopenharmony_ci DEVLINK_ATTR_DPIPE_HEADER_FIELDS); 63262306a36Sopenharmony_ci if (!fields_attr) 63362306a36Sopenharmony_ci goto nla_put_failure; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci err = devlink_dpipe_fields_put(skb, header); 63662306a36Sopenharmony_ci if (err) { 63762306a36Sopenharmony_ci nla_nest_cancel(skb, fields_attr); 63862306a36Sopenharmony_ci goto nla_put_failure; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci nla_nest_end(skb, fields_attr); 64162306a36Sopenharmony_ci nla_nest_end(skb, header_attr); 64262306a36Sopenharmony_ci return 0; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cinla_put_failure: 64562306a36Sopenharmony_ci err = -EMSGSIZE; 64662306a36Sopenharmony_ci nla_nest_cancel(skb, header_attr); 64762306a36Sopenharmony_ci return err; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic int devlink_dpipe_headers_fill(struct genl_info *info, 65162306a36Sopenharmony_ci enum devlink_command cmd, int flags, 65262306a36Sopenharmony_ci struct devlink_dpipe_headers * 65362306a36Sopenharmony_ci dpipe_headers) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci struct devlink *devlink = info->user_ptr[0]; 65662306a36Sopenharmony_ci struct nlattr *headers_attr; 65762306a36Sopenharmony_ci struct sk_buff *skb = NULL; 65862306a36Sopenharmony_ci struct nlmsghdr *nlh; 65962306a36Sopenharmony_ci void *hdr; 66062306a36Sopenharmony_ci int i, j; 66162306a36Sopenharmony_ci int err; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci i = 0; 66462306a36Sopenharmony_cistart_again: 66562306a36Sopenharmony_ci err = devlink_dpipe_send_and_alloc_skb(&skb, info); 66662306a36Sopenharmony_ci if (err) 66762306a36Sopenharmony_ci return err; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq, 67062306a36Sopenharmony_ci &devlink_nl_family, NLM_F_MULTI, cmd); 67162306a36Sopenharmony_ci if (!hdr) { 67262306a36Sopenharmony_ci nlmsg_free(skb); 67362306a36Sopenharmony_ci return -EMSGSIZE; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if (devlink_nl_put_handle(skb, devlink)) 67762306a36Sopenharmony_ci goto nla_put_failure; 67862306a36Sopenharmony_ci headers_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_DPIPE_HEADERS); 67962306a36Sopenharmony_ci if (!headers_attr) 68062306a36Sopenharmony_ci goto nla_put_failure; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci j = 0; 68362306a36Sopenharmony_ci for (; i < dpipe_headers->headers_count; i++) { 68462306a36Sopenharmony_ci err = devlink_dpipe_header_put(skb, dpipe_headers->headers[i]); 68562306a36Sopenharmony_ci if (err) { 68662306a36Sopenharmony_ci if (!j) 68762306a36Sopenharmony_ci goto err_table_put; 68862306a36Sopenharmony_ci break; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci j++; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci nla_nest_end(skb, headers_attr); 69362306a36Sopenharmony_ci genlmsg_end(skb, hdr); 69462306a36Sopenharmony_ci if (i != dpipe_headers->headers_count) 69562306a36Sopenharmony_ci goto start_again; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cisend_done: 69862306a36Sopenharmony_ci nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq, 69962306a36Sopenharmony_ci NLMSG_DONE, 0, flags | NLM_F_MULTI); 70062306a36Sopenharmony_ci if (!nlh) { 70162306a36Sopenharmony_ci err = devlink_dpipe_send_and_alloc_skb(&skb, info); 70262306a36Sopenharmony_ci if (err) 70362306a36Sopenharmony_ci return err; 70462306a36Sopenharmony_ci goto send_done; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci return genlmsg_reply(skb, info); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cinla_put_failure: 70962306a36Sopenharmony_ci err = -EMSGSIZE; 71062306a36Sopenharmony_cierr_table_put: 71162306a36Sopenharmony_ci nlmsg_free(skb); 71262306a36Sopenharmony_ci return err; 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ciint devlink_nl_cmd_dpipe_headers_get(struct sk_buff *skb, 71662306a36Sopenharmony_ci struct genl_info *info) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci struct devlink *devlink = info->user_ptr[0]; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (!devlink->dpipe_headers) 72162306a36Sopenharmony_ci return -EOPNOTSUPP; 72262306a36Sopenharmony_ci return devlink_dpipe_headers_fill(info, DEVLINK_CMD_DPIPE_HEADERS_GET, 72362306a36Sopenharmony_ci 0, devlink->dpipe_headers); 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic int devlink_dpipe_table_counters_set(struct devlink *devlink, 72762306a36Sopenharmony_ci const char *table_name, 72862306a36Sopenharmony_ci bool enable) 72962306a36Sopenharmony_ci{ 73062306a36Sopenharmony_ci struct devlink_dpipe_table *table; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci table = devlink_dpipe_table_find(&devlink->dpipe_table_list, 73362306a36Sopenharmony_ci table_name, devlink); 73462306a36Sopenharmony_ci if (!table) 73562306a36Sopenharmony_ci return -EINVAL; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci if (table->counter_control_extern) 73862306a36Sopenharmony_ci return -EOPNOTSUPP; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (!(table->counters_enabled ^ enable)) 74162306a36Sopenharmony_ci return 0; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci table->counters_enabled = enable; 74462306a36Sopenharmony_ci if (table->table_ops->counters_set_update) 74562306a36Sopenharmony_ci table->table_ops->counters_set_update(table->priv, enable); 74662306a36Sopenharmony_ci return 0; 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ciint devlink_nl_cmd_dpipe_table_counters_set(struct sk_buff *skb, 75062306a36Sopenharmony_ci struct genl_info *info) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci struct devlink *devlink = info->user_ptr[0]; 75362306a36Sopenharmony_ci const char *table_name; 75462306a36Sopenharmony_ci bool counters_enable; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_DPIPE_TABLE_NAME) || 75762306a36Sopenharmony_ci GENL_REQ_ATTR_CHECK(info, 75862306a36Sopenharmony_ci DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED)) 75962306a36Sopenharmony_ci return -EINVAL; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci table_name = nla_data(info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]); 76262306a36Sopenharmony_ci counters_enable = !!nla_get_u8(info->attrs[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED]); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci return devlink_dpipe_table_counters_set(devlink, table_name, 76562306a36Sopenharmony_ci counters_enable); 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci/** 76962306a36Sopenharmony_ci * devl_dpipe_headers_register - register dpipe headers 77062306a36Sopenharmony_ci * 77162306a36Sopenharmony_ci * @devlink: devlink 77262306a36Sopenharmony_ci * @dpipe_headers: dpipe header array 77362306a36Sopenharmony_ci * 77462306a36Sopenharmony_ci * Register the headers supported by hardware. 77562306a36Sopenharmony_ci */ 77662306a36Sopenharmony_civoid devl_dpipe_headers_register(struct devlink *devlink, 77762306a36Sopenharmony_ci struct devlink_dpipe_headers *dpipe_headers) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci lockdep_assert_held(&devlink->lock); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci devlink->dpipe_headers = dpipe_headers; 78262306a36Sopenharmony_ci} 78362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devl_dpipe_headers_register); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci/** 78662306a36Sopenharmony_ci * devl_dpipe_headers_unregister - unregister dpipe headers 78762306a36Sopenharmony_ci * 78862306a36Sopenharmony_ci * @devlink: devlink 78962306a36Sopenharmony_ci * 79062306a36Sopenharmony_ci * Unregister the headers supported by hardware. 79162306a36Sopenharmony_ci */ 79262306a36Sopenharmony_civoid devl_dpipe_headers_unregister(struct devlink *devlink) 79362306a36Sopenharmony_ci{ 79462306a36Sopenharmony_ci lockdep_assert_held(&devlink->lock); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci devlink->dpipe_headers = NULL; 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devl_dpipe_headers_unregister); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci/** 80162306a36Sopenharmony_ci * devlink_dpipe_table_counter_enabled - check if counter allocation 80262306a36Sopenharmony_ci * required 80362306a36Sopenharmony_ci * @devlink: devlink 80462306a36Sopenharmony_ci * @table_name: tables name 80562306a36Sopenharmony_ci * 80662306a36Sopenharmony_ci * Used by driver to check if counter allocation is required. 80762306a36Sopenharmony_ci * After counter allocation is turned on the table entries 80862306a36Sopenharmony_ci * are updated to include counter statistics. 80962306a36Sopenharmony_ci * 81062306a36Sopenharmony_ci * After that point on the driver must respect the counter 81162306a36Sopenharmony_ci * state so that each entry added to the table is added 81262306a36Sopenharmony_ci * with a counter. 81362306a36Sopenharmony_ci */ 81462306a36Sopenharmony_cibool devlink_dpipe_table_counter_enabled(struct devlink *devlink, 81562306a36Sopenharmony_ci const char *table_name) 81662306a36Sopenharmony_ci{ 81762306a36Sopenharmony_ci struct devlink_dpipe_table *table; 81862306a36Sopenharmony_ci bool enabled; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci rcu_read_lock(); 82162306a36Sopenharmony_ci table = devlink_dpipe_table_find(&devlink->dpipe_table_list, 82262306a36Sopenharmony_ci table_name, devlink); 82362306a36Sopenharmony_ci enabled = false; 82462306a36Sopenharmony_ci if (table) 82562306a36Sopenharmony_ci enabled = table->counters_enabled; 82662306a36Sopenharmony_ci rcu_read_unlock(); 82762306a36Sopenharmony_ci return enabled; 82862306a36Sopenharmony_ci} 82962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_dpipe_table_counter_enabled); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci/** 83262306a36Sopenharmony_ci * devl_dpipe_table_register - register dpipe table 83362306a36Sopenharmony_ci * 83462306a36Sopenharmony_ci * @devlink: devlink 83562306a36Sopenharmony_ci * @table_name: table name 83662306a36Sopenharmony_ci * @table_ops: table ops 83762306a36Sopenharmony_ci * @priv: priv 83862306a36Sopenharmony_ci * @counter_control_extern: external control for counters 83962306a36Sopenharmony_ci */ 84062306a36Sopenharmony_ciint devl_dpipe_table_register(struct devlink *devlink, 84162306a36Sopenharmony_ci const char *table_name, 84262306a36Sopenharmony_ci struct devlink_dpipe_table_ops *table_ops, 84362306a36Sopenharmony_ci void *priv, bool counter_control_extern) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci struct devlink_dpipe_table *table; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci lockdep_assert_held(&devlink->lock); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (WARN_ON(!table_ops->size_get)) 85062306a36Sopenharmony_ci return -EINVAL; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci if (devlink_dpipe_table_find(&devlink->dpipe_table_list, table_name, 85362306a36Sopenharmony_ci devlink)) 85462306a36Sopenharmony_ci return -EEXIST; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci table = kzalloc(sizeof(*table), GFP_KERNEL); 85762306a36Sopenharmony_ci if (!table) 85862306a36Sopenharmony_ci return -ENOMEM; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci table->name = table_name; 86162306a36Sopenharmony_ci table->table_ops = table_ops; 86262306a36Sopenharmony_ci table->priv = priv; 86362306a36Sopenharmony_ci table->counter_control_extern = counter_control_extern; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci list_add_tail_rcu(&table->list, &devlink->dpipe_table_list); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci return 0; 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devl_dpipe_table_register); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci/** 87262306a36Sopenharmony_ci * devl_dpipe_table_unregister - unregister dpipe table 87362306a36Sopenharmony_ci * 87462306a36Sopenharmony_ci * @devlink: devlink 87562306a36Sopenharmony_ci * @table_name: table name 87662306a36Sopenharmony_ci */ 87762306a36Sopenharmony_civoid devl_dpipe_table_unregister(struct devlink *devlink, 87862306a36Sopenharmony_ci const char *table_name) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci struct devlink_dpipe_table *table; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci lockdep_assert_held(&devlink->lock); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci table = devlink_dpipe_table_find(&devlink->dpipe_table_list, 88562306a36Sopenharmony_ci table_name, devlink); 88662306a36Sopenharmony_ci if (!table) 88762306a36Sopenharmony_ci return; 88862306a36Sopenharmony_ci list_del_rcu(&table->list); 88962306a36Sopenharmony_ci kfree_rcu(table, rcu); 89062306a36Sopenharmony_ci} 89162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devl_dpipe_table_unregister); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci/** 89462306a36Sopenharmony_ci * devl_dpipe_table_resource_set - set the resource id 89562306a36Sopenharmony_ci * 89662306a36Sopenharmony_ci * @devlink: devlink 89762306a36Sopenharmony_ci * @table_name: table name 89862306a36Sopenharmony_ci * @resource_id: resource id 89962306a36Sopenharmony_ci * @resource_units: number of resource's units consumed per table's entry 90062306a36Sopenharmony_ci */ 90162306a36Sopenharmony_ciint devl_dpipe_table_resource_set(struct devlink *devlink, 90262306a36Sopenharmony_ci const char *table_name, u64 resource_id, 90362306a36Sopenharmony_ci u64 resource_units) 90462306a36Sopenharmony_ci{ 90562306a36Sopenharmony_ci struct devlink_dpipe_table *table; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci table = devlink_dpipe_table_find(&devlink->dpipe_table_list, 90862306a36Sopenharmony_ci table_name, devlink); 90962306a36Sopenharmony_ci if (!table) 91062306a36Sopenharmony_ci return -EINVAL; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci table->resource_id = resource_id; 91362306a36Sopenharmony_ci table->resource_units = resource_units; 91462306a36Sopenharmony_ci table->resource_valid = true; 91562306a36Sopenharmony_ci return 0; 91662306a36Sopenharmony_ci} 91762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devl_dpipe_table_resource_set); 918