Skip to content

Commit

Permalink
Merge branch 'felix-dsa-driver-fixes'
Browse files Browse the repository at this point in the history
Vladimir Oltean says:

====================
Felix DSA driver fixes

This is an assorted collection of fixes for issues seen on the NXP
LS1028A switch.

- PTP packet drops due to switch congestion result in catastrophic
  damage to the driver's state
- loops are not blocked by STP if using the ocelot-8021q tagger
- driver uses the wrong CPU port when two of them are defined in DT
- module autoloading is broken* with both tagging protocol drivers
  (ocelot and ocelot-8021q)

Changes in v2:
- Stop printing that we aren't going to take TX timestamps if we don't
  have TX timestamping anyway, and we are just carrying PTP frames for a
  cascaded DSA switch.
- Shorten the deferred xmit kthread name so that it fits the 16
  character limit (TASK_COMM_LEN)
====================

Link: https://lore.kernel.org/r/20211012114044.2526146-1-vladimir.oltean@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
Jakub Kicinski committed Oct 13, 2021
2 parents 3af760e + 8d5f795 commit 847c6bd
Show file tree
Hide file tree
Showing 10 changed files with 291 additions and 115 deletions.
149 changes: 137 additions & 12 deletions drivers/net/dsa/ocelot/felix.c
Original file line number Diff line number Diff line change
Expand Up @@ -266,12 +266,12 @@ static void felix_8021q_cpu_port_deinit(struct ocelot *ocelot, int port)
*/
static int felix_setup_mmio_filtering(struct felix *felix)
{
unsigned long user_ports = 0, cpu_ports = 0;
unsigned long user_ports = dsa_user_ports(felix->ds);
struct ocelot_vcap_filter *redirect_rule;
struct ocelot_vcap_filter *tagging_rule;
struct ocelot *ocelot = &felix->ocelot;
struct dsa_switch *ds = felix->ds;
int port, ret;
int cpu = -1, port, ret;

tagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL);
if (!tagging_rule)
Expand All @@ -284,12 +284,15 @@ static int felix_setup_mmio_filtering(struct felix *felix)
}

for (port = 0; port < ocelot->num_phys_ports; port++) {
if (dsa_is_user_port(ds, port))
user_ports |= BIT(port);
if (dsa_is_cpu_port(ds, port))
cpu_ports |= BIT(port);
if (dsa_is_cpu_port(ds, port)) {
cpu = port;
break;
}
}

if (cpu < 0)
return -EINVAL;

tagging_rule->key_type = OCELOT_VCAP_KEY_ETYPE;
*(__be16 *)tagging_rule->key.etype.etype.value = htons(ETH_P_1588);
*(__be16 *)tagging_rule->key.etype.etype.mask = htons(0xffff);
Expand Down Expand Up @@ -325,7 +328,7 @@ static int felix_setup_mmio_filtering(struct felix *felix)
* the CPU port module
*/
redirect_rule->action.mask_mode = OCELOT_MASK_MODE_REDIRECT;
redirect_rule->action.port_mask = cpu_ports;
redirect_rule->action.port_mask = BIT(cpu);
} else {
/* Trap PTP packets only to the CPU port module (which is
* redirected to the NPI port)
Expand Down Expand Up @@ -1074,6 +1077,101 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
return 0;
}

static void ocelot_port_purge_txtstamp_skb(struct ocelot *ocelot, int port,
struct sk_buff *skb)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
struct sk_buff *clone = OCELOT_SKB_CB(skb)->clone;
struct sk_buff *skb_match = NULL, *skb_tmp;
unsigned long flags;

if (!clone)
return;

spin_lock_irqsave(&ocelot_port->tx_skbs.lock, flags);

skb_queue_walk_safe(&ocelot_port->tx_skbs, skb, skb_tmp) {
if (skb != clone)
continue;
__skb_unlink(skb, &ocelot_port->tx_skbs);
skb_match = skb;
break;
}

spin_unlock_irqrestore(&ocelot_port->tx_skbs.lock, flags);

WARN_ONCE(!skb_match,
"Could not find skb clone in TX timestamping list\n");
}

#define work_to_xmit_work(w) \
container_of((w), struct felix_deferred_xmit_work, work)

static void felix_port_deferred_xmit(struct kthread_work *work)
{
struct felix_deferred_xmit_work *xmit_work = work_to_xmit_work(work);
struct dsa_switch *ds = xmit_work->dp->ds;
struct sk_buff *skb = xmit_work->skb;
u32 rew_op = ocelot_ptp_rew_op(skb);
struct ocelot *ocelot = ds->priv;
int port = xmit_work->dp->index;
int retries = 10;

do {
if (ocelot_can_inject(ocelot, 0))
break;

cpu_relax();
} while (--retries);

if (!retries) {
dev_err(ocelot->dev, "port %d failed to inject skb\n",
port);
ocelot_port_purge_txtstamp_skb(ocelot, port, skb);
kfree_skb(skb);
return;
}

ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb);

consume_skb(skb);
kfree(xmit_work);
}

static int felix_port_setup_tagger_data(struct dsa_switch *ds, int port)
{
struct dsa_port *dp = dsa_to_port(ds, port);
struct ocelot *ocelot = ds->priv;
struct felix *felix = ocelot_to_felix(ocelot);
struct felix_port *felix_port;

if (!dsa_port_is_user(dp))
return 0;

felix_port = kzalloc(sizeof(*felix_port), GFP_KERNEL);
if (!felix_port)
return -ENOMEM;

felix_port->xmit_worker = felix->xmit_worker;
felix_port->xmit_work_fn = felix_port_deferred_xmit;

dp->priv = felix_port;

return 0;
}

static void felix_port_teardown_tagger_data(struct dsa_switch *ds, int port)
{
struct dsa_port *dp = dsa_to_port(ds, port);
struct felix_port *felix_port = dp->priv;

if (!felix_port)
return;

dp->priv = NULL;
kfree(felix_port);
}

/* Hardware initialization done here so that we can allocate structures with
* devm without fear of dsa_register_switch returning -EPROBE_DEFER and causing
* us to allocate structures twice (leak memory) and map PCI memory twice
Expand Down Expand Up @@ -1102,6 +1200,12 @@ static int felix_setup(struct dsa_switch *ds)
}
}

felix->xmit_worker = kthread_create_worker(0, "felix_xmit");
if (IS_ERR(felix->xmit_worker)) {
err = PTR_ERR(felix->xmit_worker);
goto out_deinit_timestamp;
}

for (port = 0; port < ds->num_ports; port++) {
if (dsa_is_unused_port(ds, port))
continue;
Expand All @@ -1112,6 +1216,14 @@ static int felix_setup(struct dsa_switch *ds)
* bits of vlan tag.
*/
felix_port_qos_map_init(ocelot, port);

err = felix_port_setup_tagger_data(ds, port);
if (err) {
dev_err(ds->dev,
"port %d failed to set up tagger data: %pe\n",
port, ERR_PTR(err));
goto out_deinit_ports;
}
}

err = ocelot_devlink_sb_register(ocelot);
Expand All @@ -1126,6 +1238,7 @@ static int felix_setup(struct dsa_switch *ds)
* there's no real point in checking for errors.
*/
felix_set_tag_protocol(ds, port, felix->tag_proto);
break;
}

ds->mtu_enforcement_ingress = true;
Expand All @@ -1138,9 +1251,13 @@ static int felix_setup(struct dsa_switch *ds)
if (dsa_is_unused_port(ds, port))
continue;

felix_port_teardown_tagger_data(ds, port);
ocelot_deinit_port(ocelot, port);
}

kthread_destroy_worker(felix->xmit_worker);

out_deinit_timestamp:
ocelot_deinit_timestamp(ocelot);
ocelot_deinit(ocelot);

Expand All @@ -1162,19 +1279,23 @@ static void felix_teardown(struct dsa_switch *ds)
continue;

felix_del_tag_protocol(ds, port, felix->tag_proto);
break;
}

ocelot_devlink_sb_unregister(ocelot);
ocelot_deinit_timestamp(ocelot);
ocelot_deinit(ocelot);

for (port = 0; port < ocelot->num_phys_ports; port++) {
if (dsa_is_unused_port(ds, port))
continue;

felix_port_teardown_tagger_data(ds, port);
ocelot_deinit_port(ocelot, port);
}

kthread_destroy_worker(felix->xmit_worker);

ocelot_devlink_sb_unregister(ocelot);
ocelot_deinit_timestamp(ocelot);
ocelot_deinit(ocelot);

if (felix->info->mdio_bus_free)
felix->info->mdio_bus_free(ocelot);
}
Expand Down Expand Up @@ -1291,8 +1412,12 @@ static void felix_txtstamp(struct dsa_switch *ds, int port,
if (!ocelot->ptp)
return;

if (ocelot_port_txtstamp_request(ocelot, port, skb, &clone))
if (ocelot_port_txtstamp_request(ocelot, port, skb, &clone)) {
dev_err_ratelimited(ds->dev,
"port %d delivering skb without TX timestamp\n",
port);
return;
}

if (clone)
OCELOT_SKB_CB(skb)->clone = clone;
Expand Down
1 change: 1 addition & 0 deletions drivers/net/dsa/ocelot/felix.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ struct felix {
resource_size_t switch_base;
resource_size_t imdio_base;
enum dsa_tag_protocol tag_proto;
struct kthread_worker *xmit_worker;
};

struct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port);
Expand Down
Loading

0 comments on commit 847c6bd

Please sign in to comment.