[PATCH/RFC net-next 1/2] devlink: expose port function commands to assign VFs to multiple netdevs

Simon Horman
2023-02-06T15:40:42Z

From: Fei Qin <fei.qin@corigine.com>

Multiple physical ports of the same NIC may share the single
PCI address. In some cases, assigning VFs to different physical
ports can be demanded, especially under high-traffic scenario.
Load balancing can be realized in virtualised use¬cases through
distributing packets between different physical ports with LAGs
of VFs which are assigned to those physical ports.

This patch adds new attribute "vf_count" to 'devlink port function'
API which only can be shown and configured under devlink ports
with flavor "DEVLINK_PORT_FLAVOUR_PHYSICAL".

e.g.
$ devlink port show pci/0000:82:00.0/0
pci/0000:82:00.0/0: type eth netdev enp130s0np0 flavour physical
port 0 splittable true lanes 4
function:
vf_count 0

$ devlink port function set pci/0000:82:00.0/0 vf_count 3

$ devlink port show pci/0000:82:00.0/0
pci/0000:82:00.0/0: type eth netdev enp130s0np0 flavour physical
port 0 splittable true lanes 4
function:
vf_count 3

Signed-off-by: Fei Qin <fei.qin@corigine.com>
Signed-off-by: Simon Horman <simon.horman@corigine.com>
---
.../networking/devlink/devlink-port.rst | 24 +++++++
include/net/devlink.h | 21 ++++++
include/uapi/linux/devlink.h | 1 +
net/devlink/leftover.c | 65 +++++++++++++++++++
4 files changed, 111 insertions(+)

diff --git a/Documentation/networking/devlink/devlink-port.rst b/Documentation/networking/devlink/devlink-port.rst
index 3da590953ce8..5c3996bce6d9 100644
--- a/Documentation/networking/devlink/devlink-port.rst
+++ b/Documentation/networking/devlink/devlink-port.rst
@@ -128,6 +128,9 @@ Users may also set the RoCE capability of the function using
Users may also set the function as migratable using
'devlink port function set migratable' command.

+Users may also assign VFs to physical ports using
+'devlink port function set vf_count' command.
+
Function attributes
===================

@@ -240,6 +243,27 @@ Attach VF to the VM.
Start the VM.
Perform live migration.

+
+VF assignment setup
+---------------------------
+In some cases, NICs could have multiple physical ports per PF. Users can assign VFs to
+different ports.
+
+- Get count of VFs assigned to physical port::
+
+ $ devlink port show pci/0000:82:00.0/0
+ pci/0000:82:00.0/0: type eth netdev enp130s0np0 flavour physical port 0 splittable true lanes 4
+ function:
+ vf_count 0
+
+- Set count of VFs assigned to physical port::
+ $ devlink port function set pci/0000:82:00.0/0 vf_count 3
+
+ $ devlink port show pci/0000:82:00.0/0
+ pci/0000:82:00.0/0: type eth netdev enp130s0np0 flavour physical port 0 splittable true lanes 4
+ function:
+ vf_count 3
+
Subfunction
============

diff --git a/include/net/devlink.h b/include/net/devlink.h
index 2e85a5970a32..3e98fa3d251f 100644
--- a/include/net/devlink.h
+++ b/include/net/devlink.h
@@ -1491,6 +1491,27 @@ struct devlink_ops {
int (*port_fn_migratable_set)(struct devlink_port *devlink_port,
bool enable,
struct netlink_ext_ack *extack);
+
+ /**
+ * @port_fn_vf_count_get: Port function's VF count get function
+ *
+ * Get assigned VF count of a function managed by the devlink port,
+ * should only be used for DEVLINK_PORT_FLAVOUR_PHYSICAL.
+ * Return -EOPNOTSUPP if port function vf_count setup is not supported.
+ */
+ int (*port_fn_vf_count_get)(struct devlink_port *port, u16 *vf_count,
+ struct netlink_ext_ack *extack);
+
+ /**
+ * @port_fn_vf_count_set: Port function's VF count set function
+ *
+ * Set assigned VF count of a function managed by the devlink port,
+ * should only be used for DEVLINK_PORT_FLAVOUR_PHYSICAL.
+ * Return -EOPNOTSUPP if port function vf_count setup is not supported.
+ */
+ int (*port_fn_vf_count_set)(struct devlink_port *port, u16 vf_count,
+ struct netlink_ext_ack *extack);
+
/**
* port_new() - Add a new port function of a specified flavor
* @devlink: Devlink instance
diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h
index 3782d4219ac9..774e17f6100b 100644
--- a/include/uapi/linux/devlink.h
+++ b/include/uapi/linux/devlink.h
@@ -676,6 +676,7 @@ enum devlink_port_function_attr {
DEVLINK_PORT_FN_ATTR_STATE, /* u8 */
DEVLINK_PORT_FN_ATTR_OPSTATE, /* u8 */
DEVLINK_PORT_FN_ATTR_CAPS, /* bitfield32 */
+ DEVLINK_PORT_FN_ATTR_VF_COUNT, /* u16 */

__DEVLINK_PORT_FUNCTION_ATTR_MAX,
DEVLINK_PORT_FUNCTION_ATTR_MAX = __DEVLINK_PORT_FUNCTION_ATTR_MAX - 1
diff --git a/net/devlink/leftover.c b/net/devlink/leftover.c
index 97d30ea98b00..6dac8b562232 100644
--- a/net/devlink/leftover.c
+++ b/net/devlink/leftover.c
@@ -141,6 +141,7 @@ static const struct nla_policy devlink_function_nl_policy[DEVLINK_PORT_FUNCTION_
DEVLINK_PORT_FN_STATE_ACTIVE),
[DEVLINK_PORT_FN_ATTR_CAPS] =
NLA_POLICY_BITFIELD32(DEVLINK_PORT_FN_CAPS_VALID_MASK),
+ [DEVLINK_PORT_FN_ATTR_VF_COUNT] = { .type = NLA_U16 },
};

#define ASSERT_DEVLINK_PORT_REGISTERED(devlink_port) \
@@ -520,6 +521,35 @@ static int devlink_port_fn_caps_fill(const struct devlink_ops *ops,
return 0;
}

+static int devlink_port_fn_vf_count_fill(const struct devlink_ops *ops,
+ struct devlink_port *devlink_port,
+ struct sk_buff *msg,
+ struct netlink_ext_ack *extack,
+ bool *msg_updated)
+{
+ u16 vf_count;
+ int err;
+
+ if (!ops->port_fn_vf_count_get ||
+ devlink_port->attrs.flavour != DEVLINK_PORT_FLAVOUR_PHYSICAL)
+ return 0;
+
+ err = ops->port_fn_vf_count_get(devlink_port, &vf_count, extack);
+ if (err) {
+ if (err == -EOPNOTSUPP)
+ return 0;
+ return err;
+ }
+
+ err = nla_put_u16(msg, DEVLINK_PORT_FN_ATTR_VF_COUNT, vf_count);
+ if (err)
+ return err;
+
+ *msg_updated = true;
+
+ return 0;
+}
+
static int
devlink_sb_tc_index_get_from_info(struct devlink_sb *devlink_sb,
struct genl_info *info,
@@ -871,6 +901,16 @@ static int devlink_port_fn_caps_set(struct devlink_port *devlink_port,
return 0;
}

+static int devlink_port_fn_vf_count_set(struct devlink_port *devlink_port,
+ const struct nlattr *attr,
+ struct netlink_ext_ack *extack)
+{
+ const struct devlink_ops *ops = devlink_port->devlink->ops;
+ u16 vf_count = nla_get_u16(attr);
+
+ return ops->port_fn_vf_count_set(devlink_port, vf_count, extack);
+}
+
static int
devlink_nl_port_function_attrs_put(struct sk_buff *msg, struct devlink_port *port,
struct netlink_ext_ack *extack)
@@ -893,6 +933,11 @@ devlink_nl_port_function_attrs_put(struct sk_buff *msg, struct devlink_port *por
&msg_updated);
if (err)
goto out;
+
+ err = devlink_port_fn_vf_count_fill(ops, port, msg, extack, &msg_updated);
+ if (err)
+ goto out;
+
err = devlink_port_fn_state_fill(ops, port, msg, extack, &msg_updated);
out:
if (err || !msg_updated)
@@ -1219,6 +1264,19 @@ static int devlink_port_function_validate(struct devlink_port *devlink_port,
"Function does not support state setting");
return -EOPNOTSUPP;
}
+ attr = tb[DEVLINK_PORT_FN_ATTR_VF_COUNT];
+ if (attr) {
+ if (!ops->port_fn_vf_count_set) {
+ NL_SET_ERR_MSG_ATTR(extack, attr,
+ "Function doesn't support VF assignment");
+ return -EOPNOTSUPP;
+ }
+ if (devlink_port->attrs.flavour != DEVLINK_PORT_FLAVOUR_PHYSICAL) {
+ NL_SET_ERR_MSG_ATTR(extack, attr,
+ "VFs assignment supported for physical ports only");
+ return -EOPNOTSUPP;
+ }
+ }
attr = tb[DEVLINK_PORT_FN_ATTR_CAPS];
if (attr) {
struct nla_bitfield32 caps;
@@ -1278,6 +1336,13 @@ static int devlink_port_function_set(struct devlink_port *port,
return err;
}

+ attr = tb[DEVLINK_PORT_FN_ATTR_VF_COUNT];
+ if (attr) {
+ err = devlink_port_fn_vf_count_set(port, attr, extack);
+ if (err)
+ return err;
+ }
+
/* Keep this as the last function attribute set, so that when
* multiple port function attributes are set along with state,
* Those can be applied first before activating the state.
--
2.30.2