Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016// 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, }, { .id = DEVLINK_PARAM_GENERIC_ID_MAX_SFS, .name = DEVLINK_PARAM_GENERIC_MAX_SFS_NAME, .type = DEVLINK_PARAM_GENERIC_MAX_SFS_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, ¶m->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 = kzalloc_objs(*default_value, DEVLINK_PARAM_CMODE_MAX + 1); if (!default_value) return -ENOMEM;
param_value = kzalloc_objs(*param_value, DEVLINK_PARAM_CMODE_MAX + 1); 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, ¶m_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, int flags){ struct devlink_nl_dump_state *state = devlink_dump_state(cb); struct devlink_param_item *param_item; unsigned long param_id; int err = 0;
xa_for_each_start(&devlink->params, param_id, param_item, state->idx) { err = devlink_nl_param_fill(msg, devlink, 0, param_item, DEVLINK_CMD_PARAM_GET, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, flags, cb->extack); if (err == -EOPNOTSUPP) { err = 0; } else if (err) { state->idx = param_id; break; } }
return err;}
int devlink_nl_param_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb){ return devlink_nl_dumpit(skb, cb, devlink_nl_param_get_dump_one);}
static intdevlink_param_type_get_from_info(struct genl_info *info, enum devlink_param_type *param_type){ if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_PARAM_TYPE)) return -EINVAL;
*param_type = nla_get_u8(info->attrs[DEVLINK_ATTR_PARAM_TYPE]);
return 0;}
static intdevlink_param_value_get_from_info(const struct devlink_param *param, struct genl_info *info, union devlink_param_value *value){ struct nlattr *param_data; int len, cnt, rem;
param_data = info->attrs[DEVLINK_ATTR_PARAM_VALUE_DATA];
if (param->type != DEVLINK_PARAM_TYPE_BOOL && !param_data) return -EINVAL;
switch (param->type) { case DEVLINK_PARAM_TYPE_U8: if (nla_len(param_data) != sizeof(u8)) return -EINVAL; value->vu8 = nla_get_u8(param_data); break; case DEVLINK_PARAM_TYPE_U16: if (nla_len(param_data) != sizeof(u16)) return -EINVAL; value->vu16 = nla_get_u16(param_data); break; case DEVLINK_PARAM_TYPE_U32: if (nla_len(param_data) != sizeof(u32)) return -EINVAL; value->vu32 = nla_get_u32(param_data); break; case DEVLINK_PARAM_TYPE_U64: if (nla_len(param_data) != sizeof(u64)) return -EINVAL; value->vu64 = nla_get_u64(param_data); break; case DEVLINK_PARAM_TYPE_STRING: len = strnlen(nla_data(param_data), nla_len(param_data)); if (len == nla_len(param_data) || len >= __DEVLINK_PARAM_MAX_STRING_VALUE) return -EINVAL; strcpy(value->vstr, nla_data(param_data)); break; case DEVLINK_PARAM_TYPE_BOOL: if (param_data && nla_len(param_data)) return -EINVAL; value->vbool = nla_get_flag(param_data); break;
case DEVLINK_PARAM_TYPE_U64_ARRAY: cnt = 0; nla_for_each_attr_type(param_data, DEVLINK_ATTR_PARAM_VALUE_DATA, genlmsg_data(info->genlhdr), genlmsg_len(info->genlhdr), rem) { if (cnt >= __DEVLINK_PARAM_MAX_ARRAY_SIZE) return -EMSGSIZE;
if ((nla_len(param_data) != sizeof(u64)) && (nla_len(param_data) != sizeof(u32))) { NL_SET_BAD_ATTR(info->extack, param_data); return -EINVAL; }
value->u64arr.val[cnt] = nla_get_uint(param_data); cnt++; }
value->u64arr.size = cnt; break; } return 0;}
static struct devlink_param_item *devlink_param_get_from_info(struct xarray *params, struct genl_info *info){ char *param_name;
if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_PARAM_NAME)) return NULL;
param_name = nla_data(info->attrs[DEVLINK_ATTR_PARAM_NAME]); return devlink_param_find_by_name(params, param_name);}
int devlink_nl_param_get_doit(struct sk_buff *skb, struct genl_info *info){ struct devlink *devlink = devlink_nl_ctx(info)->devlink; struct devlink_param_item *param_item; struct sk_buff *msg; int err;
param_item = devlink_param_get_from_info(&devlink->params, info); if (!param_item) return -EINVAL;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM;
err = devlink_nl_param_fill(msg, devlink, 0, param_item, DEVLINK_CMD_PARAM_GET, info->snd_portid, info->snd_seq, 0, info->extack); if (err) { nlmsg_free(msg); return err; }
return genlmsg_reply(msg, info);}
static int __devlink_nl_cmd_param_set_doit(struct devlink *devlink, unsigned int port_index, struct xarray *params, struct genl_info *info, enum devlink_command cmd){ enum devlink_param_type param_type; struct devlink_param_gset_ctx ctx; enum devlink_param_cmode cmode; struct devlink_param_item *param_item; const struct devlink_param *param; union devlink_param_value value; bool reset_default; int err = 0;
param_item = devlink_param_get_from_info(params, info); if (!param_item) return -EINVAL; param = param_item->param; err = devlink_param_type_get_from_info(info, ¶m_type); if (err) return err; if (param_type != param->type) return -EINVAL;
reset_default = info->attrs[DEVLINK_ATTR_PARAM_RESET_DEFAULT]; if (!reset_default) { err = devlink_param_value_get_from_info(param, info, &value); if (err) return err; if (param->validate) { err = param->validate(devlink, param->id, &value, info->extack); if (err) return err; } }
if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_PARAM_VALUE_CMODE)) return -EINVAL; cmode = nla_get_u8(info->attrs[DEVLINK_ATTR_PARAM_VALUE_CMODE]); if (!devlink_param_cmode_is_supported(param, cmode)) return -EOPNOTSUPP;
if (cmode == DEVLINK_PARAM_CMODE_DRIVERINIT) { if (reset_default) { if (!param_item->driverinit_value_valid) { NL_SET_ERR_MSG(info->extack, "Default value not available"); return -EOPNOTSUPP; } value = param_item->driverinit_default; }
param_item->driverinit_value_new = value; param_item->driverinit_value_new_valid = true; } else { if (!param->set) return -EOPNOTSUPP; ctx.val = value; ctx.cmode = cmode; if (reset_default) err = devlink_param_reset_default(devlink, param, cmode, info->extack); else err = devlink_param_set(devlink, param, &ctx, info->extack); if (err) return err; }
devlink_param_notify(devlink, port_index, param_item, cmd); return 0;}
int devlink_nl_param_set_doit(struct sk_buff *skb, struct genl_info *info){ struct devlink *devlink = devlink_nl_ctx(info)->devlink;
return __devlink_nl_cmd_param_set_doit(devlink, 0, &devlink->params, info, DEVLINK_CMD_PARAM_NEW);}
int devlink_nl_port_param_get_dumpit(struct sk_buff *msg, struct netlink_callback *cb){ NL_SET_ERR_MSG(cb->extack, "Port params are not supported"); return msg->len;}
int devlink_nl_port_param_get_doit(struct sk_buff *skb, struct genl_info *info){ NL_SET_ERR_MSG(info->extack, "Port params are not supported"); return -EINVAL;}
int devlink_nl_port_param_set_doit(struct sk_buff *skb, struct genl_info *info){ NL_SET_ERR_MSG(info->extack, "Port params are not supported"); return -EINVAL;}
static int devlink_param_verify(const struct devlink_param *param){ if (!param || !param->name || !param->supported_cmodes) return -EINVAL; if (param->generic) return devlink_param_generic_verify(param); else return devlink_param_driver_verify(param);}
static int devlink_param_register(struct devlink *devlink, const struct devlink_param *param){ struct devlink_param_item *param_item; int err;
WARN_ON(devlink_param_verify(param)); WARN_ON(devlink_param_find_by_name(&devlink->params, param->name));
if (param->supported_cmodes == BIT(DEVLINK_PARAM_CMODE_DRIVERINIT)) WARN_ON(param->get || param->set); else WARN_ON(!param->get || !param->set);
param_item = kzalloc_obj(*param_item); if (!param_item) return -ENOMEM;
param_item->param = param;
err = xa_insert(&devlink->params, param->id, param_item, GFP_KERNEL); if (err) goto err_xa_insert;
devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_NEW); return 0;
err_xa_insert: kfree(param_item); return err;}
static void devlink_param_unregister(struct devlink *devlink, const struct devlink_param *param){ struct devlink_param_item *param_item;
param_item = devlink_param_find_by_id(&devlink->params, param->id); if (WARN_ON(!param_item)) return; devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_DEL); xa_erase(&devlink->params, param->id); kfree(param_item);}
/** * devl_params_register - register configuration parameters * * @devlink: devlink * @params: configuration parameters array * @params_count: number of parameters provided * * Register the configuration parameters supported by the driver. */int devl_params_register(struct devlink *devlink, const struct devlink_param *params, size_t params_count){ const struct devlink_param *param = params; int i, err;
lockdep_assert_held(&devlink->lock);
for (i = 0; i < params_count; i++, param++) { err = devlink_param_register(devlink, param); if (err) goto rollback; } return 0;
rollback: if (!i) return err;
for (param--; i > 0; i--, param--) devlink_param_unregister(devlink, param); return err;}EXPORT_SYMBOL_GPL(devl_params_register);
int devlink_params_register(struct devlink *devlink, const struct devlink_param *params, size_t params_count){ int err;
devl_lock(devlink); err = devl_params_register(devlink, params, params_count); devl_unlock(devlink); return err;}EXPORT_SYMBOL_GPL(devlink_params_register);
/** * devl_params_unregister - unregister configuration parameters * @devlink: devlink * @params: configuration parameters to unregister * @params_count: number of parameters provided */void devl_params_unregister(struct devlink *devlink, const struct devlink_param *params, size_t params_count){ const struct devlink_param *param = params; int i;
lockdep_assert_held(&devlink->lock);
for (i = 0; i < params_count; i++, param++) devlink_param_unregister(devlink, param);}EXPORT_SYMBOL_GPL(devl_params_unregister);
void devlink_params_unregister(struct devlink *devlink, const struct devlink_param *params, size_t params_count){ devl_lock(devlink); devl_params_unregister(devlink, params, params_count); devl_unlock(devlink);}EXPORT_SYMBOL_GPL(devlink_params_unregister);
/** * devl_param_driverinit_value_get - get configuration parameter * value for driver initializing * * @devlink: devlink * @param_id: parameter ID * @val: pointer to store the value of parameter in driverinit * configuration mode * * This function should be used by the driver to get driverinit * configuration for initialization after reload command. * * Note that lockless call of this function relies on the * driver to maintain following basic sane behavior: * 1) Driver ensures a call to this function cannot race with * registering/unregistering the parameter with the same parameter ID. * 2) Driver ensures a call to this function cannot race with * devl_param_driverinit_value_set() call with the same parameter ID. * 3) Driver ensures a call to this function cannot race with * reload operation. * If the driver is not able to comply, it has to take the devlink->lock * while calling this. */int devl_param_driverinit_value_get(struct devlink *devlink, u32 param_id, union devlink_param_value *val){ struct devlink_param_item *param_item;
if (WARN_ON(!devlink_reload_supported(devlink->ops))) return -EOPNOTSUPP;
param_item = devlink_param_find_by_id(&devlink->params, param_id); if (!param_item) return -EINVAL;
if (!param_item->driverinit_value_valid) return -EOPNOTSUPP;
if (WARN_ON(!devlink_param_cmode_is_supported(param_item->param, DEVLINK_PARAM_CMODE_DRIVERINIT))) return -EOPNOTSUPP;
*val = param_item->driverinit_value;
return 0;}EXPORT_SYMBOL_GPL(devl_param_driverinit_value_get);
/** * devl_param_driverinit_value_set - set value of configuration * parameter for driverinit * configuration mode * * @devlink: devlink * @param_id: parameter ID * @init_val: value of parameter to set for driverinit configuration mode * * This function should be used by the driver to set driverinit * configuration mode default value. */void devl_param_driverinit_value_set(struct devlink *devlink, u32 param_id, union devlink_param_value *init_val){ struct devlink_param_item *param_item;
devl_assert_locked(devlink);
param_item = devlink_param_find_by_id(&devlink->params, param_id); if (WARN_ON(!param_item)) return;
if (WARN_ON(!devlink_param_cmode_is_supported(param_item->param, DEVLINK_PARAM_CMODE_DRIVERINIT))) return;
param_item->driverinit_value = *init_val; param_item->driverinit_value_valid = true; param_item->driverinit_default = *init_val;
devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_NEW);}EXPORT_SYMBOL_GPL(devl_param_driverinit_value_set);
void devlink_params_driverinit_load_new(struct devlink *devlink){ struct devlink_param_item *param_item; unsigned long param_id;
xa_for_each(&devlink->params, param_id, param_item) { if (!devlink_param_cmode_is_supported(param_item->param, DEVLINK_PARAM_CMODE_DRIVERINIT) || !param_item->driverinit_value_new_valid) continue; param_item->driverinit_value = param_item->driverinit_value_new; param_item->driverinit_value_valid = true; param_item->driverinit_value_new_valid = false; }}
/** * devl_param_value_changed - notify devlink on a parameter's value * change. Should be called by the driver * right after the change. * * @devlink: devlink * @param_id: parameter ID * * This function should be used by the driver to notify devlink on value * change, excluding driverinit configuration mode. * For driverinit configuration mode driver should use the function */void devl_param_value_changed(struct devlink *devlink, u32 param_id){ struct devlink_param_item *param_item;
param_item = devlink_param_find_by_id(&devlink->params, param_id); WARN_ON(!param_item);
devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_NEW);}EXPORT_SYMBOL_GPL(devl_param_value_changed);