Commit 35d97680 authored by Vladimir Oltean's avatar Vladimir Oltean Committed by David S. Miller
Browse files

net: dsa: tag_ocelot: convert to tagger-owned data



The felix driver makes very light use of dp->priv, and the tagger is
effectively stateless. dp->priv is practically only needed to set up a
callback to perform deferred xmit of PTP and STP packets using the
ocelot-8021q tagging protocol (the main ocelot tagging protocol makes no
use of dp->priv, although this driver sets up dp->priv irrespective of
actual tagging protocol in use).

struct felix_port (what used to be pointed to by dp->priv) is removed
and replaced with a two-sided structure. The public side of this
structure, visible to the switch driver, is ocelot_8021q_tagger_data.
The private side is ocelot_8021q_tagger_private, and the latter
structure physically encapsulates the former. The public half of the
tagger data structure can be accessed through a helper of the same name
(ocelot_8021q_tagger_data) which also sanity-checks the protocol
currently in use by the switch. The public/private split was requested
by Andrew Lunn.

Suggested-by: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent dc452a47
Loading
Loading
Loading
Loading
+14 −50
Original line number Diff line number Diff line
@@ -1155,38 +1155,22 @@ static void felix_port_deferred_xmit(struct kthread_work *work)
	kfree(xmit_work);
}

static int felix_port_setup_tagger_data(struct dsa_switch *ds, int port)
static int felix_connect_tag_protocol(struct dsa_switch *ds,
				      enum dsa_tag_protocol proto)
{
	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;
	struct ocelot_8021q_tagger_data *tagger_data;

	if (!dsa_port_is_user(dp))
	switch (proto) {
	case DSA_TAG_PROTO_OCELOT_8021Q:
		tagger_data = ocelot_8021q_tagger_data(ds);
		tagger_data->xmit_work_fn = felix_port_deferred_xmit;
		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;

	case DSA_TAG_PROTO_OCELOT:
	case DSA_TAG_PROTO_SEVILLE:
		return 0;
	default:
		return -EPROTONOSUPPORT;
	}

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
@@ -1217,12 +1201,6 @@ 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;
@@ -1233,14 +1211,6 @@ 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);
@@ -1268,13 +1238,9 @@ 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);

@@ -1303,12 +1269,9 @@ static void felix_teardown(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);

	ocelot_devlink_sb_unregister(ocelot);
	ocelot_deinit_timestamp(ocelot);
	ocelot_deinit(ocelot);
@@ -1648,6 +1611,7 @@ felix_mrp_del_ring_role(struct dsa_switch *ds, int port,
const struct dsa_switch_ops felix_switch_ops = {
	.get_tag_protocol		= felix_get_tag_protocol,
	.change_tag_protocol		= felix_change_tag_protocol,
	.connect_tag_protocol		= felix_connect_tag_protocol,
	.setup				= felix_setup,
	.teardown			= felix_teardown,
	.set_ageing_time		= felix_set_ageing_time,
+10 −2
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
#include <linux/kthread.h>
#include <linux/packing.h>
#include <linux/skbuff.h>
#include <net/dsa.h>

struct ocelot_skb_cb {
	struct sk_buff *clone;
@@ -168,11 +169,18 @@ struct felix_deferred_xmit_work {
	struct kthread_work work;
};

struct felix_port {
struct ocelot_8021q_tagger_data {
	void (*xmit_work_fn)(struct kthread_work *work);
	struct kthread_worker *xmit_worker;
};

static inline struct ocelot_8021q_tagger_data *
ocelot_8021q_tagger_data(struct dsa_switch *ds)
{
	BUG_ON(ds->dst->tag_ops->proto != DSA_TAG_PROTO_OCELOT_8021Q);

	return ds->tagger_data;
}

static inline void ocelot_xfh_get_rew_val(void *extraction, u64 *rew_val)
{
	packing(extraction, rew_val, 116, 85, OCELOT_TAG_LEN, UNPACK, 0);
+70 −3
Original line number Diff line number Diff line
@@ -12,25 +12,39 @@
#include <linux/dsa/ocelot.h>
#include "dsa_priv.h"

struct ocelot_8021q_tagger_private {
	struct ocelot_8021q_tagger_data data; /* Must be first */
	struct kthread_worker *xmit_worker;
};

static struct sk_buff *ocelot_defer_xmit(struct dsa_port *dp,
					 struct sk_buff *skb)
{
	struct ocelot_8021q_tagger_private *priv = dp->ds->tagger_data;
	struct ocelot_8021q_tagger_data *data = &priv->data;
	void (*xmit_work_fn)(struct kthread_work *work);
	struct felix_deferred_xmit_work *xmit_work;
	struct felix_port *felix_port = dp->priv;
	struct kthread_worker *xmit_worker;

	xmit_work_fn = data->xmit_work_fn;
	xmit_worker = priv->xmit_worker;

	if (!xmit_work_fn || !xmit_worker)
		return NULL;

	xmit_work = kzalloc(sizeof(*xmit_work), GFP_ATOMIC);
	if (!xmit_work)
		return NULL;

	/* Calls felix_port_deferred_xmit in felix.c */
	kthread_init_work(&xmit_work->work, felix_port->xmit_work_fn);
	kthread_init_work(&xmit_work->work, xmit_work_fn);
	/* Increase refcount so the kfree_skb in dsa_slave_xmit
	 * won't really free the packet.
	 */
	xmit_work->dp = dp;
	xmit_work->skb = skb_get(skb);

	kthread_queue_work(felix_port->xmit_worker, &xmit_work->work);
	kthread_queue_work(xmit_worker, &xmit_work->work);

	return NULL;
}
@@ -67,11 +81,64 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb,
	return skb;
}

static void ocelot_disconnect(struct dsa_switch_tree *dst)
{
	struct ocelot_8021q_tagger_private *priv;
	struct dsa_port *dp;

	list_for_each_entry(dp, &dst->ports, list) {
		priv = dp->ds->tagger_data;

		if (!priv)
			continue;

		if (priv->xmit_worker)
			kthread_destroy_worker(priv->xmit_worker);

		kfree(priv);
		dp->ds->tagger_data = NULL;
	}
}

static int ocelot_connect(struct dsa_switch_tree *dst)
{
	struct ocelot_8021q_tagger_private *priv;
	struct dsa_port *dp;
	int err;

	list_for_each_entry(dp, &dst->ports, list) {
		if (dp->ds->tagger_data)
			continue;

		priv = kzalloc(sizeof(*priv), GFP_KERNEL);
		if (!priv) {
			err = -ENOMEM;
			goto out;
		}

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

		dp->ds->tagger_data = priv;
	}

	return 0;

out:
	ocelot_disconnect(dst);
	return err;
}

static const struct dsa_device_ops ocelot_8021q_netdev_ops = {
	.name			= "ocelot-8021q",
	.proto			= DSA_TAG_PROTO_OCELOT_8021Q,
	.xmit			= ocelot_xmit,
	.rcv			= ocelot_rcv,
	.connect		= ocelot_connect,
	.disconnect		= ocelot_disconnect,
	.needed_headroom	= VLAN_HLEN,
	.promisc_on_master	= true,
};