Commit de1b23b9 authored by David S. Miller's avatar David S. Miller

Merge branch 'dsa-deferred-xmit'

Vladimir Oltean says:

====================
Improvements to the DSA deferred xmit

After the feedback received on v1:
https://www.spinics.net/lists/netdev/msg622617.html

I've decided to move the deferred xmit implementation completely within
the sja1105 driver.

The executive summary for this series is the same as it was for v1
(better for everybody):

- For those who don't use it, thanks to one less assignment in the
  hotpath (and now also thanks to less code in the DSA core)
- For those who do, by making its scheduling more amenable and moving it
  outside the generic workqueue (since it still deals with packet
  hotpath, after all)

There are some simplification (1/3) and cosmetic (3/3) patches in the
areas next to the code touched by the main patch (2/3).
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 8bd17dc6 2821d50f
......@@ -426,14 +426,6 @@ static int sja1105_init_general_params(struct sja1105_private *priv)
.tpid2 = ETH_P_SJA1105,
};
struct sja1105_table *table;
int i, k = 0;
for (i = 0; i < SJA1105_NUM_PORTS; i++) {
if (dsa_is_dsa_port(priv->ds, i))
default_general_params.casc_port = i;
else if (dsa_is_user_port(priv->ds, i))
priv->ports[i].mgmt_slot = k++;
}
table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS];
......@@ -1740,6 +1732,16 @@ static int sja1105_setup(struct dsa_switch *ds)
static void sja1105_teardown(struct dsa_switch *ds)
{
struct sja1105_private *priv = ds->priv;
int port;
for (port = 0; port < SJA1105_NUM_PORTS; port++) {
struct sja1105_port *sp = &priv->ports[port];
if (!dsa_is_user_port(ds, port))
continue;
kthread_destroy_worker(sp->xmit_worker);
}
sja1105_tas_teardown(ds);
sja1105_ptp_clock_unregister(ds);
......@@ -1761,6 +1763,18 @@ static int sja1105_port_enable(struct dsa_switch *ds, int port,
return 0;
}
static void sja1105_port_disable(struct dsa_switch *ds, int port)
{
struct sja1105_private *priv = ds->priv;
struct sja1105_port *sp = &priv->ports[port];
if (!dsa_is_user_port(ds, port))
return;
kthread_cancel_work_sync(&sp->xmit_work);
skb_queue_purge(&sp->xmit_queue);
}
static int sja1105_mgmt_xmit(struct dsa_switch *ds, int port, int slot,
struct sk_buff *skb, bool takets)
{
......@@ -1819,47 +1833,36 @@ static int sja1105_mgmt_xmit(struct dsa_switch *ds, int port, int slot,
return NETDEV_TX_OK;
}
#define work_to_port(work) \
container_of((work), struct sja1105_port, xmit_work)
#define tagger_to_sja1105(t) \
container_of((t), struct sja1105_private, tagger_data)
/* Deferred work is unfortunately necessary because setting up the management
* route cannot be done from atomit context (SPI transfer takes a sleepable
* lock on the bus)
*/
static netdev_tx_t sja1105_port_deferred_xmit(struct dsa_switch *ds, int port,
struct sk_buff *skb)
static void sja1105_port_deferred_xmit(struct kthread_work *work)
{
struct sja1105_private *priv = ds->priv;
struct sja1105_port *sp = &priv->ports[port];
int slot = sp->mgmt_slot;
struct sk_buff *clone;
/* The tragic fact about the switch having 4x2 slots for installing
* management routes is that all of them except one are actually
* useless.
* If 2 slots are simultaneously configured for two BPDUs sent to the
* same (multicast) DMAC but on different egress ports, the switch
* would confuse them and redirect first frame it receives on the CPU
* port towards the port configured on the numerically first slot
* (therefore wrong port), then second received frame on second slot
* (also wrong port).
* So for all practical purposes, there needs to be a lock that
* prevents that from happening. The slot used here is utterly useless
* (could have simply been 0 just as fine), but we are doing it
* nonetheless, in case a smarter idea ever comes up in the future.
*/
mutex_lock(&priv->mgmt_lock);
struct sja1105_port *sp = work_to_port(work);
struct sja1105_tagger_data *tagger_data = sp->data;
struct sja1105_private *priv = tagger_to_sja1105(tagger_data);
int port = sp - priv->ports;
struct sk_buff *skb;
/* The clone, if there, was made by dsa_skb_tx_timestamp */
clone = DSA_SKB_CB(skb)->clone;
while ((skb = skb_dequeue(&sp->xmit_queue)) != NULL) {
struct sk_buff *clone = DSA_SKB_CB(skb)->clone;
sja1105_mgmt_xmit(ds, port, slot, skb, !!clone);
mutex_lock(&priv->mgmt_lock);
if (!clone)
goto out;
sja1105_mgmt_xmit(priv->ds, port, 0, skb, !!clone);
sja1105_ptp_txtstamp_skb(ds, port, clone);
/* The clone, if there, was made by dsa_skb_tx_timestamp */
if (clone)
sja1105_ptp_txtstamp_skb(priv->ds, port, clone);
out:
mutex_unlock(&priv->mgmt_lock);
return NETDEV_TX_OK;
mutex_unlock(&priv->mgmt_lock);
}
}
/* The MAXAGE setting belongs to the L2 Forwarding Parameters table,
......@@ -1990,6 +1993,7 @@ static const struct dsa_switch_ops sja1105_switch_ops = {
.get_sset_count = sja1105_get_sset_count,
.get_ts_info = sja1105_get_ts_info,
.port_enable = sja1105_port_enable,
.port_disable = sja1105_port_disable,
.port_fdb_dump = sja1105_fdb_dump,
.port_fdb_add = sja1105_fdb_add,
.port_fdb_del = sja1105_fdb_del,
......@@ -2003,7 +2007,6 @@ static const struct dsa_switch_ops sja1105_switch_ops = {
.port_mdb_prepare = sja1105_mdb_prepare,
.port_mdb_add = sja1105_mdb_add,
.port_mdb_del = sja1105_mdb_del,
.port_deferred_xmit = sja1105_port_deferred_xmit,
.port_hwtstamp_get = sja1105_hwtstamp_get,
.port_hwtstamp_set = sja1105_hwtstamp_set,
.port_rxtstamp = sja1105_port_rxtstamp,
......@@ -2055,7 +2058,7 @@ static int sja1105_probe(struct spi_device *spi)
struct device *dev = &spi->dev;
struct sja1105_private *priv;
struct dsa_switch *ds;
int rc, i;
int rc, port;
if (!dev->of_node) {
dev_err(dev, "No DTS bindings for SJA1105 driver\n");
......@@ -2120,15 +2123,42 @@ static int sja1105_probe(struct spi_device *spi)
return rc;
/* Connections between dsa_port and sja1105_port */
for (i = 0; i < SJA1105_NUM_PORTS; i++) {
struct sja1105_port *sp = &priv->ports[i];
for (port = 0; port < SJA1105_NUM_PORTS; port++) {
struct sja1105_port *sp = &priv->ports[port];
struct dsa_port *dp = dsa_to_port(ds, port);
struct net_device *slave;
dsa_to_port(ds, i)->priv = sp;
sp->dp = dsa_to_port(ds, i);
if (!dsa_is_user_port(ds, port))
continue;
dp->priv = sp;
sp->dp = dp;
sp->data = tagger_data;
slave = dp->slave;
kthread_init_work(&sp->xmit_work, sja1105_port_deferred_xmit);
sp->xmit_worker = kthread_create_worker(0, "%s_xmit",
slave->name);
if (IS_ERR(sp->xmit_worker)) {
rc = PTR_ERR(sp->xmit_worker);
dev_err(ds->dev,
"failed to create deferred xmit thread: %d\n",
rc);
goto out;
}
skb_queue_head_init(&sp->xmit_queue);
}
return 0;
out:
while (port-- > 0) {
struct sja1105_port *sp = &priv->ports[port];
if (!dsa_is_user_port(ds, port))
continue;
kthread_destroy_worker(sp->xmit_worker);
}
return rc;
}
static int sja1105_remove(struct spi_device *spi)
......
......@@ -53,10 +53,12 @@ struct sja1105_skb_cb {
((struct sja1105_skb_cb *)DSA_SKB_CB_PRIV(skb))
struct sja1105_port {
struct kthread_worker *xmit_worker;
struct kthread_work xmit_work;
struct sk_buff_head xmit_queue;
struct sja1105_tagger_data *data;
struct dsa_port *dp;
bool hwts_tx_en;
int mgmt_slot;
};
#endif /* _NET_DSA_SJA1105_H */
......@@ -90,7 +90,6 @@ struct dsa_device_ops {
struct dsa_skb_cb {
struct sk_buff *clone;
bool deferred_xmit;
};
struct __dsa_skb_cb {
......@@ -192,9 +191,6 @@ struct dsa_port {
struct phylink *pl;
struct phylink_config pl_config;
struct work_struct xmit_work;
struct sk_buff_head xmit_queue;
struct list_head list;
/*
......@@ -564,11 +560,6 @@ struct dsa_switch_ops {
bool (*port_rxtstamp)(struct dsa_switch *ds, int port,
struct sk_buff *skb, unsigned int type);
/*
* Deferred frame Tx
*/
netdev_tx_t (*port_deferred_xmit)(struct dsa_switch *ds, int port,
struct sk_buff *skb);
/* Devlink parameters */
int (*devlink_param_get)(struct dsa_switch *ds, u32 id,
struct devlink_param_gset_ctx *ctx);
......
......@@ -162,8 +162,6 @@ int dsa_slave_resume(struct net_device *slave_dev);
int dsa_slave_register_notifier(void);
void dsa_slave_unregister_notifier(void);
void *dsa_defer_xmit(struct sk_buff *skb, struct net_device *dev);
static inline struct dsa_port *dsa_slave_to_port(const struct net_device *dev)
{
struct dsa_slave_priv *p = netdev_priv(dev);
......
......@@ -116,9 +116,6 @@ static int dsa_slave_close(struct net_device *dev)
struct net_device *master = dsa_slave_to_master(dev);
struct dsa_port *dp = dsa_slave_to_port(dev);
cancel_work_sync(&dp->xmit_work);
skb_queue_purge(&dp->xmit_queue);
phylink_stop(dp->pl);
dsa_port_disable(dp);
......@@ -518,7 +515,6 @@ static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev)
s->tx_bytes += skb->len;
u64_stats_update_end(&s->syncp);
DSA_SKB_CB(skb)->deferred_xmit = false;
DSA_SKB_CB(skb)->clone = NULL;
/* Identify PTP protocol packets, clone them, and pass them to the
......@@ -531,39 +527,13 @@ static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev)
*/
nskb = p->xmit(skb, dev);
if (!nskb) {
if (!DSA_SKB_CB(skb)->deferred_xmit)
kfree_skb(skb);
kfree_skb(skb);
return NETDEV_TX_OK;
}
return dsa_enqueue_skb(nskb, dev);
}
void *dsa_defer_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
DSA_SKB_CB(skb)->deferred_xmit = true;
skb_queue_tail(&dp->xmit_queue, skb);
schedule_work(&dp->xmit_work);
return NULL;
}
EXPORT_SYMBOL_GPL(dsa_defer_xmit);
static void dsa_port_xmit_work(struct work_struct *work)
{
struct dsa_port *dp = container_of(work, struct dsa_port, xmit_work);
struct dsa_switch *ds = dp->ds;
struct sk_buff *skb;
if (unlikely(!ds->ops->port_deferred_xmit))
return;
while ((skb = skb_dequeue(&dp->xmit_queue)) != NULL)
ds->ops->port_deferred_xmit(ds, dp->index, skb);
}
/* ethtool operations *******************************************************/
static void dsa_slave_get_drvinfo(struct net_device *dev,
......@@ -1367,9 +1337,6 @@ int dsa_slave_suspend(struct net_device *slave_dev)
if (!netif_running(slave_dev))
return 0;
cancel_work_sync(&dp->xmit_work);
skb_queue_purge(&dp->xmit_queue);
netif_device_detach(slave_dev);
rtnl_lock();
......@@ -1455,8 +1422,6 @@ int dsa_slave_create(struct dsa_port *port)
}
p->dp = port;
INIT_LIST_HEAD(&p->mall_tc_list);
INIT_WORK(&port->xmit_work, dsa_port_xmit_work);
skb_queue_head_init(&port->xmit_queue);
p->xmit = cpu_dp->tag_ops->xmit;
port->slave = slave_dev;
......
......@@ -83,12 +83,24 @@ static bool sja1105_filter(const struct sk_buff *skb, struct net_device *dev)
return false;
}
/* Calls sja1105_port_deferred_xmit in sja1105_main.c */
static struct sk_buff *sja1105_defer_xmit(struct sja1105_port *sp,
struct sk_buff *skb)
{
/* Increase refcount so the kfree_skb in dsa_slave_xmit
* won't really free the packet.
*/
skb_queue_tail(&sp->xmit_queue, skb_get(skb));
kthread_queue_work(sp->xmit_worker, &sp->xmit_work);
return NULL;
}
static struct sk_buff *sja1105_xmit(struct sk_buff *skb,
struct net_device *netdev)
{
struct dsa_port *dp = dsa_slave_to_port(netdev);
struct dsa_switch *ds = dp->ds;
u16 tx_vid = dsa_8021q_tx_vid(ds, dp->index);
u16 tx_vid = dsa_8021q_tx_vid(dp->ds, dp->index);
u16 queue_mapping = skb_get_queue_mapping(skb);
u8 pcp = netdev_txq_to_tc(netdev, queue_mapping);
......@@ -97,7 +109,7 @@ static struct sk_buff *sja1105_xmit(struct sk_buff *skb,
* is the .port_deferred_xmit driver callback.
*/
if (unlikely(sja1105_is_link_local(skb)))
return dsa_defer_xmit(skb, netdev);
return sja1105_defer_xmit(dp->priv, skb);
/* If we are under a vlan_filtering bridge, IP termination on
* switch ports based on 802.1Q tags is simply too brittle to
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment