Subject: [PATCH net-next 2/2] net: dsa: Create a kernel thread for each port's deferred xmit work

From: Vladimir Oltean <[email-masked]>
Date: Fri, 27 Dec 2019 03:42:08 +0200

Because this callback can be used on certain switches (at the moment
only sja1105) to do TX timstamping, this should have been a kernel
thread from the get-go.

PTP is a time-critical protocol and requires the ability to control the
scheduling priority with tools such as chrt, as opposed to just
executing the work in the generic workqueue.

If the user switch ports are named swp0, swp1, swp2, the kernel threads
will be named dsa_swp0_xmit, dsa_swp1_xmit, dsa_swp2_xmit.

Signed-off-by: Vladimir Oltean <[email-masked]>
---
 include/net/dsa.h |  3 ++-
 net/dsa/slave.c   | 43 ++++++++++++++++++++++++++++++++++---------
 2 files changed, 36 insertions(+), 10 deletions(-)

diff --git a/include/net/dsa.h b/include/net/dsa.h
index 5d510a4da5d0..8460eae69dd9 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -189,7 +189,8 @@ struct dsa_port {
 	struct phylink		*pl;
 	struct phylink_config	pl_config;
 
-	struct work_struct	xmit_work;
+	struct kthread_worker	*xmit_worker;
+	struct kthread_work	xmit_work;
 	struct sk_buff_head	xmit_queue;
 
 	struct list_head list;
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 9f7e47dcdc20..c7e8a29e1e86 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -115,9 +115,12 @@ 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);
+	struct dsa_switch *ds = dp->ds;
 
-	cancel_work_sync(&dp->xmit_work);
-	skb_queue_purge(&dp->xmit_queue);
+	if (ds->ops->port_deferred_xmit) {
+		kthread_cancel_work_sync(&dp->xmit_work);
+		skb_queue_purge(&dp->xmit_queue);
+	}
 
 	phylink_stop(dp->pl);
 
@@ -545,13 +548,13 @@ void *dsa_defer_xmit(struct sk_buff *skb, struct net_device *dev)
 	 * won't really free the packet.
 	 */
 	skb_queue_tail(&dp->xmit_queue, skb_get(skb));
-	schedule_work(&dp->xmit_work);
+	kthread_queue_work(dp->xmit_worker, &dp->xmit_work);
 
 	return NULL;
 }
 EXPORT_SYMBOL_GPL(dsa_defer_xmit);
 
-static void dsa_port_xmit_work(struct work_struct *work)
+static void dsa_port_xmit_work(struct kthread_work *work)
 {
 	struct dsa_port *dp = container_of(work, struct dsa_port, xmit_work);
 	struct dsa_switch *ds = dp->ds;
@@ -1363,12 +1366,15 @@ static int dsa_slave_phy_setup(struct net_device *slave_dev)
 int dsa_slave_suspend(struct net_device *slave_dev)
 {
 	struct dsa_port *dp = dsa_slave_to_port(slave_dev);
+	struct dsa_switch *ds = dp->ds;
 
 	if (!netif_running(slave_dev))
 		return 0;
 
-	cancel_work_sync(&dp->xmit_work);
-	skb_queue_purge(&dp->xmit_queue);
+	if (ds->ops->port_deferred_xmit) {
+		kthread_cancel_work_sync(&dp->xmit_work);
+		skb_queue_purge(&dp->xmit_queue);
+	}
 
 	netif_device_detach(slave_dev);
 
@@ -1455,17 +1461,29 @@ 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;
 
+	if (ds->ops->port_deferred_xmit) {
+		kthread_init_work(&port->xmit_work, dsa_port_xmit_work);
+		port->xmit_worker = kthread_create_worker(0, "dsa_%s_xmit",
+							  slave_dev->name);
+		if (IS_ERR(port->xmit_worker)) {
+			ret = PTR_ERR(port->xmit_worker);
+			netdev_err(master,
+				   "failed to create deferred xmit thread: %d\n",
+				   ret);
+			goto out_free;
+		}
+		skb_queue_head_init(&port->xmit_queue);
+	}
+
 	netif_carrier_off(slave_dev);
 
 	ret = dsa_slave_phy_setup(slave_dev);
 	if (ret) {
 		netdev_err(master, "error %d setting up slave phy\n", ret);
-		goto out_free;
+		goto out_destroy;
 	}
 
 	dsa_slave_notify(slave_dev, DSA_PORT_REGISTER);
@@ -1484,6 +1502,9 @@ int dsa_slave_create(struct dsa_port *port)
 	phylink_disconnect_phy(p->dp->pl);
 	rtnl_unlock();
 	phylink_destroy(p->dp->pl);
+out_destroy:
+	if (ds->ops->port_deferred_xmit)
+		kthread_destroy_worker(port->xmit_worker);
 out_free:
 	free_percpu(p->stats64);
 	free_netdev(slave_dev);
@@ -1495,6 +1516,10 @@ void dsa_slave_destroy(struct net_device *slave_dev)
 {
 	struct dsa_port *dp = dsa_slave_to_port(slave_dev);
 	struct dsa_slave_priv *p = netdev_priv(slave_dev);
+	struct dsa_switch *ds = dp->ds;
+
+	if (ds->ops->port_deferred_xmit)
+		kthread_destroy_worker(dp->xmit_worker);
 
 	netif_carrier_off(slave_dev);
 	rtnl_lock();
-- 
2.17.1