static bool mv88e6xxx_lag_can_offload(struct dsa_switch *ds,
struct dsa_lag lag,
- struct netdev_lag_upper_info *info)
+ struct netdev_lag_upper_info *info,
+ struct netlink_ext_ack *extack)
{
struct mv88e6xxx_chip *chip = ds->priv;
struct dsa_port *dp;
int members = 0;
- if (!mv88e6xxx_has_lag(chip))
+ if (!mv88e6xxx_has_lag(chip)) {
+ NL_SET_ERR_MSG_MOD(extack, "Chip does not support LAG offload");
return false;
+ }
if (!lag.id)
return false;
/* Includes the port joining the LAG */
members++;
- if (members > 8)
+ if (members > 8) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Cannot offload more than 8 LAG ports");
return false;
+ }
/* We could potentially relax this to include active
* backup in the future.
*/
- if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
+ if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Can only offload LAG using hash TX type");
return false;
+ }
/* Ideally we would also validate that the hash type matches
* the hardware. Alas, this is always set to unknown on team
static int mv88e6xxx_port_lag_join(struct dsa_switch *ds, int port,
struct dsa_lag lag,
- struct netdev_lag_upper_info *info)
+ struct netdev_lag_upper_info *info,
+ struct netlink_ext_ack *extack)
{
struct mv88e6xxx_chip *chip = ds->priv;
int err, id;
- if (!mv88e6xxx_lag_can_offload(ds, lag, info))
+ if (!mv88e6xxx_lag_can_offload(ds, lag, info, extack))
return -EOPNOTSUPP;
/* DSA LAG IDs are one-based */
static int mv88e6xxx_crosschip_lag_join(struct dsa_switch *ds, int sw_index,
int port, struct dsa_lag lag,
- struct netdev_lag_upper_info *info)
+ struct netdev_lag_upper_info *info,
+ struct netlink_ext_ack *extack)
{
struct mv88e6xxx_chip *chip = ds->priv;
int err;
- if (!mv88e6xxx_lag_can_offload(ds, lag, info))
+ if (!mv88e6xxx_lag_can_offload(ds, lag, info, extack))
return -EOPNOTSUPP;
mv88e6xxx_reg_lock(chip);
static int felix_lag_join(struct dsa_switch *ds, int port,
struct dsa_lag lag,
- struct netdev_lag_upper_info *info)
+ struct netdev_lag_upper_info *info,
+ struct netlink_ext_ack *extack)
{
struct ocelot *ocelot = ds->priv;
- return ocelot_port_lag_join(ocelot, port, lag.dev, info);
+ return ocelot_port_lag_join(ocelot, port, lag.dev, info, extack);
}
static int felix_lag_leave(struct dsa_switch *ds, int port,
static bool qca8k_lag_can_offload(struct dsa_switch *ds,
struct dsa_lag lag,
- struct netdev_lag_upper_info *info)
+ struct netdev_lag_upper_info *info,
+ struct netlink_ext_ack *extack)
{
struct dsa_port *dp;
int members = 0;
/* Includes the port joining the LAG */
members++;
- if (members > QCA8K_NUM_PORTS_FOR_LAG)
+ if (members > QCA8K_NUM_PORTS_FOR_LAG) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Cannot offload more than 4 LAG ports");
return false;
+ }
- if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
+ if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Can only offload LAG using hash TX type");
return false;
+ }
if (info->hash_type != NETDEV_LAG_HASH_L2 &&
- info->hash_type != NETDEV_LAG_HASH_L23)
+ info->hash_type != NETDEV_LAG_HASH_L23) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Can only offload L2 or L2+L3 TX hash");
return false;
+ }
return true;
}
}
int qca8k_port_lag_join(struct dsa_switch *ds, int port, struct dsa_lag lag,
- struct netdev_lag_upper_info *info)
+ struct netdev_lag_upper_info *info,
+ struct netlink_ext_ack *extack)
{
int ret;
- if (!qca8k_lag_can_offload(ds, lag, info))
+ if (!qca8k_lag_can_offload(ds, lag, info, extack))
return -EOPNOTSUPP;
ret = qca8k_lag_setup_hash(ds, lag, info);
/* Common port LAG function */
int qca8k_port_lag_join(struct dsa_switch *ds, int port, struct dsa_lag lag,
- struct netdev_lag_upper_info *info);
+ struct netdev_lag_upper_info *info,
+ struct netlink_ext_ack *extack);
int qca8k_port_lag_leave(struct dsa_switch *ds, int port,
struct dsa_lag lag);
int ocelot_port_lag_join(struct ocelot *ocelot, int port,
struct net_device *bond,
- struct netdev_lag_upper_info *info)
+ struct netdev_lag_upper_info *info,
+ struct netlink_ext_ack *extack)
{
- if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
+ if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Can only offload LAG using hash TX type");
return -EOPNOTSUPP;
+ }
mutex_lock(&ocelot->fwd_domain_lock);
int port = priv->port.index;
int err;
- err = ocelot_port_lag_join(ocelot, port, bond, info);
- if (err == -EOPNOTSUPP) {
- NL_SET_ERR_MSG_MOD(extack, "Offloading not supported");
+ err = ocelot_port_lag_join(ocelot, port, bond, info, extack);
+ if (err == -EOPNOTSUPP)
+ /* Offloading not supported, fall back to software LAG */
return 0;
- }
bridge_dev = netdev_master_upper_dev_get(bond);
if (!bridge_dev || !netif_is_bridge_master(bridge_dev))
int port);
int (*crosschip_lag_join)(struct dsa_switch *ds, int sw_index,
int port, struct dsa_lag lag,
- struct netdev_lag_upper_info *info);
+ struct netdev_lag_upper_info *info,
+ struct netlink_ext_ack *extack);
int (*crosschip_lag_leave)(struct dsa_switch *ds, int sw_index,
int port, struct dsa_lag lag);
int (*port_lag_change)(struct dsa_switch *ds, int port);
int (*port_lag_join)(struct dsa_switch *ds, int port,
struct dsa_lag lag,
- struct netdev_lag_upper_info *info);
+ struct netdev_lag_upper_info *info,
+ struct netlink_ext_ack *extack);
int (*port_lag_leave)(struct dsa_switch *ds, int port,
struct dsa_lag lag);
const struct net_device *bridge);
int ocelot_port_lag_join(struct ocelot *ocelot, int port,
struct net_device *bond,
- struct netdev_lag_upper_info *info);
+ struct netdev_lag_upper_info *info,
+ struct netlink_ext_ack *extack);
void ocelot_port_lag_leave(struct ocelot *ocelot, int port,
struct net_device *bond);
void ocelot_port_lag_change(struct ocelot *ocelot, int port, bool lag_tx_active);
const struct dsa_port *dp;
struct dsa_lag lag;
struct netdev_lag_upper_info *info;
+ struct netlink_ext_ack *extack;
};
/* DSA_NOTIFIER_VLAN_* */
struct dsa_notifier_lag_info info = {
.dp = dp,
.info = uinfo,
+ .extack = extack,
};
struct net_device *bridge_dev;
int err;
{
if (info->dp->ds == ds && ds->ops->port_lag_join)
return ds->ops->port_lag_join(ds, info->dp->index, info->lag,
- info->info);
+ info->info, info->extack);
if (info->dp->ds != ds && ds->ops->crosschip_lag_join)
return ds->ops->crosschip_lag_join(ds, info->dp->ds->index,
info->dp->index, info->lag,
- info->info);
+ info->info, info->extack);
return -EOPNOTSUPP;
}