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 112ee6d78acSJiri Pirko static int __devlink_nl_pre_doit(struct sk_buff *skb, struct genl_info *info, 113ee6d78acSJiri Pirko u8 flags) 114623cd13bSJakub Kicinski { 115623cd13bSJakub Kicinski struct devlink_port *devlink_port; 116623cd13bSJakub Kicinski struct devlink *devlink; 117623cd13bSJakub Kicinski int err; 118623cd13bSJakub Kicinski 119870c7ad4SJakub Kicinski devlink = devlink_get_from_attrs_lock(genl_info_net(info), info->attrs); 120623cd13bSJakub Kicinski if (IS_ERR(devlink)) 121623cd13bSJakub Kicinski return PTR_ERR(devlink); 122870c7ad4SJakub Kicinski 123623cd13bSJakub Kicinski info->user_ptr[0] = devlink; 124ee6d78acSJiri Pirko if (flags & DEVLINK_NL_FLAG_NEED_PORT) { 125623cd13bSJakub Kicinski devlink_port = devlink_port_get_from_info(devlink, info); 126623cd13bSJakub Kicinski if (IS_ERR(devlink_port)) { 127623cd13bSJakub Kicinski err = PTR_ERR(devlink_port); 128623cd13bSJakub Kicinski goto unlock; 129623cd13bSJakub Kicinski } 130623cd13bSJakub Kicinski info->user_ptr[1] = devlink_port; 131ee6d78acSJiri Pirko } else if (flags & DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT) { 132623cd13bSJakub Kicinski devlink_port = devlink_port_get_from_info(devlink, info); 133623cd13bSJakub Kicinski if (!IS_ERR(devlink_port)) 134623cd13bSJakub Kicinski info->user_ptr[1] = devlink_port; 135623cd13bSJakub Kicinski } 136623cd13bSJakub Kicinski return 0; 137623cd13bSJakub Kicinski 138623cd13bSJakub Kicinski unlock: 139623cd13bSJakub Kicinski devl_unlock(devlink); 140623cd13bSJakub Kicinski devlink_put(devlink); 141623cd13bSJakub Kicinski return err; 142623cd13bSJakub Kicinski } 143623cd13bSJakub Kicinski 144ee6d78acSJiri Pirko int devlink_nl_pre_doit(const struct genl_split_ops *ops, 145ee6d78acSJiri Pirko struct sk_buff *skb, struct genl_info *info) 146ee6d78acSJiri Pirko { 147ee6d78acSJiri Pirko return __devlink_nl_pre_doit(skb, info, ops->internal_flags); 148ee6d78acSJiri Pirko } 149ee6d78acSJiri Pirko 150ee6d78acSJiri Pirko int devlink_nl_pre_doit_port(const struct genl_split_ops *ops, 151ee6d78acSJiri Pirko struct sk_buff *skb, struct genl_info *info) 152ee6d78acSJiri Pirko { 153ee6d78acSJiri Pirko return __devlink_nl_pre_doit(skb, info, DEVLINK_NL_FLAG_NEED_PORT); 154ee6d78acSJiri Pirko } 155ee6d78acSJiri Pirko 156ee6d78acSJiri Pirko int devlink_nl_pre_doit_port_optional(const struct genl_split_ops *ops, 157ee6d78acSJiri Pirko struct sk_buff *skb, 158ee6d78acSJiri Pirko struct genl_info *info) 159ee6d78acSJiri Pirko { 160ee6d78acSJiri Pirko return __devlink_nl_pre_doit(skb, info, DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT); 161ee6d78acSJiri Pirko } 162ee6d78acSJiri Pirko 1638300dce5SJiri Pirko void devlink_nl_post_doit(const struct genl_split_ops *ops, 164623cd13bSJakub Kicinski struct sk_buff *skb, struct genl_info *info) 165623cd13bSJakub Kicinski { 166623cd13bSJakub Kicinski struct devlink *devlink; 167623cd13bSJakub Kicinski 168623cd13bSJakub Kicinski devlink = info->user_ptr[0]; 169623cd13bSJakub Kicinski devl_unlock(devlink); 170623cd13bSJakub Kicinski devlink_put(devlink); 171623cd13bSJakub Kicinski } 172623cd13bSJakub Kicinski 1734a1b5aa8SJiri Pirko static int devlink_nl_inst_single_dumpit(struct sk_buff *msg, 1744a1b5aa8SJiri Pirko struct netlink_callback *cb, int flags, 1754a1b5aa8SJiri Pirko devlink_nl_dump_one_func_t *dump_one, 1764a1b5aa8SJiri Pirko struct nlattr **attrs) 1774a1b5aa8SJiri Pirko { 1784a1b5aa8SJiri Pirko struct devlink *devlink; 1794a1b5aa8SJiri Pirko int err; 1804a1b5aa8SJiri Pirko 1814a1b5aa8SJiri Pirko devlink = devlink_get_from_attrs_lock(sock_net(msg->sk), attrs); 1824a1b5aa8SJiri Pirko if (IS_ERR(devlink)) 1834a1b5aa8SJiri Pirko return PTR_ERR(devlink); 1844a1b5aa8SJiri Pirko err = dump_one(msg, devlink, cb, flags | NLM_F_DUMP_FILTERED); 1854a1b5aa8SJiri Pirko 1864a1b5aa8SJiri Pirko devl_unlock(devlink); 1874a1b5aa8SJiri Pirko devlink_put(devlink); 1884a1b5aa8SJiri Pirko 1894a1b5aa8SJiri Pirko if (err != -EMSGSIZE) 1904a1b5aa8SJiri Pirko return err; 1914a1b5aa8SJiri Pirko return msg->len; 1924a1b5aa8SJiri Pirko } 1934a1b5aa8SJiri Pirko 1944a1b5aa8SJiri Pirko static int devlink_nl_inst_iter_dumpit(struct sk_buff *msg, 1954a1b5aa8SJiri Pirko struct netlink_callback *cb, int flags, 196491a2487SJiri Pirko devlink_nl_dump_one_func_t *dump_one) 19707f3af66SJakub Kicinski { 19807f3af66SJakub Kicinski struct devlink_nl_dump_state *state = devlink_dump_state(cb); 19907f3af66SJakub Kicinski struct devlink *devlink; 20007f3af66SJakub Kicinski int err = 0; 20107f3af66SJakub Kicinski 202543753d9SJiri Pirko while ((devlink = devlinks_xa_find_get(sock_net(msg->sk), 203543753d9SJiri Pirko &state->instance))) { 20407f3af66SJakub Kicinski devl_lock(devlink); 205ed539ba6SJakub Kicinski 206ed539ba6SJakub Kicinski if (devl_is_registered(devlink)) 2074a1b5aa8SJiri Pirko err = dump_one(msg, devlink, cb, flags); 208ed539ba6SJakub Kicinski else 209ed539ba6SJakub Kicinski err = 0; 210ed539ba6SJakub Kicinski 21107f3af66SJakub Kicinski devl_unlock(devlink); 21207f3af66SJakub Kicinski devlink_put(devlink); 21307f3af66SJakub Kicinski 21407f3af66SJakub Kicinski if (err) 21507f3af66SJakub Kicinski break; 21607f3af66SJakub Kicinski 217543753d9SJiri Pirko state->instance++; 218543753d9SJiri Pirko 21907f3af66SJakub Kicinski /* restart sub-object walk for the next instance */ 22007f3af66SJakub Kicinski state->idx = 0; 22107f3af66SJakub Kicinski } 22207f3af66SJakub Kicinski 22307f3af66SJakub Kicinski if (err != -EMSGSIZE) 22407f3af66SJakub Kicinski return err; 22507f3af66SJakub Kicinski return msg->len; 22607f3af66SJakub Kicinski } 22707f3af66SJakub Kicinski 2284a1b5aa8SJiri Pirko int devlink_nl_dumpit(struct sk_buff *msg, struct netlink_callback *cb, 2294a1b5aa8SJiri Pirko devlink_nl_dump_one_func_t *dump_one) 2304a1b5aa8SJiri Pirko { 231*7288dd2fSJakub Kicinski const struct genl_info *info = genl_info_dump(cb); 2324a1b5aa8SJiri Pirko struct nlattr **attrs = info->attrs; 2334a1b5aa8SJiri Pirko int flags = NLM_F_MULTI; 2344a1b5aa8SJiri Pirko 2354a1b5aa8SJiri Pirko if (attrs && 2364a1b5aa8SJiri Pirko (attrs[DEVLINK_ATTR_BUS_NAME] || attrs[DEVLINK_ATTR_DEV_NAME])) 2374a1b5aa8SJiri Pirko return devlink_nl_inst_single_dumpit(msg, cb, flags, dump_one, 2384a1b5aa8SJiri Pirko attrs); 2394a1b5aa8SJiri Pirko else 2404a1b5aa8SJiri Pirko return devlink_nl_inst_iter_dumpit(msg, cb, flags, dump_one); 2414a1b5aa8SJiri Pirko } 2424a1b5aa8SJiri Pirko 243623cd13bSJakub Kicinski struct genl_family devlink_nl_family __ro_after_init = { 244623cd13bSJakub Kicinski .name = DEVLINK_GENL_NAME, 245623cd13bSJakub Kicinski .version = DEVLINK_GENL_VERSION, 246623cd13bSJakub Kicinski .maxattr = DEVLINK_ATTR_MAX, 247623cd13bSJakub Kicinski .policy = devlink_nl_policy, 248623cd13bSJakub Kicinski .netnsok = true, 249623cd13bSJakub Kicinski .parallel_ops = true, 250623cd13bSJakub Kicinski .pre_doit = devlink_nl_pre_doit, 251623cd13bSJakub Kicinski .post_doit = devlink_nl_post_doit, 252623cd13bSJakub Kicinski .module = THIS_MODULE, 253ba0f66c9SJiri Pirko .small_ops = devlink_nl_small_ops, 254ba0f66c9SJiri Pirko .n_small_ops = ARRAY_SIZE(devlink_nl_small_ops), 2556e067d0cSJiri Pirko .split_ops = devlink_nl_ops, 2566e067d0cSJiri Pirko .n_split_ops = ARRAY_SIZE(devlink_nl_ops), 257623cd13bSJakub Kicinski .resv_start_op = DEVLINK_CMD_SELFTESTS_RUN + 1, 258623cd13bSJakub Kicinski .mcgrps = devlink_nl_mcgrps, 259623cd13bSJakub Kicinski .n_mcgrps = ARRAY_SIZE(devlink_nl_mcgrps), 260623cd13bSJakub Kicinski }; 261