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: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Vladimir Oltean 2021-12-10 01:34:38 +02:00 committed by David S. Miller
parent dc452a471d
commit 35d9768021
3 changed files with 94 additions and 55 deletions

View file

@ -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;
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);
case DSA_TAG_PROTO_OCELOT:
case DSA_TAG_PROTO_SEVILLE:
return 0;
default:
return -EPROTONOSUPPORT;
}
}
/* 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,

View file

@ -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);

View file

@ -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,
};