devlink: implement line card provisioning

In order to be able to configure all needed stuff on a port/netdevice
of a line card without the line card being present, introduce line card
provisioning. Basically by setting a type, provisioning process will
start and driver is supposed to create a placeholder for instances
(ports/netdevices) for a line card type.

Allow the user to query the supported line card types over line card
get command. Then implement two netlink command SET to allow user to
set/unset the card type.

On the driver API side, add provision/unprovision ops and supported
types array to be advertised. Upon provision op call, the driver should
take care of creating the instances for the particular line card type.
Introduce provision_set/clear() functions to be called by the driver
once the provisioning/unprovisioning is done on its side. These helpers
are not to be called directly due to the async nature of provisioning.

Example:
$ devlink port # No ports are listed
$ devlink lc
pci/0000:01:00.0:
  lc 1 state unprovisioned
    supported_types:
       16x100G
  lc 2 state unprovisioned
    supported_types:
       16x100G
  lc 3 state unprovisioned
    supported_types:
       16x100G
  lc 4 state unprovisioned
    supported_types:
       16x100G
  lc 5 state unprovisioned
    supported_types:
       16x100G
  lc 6 state unprovisioned
    supported_types:
       16x100G
  lc 7 state unprovisioned
    supported_types:
       16x100G
  lc 8 state unprovisioned
    supported_types:
       16x100G

$ devlink lc set pci/0000:01:00.0 lc 8 type 16x100G
$ devlink lc show pci/0000:01:00.0 lc 8
pci/0000:01:00.0:
  lc 8 state active type 16x100G
    supported_types:
       16x100G
$ devlink port
pci/0000:01:00.0/0: type notset flavour cpu port 0 splittable false
pci/0000:01:00.0/53: type eth netdev enp1s0nl8p1 flavour physical lc 8 port 1 splittable true lanes 4
pci/0000:01:00.0/54: type eth netdev enp1s0nl8p2 flavour physical lc 8 port 2 splittable true lanes 4
pci/0000:01:00.0/55: type eth netdev enp1s0nl8p3 flavour physical lc 8 port 3 splittable true lanes 4
pci/0000:01:00.0/56: type eth netdev enp1s0nl8p4 flavour physical lc 8 port 4 splittable true lanes 4
pci/0000:01:00.0/57: type eth netdev enp1s0nl8p5 flavour physical lc 8 port 5 splittable true lanes 4
pci/0000:01:00.0/58: type eth netdev enp1s0nl8p6 flavour physical lc 8 port 6 splittable true lanes 4
pci/0000:01:00.0/59: type eth netdev enp1s0nl8p7 flavour physical lc 8 port 7 splittable true lanes 4
pci/0000:01:00.0/60: type eth netdev enp1s0nl8p8 flavour physical lc 8 port 8 splittable true lanes 4
pci/0000:01:00.0/61: type eth netdev enp1s0nl8p9 flavour physical lc 8 port 9 splittable true lanes 4
pci/0000:01:00.0/62: type eth netdev enp1s0nl8p10 flavour physical lc 8 port 10 splittable true lanes 4
pci/0000:01:00.0/63: type eth netdev enp1s0nl8p11 flavour physical lc 8 port 11 splittable true lanes 4
pci/0000:01:00.0/64: type eth netdev enp1s0nl8p12 flavour physical lc 8 port 12 splittable true lanes 4
pci/0000:01:00.0/125: type eth netdev enp1s0nl8p13 flavour physical lc 8 port 13 splittable true lanes 4
pci/0000:01:00.0/126: type eth netdev enp1s0nl8p14 flavour physical lc 8 port 14 splittable true lanes 4
pci/0000:01:00.0/127: type eth netdev enp1s0nl8p15 flavour physical lc 8 port 15 splittable true lanes 4
pci/0000:01:00.0/128: type eth netdev enp1s0nl8p16 flavour physical lc 8 port 16 splittable true lanes 4

$ devlink lc set pci/0000:01:00.0 lc 8 notype

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Jiri Pirko 2022-04-18 09:42:26 +03:00 коммит произвёл David S. Miller
Родитель c246f9b5fd
Коммит fcdc8ce23a
5 изменённых файлов: 497 добавлений и 5 удалений

Просмотреть файл

@ -0,0 +1,121 @@
.. SPDX-License-Identifier: GPL-2.0
=================
Devlink Line card
=================
Background
==========
The ``devlink-linecard`` mechanism is targeted for manipulation of
line cards that serve as a detachable PHY modules for modular switch
system. Following operations are provided:
* Get a list of supported line card types.
* Provision of a slot with specific line card type.
* Get and monitor of line card state and its change.
Line card according to the type may contain one or more gearboxes
to mux the lanes with certain speed to multiple ports with lanes
of different speed. Line card ensures N:M mapping between
the switch ASIC modules and physical front panel ports.
Overview
========
Each line card devlink object is created by device driver,
according to the physical line card slots available on the device.
Similar to splitter cable, where the device might have no way
of detection of the splitter cable geometry, the device
might not have a way to detect line card type. For that devices,
concept of provisioning is introduced. It allows the user to:
* Provision a line card slot with certain line card type
- Device driver would instruct the ASIC to prepare all
resources accordingly. The device driver would
create all instances, namely devlink port and netdevices
that reside on the line card, according to the line card type
* Manipulate of line card entities even without line card
being physically connected or powered-up
* Setup splitter cable on line card ports
- As on the ordinary ports, user may provision a splitter
cable of a certain type, without the need to
be physically connected to the port
* Configure devlink ports and netdevices
Netdevice carrier is decided as follows:
* Line card is not inserted or powered-down
- The carrier is always down
* Line card is inserted and powered up
- The carrier is decided as for ordinary port netdevice
Line card state
===============
The ``devlink-linecard`` mechanism supports the following line card states:
* ``unprovisioned``: Line card is not provisioned on the slot.
* ``unprovisioning``: Line card slot is currently being unprovisioned.
* ``provisioning``: Line card slot is currently in a process of being provisioned
with a line card type.
* ``provisioning_failed``: Provisioning was not successful.
* ``provisioned``: Line card slot is provisioned with a type.
The following diagram provides a general overview of ``devlink-linecard``
state transitions::
+-------------------------+
| |
+----------------------------------> unprovisioned |
| | |
| +--------|-------^--------+
| | |
| | |
| +--------v-------|--------+
| | |
| | provisioning |
| | |
| +------------|------------+
| |
| +-----------------------------+
| | |
| +------------v------------+ +------------v------------+
| | | | |
+----- provisioning_failed | | provisioned |
| | | | |
| +------------^------------+ +------------|------------+
| | |
| | |
| | +------------v------------+
| | | |
| | | unprovisioning |
| | | |
| | +------------|------------+
| | |
| +-----------------------------+
| |
+-----------------------------------------------+
Example usage
=============
.. code:: shell
$ devlink lc show [ DEV [ lc LC_INDEX ] ]
$ devlink lc set DEV lc LC_INDEX [ { type LC_TYPE | notype } ]
# Show current line card configuration and status for all slots:
$ devlink lc
# Set slot 8 to be provisioned with type "16x100G":
$ devlink lc set pci/0000:01:00.0 lc 8 type 16x100G
# Set slot 8 to be unprovisioned:
$ devlink lc set pci/0000:01:00.0 lc 8 notype

Просмотреть файл

@ -39,6 +39,7 @@ general.
devlink-resource devlink-resource
devlink-reload devlink-reload
devlink-trap devlink-trap
devlink-linecard
Driver-specific documentation Driver-specific documentation
----------------------------- -----------------------------

Просмотреть файл

@ -149,6 +149,40 @@ struct devlink_port_new_attrs {
sfnum_valid:1; sfnum_valid:1;
}; };
/**
* struct devlink_linecard_ops - Linecard operations
* @provision: callback to provision the linecard slot with certain
* type of linecard. As a result of this operation,
* driver is expected to eventually (could be after
* the function call returns) call one of:
* devlink_linecard_provision_set()
* devlink_linecard_provision_fail()
* @unprovision: callback to unprovision the linecard slot. As a result
* of this operation, driver is expected to eventually
* (could be after the function call returns) call
* devlink_linecard_provision_clear()
* devlink_linecard_provision_fail()
* @same_provision: callback to ask the driver if linecard is already
* provisioned in the same way user asks this linecard to be
* provisioned.
* @types_count: callback to get number of supported types
* @types_get: callback to get next type in list
*/
struct devlink_linecard_ops {
int (*provision)(struct devlink_linecard *linecard, void *priv,
const char *type, const void *type_priv,
struct netlink_ext_ack *extack);
int (*unprovision)(struct devlink_linecard *linecard, void *priv,
struct netlink_ext_ack *extack);
bool (*same_provision)(struct devlink_linecard *linecard, void *priv,
const char *type, const void *type_priv);
unsigned int (*types_count)(struct devlink_linecard *linecard,
void *priv);
void (*types_get)(struct devlink_linecard *linecard,
void *priv, unsigned int index, const char **type,
const void **type_priv);
};
struct devlink_sb_pool_info { struct devlink_sb_pool_info {
enum devlink_sb_pool_type pool_type; enum devlink_sb_pool_type pool_type;
u32 size; u32 size;
@ -1537,9 +1571,14 @@ void devlink_port_attrs_pci_sf_set(struct devlink_port *devlink_port,
int devlink_rate_leaf_create(struct devlink_port *port, void *priv); int devlink_rate_leaf_create(struct devlink_port *port, void *priv);
void devlink_rate_leaf_destroy(struct devlink_port *devlink_port); void devlink_rate_leaf_destroy(struct devlink_port *devlink_port);
void devlink_rate_nodes_destroy(struct devlink *devlink); void devlink_rate_nodes_destroy(struct devlink *devlink);
struct devlink_linecard *devlink_linecard_create(struct devlink *devlink, struct devlink_linecard *
unsigned int linecard_index); devlink_linecard_create(struct devlink *devlink, unsigned int linecard_index,
const struct devlink_linecard_ops *ops, void *priv);
void devlink_linecard_destroy(struct devlink_linecard *linecard); void devlink_linecard_destroy(struct devlink_linecard *linecard);
void devlink_linecard_provision_set(struct devlink_linecard *linecard,
const char *type);
void devlink_linecard_provision_clear(struct devlink_linecard *linecard);
void devlink_linecard_provision_fail(struct devlink_linecard *linecard);
int devlink_sb_register(struct devlink *devlink, unsigned int sb_index, int devlink_sb_register(struct devlink *devlink, unsigned int sb_index,
u32 size, u16 ingress_pools_count, u32 size, u16 ingress_pools_count,
u16 egress_pools_count, u16 ingress_tc_count, u16 egress_pools_count, u16 ingress_tc_count,

Просмотреть файл

@ -343,6 +343,18 @@ enum devlink_reload_limit {
#define DEVLINK_RELOAD_LIMITS_VALID_MASK (_BITUL(__DEVLINK_RELOAD_LIMIT_MAX) - 1) #define DEVLINK_RELOAD_LIMITS_VALID_MASK (_BITUL(__DEVLINK_RELOAD_LIMIT_MAX) - 1)
enum devlink_linecard_state {
DEVLINK_LINECARD_STATE_UNSPEC,
DEVLINK_LINECARD_STATE_UNPROVISIONED,
DEVLINK_LINECARD_STATE_UNPROVISIONING,
DEVLINK_LINECARD_STATE_PROVISIONING,
DEVLINK_LINECARD_STATE_PROVISIONING_FAILED,
DEVLINK_LINECARD_STATE_PROVISIONED,
__DEVLINK_LINECARD_STATE_MAX,
DEVLINK_LINECARD_STATE_MAX = __DEVLINK_LINECARD_STATE_MAX - 1
};
enum devlink_attr { enum devlink_attr {
/* don't change the order or add anything between, this is ABI! */ /* don't change the order or add anything between, this is ABI! */
DEVLINK_ATTR_UNSPEC, DEVLINK_ATTR_UNSPEC,
@ -559,6 +571,9 @@ enum devlink_attr {
DEVLINK_ATTR_REGION_MAX_SNAPSHOTS, /* u32 */ DEVLINK_ATTR_REGION_MAX_SNAPSHOTS, /* u32 */
DEVLINK_ATTR_LINECARD_INDEX, /* u32 */ DEVLINK_ATTR_LINECARD_INDEX, /* u32 */
DEVLINK_ATTR_LINECARD_STATE, /* u8 */
DEVLINK_ATTR_LINECARD_TYPE, /* string */
DEVLINK_ATTR_LINECARD_SUPPORTED_TYPES, /* nested */
/* add new attributes above here, update the policy in devlink.c */ /* add new attributes above here, update the policy in devlink.c */

Просмотреть файл

@ -72,11 +72,21 @@ struct devlink {
char priv[] __aligned(NETDEV_ALIGN); char priv[] __aligned(NETDEV_ALIGN);
}; };
struct devlink_linecard_ops;
struct devlink_linecard_type;
struct devlink_linecard { struct devlink_linecard {
struct list_head list; struct list_head list;
struct devlink *devlink; struct devlink *devlink;
unsigned int index; unsigned int index;
refcount_t refcount; refcount_t refcount;
const struct devlink_linecard_ops *ops;
void *priv;
enum devlink_linecard_state state;
struct mutex state_lock; /* Protects state */
const char *type;
struct devlink_linecard_type *types;
unsigned int types_count;
}; };
/** /**
@ -452,8 +462,10 @@ devlink_linecard_get_from_info(struct devlink *devlink, struct genl_info *info)
static void devlink_linecard_put(struct devlink_linecard *linecard) static void devlink_linecard_put(struct devlink_linecard *linecard)
{ {
if (refcount_dec_and_test(&linecard->refcount)) if (refcount_dec_and_test(&linecard->refcount)) {
mutex_destroy(&linecard->state_lock);
kfree(linecard); kfree(linecard);
}
} }
struct devlink_sb { struct devlink_sb {
@ -2037,6 +2049,11 @@ static int devlink_nl_cmd_rate_del_doit(struct sk_buff *skb,
return err; return err;
} }
struct devlink_linecard_type {
const char *type;
const void *priv;
};
static int devlink_nl_linecard_fill(struct sk_buff *msg, static int devlink_nl_linecard_fill(struct sk_buff *msg,
struct devlink *devlink, struct devlink *devlink,
struct devlink_linecard *linecard, struct devlink_linecard *linecard,
@ -2044,7 +2061,10 @@ static int devlink_nl_linecard_fill(struct sk_buff *msg,
u32 seq, int flags, u32 seq, int flags,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct devlink_linecard_type *linecard_type;
struct nlattr *attr;
void *hdr; void *hdr;
int i;
hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd); hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
if (!hdr) if (!hdr)
@ -2054,6 +2074,27 @@ static int devlink_nl_linecard_fill(struct sk_buff *msg,
goto nla_put_failure; goto nla_put_failure;
if (nla_put_u32(msg, DEVLINK_ATTR_LINECARD_INDEX, linecard->index)) if (nla_put_u32(msg, DEVLINK_ATTR_LINECARD_INDEX, linecard->index))
goto nla_put_failure; goto nla_put_failure;
if (nla_put_u8(msg, DEVLINK_ATTR_LINECARD_STATE, linecard->state))
goto nla_put_failure;
if (linecard->type &&
nla_put_string(msg, DEVLINK_ATTR_LINECARD_TYPE, linecard->type))
goto nla_put_failure;
if (linecard->types_count) {
attr = nla_nest_start(msg,
DEVLINK_ATTR_LINECARD_SUPPORTED_TYPES);
if (!attr)
goto nla_put_failure;
for (i = 0; i < linecard->types_count; i++) {
linecard_type = &linecard->types[i];
if (nla_put_string(msg, DEVLINK_ATTR_LINECARD_TYPE,
linecard_type->type)) {
nla_nest_cancel(msg, attr);
goto nla_put_failure;
}
}
nla_nest_end(msg, attr);
}
genlmsg_end(msg, hdr); genlmsg_end(msg, hdr);
return 0; return 0;
@ -2103,10 +2144,12 @@ static int devlink_nl_cmd_linecard_get_doit(struct sk_buff *skb,
if (!msg) if (!msg)
return -ENOMEM; return -ENOMEM;
mutex_lock(&linecard->state_lock);
err = devlink_nl_linecard_fill(msg, devlink, linecard, err = devlink_nl_linecard_fill(msg, devlink, linecard,
DEVLINK_CMD_LINECARD_NEW, DEVLINK_CMD_LINECARD_NEW,
info->snd_portid, info->snd_seq, 0, info->snd_portid, info->snd_seq, 0,
info->extack); info->extack);
mutex_unlock(&linecard->state_lock);
if (err) { if (err) {
nlmsg_free(msg); nlmsg_free(msg);
return err; return err;
@ -2139,12 +2182,14 @@ static int devlink_nl_cmd_linecard_get_dumpit(struct sk_buff *msg,
idx++; idx++;
continue; continue;
} }
mutex_lock(&linecard->state_lock);
err = devlink_nl_linecard_fill(msg, devlink, linecard, err = devlink_nl_linecard_fill(msg, devlink, linecard,
DEVLINK_CMD_LINECARD_NEW, DEVLINK_CMD_LINECARD_NEW,
NETLINK_CB(cb->skb).portid, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, cb->nlh->nlmsg_seq,
NLM_F_MULTI, NLM_F_MULTI,
cb->extack); cb->extack);
mutex_unlock(&linecard->state_lock);
if (err) { if (err) {
mutex_unlock(&devlink->linecards_lock); mutex_unlock(&devlink->linecards_lock);
devlink_put(devlink); devlink_put(devlink);
@ -2163,6 +2208,163 @@ out:
return msg->len; return msg->len;
} }
static struct devlink_linecard_type *
devlink_linecard_type_lookup(struct devlink_linecard *linecard,
const char *type)
{
struct devlink_linecard_type *linecard_type;
int i;
for (i = 0; i < linecard->types_count; i++) {
linecard_type = &linecard->types[i];
if (!strcmp(type, linecard_type->type))
return linecard_type;
}
return NULL;
}
static int devlink_linecard_type_set(struct devlink_linecard *linecard,
const char *type,
struct netlink_ext_ack *extack)
{
const struct devlink_linecard_ops *ops = linecard->ops;
struct devlink_linecard_type *linecard_type;
int err;
mutex_lock(&linecard->state_lock);
if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING) {
NL_SET_ERR_MSG_MOD(extack, "Line card is currently being provisioned");
err = -EBUSY;
goto out;
}
if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONING) {
NL_SET_ERR_MSG_MOD(extack, "Line card is currently being unprovisioned");
err = -EBUSY;
goto out;
}
linecard_type = devlink_linecard_type_lookup(linecard, type);
if (!linecard_type) {
NL_SET_ERR_MSG_MOD(extack, "Unsupported line card type provided");
err = -EINVAL;
goto out;
}
if (linecard->state != DEVLINK_LINECARD_STATE_UNPROVISIONED &&
linecard->state != DEVLINK_LINECARD_STATE_PROVISIONING_FAILED) {
NL_SET_ERR_MSG_MOD(extack, "Line card already provisioned");
err = -EBUSY;
/* Check if the line card is provisioned in the same
* way the user asks. In case it is, make the operation
* to return success.
*/
if (ops->same_provision &&
ops->same_provision(linecard, linecard->priv,
linecard_type->type,
linecard_type->priv))
err = 0;
goto out;
}
linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING;
linecard->type = linecard_type->type;
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
mutex_unlock(&linecard->state_lock);
err = ops->provision(linecard, linecard->priv, linecard_type->type,
linecard_type->priv, extack);
if (err) {
/* Provisioning failed. Assume the linecard is unprovisioned
* for future operations.
*/
mutex_lock(&linecard->state_lock);
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
linecard->type = NULL;
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
mutex_unlock(&linecard->state_lock);
}
return err;
out:
mutex_unlock(&linecard->state_lock);
return err;
}
static int devlink_linecard_type_unset(struct devlink_linecard *linecard,
struct netlink_ext_ack *extack)
{
int err;
mutex_lock(&linecard->state_lock);
if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING) {
NL_SET_ERR_MSG_MOD(extack, "Line card is currently being provisioned");
err = -EBUSY;
goto out;
}
if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONING) {
NL_SET_ERR_MSG_MOD(extack, "Line card is currently being unprovisioned");
err = -EBUSY;
goto out;
}
if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING_FAILED) {
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
linecard->type = NULL;
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
err = 0;
goto out;
}
if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONED) {
NL_SET_ERR_MSG_MOD(extack, "Line card is not provisioned");
err = 0;
goto out;
}
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONING;
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
mutex_unlock(&linecard->state_lock);
err = linecard->ops->unprovision(linecard, linecard->priv,
extack);
if (err) {
/* Unprovisioning failed. Assume the linecard is unprovisioned
* for future operations.
*/
mutex_lock(&linecard->state_lock);
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
linecard->type = NULL;
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
mutex_unlock(&linecard->state_lock);
}
return err;
out:
mutex_unlock(&linecard->state_lock);
return err;
}
static int devlink_nl_cmd_linecard_set_doit(struct sk_buff *skb,
struct genl_info *info)
{
struct devlink_linecard *linecard = info->user_ptr[1];
struct netlink_ext_ack *extack = info->extack;
int err;
if (info->attrs[DEVLINK_ATTR_LINECARD_TYPE]) {
const char *type;
type = nla_data(info->attrs[DEVLINK_ATTR_LINECARD_TYPE]);
if (strcmp(type, "")) {
err = devlink_linecard_type_set(linecard, type, extack);
if (err)
return err;
} else {
err = devlink_linecard_type_unset(linecard, extack);
if (err)
return err;
}
}
return 0;
}
static int devlink_nl_sb_fill(struct sk_buff *msg, struct devlink *devlink, static int devlink_nl_sb_fill(struct sk_buff *msg, struct devlink *devlink,
struct devlink_sb *devlink_sb, struct devlink_sb *devlink_sb,
enum devlink_command cmd, u32 portid, enum devlink_command cmd, u32 portid,
@ -8789,6 +8991,7 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
[DEVLINK_ATTR_RATE_NODE_NAME] = { .type = NLA_NUL_STRING }, [DEVLINK_ATTR_RATE_NODE_NAME] = { .type = NLA_NUL_STRING },
[DEVLINK_ATTR_RATE_PARENT_NODE_NAME] = { .type = NLA_NUL_STRING }, [DEVLINK_ATTR_RATE_PARENT_NODE_NAME] = { .type = NLA_NUL_STRING },
[DEVLINK_ATTR_LINECARD_INDEX] = { .type = NLA_U32 }, [DEVLINK_ATTR_LINECARD_INDEX] = { .type = NLA_U32 },
[DEVLINK_ATTR_LINECARD_TYPE] = { .type = NLA_NUL_STRING },
}; };
static const struct genl_small_ops devlink_nl_ops[] = { static const struct genl_small_ops devlink_nl_ops[] = {
@ -8871,6 +9074,12 @@ static const struct genl_small_ops devlink_nl_ops[] = {
.internal_flags = DEVLINK_NL_FLAG_NEED_LINECARD, .internal_flags = DEVLINK_NL_FLAG_NEED_LINECARD,
/* can be retrieved by unprivileged users */ /* can be retrieved by unprivileged users */
}, },
{
.cmd = DEVLINK_CMD_LINECARD_SET,
.doit = devlink_nl_cmd_linecard_set_doit,
.flags = GENL_ADMIN_PERM,
.internal_flags = DEVLINK_NL_FLAG_NEED_LINECARD,
},
{ {
.cmd = DEVLINK_CMD_SB_GET, .cmd = DEVLINK_CMD_SB_GET,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
@ -9962,19 +10171,56 @@ static int __devlink_port_phys_port_name_get(struct devlink_port *devlink_port,
return 0; return 0;
} }
static int devlink_linecard_types_init(struct devlink_linecard *linecard)
{
struct devlink_linecard_type *linecard_type;
unsigned int count;
int i;
count = linecard->ops->types_count(linecard, linecard->priv);
linecard->types = kmalloc_array(count, sizeof(*linecard_type),
GFP_KERNEL);
if (!linecard->types)
return -ENOMEM;
linecard->types_count = count;
for (i = 0; i < count; i++) {
linecard_type = &linecard->types[i];
linecard->ops->types_get(linecard, linecard->priv, i,
&linecard_type->type,
&linecard_type->priv);
}
return 0;
}
static void devlink_linecard_types_fini(struct devlink_linecard *linecard)
{
kfree(linecard->types);
}
/** /**
* devlink_linecard_create - Create devlink linecard * devlink_linecard_create - Create devlink linecard
* *
* @devlink: devlink * @devlink: devlink
* @linecard_index: driver-specific numerical identifier of the linecard * @linecard_index: driver-specific numerical identifier of the linecard
* @ops: linecards ops
* @priv: user priv pointer
* *
* Create devlink linecard instance with provided linecard index. * Create devlink linecard instance with provided linecard index.
* Caller can use any indexing, even hw-related one. * Caller can use any indexing, even hw-related one.
*
* Return: Line card structure or an ERR_PTR() encoded error code.
*/ */
struct devlink_linecard *devlink_linecard_create(struct devlink *devlink, struct devlink_linecard *
unsigned int linecard_index) devlink_linecard_create(struct devlink *devlink, unsigned int linecard_index,
const struct devlink_linecard_ops *ops, void *priv)
{ {
struct devlink_linecard *linecard; struct devlink_linecard *linecard;
int err;
if (WARN_ON(!ops || !ops->provision || !ops->unprovision ||
!ops->types_count || !ops->types_get))
return ERR_PTR(-EINVAL);
mutex_lock(&devlink->linecards_lock); mutex_lock(&devlink->linecards_lock);
if (devlink_linecard_index_exists(devlink, linecard_index)) { if (devlink_linecard_index_exists(devlink, linecard_index)) {
@ -9990,6 +10236,19 @@ struct devlink_linecard *devlink_linecard_create(struct devlink *devlink,
linecard->devlink = devlink; linecard->devlink = devlink;
linecard->index = linecard_index; linecard->index = linecard_index;
linecard->ops = ops;
linecard->priv = priv;
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
mutex_init(&linecard->state_lock);
err = devlink_linecard_types_init(linecard);
if (err) {
mutex_destroy(&linecard->state_lock);
kfree(linecard);
mutex_unlock(&devlink->linecards_lock);
return ERR_PTR(err);
}
list_add_tail(&linecard->list, &devlink->linecard_list); list_add_tail(&linecard->list, &devlink->linecard_list);
refcount_set(&linecard->refcount, 1); refcount_set(&linecard->refcount, 1);
mutex_unlock(&devlink->linecards_lock); mutex_unlock(&devlink->linecards_lock);
@ -10010,11 +10269,68 @@ void devlink_linecard_destroy(struct devlink_linecard *linecard)
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL); devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL);
mutex_lock(&devlink->linecards_lock); mutex_lock(&devlink->linecards_lock);
list_del(&linecard->list); list_del(&linecard->list);
devlink_linecard_types_fini(linecard);
mutex_unlock(&devlink->linecards_lock); mutex_unlock(&devlink->linecards_lock);
devlink_linecard_put(linecard); devlink_linecard_put(linecard);
} }
EXPORT_SYMBOL_GPL(devlink_linecard_destroy); EXPORT_SYMBOL_GPL(devlink_linecard_destroy);
/**
* devlink_linecard_provision_set - Set provisioning on linecard
*
* @linecard: devlink linecard
* @type: linecard type
*
* This is either called directly from the provision() op call or
* as a result of the provision() op call asynchronously.
*/
void devlink_linecard_provision_set(struct devlink_linecard *linecard,
const char *type)
{
mutex_lock(&linecard->state_lock);
WARN_ON(linecard->type && strcmp(linecard->type, type));
linecard->state = DEVLINK_LINECARD_STATE_PROVISIONED;
linecard->type = type;
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
mutex_unlock(&linecard->state_lock);
}
EXPORT_SYMBOL_GPL(devlink_linecard_provision_set);
/**
* devlink_linecard_provision_clear - Clear provisioning on linecard
*
* @linecard: devlink linecard
*
* This is either called directly from the unprovision() op call or
* as a result of the unprovision() op call asynchronously.
*/
void devlink_linecard_provision_clear(struct devlink_linecard *linecard)
{
mutex_lock(&linecard->state_lock);
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
linecard->type = NULL;
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
mutex_unlock(&linecard->state_lock);
}
EXPORT_SYMBOL_GPL(devlink_linecard_provision_clear);
/**
* devlink_linecard_provision_fail - Fail provisioning on linecard
*
* @linecard: devlink linecard
*
* This is either called directly from the provision() op call or
* as a result of the provision() op call asynchronously.
*/
void devlink_linecard_provision_fail(struct devlink_linecard *linecard)
{
mutex_lock(&linecard->state_lock);
linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING_FAILED;
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
mutex_unlock(&linecard->state_lock);
}
EXPORT_SYMBOL_GPL(devlink_linecard_provision_fail);
int devlink_sb_register(struct devlink *devlink, unsigned int sb_index, int devlink_sb_register(struct devlink *devlink, unsigned int sb_index,
u32 size, u16 ingress_pools_count, u32 size, u16 ingress_pools_count,
u16 egress_pools_count, u16 ingress_tc_count, u16 egress_pools_count, u16 ingress_tc_count,