From cd8d7dd41bfddaa02e34db35abfab14ce4584dee Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 19 Sep 2017 11:56:58 -0400 Subject: net: dsa: setup master ethtool unconditionally When a DSA switch tree is meant to be applied, it already has a CPU port. Thus remove the condition of dst->cpu_dp. Moreover, the next lines access dst->cpu_dp unconditionally. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 873af0108e24..bd19304f862f 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -433,11 +433,9 @@ 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; - } + 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 @@ -474,10 +472,8 @@ 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; - } + dsa_cpu_port_ethtool_restore(dst->cpu_dp); + dst->cpu_dp = NULL; pr_info("DSA: tree %d unapplied\n", dst->tree); dst->applied = false; -- cgit From 1943563dfd4b3a1f9dc102f056813112d29bb60f Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 19 Sep 2017 11:56:59 -0400 Subject: net: dsa: setup master ethtool after dsa_ptr DSA overrides the master's ethtool ops so that we can inject its CPU port's statistics. Because of that, we need to setup the ethtool ops after the master's dsa_ptr pointer has been assigned, not before. This patch setups the ethtool ops after dsa_ptr is assigned, and restores them before it gets cleared. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 12 +++++++----- net/dsa/legacy.c | 10 +++------- 2 files changed, 10 insertions(+), 12 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index bd19304f862f..032f8bc3e788 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -433,16 +433,17 @@ static int dsa_dst_apply(struct dsa_switch_tree *dst) return err; } - 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_cpu_port_ethtool_setup(dst->cpu_dp); + if (err) + return err; + dst->applied = true; return 0; @@ -456,6 +457,8 @@ static void dsa_dst_unapply(struct dsa_switch_tree *dst) if (!dst->applied) return; + dsa_cpu_port_ethtool_restore(dst->cpu_dp); + dst->cpu_dp->netdev->dsa_ptr = NULL; /* If we used a tagging format that doesn't have an ethertype @@ -472,7 +475,6 @@ static void dsa_dst_unapply(struct dsa_switch_tree *dst) dsa_ds_unapply(dst, ds); } - dsa_cpu_port_ethtool_restore(dst->cpu_dp); dst->cpu_dp = NULL; pr_info("DSA: tree %d unapplied\n", dst->tree); diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index 91e6f7981d39..163910699db7 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -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; } @@ -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_cpu_port_ethtool_setup(dst->cpu_dp); } static int dsa_probe(struct platform_device *pdev) @@ -671,6 +667,8 @@ static void dsa_remove_dst(struct dsa_switch_tree *dst) { int i; + dsa_cpu_port_ethtool_restore(dst->cpu_dp); + dst->cpu_dp->netdev->dsa_ptr = NULL; /* If we used a tagging format that doesn't have an ethertype @@ -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); } -- cgit From f2f2356685bcaf1063859356fc65a5ac808b1382 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 19 Sep 2017 11:57:00 -0400 Subject: net: dsa: move master ethtool code DSA overrides the master device ethtool ops, so that it can inject stats from its dedicated switch CPU port as well. The related code is currently split in dsa.c and slave.c, but it only scopes the master net device. Move it to a new master.c DSA core file. This file will be later extented with master net device specific code. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/Makefile | 2 +- net/dsa/dsa.c | 28 ------------- net/dsa/dsa2.c | 4 +- net/dsa/dsa_priv.h | 7 ++-- net/dsa/legacy.c | 4 +- net/dsa/master.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++ net/dsa/slave.c | 83 ------------------------------------ 7 files changed, 129 insertions(+), 119 deletions(-) create mode 100644 net/dsa/master.c (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/Makefile b/net/dsa/Makefile index fcce25da937c..2e7ac8bab19d 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -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 diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index abadf7b49236..81c852e32821 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -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; - - cpu_dp->orig_ethtool_ops = master->ethtool_ops; - if (cpu_dp->orig_ethtool_ops) - memcpy(cpu_ops, cpu_dp->orig_ethtool_ops, sizeof(*cpu_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; - cpu_dp->orig_ethtool_ops = NULL; -} - void dsa_cpu_dsa_destroy(struct dsa_port *port) { struct device_node *port_dn = port->dn; diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 032f8bc3e788..dcccaebde708 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -440,7 +440,7 @@ static int dsa_dst_apply(struct dsa_switch_tree *dst) wmb(); dst->cpu_dp->netdev->dsa_ptr = dst; - err = dsa_cpu_port_ethtool_setup(dst->cpu_dp); + err = dsa_master_ethtool_setup(dst->cpu_dp->netdev); if (err) return err; @@ -457,7 +457,7 @@ static void dsa_dst_unapply(struct dsa_switch_tree *dst) if (!dst->applied) return; - dsa_cpu_port_ethtool_restore(dst->cpu_dp); + dsa_master_ethtool_restore(dst->cpu_dp->netdev); dst->cpu_dp->netdev->dsa_ptr = NULL; diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 9c3eeb72462d..f616b3444418 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -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 */ @@ -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); @@ -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); diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index 163910699db7..ae505d8e4417 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -602,7 +602,7 @@ static int dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev, wmb(); dev->dsa_ptr = dst; - return dsa_cpu_port_ethtool_setup(dst->cpu_dp); + return dsa_master_ethtool_setup(dst->cpu_dp->netdev); } static int dsa_probe(struct platform_device *pdev) @@ -667,7 +667,7 @@ static void dsa_remove_dst(struct dsa_switch_tree *dst) { int i; - dsa_cpu_port_ethtool_restore(dst->cpu_dp); + dsa_master_ethtool_restore(dst->cpu_dp->netdev); dst->cpu_dp->netdev->dsa_ptr = NULL; diff --git a/net/dsa/master.c b/net/dsa/master.c new file mode 100644 index 000000000000..5e5147ec5a44 --- /dev/null +++ b/net/dsa/master.c @@ -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 + * + * 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; +} diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 2ff4f907d137..d51b10450e1b 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -567,82 +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; - const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; - s8 cpu_port = cpu_dp->index; - 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, 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; - const struct ethtool_ops *ops = cpu_dp->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_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; - const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; - 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 (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, 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) @@ -979,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) { -- cgit From 62fc95876298987c35e9fb10badd467f4787aae7 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 29 Sep 2017 17:19:17 -0400 Subject: net: dsa: use temporary dsa_device_ops variable When resolving the DSA tagging protocol used by a CPU switch, use a temporary "tag_ops" variable to store the dsa_device_ops instead of using directly dst->tag_ops. This will make the future patches moving this pointer around easier to read. There is no functional changes. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 8 +++++--- net/dsa/legacy.c | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index dcccaebde708..6a10c5c1639f 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -485,6 +485,7 @@ static int dsa_cpu_parse(struct dsa_port *port, u32 index, struct dsa_switch_tree *dst, struct dsa_switch *ds) { + const struct dsa_device_ops *tag_ops; enum dsa_tag_protocol tag_protocol; struct net_device *ethernet_dev; struct device_node *ethernet; @@ -514,13 +515,14 @@ static int dsa_cpu_parse(struct dsa_port *port, u32 index, ds->cpu_port_mask |= BIT(index); tag_protocol = ds->ops->get_tag_protocol(ds); - dst->tag_ops = dsa_resolve_tag_protocol(tag_protocol); - if (IS_ERR(dst->tag_ops)) { + tag_ops = dsa_resolve_tag_protocol(tag_protocol); + if (IS_ERR(tag_ops)) { dev_warn(ds->dev, "No tagger for this switch\n"); ds->cpu_port_mask &= ~BIT(index); - return PTR_ERR(dst->tag_ops); + return PTR_ERR(tag_ops); } + dst->tag_ops = tag_ops; dst->rcv = dst->tag_ops->rcv; return 0; diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index ae505d8e4417..8e849013f69d 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -144,13 +144,15 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, * switch. */ if (dst->cpu_dp->ds == ds) { + const struct dsa_device_ops *tag_ops; enum dsa_tag_protocol tag_protocol; tag_protocol = ops->get_tag_protocol(ds); - dst->tag_ops = dsa_resolve_tag_protocol(tag_protocol); - if (IS_ERR(dst->tag_ops)) - return PTR_ERR(dst->tag_ops); + tag_ops = dsa_resolve_tag_protocol(tag_protocol); + if (IS_ERR(tag_ops)) + return PTR_ERR(tag_ops); + dst->tag_ops = tag_ops; dst->rcv = dst->tag_ops->rcv; } -- cgit From 152402483ed75b167d5628d414e876ffa7a6d4c4 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 29 Sep 2017 17:19:18 -0400 Subject: net: dsa: add tagging ops to port The DSA tagging protocol operations are specific to each CPU port, thus the dsa_device_ops pointer belongs to the dsa_port structure. >From now on assign a slave's xmit copy from its CPU port tagging operations. This will ease the future support for multiple CPU ports. Also keep the tag_ops at the beginning of the dsa_port structure so that we ensure copies for hot path are in cacheline 1. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- include/net/dsa.h | 3 +++ net/dsa/dsa2.c | 1 + net/dsa/dsa_priv.h | 2 +- net/dsa/legacy.c | 1 + net/dsa/slave.c | 3 +-- 5 files changed, 7 insertions(+), 3 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/include/net/dsa.h b/include/net/dsa.h index 8dee216a5a9b..4d1df2f086e8 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -175,6 +175,9 @@ struct dsa_mall_tc_entry { struct dsa_port { + /* CPU port tagging operations used by master or slave devices */ + const struct dsa_device_ops *tag_ops; + struct dsa_switch *ds; unsigned int index; const char *name; diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 6a10c5c1639f..9eac4726dc0c 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -522,6 +522,7 @@ static int dsa_cpu_parse(struct dsa_port *port, u32 index, return PTR_ERR(tag_ops); } + dst->cpu_dp->tag_ops = tag_ops; dst->tag_ops = tag_ops; dst->rcv = dst->tag_ops->rcv; diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index d429505dc4e7..9397291bb3aa 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -66,7 +66,7 @@ struct dsa_notifier_vlan_info { }; struct dsa_slave_priv { - /* Copy of dp->ds->dst->tag_ops->xmit for faster access in hot path */ + /* Copy of CPU port xmit for faster access in slave transmit hot path */ struct sk_buff * (*xmit)(struct sk_buff *skb, struct net_device *dev); diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index 8e849013f69d..4d374541815a 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -152,6 +152,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, if (IS_ERR(tag_ops)) return PTR_ERR(tag_ops); + dst->cpu_dp->tag_ops = tag_ops; dst->tag_ops = tag_ops; dst->rcv = dst->tag_ops->rcv; } diff --git a/net/dsa/slave.c b/net/dsa/slave.c index bf8800de13c1..4b634db05cee 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -1117,7 +1117,6 @@ int dsa_slave_resume(struct net_device *slave_dev) int dsa_slave_create(struct dsa_port *port, const char *name) { struct dsa_switch *ds = port->ds; - struct dsa_switch_tree *dst = ds->dst; struct net_device *master; struct net_device *slave_dev; struct dsa_slave_priv *p; @@ -1162,7 +1161,7 @@ int dsa_slave_create(struct dsa_port *port, const char *name) } p->dp = port; INIT_LIST_HEAD(&p->mall_tc_list); - p->xmit = dst->tag_ops->xmit; + p->xmit = cpu_dp->tag_ops->xmit; p->old_pause = -1; p->old_link = -1; -- cgit From 3e41f93b358a8800194b87995ad076fc50919719 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 29 Sep 2017 17:19:19 -0400 Subject: net: dsa: prepare master receive hot path In preparation to make DSA master devices point to their corresponding CPU port instead of the whole tree, add copies of dst and rcv in the dsa_port structure so that we keep fast access in the receive hot path. Also keep the copies at the beginning of the dsa_port structure in order to ensure they are available in cacheline 1. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- include/net/dsa.h | 5 +++++ net/dsa/dsa2.c | 4 ++++ net/dsa/legacy.c | 4 ++++ 3 files changed, 13 insertions(+) (limited to 'net/dsa/dsa2.c') diff --git a/include/net/dsa.h b/include/net/dsa.h index 4d1df2f086e8..6bda01fa5747 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -178,6 +178,11 @@ struct dsa_port { /* CPU port tagging operations used by master or slave devices */ const struct dsa_device_ops *tag_ops; + /* Copies for faster access in master receive hot path */ + struct dsa_switch_tree *dst; + struct sk_buff *(*rcv)(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt); + struct dsa_switch *ds; unsigned int index; const char *name; diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 9eac4726dc0c..b71e3bb478e4 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -524,7 +524,11 @@ static int dsa_cpu_parse(struct dsa_port *port, u32 index, dst->cpu_dp->tag_ops = tag_ops; dst->tag_ops = tag_ops; + + /* Make a few copies for faster access in master receive hot path */ + dst->cpu_dp->rcv = dst->cpu_dp->tag_ops->rcv; dst->rcv = dst->tag_ops->rcv; + dst->cpu_dp->dst = dst; return 0; } diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index 4d374541815a..96c7e3f8b8bb 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -154,7 +154,11 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, dst->cpu_dp->tag_ops = tag_ops; dst->tag_ops = tag_ops; + + /* Few copies for faster access in master receive hot path */ + dst->cpu_dp->rcv = dst->cpu_dp->tag_ops->rcv; dst->rcv = dst->tag_ops->rcv; + dst->cpu_dp->dst = dst; } memcpy(ds->rtable, cd->rtable, sizeof(ds->rtable)); -- cgit From 2f657a600409f1961d67642fe384a9d4be71d36a Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 29 Sep 2017 17:19:20 -0400 Subject: net: dsa: change dsa_ptr for a dsa_port With DSA, a master net device (CPU facing interface) has a dsa_ptr pointer to which hangs a dsa_switch_tree. This is not correct because a master interface is wired to a dedicated switch port, and because we can theoretically have several master interfaces pointing to several CPU ports of the same switch fabric. Change the master interface's dsa_ptr for the CPU dsa_port pointer. This is a step towards supporting multiple CPU ports. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- include/linux/netdevice.h | 4 ++-- net/dsa/dsa.c | 6 +++--- net/dsa/dsa2.c | 2 +- net/dsa/dsa_priv.h | 3 ++- net/dsa/legacy.c | 2 +- net/dsa/master.c | 15 +++++---------- 6 files changed, 14 insertions(+), 18 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index f535779d9dc1..e1d6ef130611 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -55,7 +55,7 @@ struct netpoll_info; struct device; struct phy_device; -struct dsa_switch_tree; +struct dsa_port; /* 802.11 specific */ struct wireless_dev; @@ -1752,7 +1752,7 @@ struct net_device { struct vlan_info __rcu *vlan_info; #endif #if IS_ENABLED(CONFIG_NET_DSA) - struct dsa_switch_tree *dsa_ptr; + struct dsa_port *dsa_ptr; #endif #if IS_ENABLED(CONFIG_TIPC) struct tipc_bearer __rcu *tipc_ptr; diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 81c852e32821..51ca2a524a27 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -160,12 +160,12 @@ EXPORT_SYMBOL_GPL(dsa_dev_to_net_device); static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *unused) { - struct dsa_switch_tree *dst = dev->dsa_ptr; + struct dsa_port *cpu_dp = dev->dsa_ptr; struct sk_buff *nskb = NULL; struct pcpu_sw_netstats *s; struct dsa_slave_priv *p; - if (unlikely(dst == NULL)) { + if (unlikely(!cpu_dp)) { kfree_skb(skb); return 0; } @@ -174,7 +174,7 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, if (!skb) return 0; - nskb = dst->rcv(skb, dev, pt); + nskb = cpu_dp->rcv(skb, dev, pt); if (!nskb) { kfree_skb(skb); return 0; diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index b71e3bb478e4..62302558f38c 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -438,7 +438,7 @@ static int dsa_dst_apply(struct dsa_switch_tree *dst) * sent to the tag format's receive function. */ wmb(); - dst->cpu_dp->netdev->dsa_ptr = dst; + dst->cpu_dp->netdev->dsa_ptr = dst->cpu_dp; err = dsa_master_ethtool_setup(dst->cpu_dp->netdev); if (err) diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 9397291bb3aa..2850077cc9cc 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -116,7 +116,8 @@ void dsa_master_ethtool_restore(struct net_device *dev); static inline struct net_device *dsa_master_get_slave(struct net_device *dev, int device, int port) { - struct dsa_switch_tree *dst = dev->dsa_ptr; + struct dsa_port *cpu_dp = dev->dsa_ptr; + struct dsa_switch_tree *dst = cpu_dp->dst; struct dsa_switch *ds; if (device < 0 || device >= DSA_MAX_SWITCHES) diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index 96c7e3f8b8bb..71917505a5cc 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -607,7 +607,7 @@ static int dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev, * sent to the tag format's receive function. */ wmb(); - dev->dsa_ptr = dst; + dev->dsa_ptr = dst->cpu_dp; return dsa_master_ethtool_setup(dst->cpu_dp->netdev); } diff --git a/net/dsa/master.c b/net/dsa/master.c index ef15d35f1574..5f3f57e372e0 100644 --- a/net/dsa/master.c +++ b/net/dsa/master.c @@ -16,8 +16,7 @@ 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 *cpu_dp = dst->cpu_dp; + struct dsa_port *cpu_dp = dev->dsa_ptr; const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; struct dsa_switch *ds = cpu_dp->ds; int port = cpu_dp->index; @@ -34,8 +33,7 @@ static void dsa_master_get_ethtool_stats(struct net_device *dev, static int dsa_master_get_sset_count(struct net_device *dev, int sset) { - struct dsa_switch_tree *dst = dev->dsa_ptr; - struct dsa_port *cpu_dp = dst->cpu_dp; + struct dsa_port *cpu_dp = dev->dsa_ptr; const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; struct dsa_switch *ds = cpu_dp->ds; int count = 0; @@ -52,8 +50,7 @@ static int dsa_master_get_sset_count(struct net_device *dev, int sset) 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 *cpu_dp = dst->cpu_dp; + struct dsa_port *cpu_dp = dev->dsa_ptr; const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; struct dsa_switch *ds = cpu_dp->ds; int port = cpu_dp->index; @@ -90,8 +87,7 @@ static void dsa_master_get_strings(struct net_device *dev, uint32_t stringset, int dsa_master_ethtool_setup(struct net_device *dev) { - struct dsa_switch_tree *dst = dev->dsa_ptr; - struct dsa_port *cpu_dp = dst->cpu_dp; + struct dsa_port *cpu_dp = dev->dsa_ptr; struct dsa_switch *ds = cpu_dp->ds; struct ethtool_ops *ops; @@ -114,8 +110,7 @@ int dsa_master_ethtool_setup(struct net_device *dev) void dsa_master_ethtool_restore(struct net_device *dev) { - struct dsa_switch_tree *dst = dev->dsa_ptr; - struct dsa_port *cpu_dp = dst->cpu_dp; + struct dsa_port *cpu_dp = dev->dsa_ptr; dev->ethtool_ops = cpu_dp->orig_ethtool_ops; cpu_dp->orig_ethtool_ops = NULL; -- cgit From aa193d9b1d7ea6893ce24a9d141f676950563987 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 29 Sep 2017 17:19:21 -0400 Subject: net: dsa: remove tag ops from the switch tree Now that the dsa_ptr is a dsa_port instance, there is no need to keep the tag operations in the dsa_switch_tree structure. Remove it. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/dsa.h | 11 ----------- net/dsa/dsa2.c | 2 -- net/dsa/legacy.c | 2 -- 3 files changed, 15 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/include/net/dsa.h b/include/net/dsa.h index 6bda01fa5747..10dceccd9ce8 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -130,11 +130,6 @@ struct dsa_switch_tree { */ struct dsa_platform_data *pd; - /* Copy of tag_ops->rcv for faster access in hot path */ - struct sk_buff * (*rcv)(struct sk_buff *skb, - struct net_device *dev, - struct packet_type *pt); - /* * The switch port to which the CPU is attached. */ @@ -144,12 +139,6 @@ struct dsa_switch_tree { * Data for the individual switch chips. */ struct dsa_switch *ds[DSA_MAX_SWITCHES]; - - /* - * Tagging protocol operations for adding and removing an - * encapsulation tag. - */ - const struct dsa_device_ops *tag_ops; }; /* TC matchall action types, only mirroring for now */ diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 62302558f38c..54ed054777bd 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -523,11 +523,9 @@ static int dsa_cpu_parse(struct dsa_port *port, u32 index, } dst->cpu_dp->tag_ops = tag_ops; - dst->tag_ops = tag_ops; /* Make a few copies for faster access in master receive hot path */ dst->cpu_dp->rcv = dst->cpu_dp->tag_ops->rcv; - dst->rcv = dst->tag_ops->rcv; dst->cpu_dp->dst = dst; return 0; diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index 71917505a5cc..19ff6e0a21dc 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -153,11 +153,9 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, return PTR_ERR(tag_ops); dst->cpu_dp->tag_ops = tag_ops; - dst->tag_ops = tag_ops; /* Few copies for faster access in master receive hot path */ dst->cpu_dp->rcv = dst->cpu_dp->tag_ops->rcv; - dst->rcv = dst->tag_ops->rcv; dst->cpu_dp->dst = dst; } -- cgit From 841f4f24053acad69240c6ab7427a1d24bc29491 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 13 Oct 2017 14:18:09 -0400 Subject: net: dsa: remove .set_addr Now that there is no user for the .set_addr function, remove it from DSA. If a switch supports this feature (like mv88e6xxx), the implementation can be done in the driver setup. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- include/net/dsa.h | 1 - net/dsa/dsa2.c | 6 ------ net/dsa/legacy.c | 6 ------ 3 files changed, 13 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/include/net/dsa.h b/include/net/dsa.h index ce1d622734d7..2746741f74cf 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -291,7 +291,6 @@ struct dsa_switch_ops { enum dsa_tag_protocol (*get_tag_protocol)(struct dsa_switch *ds); int (*setup)(struct dsa_switch *ds); - int (*set_addr)(struct dsa_switch *ds, u8 *addr); u32 (*get_phy_flags)(struct dsa_switch *ds, int port); /* diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 54ed054777bd..6ac9e11d385c 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -336,12 +336,6 @@ static int dsa_ds_apply(struct dsa_switch_tree *dst, struct dsa_switch *ds) if (err) return err; - if (ds->ops->set_addr) { - err = ds->ops->set_addr(ds, dst->cpu_dp->netdev->dev_addr); - if (err < 0) - return err; - } - if (!ds->slave_mii_bus && ds->ops->phy_read) { ds->slave_mii_bus = devm_mdiobus_alloc(ds->dev); if (!ds->slave_mii_bus) diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index 19ff6e0a21dc..b0fefbffe082 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -172,12 +172,6 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, if (ret) return ret; - if (ops->set_addr) { - ret = ops->set_addr(ds, master->dev_addr); - if (ret < 0) - return ret; - } - if (!ds->slave_mii_bus && ops->phy_read) { ds->slave_mii_bus = devm_mdiobus_alloc(ds->dev); if (!ds->slave_mii_bus) -- cgit From f8b8b1cd5aadd221742b45eb0ee3c8a80abf036a Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 16 Oct 2017 11:12:18 -0400 Subject: net: dsa: split dsa_port's netdev member The dsa_port structure has a "netdev" member, which can be used for either the master device, or the slave device, depending on its type. It is true that today, CPU port are not exposed to userspace, thus the port's netdev member can be used to point to its master interface. But it is still slightly confusing, so split it into more explicit "master" and "slave" members inside an anonymous union. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/bcm_sf2.c | 6 +++--- drivers/net/dsa/mt7530.c | 2 +- drivers/net/dsa/mv88e6xxx/chip.c | 2 +- include/net/dsa.h | 9 ++++++++- net/dsa/dsa.c | 6 +++--- net/dsa/dsa2.c | 20 ++++++++++---------- net/dsa/dsa_priv.h | 4 ++-- net/dsa/legacy.c | 14 +++++++------- net/dsa/slave.c | 6 +++--- 9 files changed, 38 insertions(+), 31 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 32025b990437..b43c063b9634 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -601,7 +601,7 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port, * state machine and make it go in PHY_FORCING state instead. */ if (!status->link) - netif_carrier_off(ds->ports[port].netdev); + netif_carrier_off(ds->ports[port].slave); status->duplex = 1; } else { status->link = 1; @@ -690,7 +690,7 @@ static int bcm_sf2_sw_resume(struct dsa_switch *ds) static void bcm_sf2_sw_get_wol(struct dsa_switch *ds, int port, struct ethtool_wolinfo *wol) { - struct net_device *p = ds->ports[port].cpu_dp->netdev; + struct net_device *p = ds->ports[port].cpu_dp->master; struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); struct ethtool_wolinfo pwol; @@ -713,7 +713,7 @@ static void bcm_sf2_sw_get_wol(struct dsa_switch *ds, int port, static int bcm_sf2_sw_set_wol(struct dsa_switch *ds, int port, struct ethtool_wolinfo *wol) { - struct net_device *p = ds->ports[port].cpu_dp->netdev; + struct net_device *p = ds->ports[port].cpu_dp->master; struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); s8 cpu_port = ds->ports[port].cpu_dp->index; struct ethtool_wolinfo pwol; diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 034241696ce2..fea2e665d0cb 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -933,7 +933,7 @@ mt7530_setup(struct dsa_switch *ds) * controller also is the container for two GMACs nodes representing * as two netdev instances. */ - dn = ds->ports[MT7530_CPU_PORT].netdev->dev.of_node->parent; + dn = ds->ports[MT7530_CPU_PORT].master->dev.of_node->parent; priv->ethernet = syscon_node_to_regmap(dn); if (IS_ERR(priv->ethernet)) return PTR_ERR(priv->ethernet); diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 76cf383dcf90..88b47fa09b41 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1137,7 +1137,7 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i)) continue; - if (!ds->ports[port].netdev) + if (!ds->ports[port].slave) continue; if (vlan.member[i] == diff --git a/include/net/dsa.h b/include/net/dsa.h index 2746741f74cf..6ed1a17ed1bd 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -164,6 +164,14 @@ struct dsa_mall_tc_entry { struct dsa_port { + /* A CPU port is physically connected to a master device. + * A user port exposed to userspace has a slave device. + */ + union { + struct net_device *master; + struct net_device *slave; + }; + /* CPU port tagging operations used by master or slave devices */ const struct dsa_device_ops *tag_ops; @@ -176,7 +184,6 @@ struct dsa_port { unsigned int index; const char *name; struct dsa_port *cpu_dp; - struct net_device *netdev; struct device_node *dn; unsigned int ageing_time; u8 stp_state; diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 832c659ff993..a3abf7a7b9a2 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -201,7 +201,7 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, #ifdef CONFIG_PM_SLEEP static bool dsa_is_port_initialized(struct dsa_switch *ds, int p) { - return ds->enabled_port_mask & (1 << p) && ds->ports[p].netdev; + return ds->enabled_port_mask & (1 << p) && ds->ports[p].slave; } int dsa_switch_suspend(struct dsa_switch *ds) @@ -213,7 +213,7 @@ int dsa_switch_suspend(struct dsa_switch *ds) if (!dsa_is_port_initialized(ds, i)) continue; - ret = dsa_slave_suspend(ds->ports[i].netdev); + ret = dsa_slave_suspend(ds->ports[i].slave); if (ret) return ret; } @@ -240,7 +240,7 @@ int dsa_switch_resume(struct dsa_switch *ds) if (!dsa_is_port_initialized(ds, i)) continue; - ret = dsa_slave_resume(ds->ports[i].netdev); + ret = dsa_slave_resume(ds->ports[i].slave); if (ret) return ret; } diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 6ac9e11d385c..9e8b8aab049d 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -279,7 +279,7 @@ static int dsa_user_port_apply(struct dsa_port *port) if (err) { dev_warn(ds->dev, "Failed to create slave %d: %d\n", port->index, err); - port->netdev = NULL; + port->slave = NULL; return err; } @@ -289,7 +289,7 @@ static int dsa_user_port_apply(struct dsa_port *port) if (err) return err; - devlink_port_type_eth_set(&port->devlink_port, port->netdev); + devlink_port_type_eth_set(&port->devlink_port, port->slave); return 0; } @@ -297,9 +297,9 @@ static int dsa_user_port_apply(struct dsa_port *port) static void dsa_user_port_unapply(struct dsa_port *port) { devlink_port_unregister(&port->devlink_port); - if (port->netdev) { - dsa_slave_destroy(port->netdev); - port->netdev = NULL; + if (port->slave) { + dsa_slave_destroy(port->slave); + port->slave = NULL; port->ds->enabled_port_mask &= ~(1 << port->index); } } @@ -432,9 +432,9 @@ static int dsa_dst_apply(struct dsa_switch_tree *dst) * sent to the tag format's receive function. */ wmb(); - dst->cpu_dp->netdev->dsa_ptr = dst->cpu_dp; + dst->cpu_dp->master->dsa_ptr = dst->cpu_dp; - err = dsa_master_ethtool_setup(dst->cpu_dp->netdev); + err = dsa_master_ethtool_setup(dst->cpu_dp->master); if (err) return err; @@ -451,9 +451,9 @@ static void dsa_dst_unapply(struct dsa_switch_tree *dst) if (!dst->applied) return; - dsa_master_ethtool_restore(dst->cpu_dp->netdev); + dsa_master_ethtool_restore(dst->cpu_dp->master); - dst->cpu_dp->netdev->dsa_ptr = NULL; + dst->cpu_dp->master->dsa_ptr = NULL; /* If we used a tagging format that doesn't have an ethertype * field, make sure that all packets from this point get sent @@ -499,7 +499,7 @@ static int dsa_cpu_parse(struct dsa_port *port, u32 index, if (!dst->cpu_dp) { dst->cpu_dp = port; - dst->cpu_dp->netdev = ethernet_dev; + dst->cpu_dp->master = ethernet_dev; } /* Initialize cpu_port_mask now for drv->setup() diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 623c22b75e81..1e9914062d0b 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -130,7 +130,7 @@ static inline struct net_device *dsa_master_find_slave(struct net_device *dev, if (port < 0 || port >= ds->num_ports) return NULL; - return ds->ports[port].netdev; + return ds->ports[port].slave; } /* port.c */ @@ -181,7 +181,7 @@ dsa_slave_to_master(const struct net_device *dev) { struct dsa_port *dp = dsa_slave_to_port(dev); - return dp->cpu_dp->netdev; + return dp->cpu_dp->master; } /* switch.c */ diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index cc28c6f792a3..b6c88fd33d4f 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -120,7 +120,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, return -EINVAL; } dst->cpu_dp = &ds->ports[i]; - dst->cpu_dp->netdev = master; + dst->cpu_dp->master = master; ds->cpu_port_mask |= 1 << i; } else if (!strcmp(name, "dsa")) { ds->dsa_port_mask |= 1 << i; @@ -261,10 +261,10 @@ static void dsa_switch_destroy(struct dsa_switch *ds) if (!(ds->enabled_port_mask & (1 << port))) continue; - if (!ds->ports[port].netdev) + if (!ds->ports[port].slave) continue; - dsa_slave_destroy(ds->ports[port].netdev); + dsa_slave_destroy(ds->ports[port].slave); } /* Disable configuration of the CPU and DSA ports */ @@ -601,7 +601,7 @@ static int dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev, wmb(); dev->dsa_ptr = dst->cpu_dp; - return dsa_master_ethtool_setup(dst->cpu_dp->netdev); + return dsa_master_ethtool_setup(dst->cpu_dp->master); } static int dsa_probe(struct platform_device *pdev) @@ -666,9 +666,9 @@ static void dsa_remove_dst(struct dsa_switch_tree *dst) { int i; - dsa_master_ethtool_restore(dst->cpu_dp->netdev); + dsa_master_ethtool_restore(dst->cpu_dp->master); - dst->cpu_dp->netdev->dsa_ptr = NULL; + dst->cpu_dp->master->dsa_ptr = NULL; /* If we used a tagging format that doesn't have an ethertype * field, make sure that all packets from this point get sent @@ -683,7 +683,7 @@ static void dsa_remove_dst(struct dsa_switch_tree *dst) dsa_switch_destroy(ds); } - dev_put(dst->cpu_dp->netdev); + dev_put(dst->cpu_dp->master); } static int dsa_remove(struct platform_device *pdev) diff --git a/net/dsa/slave.c b/net/dsa/slave.c index b72e07503a40..6906de0f0050 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -1123,7 +1123,7 @@ static void dsa_slave_notify(struct net_device *dev, unsigned long val) int dsa_slave_create(struct dsa_port *port, const char *name) { struct dsa_port *cpu_dp = port->cpu_dp; - struct net_device *master = cpu_dp->netdev; + struct net_device *master = cpu_dp->master; struct dsa_switch *ds = port->ds; struct net_device *slave_dev; struct dsa_slave_priv *p; @@ -1170,7 +1170,7 @@ int dsa_slave_create(struct dsa_port *port, const char *name) p->old_link = -1; p->old_duplex = -1; - port->netdev = slave_dev; + port->slave = slave_dev; netif_carrier_off(slave_dev); @@ -1198,7 +1198,7 @@ out_phy: out_free: free_percpu(p->stats64); free_netdev(slave_dev); - port->netdev = NULL; + port->slave = NULL; return ret; } -- cgit From eaac97466ee4e0ab6ed67afbf9fc400862e64986 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 23 Oct 2017 14:17:31 -0400 Subject: net: dsa: don't unmask port bitmaps The unapply functions are called on the error path. As for dsa_port_mask, enabled_port_mask and cpu_port_mask won't be used after so there's no need to unmask the corresponding port bit from them. This makes dsa_cpu_port_unapply() and dsa_dsa_port_unapply() identical, which can be factorized later. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 9e8b8aab049d..62485a57dbfc 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -260,8 +260,6 @@ static void dsa_cpu_port_unapply(struct dsa_port *port) { devlink_port_unregister(&port->devlink_port); dsa_cpu_dsa_destroy(port); - port->ds->cpu_port_mask &= ~BIT(port->index); - } static int dsa_user_port_apply(struct dsa_port *port) @@ -300,7 +298,6 @@ static void dsa_user_port_unapply(struct dsa_port *port) if (port->slave) { dsa_slave_destroy(port->slave); port->slave = NULL; - port->ds->enabled_port_mask &= ~(1 << port->index); } } @@ -512,7 +509,6 @@ static int dsa_cpu_parse(struct dsa_port *port, u32 index, tag_ops = dsa_resolve_tag_protocol(tag_protocol); if (IS_ERR(tag_ops)) { dev_warn(ds->dev, "No tagger for this switch\n"); - ds->cpu_port_mask &= ~BIT(index); return PTR_ERR(tag_ops); } -- cgit From 02bc6e546e858b209c3ebe380a13a73b333b1b3f Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 26 Oct 2017 11:22:56 -0400 Subject: net: dsa: introduce dsa_user_ports helper Introduce a dsa_user_ports() helper to return the ds->enabled_port_mask mask which is more explicit. This will also minimize diffs when touching this internal mask. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mt7530.c | 2 +- drivers/net/dsa/mv88e6060.c | 5 ++--- drivers/net/dsa/qca8k.c | 3 +-- include/net/dsa.h | 5 +++++ net/dsa/dsa2.c | 2 +- net/dsa/legacy.c | 2 +- 6 files changed, 11 insertions(+), 8 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index d1d4eea6a875..627c039f12ca 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -688,7 +688,7 @@ mt7530_cpu_port_enable(struct mt7530_priv *priv, * the switch */ mt7530_write(priv, MT7530_PCR_P(port), - PCR_MATRIX(priv->ds->enabled_port_mask)); + PCR_MATRIX(dsa_user_ports(priv->ds))); return 0; } diff --git a/drivers/net/dsa/mv88e6060.c b/drivers/net/dsa/mv88e6060.c index f78b9e13be1c..45768e3c5bc5 100644 --- a/drivers/net/dsa/mv88e6060.c +++ b/drivers/net/dsa/mv88e6060.c @@ -175,9 +175,8 @@ static int mv88e6060_setup_port(struct dsa_switch *ds, int p) */ REG_WRITE(addr, PORT_VLAN_MAP, ((p & 0xf) << PORT_VLAN_MAP_DBNUM_SHIFT) | - (dsa_is_cpu_port(ds, p) ? - ds->enabled_port_mask : - BIT(dsa_to_port(ds, p)->cpu_dp->index))); + (dsa_is_cpu_port(ds, p) ? dsa_user_ports(ds) : + BIT(dsa_to_port(ds, p)->cpu_dp->index))); /* Port Association Vector: when learning source addresses * of packets, add the address to the address database using diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index 37125858fe80..cf72e274275f 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -551,8 +551,7 @@ qca8k_setup(struct dsa_switch *ds) /* CPU port gets connected to all user ports of the switch */ if (dsa_is_cpu_port(ds, i)) { qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(QCA8K_CPU_PORT), - QCA8K_PORT_LOOKUP_MEMBER, - ds->enabled_port_mask); + QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); } /* Invividual user ports get connected to CPU port only */ diff --git a/include/net/dsa.h b/include/net/dsa.h index 49701d958663..dc7728062396 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -276,6 +276,11 @@ static inline bool dsa_is_user_port(struct dsa_switch *ds, int p) return !!(ds->enabled_port_mask & BIT(p)); } +static inline u32 dsa_user_ports(struct dsa_switch *ds) +{ + return ds->enabled_port_mask; +} + static inline const struct dsa_port *dsa_to_port(struct dsa_switch *ds, int p) { return &ds->ports[p]; diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 62485a57dbfc..d43c59c91058 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -312,7 +312,7 @@ static int dsa_ds_apply(struct dsa_switch_tree *dst, struct dsa_switch *ds) * the slave MDIO bus driver rely on these values for probing PHY * devices or not */ - ds->phys_mii_mask = ds->enabled_port_mask; + ds->phys_mii_mask |= dsa_user_ports(ds); /* Add the switch to devlink before calling setup, so that setup can * add dpipe tables diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index 0b79c6171d0d..fa543c4a6061 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -136,7 +136,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, /* Make the built-in MII bus mask match the number of ports, * switch drivers can override this later */ - ds->phys_mii_mask = ds->enabled_port_mask; + ds->phys_mii_mask |= dsa_user_ports(ds); /* * If the CPU connects to this switch, set the switch tree -- cgit From 057cad2c59d73b0c4a6638546f3099d6fb444094 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 26 Oct 2017 11:22:57 -0400 Subject: net: dsa: define port types Introduce an enumerated type for ports, which will be way more explicit to identify a port type instead of digging into switch port masks. A port can be of type CPU, DSA, user, or unused by default. This is a static parsed information that cannot be changed at runtime. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- include/net/dsa.h | 7 +++++++ net/dsa/dsa2.c | 3 +++ net/dsa/legacy.c | 6 ++++++ 3 files changed, 16 insertions(+) (limited to 'net/dsa/dsa2.c') diff --git a/include/net/dsa.h b/include/net/dsa.h index dc7728062396..8da20c4a6552 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -180,6 +180,13 @@ struct dsa_port { struct sk_buff *(*rcv)(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt); + enum { + DSA_PORT_TYPE_UNUSED = 0, + DSA_PORT_TYPE_CPU, + DSA_PORT_TYPE_DSA, + DSA_PORT_TYPE_USER, + } type; + struct dsa_switch *ds; unsigned int index; const char *name; diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index d43c59c91058..dd6f35b92937 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -185,6 +185,7 @@ static int dsa_ds_complete(struct dsa_switch_tree *dst, struct dsa_switch *ds) return err; ds->dsa_port_mask |= BIT(index); + port->type = DSA_PORT_TYPE_DSA; } return 0; @@ -504,6 +505,7 @@ static int dsa_cpu_parse(struct dsa_port *port, u32 index, * net/dsa/dsa.c::dsa_switch_setup_one does. */ ds->cpu_port_mask |= BIT(index); + port->type = DSA_PORT_TYPE_CPU; tag_protocol = ds->ops->get_tag_protocol(ds); tag_ops = dsa_resolve_tag_protocol(tag_protocol); @@ -543,6 +545,7 @@ static int dsa_ds_parse(struct dsa_switch_tree *dst, struct dsa_switch *ds) * net/dsa/dsa.c::dsa_switch_setup_one does. */ ds->enabled_port_mask |= BIT(index); + port->type = DSA_PORT_TYPE_USER; } } diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index fa543c4a6061..9fd5b3adab1e 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -101,6 +101,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct dsa_chip_data *cd = ds->cd; bool valid_name_found = false; int index = ds->index; + struct dsa_port *dp; int i, ret; /* @@ -109,6 +110,8 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, for (i = 0; i < ds->num_ports; i++) { char *name; + dp = &ds->ports[i]; + name = cd->port_names[i]; if (name == NULL) continue; @@ -122,10 +125,13 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, dst->cpu_dp = &ds->ports[i]; dst->cpu_dp->master = master; ds->cpu_port_mask |= 1 << i; + dp->type = DSA_PORT_TYPE_CPU; } else if (!strcmp(name, "dsa")) { ds->dsa_port_mask |= 1 << i; + dp->type = DSA_PORT_TYPE_DSA; } else { ds->enabled_port_mask |= 1 << i; + dp->type = DSA_PORT_TYPE_USER; } valid_name_found = true; } -- cgit From 5749f0f3772b9d98f37e3a92539f49fafaa64eca Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 26 Oct 2017 11:22:59 -0400 Subject: net: dsa: remove port masks Now that DSA core provides port types, there is no need to keep this information at the switch level. This is a static information that is part of a DSA core dsa_port structure. Remove them. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- include/net/dsa.h | 3 --- net/dsa/dsa2.c | 11 ----------- net/dsa/legacy.c | 3 --- 3 files changed, 17 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/include/net/dsa.h b/include/net/dsa.h index 07dfbd7f4fd5..50e276dc4c01 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -240,9 +240,6 @@ struct dsa_switch { /* * Slave mii_bus and devices for the individual ports. */ - u32 dsa_port_mask; - u32 cpu_port_mask; - u32 enabled_port_mask; u32 phys_mii_mask; struct mii_bus *slave_mii_bus; diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index dd6f35b92937..ec58654a71cd 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -184,7 +184,6 @@ static int dsa_ds_complete(struct dsa_switch_tree *dst, struct dsa_switch *ds) if (err != 0) return err; - ds->dsa_port_mask |= BIT(index); port->type = DSA_PORT_TYPE_DSA; } @@ -500,11 +499,6 @@ static int dsa_cpu_parse(struct dsa_port *port, u32 index, dst->cpu_dp->master = ethernet_dev; } - /* Initialize cpu_port_mask now for drv->setup() - * to have access to a correct value, just like what - * net/dsa/dsa.c::dsa_switch_setup_one does. - */ - ds->cpu_port_mask |= BIT(index); port->type = DSA_PORT_TYPE_CPU; tag_protocol = ds->ops->get_tag_protocol(ds); @@ -540,11 +534,6 @@ static int dsa_ds_parse(struct dsa_switch_tree *dst, struct dsa_switch *ds) if (err) return err; } else { - /* Initialize enabled_port_mask now for drv->setup() - * to have access to a correct value, just like what - * net/dsa/dsa.c::dsa_switch_setup_one does. - */ - ds->enabled_port_mask |= BIT(index); port->type = DSA_PORT_TYPE_USER; } diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index 9fd5b3adab1e..93e1b116ef83 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -124,13 +124,10 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, } dst->cpu_dp = &ds->ports[i]; dst->cpu_dp->master = master; - ds->cpu_port_mask |= 1 << i; dp->type = DSA_PORT_TYPE_CPU; } else if (!strcmp(name, "dsa")) { - ds->dsa_port_mask |= 1 << i; dp->type = DSA_PORT_TYPE_DSA; } else { - ds->enabled_port_mask |= 1 << i; dp->type = DSA_PORT_TYPE_USER; } valid_name_found = true; -- cgit From 57ab1ca215971702df534ae93cd76c15ca084c77 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 26 Oct 2017 10:50:07 -0400 Subject: net: dsa: move fixed link registration helpers The new bindings (dsa2.c) and the old bindings (legacy.c) share two helpers dsa_cpu_dsa_setup and dsa_cpu_dsa_destroy, used to register or deregister a fixed PHY if a given port has a corresponding device node. Unclutter the code by moving them into two new port.c helpers, dsa_port_fixed_link_register_of and dsa_port_fixed_link_(un)register_of. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- net/dsa/dsa.c | 39 --------------------------------------- net/dsa/dsa2.c | 8 ++++---- net/dsa/dsa_priv.h | 5 +++-- net/dsa/legacy.c | 4 ++-- net/dsa/port.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 56 insertions(+), 47 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index fe0081730305..b8f2d9f7c3ed 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -68,37 +68,6 @@ const struct dsa_device_ops *dsa_device_ops[DSA_TAG_LAST] = { [DSA_TAG_PROTO_NONE] = &none_ops, }; -int dsa_cpu_dsa_setup(struct dsa_port *port) -{ - struct device_node *port_dn = port->dn; - struct dsa_switch *ds = port->ds; - struct phy_device *phydev; - int ret, mode; - - if (of_phy_is_fixed_link(port_dn)) { - ret = of_phy_register_fixed_link(port_dn); - if (ret) { - dev_err(ds->dev, "failed to register fixed PHY\n"); - return ret; - } - phydev = of_phy_find_device(port_dn); - - mode = of_get_phy_mode(port_dn); - if (mode < 0) - mode = PHY_INTERFACE_MODE_NA; - phydev->interface = mode; - - genphy_config_init(phydev); - genphy_read_status(phydev); - if (ds->ops->adjust_link) - ds->ops->adjust_link(ds, port->index, phydev); - - put_device(&phydev->mdio.dev); - } - - return 0; -} - const struct dsa_device_ops *dsa_resolve_tag_protocol(int tag_protocol) { const struct dsa_device_ops *ops; @@ -113,14 +82,6 @@ const struct dsa_device_ops *dsa_resolve_tag_protocol(int tag_protocol) return ops; } -void dsa_cpu_dsa_destroy(struct dsa_port *port) -{ - struct device_node *port_dn = port->dn; - - if (of_phy_is_fixed_link(port_dn)) - of_phy_deregister_fixed_link(port_dn); -} - static int dev_is_class(struct device *dev, void *class) { if (dev->class != NULL && !strcmp(dev->class->name, class)) diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index ec58654a71cd..de91c48b6806 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -219,7 +219,7 @@ static int dsa_dsa_port_apply(struct dsa_port *port) struct dsa_switch *ds = port->ds; int err; - err = dsa_cpu_dsa_setup(port); + err = dsa_port_fixed_link_register_of(port); if (err) { dev_warn(ds->dev, "Failed to setup dsa port %d: %d\n", port->index, err); @@ -235,7 +235,7 @@ static int dsa_dsa_port_apply(struct dsa_port *port) static void dsa_dsa_port_unapply(struct dsa_port *port) { devlink_port_unregister(&port->devlink_port); - dsa_cpu_dsa_destroy(port); + dsa_port_fixed_link_unregister_of(port); } static int dsa_cpu_port_apply(struct dsa_port *port) @@ -243,7 +243,7 @@ static int dsa_cpu_port_apply(struct dsa_port *port) struct dsa_switch *ds = port->ds; int err; - err = dsa_cpu_dsa_setup(port); + err = dsa_port_fixed_link_register_of(port); if (err) { dev_warn(ds->dev, "Failed to setup cpu port %d: %d\n", port->index, err); @@ -259,7 +259,7 @@ static int dsa_cpu_port_apply(struct dsa_port *port) static void dsa_cpu_port_unapply(struct dsa_port *port) { devlink_port_unregister(&port->devlink_port); - dsa_cpu_dsa_destroy(port); + dsa_port_fixed_link_unregister_of(port); } static int dsa_user_port_apply(struct dsa_port *port) diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 1e9914062d0b..1e65afd6989e 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -93,8 +93,6 @@ struct dsa_slave_priv { }; /* dsa.c */ -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); bool dsa_schedule_work(struct work_struct *work); @@ -159,6 +157,9 @@ int dsa_port_vlan_add(struct dsa_port *dp, struct switchdev_trans *trans); int dsa_port_vlan_del(struct dsa_port *dp, const struct switchdev_obj_port_vlan *vlan); +int dsa_port_fixed_link_register_of(struct dsa_port *dp); +void dsa_port_fixed_link_unregister_of(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); diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index 93e1b116ef83..ed7aae342fca 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -86,7 +86,7 @@ static int dsa_cpu_dsa_setups(struct dsa_switch *ds) if (!(dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))) continue; - ret = dsa_cpu_dsa_setup(&ds->ports[port]); + ret = dsa_port_fixed_link_register_of(&ds->ports[port]); if (ret) return ret; } @@ -274,7 +274,7 @@ static void dsa_switch_destroy(struct dsa_switch *ds) for (port = 0; port < ds->num_ports; port++) { if (!(dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))) continue; - dsa_cpu_dsa_destroy(&ds->ports[port]); + dsa_port_fixed_link_unregister_of(&ds->ports[port]); } if (ds->slave_mii_bus && ds->ops->phy_read) diff --git a/net/dsa/port.c b/net/dsa/port.c index 72c8dbd3d3f2..bb30b1a7de3a 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -12,6 +12,8 @@ #include #include +#include +#include #include "dsa_priv.h" @@ -264,3 +266,48 @@ int dsa_port_vlan_del(struct dsa_port *dp, return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info); } + +int dsa_port_fixed_link_register_of(struct dsa_port *dp) +{ + struct device_node *dn = dp->dn; + struct dsa_switch *ds = dp->ds; + struct phy_device *phydev; + int port = dp->index; + int mode; + int err; + + if (of_phy_is_fixed_link(dn)) { + err = of_phy_register_fixed_link(dn); + if (err) { + dev_err(ds->dev, + "failed to register the fixed PHY of port %d\n", + port); + return err; + } + + phydev = of_phy_find_device(dn); + + mode = of_get_phy_mode(dn); + if (mode < 0) + mode = PHY_INTERFACE_MODE_NA; + phydev->interface = mode; + + genphy_config_init(phydev); + genphy_read_status(phydev); + + if (ds->ops->adjust_link) + ds->ops->adjust_link(ds, port, phydev); + + put_device(&phydev->mdio.dev); + } + + return 0; +} + +void dsa_port_fixed_link_unregister_of(struct dsa_port *dp) +{ + struct device_node *dn = dp->dn; + + if (of_phy_is_fixed_link(dn)) + of_phy_deregister_fixed_link(dn); +} -- cgit From 5b32fe070c2ddf31adc42c26dab8af346b652538 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 27 Oct 2017 15:55:13 -0400 Subject: net: dsa: get ports within parsing code There is no point into hiding the -EINVAL error code in ERR_PTR from a dsa_get_ports function, simply get the "ports" node directly from within the dsa_parse_ports_dn function. This also has the effect to make the pdata and device tree handling code symmetrical inside _dsa_register_switch. At the same time, rename dsa_parse_ports_dn to dsa_parse_ports_of because _of is a more common suffix for device tree parsing functions. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 33 ++++++++++----------------------- 1 file changed, 10 insertions(+), 23 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 4d1ccf87c59c..9d57f8dee9a1 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -590,11 +590,17 @@ static int dsa_dst_parse(struct dsa_switch_tree *dst) return 0; } -static int dsa_parse_ports_dn(struct device_node *ports, struct dsa_switch *ds) +static int dsa_parse_ports_of(struct device_node *dn, struct dsa_switch *ds) { - struct device_node *port; - int err; + struct device_node *ports, *port; u32 reg; + int err; + + ports = of_get_child_by_name(dn, "ports"); + if (!ports) { + dev_err(ds->dev, "no ports child node found\n"); + return -EINVAL; + } for_each_available_child_of_node(ports, port) { err = of_property_read_u32(port, "reg", ®); @@ -665,26 +671,11 @@ static int dsa_parse_member(struct dsa_chip_data *pd, u32 *tree, u32 *index) return 0; } -static struct device_node *dsa_get_ports(struct dsa_switch *ds, - struct device_node *np) -{ - struct device_node *ports; - - ports = of_get_child_by_name(np, "ports"); - if (!ports) { - dev_err(ds->dev, "no ports child node found\n"); - return ERR_PTR(-EINVAL); - } - - return ports; -} - static int _dsa_register_switch(struct dsa_switch *ds) { struct dsa_chip_data *pdata = ds->dev->platform_data; struct device_node *np = ds->dev->of_node; struct dsa_switch_tree *dst; - struct device_node *ports; u32 tree, index; int i, err; @@ -693,11 +684,7 @@ static int _dsa_register_switch(struct dsa_switch *ds) if (err) return err; - ports = dsa_get_ports(ds, np); - if (IS_ERR(ports)) - return PTR_ERR(ports); - - err = dsa_parse_ports_dn(ports, ds); + err = dsa_parse_ports_of(np, ds); if (err) return err; } else { -- cgit From fd223e2e66eb076b5dda586db9a5a3c99f76f99a Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 27 Oct 2017 15:55:14 -0400 Subject: net: dsa: add port parse functions Add symmetrical DSA port parsing functions for pdata and device tree, used to parse and validate a given port node or platform data. They don't do much for the moment but will be extended later on to assign a port type and get device references. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 9d57f8dee9a1..a0ee91cd3814 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -590,9 +590,17 @@ static int dsa_dst_parse(struct dsa_switch_tree *dst) return 0; } +static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn) +{ + dp->dn = dn; + + return 0; +} + static int dsa_parse_ports_of(struct device_node *dn, struct dsa_switch *ds) { struct device_node *ports, *port; + struct dsa_port *dp; u32 reg; int err; @@ -610,22 +618,45 @@ static int dsa_parse_ports_of(struct device_node *dn, struct dsa_switch *ds) if (reg >= ds->num_ports) return -EINVAL; - ds->ports[reg].dn = port; + dp = &ds->ports[reg]; + + err = dsa_port_parse_of(dp, port); + if (err) + return err; } return 0; } +static int dsa_port_parse(struct dsa_port *dp, const char *name, + struct device *dev) +{ + dp->name = name; + + return 0; +} + static int dsa_parse_ports(struct dsa_chip_data *cd, struct dsa_switch *ds) { bool valid_name_found = false; + struct dsa_port *dp; + struct device *dev; + const char *name; unsigned int i; + int err; for (i = 0; i < DSA_MAX_PORTS; i++) { - if (!cd->port_names[i]) + name = cd->port_names[i]; + dev = cd->netdev[i]; + dp = &ds->ports[i]; + + if (!name) continue; - ds->ports[i].name = cd->port_names[i]; + err = dsa_port_parse(dp, name, dev); + if (err) + return err; + valid_name_found = true; } -- cgit From 6d4e5c570c2d66c806ecc6bd851fcf881fe8a38e Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 27 Oct 2017 15:55:15 -0400 Subject: net: dsa: get port type at parse time Assign a port's type at parsed time instead of waiting for the tree to be completed. Because this is now done earlier, we can use the port's type in dsa_port_is_* helpers instead of digging again in topology description. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index a0ee91cd3814..895c38427ef0 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -87,23 +87,17 @@ static void dsa_dst_del_ds(struct dsa_switch_tree *dst, */ static bool dsa_port_is_valid(struct dsa_port *port) { - return !!(port->dn || port->name); + return port->type != DSA_PORT_TYPE_UNUSED; } static bool dsa_port_is_dsa(struct dsa_port *port) { - if (port->name && !strcmp(port->name, "dsa")) - return true; - else - return !!of_parse_phandle(port->dn, "link", 0); + return port->type == DSA_PORT_TYPE_DSA; } static bool dsa_port_is_cpu(struct dsa_port *port) { - if (port->name && !strcmp(port->name, "cpu")) - return true; - else - return !!of_parse_phandle(port->dn, "ethernet", 0); + return port->type == DSA_PORT_TYPE_CPU; } static bool dsa_ds_find_port_dn(struct dsa_switch *ds, @@ -183,8 +177,6 @@ static int dsa_ds_complete(struct dsa_switch_tree *dst, struct dsa_switch *ds) err = dsa_port_complete(dst, ds, port, index); if (err != 0) return err; - - port->type = DSA_PORT_TYPE_DSA; } return 0; @@ -500,8 +492,6 @@ static int dsa_cpu_parse(struct dsa_port *port, u32 index, dst->cpu_dp->master = ethernet_dev; } - port->type = DSA_PORT_TYPE_CPU; - tag_protocol = ds->ops->get_tag_protocol(ds); tag_ops = dsa_resolve_tag_protocol(tag_protocol); if (IS_ERR(tag_ops)) { @@ -534,8 +524,6 @@ static int dsa_ds_parse(struct dsa_switch_tree *dst, struct dsa_switch *ds) err = dsa_cpu_parse(port, index, dst, ds); if (err) return err; - } else { - port->type = DSA_PORT_TYPE_USER; } } @@ -592,6 +580,17 @@ static int dsa_dst_parse(struct dsa_switch_tree *dst) static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn) { + struct device_node *ethernet = of_parse_phandle(dn, "ethernet", 0); + struct device_node *link = of_parse_phandle(dn, "link", 0); + + if (ethernet) { + dp->type = DSA_PORT_TYPE_CPU; + } else if (link) { + dp->type = DSA_PORT_TYPE_DSA; + } else { + dp->type = DSA_PORT_TYPE_USER; + } + dp->dn = dn; return 0; @@ -631,6 +630,14 @@ static int dsa_parse_ports_of(struct device_node *dn, struct dsa_switch *ds) static int dsa_port_parse(struct dsa_port *dp, const char *name, struct device *dev) { + if (!strcmp(name, "cpu")) { + dp->type = DSA_PORT_TYPE_CPU; + } else if (!strcmp(name, "dsa")) { + dp->type = DSA_PORT_TYPE_DSA; + } else { + dp->type = DSA_PORT_TYPE_USER; + } + dp->name = name; return 0; -- cgit From cbabb0ac01052f79cf96d7b7e3d3451ffd275864 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 27 Oct 2017 15:55:17 -0400 Subject: net: dsa: get master device at port parsing time Fetching the master device can be done directly when a port is parsed from device tree or pdata, instead of waiting until dsa_dst_parse. Now that -EPROBE_DEFER is returned before we add the switch to the tree, there is no need to check for this error after dsa_dst_parse. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 44 ++++++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 26 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 895c38427ef0..8cd84c1b3dc0 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -470,27 +470,9 @@ static int dsa_cpu_parse(struct dsa_port *port, u32 index, { const struct dsa_device_ops *tag_ops; enum dsa_tag_protocol tag_protocol; - struct net_device *ethernet_dev; - struct device_node *ethernet; - if (port->dn) { - ethernet = of_parse_phandle(port->dn, "ethernet", 0); - if (!ethernet) - return -EINVAL; - ethernet_dev = of_find_net_device_by_node(ethernet); - if (!ethernet_dev) - return -EPROBE_DEFER; - } else { - ethernet_dev = dsa_dev_to_net_device(ds->cd->netdev[index]); - if (!ethernet_dev) - return -EPROBE_DEFER; - dev_put(ethernet_dev); - } - - if (!dst->cpu_dp) { + if (!dst->cpu_dp) dst->cpu_dp = port; - dst->cpu_dp->master = ethernet_dev; - } tag_protocol = ds->ops->get_tag_protocol(ds); tag_ops = dsa_resolve_tag_protocol(tag_protocol); @@ -584,7 +566,14 @@ static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn) struct device_node *link = of_parse_phandle(dn, "link", 0); if (ethernet) { + struct net_device *master; + + master = of_find_net_device_by_node(ethernet); + if (!master) + return -EPROBE_DEFER; + dp->type = DSA_PORT_TYPE_CPU; + dp->master = master; } else if (link) { dp->type = DSA_PORT_TYPE_DSA; } else { @@ -631,7 +620,16 @@ static int dsa_port_parse(struct dsa_port *dp, const char *name, struct device *dev) { if (!strcmp(name, "cpu")) { + struct net_device *master; + + master = dsa_dev_to_net_device(dev); + if (!master) + return -EPROBE_DEFER; + + dev_put(master); + dp->type = DSA_PORT_TYPE_CPU; + dp->master = master; } else if (!strcmp(name, "dsa")) { dp->type = DSA_PORT_TYPE_DSA; } else { @@ -773,14 +771,8 @@ static int _dsa_register_switch(struct dsa_switch *ds) } err = dsa_dst_parse(dst); - if (err) { - if (err == -EPROBE_DEFER) { - dsa_dst_del_ds(dst, ds, ds->index); - return err; - } - + if (err) goto out_del_dst; - } err = dsa_dst_apply(dst); if (err) { -- cgit From 1838fa89a22cbc9ec87e995683e241a82d87e6df Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 27 Oct 2017 15:55:18 -0400 Subject: net: dsa: get port name at parse time Get the optional "label" property and assign a default one directly at parse time instead of doing it when creating the slave. For legacy, simply assign the port name stored in cd->port_names. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 10 +++++----- net/dsa/legacy.c | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 8cd84c1b3dc0..3c134ff26863 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -260,11 +260,6 @@ static int dsa_user_port_apply(struct dsa_port *port) const char *name = port->name; int err; - if (port->dn) - name = of_get_property(port->dn, "label", NULL); - if (!name) - name = "eth%d"; - err = dsa_slave_create(port, name); if (err) { dev_warn(ds->dev, "Failed to create slave %d: %d\n", @@ -564,6 +559,7 @@ static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn) { struct device_node *ethernet = of_parse_phandle(dn, "ethernet", 0); struct device_node *link = of_parse_phandle(dn, "link", 0); + const char *name = of_get_property(dn, "label", NULL); if (ethernet) { struct net_device *master; @@ -577,7 +573,11 @@ static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn) } else if (link) { dp->type = DSA_PORT_TYPE_DSA; } else { + if (!name) + name = "eth%d"; + dp->type = DSA_PORT_TYPE_USER; + dp->name = name; } dp->dn = dn; diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index ed7aae342fca..afe6e1539bd0 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -115,6 +115,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, name = cd->port_names[i]; if (name == NULL) continue; + dp->name = name; if (!strcmp(name, "cpu")) { if (dst->cpu_dp) { -- cgit From 951259aa60180e2897d28b538bf68a3a213da471 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 27 Oct 2017 15:55:19 -0400 Subject: net: dsa: remove name arg from slave create Now that slave dsa_port always have their name set, there is no need to pass it to dsa_slave_create() anymore. Remove this argument. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 3 +-- net/dsa/dsa_priv.h | 2 +- net/dsa/legacy.c | 2 +- net/dsa/slave.c | 3 ++- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 3c134ff26863..797d1156b4e6 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -257,10 +257,9 @@ static void dsa_cpu_port_unapply(struct dsa_port *port) static int dsa_user_port_apply(struct dsa_port *port) { struct dsa_switch *ds = port->ds; - const char *name = port->name; int err; - err = dsa_slave_create(port, name); + err = dsa_slave_create(port); if (err) { dev_warn(ds->dev, "Failed to create slave %d: %d\n", port->index, err); diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 1e65afd6989e..253a613c40cd 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -163,7 +163,7 @@ void dsa_port_fixed_link_unregister_of(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); -int dsa_slave_create(struct dsa_port *port, const char *name); +int dsa_slave_create(struct dsa_port *dp); void dsa_slave_destroy(struct net_device *slave_dev); int dsa_slave_suspend(struct net_device *slave_dev); int dsa_slave_resume(struct net_device *slave_dev); diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index afe6e1539bd0..0511fe2feff7 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -197,7 +197,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, if (dsa_is_user_port(ds, i)) continue; - ret = dsa_slave_create(&ds->ports[i], cd->port_names[i]); + ret = dsa_slave_create(&ds->ports[i]); if (ret < 0) netdev_err(master, "[%d]: can't create dsa slave device for port %d(%s): %d\n", index, i, cd->port_names[i], ret); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 808e205227c3..48b954a76b0d 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -1142,11 +1142,12 @@ static void dsa_slave_notify(struct net_device *dev, unsigned long val) call_dsa_notifiers(val, dev, &rinfo.info); } -int dsa_slave_create(struct dsa_port *port, const char *name) +int dsa_slave_create(struct dsa_port *port) { struct dsa_port *cpu_dp = port->cpu_dp; struct net_device *master = cpu_dp->master; struct dsa_switch *ds = port->ds; + const char *name = port->name; struct net_device *slave_dev; struct dsa_slave_priv *p; int ret; -- cgit From 49463b7f2da1a115404b02c5533bc2c2125833a3 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 3 Nov 2017 19:05:21 -0400 Subject: net: dsa: make tree index unsigned Similarly to a DSA switch and port, rename the tree index from "tree" to "index" and make it an unsigned int because it isn't supposed to be less than 0. u32 is an OF specific data used to retrieve the value and has no need to be propagated up to the tree index. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/dsa.h | 2 +- net/dsa/dsa2.c | 14 +++++++------- net/dsa/slave.c | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/include/net/dsa.h b/include/net/dsa.h index fa1c21ab8092..e54332968417 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -116,7 +116,7 @@ struct dsa_switch_tree { struct raw_notifier_head nh; /* Tree identifier */ - u32 tree; + unsigned int index; /* Number of switches attached to this tree */ struct kref refcount; diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 797d1156b4e6..8b68dc2f5707 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -27,12 +27,12 @@ static DEFINE_MUTEX(dsa2_mutex); static const struct devlink_ops dsa_devlink_ops = { }; -static struct dsa_switch_tree *dsa_get_dst(u32 tree) +static struct dsa_switch_tree *dsa_get_dst(unsigned int index) { struct dsa_switch_tree *dst; list_for_each_entry(dst, &dsa_switch_trees, list) - if (dst->tree == tree) { + if (dst->index == index) { kref_get(&dst->refcount); return dst; } @@ -53,14 +53,14 @@ static void dsa_put_dst(struct dsa_switch_tree *dst) kref_put(&dst->refcount, dsa_free_dst); } -static struct dsa_switch_tree *dsa_add_dst(u32 tree) +static struct dsa_switch_tree *dsa_add_dst(unsigned int index) { struct dsa_switch_tree *dst; dst = kzalloc(sizeof(*dst), GFP_KERNEL); if (!dst) return NULL; - dst->tree = tree; + dst->index = index; INIT_LIST_HEAD(&dst->list); list_add_tail(&dsa_switch_trees, &dst->list); kref_init(&dst->refcount); @@ -454,7 +454,7 @@ static void dsa_dst_unapply(struct dsa_switch_tree *dst) dst->cpu_dp = NULL; - pr_info("DSA: tree %d unapplied\n", dst->tree); + pr_info("DSA: tree %d unapplied\n", dst->index); dst->applied = false; } @@ -504,7 +504,7 @@ static int dsa_ds_parse(struct dsa_switch_tree *dst, struct dsa_switch *ds) } - pr_info("DSA: switch %d %d parsed\n", dst->tree, ds->index); + pr_info("DSA: switch %d %d parsed\n", dst->index, ds->index); return 0; } @@ -549,7 +549,7 @@ static int dsa_dst_parse(struct dsa_switch_tree *dst) } } - pr_info("DSA: tree %d parsed\n", dst->tree); + pr_info("DSA: tree %d parsed\n", dst->index); return 0; } diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 9b75d0ac4092..814ced75a0cc 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -55,7 +55,7 @@ void dsa_slave_mii_bus_init(struct dsa_switch *ds) ds->slave_mii_bus->read = dsa_slave_phy_read; ds->slave_mii_bus->write = dsa_slave_phy_write; snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "dsa-%d.%d", - ds->dst->tree, ds->index); + ds->dst->index, ds->index); ds->slave_mii_bus->parent = ds->dev; ds->slave_mii_bus->phy_mask = ~ds->phys_mii_mask; } -- cgit From 8e5bf9759a06be2251fa96cfd8b412f1808c62f9 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 3 Nov 2017 19:05:22 -0400 Subject: net: dsa: simplify tree reference counting DSA trees have a refcount used to automatically free the dsa_switch_tree structure once there is no switch devices inside of it. The refcount is incremented when a switch is added to the tree, and decremented when it is removed from it. But because of kref_init, the refcount is also incremented at initialization, and when looking up the tree from the list for symmetry. Thus the current code stores the number of switches plus one, and makes the switch registration more complex. To simplify the switch registration function, we reset the refcount to zero after initialization and don't increment it when looking up a tree. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 8b68dc2f5707..d3f1a7607463 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -32,10 +32,9 @@ static struct dsa_switch_tree *dsa_get_dst(unsigned int index) struct dsa_switch_tree *dst; list_for_each_entry(dst, &dsa_switch_trees, list) - if (dst->index == index) { - kref_get(&dst->refcount); + if (dst->index == index) return dst; - } + return NULL; } @@ -48,11 +47,6 @@ static void dsa_free_dst(struct kref *ref) kfree(dst); } -static void dsa_put_dst(struct dsa_switch_tree *dst) -{ - kref_put(&dst->refcount, dsa_free_dst); -} - static struct dsa_switch_tree *dsa_add_dst(unsigned int index) { struct dsa_switch_tree *dst; @@ -63,7 +57,10 @@ static struct dsa_switch_tree *dsa_add_dst(unsigned int index) dst->index = index; INIT_LIST_HEAD(&dst->list); list_add_tail(&dsa_switch_trees, &dst->list); + + /* Initialize the reference counter to the number of switches, not 1 */ kref_init(&dst->refcount); + refcount_set(&dst->refcount.refcount, 0); return dst; } @@ -739,10 +736,8 @@ static int _dsa_register_switch(struct dsa_switch *ds) return -ENOMEM; } - if (dst->ds[index]) { - err = -EBUSY; - goto out; - } + if (dst->ds[index]) + return -EBUSY; ds->dst = dst; ds->index = index; @@ -758,11 +753,9 @@ static int _dsa_register_switch(struct dsa_switch *ds) if (err < 0) goto out_del_dst; - if (err == 1) { - /* Not all switches registered yet */ - err = 0; - goto out; - } + /* Not all switches registered yet */ + if (err == 1) + return 0; if (dst->applied) { pr_info("DSA: Disjoint trees?\n"); @@ -779,13 +772,10 @@ static int _dsa_register_switch(struct dsa_switch *ds) goto out_del_dst; } - dsa_put_dst(dst); return 0; out_del_dst: dsa_dst_del_ds(dst, ds, ds->index); -out: - dsa_put_dst(dst); return err; } -- cgit From 65254108b4655dc55e8d8f62ee895960085e73f4 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 3 Nov 2017 19:05:23 -0400 Subject: net: dsa: get and put tree reference counting Provide convenient dsa_tree_get and dsa_tree_put functions scoping a DSA tree used to increment and decrement its reference counter, instead of poking directly its kref structure. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index d3f1a7607463..609d92684505 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -38,15 +38,6 @@ static struct dsa_switch_tree *dsa_get_dst(unsigned int index) return NULL; } -static void dsa_free_dst(struct kref *ref) -{ - struct dsa_switch_tree *dst = container_of(ref, struct dsa_switch_tree, - refcount); - - list_del(&dst->list); - kfree(dst); -} - static struct dsa_switch_tree *dsa_add_dst(unsigned int index) { struct dsa_switch_tree *dst; @@ -65,10 +56,35 @@ static struct dsa_switch_tree *dsa_add_dst(unsigned int index) return dst; } +static void dsa_tree_free(struct dsa_switch_tree *dst) +{ + list_del(&dst->list); + kfree(dst); +} + +static void dsa_tree_get(struct dsa_switch_tree *dst) +{ + kref_get(&dst->refcount); +} + +static void dsa_tree_release(struct kref *ref) +{ + struct dsa_switch_tree *dst; + + dst = container_of(ref, struct dsa_switch_tree, refcount); + + dsa_tree_free(dst); +} + +static void dsa_tree_put(struct dsa_switch_tree *dst) +{ + kref_put(&dst->refcount, dsa_tree_release); +} + static void dsa_dst_add_ds(struct dsa_switch_tree *dst, struct dsa_switch *ds, u32 index) { - kref_get(&dst->refcount); + dsa_tree_get(dst); dst->ds[index] = ds; } @@ -76,7 +92,7 @@ static void dsa_dst_del_ds(struct dsa_switch_tree *dst, struct dsa_switch *ds, u32 index) { dst->ds[index] = NULL; - kref_put(&dst->refcount, dsa_free_dst); + dsa_tree_put(dst); } /* For platform data configurations, we need to have a valid name argument to -- cgit From 1ca28ec9abff927178b1ea9d6431e4c320145b10 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 3 Nov 2017 19:05:24 -0400 Subject: net: dsa: provide a find or new tree helper Rename dsa_get_dst to dsa_tree_find since it doesn't increment the reference counter, rename dsa_add_dst to dsa_tree_alloc for symmetry with dsa_tree_free, and provide a convenient dsa_tree_touch function to find or allocate a new tree. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 609d92684505..bda222cfc02c 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -21,33 +21,35 @@ #include "dsa_priv.h" -static LIST_HEAD(dsa_switch_trees); +static LIST_HEAD(dsa_tree_list); static DEFINE_MUTEX(dsa2_mutex); static const struct devlink_ops dsa_devlink_ops = { }; -static struct dsa_switch_tree *dsa_get_dst(unsigned int index) +static struct dsa_switch_tree *dsa_tree_find(int index) { struct dsa_switch_tree *dst; - list_for_each_entry(dst, &dsa_switch_trees, list) + list_for_each_entry(dst, &dsa_tree_list, list) if (dst->index == index) return dst; return NULL; } -static struct dsa_switch_tree *dsa_add_dst(unsigned int index) +static struct dsa_switch_tree *dsa_tree_alloc(int index) { struct dsa_switch_tree *dst; dst = kzalloc(sizeof(*dst), GFP_KERNEL); if (!dst) return NULL; + dst->index = index; + INIT_LIST_HEAD(&dst->list); - list_add_tail(&dsa_switch_trees, &dst->list); + list_add_tail(&dsa_tree_list, &dst->list); /* Initialize the reference counter to the number of switches, not 1 */ kref_init(&dst->refcount); @@ -62,6 +64,17 @@ static void dsa_tree_free(struct dsa_switch_tree *dst) kfree(dst); } +static struct dsa_switch_tree *dsa_tree_touch(int index) +{ + struct dsa_switch_tree *dst; + + dst = dsa_tree_find(index); + if (!dst) + dst = dsa_tree_alloc(index); + + return dst; +} + static void dsa_tree_get(struct dsa_switch_tree *dst) { kref_get(&dst->refcount); @@ -745,12 +758,9 @@ static int _dsa_register_switch(struct dsa_switch *ds) return err; } - dst = dsa_get_dst(tree); - if (!dst) { - dst = dsa_add_dst(tree); - if (!dst) - return -ENOMEM; - } + dst = dsa_tree_touch(tree); + if (!dst) + return -ENOMEM; if (dst->ds[index]) return -EBUSY; -- cgit From 6da2a940ac6a0680e50b3aaf945e409cea03c346 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 3 Nov 2017 19:05:25 -0400 Subject: net: dsa: rework switch addition and removal This patch removes the unnecessary index argument from the dsa_dst_add_ds and dsa_dst_del_ds functions and renames them to dsa_tree_add_switch and dsa_tree_remove_switch respectively. In addition to a more explicit scope, we now check the presence of an existing switch with the same index directly within dsa_tree_add_switch. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 47 +++++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 20 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index bda222cfc02c..5b6a3dad8015 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -94,20 +94,6 @@ static void dsa_tree_put(struct dsa_switch_tree *dst) kref_put(&dst->refcount, dsa_tree_release); } -static void dsa_dst_add_ds(struct dsa_switch_tree *dst, - struct dsa_switch *ds, u32 index) -{ - dsa_tree_get(dst); - dst->ds[index] = ds; -} - -static void dsa_dst_del_ds(struct dsa_switch_tree *dst, - struct dsa_switch *ds, u32 index) -{ - dst->ds[index] = NULL; - dsa_tree_put(dst); -} - /* For platform data configurations, we need to have a valid name argument to * differentiate a disabled port from an enabled one */ @@ -484,6 +470,27 @@ static void dsa_dst_unapply(struct dsa_switch_tree *dst) dst->applied = false; } +static void dsa_tree_remove_switch(struct dsa_switch_tree *dst, + unsigned int index) +{ + dst->ds[index] = NULL; + dsa_tree_put(dst); +} + +static int dsa_tree_add_switch(struct dsa_switch_tree *dst, + struct dsa_switch *ds) +{ + unsigned int index = ds->index; + + if (dst->ds[index]) + return -EBUSY; + + dsa_tree_get(dst); + dst->ds[index] = ds; + + return 0; +} + static int dsa_cpu_parse(struct dsa_port *port, u32 index, struct dsa_switch_tree *dst, struct dsa_switch *ds) @@ -762,9 +769,6 @@ static int _dsa_register_switch(struct dsa_switch *ds) if (!dst) return -ENOMEM; - if (dst->ds[index]) - return -EBUSY; - ds->dst = dst; ds->index = index; ds->cd = pdata; @@ -773,7 +777,9 @@ static int _dsa_register_switch(struct dsa_switch *ds) for (i = 0; i < DSA_MAX_SWITCHES; ++i) ds->rtable[i] = DSA_RTABLE_NONE; - dsa_dst_add_ds(dst, ds, index); + err = dsa_tree_add_switch(dst, ds); + if (err) + return err; err = dsa_dst_complete(dst); if (err < 0) @@ -801,7 +807,7 @@ static int _dsa_register_switch(struct dsa_switch *ds) return 0; out_del_dst: - dsa_dst_del_ds(dst, ds, ds->index); + dsa_tree_remove_switch(dst, index); return err; } @@ -843,10 +849,11 @@ EXPORT_SYMBOL_GPL(dsa_register_switch); static void _dsa_unregister_switch(struct dsa_switch *ds) { struct dsa_switch_tree *dst = ds->dst; + unsigned int index = ds->index; dsa_dst_unapply(dst); - dsa_dst_del_ds(dst, ds, ds->index); + dsa_tree_remove_switch(dst, index); } void dsa_unregister_switch(struct dsa_switch *ds) -- cgit From 0eefe2c1730020c6207bc9695fd466e558301dbb Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 3 Nov 2017 19:05:26 -0400 Subject: net: dsa: get tree before parsing ports We will need a reference to the dsa_switch_tree when parsing a CPU port, so fetch it right after parsing the member and before parsing ports. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 5b6a3dad8015..5918fbddb0ab 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -751,18 +751,10 @@ static int _dsa_register_switch(struct dsa_switch *ds) err = dsa_parse_member_dn(np, &tree, &index); if (err) return err; - - err = dsa_parse_ports_of(np, ds); - if (err) - return err; } else { err = dsa_parse_member(pdata, &tree, &index); if (err) return err; - - err = dsa_parse_ports(pdata, ds); - if (err) - return err; } dst = dsa_tree_touch(tree); @@ -773,6 +765,16 @@ static int _dsa_register_switch(struct dsa_switch *ds) ds->index = index; ds->cd = pdata; + if (np) { + err = dsa_parse_ports_of(np, ds); + if (err) + return err; + } else { + err = dsa_parse_ports(pdata, ds); + if (err) + return err; + } + /* Initialize the routing table */ for (i = 0; i < DSA_MAX_SWITCHES; ++i) ds->rtable[i] = DSA_RTABLE_NONE; -- cgit From 975e6e32215e6cbc09b65d762865b1a46e8e9103 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 3 Nov 2017 19:05:27 -0400 Subject: net: dsa: rework switch parsing When parsing a switch, we have to identify to which tree it belongs and parse its ports. Provide two functions to separate the OF and platform data specific paths. Also use the of_property_read_variable_u32_array function to parse the OF member array instead of calling of_property_read_u32_index twice. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 117 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 58 insertions(+), 59 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 5918fbddb0ab..dfcb6247f2f2 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -617,7 +617,8 @@ static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn) return 0; } -static int dsa_parse_ports_of(struct device_node *dn, struct dsa_switch *ds) +static int dsa_switch_parse_ports_of(struct dsa_switch *ds, + struct device_node *dn) { struct device_node *ports, *port; struct dsa_port *dp; @@ -648,6 +649,39 @@ static int dsa_parse_ports_of(struct device_node *dn, struct dsa_switch *ds) return 0; } +static int dsa_switch_parse_member_of(struct dsa_switch *ds, + struct device_node *dn) +{ + u32 m[2] = { 0, 0 }; + int sz; + + /* Don't error out if this optional property isn't found */ + sz = of_property_read_variable_u32_array(dn, "dsa,member", m, 2, 2); + if (sz < 0 && sz != -EINVAL) + return sz; + + ds->index = m[1]; + if (ds->index >= DSA_MAX_SWITCHES) + return -EINVAL; + + ds->dst = dsa_tree_touch(m[0]); + if (!ds->dst) + return -ENOMEM; + + return 0; +} + +static int dsa_switch_parse_of(struct dsa_switch *ds, struct device_node *dn) +{ + int err; + + err = dsa_switch_parse_member_of(ds, dn); + if (err) + return err; + + return dsa_switch_parse_ports_of(ds, dn); +} + static int dsa_port_parse(struct dsa_port *dp, const char *name, struct device *dev) { @@ -673,7 +707,8 @@ static int dsa_port_parse(struct dsa_port *dp, const char *name, return 0; } -static int dsa_parse_ports(struct dsa_chip_data *cd, struct dsa_switch *ds) +static int dsa_switch_parse_ports(struct dsa_switch *ds, + struct dsa_chip_data *cd) { bool valid_name_found = false; struct dsa_port *dp; @@ -703,40 +738,19 @@ static int dsa_parse_ports(struct dsa_chip_data *cd, struct dsa_switch *ds) return 0; } -static int dsa_parse_member_dn(struct device_node *np, u32 *tree, u32 *index) -{ - int err; - - *tree = *index = 0; - - err = of_property_read_u32_index(np, "dsa,member", 0, tree); - if (err) { - /* Does not exist, but it is optional */ - if (err == -EINVAL) - return 0; - return err; - } - - err = of_property_read_u32_index(np, "dsa,member", 1, index); - if (err) - return err; - - if (*index >= DSA_MAX_SWITCHES) - return -EINVAL; - - return 0; -} - -static int dsa_parse_member(struct dsa_chip_data *pd, u32 *tree, u32 *index) +static int dsa_switch_parse(struct dsa_switch *ds, struct dsa_chip_data *cd) { - if (!pd) - return -ENODEV; + ds->cd = cd; - /* We do not support complex trees with dsa_chip_data */ - *tree = 0; - *index = 0; + /* We don't support interconnected switches nor multiple trees via + * platform data, so this is the unique switch of the tree. + */ + ds->index = 0; + ds->dst = dsa_tree_touch(0); + if (!ds->dst) + return -ENOMEM; - return 0; + return dsa_switch_parse_ports(ds, cd); } static int _dsa_register_switch(struct dsa_switch *ds) @@ -744,36 +758,21 @@ static int _dsa_register_switch(struct dsa_switch *ds) struct dsa_chip_data *pdata = ds->dev->platform_data; struct device_node *np = ds->dev->of_node; struct dsa_switch_tree *dst; - u32 tree, index; + unsigned int index; int i, err; - if (np) { - err = dsa_parse_member_dn(np, &tree, &index); - if (err) - return err; - } else { - err = dsa_parse_member(pdata, &tree, &index); - if (err) - return err; - } + if (np) + err = dsa_switch_parse_of(ds, np); + else if (pdata) + err = dsa_switch_parse(ds, pdata); + else + err = -ENODEV; - dst = dsa_tree_touch(tree); - if (!dst) - return -ENOMEM; - - ds->dst = dst; - ds->index = index; - ds->cd = pdata; + if (err) + return err; - if (np) { - err = dsa_parse_ports_of(np, ds); - if (err) - return err; - } else { - err = dsa_parse_ports(pdata, ds); - if (err) - return err; - } + index = ds->index; + dst = ds->dst; /* Initialize the routing table */ for (i = 0; i < DSA_MAX_SWITCHES; ++i) -- cgit From 54df6fa9541752b3b28106eb1a9764dcda815fd2 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 3 Nov 2017 19:05:28 -0400 Subject: net: dsa: only check presence of link property When parsing a port, simply use of_property_read_bool which checks the presence of a given property, instead of parsing the link phandle. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index dfcb6247f2f2..06bcdb6bc796 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -590,8 +590,8 @@ static int dsa_dst_parse(struct dsa_switch_tree *dst) static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn) { struct device_node *ethernet = of_parse_phandle(dn, "ethernet", 0); - struct device_node *link = of_parse_phandle(dn, "link", 0); const char *name = of_get_property(dn, "label", NULL); + bool link = of_property_read_bool(dn, "link"); if (ethernet) { struct net_device *master; -- cgit From 06e24d0868a361774fc46d0819450915e63a2815 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 3 Nov 2017 19:05:29 -0400 Subject: net: dsa: add one port parsing function per type Add dsa_port_parse_user, dsa_port_parse_dsa and dsa_port_parse_cpu functions to factorize the code shared by both OF and pdata parsing. They don't do much for the moment but will be extended later to support tagging protocol resolution for example. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 56 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 20 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 06bcdb6bc796..271a97ef5bf6 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -491,6 +491,32 @@ static int dsa_tree_add_switch(struct dsa_switch_tree *dst, return 0; } +static int dsa_port_parse_user(struct dsa_port *dp, const char *name) +{ + if (!name) + name = "eth%d"; + + dp->type = DSA_PORT_TYPE_USER; + dp->name = name; + + return 0; +} + +static int dsa_port_parse_dsa(struct dsa_port *dp) +{ + dp->type = DSA_PORT_TYPE_DSA; + + return 0; +} + +static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master) +{ + dp->type = DSA_PORT_TYPE_CPU; + dp->master = master; + + return 0; +} + static int dsa_cpu_parse(struct dsa_port *port, u32 index, struct dsa_switch_tree *dst, struct dsa_switch *ds) @@ -593,6 +619,8 @@ static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn) const char *name = of_get_property(dn, "label", NULL); bool link = of_property_read_bool(dn, "link"); + dp->dn = dn; + if (ethernet) { struct net_device *master; @@ -600,21 +628,13 @@ static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn) if (!master) return -EPROBE_DEFER; - dp->type = DSA_PORT_TYPE_CPU; - dp->master = master; - } else if (link) { - dp->type = DSA_PORT_TYPE_DSA; - } else { - if (!name) - name = "eth%d"; - - dp->type = DSA_PORT_TYPE_USER; - dp->name = name; + return dsa_port_parse_cpu(dp, master); } - dp->dn = dn; + if (link) + return dsa_port_parse_dsa(dp); - return 0; + return dsa_port_parse_user(dp, name); } static int dsa_switch_parse_ports_of(struct dsa_switch *ds, @@ -694,17 +714,13 @@ static int dsa_port_parse(struct dsa_port *dp, const char *name, dev_put(master); - dp->type = DSA_PORT_TYPE_CPU; - dp->master = master; - } else if (!strcmp(name, "dsa")) { - dp->type = DSA_PORT_TYPE_DSA; - } else { - dp->type = DSA_PORT_TYPE_USER; + return dsa_port_parse_cpu(dp, master); } - dp->name = name; + if (!strcmp(name, "dsa")) + return dsa_port_parse_dsa(dp); - return 0; + return dsa_port_parse_user(dp, name); } static int dsa_switch_parse_ports(struct dsa_switch *ds, -- cgit From 7354fcb0a3a3a5518107f8de117a6ce5ce08cc7c Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 3 Nov 2017 19:05:30 -0400 Subject: net: dsa: resolve tagging protocol at parse time Extend the dsa_port_parse_cpu() function to resolve the tagging protocol at port parsing time, instead of waiting for the whole tree to be complete. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 271a97ef5bf6..283104e5ca6a 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -511,8 +511,23 @@ static int dsa_port_parse_dsa(struct dsa_port *dp) static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master) { + struct dsa_switch *ds = dp->ds; + struct dsa_switch_tree *dst = ds->dst; + const struct dsa_device_ops *tag_ops; + enum dsa_tag_protocol tag_protocol; + + tag_protocol = ds->ops->get_tag_protocol(ds); + tag_ops = dsa_resolve_tag_protocol(tag_protocol); + if (IS_ERR(tag_ops)) { + dev_warn(ds->dev, "No tagger for this switch\n"); + return PTR_ERR(tag_ops); + } + dp->type = DSA_PORT_TYPE_CPU; + dp->rcv = tag_ops->rcv; + dp->tag_ops = tag_ops; dp->master = master; + dp->dst = dst; return 0; } @@ -521,25 +536,9 @@ static int dsa_cpu_parse(struct dsa_port *port, u32 index, struct dsa_switch_tree *dst, struct dsa_switch *ds) { - const struct dsa_device_ops *tag_ops; - enum dsa_tag_protocol tag_protocol; - if (!dst->cpu_dp) dst->cpu_dp = port; - tag_protocol = ds->ops->get_tag_protocol(ds); - tag_ops = dsa_resolve_tag_protocol(tag_protocol); - if (IS_ERR(tag_ops)) { - dev_warn(ds->dev, "No tagger for this switch\n"); - return PTR_ERR(tag_ops); - } - - dst->cpu_dp->tag_ops = tag_ops; - - /* Make a few copies for faster access in master receive hot path */ - dst->cpu_dp->rcv = dst->cpu_dp->tag_ops->rcv; - dst->cpu_dp->dst = dst; - return 0; } -- cgit From f070464cf000131928b2c3fd592efd1946610eea Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 6 Nov 2017 16:11:44 -0500 Subject: net: dsa: setup and teardown default CPU port The dsa_dst_parse function called just before dsa_dst_apply does not parse the tree but does only one thing: it assigns the default CPU port to dst->cpu_dp and to each user ports. This patch simplifies this by calling a dsa_tree_setup_default_cpu function at the beginning of dsa_dst_apply directly. A dsa_port_is_user helper is added for convenience. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 153 +++++++++++++++++++++++++-------------------------------- 1 file changed, 68 insertions(+), 85 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 283104e5ca6a..0a63a2119cd0 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -112,6 +112,11 @@ static bool dsa_port_is_cpu(struct dsa_port *port) return port->type == DSA_PORT_TYPE_CPU; } +static bool dsa_port_is_user(struct dsa_port *dp) +{ + return dp->type == DSA_PORT_TYPE_USER; +} + static bool dsa_ds_find_port_dn(struct dsa_switch *ds, struct device_node *port) { @@ -218,6 +223,64 @@ static int dsa_dst_complete(struct dsa_switch_tree *dst) return 0; } +static struct dsa_port *dsa_tree_find_first_cpu(struct dsa_switch_tree *dst) +{ + struct dsa_switch *ds; + struct dsa_port *dp; + int device, port; + + for (device = 0; device < DSA_MAX_SWITCHES; device++) { + ds = dst->ds[device]; + if (!ds) + continue; + + for (port = 0; port < ds->num_ports; port++) { + dp = &ds->ports[port]; + + if (dsa_port_is_cpu(dp)) + return dp; + } + } + + return NULL; +} + +static int dsa_tree_setup_default_cpu(struct dsa_switch_tree *dst) +{ + struct dsa_switch *ds; + struct dsa_port *dp; + int device, port; + + /* DSA currently only supports a single CPU port */ + dst->cpu_dp = dsa_tree_find_first_cpu(dst); + if (!dst->cpu_dp) { + pr_warn("Tree has no master device\n"); + return -EINVAL; + } + + /* Assign the default CPU port to all ports of the fabric */ + for (device = 0; device < DSA_MAX_SWITCHES; device++) { + ds = dst->ds[device]; + if (!ds) + continue; + + for (port = 0; port < ds->num_ports; port++) { + dp = &ds->ports[port]; + + if (dsa_port_is_user(dp)) + dp->cpu_dp = dst->cpu_dp; + } + } + + return 0; +} + +static void dsa_tree_teardown_default_cpu(struct dsa_switch_tree *dst) +{ + /* DSA currently only supports a single CPU port */ + dst->cpu_dp = NULL; +} + static int dsa_dsa_port_apply(struct dsa_port *port) { struct dsa_switch *ds = port->ds; @@ -412,6 +475,10 @@ static int dsa_dst_apply(struct dsa_switch_tree *dst) u32 index; int err; + err = dsa_tree_setup_default_cpu(dst); + if (err) + return err; + for (index = 0; index < DSA_MAX_SWITCHES; index++) { ds = dst->ds[index]; if (!ds) @@ -464,7 +531,7 @@ static void dsa_dst_unapply(struct dsa_switch_tree *dst) dsa_ds_unapply(dst, ds); } - dst->cpu_dp = NULL; + dsa_tree_teardown_default_cpu(dst); pr_info("DSA: tree %d unapplied\n", dst->index); dst->applied = false; @@ -532,86 +599,6 @@ static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master) return 0; } -static int dsa_cpu_parse(struct dsa_port *port, u32 index, - struct dsa_switch_tree *dst, - struct dsa_switch *ds) -{ - if (!dst->cpu_dp) - dst->cpu_dp = port; - - return 0; -} - -static int dsa_ds_parse(struct dsa_switch_tree *dst, struct dsa_switch *ds) -{ - struct dsa_port *port; - u32 index; - int err; - - for (index = 0; index < ds->num_ports; index++) { - port = &ds->ports[index]; - if (!dsa_port_is_valid(port) || - dsa_port_is_dsa(port)) - continue; - - if (dsa_port_is_cpu(port)) { - err = dsa_cpu_parse(port, index, dst, ds); - if (err) - return err; - } - - } - - pr_info("DSA: switch %d %d parsed\n", dst->index, ds->index); - - return 0; -} - -static int dsa_dst_parse(struct dsa_switch_tree *dst) -{ - struct dsa_switch *ds; - struct dsa_port *dp; - u32 index; - int port; - int err; - - for (index = 0; index < DSA_MAX_SWITCHES; index++) { - ds = dst->ds[index]; - if (!ds) - continue; - - err = dsa_ds_parse(dst, ds); - if (err) - return err; - } - - if (!dst->cpu_dp) { - pr_warn("Tree has no master device\n"); - return -EINVAL; - } - - /* Assign the default CPU port to all ports of the fabric */ - for (index = 0; index < DSA_MAX_SWITCHES; index++) { - ds = dst->ds[index]; - if (!ds) - continue; - - for (port = 0; port < ds->num_ports; port++) { - dp = &ds->ports[port]; - if (!dsa_port_is_valid(dp) || - dsa_port_is_dsa(dp) || - dsa_port_is_cpu(dp)) - continue; - - dp->cpu_dp = dst->cpu_dp; - } - } - - pr_info("DSA: tree %d parsed\n", dst->index); - - return 0; -} - static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn) { struct device_node *ethernet = of_parse_phandle(dn, "ethernet", 0); @@ -810,10 +797,6 @@ static int _dsa_register_switch(struct dsa_switch *ds) return -EINVAL; } - err = dsa_dst_parse(dst); - if (err) - goto out_del_dst; - err = dsa_dst_apply(dst); if (err) { dsa_dst_unapply(dst); -- cgit From 17a22fcfc84a422d98a0f54e67d4ee8ee3875849 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 6 Nov 2017 16:11:45 -0500 Subject: net: dsa: setup and teardown master device Add DSA helpers to setup and teardown a master net device wired to its CPU port. This centralizes the dsa_ptr assignment. This also makes the master ethtool helpers static at the same time. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 36 +++++++++++++++++++----------------- net/dsa/dsa_priv.h | 4 ++-- net/dsa/legacy.c | 20 ++------------------ net/dsa/master.c | 30 ++++++++++++++++++++++++++++-- 4 files changed, 51 insertions(+), 39 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 0a63a2119cd0..c9b50339fcac 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -469,6 +469,23 @@ static void dsa_ds_unapply(struct dsa_switch_tree *dst, struct dsa_switch *ds) } +static int dsa_tree_setup_master(struct dsa_switch_tree *dst) +{ + struct dsa_port *cpu_dp = dst->cpu_dp; + struct net_device *master = cpu_dp->master; + + /* DSA currently supports a single pair of CPU port and master device */ + return dsa_master_setup(master, cpu_dp); +} + +static void dsa_tree_teardown_master(struct dsa_switch_tree *dst) +{ + struct dsa_port *cpu_dp = dst->cpu_dp; + struct net_device *master = cpu_dp->master; + + return dsa_master_teardown(master); +} + static int dsa_dst_apply(struct dsa_switch_tree *dst) { struct dsa_switch *ds; @@ -489,14 +506,7 @@ static int dsa_dst_apply(struct dsa_switch_tree *dst) 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->master->dsa_ptr = dst->cpu_dp; - - err = dsa_master_ethtool_setup(dst->cpu_dp->master); + err = dsa_tree_setup_master(dst); if (err) return err; @@ -513,15 +523,7 @@ static void dsa_dst_unapply(struct dsa_switch_tree *dst) if (!dst->applied) return; - dsa_master_ethtool_restore(dst->cpu_dp->master); - - dst->cpu_dp->master->dsa_ptr = NULL; - - /* If we used a tagging format that doesn't have an ethertype - * field, make sure that all packets from this point get sent - * without the tag and go through the regular receive path. - */ - wmb(); + dsa_tree_teardown_master(dst); for (index = 0; index < DSA_MAX_SWITCHES; index++) { ds = dst->ds[index]; diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 253a613c40cd..bb0218c1b570 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -108,8 +108,8 @@ int dsa_legacy_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], 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); +int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp); +void dsa_master_teardown(struct net_device *dev); static inline struct net_device *dsa_master_find_slave(struct net_device *dev, int device, int port) diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index 0511fe2feff7..4863e3e398b6 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -593,15 +593,7 @@ static int dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev, if (!configured) return -EPROBE_DEFER; - /* - * 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(); - dev->dsa_ptr = dst->cpu_dp; - - return dsa_master_ethtool_setup(dst->cpu_dp->master); + return dsa_master_setup(dst->cpu_dp->master, dst->cpu_dp); } static int dsa_probe(struct platform_device *pdev) @@ -666,15 +658,7 @@ static void dsa_remove_dst(struct dsa_switch_tree *dst) { int i; - dsa_master_ethtool_restore(dst->cpu_dp->master); - - dst->cpu_dp->master->dsa_ptr = NULL; - - /* If we used a tagging format that doesn't have an ethertype - * field, make sure that all packets from this point get sent - * without the tag and go through the regular receive path. - */ - wmb(); + dsa_master_teardown(dst->cpu_dp->master); for (i = 0; i < dst->pd->nr_chips; i++) { struct dsa_switch *ds = dst->ds[i]; diff --git a/net/dsa/master.c b/net/dsa/master.c index 5f3f57e372e0..00589147f042 100644 --- a/net/dsa/master.c +++ b/net/dsa/master.c @@ -85,7 +85,7 @@ static void dsa_master_get_strings(struct net_device *dev, uint32_t stringset, } } -int dsa_master_ethtool_setup(struct net_device *dev) +static int dsa_master_ethtool_setup(struct net_device *dev) { struct dsa_port *cpu_dp = dev->dsa_ptr; struct dsa_switch *ds = cpu_dp->ds; @@ -108,10 +108,36 @@ int dsa_master_ethtool_setup(struct net_device *dev) return 0; } -void dsa_master_ethtool_restore(struct net_device *dev) +static void dsa_master_ethtool_teardown(struct net_device *dev) { struct dsa_port *cpu_dp = dev->dsa_ptr; dev->ethtool_ops = cpu_dp->orig_ethtool_ops; cpu_dp->orig_ethtool_ops = NULL; } + +int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp) +{ + /* 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(); + + dev->dsa_ptr = cpu_dp; + + return dsa_master_ethtool_setup(dev); +} + +void dsa_master_teardown(struct net_device *dev) +{ + dsa_master_ethtool_teardown(dev); + + dev->dsa_ptr = NULL; + + /* If we used a tagging format that doesn't have an ethertype + * field, make sure that all packets from this point get sent + * without the tag and go through the regular receive path. + */ + wmb(); +} -- cgit From ec15dd4269d0cbf947c9a2dfdcf08a917098fab1 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 6 Nov 2017 16:11:46 -0500 Subject: net: dsa: setup and teardown tree This commit provides better scope for the DSA tree setup and teardown functions. It renames the "applied" bool to "setup" and print a message when the tree is setup, as it is done during teardown. At the same time, check dst->setup in dsa_tree_setup, where it is set to true. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- include/net/dsa.h | 2 +- net/dsa/dsa2.c | 32 ++++++++++++++++++-------------- 2 files changed, 19 insertions(+), 15 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/include/net/dsa.h b/include/net/dsa.h index 2a8613b5a23d..6c239257309b 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -122,7 +122,7 @@ struct dsa_switch_tree { struct kref refcount; /* Has this tree been applied to the hardware? */ - bool applied; + bool setup; /* * Configuration data for the platform device that owns diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index c9b50339fcac..1a8df0a177b5 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -486,12 +486,18 @@ static void dsa_tree_teardown_master(struct dsa_switch_tree *dst) return dsa_master_teardown(master); } -static int dsa_dst_apply(struct dsa_switch_tree *dst) +static int dsa_tree_setup(struct dsa_switch_tree *dst) { struct dsa_switch *ds; u32 index; int err; + if (dst->setup) { + pr_err("DSA: tree %d already setup! Disjoint trees?\n", + dst->index); + return -EEXIST; + } + err = dsa_tree_setup_default_cpu(dst); if (err) return err; @@ -510,17 +516,19 @@ static int dsa_dst_apply(struct dsa_switch_tree *dst) if (err) return err; - dst->applied = true; + dst->setup = true; + + pr_info("DSA: tree %d setup\n", dst->index); return 0; } -static void dsa_dst_unapply(struct dsa_switch_tree *dst) +static void dsa_tree_teardown(struct dsa_switch_tree *dst) { struct dsa_switch *ds; u32 index; - if (!dst->applied) + if (!dst->setup) return; dsa_tree_teardown_master(dst); @@ -535,8 +543,9 @@ static void dsa_dst_unapply(struct dsa_switch_tree *dst) dsa_tree_teardown_default_cpu(dst); - pr_info("DSA: tree %d unapplied\n", dst->index); - dst->applied = false; + pr_info("DSA: tree %d torn down\n", dst->index); + + dst->setup = false; } static void dsa_tree_remove_switch(struct dsa_switch_tree *dst, @@ -794,14 +803,9 @@ static int _dsa_register_switch(struct dsa_switch *ds) if (err == 1) return 0; - if (dst->applied) { - pr_info("DSA: Disjoint trees?\n"); - return -EINVAL; - } - - err = dsa_dst_apply(dst); + err = dsa_tree_setup(dst); if (err) { - dsa_dst_unapply(dst); + dsa_tree_teardown(dst); goto out_del_dst; } @@ -852,7 +856,7 @@ static void _dsa_unregister_switch(struct dsa_switch *ds) struct dsa_switch_tree *dst = ds->dst; unsigned int index = ds->index; - dsa_dst_unapply(dst); + dsa_tree_teardown(dst); dsa_tree_remove_switch(dst, index); } -- cgit From 1f08f9e9cbc63d92c246f40ae4221cba86ef8ec6 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 6 Nov 2017 16:11:47 -0500 Subject: net: dsa: setup and teardown switches This patches brings no functional changes. It removes the unused dst argument from the dsa_ds_apply and dsa_ds_unapply functions, rename them to dsa_switch_setup and dsa_switch_teardown for a more explicit scope. This clarifies the steps of the setup or teardown of a switch fabric. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 62 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 23 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 1a8df0a177b5..2b3b2a86791d 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -362,7 +362,7 @@ static void dsa_user_port_unapply(struct dsa_port *port) } } -static int dsa_ds_apply(struct dsa_switch_tree *dst, struct dsa_switch *ds) +static int dsa_switch_setup(struct dsa_switch *ds) { struct dsa_port *port; u32 index; @@ -433,7 +433,7 @@ static int dsa_ds_apply(struct dsa_switch_tree *dst, struct dsa_switch *ds) return 0; } -static void dsa_ds_unapply(struct dsa_switch_tree *dst, struct dsa_switch *ds) +static void dsa_switch_teardown(struct dsa_switch *ds) { struct dsa_port *port; u32 index; @@ -469,6 +469,39 @@ static void dsa_ds_unapply(struct dsa_switch_tree *dst, struct dsa_switch *ds) } +static int dsa_tree_setup_switches(struct dsa_switch_tree *dst) +{ + struct dsa_switch *ds; + int device; + int err; + + for (device = 0; device < DSA_MAX_SWITCHES; device++) { + ds = dst->ds[device]; + if (!ds) + continue; + + err = dsa_switch_setup(ds); + if (err) + return err; + } + + return 0; +} + +static void dsa_tree_teardown_switches(struct dsa_switch_tree *dst) +{ + struct dsa_switch *ds; + int device; + + for (device = 0; device < DSA_MAX_SWITCHES; device++) { + ds = dst->ds[device]; + if (!ds) + continue; + + dsa_switch_teardown(ds); + } +} + static int dsa_tree_setup_master(struct dsa_switch_tree *dst) { struct dsa_port *cpu_dp = dst->cpu_dp; @@ -488,8 +521,6 @@ static void dsa_tree_teardown_master(struct dsa_switch_tree *dst) static int dsa_tree_setup(struct dsa_switch_tree *dst) { - struct dsa_switch *ds; - u32 index; int err; if (dst->setup) { @@ -502,15 +533,9 @@ static int dsa_tree_setup(struct dsa_switch_tree *dst) if (err) return err; - for (index = 0; index < DSA_MAX_SWITCHES; index++) { - ds = dst->ds[index]; - if (!ds) - continue; - - err = dsa_ds_apply(dst, ds); - if (err) - return err; - } + err = dsa_tree_setup_switches(dst); + if (err) + return err; err = dsa_tree_setup_master(dst); if (err) @@ -525,21 +550,12 @@ static int dsa_tree_setup(struct dsa_switch_tree *dst) static void dsa_tree_teardown(struct dsa_switch_tree *dst) { - struct dsa_switch *ds; - u32 index; - if (!dst->setup) return; dsa_tree_teardown_master(dst); - for (index = 0; index < DSA_MAX_SWITCHES; index++) { - ds = dst->ds[index]; - if (!ds) - continue; - - dsa_ds_unapply(dst, ds); - } + dsa_tree_teardown_switches(dst); dsa_tree_teardown_default_cpu(dst); -- cgit From 1d27732f411d57f0168af30be2adb504b8b7749d Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 6 Nov 2017 16:11:48 -0500 Subject: net: dsa: setup and teardown ports The dsa_dsa_port_apply and dsa_cpu_port_apply functions do exactly the same. The dsa_user_port_apply function does not try to register a fixed link but try to create a slave. This commit factorizes and scopes all that in two convenient dsa_port_setup and dsa_port_teardown functions. It won't hurt to register a devlink_port for unused port as well. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 173 ++++++++++++++++++++------------------------------------- 1 file changed, 59 insertions(+), 114 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 2b3b2a86791d..676c0bc943dd 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -281,91 +281,65 @@ static void dsa_tree_teardown_default_cpu(struct dsa_switch_tree *dst) dst->cpu_dp = NULL; } -static int dsa_dsa_port_apply(struct dsa_port *port) +static int dsa_port_setup(struct dsa_port *dp) { - struct dsa_switch *ds = port->ds; + struct dsa_switch *ds = dp->ds; int err; - err = dsa_port_fixed_link_register_of(port); - if (err) { - dev_warn(ds->dev, "Failed to setup dsa port %d: %d\n", - port->index, err); - return err; - } - - memset(&port->devlink_port, 0, sizeof(port->devlink_port)); - - return devlink_port_register(ds->devlink, &port->devlink_port, - port->index); -} - -static void dsa_dsa_port_unapply(struct dsa_port *port) -{ - devlink_port_unregister(&port->devlink_port); - dsa_port_fixed_link_unregister_of(port); -} - -static int dsa_cpu_port_apply(struct dsa_port *port) -{ - struct dsa_switch *ds = port->ds; - int err; + memset(&dp->devlink_port, 0, sizeof(dp->devlink_port)); - err = dsa_port_fixed_link_register_of(port); - if (err) { - dev_warn(ds->dev, "Failed to setup cpu port %d: %d\n", - port->index, err); + err = devlink_port_register(ds->devlink, &dp->devlink_port, dp->index); + if (err) return err; - } - - memset(&port->devlink_port, 0, sizeof(port->devlink_port)); - err = devlink_port_register(ds->devlink, &port->devlink_port, - port->index); - return err; -} - -static void dsa_cpu_port_unapply(struct dsa_port *port) -{ - devlink_port_unregister(&port->devlink_port); - dsa_port_fixed_link_unregister_of(port); -} -static int dsa_user_port_apply(struct dsa_port *port) -{ - struct dsa_switch *ds = port->ds; - int err; + switch (dp->type) { + case DSA_PORT_TYPE_UNUSED: + break; + case DSA_PORT_TYPE_CPU: + case DSA_PORT_TYPE_DSA: + err = dsa_port_fixed_link_register_of(dp); + if (err) { + dev_err(ds->dev, "failed to register fixed link for port %d.%d\n", + ds->index, dp->index); + return err; + } - err = dsa_slave_create(port); - if (err) { - dev_warn(ds->dev, "Failed to create slave %d: %d\n", - port->index, err); - port->slave = NULL; - return err; + break; + case DSA_PORT_TYPE_USER: + err = dsa_slave_create(dp); + if (err) + dev_err(ds->dev, "failed to create slave for port %d.%d\n", + ds->index, dp->index); + else + devlink_port_type_eth_set(&dp->devlink_port, dp->slave); + break; } - memset(&port->devlink_port, 0, sizeof(port->devlink_port)); - err = devlink_port_register(ds->devlink, &port->devlink_port, - port->index); - if (err) - return err; - - devlink_port_type_eth_set(&port->devlink_port, port->slave); - return 0; } -static void dsa_user_port_unapply(struct dsa_port *port) +static void dsa_port_teardown(struct dsa_port *dp) { - devlink_port_unregister(&port->devlink_port); - if (port->slave) { - dsa_slave_destroy(port->slave); - port->slave = NULL; + devlink_port_unregister(&dp->devlink_port); + + switch (dp->type) { + case DSA_PORT_TYPE_UNUSED: + break; + case DSA_PORT_TYPE_CPU: + case DSA_PORT_TYPE_DSA: + dsa_port_fixed_link_unregister_of(dp); + break; + case DSA_PORT_TYPE_USER: + if (dp->slave) { + dsa_slave_destroy(dp->slave); + dp->slave = NULL; + } + break; } } static int dsa_switch_setup(struct dsa_switch *ds) { - struct dsa_port *port; - u32 index; int err; /* Initialize ds->phys_mii_mask before registering the slave MDIO bus @@ -406,56 +380,11 @@ static int dsa_switch_setup(struct dsa_switch *ds) return err; } - for (index = 0; index < ds->num_ports; index++) { - port = &ds->ports[index]; - if (!dsa_port_is_valid(port)) - continue; - - if (dsa_port_is_dsa(port)) { - err = dsa_dsa_port_apply(port); - if (err) - return err; - continue; - } - - if (dsa_port_is_cpu(port)) { - err = dsa_cpu_port_apply(port); - if (err) - return err; - continue; - } - - err = dsa_user_port_apply(port); - if (err) - continue; - } - return 0; } static void dsa_switch_teardown(struct dsa_switch *ds) { - struct dsa_port *port; - u32 index; - - for (index = 0; index < ds->num_ports; index++) { - port = &ds->ports[index]; - if (!dsa_port_is_valid(port)) - continue; - - if (dsa_port_is_dsa(port)) { - dsa_dsa_port_unapply(port); - continue; - } - - if (dsa_port_is_cpu(port)) { - dsa_cpu_port_unapply(port); - continue; - } - - dsa_user_port_unapply(port); - } - if (ds->slave_mii_bus && ds->ops->phy_read) mdiobus_unregister(ds->slave_mii_bus); @@ -472,7 +401,8 @@ static void dsa_switch_teardown(struct dsa_switch *ds) static int dsa_tree_setup_switches(struct dsa_switch_tree *dst) { struct dsa_switch *ds; - int device; + struct dsa_port *dp; + int device, port; int err; for (device = 0; device < DSA_MAX_SWITCHES; device++) { @@ -483,6 +413,14 @@ static int dsa_tree_setup_switches(struct dsa_switch_tree *dst) err = dsa_switch_setup(ds); if (err) return err; + + for (port = 0; port < ds->num_ports; port++) { + dp = &ds->ports[port]; + + err = dsa_port_setup(dp); + if (err) + return err; + } } return 0; @@ -491,13 +429,20 @@ static int dsa_tree_setup_switches(struct dsa_switch_tree *dst) static void dsa_tree_teardown_switches(struct dsa_switch_tree *dst) { struct dsa_switch *ds; - int device; + struct dsa_port *dp; + int device, port; for (device = 0; device < DSA_MAX_SWITCHES; device++) { ds = dst->ds[device]; if (!ds) continue; + for (port = 0; port < ds->num_ports; port++) { + dp = &ds->ports[port]; + + dsa_port_teardown(dp); + } + dsa_switch_teardown(ds); } } -- cgit From f163da8853aa9d8060157a96ed314299b87ba070 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 6 Nov 2017 16:11:49 -0500 Subject: net: dsa: add find port by node helper Instead of having two dsa_ds_find_port_dn (which returns a bool) and dsa_dst_find_port_dn (which returns a switch) functions, provide a more explicit dsa_tree_find_port_by_node function which returns a matching port. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 676c0bc943dd..0f6f8c1701f9 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -117,30 +117,24 @@ static bool dsa_port_is_user(struct dsa_port *dp) return dp->type == DSA_PORT_TYPE_USER; } -static bool dsa_ds_find_port_dn(struct dsa_switch *ds, - struct device_node *port) -{ - u32 index; - - for (index = 0; index < ds->num_ports; index++) - if (ds->ports[index].dn == port) - return true; - return false; -} - -static struct dsa_switch *dsa_dst_find_port_dn(struct dsa_switch_tree *dst, - struct device_node *port) +static struct dsa_port *dsa_tree_find_port_by_node(struct dsa_switch_tree *dst, + struct device_node *dn) { struct dsa_switch *ds; - u32 index; + struct dsa_port *dp; + int device, port; - for (index = 0; index < DSA_MAX_SWITCHES; index++) { - ds = dst->ds[index]; + for (device = 0; device < DSA_MAX_SWITCHES; device++) { + ds = dst->ds[device]; if (!ds) continue; - if (dsa_ds_find_port_dn(ds, port)) - return ds; + for (port = 0; port < ds->num_ports; port++) { + dp = &ds->ports[port]; + + if (dp->dn == dn) + return dp; + } } return NULL; @@ -154,18 +148,21 @@ static int dsa_port_complete(struct dsa_switch_tree *dst, struct device_node *link; int index; struct dsa_switch *dst_ds; + struct dsa_port *link_dp; for (index = 0;; index++) { link = of_parse_phandle(port->dn, "link", index); if (!link) break; - dst_ds = dsa_dst_find_port_dn(dst, link); + link_dp = dsa_tree_find_port_by_node(dst, link); of_node_put(link); - if (!dst_ds) + if (!link_dp) return 1; + dst_ds = link_dp->ds; + src_ds->rtable[dst_ds->index] = src_port; } -- cgit From c52866655558e5fc87ceae8aac528a7e410c8a77 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 6 Nov 2017 16:11:50 -0500 Subject: net: dsa: use of_for_each_phandle The OF code provides a of_for_each_phandle() helper to iterate over phandles. Use it instead of arbitrary iterating ourselves over the list of phandles hanging to the "link" property of the port's device node. The of_phandle_iterator_next() helper calls of_node_put() itself on it.node. Thus We must only do it ourselves if we break the loop. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 0f6f8c1701f9..25ed41262ead 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -145,21 +145,18 @@ static int dsa_port_complete(struct dsa_switch_tree *dst, struct dsa_port *port, u32 src_port) { - struct device_node *link; - int index; + struct device_node *dn = port->dn; + struct of_phandle_iterator it; struct dsa_switch *dst_ds; struct dsa_port *link_dp; + int err; - for (index = 0;; index++) { - link = of_parse_phandle(port->dn, "link", index); - if (!link) - break; - - link_dp = dsa_tree_find_port_by_node(dst, link); - of_node_put(link); - - if (!link_dp) + of_for_each_phandle(&it, err, dn, "link", NULL, 0) { + link_dp = dsa_tree_find_port_by_node(dst, it.node); + if (!link_dp) { + of_node_put(it.node); return 1; + } dst_ds = link_dp->ds; -- cgit From 34c09a8916fb52aac948dfc861b33c0b3b37ac29 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 6 Nov 2017 16:11:51 -0500 Subject: net: dsa: setup routing table The *_complete() functions take too much arguments to do only one thing: they try to fetch the dsa_port structures corresponding to device nodes under the "link" list property of DSA ports, and use them to setup the routing table of switches. This patch simplifies them by providing instead simpler dsa_{port,switch,tree}_setup_routing_table functions which return a boolean value, true if the tree is complete. dsa_tree_setup_routing_table is called inside dsa_tree_setup which simplifies the switch registering function as well. A switch's routing table is now initialized before its setup. This also makes dsa_port_is_valid obsolete, remove it. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 108 ++++++++++++++++++++------------------------------------- 1 file changed, 37 insertions(+), 71 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 25ed41262ead..44d26b5977cd 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -94,14 +94,6 @@ static void dsa_tree_put(struct dsa_switch_tree *dst) kref_put(&dst->refcount, dsa_tree_release); } -/* For platform data configurations, we need to have a valid name argument to - * differentiate a disabled port from an enabled one - */ -static bool dsa_port_is_valid(struct dsa_port *port) -{ - return port->type != DSA_PORT_TYPE_UNUSED; -} - static bool dsa_port_is_dsa(struct dsa_port *port) { return port->type == DSA_PORT_TYPE_DSA; @@ -140,14 +132,12 @@ static struct dsa_port *dsa_tree_find_port_by_node(struct dsa_switch_tree *dst, return NULL; } -static int dsa_port_complete(struct dsa_switch_tree *dst, - struct dsa_switch *src_ds, - struct dsa_port *port, - u32 src_port) +static bool dsa_port_setup_routing_table(struct dsa_port *dp) { - struct device_node *dn = port->dn; + struct dsa_switch *ds = dp->ds; + struct dsa_switch_tree *dst = ds->dst; + struct device_node *dn = dp->dn; struct of_phandle_iterator it; - struct dsa_switch *dst_ds; struct dsa_port *link_dp; int err; @@ -155,66 +145,54 @@ static int dsa_port_complete(struct dsa_switch_tree *dst, link_dp = dsa_tree_find_port_by_node(dst, it.node); if (!link_dp) { of_node_put(it.node); - return 1; + return false; } - dst_ds = link_dp->ds; - - src_ds->rtable[dst_ds->index] = src_port; + ds->rtable[link_dp->ds->index] = dp->index; } - return 0; + return true; } -/* A switch is complete if all the DSA ports phandles point to ports - * known in the tree. A return value of 1 means the tree is not - * complete. This is not an error condition. A value of 0 is - * success. - */ -static int dsa_ds_complete(struct dsa_switch_tree *dst, struct dsa_switch *ds) +static bool dsa_switch_setup_routing_table(struct dsa_switch *ds) { - struct dsa_port *port; - u32 index; - int err; + bool complete = true; + struct dsa_port *dp; + int i; - for (index = 0; index < ds->num_ports; index++) { - port = &ds->ports[index]; - if (!dsa_port_is_valid(port)) - continue; + for (i = 0; i < DSA_MAX_SWITCHES; i++) + ds->rtable[i] = DSA_RTABLE_NONE; - if (!dsa_port_is_dsa(port)) - continue; + for (i = 0; i < ds->num_ports; i++) { + dp = &ds->ports[i]; - err = dsa_port_complete(dst, ds, port, index); - if (err != 0) - return err; + if (dsa_port_is_dsa(dp)) { + complete = dsa_port_setup_routing_table(dp); + if (!complete) + break; + } } - return 0; + return complete; } -/* A tree is complete if all the DSA ports phandles point to ports - * known in the tree. A return value of 1 means the tree is not - * complete. This is not an error condition. A value of 0 is - * success. - */ -static int dsa_dst_complete(struct dsa_switch_tree *dst) +static bool dsa_tree_setup_routing_table(struct dsa_switch_tree *dst) { struct dsa_switch *ds; - u32 index; - int err; + bool complete = true; + int device; - for (index = 0; index < DSA_MAX_SWITCHES; index++) { - ds = dst->ds[index]; + for (device = 0; device < DSA_MAX_SWITCHES; device++) { + ds = dst->ds[device]; if (!ds) continue; - err = dsa_ds_complete(dst, ds); - if (err != 0) - return err; + complete = dsa_switch_setup_routing_table(ds); + if (!complete) + break; } - return 0; + return complete; } static struct dsa_port *dsa_tree_find_first_cpu(struct dsa_switch_tree *dst) @@ -460,6 +438,7 @@ static void dsa_tree_teardown_master(struct dsa_switch_tree *dst) static int dsa_tree_setup(struct dsa_switch_tree *dst) { + bool complete; int err; if (dst->setup) { @@ -468,6 +447,10 @@ static int dsa_tree_setup(struct dsa_switch_tree *dst) return -EEXIST; } + complete = dsa_tree_setup_routing_table(dst); + if (!complete) + return 0; + err = dsa_tree_setup_default_cpu(dst); if (err) return err; @@ -727,7 +710,7 @@ static int _dsa_register_switch(struct dsa_switch *ds) struct device_node *np = ds->dev->of_node; struct dsa_switch_tree *dst; unsigned int index; - int i, err; + int err; if (np) err = dsa_switch_parse_of(ds, np); @@ -742,33 +725,16 @@ static int _dsa_register_switch(struct dsa_switch *ds) index = ds->index; dst = ds->dst; - /* Initialize the routing table */ - for (i = 0; i < DSA_MAX_SWITCHES; ++i) - ds->rtable[i] = DSA_RTABLE_NONE; - err = dsa_tree_add_switch(dst, ds); if (err) return err; - err = dsa_dst_complete(dst); - if (err < 0) - goto out_del_dst; - - /* Not all switches registered yet */ - if (err == 1) - return 0; - err = dsa_tree_setup(dst); if (err) { dsa_tree_teardown(dst); - goto out_del_dst; + dsa_tree_remove_switch(dst, index); } - return 0; - -out_del_dst: - dsa_tree_remove_switch(dst, index); - return err; } -- cgit From 308173546ac4342103541e8d4e4ce83d1a5e7eba Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 6 Nov 2017 16:11:52 -0500 Subject: net: dsa: setup a tree when adding a switch to it Now that the tree setup is centralized, we can simplify the code a bit more by setting up or tearing down the tree directly when adding or removing a switch to/from it. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 44d26b5977cd..3db50e68640e 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -489,6 +489,8 @@ static void dsa_tree_teardown(struct dsa_switch_tree *dst) static void dsa_tree_remove_switch(struct dsa_switch_tree *dst, unsigned int index) { + dsa_tree_teardown(dst); + dst->ds[index] = NULL; dsa_tree_put(dst); } @@ -497,6 +499,7 @@ static int dsa_tree_add_switch(struct dsa_switch_tree *dst, struct dsa_switch *ds) { unsigned int index = ds->index; + int err; if (dst->ds[index]) return -EBUSY; @@ -504,7 +507,11 @@ static int dsa_tree_add_switch(struct dsa_switch_tree *dst, dsa_tree_get(dst); dst->ds[index] = ds; - return 0; + err = dsa_tree_setup(dst); + if (err) + dsa_tree_remove_switch(dst, index); + + return err; } static int dsa_port_parse_user(struct dsa_port *dp, const char *name) @@ -704,12 +711,17 @@ static int dsa_switch_parse(struct dsa_switch *ds, struct dsa_chip_data *cd) return dsa_switch_parse_ports(ds, cd); } +static int dsa_switch_add(struct dsa_switch *ds) +{ + struct dsa_switch_tree *dst = ds->dst; + + return dsa_tree_add_switch(dst, ds); +} + static int _dsa_register_switch(struct dsa_switch *ds) { struct dsa_chip_data *pdata = ds->dev->platform_data; struct device_node *np = ds->dev->of_node; - struct dsa_switch_tree *dst; - unsigned int index; int err; if (np) @@ -722,20 +734,7 @@ static int _dsa_register_switch(struct dsa_switch *ds) if (err) return err; - index = ds->index; - dst = ds->dst; - - err = dsa_tree_add_switch(dst, ds); - if (err) - return err; - - err = dsa_tree_setup(dst); - if (err) { - dsa_tree_teardown(dst); - dsa_tree_remove_switch(dst, index); - } - - return err; + return dsa_switch_add(ds); } struct dsa_switch *dsa_switch_alloc(struct device *dev, size_t n) @@ -777,8 +776,6 @@ static void _dsa_unregister_switch(struct dsa_switch *ds) struct dsa_switch_tree *dst = ds->dst; unsigned int index = ds->index; - dsa_tree_teardown(dst); - dsa_tree_remove_switch(dst, index); } -- cgit From b4fbb347fe4cd7988d0f9453a7e3ab0cd1b4a75a Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 6 Nov 2017 16:11:53 -0500 Subject: net: dsa: rename probe and remove switch functions This commit brings no functional changes. It gets rid of the underscore prefixed _dsa_register_switch and _dsa_unregister_switch functions in favor of dsa_switch_probe() which parses and adds a switch to a tree and dsa_switch_remove() which removes a switch from a tree. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 3db50e68640e..fd54a8e17986 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -718,7 +718,7 @@ static int dsa_switch_add(struct dsa_switch *ds) return dsa_tree_add_switch(dst, ds); } -static int _dsa_register_switch(struct dsa_switch *ds) +static int dsa_switch_probe(struct dsa_switch *ds) { struct dsa_chip_data *pdata = ds->dev->platform_data; struct device_node *np = ds->dev->of_node; @@ -764,14 +764,14 @@ int dsa_register_switch(struct dsa_switch *ds) int err; mutex_lock(&dsa2_mutex); - err = _dsa_register_switch(ds); + err = dsa_switch_probe(ds); mutex_unlock(&dsa2_mutex); return err; } EXPORT_SYMBOL_GPL(dsa_register_switch); -static void _dsa_unregister_switch(struct dsa_switch *ds) +static void dsa_switch_remove(struct dsa_switch *ds) { struct dsa_switch_tree *dst = ds->dst; unsigned int index = ds->index; @@ -782,7 +782,7 @@ static void _dsa_unregister_switch(struct dsa_switch *ds) void dsa_unregister_switch(struct dsa_switch *ds) { mutex_lock(&dsa2_mutex); - _dsa_unregister_switch(ds); + dsa_switch_remove(ds); mutex_unlock(&dsa2_mutex); } EXPORT_SYMBOL_GPL(dsa_unregister_switch); -- cgit From 5ed4e3eb021762fee584ce65620bc822131c7aa0 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Fri, 10 Nov 2017 15:22:52 -0800 Subject: net: dsa: Pass a port to get_tag_protocol() A number of drivers want to check whether the configured CPU port is a possible configuration for enabling tagging, pass down the CPU port number so they verify that. Signed-off-by: Florian Fainelli Reviewed-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/b53/b53_common.c | 32 +++++++++++++------------------- drivers/net/dsa/bcm_sf2.c | 3 ++- drivers/net/dsa/dsa_loop.c | 3 ++- drivers/net/dsa/lan9303-core.c | 3 ++- drivers/net/dsa/microchip/ksz_common.c | 3 ++- drivers/net/dsa/mt7530.c | 4 ++-- drivers/net/dsa/mv88e6060.c | 3 ++- drivers/net/dsa/mv88e6xxx/chip.c | 3 ++- drivers/net/dsa/qca8k.c | 2 +- include/net/dsa.h | 3 ++- net/dsa/dsa2.c | 2 +- net/dsa/legacy.c | 2 +- 12 files changed, 32 insertions(+), 31 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 44a9a03bff55..f72aeb9ed12a 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -541,7 +541,8 @@ EXPORT_SYMBOL(b53_disable_port); void b53_brcm_hdr_setup(struct dsa_switch *ds, int port) { - bool tag_en = !!(ds->ops->get_tag_protocol(ds) == DSA_TAG_PROTO_BRCM); + bool tag_en = !!(ds->ops->get_tag_protocol(ds, port) == + DSA_TAG_PROTO_BRCM); struct b53_device *dev = ds->priv; u8 hdr_ctl, val; u16 reg; @@ -1478,38 +1479,31 @@ void b53_br_fast_age(struct dsa_switch *ds, int port) } EXPORT_SYMBOL(b53_br_fast_age); -static bool b53_can_enable_brcm_tags(struct dsa_switch *ds) +static bool b53_can_enable_brcm_tags(struct dsa_switch *ds, int port) { - unsigned int brcm_tag_mask; - unsigned int i; - /* Broadcom switches will accept enabling Broadcom tags on the * following ports: 5, 7 and 8, any other port is not supported */ - brcm_tag_mask = BIT(B53_CPU_PORT_25) | BIT(7) | BIT(B53_CPU_PORT); - - for (i = 0; i < ds->num_ports; i++) { - if (dsa_is_cpu_port(ds, i)) { - if (!(BIT(i) & brcm_tag_mask)) { - dev_warn(ds->dev, - "Port %d is not Broadcom tag capable\n", - i); - return false; - } - } + switch (port) { + case B53_CPU_PORT_25: + case 7: + case B53_CPU_PORT: + return true; } - return true; + dev_warn(ds->dev, "Port %d is not Broadcom tag capable\n", port); + return false; } -static enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds) +static enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, + int port) { struct b53_device *dev = ds->priv; /* Older models support a different tag format that we do not * support in net/dsa/tag_brcm.c yet. */ - if (is5325(dev) || is5365(dev) || !b53_can_enable_brcm_tags(ds)) + if (is5325(dev) || is5365(dev) || !b53_can_enable_brcm_tags(ds, port)) return DSA_TAG_PROTO_NONE; else return DSA_TAG_PROTO_BRCM; diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 2d6867f4008c..93faa1fed6f2 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -35,7 +35,8 @@ #include "b53/b53_priv.h" #include "b53/b53_regs.h" -static enum dsa_tag_protocol bcm_sf2_sw_get_tag_protocol(struct dsa_switch *ds) +static enum dsa_tag_protocol bcm_sf2_sw_get_tag_protocol(struct dsa_switch *ds, + int port) { return DSA_TAG_PROTO_BRCM; } diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c index 3a3f4f7ba364..bb71d3d6f65b 100644 --- a/drivers/net/dsa/dsa_loop.c +++ b/drivers/net/dsa/dsa_loop.c @@ -64,7 +64,8 @@ struct dsa_loop_priv { static struct phy_device *phydevs[PHY_MAX_ADDR]; -static enum dsa_tag_protocol dsa_loop_get_protocol(struct dsa_switch *ds) +static enum dsa_tag_protocol dsa_loop_get_protocol(struct dsa_switch *ds, + int port) { dev_dbg(ds->dev, "%s\n", __func__); diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index a2610085e7ba..fdfdb0edfe62 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -894,7 +894,8 @@ static int lan9303_check_device(struct lan9303 *chip) /* ---------------------------- DSA -----------------------------------*/ -static enum dsa_tag_protocol lan9303_get_tag_protocol(struct dsa_switch *ds) +static enum dsa_tag_protocol lan9303_get_tag_protocol(struct dsa_switch *ds, + int port) { return DSA_TAG_PROTO_LAN9303; } diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 56cd6d365352..b5be93a1e0df 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -394,7 +394,8 @@ static int ksz_setup(struct dsa_switch *ds) return 0; } -static enum dsa_tag_protocol ksz_get_tag_protocol(struct dsa_switch *ds) +static enum dsa_tag_protocol ksz_get_tag_protocol(struct dsa_switch *ds, + int port) { return DSA_TAG_PROTO_KSZ; } diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 627c039f12ca..2820d69810b3 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -907,11 +907,11 @@ err: } static enum dsa_tag_protocol -mtk_get_tag_protocol(struct dsa_switch *ds) +mtk_get_tag_protocol(struct dsa_switch *ds, int port) { struct mt7530_priv *priv = ds->priv; - if (!dsa_is_cpu_port(ds, MT7530_CPU_PORT)) { + if (port != MT7530_CPU_PORT) { dev_warn(priv->dev, "port not matched with tagging CPU port\n"); return DSA_TAG_PROTO_NONE; diff --git a/drivers/net/dsa/mv88e6060.c b/drivers/net/dsa/mv88e6060.c index 45768e3c5bc5..65f10fec25b3 100644 --- a/drivers/net/dsa/mv88e6060.c +++ b/drivers/net/dsa/mv88e6060.c @@ -70,7 +70,8 @@ static const char *mv88e6060_get_name(struct mii_bus *bus, int sw_addr) return NULL; } -static enum dsa_tag_protocol mv88e6060_get_tag_protocol(struct dsa_switch *ds) +static enum dsa_tag_protocol mv88e6060_get_tag_protocol(struct dsa_switch *ds, + int port) { return DSA_TAG_PROTO_TRAILER; } diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index d6c3a22c8789..8171055fde7a 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3731,7 +3731,8 @@ static int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip, return 0; } -static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds) +static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds, + int port) { struct mv88e6xxx_chip *chip = ds->priv; diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index cf72e274275f..9df22ebee822 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -823,7 +823,7 @@ qca8k_port_fdb_dump(struct dsa_switch *ds, int port, } static enum dsa_tag_protocol -qca8k_get_tag_protocol(struct dsa_switch *ds) +qca8k_get_tag_protocol(struct dsa_switch *ds, int port) { return DSA_TAG_PROTO_QCA; } diff --git a/include/net/dsa.h b/include/net/dsa.h index 6c239257309b..68e232fd4b0f 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -321,7 +321,8 @@ struct dsa_switch_ops { struct device *host_dev, int sw_addr, void **priv); - enum dsa_tag_protocol (*get_tag_protocol)(struct dsa_switch *ds); + enum dsa_tag_protocol (*get_tag_protocol)(struct dsa_switch *ds, + int port); int (*setup)(struct dsa_switch *ds); u32 (*get_phy_flags)(struct dsa_switch *ds, int port); diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index fd54a8e17986..44e3fb7dec8c 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -539,7 +539,7 @@ static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master) const struct dsa_device_ops *tag_ops; enum dsa_tag_protocol tag_protocol; - tag_protocol = ds->ops->get_tag_protocol(ds); + tag_protocol = ds->ops->get_tag_protocol(ds, dp->index); tag_ops = dsa_resolve_tag_protocol(tag_protocol); if (IS_ERR(tag_ops)) { dev_warn(ds->dev, "No tagger for this switch\n"); diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index 4863e3e398b6..84611d7fcfa2 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -151,7 +151,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, const struct dsa_device_ops *tag_ops; enum dsa_tag_protocol tag_protocol; - tag_protocol = ops->get_tag_protocol(ds); + tag_protocol = ops->get_tag_protocol(ds, dst->cpu_dp->index); tag_ops = dsa_resolve_tag_protocol(tag_protocol); if (IS_ERR(tag_ops)) return PTR_ERR(tag_ops); -- cgit From 9e741045faea17e28663f14a45f7f3304827c968 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 24 Nov 2017 11:36:06 -0500 Subject: net: dsa: fix 'increment on 0' warning Setting the refcount to 0 when allocating a tree to match the number of switch devices it holds may cause an 'increment on 0; use-after-free', if CONFIG_REFCOUNT_FULL is enabled. To fix this, do not decrement the refcount of a newly allocated tree, increment it when an already allocated tree is found, and decrement it after the probing of a switch, as done with the previous behavior. At the same time, make dsa_tree_get and dsa_tree_put accept a NULL argument to simplify callers, and return the tree after incrementation, as most kref users like of_node_get and of_node_put do. Fixes: 8e5bf9759a06 ("net: dsa: simplify tree reference counting") Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Tested-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'net/dsa/dsa2.c') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 44e3fb7dec8c..1e287420ff49 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -51,9 +51,7 @@ static struct dsa_switch_tree *dsa_tree_alloc(int index) INIT_LIST_HEAD(&dst->list); list_add_tail(&dsa_tree_list, &dst->list); - /* Initialize the reference counter to the number of switches, not 1 */ kref_init(&dst->refcount); - refcount_set(&dst->refcount.refcount, 0); return dst; } @@ -64,20 +62,23 @@ static void dsa_tree_free(struct dsa_switch_tree *dst) kfree(dst); } -static struct dsa_switch_tree *dsa_tree_touch(int index) +static struct dsa_switch_tree *dsa_tree_get(struct dsa_switch_tree *dst) { - struct dsa_switch_tree *dst; - - dst = dsa_tree_find(index); - if (!dst) - dst = dsa_tree_alloc(index); + if (dst) + kref_get(&dst->refcount); return dst; } -static void dsa_tree_get(struct dsa_switch_tree *dst) +static struct dsa_switch_tree *dsa_tree_touch(int index) { - kref_get(&dst->refcount); + struct dsa_switch_tree *dst; + + dst = dsa_tree_find(index); + if (dst) + return dsa_tree_get(dst); + else + return dsa_tree_alloc(index); } static void dsa_tree_release(struct kref *ref) @@ -91,7 +92,8 @@ static void dsa_tree_release(struct kref *ref) static void dsa_tree_put(struct dsa_switch_tree *dst) { - kref_put(&dst->refcount, dsa_tree_release); + if (dst) + kref_put(&dst->refcount, dsa_tree_release); } static bool dsa_port_is_dsa(struct dsa_port *port) @@ -765,6 +767,7 @@ int dsa_register_switch(struct dsa_switch *ds) mutex_lock(&dsa2_mutex); err = dsa_switch_probe(ds); + dsa_tree_put(ds->dst); mutex_unlock(&dsa2_mutex); return err; -- cgit