Skip to content

Commit

Permalink
Merge branch 'dsa-master-ethtool-move'
Browse files Browse the repository at this point in the history
Vivien Didelot says:

====================
net: dsa: move master ethtool code

The DSA core overrides the master device's ethtool_ops structure so that
it can inject statistics and such of its dedicated switch CPU port.

This ethtool code is currently called on unnecessary conditions or
before the master interface and its switch CPU port get wired up.
This patchset fixes this.

Similarly to slave.c where the DSA slave net_device is the entry point
of the dsa_slave_* functions, this patchset also isolates the master's
ethtool code in a new master.c file, where the DSA master net_device is
the entry point of the dsa_master_* functions.

This is a first step towards better control of the master device and
support for multiple CPU ports.
====================

Tested-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Sep 19, 2017
2 parents 69e33b2 + f2f2356 commit d43a9d1
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 130 deletions.
1 change: 0 additions & 1 deletion include/net/dsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,6 @@ struct dsa_port {
/*
* Original copy of the master netdev ethtool_ops
*/
struct ethtool_ops ethtool_ops;
const struct ethtool_ops *orig_ethtool_ops;
};

Expand Down
2 changes: 1 addition & 1 deletion net/dsa/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# the core
obj-$(CONFIG_NET_DSA) += dsa_core.o
dsa_core-y += dsa.o dsa2.o legacy.o port.o slave.o switch.o
dsa_core-y += dsa.o dsa2.o legacy.o master.o port.o slave.o switch.o

# tagging formats
dsa_core-$(CONFIG_NET_DSA_TAG_BRCM) += tag_brcm.o
Expand Down
28 changes: 0 additions & 28 deletions net/dsa/dsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,34 +112,6 @@ const struct dsa_device_ops *dsa_resolve_tag_protocol(int tag_protocol)
return ops;
}

int dsa_cpu_port_ethtool_setup(struct dsa_port *cpu_dp)
{
struct dsa_switch *ds = cpu_dp->ds;
struct net_device *master;
struct ethtool_ops *cpu_ops;

master = cpu_dp->netdev;

cpu_ops = devm_kzalloc(ds->dev, sizeof(*cpu_ops), GFP_KERNEL);
if (!cpu_ops)
return -ENOMEM;

memcpy(&cpu_dp->ethtool_ops, master->ethtool_ops,
sizeof(struct ethtool_ops));
cpu_dp->orig_ethtool_ops = master->ethtool_ops;
memcpy(cpu_ops, &cpu_dp->ethtool_ops,
sizeof(struct ethtool_ops));
dsa_cpu_port_ethtool_init(cpu_ops);
master->ethtool_ops = cpu_ops;

return 0;
}

void dsa_cpu_port_ethtool_restore(struct dsa_port *cpu_dp)
{
cpu_dp->netdev->ethtool_ops = cpu_dp->orig_ethtool_ops;
}

void dsa_cpu_dsa_destroy(struct dsa_port *port)
{
struct device_node *port_dn = port->dn;
Expand Down
18 changes: 8 additions & 10 deletions net/dsa/dsa2.c
Original file line number Diff line number Diff line change
Expand Up @@ -433,18 +433,17 @@ static int dsa_dst_apply(struct dsa_switch_tree *dst)
return err;
}

if (dst->cpu_dp) {
err = dsa_cpu_port_ethtool_setup(dst->cpu_dp);
if (err)
return err;
}

/* If we use a tagging format that doesn't have an ethertype
* field, make sure that all packets from this point on get
* sent to the tag format's receive function.
*/
wmb();
dst->cpu_dp->netdev->dsa_ptr = dst;

err = dsa_master_ethtool_setup(dst->cpu_dp->netdev);
if (err)
return err;

dst->applied = true;

return 0;
Expand All @@ -458,6 +457,8 @@ static void dsa_dst_unapply(struct dsa_switch_tree *dst)
if (!dst->applied)
return;

dsa_master_ethtool_restore(dst->cpu_dp->netdev);

dst->cpu_dp->netdev->dsa_ptr = NULL;

/* If we used a tagging format that doesn't have an ethertype
Expand All @@ -474,10 +475,7 @@ static void dsa_dst_unapply(struct dsa_switch_tree *dst)
dsa_ds_unapply(dst, ds);
}

if (dst->cpu_dp) {
dsa_cpu_port_ethtool_restore(dst->cpu_dp);
dst->cpu_dp = NULL;
}
dst->cpu_dp = NULL;

pr_info("DSA: tree %d unapplied\n", dst->tree);
dst->applied = false;
Expand Down
7 changes: 4 additions & 3 deletions net/dsa/dsa_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,6 @@ struct dsa_slave_priv {
int dsa_cpu_dsa_setup(struct dsa_port *port);
void dsa_cpu_dsa_destroy(struct dsa_port *dport);
const struct dsa_device_ops *dsa_resolve_tag_protocol(int tag_protocol);
int dsa_cpu_port_ethtool_setup(struct dsa_port *cpu_dp);
void dsa_cpu_port_ethtool_restore(struct dsa_port *cpu_dp);
bool dsa_schedule_work(struct work_struct *work);

/* legacy.c */
Expand All @@ -112,6 +110,10 @@ int dsa_legacy_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid);

/* master.c */
int dsa_master_ethtool_setup(struct net_device *dev);
void dsa_master_ethtool_restore(struct net_device *dev);

/* port.c */
int dsa_port_set_state(struct dsa_port *dp, u8 state,
struct switchdev_trans *trans);
Expand Down Expand Up @@ -139,7 +141,6 @@ int dsa_port_vlan_del(struct dsa_port *dp,
/* slave.c */
extern const struct dsa_device_ops notag_netdev_ops;
void dsa_slave_mii_bus_init(struct dsa_switch *ds);
void dsa_cpu_port_ethtool_init(struct ethtool_ops *ops);
int dsa_slave_create(struct dsa_port *port, const char *name);
void dsa_slave_destroy(struct net_device *slave_dev);
int dsa_slave_suspend(struct net_device *slave_dev);
Expand Down
10 changes: 3 additions & 7 deletions net/dsa/legacy.c
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,6 @@ static int dsa_switch_setup_one(struct dsa_switch *ds,
netdev_err(master, "[%d] : can't configure CPU and DSA ports\n",
index);

ret = dsa_cpu_port_ethtool_setup(ds->dst->cpu_dp);
if (ret)
return ret;

return 0;
}

Expand Down Expand Up @@ -606,7 +602,7 @@ static int dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev,
wmb();
dev->dsa_ptr = dst;

return 0;
return dsa_master_ethtool_setup(dst->cpu_dp->netdev);
}

static int dsa_probe(struct platform_device *pdev)
Expand Down Expand Up @@ -671,6 +667,8 @@ static void dsa_remove_dst(struct dsa_switch_tree *dst)
{
int i;

dsa_master_ethtool_restore(dst->cpu_dp->netdev);

dst->cpu_dp->netdev->dsa_ptr = NULL;

/* If we used a tagging format that doesn't have an ethertype
Expand All @@ -686,8 +684,6 @@ static void dsa_remove_dst(struct dsa_switch_tree *dst)
dsa_switch_destroy(ds);
}

dsa_cpu_port_ethtool_restore(dst->cpu_dp);

dev_put(dst->cpu_dp->netdev);
}

Expand Down
120 changes: 120 additions & 0 deletions net/dsa/master.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* Handling of a master device, switching frames via its switch fabric CPU port
*
* Copyright (c) 2017 Savoir-faire Linux Inc.
* Vivien Didelot <vivien.didelot@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/

#include "dsa_priv.h"

static void dsa_master_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *stats,
uint64_t *data)
{
struct dsa_switch_tree *dst = dev->dsa_ptr;
struct dsa_port *port = dst->cpu_dp;
struct dsa_switch *ds = port->ds;
const struct ethtool_ops *ops = port->orig_ethtool_ops;
int count = 0;

if (ops && ops->get_sset_count && ops->get_ethtool_stats) {
count = ops->get_sset_count(dev, ETH_SS_STATS);
ops->get_ethtool_stats(dev, stats, data);
}

if (ds->ops->get_ethtool_stats)
ds->ops->get_ethtool_stats(ds, port->index, data + count);
}

static int dsa_master_get_sset_count(struct net_device *dev, int sset)
{
struct dsa_switch_tree *dst = dev->dsa_ptr;
struct dsa_port *port = dst->cpu_dp;
struct dsa_switch *ds = port->ds;
const struct ethtool_ops *ops = port->orig_ethtool_ops;
int count = 0;

if (ops && ops->get_sset_count)
count += ops->get_sset_count(dev, sset);

if (sset == ETH_SS_STATS && ds->ops->get_sset_count)
count += ds->ops->get_sset_count(ds);

return count;
}

static void dsa_master_get_strings(struct net_device *dev, uint32_t stringset,
uint8_t *data)
{
struct dsa_switch_tree *dst = dev->dsa_ptr;
struct dsa_port *port = dst->cpu_dp;
struct dsa_switch *ds = port->ds;
const struct ethtool_ops *ops = port->orig_ethtool_ops;
int len = ETH_GSTRING_LEN;
int mcount = 0, count;
unsigned int i;
uint8_t pfx[4];
uint8_t *ndata;

snprintf(pfx, sizeof(pfx), "p%.2d", port->index);
/* We do not want to be NULL-terminated, since this is a prefix */
pfx[sizeof(pfx) - 1] = '_';

if (ops && ops->get_sset_count && ops->get_strings) {
mcount = ops->get_sset_count(dev, ETH_SS_STATS);
ops->get_strings(dev, stringset, data);
}

if (stringset == ETH_SS_STATS && ds->ops->get_strings) {
ndata = data + mcount * len;
/* This function copies ETH_GSTRINGS_LEN bytes, we will mangle
* the output after to prepend our CPU port prefix we
* constructed earlier
*/
ds->ops->get_strings(ds, port->index, ndata);
count = ds->ops->get_sset_count(ds);
for (i = 0; i < count; i++) {
memmove(ndata + (i * len + sizeof(pfx)),
ndata + i * len, len - sizeof(pfx));
memcpy(ndata + i * len, pfx, sizeof(pfx));
}
}
}

int dsa_master_ethtool_setup(struct net_device *dev)
{
struct dsa_switch_tree *dst = dev->dsa_ptr;
struct dsa_port *port = dst->cpu_dp;
struct dsa_switch *ds = port->ds;
struct ethtool_ops *ops;

ops = devm_kzalloc(ds->dev, sizeof(*ops), GFP_KERNEL);
if (!ops)
return -ENOMEM;

port->orig_ethtool_ops = dev->ethtool_ops;
if (port->orig_ethtool_ops)
memcpy(ops, port->orig_ethtool_ops, sizeof(*ops));

ops->get_sset_count = dsa_master_get_sset_count;
ops->get_ethtool_stats = dsa_master_get_ethtool_stats;
ops->get_strings = dsa_master_get_strings;

dev->ethtool_ops = ops;

return 0;
}

void dsa_master_ethtool_restore(struct net_device *dev)
{
struct dsa_switch_tree *dst = dev->dsa_ptr;
struct dsa_port *port = dst->cpu_dp;

dev->ethtool_ops = port->orig_ethtool_ops;
port->orig_ethtool_ops = NULL;
}
80 changes: 0 additions & 80 deletions net/dsa/slave.c
Original file line number Diff line number Diff line change
Expand Up @@ -567,79 +567,6 @@ static void dsa_slave_get_strings(struct net_device *dev,
}
}

static void dsa_cpu_port_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *stats,
uint64_t *data)
{
struct dsa_switch_tree *dst = dev->dsa_ptr;
struct dsa_port *cpu_dp = dsa_get_cpu_port(dst);
struct dsa_switch *ds = cpu_dp->ds;
s8 cpu_port = cpu_dp->index;
int count = 0;

if (cpu_dp->ethtool_ops.get_sset_count) {
count = cpu_dp->ethtool_ops.get_sset_count(dev, ETH_SS_STATS);
cpu_dp->ethtool_ops.get_ethtool_stats(dev, stats, data);
}

if (ds->ops->get_ethtool_stats)
ds->ops->get_ethtool_stats(ds, cpu_port, data + count);
}

static int dsa_cpu_port_get_sset_count(struct net_device *dev, int sset)
{
struct dsa_switch_tree *dst = dev->dsa_ptr;
struct dsa_port *cpu_dp = dsa_get_cpu_port(dst);
struct dsa_switch *ds = cpu_dp->ds;
int count = 0;

if (cpu_dp->ethtool_ops.get_sset_count)
count += cpu_dp->ethtool_ops.get_sset_count(dev, sset);

if (sset == ETH_SS_STATS && ds->ops->get_sset_count)
count += ds->ops->get_sset_count(ds);

return count;
}

static void dsa_cpu_port_get_strings(struct net_device *dev,
uint32_t stringset, uint8_t *data)
{
struct dsa_switch_tree *dst = dev->dsa_ptr;
struct dsa_port *cpu_dp = dsa_get_cpu_port(dst);
struct dsa_switch *ds = cpu_dp->ds;
s8 cpu_port = cpu_dp->index;
int len = ETH_GSTRING_LEN;
int mcount = 0, count;
unsigned int i;
uint8_t pfx[4];
uint8_t *ndata;

snprintf(pfx, sizeof(pfx), "p%.2d", cpu_port);
/* We do not want to be NULL-terminated, since this is a prefix */
pfx[sizeof(pfx) - 1] = '_';

if (cpu_dp->ethtool_ops.get_sset_count) {
mcount = cpu_dp->ethtool_ops.get_sset_count(dev, ETH_SS_STATS);
cpu_dp->ethtool_ops.get_strings(dev, stringset, data);
}

if (stringset == ETH_SS_STATS && ds->ops->get_strings) {
ndata = data + mcount * len;
/* This function copies ETH_GSTRINGS_LEN bytes, we will mangle
* the output after to prepend our CPU port prefix we
* constructed earlier
*/
ds->ops->get_strings(ds, cpu_port, ndata);
count = ds->ops->get_sset_count(ds);
for (i = 0; i < count; i++) {
memmove(ndata + (i * len + sizeof(pfx)),
ndata + i * len, len - sizeof(pfx));
memcpy(ndata + i * len, pfx, sizeof(pfx));
}
}
}

static void dsa_slave_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *stats,
uint64_t *data)
Expand Down Expand Up @@ -976,13 +903,6 @@ static void dsa_slave_get_stats64(struct net_device *dev,
}
}

void dsa_cpu_port_ethtool_init(struct ethtool_ops *ops)
{
ops->get_sset_count = dsa_cpu_port_get_sset_count;
ops->get_ethtool_stats = dsa_cpu_port_get_ethtool_stats;
ops->get_strings = dsa_cpu_port_get_strings;
}

static int dsa_slave_get_rxnfc(struct net_device *dev,
struct ethtool_rxnfc *nfc, u32 *rule_locs)
{
Expand Down

0 comments on commit d43a9d1

Please sign in to comment.