1623cd13bSJakub Kicinski // SPDX-License-Identifier: GPL-2.0-or-later 2623cd13bSJakub Kicinski /* 3623cd13bSJakub Kicinski * Copyright (c) 2016 Mellanox Technologies. All rights reserved. 4623cd13bSJakub Kicinski * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> 5623cd13bSJakub Kicinski */ 6623cd13bSJakub Kicinski 7623cd13bSJakub Kicinski #include <net/genetlink.h> 807f3af66SJakub Kicinski #include <net/sock.h> 9623cd13bSJakub Kicinski 10623cd13bSJakub Kicinski #include "devl_internal.h" 11623cd13bSJakub Kicinski 12623cd13bSJakub Kicinski static const struct genl_multicast_group devlink_nl_mcgrps[] = { 13623cd13bSJakub Kicinski [DEVLINK_MCGRP_CONFIG] = { .name = DEVLINK_GENL_MCGRP_CONFIG_NAME }, 14623cd13bSJakub Kicinski }; 15623cd13bSJakub Kicinski 16623cd13bSJakub Kicinski static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = { 17623cd13bSJakub Kicinski [DEVLINK_ATTR_UNSPEC] = { .strict_start_type = 18623cd13bSJakub Kicinski DEVLINK_ATTR_TRAP_POLICER_ID }, 19623cd13bSJakub Kicinski [DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING }, 20623cd13bSJakub Kicinski [DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING }, 21623cd13bSJakub Kicinski [DEVLINK_ATTR_PORT_INDEX] = { .type = NLA_U32 }, 22623cd13bSJakub Kicinski [DEVLINK_ATTR_PORT_TYPE] = NLA_POLICY_RANGE(NLA_U16, DEVLINK_PORT_TYPE_AUTO, 23623cd13bSJakub Kicinski DEVLINK_PORT_TYPE_IB), 24623cd13bSJakub Kicinski [DEVLINK_ATTR_PORT_SPLIT_COUNT] = { .type = NLA_U32 }, 25623cd13bSJakub Kicinski [DEVLINK_ATTR_SB_INDEX] = { .type = NLA_U32 }, 26623cd13bSJakub Kicinski [DEVLINK_ATTR_SB_POOL_INDEX] = { .type = NLA_U16 }, 27623cd13bSJakub Kicinski [DEVLINK_ATTR_SB_POOL_TYPE] = { .type = NLA_U8 }, 28623cd13bSJakub Kicinski [DEVLINK_ATTR_SB_POOL_SIZE] = { .type = NLA_U32 }, 29623cd13bSJakub Kicinski [DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE] = { .type = NLA_U8 }, 30623cd13bSJakub Kicinski [DEVLINK_ATTR_SB_THRESHOLD] = { .type = NLA_U32 }, 31623cd13bSJakub Kicinski [DEVLINK_ATTR_SB_TC_INDEX] = { .type = NLA_U16 }, 32623cd13bSJakub Kicinski [DEVLINK_ATTR_ESWITCH_MODE] = NLA_POLICY_RANGE(NLA_U16, DEVLINK_ESWITCH_MODE_LEGACY, 33623cd13bSJakub Kicinski DEVLINK_ESWITCH_MODE_SWITCHDEV), 34623cd13bSJakub Kicinski [DEVLINK_ATTR_ESWITCH_INLINE_MODE] = { .type = NLA_U8 }, 35623cd13bSJakub Kicinski [DEVLINK_ATTR_ESWITCH_ENCAP_MODE] = { .type = NLA_U8 }, 36623cd13bSJakub Kicinski [DEVLINK_ATTR_DPIPE_TABLE_NAME] = { .type = NLA_NUL_STRING }, 37623cd13bSJakub Kicinski [DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED] = { .type = NLA_U8 }, 38623cd13bSJakub Kicinski [DEVLINK_ATTR_RESOURCE_ID] = { .type = NLA_U64}, 39623cd13bSJakub Kicinski [DEVLINK_ATTR_RESOURCE_SIZE] = { .type = NLA_U64}, 40623cd13bSJakub Kicinski [DEVLINK_ATTR_PARAM_NAME] = { .type = NLA_NUL_STRING }, 41623cd13bSJakub Kicinski [DEVLINK_ATTR_PARAM_TYPE] = { .type = NLA_U8 }, 42623cd13bSJakub Kicinski [DEVLINK_ATTR_PARAM_VALUE_CMODE] = { .type = NLA_U8 }, 43623cd13bSJakub Kicinski [DEVLINK_ATTR_REGION_NAME] = { .type = NLA_NUL_STRING }, 44623cd13bSJakub Kicinski [DEVLINK_ATTR_REGION_SNAPSHOT_ID] = { .type = NLA_U32 }, 45623cd13bSJakub Kicinski [DEVLINK_ATTR_REGION_CHUNK_ADDR] = { .type = NLA_U64 }, 46623cd13bSJakub Kicinski [DEVLINK_ATTR_REGION_CHUNK_LEN] = { .type = NLA_U64 }, 47623cd13bSJakub Kicinski [DEVLINK_ATTR_HEALTH_REPORTER_NAME] = { .type = NLA_NUL_STRING }, 48623cd13bSJakub Kicinski [DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] = { .type = NLA_U64 }, 49623cd13bSJakub Kicinski [DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER] = { .type = NLA_U8 }, 50623cd13bSJakub Kicinski [DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME] = { .type = NLA_NUL_STRING }, 51623cd13bSJakub Kicinski [DEVLINK_ATTR_FLASH_UPDATE_COMPONENT] = { .type = NLA_NUL_STRING }, 52623cd13bSJakub Kicinski [DEVLINK_ATTR_FLASH_UPDATE_OVERWRITE_MASK] = 53623cd13bSJakub Kicinski NLA_POLICY_BITFIELD32(DEVLINK_SUPPORTED_FLASH_OVERWRITE_SECTIONS), 54623cd13bSJakub Kicinski [DEVLINK_ATTR_TRAP_NAME] = { .type = NLA_NUL_STRING }, 55623cd13bSJakub Kicinski [DEVLINK_ATTR_TRAP_ACTION] = { .type = NLA_U8 }, 56623cd13bSJakub Kicinski [DEVLINK_ATTR_TRAP_GROUP_NAME] = { .type = NLA_NUL_STRING }, 57623cd13bSJakub Kicinski [DEVLINK_ATTR_NETNS_PID] = { .type = NLA_U32 }, 58623cd13bSJakub Kicinski [DEVLINK_ATTR_NETNS_FD] = { .type = NLA_U32 }, 59623cd13bSJakub Kicinski [DEVLINK_ATTR_NETNS_ID] = { .type = NLA_U32 }, 60623cd13bSJakub Kicinski [DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP] = { .type = NLA_U8 }, 61623cd13bSJakub Kicinski [DEVLINK_ATTR_TRAP_POLICER_ID] = { .type = NLA_U32 }, 62623cd13bSJakub Kicinski [DEVLINK_ATTR_TRAP_POLICER_RATE] = { .type = NLA_U64 }, 63623cd13bSJakub Kicinski [DEVLINK_ATTR_TRAP_POLICER_BURST] = { .type = NLA_U64 }, 64623cd13bSJakub Kicinski [DEVLINK_ATTR_PORT_FUNCTION] = { .type = NLA_NESTED }, 65623cd13bSJakub Kicinski [DEVLINK_ATTR_RELOAD_ACTION] = NLA_POLICY_RANGE(NLA_U8, DEVLINK_RELOAD_ACTION_DRIVER_REINIT, 66623cd13bSJakub Kicinski DEVLINK_RELOAD_ACTION_MAX), 67623cd13bSJakub Kicinski [DEVLINK_ATTR_RELOAD_LIMITS] = NLA_POLICY_BITFIELD32(DEVLINK_RELOAD_LIMITS_VALID_MASK), 68623cd13bSJakub Kicinski [DEVLINK_ATTR_PORT_FLAVOUR] = { .type = NLA_U16 }, 69623cd13bSJakub Kicinski [DEVLINK_ATTR_PORT_PCI_PF_NUMBER] = { .type = NLA_U16 }, 70623cd13bSJakub Kicinski [DEVLINK_ATTR_PORT_PCI_SF_NUMBER] = { .type = NLA_U32 }, 71623cd13bSJakub Kicinski [DEVLINK_ATTR_PORT_CONTROLLER_NUMBER] = { .type = NLA_U32 }, 72623cd13bSJakub Kicinski [DEVLINK_ATTR_RATE_TYPE] = { .type = NLA_U16 }, 73623cd13bSJakub Kicinski [DEVLINK_ATTR_RATE_TX_SHARE] = { .type = NLA_U64 }, 74623cd13bSJakub Kicinski [DEVLINK_ATTR_RATE_TX_MAX] = { .type = NLA_U64 }, 75623cd13bSJakub Kicinski [DEVLINK_ATTR_RATE_NODE_NAME] = { .type = NLA_NUL_STRING }, 76623cd13bSJakub Kicinski [DEVLINK_ATTR_RATE_PARENT_NODE_NAME] = { .type = NLA_NUL_STRING }, 77623cd13bSJakub Kicinski [DEVLINK_ATTR_LINECARD_INDEX] = { .type = NLA_U32 }, 78623cd13bSJakub Kicinski [DEVLINK_ATTR_LINECARD_TYPE] = { .type = NLA_NUL_STRING }, 79623cd13bSJakub Kicinski [DEVLINK_ATTR_SELFTESTS] = { .type = NLA_NESTED }, 80623cd13bSJakub Kicinski [DEVLINK_ATTR_RATE_TX_PRIORITY] = { .type = NLA_U32 }, 81623cd13bSJakub Kicinski [DEVLINK_ATTR_RATE_TX_WEIGHT] = { .type = NLA_U32 }, 82623cd13bSJakub Kicinski [DEVLINK_ATTR_REGION_DIRECT] = { .type = NLA_FLAG }, 83623cd13bSJakub Kicinski }; 84623cd13bSJakub Kicinski 85870c7ad4SJakub Kicinski struct devlink * 86870c7ad4SJakub Kicinski devlink_get_from_attrs_lock(struct net *net, struct nlattr **attrs) 87623cd13bSJakub Kicinski { 88623cd13bSJakub Kicinski struct devlink *devlink; 89623cd13bSJakub Kicinski unsigned long index; 90623cd13bSJakub Kicinski char *busname; 91623cd13bSJakub Kicinski char *devname; 92623cd13bSJakub Kicinski 93623cd13bSJakub Kicinski if (!attrs[DEVLINK_ATTR_BUS_NAME] || !attrs[DEVLINK_ATTR_DEV_NAME]) 94623cd13bSJakub Kicinski return ERR_PTR(-EINVAL); 95623cd13bSJakub Kicinski 96623cd13bSJakub Kicinski busname = nla_data(attrs[DEVLINK_ATTR_BUS_NAME]); 97623cd13bSJakub Kicinski devname = nla_data(attrs[DEVLINK_ATTR_DEV_NAME]); 98623cd13bSJakub Kicinski 99623cd13bSJakub Kicinski devlinks_xa_for_each_registered_get(net, index, devlink) { 100870c7ad4SJakub Kicinski devl_lock(devlink); 101ed539ba6SJakub Kicinski if (devl_is_registered(devlink) && 102ed539ba6SJakub Kicinski strcmp(devlink->dev->bus->name, busname) == 0 && 103623cd13bSJakub Kicinski strcmp(dev_name(devlink->dev), devname) == 0) 104623cd13bSJakub Kicinski return devlink; 105870c7ad4SJakub Kicinski devl_unlock(devlink); 106623cd13bSJakub Kicinski devlink_put(devlink); 107623cd13bSJakub Kicinski } 108623cd13bSJakub Kicinski 109623cd13bSJakub Kicinski return ERR_PTR(-ENODEV); 110623cd13bSJakub Kicinski } 111623cd13bSJakub Kicinski 112623cd13bSJakub Kicinski static int devlink_nl_pre_doit(const struct genl_split_ops *ops, 113623cd13bSJakub Kicinski struct sk_buff *skb, struct genl_info *info) 114623cd13bSJakub Kicinski { 115623cd13bSJakub Kicinski struct devlink_linecard *linecard; 116623cd13bSJakub Kicinski struct devlink_port *devlink_port; 117623cd13bSJakub Kicinski struct devlink *devlink; 118623cd13bSJakub Kicinski int err; 119623cd13bSJakub Kicinski 120870c7ad4SJakub Kicinski devlink = devlink_get_from_attrs_lock(genl_info_net(info), info->attrs); 121623cd13bSJakub Kicinski if (IS_ERR(devlink)) 122623cd13bSJakub Kicinski return PTR_ERR(devlink); 123870c7ad4SJakub Kicinski 124623cd13bSJakub Kicinski info->user_ptr[0] = devlink; 125623cd13bSJakub Kicinski if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_PORT) { 126623cd13bSJakub Kicinski devlink_port = devlink_port_get_from_info(devlink, info); 127623cd13bSJakub Kicinski if (IS_ERR(devlink_port)) { 128623cd13bSJakub Kicinski err = PTR_ERR(devlink_port); 129623cd13bSJakub Kicinski goto unlock; 130623cd13bSJakub Kicinski } 131623cd13bSJakub Kicinski info->user_ptr[1] = devlink_port; 132623cd13bSJakub Kicinski } else if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT) { 133623cd13bSJakub Kicinski devlink_port = devlink_port_get_from_info(devlink, info); 134623cd13bSJakub Kicinski if (!IS_ERR(devlink_port)) 135623cd13bSJakub Kicinski info->user_ptr[1] = devlink_port; 136623cd13bSJakub Kicinski } else if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_RATE) { 137623cd13bSJakub Kicinski struct devlink_rate *devlink_rate; 138623cd13bSJakub Kicinski 139623cd13bSJakub Kicinski devlink_rate = devlink_rate_get_from_info(devlink, info); 140623cd13bSJakub Kicinski if (IS_ERR(devlink_rate)) { 141623cd13bSJakub Kicinski err = PTR_ERR(devlink_rate); 142623cd13bSJakub Kicinski goto unlock; 143623cd13bSJakub Kicinski } 144623cd13bSJakub Kicinski info->user_ptr[1] = devlink_rate; 145623cd13bSJakub Kicinski } else if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_RATE_NODE) { 146623cd13bSJakub Kicinski struct devlink_rate *rate_node; 147623cd13bSJakub Kicinski 148623cd13bSJakub Kicinski rate_node = devlink_rate_node_get_from_info(devlink, info); 149623cd13bSJakub Kicinski if (IS_ERR(rate_node)) { 150623cd13bSJakub Kicinski err = PTR_ERR(rate_node); 151623cd13bSJakub Kicinski goto unlock; 152623cd13bSJakub Kicinski } 153623cd13bSJakub Kicinski info->user_ptr[1] = rate_node; 154623cd13bSJakub Kicinski } else if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_LINECARD) { 155623cd13bSJakub Kicinski linecard = devlink_linecard_get_from_info(devlink, info); 156623cd13bSJakub Kicinski if (IS_ERR(linecard)) { 157623cd13bSJakub Kicinski err = PTR_ERR(linecard); 158623cd13bSJakub Kicinski goto unlock; 159623cd13bSJakub Kicinski } 160623cd13bSJakub Kicinski info->user_ptr[1] = linecard; 161623cd13bSJakub Kicinski } 162623cd13bSJakub Kicinski return 0; 163623cd13bSJakub Kicinski 164623cd13bSJakub Kicinski unlock: 165623cd13bSJakub Kicinski devl_unlock(devlink); 166623cd13bSJakub Kicinski devlink_put(devlink); 167623cd13bSJakub Kicinski return err; 168623cd13bSJakub Kicinski } 169623cd13bSJakub Kicinski 170623cd13bSJakub Kicinski static void devlink_nl_post_doit(const struct genl_split_ops *ops, 171623cd13bSJakub Kicinski struct sk_buff *skb, struct genl_info *info) 172623cd13bSJakub Kicinski { 173623cd13bSJakub Kicinski struct devlink *devlink; 174623cd13bSJakub Kicinski 175623cd13bSJakub Kicinski devlink = info->user_ptr[0]; 176623cd13bSJakub Kicinski devl_unlock(devlink); 177623cd13bSJakub Kicinski devlink_put(devlink); 178623cd13bSJakub Kicinski } 179623cd13bSJakub Kicinski 1808589ba4eSJiri Pirko static const struct devlink_cmd *devl_cmds[] = { 1818589ba4eSJiri Pirko [DEVLINK_CMD_PORT_GET] = &devl_cmd_port_get, 1828589ba4eSJiri Pirko [DEVLINK_CMD_SB_GET] = &devl_cmd_sb_get, 1838589ba4eSJiri Pirko [DEVLINK_CMD_SB_POOL_GET] = &devl_cmd_sb_pool_get, 1848589ba4eSJiri Pirko [DEVLINK_CMD_SB_PORT_POOL_GET] = &devl_cmd_sb_port_pool_get, 1858589ba4eSJiri Pirko [DEVLINK_CMD_SB_TC_POOL_BIND_GET] = &devl_cmd_sb_tc_pool_bind_get, 1868589ba4eSJiri Pirko [DEVLINK_CMD_PARAM_GET] = &devl_cmd_param_get, 1878589ba4eSJiri Pirko [DEVLINK_CMD_REGION_GET] = &devl_cmd_region_get, 1888589ba4eSJiri Pirko [DEVLINK_CMD_HEALTH_REPORTER_GET] = &devl_cmd_health_reporter_get, 1898589ba4eSJiri Pirko [DEVLINK_CMD_TRAP_GET] = &devl_cmd_trap_get, 1908589ba4eSJiri Pirko [DEVLINK_CMD_TRAP_GROUP_GET] = &devl_cmd_trap_group_get, 1918589ba4eSJiri Pirko [DEVLINK_CMD_TRAP_POLICER_GET] = &devl_cmd_trap_policer_get, 1928589ba4eSJiri Pirko [DEVLINK_CMD_RATE_GET] = &devl_cmd_rate_get, 1938589ba4eSJiri Pirko [DEVLINK_CMD_LINECARD_GET] = &devl_cmd_linecard_get, 1948589ba4eSJiri Pirko [DEVLINK_CMD_SELFTESTS_GET] = &devl_cmd_selftests_get, 19507f3af66SJakub Kicinski }; 19607f3af66SJakub Kicinski 197*491a2487SJiri Pirko int devlink_nl_dumpit(struct sk_buff *msg, struct netlink_callback *cb, 198*491a2487SJiri Pirko devlink_nl_dump_one_func_t *dump_one) 19907f3af66SJakub Kicinski { 20007f3af66SJakub Kicinski struct devlink_nl_dump_state *state = devlink_dump_state(cb); 20107f3af66SJakub Kicinski struct devlink *devlink; 20207f3af66SJakub Kicinski int err = 0; 20307f3af66SJakub Kicinski 204543753d9SJiri Pirko while ((devlink = devlinks_xa_find_get(sock_net(msg->sk), 205543753d9SJiri Pirko &state->instance))) { 20607f3af66SJakub Kicinski devl_lock(devlink); 207ed539ba6SJakub Kicinski 208ed539ba6SJakub Kicinski if (devl_is_registered(devlink)) 209*491a2487SJiri Pirko err = dump_one(msg, devlink, cb); 210ed539ba6SJakub Kicinski else 211ed539ba6SJakub Kicinski err = 0; 212ed539ba6SJakub Kicinski 21307f3af66SJakub Kicinski devl_unlock(devlink); 21407f3af66SJakub Kicinski devlink_put(devlink); 21507f3af66SJakub Kicinski 21607f3af66SJakub Kicinski if (err) 21707f3af66SJakub Kicinski break; 21807f3af66SJakub Kicinski 219543753d9SJiri Pirko state->instance++; 220543753d9SJiri Pirko 22107f3af66SJakub Kicinski /* restart sub-object walk for the next instance */ 22207f3af66SJakub Kicinski state->idx = 0; 22307f3af66SJakub Kicinski } 22407f3af66SJakub Kicinski 22507f3af66SJakub Kicinski if (err != -EMSGSIZE) 22607f3af66SJakub Kicinski return err; 22707f3af66SJakub Kicinski return msg->len; 22807f3af66SJakub Kicinski } 22907f3af66SJakub Kicinski 230*491a2487SJiri Pirko int devlink_nl_instance_iter_dumpit(struct sk_buff *msg, 231*491a2487SJiri Pirko struct netlink_callback *cb) 232*491a2487SJiri Pirko { 233*491a2487SJiri Pirko const struct genl_dumpit_info *info = genl_dumpit_info(cb); 234*491a2487SJiri Pirko const struct devlink_cmd *cmd = devl_cmds[info->op.cmd]; 235*491a2487SJiri Pirko 236*491a2487SJiri Pirko return devlink_nl_dumpit(msg, cb, cmd->dump_one); 237*491a2487SJiri Pirko } 238*491a2487SJiri Pirko 239623cd13bSJakub Kicinski struct genl_family devlink_nl_family __ro_after_init = { 240623cd13bSJakub Kicinski .name = DEVLINK_GENL_NAME, 241623cd13bSJakub Kicinski .version = DEVLINK_GENL_VERSION, 242623cd13bSJakub Kicinski .maxattr = DEVLINK_ATTR_MAX, 243623cd13bSJakub Kicinski .policy = devlink_nl_policy, 244623cd13bSJakub Kicinski .netnsok = true, 245623cd13bSJakub Kicinski .parallel_ops = true, 246623cd13bSJakub Kicinski .pre_doit = devlink_nl_pre_doit, 247623cd13bSJakub Kicinski .post_doit = devlink_nl_post_doit, 248623cd13bSJakub Kicinski .module = THIS_MODULE, 249ba0f66c9SJiri Pirko .small_ops = devlink_nl_small_ops, 250ba0f66c9SJiri Pirko .n_small_ops = ARRAY_SIZE(devlink_nl_small_ops), 251623cd13bSJakub Kicinski .resv_start_op = DEVLINK_CMD_SELFTESTS_RUN + 1, 252623cd13bSJakub Kicinski .mcgrps = devlink_nl_mcgrps, 253623cd13bSJakub Kicinski .n_mcgrps = ARRAY_SIZE(devlink_nl_mcgrps), 254623cd13bSJakub Kicinski }; 255