1*623cd13bSJakub Kicinski // SPDX-License-Identifier: GPL-2.0-or-later 2*623cd13bSJakub Kicinski /* 3*623cd13bSJakub Kicinski * Copyright (c) 2016 Mellanox Technologies. All rights reserved. 4*623cd13bSJakub Kicinski * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> 5*623cd13bSJakub Kicinski */ 6*623cd13bSJakub Kicinski 7*623cd13bSJakub Kicinski #include <net/genetlink.h> 8*623cd13bSJakub Kicinski 9*623cd13bSJakub Kicinski #include "devl_internal.h" 10*623cd13bSJakub Kicinski 11*623cd13bSJakub Kicinski static const struct genl_multicast_group devlink_nl_mcgrps[] = { 12*623cd13bSJakub Kicinski [DEVLINK_MCGRP_CONFIG] = { .name = DEVLINK_GENL_MCGRP_CONFIG_NAME }, 13*623cd13bSJakub Kicinski }; 14*623cd13bSJakub Kicinski 15*623cd13bSJakub Kicinski static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = { 16*623cd13bSJakub Kicinski [DEVLINK_ATTR_UNSPEC] = { .strict_start_type = 17*623cd13bSJakub Kicinski DEVLINK_ATTR_TRAP_POLICER_ID }, 18*623cd13bSJakub Kicinski [DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING }, 19*623cd13bSJakub Kicinski [DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING }, 20*623cd13bSJakub Kicinski [DEVLINK_ATTR_PORT_INDEX] = { .type = NLA_U32 }, 21*623cd13bSJakub Kicinski [DEVLINK_ATTR_PORT_TYPE] = NLA_POLICY_RANGE(NLA_U16, DEVLINK_PORT_TYPE_AUTO, 22*623cd13bSJakub Kicinski DEVLINK_PORT_TYPE_IB), 23*623cd13bSJakub Kicinski [DEVLINK_ATTR_PORT_SPLIT_COUNT] = { .type = NLA_U32 }, 24*623cd13bSJakub Kicinski [DEVLINK_ATTR_SB_INDEX] = { .type = NLA_U32 }, 25*623cd13bSJakub Kicinski [DEVLINK_ATTR_SB_POOL_INDEX] = { .type = NLA_U16 }, 26*623cd13bSJakub Kicinski [DEVLINK_ATTR_SB_POOL_TYPE] = { .type = NLA_U8 }, 27*623cd13bSJakub Kicinski [DEVLINK_ATTR_SB_POOL_SIZE] = { .type = NLA_U32 }, 28*623cd13bSJakub Kicinski [DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE] = { .type = NLA_U8 }, 29*623cd13bSJakub Kicinski [DEVLINK_ATTR_SB_THRESHOLD] = { .type = NLA_U32 }, 30*623cd13bSJakub Kicinski [DEVLINK_ATTR_SB_TC_INDEX] = { .type = NLA_U16 }, 31*623cd13bSJakub Kicinski [DEVLINK_ATTR_ESWITCH_MODE] = NLA_POLICY_RANGE(NLA_U16, DEVLINK_ESWITCH_MODE_LEGACY, 32*623cd13bSJakub Kicinski DEVLINK_ESWITCH_MODE_SWITCHDEV), 33*623cd13bSJakub Kicinski [DEVLINK_ATTR_ESWITCH_INLINE_MODE] = { .type = NLA_U8 }, 34*623cd13bSJakub Kicinski [DEVLINK_ATTR_ESWITCH_ENCAP_MODE] = { .type = NLA_U8 }, 35*623cd13bSJakub Kicinski [DEVLINK_ATTR_DPIPE_TABLE_NAME] = { .type = NLA_NUL_STRING }, 36*623cd13bSJakub Kicinski [DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED] = { .type = NLA_U8 }, 37*623cd13bSJakub Kicinski [DEVLINK_ATTR_RESOURCE_ID] = { .type = NLA_U64}, 38*623cd13bSJakub Kicinski [DEVLINK_ATTR_RESOURCE_SIZE] = { .type = NLA_U64}, 39*623cd13bSJakub Kicinski [DEVLINK_ATTR_PARAM_NAME] = { .type = NLA_NUL_STRING }, 40*623cd13bSJakub Kicinski [DEVLINK_ATTR_PARAM_TYPE] = { .type = NLA_U8 }, 41*623cd13bSJakub Kicinski [DEVLINK_ATTR_PARAM_VALUE_CMODE] = { .type = NLA_U8 }, 42*623cd13bSJakub Kicinski [DEVLINK_ATTR_REGION_NAME] = { .type = NLA_NUL_STRING }, 43*623cd13bSJakub Kicinski [DEVLINK_ATTR_REGION_SNAPSHOT_ID] = { .type = NLA_U32 }, 44*623cd13bSJakub Kicinski [DEVLINK_ATTR_REGION_CHUNK_ADDR] = { .type = NLA_U64 }, 45*623cd13bSJakub Kicinski [DEVLINK_ATTR_REGION_CHUNK_LEN] = { .type = NLA_U64 }, 46*623cd13bSJakub Kicinski [DEVLINK_ATTR_HEALTH_REPORTER_NAME] = { .type = NLA_NUL_STRING }, 47*623cd13bSJakub Kicinski [DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] = { .type = NLA_U64 }, 48*623cd13bSJakub Kicinski [DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER] = { .type = NLA_U8 }, 49*623cd13bSJakub Kicinski [DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME] = { .type = NLA_NUL_STRING }, 50*623cd13bSJakub Kicinski [DEVLINK_ATTR_FLASH_UPDATE_COMPONENT] = { .type = NLA_NUL_STRING }, 51*623cd13bSJakub Kicinski [DEVLINK_ATTR_FLASH_UPDATE_OVERWRITE_MASK] = 52*623cd13bSJakub Kicinski NLA_POLICY_BITFIELD32(DEVLINK_SUPPORTED_FLASH_OVERWRITE_SECTIONS), 53*623cd13bSJakub Kicinski [DEVLINK_ATTR_TRAP_NAME] = { .type = NLA_NUL_STRING }, 54*623cd13bSJakub Kicinski [DEVLINK_ATTR_TRAP_ACTION] = { .type = NLA_U8 }, 55*623cd13bSJakub Kicinski [DEVLINK_ATTR_TRAP_GROUP_NAME] = { .type = NLA_NUL_STRING }, 56*623cd13bSJakub Kicinski [DEVLINK_ATTR_NETNS_PID] = { .type = NLA_U32 }, 57*623cd13bSJakub Kicinski [DEVLINK_ATTR_NETNS_FD] = { .type = NLA_U32 }, 58*623cd13bSJakub Kicinski [DEVLINK_ATTR_NETNS_ID] = { .type = NLA_U32 }, 59*623cd13bSJakub Kicinski [DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP] = { .type = NLA_U8 }, 60*623cd13bSJakub Kicinski [DEVLINK_ATTR_TRAP_POLICER_ID] = { .type = NLA_U32 }, 61*623cd13bSJakub Kicinski [DEVLINK_ATTR_TRAP_POLICER_RATE] = { .type = NLA_U64 }, 62*623cd13bSJakub Kicinski [DEVLINK_ATTR_TRAP_POLICER_BURST] = { .type = NLA_U64 }, 63*623cd13bSJakub Kicinski [DEVLINK_ATTR_PORT_FUNCTION] = { .type = NLA_NESTED }, 64*623cd13bSJakub Kicinski [DEVLINK_ATTR_RELOAD_ACTION] = NLA_POLICY_RANGE(NLA_U8, DEVLINK_RELOAD_ACTION_DRIVER_REINIT, 65*623cd13bSJakub Kicinski DEVLINK_RELOAD_ACTION_MAX), 66*623cd13bSJakub Kicinski [DEVLINK_ATTR_RELOAD_LIMITS] = NLA_POLICY_BITFIELD32(DEVLINK_RELOAD_LIMITS_VALID_MASK), 67*623cd13bSJakub Kicinski [DEVLINK_ATTR_PORT_FLAVOUR] = { .type = NLA_U16 }, 68*623cd13bSJakub Kicinski [DEVLINK_ATTR_PORT_PCI_PF_NUMBER] = { .type = NLA_U16 }, 69*623cd13bSJakub Kicinski [DEVLINK_ATTR_PORT_PCI_SF_NUMBER] = { .type = NLA_U32 }, 70*623cd13bSJakub Kicinski [DEVLINK_ATTR_PORT_CONTROLLER_NUMBER] = { .type = NLA_U32 }, 71*623cd13bSJakub Kicinski [DEVLINK_ATTR_RATE_TYPE] = { .type = NLA_U16 }, 72*623cd13bSJakub Kicinski [DEVLINK_ATTR_RATE_TX_SHARE] = { .type = NLA_U64 }, 73*623cd13bSJakub Kicinski [DEVLINK_ATTR_RATE_TX_MAX] = { .type = NLA_U64 }, 74*623cd13bSJakub Kicinski [DEVLINK_ATTR_RATE_NODE_NAME] = { .type = NLA_NUL_STRING }, 75*623cd13bSJakub Kicinski [DEVLINK_ATTR_RATE_PARENT_NODE_NAME] = { .type = NLA_NUL_STRING }, 76*623cd13bSJakub Kicinski [DEVLINK_ATTR_LINECARD_INDEX] = { .type = NLA_U32 }, 77*623cd13bSJakub Kicinski [DEVLINK_ATTR_LINECARD_TYPE] = { .type = NLA_NUL_STRING }, 78*623cd13bSJakub Kicinski [DEVLINK_ATTR_SELFTESTS] = { .type = NLA_NESTED }, 79*623cd13bSJakub Kicinski [DEVLINK_ATTR_RATE_TX_PRIORITY] = { .type = NLA_U32 }, 80*623cd13bSJakub Kicinski [DEVLINK_ATTR_RATE_TX_WEIGHT] = { .type = NLA_U32 }, 81*623cd13bSJakub Kicinski [DEVLINK_ATTR_REGION_DIRECT] = { .type = NLA_FLAG }, 82*623cd13bSJakub Kicinski }; 83*623cd13bSJakub Kicinski 84*623cd13bSJakub Kicinski struct devlink *devlink_get_from_attrs(struct net *net, struct nlattr **attrs) 85*623cd13bSJakub Kicinski { 86*623cd13bSJakub Kicinski struct devlink *devlink; 87*623cd13bSJakub Kicinski unsigned long index; 88*623cd13bSJakub Kicinski char *busname; 89*623cd13bSJakub Kicinski char *devname; 90*623cd13bSJakub Kicinski 91*623cd13bSJakub Kicinski if (!attrs[DEVLINK_ATTR_BUS_NAME] || !attrs[DEVLINK_ATTR_DEV_NAME]) 92*623cd13bSJakub Kicinski return ERR_PTR(-EINVAL); 93*623cd13bSJakub Kicinski 94*623cd13bSJakub Kicinski busname = nla_data(attrs[DEVLINK_ATTR_BUS_NAME]); 95*623cd13bSJakub Kicinski devname = nla_data(attrs[DEVLINK_ATTR_DEV_NAME]); 96*623cd13bSJakub Kicinski 97*623cd13bSJakub Kicinski devlinks_xa_for_each_registered_get(net, index, devlink) { 98*623cd13bSJakub Kicinski if (strcmp(devlink->dev->bus->name, busname) == 0 && 99*623cd13bSJakub Kicinski strcmp(dev_name(devlink->dev), devname) == 0) 100*623cd13bSJakub Kicinski return devlink; 101*623cd13bSJakub Kicinski devlink_put(devlink); 102*623cd13bSJakub Kicinski } 103*623cd13bSJakub Kicinski 104*623cd13bSJakub Kicinski return ERR_PTR(-ENODEV); 105*623cd13bSJakub Kicinski } 106*623cd13bSJakub Kicinski 107*623cd13bSJakub Kicinski static int devlink_nl_pre_doit(const struct genl_split_ops *ops, 108*623cd13bSJakub Kicinski struct sk_buff *skb, struct genl_info *info) 109*623cd13bSJakub Kicinski { 110*623cd13bSJakub Kicinski struct devlink_linecard *linecard; 111*623cd13bSJakub Kicinski struct devlink_port *devlink_port; 112*623cd13bSJakub Kicinski struct devlink *devlink; 113*623cd13bSJakub Kicinski int err; 114*623cd13bSJakub Kicinski 115*623cd13bSJakub Kicinski devlink = devlink_get_from_attrs(genl_info_net(info), info->attrs); 116*623cd13bSJakub Kicinski if (IS_ERR(devlink)) 117*623cd13bSJakub Kicinski return PTR_ERR(devlink); 118*623cd13bSJakub Kicinski devl_lock(devlink); 119*623cd13bSJakub Kicinski info->user_ptr[0] = devlink; 120*623cd13bSJakub Kicinski if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_PORT) { 121*623cd13bSJakub Kicinski devlink_port = devlink_port_get_from_info(devlink, info); 122*623cd13bSJakub Kicinski if (IS_ERR(devlink_port)) { 123*623cd13bSJakub Kicinski err = PTR_ERR(devlink_port); 124*623cd13bSJakub Kicinski goto unlock; 125*623cd13bSJakub Kicinski } 126*623cd13bSJakub Kicinski info->user_ptr[1] = devlink_port; 127*623cd13bSJakub Kicinski } else if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT) { 128*623cd13bSJakub Kicinski devlink_port = devlink_port_get_from_info(devlink, info); 129*623cd13bSJakub Kicinski if (!IS_ERR(devlink_port)) 130*623cd13bSJakub Kicinski info->user_ptr[1] = devlink_port; 131*623cd13bSJakub Kicinski } else if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_RATE) { 132*623cd13bSJakub Kicinski struct devlink_rate *devlink_rate; 133*623cd13bSJakub Kicinski 134*623cd13bSJakub Kicinski devlink_rate = devlink_rate_get_from_info(devlink, info); 135*623cd13bSJakub Kicinski if (IS_ERR(devlink_rate)) { 136*623cd13bSJakub Kicinski err = PTR_ERR(devlink_rate); 137*623cd13bSJakub Kicinski goto unlock; 138*623cd13bSJakub Kicinski } 139*623cd13bSJakub Kicinski info->user_ptr[1] = devlink_rate; 140*623cd13bSJakub Kicinski } else if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_RATE_NODE) { 141*623cd13bSJakub Kicinski struct devlink_rate *rate_node; 142*623cd13bSJakub Kicinski 143*623cd13bSJakub Kicinski rate_node = devlink_rate_node_get_from_info(devlink, info); 144*623cd13bSJakub Kicinski if (IS_ERR(rate_node)) { 145*623cd13bSJakub Kicinski err = PTR_ERR(rate_node); 146*623cd13bSJakub Kicinski goto unlock; 147*623cd13bSJakub Kicinski } 148*623cd13bSJakub Kicinski info->user_ptr[1] = rate_node; 149*623cd13bSJakub Kicinski } else if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_LINECARD) { 150*623cd13bSJakub Kicinski linecard = devlink_linecard_get_from_info(devlink, info); 151*623cd13bSJakub Kicinski if (IS_ERR(linecard)) { 152*623cd13bSJakub Kicinski err = PTR_ERR(linecard); 153*623cd13bSJakub Kicinski goto unlock; 154*623cd13bSJakub Kicinski } 155*623cd13bSJakub Kicinski info->user_ptr[1] = linecard; 156*623cd13bSJakub Kicinski } 157*623cd13bSJakub Kicinski return 0; 158*623cd13bSJakub Kicinski 159*623cd13bSJakub Kicinski unlock: 160*623cd13bSJakub Kicinski devl_unlock(devlink); 161*623cd13bSJakub Kicinski devlink_put(devlink); 162*623cd13bSJakub Kicinski return err; 163*623cd13bSJakub Kicinski } 164*623cd13bSJakub Kicinski 165*623cd13bSJakub Kicinski static void devlink_nl_post_doit(const struct genl_split_ops *ops, 166*623cd13bSJakub Kicinski struct sk_buff *skb, struct genl_info *info) 167*623cd13bSJakub Kicinski { 168*623cd13bSJakub Kicinski struct devlink_linecard *linecard; 169*623cd13bSJakub Kicinski struct devlink *devlink; 170*623cd13bSJakub Kicinski 171*623cd13bSJakub Kicinski devlink = info->user_ptr[0]; 172*623cd13bSJakub Kicinski if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_LINECARD) { 173*623cd13bSJakub Kicinski linecard = info->user_ptr[1]; 174*623cd13bSJakub Kicinski devlink_linecard_put(linecard); 175*623cd13bSJakub Kicinski } 176*623cd13bSJakub Kicinski devl_unlock(devlink); 177*623cd13bSJakub Kicinski devlink_put(devlink); 178*623cd13bSJakub Kicinski } 179*623cd13bSJakub Kicinski 180*623cd13bSJakub Kicinski struct genl_family devlink_nl_family __ro_after_init = { 181*623cd13bSJakub Kicinski .name = DEVLINK_GENL_NAME, 182*623cd13bSJakub Kicinski .version = DEVLINK_GENL_VERSION, 183*623cd13bSJakub Kicinski .maxattr = DEVLINK_ATTR_MAX, 184*623cd13bSJakub Kicinski .policy = devlink_nl_policy, 185*623cd13bSJakub Kicinski .netnsok = true, 186*623cd13bSJakub Kicinski .parallel_ops = true, 187*623cd13bSJakub Kicinski .pre_doit = devlink_nl_pre_doit, 188*623cd13bSJakub Kicinski .post_doit = devlink_nl_post_doit, 189*623cd13bSJakub Kicinski .module = THIS_MODULE, 190*623cd13bSJakub Kicinski .small_ops = devlink_nl_ops, 191*623cd13bSJakub Kicinski .n_small_ops = ARRAY_SIZE(devlink_nl_ops), 192*623cd13bSJakub Kicinski .resv_start_op = DEVLINK_CMD_SELFTESTS_RUN + 1, 193*623cd13bSJakub Kicinski .mcgrps = devlink_nl_mcgrps, 194*623cd13bSJakub Kicinski .n_mcgrps = ARRAY_SIZE(devlink_nl_mcgrps), 195*623cd13bSJakub Kicinski }; 196