162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2016 Mellanox Technologies. All rights reserved. 462306a36Sopenharmony_ci * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "devl_internal.h" 862306a36Sopenharmony_ci 962306a36Sopenharmony_cistatic struct devlink_linecard * 1062306a36Sopenharmony_cidevlink_linecard_get_by_index(struct devlink *devlink, 1162306a36Sopenharmony_ci unsigned int linecard_index) 1262306a36Sopenharmony_ci{ 1362306a36Sopenharmony_ci struct devlink_linecard *devlink_linecard; 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci list_for_each_entry(devlink_linecard, &devlink->linecard_list, list) { 1662306a36Sopenharmony_ci if (devlink_linecard->index == linecard_index) 1762306a36Sopenharmony_ci return devlink_linecard; 1862306a36Sopenharmony_ci } 1962306a36Sopenharmony_ci return NULL; 2062306a36Sopenharmony_ci} 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic bool devlink_linecard_index_exists(struct devlink *devlink, 2362306a36Sopenharmony_ci unsigned int linecard_index) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci return devlink_linecard_get_by_index(devlink, linecard_index); 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic struct devlink_linecard * 2962306a36Sopenharmony_cidevlink_linecard_get_from_attrs(struct devlink *devlink, struct nlattr **attrs) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci if (attrs[DEVLINK_ATTR_LINECARD_INDEX]) { 3262306a36Sopenharmony_ci u32 linecard_index = nla_get_u32(attrs[DEVLINK_ATTR_LINECARD_INDEX]); 3362306a36Sopenharmony_ci struct devlink_linecard *linecard; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci linecard = devlink_linecard_get_by_index(devlink, linecard_index); 3662306a36Sopenharmony_ci if (!linecard) 3762306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 3862306a36Sopenharmony_ci return linecard; 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic struct devlink_linecard * 4462306a36Sopenharmony_cidevlink_linecard_get_from_info(struct devlink *devlink, struct genl_info *info) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci return devlink_linecard_get_from_attrs(devlink, info->attrs); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic int devlink_nl_put_nested_handle(struct sk_buff *msg, struct devlink *devlink) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct nlattr *nested_attr; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci nested_attr = nla_nest_start(msg, DEVLINK_ATTR_NESTED_DEVLINK); 5462306a36Sopenharmony_ci if (!nested_attr) 5562306a36Sopenharmony_ci return -EMSGSIZE; 5662306a36Sopenharmony_ci if (devlink_nl_put_handle(msg, devlink)) 5762306a36Sopenharmony_ci goto nla_put_failure; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci nla_nest_end(msg, nested_attr); 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cinla_put_failure: 6362306a36Sopenharmony_ci nla_nest_cancel(msg, nested_attr); 6462306a36Sopenharmony_ci return -EMSGSIZE; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistruct devlink_linecard_type { 6862306a36Sopenharmony_ci const char *type; 6962306a36Sopenharmony_ci const void *priv; 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int devlink_nl_linecard_fill(struct sk_buff *msg, 7362306a36Sopenharmony_ci struct devlink *devlink, 7462306a36Sopenharmony_ci struct devlink_linecard *linecard, 7562306a36Sopenharmony_ci enum devlink_command cmd, u32 portid, 7662306a36Sopenharmony_ci u32 seq, int flags, 7762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct devlink_linecard_type *linecard_type; 8062306a36Sopenharmony_ci struct nlattr *attr; 8162306a36Sopenharmony_ci void *hdr; 8262306a36Sopenharmony_ci int i; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd); 8562306a36Sopenharmony_ci if (!hdr) 8662306a36Sopenharmony_ci return -EMSGSIZE; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (devlink_nl_put_handle(msg, devlink)) 8962306a36Sopenharmony_ci goto nla_put_failure; 9062306a36Sopenharmony_ci if (nla_put_u32(msg, DEVLINK_ATTR_LINECARD_INDEX, linecard->index)) 9162306a36Sopenharmony_ci goto nla_put_failure; 9262306a36Sopenharmony_ci if (nla_put_u8(msg, DEVLINK_ATTR_LINECARD_STATE, linecard->state)) 9362306a36Sopenharmony_ci goto nla_put_failure; 9462306a36Sopenharmony_ci if (linecard->type && 9562306a36Sopenharmony_ci nla_put_string(msg, DEVLINK_ATTR_LINECARD_TYPE, linecard->type)) 9662306a36Sopenharmony_ci goto nla_put_failure; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (linecard->types_count) { 9962306a36Sopenharmony_ci attr = nla_nest_start(msg, 10062306a36Sopenharmony_ci DEVLINK_ATTR_LINECARD_SUPPORTED_TYPES); 10162306a36Sopenharmony_ci if (!attr) 10262306a36Sopenharmony_ci goto nla_put_failure; 10362306a36Sopenharmony_ci for (i = 0; i < linecard->types_count; i++) { 10462306a36Sopenharmony_ci linecard_type = &linecard->types[i]; 10562306a36Sopenharmony_ci if (nla_put_string(msg, DEVLINK_ATTR_LINECARD_TYPE, 10662306a36Sopenharmony_ci linecard_type->type)) { 10762306a36Sopenharmony_ci nla_nest_cancel(msg, attr); 10862306a36Sopenharmony_ci goto nla_put_failure; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci nla_nest_end(msg, attr); 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (linecard->nested_devlink && 11562306a36Sopenharmony_ci devlink_nl_put_nested_handle(msg, linecard->nested_devlink)) 11662306a36Sopenharmony_ci goto nla_put_failure; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci genlmsg_end(msg, hdr); 11962306a36Sopenharmony_ci return 0; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cinla_put_failure: 12262306a36Sopenharmony_ci genlmsg_cancel(msg, hdr); 12362306a36Sopenharmony_ci return -EMSGSIZE; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic void devlink_linecard_notify(struct devlink_linecard *linecard, 12762306a36Sopenharmony_ci enum devlink_command cmd) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct devlink *devlink = linecard->devlink; 13062306a36Sopenharmony_ci struct sk_buff *msg; 13162306a36Sopenharmony_ci int err; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci WARN_ON(cmd != DEVLINK_CMD_LINECARD_NEW && 13462306a36Sopenharmony_ci cmd != DEVLINK_CMD_LINECARD_DEL); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (!xa_get_mark(&devlinks, devlink->index, DEVLINK_REGISTERED)) 13762306a36Sopenharmony_ci return; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 14062306a36Sopenharmony_ci if (!msg) 14162306a36Sopenharmony_ci return; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci err = devlink_nl_linecard_fill(msg, devlink, linecard, cmd, 0, 0, 0, 14462306a36Sopenharmony_ci NULL); 14562306a36Sopenharmony_ci if (err) { 14662306a36Sopenharmony_ci nlmsg_free(msg); 14762306a36Sopenharmony_ci return; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink), 15162306a36Sopenharmony_ci msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_civoid devlink_linecards_notify_register(struct devlink *devlink) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct devlink_linecard *linecard; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci list_for_each_entry(linecard, &devlink->linecard_list, list) 15962306a36Sopenharmony_ci devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_civoid devlink_linecards_notify_unregister(struct devlink *devlink) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci struct devlink_linecard *linecard; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci list_for_each_entry_reverse(linecard, &devlink->linecard_list, list) 16762306a36Sopenharmony_ci devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ciint devlink_nl_linecard_get_doit(struct sk_buff *skb, struct genl_info *info) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct devlink *devlink = info->user_ptr[0]; 17362306a36Sopenharmony_ci struct devlink_linecard *linecard; 17462306a36Sopenharmony_ci struct sk_buff *msg; 17562306a36Sopenharmony_ci int err; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci linecard = devlink_linecard_get_from_info(devlink, info); 17862306a36Sopenharmony_ci if (IS_ERR(linecard)) 17962306a36Sopenharmony_ci return PTR_ERR(linecard); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 18262306a36Sopenharmony_ci if (!msg) 18362306a36Sopenharmony_ci return -ENOMEM; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci mutex_lock(&linecard->state_lock); 18662306a36Sopenharmony_ci err = devlink_nl_linecard_fill(msg, devlink, linecard, 18762306a36Sopenharmony_ci DEVLINK_CMD_LINECARD_NEW, 18862306a36Sopenharmony_ci info->snd_portid, info->snd_seq, 0, 18962306a36Sopenharmony_ci info->extack); 19062306a36Sopenharmony_ci mutex_unlock(&linecard->state_lock); 19162306a36Sopenharmony_ci if (err) { 19262306a36Sopenharmony_ci nlmsg_free(msg); 19362306a36Sopenharmony_ci return err; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return genlmsg_reply(msg, info); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic int devlink_nl_linecard_get_dump_one(struct sk_buff *msg, 20062306a36Sopenharmony_ci struct devlink *devlink, 20162306a36Sopenharmony_ci struct netlink_callback *cb, 20262306a36Sopenharmony_ci int flags) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct devlink_nl_dump_state *state = devlink_dump_state(cb); 20562306a36Sopenharmony_ci struct devlink_linecard *linecard; 20662306a36Sopenharmony_ci int idx = 0; 20762306a36Sopenharmony_ci int err = 0; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci list_for_each_entry(linecard, &devlink->linecard_list, list) { 21062306a36Sopenharmony_ci if (idx < state->idx) { 21162306a36Sopenharmony_ci idx++; 21262306a36Sopenharmony_ci continue; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci mutex_lock(&linecard->state_lock); 21562306a36Sopenharmony_ci err = devlink_nl_linecard_fill(msg, devlink, linecard, 21662306a36Sopenharmony_ci DEVLINK_CMD_LINECARD_NEW, 21762306a36Sopenharmony_ci NETLINK_CB(cb->skb).portid, 21862306a36Sopenharmony_ci cb->nlh->nlmsg_seq, flags, 21962306a36Sopenharmony_ci cb->extack); 22062306a36Sopenharmony_ci mutex_unlock(&linecard->state_lock); 22162306a36Sopenharmony_ci if (err) { 22262306a36Sopenharmony_ci state->idx = idx; 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci idx++; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci return err; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ciint devlink_nl_linecard_get_dumpit(struct sk_buff *skb, 23262306a36Sopenharmony_ci struct netlink_callback *cb) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci return devlink_nl_dumpit(skb, cb, devlink_nl_linecard_get_dump_one); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic struct devlink_linecard_type * 23862306a36Sopenharmony_cidevlink_linecard_type_lookup(struct devlink_linecard *linecard, 23962306a36Sopenharmony_ci const char *type) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct devlink_linecard_type *linecard_type; 24262306a36Sopenharmony_ci int i; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci for (i = 0; i < linecard->types_count; i++) { 24562306a36Sopenharmony_ci linecard_type = &linecard->types[i]; 24662306a36Sopenharmony_ci if (!strcmp(type, linecard_type->type)) 24762306a36Sopenharmony_ci return linecard_type; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci return NULL; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic int devlink_linecard_type_set(struct devlink_linecard *linecard, 25362306a36Sopenharmony_ci const char *type, 25462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci const struct devlink_linecard_ops *ops = linecard->ops; 25762306a36Sopenharmony_ci struct devlink_linecard_type *linecard_type; 25862306a36Sopenharmony_ci int err; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci mutex_lock(&linecard->state_lock); 26162306a36Sopenharmony_ci if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING) { 26262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Line card is currently being provisioned"); 26362306a36Sopenharmony_ci err = -EBUSY; 26462306a36Sopenharmony_ci goto out; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONING) { 26762306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Line card is currently being unprovisioned"); 26862306a36Sopenharmony_ci err = -EBUSY; 26962306a36Sopenharmony_ci goto out; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci linecard_type = devlink_linecard_type_lookup(linecard, type); 27362306a36Sopenharmony_ci if (!linecard_type) { 27462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Unsupported line card type provided"); 27562306a36Sopenharmony_ci err = -EINVAL; 27662306a36Sopenharmony_ci goto out; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (linecard->state != DEVLINK_LINECARD_STATE_UNPROVISIONED && 28062306a36Sopenharmony_ci linecard->state != DEVLINK_LINECARD_STATE_PROVISIONING_FAILED) { 28162306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Line card already provisioned"); 28262306a36Sopenharmony_ci err = -EBUSY; 28362306a36Sopenharmony_ci /* Check if the line card is provisioned in the same 28462306a36Sopenharmony_ci * way the user asks. In case it is, make the operation 28562306a36Sopenharmony_ci * to return success. 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_ci if (ops->same_provision && 28862306a36Sopenharmony_ci ops->same_provision(linecard, linecard->priv, 28962306a36Sopenharmony_ci linecard_type->type, 29062306a36Sopenharmony_ci linecard_type->priv)) 29162306a36Sopenharmony_ci err = 0; 29262306a36Sopenharmony_ci goto out; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING; 29662306a36Sopenharmony_ci linecard->type = linecard_type->type; 29762306a36Sopenharmony_ci devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 29862306a36Sopenharmony_ci mutex_unlock(&linecard->state_lock); 29962306a36Sopenharmony_ci err = ops->provision(linecard, linecard->priv, linecard_type->type, 30062306a36Sopenharmony_ci linecard_type->priv, extack); 30162306a36Sopenharmony_ci if (err) { 30262306a36Sopenharmony_ci /* Provisioning failed. Assume the linecard is unprovisioned 30362306a36Sopenharmony_ci * for future operations. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_ci mutex_lock(&linecard->state_lock); 30662306a36Sopenharmony_ci linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED; 30762306a36Sopenharmony_ci linecard->type = NULL; 30862306a36Sopenharmony_ci devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 30962306a36Sopenharmony_ci mutex_unlock(&linecard->state_lock); 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci return err; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ciout: 31462306a36Sopenharmony_ci mutex_unlock(&linecard->state_lock); 31562306a36Sopenharmony_ci return err; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic int devlink_linecard_type_unset(struct devlink_linecard *linecard, 31962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci int err; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci mutex_lock(&linecard->state_lock); 32462306a36Sopenharmony_ci if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING) { 32562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Line card is currently being provisioned"); 32662306a36Sopenharmony_ci err = -EBUSY; 32762306a36Sopenharmony_ci goto out; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONING) { 33062306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Line card is currently being unprovisioned"); 33162306a36Sopenharmony_ci err = -EBUSY; 33262306a36Sopenharmony_ci goto out; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING_FAILED) { 33562306a36Sopenharmony_ci linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED; 33662306a36Sopenharmony_ci linecard->type = NULL; 33762306a36Sopenharmony_ci devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 33862306a36Sopenharmony_ci err = 0; 33962306a36Sopenharmony_ci goto out; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONED) { 34362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Line card is not provisioned"); 34462306a36Sopenharmony_ci err = 0; 34562306a36Sopenharmony_ci goto out; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONING; 34862306a36Sopenharmony_ci devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 34962306a36Sopenharmony_ci mutex_unlock(&linecard->state_lock); 35062306a36Sopenharmony_ci err = linecard->ops->unprovision(linecard, linecard->priv, 35162306a36Sopenharmony_ci extack); 35262306a36Sopenharmony_ci if (err) { 35362306a36Sopenharmony_ci /* Unprovisioning failed. Assume the linecard is unprovisioned 35462306a36Sopenharmony_ci * for future operations. 35562306a36Sopenharmony_ci */ 35662306a36Sopenharmony_ci mutex_lock(&linecard->state_lock); 35762306a36Sopenharmony_ci linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED; 35862306a36Sopenharmony_ci linecard->type = NULL; 35962306a36Sopenharmony_ci devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 36062306a36Sopenharmony_ci mutex_unlock(&linecard->state_lock); 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci return err; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ciout: 36562306a36Sopenharmony_ci mutex_unlock(&linecard->state_lock); 36662306a36Sopenharmony_ci return err; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ciint devlink_nl_cmd_linecard_set_doit(struct sk_buff *skb, 37062306a36Sopenharmony_ci struct genl_info *info) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct netlink_ext_ack *extack = info->extack; 37362306a36Sopenharmony_ci struct devlink *devlink = info->user_ptr[0]; 37462306a36Sopenharmony_ci struct devlink_linecard *linecard; 37562306a36Sopenharmony_ci int err; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci linecard = devlink_linecard_get_from_info(devlink, info); 37862306a36Sopenharmony_ci if (IS_ERR(linecard)) 37962306a36Sopenharmony_ci return PTR_ERR(linecard); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (info->attrs[DEVLINK_ATTR_LINECARD_TYPE]) { 38262306a36Sopenharmony_ci const char *type; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci type = nla_data(info->attrs[DEVLINK_ATTR_LINECARD_TYPE]); 38562306a36Sopenharmony_ci if (strcmp(type, "")) { 38662306a36Sopenharmony_ci err = devlink_linecard_type_set(linecard, type, extack); 38762306a36Sopenharmony_ci if (err) 38862306a36Sopenharmony_ci return err; 38962306a36Sopenharmony_ci } else { 39062306a36Sopenharmony_ci err = devlink_linecard_type_unset(linecard, extack); 39162306a36Sopenharmony_ci if (err) 39262306a36Sopenharmony_ci return err; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci return 0; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic int devlink_linecard_types_init(struct devlink_linecard *linecard) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci struct devlink_linecard_type *linecard_type; 40262306a36Sopenharmony_ci unsigned int count; 40362306a36Sopenharmony_ci int i; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci count = linecard->ops->types_count(linecard, linecard->priv); 40662306a36Sopenharmony_ci linecard->types = kmalloc_array(count, sizeof(*linecard_type), 40762306a36Sopenharmony_ci GFP_KERNEL); 40862306a36Sopenharmony_ci if (!linecard->types) 40962306a36Sopenharmony_ci return -ENOMEM; 41062306a36Sopenharmony_ci linecard->types_count = count; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci for (i = 0; i < count; i++) { 41362306a36Sopenharmony_ci linecard_type = &linecard->types[i]; 41462306a36Sopenharmony_ci linecard->ops->types_get(linecard, linecard->priv, i, 41562306a36Sopenharmony_ci &linecard_type->type, 41662306a36Sopenharmony_ci &linecard_type->priv); 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci return 0; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic void devlink_linecard_types_fini(struct devlink_linecard *linecard) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci kfree(linecard->types); 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci/** 42762306a36Sopenharmony_ci * devl_linecard_create - Create devlink linecard 42862306a36Sopenharmony_ci * 42962306a36Sopenharmony_ci * @devlink: devlink 43062306a36Sopenharmony_ci * @linecard_index: driver-specific numerical identifier of the linecard 43162306a36Sopenharmony_ci * @ops: linecards ops 43262306a36Sopenharmony_ci * @priv: user priv pointer 43362306a36Sopenharmony_ci * 43462306a36Sopenharmony_ci * Create devlink linecard instance with provided linecard index. 43562306a36Sopenharmony_ci * Caller can use any indexing, even hw-related one. 43662306a36Sopenharmony_ci * 43762306a36Sopenharmony_ci * Return: Line card structure or an ERR_PTR() encoded error code. 43862306a36Sopenharmony_ci */ 43962306a36Sopenharmony_cistruct devlink_linecard * 44062306a36Sopenharmony_cidevl_linecard_create(struct devlink *devlink, unsigned int linecard_index, 44162306a36Sopenharmony_ci const struct devlink_linecard_ops *ops, void *priv) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci struct devlink_linecard *linecard; 44462306a36Sopenharmony_ci int err; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (WARN_ON(!ops || !ops->provision || !ops->unprovision || 44762306a36Sopenharmony_ci !ops->types_count || !ops->types_get)) 44862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (devlink_linecard_index_exists(devlink, linecard_index)) 45162306a36Sopenharmony_ci return ERR_PTR(-EEXIST); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci linecard = kzalloc(sizeof(*linecard), GFP_KERNEL); 45462306a36Sopenharmony_ci if (!linecard) 45562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci linecard->devlink = devlink; 45862306a36Sopenharmony_ci linecard->index = linecard_index; 45962306a36Sopenharmony_ci linecard->ops = ops; 46062306a36Sopenharmony_ci linecard->priv = priv; 46162306a36Sopenharmony_ci linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED; 46262306a36Sopenharmony_ci mutex_init(&linecard->state_lock); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci err = devlink_linecard_types_init(linecard); 46562306a36Sopenharmony_ci if (err) { 46662306a36Sopenharmony_ci mutex_destroy(&linecard->state_lock); 46762306a36Sopenharmony_ci kfree(linecard); 46862306a36Sopenharmony_ci return ERR_PTR(err); 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci list_add_tail(&linecard->list, &devlink->linecard_list); 47262306a36Sopenharmony_ci devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 47362306a36Sopenharmony_ci return linecard; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devl_linecard_create); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci/** 47862306a36Sopenharmony_ci * devl_linecard_destroy - Destroy devlink linecard 47962306a36Sopenharmony_ci * 48062306a36Sopenharmony_ci * @linecard: devlink linecard 48162306a36Sopenharmony_ci */ 48262306a36Sopenharmony_civoid devl_linecard_destroy(struct devlink_linecard *linecard) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL); 48562306a36Sopenharmony_ci list_del(&linecard->list); 48662306a36Sopenharmony_ci devlink_linecard_types_fini(linecard); 48762306a36Sopenharmony_ci mutex_destroy(&linecard->state_lock); 48862306a36Sopenharmony_ci kfree(linecard); 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devl_linecard_destroy); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci/** 49362306a36Sopenharmony_ci * devlink_linecard_provision_set - Set provisioning on linecard 49462306a36Sopenharmony_ci * 49562306a36Sopenharmony_ci * @linecard: devlink linecard 49662306a36Sopenharmony_ci * @type: linecard type 49762306a36Sopenharmony_ci * 49862306a36Sopenharmony_ci * This is either called directly from the provision() op call or 49962306a36Sopenharmony_ci * as a result of the provision() op call asynchronously. 50062306a36Sopenharmony_ci */ 50162306a36Sopenharmony_civoid devlink_linecard_provision_set(struct devlink_linecard *linecard, 50262306a36Sopenharmony_ci const char *type) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci mutex_lock(&linecard->state_lock); 50562306a36Sopenharmony_ci WARN_ON(linecard->type && strcmp(linecard->type, type)); 50662306a36Sopenharmony_ci linecard->state = DEVLINK_LINECARD_STATE_PROVISIONED; 50762306a36Sopenharmony_ci linecard->type = type; 50862306a36Sopenharmony_ci devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 50962306a36Sopenharmony_ci mutex_unlock(&linecard->state_lock); 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_linecard_provision_set); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci/** 51462306a36Sopenharmony_ci * devlink_linecard_provision_clear - Clear provisioning on linecard 51562306a36Sopenharmony_ci * 51662306a36Sopenharmony_ci * @linecard: devlink linecard 51762306a36Sopenharmony_ci * 51862306a36Sopenharmony_ci * This is either called directly from the unprovision() op call or 51962306a36Sopenharmony_ci * as a result of the unprovision() op call asynchronously. 52062306a36Sopenharmony_ci */ 52162306a36Sopenharmony_civoid devlink_linecard_provision_clear(struct devlink_linecard *linecard) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci mutex_lock(&linecard->state_lock); 52462306a36Sopenharmony_ci WARN_ON(linecard->nested_devlink); 52562306a36Sopenharmony_ci linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED; 52662306a36Sopenharmony_ci linecard->type = NULL; 52762306a36Sopenharmony_ci devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 52862306a36Sopenharmony_ci mutex_unlock(&linecard->state_lock); 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_linecard_provision_clear); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci/** 53362306a36Sopenharmony_ci * devlink_linecard_provision_fail - Fail provisioning on linecard 53462306a36Sopenharmony_ci * 53562306a36Sopenharmony_ci * @linecard: devlink linecard 53662306a36Sopenharmony_ci * 53762306a36Sopenharmony_ci * This is either called directly from the provision() op call or 53862306a36Sopenharmony_ci * as a result of the provision() op call asynchronously. 53962306a36Sopenharmony_ci */ 54062306a36Sopenharmony_civoid devlink_linecard_provision_fail(struct devlink_linecard *linecard) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci mutex_lock(&linecard->state_lock); 54362306a36Sopenharmony_ci WARN_ON(linecard->nested_devlink); 54462306a36Sopenharmony_ci linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING_FAILED; 54562306a36Sopenharmony_ci devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 54662306a36Sopenharmony_ci mutex_unlock(&linecard->state_lock); 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_linecard_provision_fail); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci/** 55162306a36Sopenharmony_ci * devlink_linecard_activate - Set linecard active 55262306a36Sopenharmony_ci * 55362306a36Sopenharmony_ci * @linecard: devlink linecard 55462306a36Sopenharmony_ci */ 55562306a36Sopenharmony_civoid devlink_linecard_activate(struct devlink_linecard *linecard) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci mutex_lock(&linecard->state_lock); 55862306a36Sopenharmony_ci WARN_ON(linecard->state != DEVLINK_LINECARD_STATE_PROVISIONED); 55962306a36Sopenharmony_ci linecard->state = DEVLINK_LINECARD_STATE_ACTIVE; 56062306a36Sopenharmony_ci devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 56162306a36Sopenharmony_ci mutex_unlock(&linecard->state_lock); 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_linecard_activate); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci/** 56662306a36Sopenharmony_ci * devlink_linecard_deactivate - Set linecard inactive 56762306a36Sopenharmony_ci * 56862306a36Sopenharmony_ci * @linecard: devlink linecard 56962306a36Sopenharmony_ci */ 57062306a36Sopenharmony_civoid devlink_linecard_deactivate(struct devlink_linecard *linecard) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci mutex_lock(&linecard->state_lock); 57362306a36Sopenharmony_ci switch (linecard->state) { 57462306a36Sopenharmony_ci case DEVLINK_LINECARD_STATE_ACTIVE: 57562306a36Sopenharmony_ci linecard->state = DEVLINK_LINECARD_STATE_PROVISIONED; 57662306a36Sopenharmony_ci devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 57762306a36Sopenharmony_ci break; 57862306a36Sopenharmony_ci case DEVLINK_LINECARD_STATE_UNPROVISIONING: 57962306a36Sopenharmony_ci /* Line card is being deactivated as part 58062306a36Sopenharmony_ci * of unprovisioning flow. 58162306a36Sopenharmony_ci */ 58262306a36Sopenharmony_ci break; 58362306a36Sopenharmony_ci default: 58462306a36Sopenharmony_ci WARN_ON(1); 58562306a36Sopenharmony_ci break; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci mutex_unlock(&linecard->state_lock); 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_linecard_deactivate); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci/** 59262306a36Sopenharmony_ci * devlink_linecard_nested_dl_set - Attach/detach nested devlink 59362306a36Sopenharmony_ci * instance to linecard. 59462306a36Sopenharmony_ci * 59562306a36Sopenharmony_ci * @linecard: devlink linecard 59662306a36Sopenharmony_ci * @nested_devlink: devlink instance to attach or NULL to detach 59762306a36Sopenharmony_ci */ 59862306a36Sopenharmony_civoid devlink_linecard_nested_dl_set(struct devlink_linecard *linecard, 59962306a36Sopenharmony_ci struct devlink *nested_devlink) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci mutex_lock(&linecard->state_lock); 60262306a36Sopenharmony_ci linecard->nested_devlink = nested_devlink; 60362306a36Sopenharmony_ci devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); 60462306a36Sopenharmony_ci mutex_unlock(&linecard->state_lock); 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_linecard_nested_dl_set); 607