if (tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR] &&
!ops->port_fn_hw_addr_set) {
NL_SET_ERR_MSG_ATTR(extack, tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR], "Port doesn't support function attributes"); return -EOPNOTSUPP;
} if (tb[DEVLINK_PORT_FN_ATTR_STATE] && !ops->port_fn_state_set) {
NL_SET_ERR_MSG_ATTR(extack, tb[DEVLINK_PORT_FN_ATTR_STATE], "Function does not support state setting"); return -EOPNOTSUPP;
}
attr = tb[DEVLINK_PORT_FN_ATTR_CAPS]; if (attr) { struct nla_bitfield32 caps;
caps = nla_get_bitfield32(attr); if (caps.selector & DEVLINK_PORT_FN_CAP_ROCE &&
!ops->port_fn_roce_set) {
NL_SET_ERR_MSG_ATTR(extack, attr, "Port doesn't support RoCE function attribute"); return -EOPNOTSUPP;
} if (caps.selector & DEVLINK_PORT_FN_CAP_MIGRATABLE) { if (!ops->port_fn_migratable_set) {
NL_SET_ERR_MSG_ATTR(extack, attr, "Port doesn't support migratable function attribute"); return -EOPNOTSUPP;
} if (devlink_port->attrs.flavour != DEVLINK_PORT_FLAVOUR_PCI_VF) {
NL_SET_ERR_MSG_ATTR(extack, attr, "migratable function attribute supported for VFs only"); return -EOPNOTSUPP;
}
} if (caps.selector & DEVLINK_PORT_FN_CAP_IPSEC_CRYPTO) { if (!ops->port_fn_ipsec_crypto_set) {
NL_SET_ERR_MSG_ATTR(extack, attr, "Port doesn't support ipsec_crypto function attribute"); return -EOPNOTSUPP;
} if (devlink_port->attrs.flavour != DEVLINK_PORT_FLAVOUR_PCI_VF) {
NL_SET_ERR_MSG_ATTR(extack, attr, "ipsec_crypto function attribute supported for VFs only"); return -EOPNOTSUPP;
}
} if (caps.selector & DEVLINK_PORT_FN_CAP_IPSEC_PACKET) { if (!ops->port_fn_ipsec_packet_set) {
NL_SET_ERR_MSG_ATTR(extack, attr, "Port doesn't support ipsec_packet function attribute"); return -EOPNOTSUPP;
} if (devlink_port->attrs.flavour != DEVLINK_PORT_FLAVOUR_PCI_VF) {
NL_SET_ERR_MSG_ATTR(extack, attr, "ipsec_packet function attribute supported for VFs only"); return -EOPNOTSUPP;
}
}
} if (tb[DEVLINK_PORT_FN_ATTR_MAX_IO_EQS] &&
!ops->port_fn_max_io_eqs_set) {
NL_SET_ERR_MSG_ATTR(extack, tb[DEVLINK_PORT_FN_ATTR_MAX_IO_EQS], "Function does not support max_io_eqs setting"); return -EOPNOTSUPP;
} return 0;
}
err = nla_parse_nested(tb, DEVLINK_PORT_FUNCTION_ATTR_MAX, attr,
devlink_function_nl_policy, extack); if (err < 0) {
NL_SET_ERR_MSG(extack, "Fail to parse port function attributes"); return err;
}
err = devlink_port_function_validate(port, tb, extack); if (err) return err;
attr = tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR]; if (attr) {
err = devlink_port_function_hw_addr_set(port, attr, extack); if (err) return err;
}
attr = tb[DEVLINK_PORT_FN_ATTR_CAPS]; if (attr) {
err = devlink_port_fn_caps_set(port, attr, extack); if (err) return err;
}
attr = tb[DEVLINK_PORT_FN_ATTR_MAX_IO_EQS]; if (attr) {
err = devlink_port_fn_max_io_eqs_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.
*/
attr = tb[DEVLINK_PORT_FN_ATTR_STATE]; if (attr)
err = devlink_port_fn_state_set(port, attr, extack);
if (!err)
devlink_port_notify(port, DEVLINK_CMD_PORT_NEW); return err;
}
int devlink_nl_port_set_doit(struct sk_buff *skb, struct genl_info *info)
{ struct devlink_port *devlink_port = info->user_ptr[1]; int err;
if (info->attrs[DEVLINK_ATTR_PORT_TYPE]) { enum devlink_port_type port_type;
if (!info->attrs[DEVLINK_ATTR_PORT_FLAVOUR] ||
!info->attrs[DEVLINK_ATTR_PORT_PCI_PF_NUMBER]) {
NL_SET_ERR_MSG(extack, "Port flavour or PCI PF are not specified"); return -EINVAL;
}
new_attrs.flavour = nla_get_u16(info->attrs[DEVLINK_ATTR_PORT_FLAVOUR]);
new_attrs.pfnum =
nla_get_u16(info->attrs[DEVLINK_ATTR_PORT_PCI_PF_NUMBER]);
if (info->attrs[DEVLINK_ATTR_PORT_INDEX]) { /* Port index of the new port being created by driver. */
new_attrs.port_index =
nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]);
new_attrs.port_index_valid = true;
} if (info->attrs[DEVLINK_ATTR_PORT_CONTROLLER_NUMBER]) {
new_attrs.controller =
nla_get_u16(info->attrs[DEVLINK_ATTR_PORT_CONTROLLER_NUMBER]);
new_attrs.controller_valid = true;
} if (new_attrs.flavour == DEVLINK_PORT_FLAVOUR_PCI_SF &&
info->attrs[DEVLINK_ATTR_PORT_PCI_SF_NUMBER]) {
new_attrs.sfnum = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_PCI_SF_NUMBER]);
new_attrs.sfnum_valid = true;
}
err = devlink->ops->port_new(devlink, &new_attrs,
extack, &devlink_port); if (err) return err;
staticvoid devlink_port_type_warn_schedule(struct devlink_port *devlink_port)
{ if (!devlink_port_type_should_warn(devlink_port)) return; /* Schedule a work to WARN in case driver does not set port * type within timeout.
*/
schedule_delayed_work(&devlink_port->type_warn_dw,
DEVLINK_PORT_TYPE_WARN_TIMEOUT);
}
staticvoid devlink_port_type_warn_cancel(struct devlink_port *devlink_port)
{ if (!devlink_port_type_should_warn(devlink_port)) return;
cancel_delayed_work_sync(&devlink_port->type_warn_dw);
}
/** * devlink_port_init() - Init devlink port * * @devlink: devlink * @devlink_port: devlink port * * Initialize essential stuff that is needed for functions * that may be called before devlink port registration. * Call to this function is optional and not needed * in case the driver does not use such functions.
*/ void devlink_port_init(struct devlink *devlink, struct devlink_port *devlink_port)
{ if (devlink_port->initialized) return;
devlink_port->devlink = devlink;
INIT_LIST_HEAD(&devlink_port->region_list);
devlink_port->initialized = true;
}
EXPORT_SYMBOL_GPL(devlink_port_init);
/** * devlink_port_fini() - Deinitialize devlink port * * @devlink_port: devlink port * * Deinitialize essential stuff that is in use for functions * that may be called after devlink port unregistration. * Call to this function is optional and not needed * in case the driver does not use such functions.
*/ void devlink_port_fini(struct devlink_port *devlink_port)
{
WARN_ON(!list_empty(&devlink_port->region_list));
}
EXPORT_SYMBOL_GPL(devlink_port_fini);
/** * devl_port_register_with_ops() - Register devlink port * * @devlink: devlink * @devlink_port: devlink port * @port_index: driver-specific numerical identifier of the port * @ops: port ops * * Register devlink port with provided port index. User can use * any indexing, even hw-related one. devlink_port structure * is convenient to be embedded inside user driver private structure. * Note that the caller should take care of zeroing the devlink_port * structure.
*/ int devl_port_register_with_ops(struct devlink *devlink, struct devlink_port *devlink_port, unsignedint port_index, conststruct devlink_port_ops *ops)
{ int err;
/** * devlink_port_register_with_ops - Register devlink port * * @devlink: devlink * @devlink_port: devlink port * @port_index: driver-specific numerical identifier of the port * @ops: port ops * * Register devlink port with provided port index. User can use * any indexing, even hw-related one. devlink_port structure * is convenient to be embedded inside user driver private structure. * Note that the caller should take care of zeroing the devlink_port * structure. * * Context: Takes and release devlink->lock <mutex>.
*/ int devlink_port_register_with_ops(struct devlink *devlink, struct devlink_port *devlink_port, unsignedint port_index, conststruct devlink_port_ops *ops)
{ int err;
/* If driver registers devlink port, it should set devlink port * attributes accordingly so the compat functions are called * and the original ops are not used.
*/ if (ops->ndo_get_phys_port_name) { /* Some drivers use the same set of ndos for netdevs * that have devlink_port registered and also for * those who don't. Make sure that ndo_get_phys_port_name * returns -EOPNOTSUPP here in case it is defined. * Warn if not.
*/ char name[IFNAMSIZ]; int err;
err = ops->ndo_get_phys_port_name(netdev, name, sizeof(name));
WARN_ON(err != -EOPNOTSUPP);
} if (ops->ndo_get_port_parent_id) { /* Some drivers use the same set of ndos for netdevs * that have devlink_port registered and also for * those who don't. Make sure that ndo_get_port_parent_id * returns -EOPNOTSUPP here in case it is defined. * Warn if not.
*/ struct netdev_phys_item_id ppid; int err;
/** * devlink_port_type_eth_set - Set port type to Ethernet * * @devlink_port: devlink port * * If driver is calling this, most likely it is doing something wrong.
*/ void devlink_port_type_eth_set(struct devlink_port *devlink_port)
{
dev_warn(devlink_port->devlink->dev, "devlink port type for port %d set to Ethernet without a software interface reference, device type not supported by the kernel?\n",
devlink_port->index);
__devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_ETH, NULL);
}
EXPORT_SYMBOL_GPL(devlink_port_type_eth_set);
/** * devlink_port_type_ib_set - Set port type to InfiniBand * * @devlink_port: devlink port * @ibdev: related IB device
*/ void devlink_port_type_ib_set(struct devlink_port *devlink_port, struct ib_device *ibdev)
{
__devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_IB, ibdev);
}
EXPORT_SYMBOL_GPL(devlink_port_type_ib_set);
/** * devlink_port_type_clear - Clear port type * * @devlink_port: devlink port * * If driver is calling this for clearing Ethernet type, most likely * it is doing something wrong.
*/ void devlink_port_type_clear(struct devlink_port *devlink_port)
{ if (devlink_port->type == DEVLINK_PORT_TYPE_ETH)
dev_warn(devlink_port->devlink->dev, "devlink port type for port %d cleared without a software interface reference, device type not supported by the kernel?\n",
devlink_port->index);
__devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_NOTSET, NULL);
}
EXPORT_SYMBOL_GPL(devlink_port_type_clear);
if (!devlink_port) return NOTIFY_OK;
devlink = devlink_port->devlink;
switch (event) { case NETDEV_POST_INIT: /* Set the type but not netdev pointer. It is going to be set * later on by NETDEV_REGISTER event. Happens once during * netdevice register
*/
__devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_ETH,
NULL); break; case NETDEV_REGISTER: case NETDEV_CHANGENAME: if (devlink_net(devlink) != dev_net(netdev)) return NOTIFY_OK; /* Set the netdev on top of previously set type. Note this * event happens also during net namespace change so here * we take into account netdev pointer appearing in this * namespace.
*/
__devlink_port_type_set(devlink_port, devlink_port->type,
netdev); break; case NETDEV_UNREGISTER: if (devlink_net(devlink) != dev_net(netdev)) return NOTIFY_OK; /* Clear netdev pointer, but not the type. This event happens * also during net namespace change so we need to clear * pointer to netdev that is going to another net namespace.
*/
__devlink_port_type_set(devlink_port, devlink_port->type,
NULL); break; case NETDEV_PRE_UNINIT: /* Clear the type and the netdev pointer. Happens one during * netdevice unregister.
*/
__devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_NOTSET,
NULL); break;
}
/** * devlink_port_attrs_set - Set port attributes * * @devlink_port: devlink port * @attrs: devlink port attrs
*/ void devlink_port_attrs_set(struct devlink_port *devlink_port, struct devlink_port_attrs *attrs)
{ int ret;
ASSERT_DEVLINK_PORT_NOT_REGISTERED(devlink_port);
devlink_port->attrs = *attrs;
ret = __devlink_port_attrs_set(devlink_port, attrs->flavour); if (ret) return;
WARN_ON(attrs->splittable && attrs->split);
}
EXPORT_SYMBOL_GPL(devlink_port_attrs_set);
/** * devlink_port_attrs_pci_pf_set - Set PCI PF port attributes * * @devlink_port: devlink port * @controller: associated controller number for the devlink port instance * @pf: associated PCI function number for the devlink port instance * @external: indicates if the port is for an external controller
*/ void devlink_port_attrs_pci_pf_set(struct devlink_port *devlink_port, u32 controller,
u16 pf, bool external)
{ struct devlink_port_attrs *attrs = &devlink_port->attrs; int ret;
ASSERT_DEVLINK_PORT_NOT_REGISTERED(devlink_port);
ret = __devlink_port_attrs_set(devlink_port,
DEVLINK_PORT_FLAVOUR_PCI_PF); if (ret) return;
attrs->pci_pf.controller = controller;
attrs->pci_pf.pf = pf;
attrs->pci_pf.external = external;
}
EXPORT_SYMBOL_GPL(devlink_port_attrs_pci_pf_set);
/** * devlink_port_attrs_pci_vf_set - Set PCI VF port attributes * * @devlink_port: devlink port * @controller: associated controller number for the devlink port instance * @pf: associated PCI function number for the devlink port instance * @vf: associated PCI VF number of a PF for the devlink port instance; * VF number starts from 0 for the first PCI virtual function * @external: indicates if the port is for an external controller
*/ void devlink_port_attrs_pci_vf_set(struct devlink_port *devlink_port, u32 controller,
u16 pf, u16 vf, bool external)
{ struct devlink_port_attrs *attrs = &devlink_port->attrs; int ret;
/** * devlink_port_attrs_pci_sf_set - Set PCI SF port attributes * * @devlink_port: devlink port * @controller: associated controller number for the devlink port instance * @pf: associated PCI function number for the devlink port instance * @sf: associated SF number of a PF for the devlink port instance * @external: indicates if the port is for an external controller
*/ void devlink_port_attrs_pci_sf_set(struct devlink_port *devlink_port, u32 controller,
u16 pf, u32 sf, bool external)
{ struct devlink_port_attrs *attrs = &devlink_port->attrs; int ret;
staticint __devlink_port_phys_port_name_get(struct devlink_port *devlink_port, char *name, size_t len)
{ struct devlink_port_attrs *attrs = &devlink_port->attrs; int n = 0;
if (!devlink_port->attrs_set || devlink_port->attrs.no_phys_port_name) return -EOPNOTSUPP;
switch (attrs->flavour) { case DEVLINK_PORT_FLAVOUR_PHYSICAL: if (devlink_port->linecard)
n = snprintf(name, len, "l%u",
devlink_linecard_index(devlink_port->linecard)); if (n < len)
n += snprintf(name + n, len - n, "p%u",
attrs->phys.port_number); if (n < len && attrs->split)
n += snprintf(name + n, len - n, "s%u",
attrs->phys.split_subport_number); break; case DEVLINK_PORT_FLAVOUR_CPU: case DEVLINK_PORT_FLAVOUR_DSA: case DEVLINK_PORT_FLAVOUR_UNUSED: /* As CPU and DSA ports do not have a netdevice associated * case should not ever happen.
*/
WARN_ON(1); return -EINVAL; case DEVLINK_PORT_FLAVOUR_PCI_PF: if (attrs->pci_pf.external) {
n = snprintf(name, len, "c%u", attrs->pci_pf.controller); if (n >= len) return -EINVAL;
len -= n;
name += n;
}
n = snprintf(name, len, "pf%u", attrs->pci_pf.pf); break; case DEVLINK_PORT_FLAVOUR_PCI_VF: if (attrs->pci_vf.external) {
n = snprintf(name, len, "c%u", attrs->pci_vf.controller); if (n >= len) return -EINVAL;
len -= n;
name += n;
}
n = snprintf(name, len, "pf%uvf%u",
attrs->pci_vf.pf, attrs->pci_vf.vf); break; case DEVLINK_PORT_FLAVOUR_PCI_SF: if (attrs->pci_sf.external) {
n = snprintf(name, len, "c%u", attrs->pci_sf.controller); if (n >= len) return -EINVAL;
len -= n;
name += n;
}
n = snprintf(name, len, "pf%usf%u", attrs->pci_sf.pf,
attrs->pci_sf.sf); break; case DEVLINK_PORT_FLAVOUR_VIRTUAL: return -EOPNOTSUPP;
}
/* RTNL mutex is held here which ensures that devlink_port * instance cannot disappear in the middle. No need to take * any devlink lock as only permanent values are accessed.
*/
ASSERT_RTNL();
devlink_port = dev->devlink_port; if (!devlink_port) return -EOPNOTSUPP;
/* Caller must hold RTNL mutex or reference to dev, which ensures that * devlink_port instance cannot disappear in the middle. No need to take * any devlink lock as only permanent values are accessed.
*/
devlink_port = dev->devlink_port; if (!devlink_port || !devlink_port->switch_port) return -EOPNOTSUPP;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.