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