1011 lines · 26.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * Copyright (c) 2016 Mellanox Technologies. All rights reserved. * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> */ #include "devl_internal.h" static const struct devlink_param devlink_param_generic[] = {	{		.id = DEVLINK_PARAM_GENERIC_ID_INT_ERR_RESET,		.name = DEVLINK_PARAM_GENERIC_INT_ERR_RESET_NAME,		.type = DEVLINK_PARAM_GENERIC_INT_ERR_RESET_TYPE,	},	{		.id = DEVLINK_PARAM_GENERIC_ID_MAX_MACS,		.name = DEVLINK_PARAM_GENERIC_MAX_MACS_NAME,		.type = DEVLINK_PARAM_GENERIC_MAX_MACS_TYPE,	},	{		.id = DEVLINK_PARAM_GENERIC_ID_ENABLE_SRIOV,		.name = DEVLINK_PARAM_GENERIC_ENABLE_SRIOV_NAME,		.type = DEVLINK_PARAM_GENERIC_ENABLE_SRIOV_TYPE,	},	{		.id = DEVLINK_PARAM_GENERIC_ID_REGION_SNAPSHOT,		.name = DEVLINK_PARAM_GENERIC_REGION_SNAPSHOT_NAME,		.type = DEVLINK_PARAM_GENERIC_REGION_SNAPSHOT_TYPE,	},	{		.id = DEVLINK_PARAM_GENERIC_ID_IGNORE_ARI,		.name = DEVLINK_PARAM_GENERIC_IGNORE_ARI_NAME,		.type = DEVLINK_PARAM_GENERIC_IGNORE_ARI_TYPE,	},	{		.id = DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MAX,		.name = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MAX_NAME,		.type = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MAX_TYPE,	},	{		.id = DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN,		.name = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_NAME,		.type = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_TYPE,	},	{		.id = DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY,		.name = DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_NAME,		.type = DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_TYPE,	},	{		.id = DEVLINK_PARAM_GENERIC_ID_RESET_DEV_ON_DRV_PROBE,		.name = DEVLINK_PARAM_GENERIC_RESET_DEV_ON_DRV_PROBE_NAME,		.type = DEVLINK_PARAM_GENERIC_RESET_DEV_ON_DRV_PROBE_TYPE,	},	{		.id = DEVLINK_PARAM_GENERIC_ID_ENABLE_ROCE,		.name = DEVLINK_PARAM_GENERIC_ENABLE_ROCE_NAME,		.type = DEVLINK_PARAM_GENERIC_ENABLE_ROCE_TYPE,	},	{		.id = DEVLINK_PARAM_GENERIC_ID_ENABLE_REMOTE_DEV_RESET,		.name = DEVLINK_PARAM_GENERIC_ENABLE_REMOTE_DEV_RESET_NAME,		.type = DEVLINK_PARAM_GENERIC_ENABLE_REMOTE_DEV_RESET_TYPE,	},	{		.id = DEVLINK_PARAM_GENERIC_ID_ENABLE_ETH,		.name = DEVLINK_PARAM_GENERIC_ENABLE_ETH_NAME,		.type = DEVLINK_PARAM_GENERIC_ENABLE_ETH_TYPE,	},	{		.id = DEVLINK_PARAM_GENERIC_ID_ENABLE_RDMA,		.name = DEVLINK_PARAM_GENERIC_ENABLE_RDMA_NAME,		.type = DEVLINK_PARAM_GENERIC_ENABLE_RDMA_TYPE,	},	{		.id = DEVLINK_PARAM_GENERIC_ID_ENABLE_VNET,		.name = DEVLINK_PARAM_GENERIC_ENABLE_VNET_NAME,		.type = DEVLINK_PARAM_GENERIC_ENABLE_VNET_TYPE,	},	{		.id = DEVLINK_PARAM_GENERIC_ID_ENABLE_IWARP,		.name = DEVLINK_PARAM_GENERIC_ENABLE_IWARP_NAME,		.type = DEVLINK_PARAM_GENERIC_ENABLE_IWARP_TYPE,	},	{		.id = DEVLINK_PARAM_GENERIC_ID_IO_EQ_SIZE,		.name = DEVLINK_PARAM_GENERIC_IO_EQ_SIZE_NAME,		.type = DEVLINK_PARAM_GENERIC_IO_EQ_SIZE_TYPE,	},	{		.id = DEVLINK_PARAM_GENERIC_ID_EVENT_EQ_SIZE,		.name = DEVLINK_PARAM_GENERIC_EVENT_EQ_SIZE_NAME,		.type = DEVLINK_PARAM_GENERIC_EVENT_EQ_SIZE_TYPE,	},	{		.id = DEVLINK_PARAM_GENERIC_ID_ENABLE_PHC,		.name = DEVLINK_PARAM_GENERIC_ENABLE_PHC_NAME,		.type = DEVLINK_PARAM_GENERIC_ENABLE_PHC_TYPE,	},	{		.id = DEVLINK_PARAM_GENERIC_ID_CLOCK_ID,		.name = DEVLINK_PARAM_GENERIC_CLOCK_ID_NAME,		.type = DEVLINK_PARAM_GENERIC_CLOCK_ID_TYPE,	},	{		.id = DEVLINK_PARAM_GENERIC_ID_TOTAL_VFS,		.name = DEVLINK_PARAM_GENERIC_TOTAL_VFS_NAME,		.type = DEVLINK_PARAM_GENERIC_TOTAL_VFS_TYPE,	},	{		.id = DEVLINK_PARAM_GENERIC_ID_NUM_DOORBELLS,		.name = DEVLINK_PARAM_GENERIC_NUM_DOORBELLS_NAME,		.type = DEVLINK_PARAM_GENERIC_NUM_DOORBELLS_TYPE,	},	{		.id = DEVLINK_PARAM_GENERIC_ID_MAX_MAC_PER_VF,		.name = DEVLINK_PARAM_GENERIC_MAX_MAC_PER_VF_NAME,		.type = DEVLINK_PARAM_GENERIC_MAX_MAC_PER_VF_TYPE,	},}; static int devlink_param_generic_verify(const struct devlink_param *param){	/* verify it match generic parameter by id and name */	if (param->id > DEVLINK_PARAM_GENERIC_ID_MAX)		return -EINVAL;	if (strcmp(param->name, devlink_param_generic[param->id].name))		return -ENOENT; 	WARN_ON(param->type != devlink_param_generic[param->id].type); 	return 0;} static int devlink_param_driver_verify(const struct devlink_param *param){	int i; 	if (param->id <= DEVLINK_PARAM_GENERIC_ID_MAX)		return -EINVAL;	/* verify no such name in generic params */	for (i = 0; i <= DEVLINK_PARAM_GENERIC_ID_MAX; i++)		if (!strcmp(param->name, devlink_param_generic[i].name))			return -EEXIST; 	return 0;} static struct devlink_param_item *devlink_param_find_by_name(struct xarray *params, const char *param_name){	struct devlink_param_item *param_item;	unsigned long param_id; 	xa_for_each(params, param_id, param_item) {		if (!strcmp(param_item->param->name, param_name))			return param_item;	}	return NULL;} static struct devlink_param_item *devlink_param_find_by_id(struct xarray *params, u32 param_id){	return xa_load(params, param_id);} static booldevlink_param_cmode_is_supported(const struct devlink_param *param,				 enum devlink_param_cmode cmode){	return test_bit(cmode, &param->supported_cmodes);} static int devlink_param_get(struct devlink *devlink,			     const struct devlink_param *param,			     struct devlink_param_gset_ctx *ctx,			     struct netlink_ext_ack *extack){	if (!param->get)		return -EOPNOTSUPP;	return param->get(devlink, param->id, ctx, extack);} static int devlink_param_set(struct devlink *devlink,			     const struct devlink_param *param,			     struct devlink_param_gset_ctx *ctx,			     struct netlink_ext_ack *extack){	if (!param->set)		return -EOPNOTSUPP;	return param->set(devlink, param->id, ctx, extack);} static int devlink_param_get_default(struct devlink *devlink,				     const struct devlink_param *param,				     struct devlink_param_gset_ctx *ctx,				     struct netlink_ext_ack *extack){	if (!param->get_default)		return -EOPNOTSUPP; 	return param->get_default(devlink, param->id, ctx, extack);} static int devlink_param_reset_default(struct devlink *devlink,				       const struct devlink_param *param,				       enum devlink_param_cmode cmode,				       struct netlink_ext_ack *extack){	if (!param->reset_default)		return -EOPNOTSUPP; 	return param->reset_default(devlink, param->id, cmode, extack);} static intdevlink_nl_param_value_put(struct sk_buff *msg, enum devlink_param_type type,			   int nla_type, union devlink_param_value *val,			   bool flag_as_u8){	switch (type) {	case DEVLINK_PARAM_TYPE_U8:		if (nla_put_u8(msg, nla_type, val->vu8))			return -EMSGSIZE;		break;	case DEVLINK_PARAM_TYPE_U16:		if (nla_put_u16(msg, nla_type, val->vu16))			return -EMSGSIZE;		break;	case DEVLINK_PARAM_TYPE_U32:		if (nla_put_u32(msg, nla_type, val->vu32))			return -EMSGSIZE;		break;	case DEVLINK_PARAM_TYPE_U64:		if (devlink_nl_put_u64(msg, nla_type, val->vu64))			return -EMSGSIZE;		break;	case DEVLINK_PARAM_TYPE_STRING:		if (nla_put_string(msg, nla_type, val->vstr))			return -EMSGSIZE;		break;	case DEVLINK_PARAM_TYPE_BOOL:		/* default values of type bool are encoded with u8, so that		 * false can be distinguished from not present		 */		if (flag_as_u8) {			if (nla_put_u8(msg, nla_type, val->vbool))				return -EMSGSIZE;		} else {			if (val->vbool && nla_put_flag(msg, nla_type))				return -EMSGSIZE;		}		break;	case DEVLINK_PARAM_TYPE_U64_ARRAY:		if (val->u64arr.size > __DEVLINK_PARAM_MAX_ARRAY_SIZE)			return -EMSGSIZE; 		for (int i = 0; i < val->u64arr.size; i++) {			if (nla_put_uint(msg, nla_type, val->u64arr.val[i]))				return -EMSGSIZE;		}		break;	}	return 0;} static intdevlink_nl_param_value_fill_one(struct sk_buff *msg,				enum devlink_param_type type,				enum devlink_param_cmode cmode,				union devlink_param_value *val,				union devlink_param_value *default_val,				bool has_default){	struct nlattr *param_value_attr;	int err = -EMSGSIZE; 	param_value_attr = nla_nest_start_noflag(msg,						 DEVLINK_ATTR_PARAM_VALUE);	if (!param_value_attr)		return -EMSGSIZE; 	if (nla_put_u8(msg, DEVLINK_ATTR_PARAM_VALUE_CMODE, cmode))		goto value_nest_cancel; 	err = devlink_nl_param_value_put(msg, type,					 DEVLINK_ATTR_PARAM_VALUE_DATA,					 val, false);	if (err)		goto value_nest_cancel; 	if (has_default) {		err = devlink_nl_param_value_put(msg, type,						 DEVLINK_ATTR_PARAM_VALUE_DEFAULT,						 default_val, true);		if (err)			goto value_nest_cancel;	} 	nla_nest_end(msg, param_value_attr);	return 0; value_nest_cancel:	nla_nest_cancel(msg, param_value_attr);	return err;} static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,				 unsigned int port_index,				 struct devlink_param_item *param_item,				 enum devlink_command cmd,				 u32 portid, u32 seq, int flags,				 struct netlink_ext_ack *extack){	bool default_value_set[DEVLINK_PARAM_CMODE_MAX + 1] = {};	bool param_value_set[DEVLINK_PARAM_CMODE_MAX + 1] = {};	const struct devlink_param *param = param_item->param;	union devlink_param_value *default_value;	union devlink_param_value *param_value;	struct devlink_param_gset_ctx *ctx;	struct nlattr *param_values_list;	struct nlattr *param_attr;	void *hdr;	int err;	int i; 	default_value = kcalloc(DEVLINK_PARAM_CMODE_MAX + 1,				sizeof(*default_value), GFP_KERNEL);	if (!default_value)		return -ENOMEM; 	param_value = kcalloc(DEVLINK_PARAM_CMODE_MAX + 1,			      sizeof(*param_value), GFP_KERNEL);	if (!param_value) {		kfree(default_value);		return -ENOMEM;	} 	ctx = kzalloc_obj(*ctx);	if (!ctx) {		kfree(param_value);		kfree(default_value);		return -ENOMEM;	} 	/* Get value from driver part to driverinit configuration mode */	for (i = 0; i <= DEVLINK_PARAM_CMODE_MAX; i++) {		if (!devlink_param_cmode_is_supported(param, i))			continue;		if (i == DEVLINK_PARAM_CMODE_DRIVERINIT) {			if (param_item->driverinit_value_new_valid) {				param_value[i] = param_item->driverinit_value_new;			} else if (param_item->driverinit_value_valid) {				param_value[i] = param_item->driverinit_value;			} else {				err = -EOPNOTSUPP;				goto get_put_fail;			} 			if (param_item->driverinit_value_valid) {				default_value[i] = param_item->driverinit_default;				default_value_set[i] = true;			}		} else {			ctx->cmode = i;			err = devlink_param_get(devlink, param, ctx, extack);			if (err)				goto get_put_fail; 			param_value[i] = ctx->val; 			err = devlink_param_get_default(devlink, param, ctx,							extack);			if (!err) {				default_value[i] = ctx->val;				default_value_set[i] = true;			} else if (err != -EOPNOTSUPP) {				goto get_put_fail;			}		}		param_value_set[i] = true;	} 	err = -EMSGSIZE;	hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);	if (!hdr)		goto get_put_fail; 	if (devlink_nl_put_handle(msg, devlink))		goto genlmsg_cancel; 	if (cmd == DEVLINK_CMD_PORT_PARAM_GET ||	    cmd == DEVLINK_CMD_PORT_PARAM_NEW ||	    cmd == DEVLINK_CMD_PORT_PARAM_DEL)		if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, port_index))			goto genlmsg_cancel; 	param_attr = nla_nest_start_noflag(msg, DEVLINK_ATTR_PARAM);	if (!param_attr)		goto genlmsg_cancel;	if (nla_put_string(msg, DEVLINK_ATTR_PARAM_NAME, param->name))		goto param_nest_cancel;	if (param->generic && nla_put_flag(msg, DEVLINK_ATTR_PARAM_GENERIC))		goto param_nest_cancel;	if (nla_put_u8(msg, DEVLINK_ATTR_PARAM_TYPE, param->type))		goto param_nest_cancel; 	param_values_list = nla_nest_start_noflag(msg,						  DEVLINK_ATTR_PARAM_VALUES_LIST);	if (!param_values_list)		goto param_nest_cancel; 	for (i = 0; i <= DEVLINK_PARAM_CMODE_MAX; i++) {		if (!param_value_set[i])			continue;		err = devlink_nl_param_value_fill_one(msg, param->type,						      i, &param_value[i],						      &default_value[i],						      default_value_set[i]);		if (err)			goto values_list_nest_cancel;	} 	nla_nest_end(msg, param_values_list);	nla_nest_end(msg, param_attr);	genlmsg_end(msg, hdr);	kfree(default_value);	kfree(param_value);	kfree(ctx);	return 0; values_list_nest_cancel:	nla_nest_end(msg, param_values_list);param_nest_cancel:	nla_nest_cancel(msg, param_attr);genlmsg_cancel:	genlmsg_cancel(msg, hdr);get_put_fail:	kfree(default_value);	kfree(param_value);	kfree(ctx);	return err;} static void devlink_param_notify(struct devlink *devlink,				 unsigned int port_index,				 struct devlink_param_item *param_item,				 enum devlink_command cmd){	struct sk_buff *msg;	int err; 	WARN_ON(cmd != DEVLINK_CMD_PARAM_NEW && cmd != DEVLINK_CMD_PARAM_DEL &&		cmd != DEVLINK_CMD_PORT_PARAM_NEW &&		cmd != DEVLINK_CMD_PORT_PARAM_DEL); 	/* devlink_notify_register() / devlink_notify_unregister()	 * will replay the notifications if the params are added/removed	 * outside of the lifetime of the instance.	 */	if (!devl_is_registered(devlink) || !devlink_nl_notify_need(devlink))		return; 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);	if (!msg)		return;	err = devlink_nl_param_fill(msg, devlink, port_index, param_item, cmd,				    0, 0, 0, NULL);	if (err) {		nlmsg_free(msg);		return;	} 	devlink_nl_notify_send(devlink, msg);} static void devlink_params_notify(struct devlink *devlink,				  enum devlink_command cmd){	struct devlink_param_item *param_item;	unsigned long param_id; 	xa_for_each(&devlink->params, param_id, param_item)		devlink_param_notify(devlink, 0, param_item, cmd);} void devlink_params_notify_register(struct devlink *devlink){	devlink_params_notify(devlink, DEVLINK_CMD_PARAM_NEW);} void devlink_params_notify_unregister(struct devlink *devlink){	devlink_params_notify(devlink, DEVLINK_CMD_PARAM_DEL);} static int devlink_nl_param_get_dump_one(struct sk_buff *msg,					 struct devlink *devlink,					 struct netlink_callback *cb,

Lines 1–500 of 1011.