* [PATCH net-next 2/9] net: phy: phylink: Release link GPIO
From: Florian Fainelli @ 2018-05-05 19:04 UTC (permalink / raw)
To: netdev
Cc: Florian Fainelli, privat, andrew, vivien.didelot, davem,
rmk+kernel, sean.wang, Woojung.Huh, john, cphealy
In-Reply-To: <20180505190425.14378-1-f.fainelli@gmail.com>
We are not releasing the link GPIO descriptor with gpiod_put() which results in
subsequent probing to get -EBUSY when calling fwnode_get_named_gpiod(). Fix this
by doing the release in phylink_destroy().
Fixes: 9525ae83959b ("phylink: add phylink infrastructure")
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/net/phy/phylink.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 412d1cf4fa66..6392b5248cf5 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -612,6 +612,8 @@ void phylink_destroy(struct phylink *pl)
{
if (pl->sfp_bus)
sfp_unregister_upstream(pl->sfp_bus);
+ if (!IS_ERR(pl->link_gpio))
+ gpiod_put(pl->link_gpio);
cancel_work_sync(&pl->resolve);
kfree(pl);
--
2.17.0
^ permalink raw reply related
* [PATCH net-next 3/9] net: phy: phylink: Poll link GPIOs
From: Florian Fainelli @ 2018-05-05 19:04 UTC (permalink / raw)
To: netdev
Cc: Russell King, Florian Fainelli, privat, andrew, vivien.didelot,
davem, sean.wang, Woojung.Huh, john, cphealy
In-Reply-To: <20180505190425.14378-1-f.fainelli@gmail.com>
From: Russell King <rmk+kernel@armlinux.org.uk>
When using a fixed link with a link GPIO, we need to poll that GPIO to
determine link state changes. This is consistent with what fixed_phy.c does.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/net/phy/phylink.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 6392b5248cf5..581ce93ecaf9 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -19,6 +19,7 @@
#include <linux/phylink.h>
#include <linux/rtnetlink.h>
#include <linux/spinlock.h>
+#include <linux/timer.h>
#include <linux/workqueue.h>
#include "sfp.h"
@@ -54,6 +55,7 @@ struct phylink {
/* The link configuration settings */
struct phylink_link_state link_config;
struct gpio_desc *link_gpio;
+ struct timer_list link_poll;
void (*get_fixed_state)(struct net_device *dev,
struct phylink_link_state *s);
@@ -500,6 +502,15 @@ static void phylink_run_resolve(struct phylink *pl)
queue_work(system_power_efficient_wq, &pl->resolve);
}
+static void phylink_fixed_poll(struct timer_list *t)
+{
+ struct phylink *pl = container_of(t, struct phylink, link_poll);
+
+ mod_timer(t, jiffies + HZ);
+
+ phylink_run_resolve(pl);
+}
+
static const struct sfp_upstream_ops sfp_phylink_ops;
static int phylink_register_sfp(struct phylink *pl,
@@ -572,6 +583,7 @@ struct phylink *phylink_create(struct net_device *ndev,
pl->link_config.an_enabled = true;
pl->ops = ops;
__set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
+ timer_setup(&pl->link_poll, phylink_fixed_poll, 0);
bitmap_fill(pl->supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
linkmode_copy(pl->link_config.advertising, pl->supported);
@@ -905,6 +917,8 @@ void phylink_start(struct phylink *pl)
clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
phylink_run_resolve(pl);
+ if (pl->link_an_mode == MLO_AN_FIXED && !IS_ERR(pl->link_gpio))
+ mod_timer(&pl->link_poll, jiffies + HZ);
if (pl->sfp_bus)
sfp_upstream_start(pl->sfp_bus);
if (pl->phydev)
@@ -929,6 +943,8 @@ void phylink_stop(struct phylink *pl)
phy_stop(pl->phydev);
if (pl->sfp_bus)
sfp_upstream_stop(pl->sfp_bus);
+ if (pl->link_an_mode == MLO_AN_FIXED && !IS_ERR(pl->link_gpio))
+ del_timer_sync(&pl->link_poll);
set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
queue_work(system_power_efficient_wq, &pl->resolve);
--
2.17.0
^ permalink raw reply related
* [PATCH net-next 4/9] net: dsa: Add PHYLINK switch operations
From: Florian Fainelli @ 2018-05-05 19:04 UTC (permalink / raw)
To: netdev
Cc: Florian Fainelli, privat, andrew, vivien.didelot, davem,
rmk+kernel, sean.wang, Woojung.Huh, john, cphealy
In-Reply-To: <20180505190425.14378-1-f.fainelli@gmail.com>
In preparation for adding support for PHYLINK within DSA, define a number of
operations that we will need and that switch drivers can start implementing.
Proper integration with PHYLINK will follow in subsequent patches.
We start selecting PHYLINK (which implies PHYLIB) in net/dsa/Kconfig
such that drivers can be guaranteed that this dependency is properly
taken care of and can start referencing PHYLINK helper functions without
requiring stubs or anything.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
include/net/dsa.h | 24 ++++++++++++++++++++++++
net/dsa/Kconfig | 2 +-
net/dsa/slave.c | 5 +++++
3 files changed, 30 insertions(+), 1 deletion(-)
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 462e9741b210..ed64c1f3f117 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -20,12 +20,14 @@
#include <linux/of.h>
#include <linux/ethtool.h>
#include <linux/net_tstamp.h>
+#include <linux/phy.h>
#include <net/devlink.h>
#include <net/switchdev.h>
struct tc_action;
struct phy_device;
struct fixed_phy_status;
+struct phylink_link_state;
enum dsa_tag_protocol {
DSA_TAG_PROTO_NONE = 0,
@@ -353,6 +355,27 @@ struct dsa_switch_ops {
void (*fixed_link_update)(struct dsa_switch *ds, int port,
struct fixed_phy_status *st);
+ /*
+ * PHYLINK integration
+ */
+ void (*phylink_validate)(struct dsa_switch *ds, int port,
+ unsigned long *supported,
+ struct phylink_link_state *state);
+ int (*phylink_mac_link_state)(struct dsa_switch *ds, int port,
+ struct phylink_link_state *state);
+ void (*phylink_mac_config)(struct dsa_switch *ds, int port,
+ unsigned int mode,
+ const struct phylink_link_state *state);
+ void (*phylink_mac_an_restart)(struct dsa_switch *ds, int port);
+ void (*phylink_mac_link_down)(struct dsa_switch *ds, int port,
+ unsigned int mode,
+ phy_interface_t interface);
+ void (*phylink_mac_link_up)(struct dsa_switch *ds, int port,
+ unsigned int mode,
+ phy_interface_t interface,
+ struct phy_device *phydev);
+ void (*phylink_fixed_state)(struct dsa_switch *ds, int port,
+ struct phylink_link_state *state);
/*
* ethtool hardware statistics.
*/
@@ -595,5 +618,6 @@ static inline int call_dsa_notifiers(unsigned long val, struct net_device *dev,
int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data);
int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data);
int dsa_port_get_phy_sset_count(struct dsa_port *dp);
+void dsa_port_phylink_mac_change(struct dsa_switch *ds, int port, bool up);
#endif
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index bbf2c82cf7b2..4183e4ba27a5 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -9,7 +9,7 @@ config NET_DSA
depends on HAVE_NET_DSA && MAY_USE_DEVLINK
depends on BRIDGE || BRIDGE=n
select NET_SWITCHDEV
- select PHYLIB
+ select PHYLINK
---help---
Say Y if you want to enable support for the hardware switches supported
by the Distributed Switch Architecture.
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index f3fb3a0880b1..e9be9458aa94 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -1119,6 +1119,11 @@ static int dsa_slave_phy_connect(struct net_device *slave_dev, int addr)
dsa_slave_adjust_link, p->phy_interface);
}
+void dsa_port_phylink_mac_change(struct dsa_switch *ds, int port, bool up)
+{
+}
+EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_change);
+
static int dsa_slave_phy_setup(struct net_device *slave_dev)
{
struct dsa_port *dp = dsa_slave_to_port(slave_dev);
--
2.17.0
^ permalink raw reply related
* [PATCH net-next 5/9] net: dsa: bcm_sf2: Implement phylink_mac_ops
From: Florian Fainelli @ 2018-05-05 19:04 UTC (permalink / raw)
To: netdev
Cc: Florian Fainelli, privat, andrew, vivien.didelot, davem,
rmk+kernel, sean.wang, Woojung.Huh, john, cphealy
In-Reply-To: <20180505190425.14378-1-f.fainelli@gmail.com>
Make the bcm_sf2 driver implement phylink_mac_ops since it needs to
support a wide variety of network interfaces: internal & external MDIO
PHYs, fixed PHYs, MoCA with MMIO link status.
A large amount of what needs to be done already exists under
bcm_sf2_sw_adjust_link() so we are essentially breaking this down into
the necessary operation for PHYLINK to work: mac_config, mac_link_up,
mac_link_down and validate. We can now entirely get rid of most of what
fixed_link_update() provided because only the link information is actually
necessary. We still have to force DUPLEX_FULL for legacy Device Tree bindings
that did not specify that before.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/net/dsa/bcm_sf2.c | 214 ++++++++++++++++++++++++++++++++++++--
1 file changed, 206 insertions(+), 8 deletions(-)
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
index 97236cfcbae4..a20608b0329e 100644
--- a/drivers/net/dsa/bcm_sf2.c
+++ b/drivers/net/dsa/bcm_sf2.c
@@ -16,6 +16,7 @@
#include <linux/platform_device.h>
#include <linux/phy.h>
#include <linux/phy_fixed.h>
+#include <linux/phylink.h>
#include <linux/mii.h>
#include <linux/of.h>
#include <linux/of_irq.h>
@@ -306,7 +307,8 @@ static int bcm_sf2_sw_mdio_write(struct mii_bus *bus, int addr, int regnum,
static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id)
{
- struct bcm_sf2_priv *priv = dev_id;
+ struct dsa_switch *ds = dev_id;
+ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
priv->irq0_stat = intrl2_0_readl(priv, INTRL2_CPU_STATUS) &
~priv->irq0_mask;
@@ -317,16 +319,21 @@ static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id)
static irqreturn_t bcm_sf2_switch_1_isr(int irq, void *dev_id)
{
- struct bcm_sf2_priv *priv = dev_id;
+ struct dsa_switch *ds = dev_id;
+ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
priv->irq1_stat = intrl2_1_readl(priv, INTRL2_CPU_STATUS) &
~priv->irq1_mask;
intrl2_1_writel(priv, priv->irq1_stat, INTRL2_CPU_CLEAR);
- if (priv->irq1_stat & P_LINK_UP_IRQ(P7_IRQ_OFF))
- priv->port_sts[7].link = 1;
- if (priv->irq1_stat & P_LINK_DOWN_IRQ(P7_IRQ_OFF))
- priv->port_sts[7].link = 0;
+ if (priv->irq1_stat & P_LINK_UP_IRQ(P7_IRQ_OFF)) {
+ priv->port_sts[7].link = true;
+ dsa_port_phylink_mac_change(ds, 7, true);
+ }
+ if (priv->irq1_stat & P_LINK_DOWN_IRQ(P7_IRQ_OFF)) {
+ priv->port_sts[7].link = false;
+ dsa_port_phylink_mac_change(ds, 7, false);
+ }
return IRQ_HANDLED;
}
@@ -620,6 +627,192 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port,
status->pause = 1;
}
+static void bcm_sf2_sw_validate(struct dsa_switch *ds, int port,
+ unsigned long *supported,
+ struct phylink_link_state *state)
+{
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+
+ if (!phy_interface_mode_is_rgmii(state->interface) &&
+ state->interface != PHY_INTERFACE_MODE_MII &&
+ state->interface != PHY_INTERFACE_MODE_REVMII &&
+ state->interface != PHY_INTERFACE_MODE_GMII &&
+ state->interface != PHY_INTERFACE_MODE_INTERNAL &&
+ state->interface != PHY_INTERFACE_MODE_MOCA) {
+ bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
+ dev_err(ds->dev,
+ "Unsupported interface: %d\n", state->interface);
+ return;
+ }
+
+ /* Allow all the expected bits */
+ phylink_set(mask, Autoneg);
+ phylink_set_port_modes(mask);
+ phylink_set(mask, Pause);
+ phylink_set(mask, Asym_Pause);
+
+ /* With the exclusion of MII and Reverse MII, we support Gigabit,
+ * including Half duplex
+ */
+ if (state->interface != PHY_INTERFACE_MODE_MII &&
+ state->interface != PHY_INTERFACE_MODE_REVMII) {
+ phylink_set(mask, 1000baseT_Full);
+ phylink_set(mask, 1000baseT_Half);
+ }
+
+ phylink_set(mask, 10baseT_Half);
+ phylink_set(mask, 10baseT_Full);
+ phylink_set(mask, 100baseT_Half);
+ phylink_set(mask, 100baseT_Full);
+
+ bitmap_and(supported, supported, mask,
+ __ETHTOOL_LINK_MODE_MASK_NBITS);
+ bitmap_and(state->advertising, state->advertising, mask,
+ __ETHTOOL_LINK_MODE_MASK_NBITS);
+}
+
+static void bcm_sf2_sw_mac_config(struct dsa_switch *ds, int port,
+ unsigned int mode,
+ const struct phylink_link_state *state)
+{
+ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
+ u32 id_mode_dis = 0, port_mode;
+ u32 reg, offset;
+
+ if (priv->type == BCM7445_DEVICE_ID)
+ offset = CORE_STS_OVERRIDE_GMIIP_PORT(port);
+ else
+ offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port);
+
+ switch (state->interface) {
+ case PHY_INTERFACE_MODE_RGMII:
+ id_mode_dis = 1;
+ /* fallthrough */
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ port_mode = EXT_GPHY;
+ break;
+ case PHY_INTERFACE_MODE_MII:
+ port_mode = EXT_EPHY;
+ break;
+ case PHY_INTERFACE_MODE_REVMII:
+ port_mode = EXT_REVMII;
+ break;
+ default:
+ /* all other PHYs: internal and MoCA */
+ goto force_link;
+ }
+
+ /* Clear id_mode_dis bit, and the existing port mode, let
+ * RGMII_MODE_EN bet set by mac_link_{up,down}
+ */
+ reg = reg_readl(priv, REG_RGMII_CNTRL_P(port));
+ reg &= ~ID_MODE_DIS;
+ reg &= ~(PORT_MODE_MASK << PORT_MODE_SHIFT);
+ reg &= ~(RX_PAUSE_EN | TX_PAUSE_EN);
+
+ reg |= port_mode;
+ if (id_mode_dis)
+ reg |= ID_MODE_DIS;
+
+ if (state->pause & MLO_PAUSE_TXRX_MASK) {
+ if (state->pause & MLO_PAUSE_TX)
+ reg |= TX_PAUSE_EN;
+ reg |= RX_PAUSE_EN;
+ }
+
+ reg_writel(priv, reg, REG_RGMII_CNTRL_P(port));
+
+force_link:
+ /* Force link settings detected from the PHY */
+ reg = SW_OVERRIDE;
+ switch (state->speed) {
+ case SPEED_1000:
+ reg |= SPDSTS_1000 << SPEED_SHIFT;
+ break;
+ case SPEED_100:
+ reg |= SPDSTS_100 << SPEED_SHIFT;
+ break;
+ }
+
+ if (state->link)
+ reg |= LINK_STS;
+ if (state->duplex == DUPLEX_FULL)
+ reg |= DUPLX_MODE;
+
+ core_writel(priv, reg, offset);
+}
+
+static void bcm_sf2_sw_mac_link_set(struct dsa_switch *ds, int port,
+ phy_interface_t interface, bool link)
+{
+ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
+ u32 reg;
+
+ if (!phy_interface_mode_is_rgmii(interface) &&
+ interface != PHY_INTERFACE_MODE_MII &&
+ interface != PHY_INTERFACE_MODE_REVMII)
+ return;
+
+ /* If the link is down, just disable the interface to conserve power */
+ reg = reg_readl(priv, REG_RGMII_CNTRL_P(port));
+ if (link)
+ reg |= RGMII_MODE_EN;
+ else
+ reg &= ~RGMII_MODE_EN;
+ reg_writel(priv, reg, REG_RGMII_CNTRL_P(port));
+}
+
+static void bcm_sf2_sw_mac_link_down(struct dsa_switch *ds, int port,
+ unsigned int mode,
+ phy_interface_t interface)
+{
+ bcm_sf2_sw_mac_link_set(ds, port, interface, false);
+}
+
+static void bcm_sf2_sw_mac_link_up(struct dsa_switch *ds, int port,
+ unsigned int mode,
+ phy_interface_t interface,
+ struct phy_device *phydev)
+{
+ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
+ struct ethtool_eee *p = &priv->dev->ports[port].eee;
+
+ bcm_sf2_sw_mac_link_set(ds, port, interface, true);
+
+ if (mode == MLO_AN_PHY && phydev)
+ p->eee_enabled = b53_eee_init(ds, port, phydev);
+}
+
+static void bcm_sf2_sw_fixed_state(struct dsa_switch *ds, int port,
+ struct phylink_link_state *status)
+{
+ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
+
+ status->link = false;
+
+ /* MoCA port is special as we do not get link status from CORE_LNKSTS,
+ * which means that we need to force the link at the port override
+ * level to get the data to flow. We do use what the interrupt handler
+ * did determine before.
+ *
+ * For the other ports, we just force the link status, since this is
+ * a fixed PHY device.
+ */
+ if (port == priv->moca_port) {
+ status->link = priv->port_sts[port].link;
+ /* For MoCA interfaces, also force a link down notification
+ * since some version of the user-space daemon (mocad) use
+ * cmd->autoneg to force the link, which messes up the PHY
+ * state machine and make it go in PHY_FORCING state instead.
+ */
+ if (!status->link)
+ netif_carrier_off(ds->ports[port].slave);
+ status->duplex = DUPLEX_FULL;
+ } else {
+ status->link = true;
+ }
+}
+
static void bcm_sf2_enable_acb(struct dsa_switch *ds)
{
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
@@ -863,6 +1056,11 @@ static const struct dsa_switch_ops bcm_sf2_ops = {
.get_phy_flags = bcm_sf2_sw_get_phy_flags,
.adjust_link = bcm_sf2_sw_adjust_link,
.fixed_link_update = bcm_sf2_sw_fixed_link_update,
+ .phylink_validate = bcm_sf2_sw_validate,
+ .phylink_mac_config = bcm_sf2_sw_mac_config,
+ .phylink_mac_link_down = bcm_sf2_sw_mac_link_down,
+ .phylink_mac_link_up = bcm_sf2_sw_mac_link_up,
+ .phylink_fixed_state = bcm_sf2_sw_fixed_state,
.suspend = bcm_sf2_sw_suspend,
.resume = bcm_sf2_sw_resume,
.get_wol = bcm_sf2_sw_get_wol,
@@ -1065,14 +1263,14 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev)
bcm_sf2_intr_disable(priv);
ret = devm_request_irq(&pdev->dev, priv->irq0, bcm_sf2_switch_0_isr, 0,
- "switch_0", priv);
+ "switch_0", ds);
if (ret < 0) {
pr_err("failed to request switch_0 IRQ\n");
goto out_mdio;
}
ret = devm_request_irq(&pdev->dev, priv->irq1, bcm_sf2_switch_1_isr, 0,
- "switch_1", priv);
+ "switch_1", ds);
if (ret < 0) {
pr_err("failed to request switch_1 IRQ\n");
goto out_mdio;
--
2.17.0
^ permalink raw reply related
* [PATCH net-next 6/9] net: dsa: Eliminate dsa_slave_get_link()
From: Florian Fainelli @ 2018-05-05 19:04 UTC (permalink / raw)
To: netdev
Cc: Florian Fainelli, privat, andrew, vivien.didelot, davem,
rmk+kernel, sean.wang, Woojung.Huh, john, cphealy
In-Reply-To: <20180505190425.14378-1-f.fainelli@gmail.com>
Since we use PHYLIB to manage the per-port link indication, this will
also be reflected correctly in the network device's carrier state, so we
can use ethtool_op_get_link() instead.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
net/dsa/slave.c | 12 +-----------
1 file changed, 1 insertion(+), 11 deletions(-)
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index e9be9458aa94..e78894477598 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -498,16 +498,6 @@ dsa_slave_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *_p)
ds->ops->get_regs(ds, dp->index, regs, _p);
}
-static u32 dsa_slave_get_link(struct net_device *dev)
-{
- if (!dev->phydev)
- return -ENODEV;
-
- genphy_update_link(dev->phydev);
-
- return dev->phydev->link;
-}
-
static int dsa_slave_get_eeprom_len(struct net_device *dev)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
@@ -982,7 +972,7 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = {
.get_regs_len = dsa_slave_get_regs_len,
.get_regs = dsa_slave_get_regs,
.nway_reset = phy_ethtool_nway_reset,
- .get_link = dsa_slave_get_link,
+ .get_link = ethtool_op_get_link,
.get_eeprom_len = dsa_slave_get_eeprom_len,
.get_eeprom = dsa_slave_get_eeprom,
.set_eeprom = dsa_slave_set_eeprom,
--
2.17.0
^ permalink raw reply related
* [PATCH net-next 7/9] net: dsa: mv88e6xxx: add PHYLINK support
From: Florian Fainelli @ 2018-05-05 19:04 UTC (permalink / raw)
To: netdev
Cc: Russell King, Florian Fainelli, privat, andrew, vivien.didelot,
davem, sean.wang, Woojung.Huh, john, cphealy
In-Reply-To: <20180505190425.14378-1-f.fainelli@gmail.com>
From: Russell King <rmk+kernel@armlinux.org.uk>
Add rudimentary phylink support to mv88e6xxx. This allows the driver
using user ports with fixed links to keep operating normally. User ports
with normal PHYs are not affected since the switch automatically manages
their link parameters. User facing ports which use a SFP/SFF with a
non-fixed link mode might require a call to phylink_mac_change() to
operate properly.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
[florian: expand commit message]
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/net/dsa/mv88e6xxx/chip.c | 81 ++++++++++++++++++++++++++++++++
drivers/net/dsa/mv88e6xxx/port.c | 39 +++++++++++++++
drivers/net/dsa/mv88e6xxx/port.h | 3 ++
3 files changed, 123 insertions(+)
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 9d62e4acc01b..f4e8a3469386 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -31,6 +31,7 @@
#include <linux/netdevice.h>
#include <linux/gpio/consumer.h>
#include <linux/phy.h>
+#include <linux/phylink.h>
#include <net/dsa.h>
#include "chip.h"
@@ -580,6 +581,81 @@ static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
dev_err(ds->dev, "p%d: failed to configure MAC\n", port);
}
+static void mv88e6xxx_validate(struct dsa_switch *ds, int port,
+ unsigned long *supported,
+ struct phylink_link_state *state)
+{
+}
+
+static int mv88e6xxx_link_state(struct dsa_switch *ds, int port,
+ struct phylink_link_state *state)
+{
+ struct mv88e6xxx_chip *chip = ds->priv;
+ int err;
+
+ mutex_lock(&chip->reg_lock);
+ err = mv88e6xxx_port_link_state(chip, port, state);
+ mutex_unlock(&chip->reg_lock);
+
+ return err;
+}
+
+static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port,
+ unsigned int mode,
+ const struct phylink_link_state *state)
+{
+ struct mv88e6xxx_chip *chip = ds->priv;
+ int speed, duplex, err;
+
+ if (mode == MLO_AN_PHY)
+ return;
+
+ if (mode != MLO_AN_INBAND) {
+ speed = state->speed;
+ duplex = state->duplex;
+ } else {
+ speed = SPEED_UNFORCED;
+ duplex = DUPLEX_UNFORCED;
+ }
+
+ mutex_lock(&chip->reg_lock);
+ err = mv88e6xxx_port_setup_mac(chip, port, LINK_UNFORCED, speed, duplex,
+ state->interface);
+ mutex_unlock(&chip->reg_lock);
+
+ if (err && err != -EOPNOTSUPP)
+ dev_err(ds->dev, "p%d: failed to configure MAC\n", port);
+}
+
+static void mv88e6xxx_mac_link_force(struct dsa_switch *ds, int port, int link)
+{
+ struct mv88e6xxx_chip *chip = ds->priv;
+ int err;
+
+ mutex_lock(&chip->reg_lock);
+ err = chip->info->ops->port_set_link(chip, port, link);
+ mutex_unlock(&chip->reg_lock);
+
+ if (err)
+ dev_err(chip->dev, "p%d: failed to force MAC link\n", port);
+}
+
+static void mv88e6xxx_mac_link_down(struct dsa_switch *ds, int port,
+ unsigned int mode,
+ phy_interface_t interface)
+{
+ if (mode == MLO_AN_FIXED)
+ mv88e6xxx_mac_link_force(ds, port, LINK_FORCED_DOWN);
+}
+
+static void mv88e6xxx_mac_link_up(struct dsa_switch *ds, int port,
+ unsigned int mode, phy_interface_t interface,
+ struct phy_device *phydev)
+{
+ if (mode == MLO_AN_FIXED)
+ mv88e6xxx_mac_link_force(ds, port, LINK_FORCED_UP);
+}
+
static int mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
{
if (!chip->info->ops->stats_snapshot)
@@ -4138,6 +4214,11 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
.get_tag_protocol = mv88e6xxx_get_tag_protocol,
.setup = mv88e6xxx_setup,
.adjust_link = mv88e6xxx_adjust_link,
+ .phylink_validate = mv88e6xxx_validate,
+ .phylink_mac_link_state = mv88e6xxx_link_state,
+ .phylink_mac_config = mv88e6xxx_mac_config,
+ .phylink_mac_link_down = mv88e6xxx_mac_link_down,
+ .phylink_mac_link_up = mv88e6xxx_mac_link_up,
.get_strings = mv88e6xxx_get_strings,
.get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
.get_sset_count = mv88e6xxx_get_sset_count,
diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
index 6315774d72b3..429d0ebcd5b1 100644
--- a/drivers/net/dsa/mv88e6xxx/port.c
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -15,6 +15,7 @@
#include <linux/bitfield.h>
#include <linux/if_bridge.h>
#include <linux/phy.h>
+#include <linux/phylink.h>
#include "chip.h"
#include "port.h"
@@ -378,6 +379,44 @@ int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode)
return 0;
}
+int mv88e6xxx_port_link_state(struct mv88e6xxx_chip *chip, int port,
+ struct phylink_link_state *state)
+{
+ int err;
+ u16 reg;
+
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®);
+ if (err)
+ return err;
+
+ switch (reg & MV88E6XXX_PORT_STS_SPEED_MASK) {
+ case MV88E6XXX_PORT_STS_SPEED_10:
+ state->speed = SPEED_10;
+ break;
+ case MV88E6XXX_PORT_STS_SPEED_100:
+ state->speed = SPEED_100;
+ break;
+ case MV88E6XXX_PORT_STS_SPEED_1000:
+ state->speed = SPEED_1000;
+ break;
+ case MV88E6XXX_PORT_STS_SPEED_10000:
+ if ((reg &MV88E6XXX_PORT_STS_CMODE_MASK) ==
+ MV88E6XXX_PORT_STS_CMODE_2500BASEX)
+ state->speed = SPEED_2500;
+ else
+ state->speed = SPEED_10000;
+ break;
+ }
+
+ state->duplex = reg & MV88E6XXX_PORT_STS_DUPLEX ?
+ DUPLEX_FULL : DUPLEX_HALF;
+ state->link = !!(reg & MV88E6XXX_PORT_STS_LINK);
+ state->an_enabled = 1;
+ state->an_complete = state->link;
+
+ return 0;
+}
+
/* Offset 0x02: Jamming Control
*
* Do not limit the period of time that this port can be paused for by
diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
index b16d5f0e6e9c..5e1db1b221ca 100644
--- a/drivers/net/dsa/mv88e6xxx/port.h
+++ b/drivers/net/dsa/mv88e6xxx/port.h
@@ -29,6 +29,7 @@
#define MV88E6XXX_PORT_STS_SPEED_10 0x0000
#define MV88E6XXX_PORT_STS_SPEED_100 0x0100
#define MV88E6XXX_PORT_STS_SPEED_1000 0x0200
+#define MV88E6XXX_PORT_STS_SPEED_10000 0x0300
#define MV88E6352_PORT_STS_EEE 0x0040
#define MV88E6165_PORT_STS_AM_DIS 0x0040
#define MV88E6185_PORT_STS_MGMII 0x0040
@@ -295,6 +296,8 @@ int mv88e6390_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in,
int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
phy_interface_t mode);
int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
+int mv88e6xxx_port_link_state(struct mv88e6xxx_chip *chip, int port,
+ struct phylink_link_state *state);
int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port);
int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,
int upstream_port);
--
2.17.0
^ permalink raw reply related
* [PATCH net-next 8/9] net: dsa: Plug in PHYLINK support
From: Florian Fainelli @ 2018-05-05 19:04 UTC (permalink / raw)
To: netdev
Cc: Florian Fainelli, privat, andrew, vivien.didelot, davem,
rmk+kernel, sean.wang, Woojung.Huh, john, cphealy
In-Reply-To: <20180505190425.14378-1-f.fainelli@gmail.com>
Add support for PHYLINK within the DSA subsystem in order to support more
complex devices such as pluggable (SFP) and non-pluggable (SFF) modules, 10G
PHYs, and traditional PHYs. Using PHYLINK allows us to drop some amount of
complexity we had while probing fixed and non-fixed PHYs using Device Tree.
Because PHYLINK separates the Ethernet MAC/port configuration into different
stages, we let switch drivers implement those, and for now, we maintain
functionality by calling dsa_slave_adjust_link() during
phylink_mac_link_{up,down} which provides semantically equivalent steps.
Drivers willing to take advantage of PHYLINK should implement the phylink_mac_*
operations that DSA wraps.
We cannot quite remove the adjust_link() callback just yet, because a number of
drivers rely on that for configuring their "CPU" and "DSA" ports, this is done
dsa_port_setup_phy_of() and dsa_port_fixed_link_register_of() still.
Drivers that utilize fixed links for user-facing ports (e.g: bcm_sf2) will need
to implement phylink_mac_ops from now on to preserve functionality, since PHYLINK
*does not* create a phy_device instance for fixed links.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
include/net/dsa.h | 1 +
net/dsa/dsa_priv.h | 9 --
net/dsa/slave.c | 294 ++++++++++++++++++++++++++-------------------
3 files changed, 172 insertions(+), 132 deletions(-)
diff --git a/include/net/dsa.h b/include/net/dsa.h
index ed64c1f3f117..fdbd6082945d 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -201,6 +201,7 @@ struct dsa_port {
u8 stp_state;
struct net_device *bridge_dev;
struct devlink_port devlink_port;
+ struct phylink *pl;
/*
* Original copy of the master netdev ethtool_ops
*/
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index 053731473c99..3964c6f7a7c0 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -75,15 +75,6 @@ struct dsa_slave_priv {
/* DSA port data, such as switch, port index, etc. */
struct dsa_port *dp;
- /*
- * The phylib phy_device pointer for the PHY connected
- * to this port.
- */
- phy_interface_t phy_interface;
- int old_link;
- int old_pause;
- int old_duplex;
-
#ifdef CONFIG_NET_POLL_CONTROLLER
struct netpoll *netpoll;
#endif
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index e78894477598..4a9abc66132f 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -13,6 +13,7 @@
#include <linux/netdevice.h>
#include <linux/phy.h>
#include <linux/phy_fixed.h>
+#include <linux/phylink.h>
#include <linux/of_net.h>
#include <linux/of_mdio.h>
#include <linux/mdio.h>
@@ -97,8 +98,7 @@ static int dsa_slave_open(struct net_device *dev)
if (err)
goto clear_promisc;
- if (dev->phydev)
- phy_start(dev->phydev);
+ phylink_start(dp->pl);
return 0;
@@ -120,8 +120,7 @@ static int dsa_slave_close(struct net_device *dev)
struct net_device *master = dsa_slave_to_master(dev);
struct dsa_port *dp = dsa_slave_to_port(dev);
- if (dev->phydev)
- phy_stop(dev->phydev);
+ phylink_stop(dp->pl);
dsa_port_disable(dp, dev->phydev);
@@ -272,10 +271,7 @@ static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
}
- if (!dev->phydev)
- return -ENODEV;
-
- return phy_mii_ioctl(dev->phydev, ifr, cmd);
+ return phylink_mii_ioctl(p->dp->pl, ifr, cmd);
}
static int dsa_slave_port_attr_set(struct net_device *dev,
@@ -498,6 +494,13 @@ dsa_slave_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *_p)
ds->ops->get_regs(ds, dp->index, regs, _p);
}
+static int dsa_slave_nway_reset(struct net_device *dev)
+{
+ struct dsa_port *dp = dsa_slave_to_port(dev);
+
+ return phylink_ethtool_nway_reset(dp->pl);
+}
+
static int dsa_slave_get_eeprom_len(struct net_device *dev)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
@@ -609,6 +612,8 @@ static void dsa_slave_get_wol(struct net_device *dev, struct ethtool_wolinfo *w)
struct dsa_port *dp = dsa_slave_to_port(dev);
struct dsa_switch *ds = dp->ds;
+ phylink_ethtool_get_wol(dp->pl, w);
+
if (ds->ops->get_wol)
ds->ops->get_wol(ds, dp->index, w);
}
@@ -619,6 +624,8 @@ static int dsa_slave_set_wol(struct net_device *dev, struct ethtool_wolinfo *w)
struct dsa_switch *ds = dp->ds;
int ret = -EOPNOTSUPP;
+ phylink_ethtool_set_wol(dp->pl, w);
+
if (ds->ops->set_wol)
ret = ds->ops->set_wol(ds, dp->index, w);
@@ -642,13 +649,7 @@ static int dsa_slave_set_eee(struct net_device *dev, struct ethtool_eee *e)
if (ret)
return ret;
- if (e->eee_enabled) {
- ret = phy_init_eee(dev->phydev, 0);
- if (ret)
- return ret;
- }
-
- return phy_ethtool_set_eee(dev->phydev, e);
+ return phylink_ethtool_set_eee(dp->pl, e);
}
static int dsa_slave_get_eee(struct net_device *dev, struct ethtool_eee *e)
@@ -668,7 +669,23 @@ static int dsa_slave_get_eee(struct net_device *dev, struct ethtool_eee *e)
if (ret)
return ret;
- return phy_ethtool_get_eee(dev->phydev, e);
+ return phylink_ethtool_get_eee(dp->pl, e);
+}
+
+static int dsa_slave_get_link_ksettings(struct net_device *dev,
+ struct ethtool_link_ksettings *cmd)
+{
+ struct dsa_port *dp = dsa_slave_to_port(dev);
+
+ return phylink_ethtool_ksettings_get(dp->pl, cmd);
+}
+
+static int dsa_slave_set_link_ksettings(struct net_device *dev,
+ const struct ethtool_link_ksettings *cmd)
+{
+ struct dsa_port *dp = dsa_slave_to_port(dev);
+
+ return phylink_ethtool_ksettings_set(dp->pl, cmd);
}
#ifdef CONFIG_NET_POLL_CONTROLLER
@@ -971,7 +988,7 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = {
.get_drvinfo = dsa_slave_get_drvinfo,
.get_regs_len = dsa_slave_get_regs_len,
.get_regs = dsa_slave_get_regs,
- .nway_reset = phy_ethtool_nway_reset,
+ .nway_reset = dsa_slave_nway_reset,
.get_link = ethtool_op_get_link,
.get_eeprom_len = dsa_slave_get_eeprom_len,
.get_eeprom = dsa_slave_get_eeprom,
@@ -983,8 +1000,8 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = {
.get_wol = dsa_slave_get_wol,
.set_eee = dsa_slave_set_eee,
.get_eee = dsa_slave_get_eee,
- .get_link_ksettings = phy_ethtool_get_link_ksettings,
- .set_link_ksettings = phy_ethtool_set_link_ksettings,
+ .get_link_ksettings = dsa_slave_get_link_ksettings,
+ .set_link_ksettings = dsa_slave_set_link_ksettings,
.get_rxnfc = dsa_slave_get_rxnfc,
.set_rxnfc = dsa_slave_set_rxnfc,
.get_ts_info = dsa_slave_get_ts_info,
@@ -1043,56 +1060,122 @@ static struct device_type dsa_type = {
.name = "dsa",
};
-static void dsa_slave_adjust_link(struct net_device *dev)
+static void dsa_slave_phylink_validate(struct net_device *dev,
+ unsigned long *supported,
+ struct phylink_link_state *state)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
- struct dsa_slave_priv *p = netdev_priv(dev);
struct dsa_switch *ds = dp->ds;
- unsigned int status_changed = 0;
- if (p->old_link != dev->phydev->link) {
- status_changed = 1;
- p->old_link = dev->phydev->link;
- }
+ if (!ds->ops->phylink_validate)
+ return;
- if (p->old_duplex != dev->phydev->duplex) {
- status_changed = 1;
- p->old_duplex = dev->phydev->duplex;
- }
+ ds->ops->phylink_validate(ds, dp->index, supported, state);
+}
- if (p->old_pause != dev->phydev->pause) {
- status_changed = 1;
- p->old_pause = dev->phydev->pause;
- }
+static int dsa_slave_phylink_mac_link_state(struct net_device *dev,
+ struct phylink_link_state *state)
+{
+ struct dsa_port *dp = dsa_slave_to_port(dev);
+ struct dsa_switch *ds = dp->ds;
- if (ds->ops->adjust_link && status_changed)
- ds->ops->adjust_link(ds, dp->index, dev->phydev);
+ /* Only called for SGMII and 802.3z */
+ if (!ds->ops->phylink_mac_link_state)
+ return -EOPNOTSUPP;
- if (status_changed)
- phy_print_status(dev->phydev);
+ return ds->ops->phylink_mac_link_state(ds, dp->index, state);
}
-static int dsa_slave_fixed_link_update(struct net_device *dev,
- struct fixed_phy_status *status)
+static void dsa_slave_phylink_mac_config(struct net_device *dev,
+ unsigned int mode,
+ const struct phylink_link_state *state)
{
- struct dsa_switch *ds;
- struct dsa_port *dp;
+ struct dsa_port *dp = dsa_slave_to_port(dev);
+ struct dsa_switch *ds = dp->ds;
+
+ if (!ds->ops->phylink_mac_config)
+ return;
- if (dev) {
- dp = dsa_slave_to_port(dev);
- ds = dp->ds;
- if (ds->ops->fixed_link_update)
- ds->ops->fixed_link_update(ds, dp->index, status);
+ ds->ops->phylink_mac_config(ds, dp->index, mode, state);
+}
+
+static void dsa_slave_phylink_mac_an_restart(struct net_device *dev)
+{
+ struct dsa_port *dp = dsa_slave_to_port(dev);
+ struct dsa_switch *ds = dp->ds;
+
+ if (!ds->ops->phylink_mac_an_restart)
+ return;
+
+ ds->ops->phylink_mac_an_restart(ds, dp->index);
+}
+
+static void dsa_slave_phylink_mac_link_down(struct net_device *dev,
+ unsigned int mode,
+ phy_interface_t interface)
+{
+ struct dsa_port *dp = dsa_slave_to_port(dev);
+ struct dsa_switch *ds = dp->ds;
+
+ if (!ds->ops->phylink_mac_link_down) {
+ if (ds->ops->adjust_link && dev->phydev)
+ ds->ops->adjust_link(ds, dp->index, dev->phydev);
+ return;
}
- return 0;
+ ds->ops->phylink_mac_link_down(ds, dp->index, mode, interface);
+}
+
+static void dsa_slave_phylink_mac_link_up(struct net_device *dev,
+ unsigned int mode,
+ phy_interface_t interface,
+ struct phy_device *phydev)
+{
+ struct dsa_port *dp = dsa_slave_to_port(dev);
+ struct dsa_switch *ds = dp->ds;
+
+ if (!ds->ops->phylink_mac_link_up) {
+ if (ds->ops->adjust_link && dev->phydev)
+ ds->ops->adjust_link(ds, dp->index, dev->phydev);
+ return;
+ }
+
+ ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev);
+}
+
+static const struct phylink_mac_ops dsa_slave_phylink_mac_ops = {
+ .validate = dsa_slave_phylink_validate,
+ .mac_link_state = dsa_slave_phylink_mac_link_state,
+ .mac_config = dsa_slave_phylink_mac_config,
+ .mac_an_restart = dsa_slave_phylink_mac_an_restart,
+ .mac_link_down = dsa_slave_phylink_mac_link_down,
+ .mac_link_up = dsa_slave_phylink_mac_link_up,
+};
+
+void dsa_port_phylink_mac_change(struct dsa_switch *ds, int port, bool up)
+{
+ const struct dsa_port *dp = dsa_to_port(ds, port);
+
+ phylink_mac_change(dp->pl, up);
+}
+EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_change);
+
+static void dsa_slave_phylink_fixed_state(struct net_device *dev,
+ struct phylink_link_state *state)
+{
+ struct dsa_port *dp = dsa_slave_to_port(dev);
+ struct dsa_switch *ds = dp->ds;
+
+ /* No need to check that this operation is valid, the callback would
+ * not be called if it was not.
+ */
+ ds->ops->phylink_fixed_state(ds, dp->index, state);
}
/* slave device setup *******************************************************/
static int dsa_slave_phy_connect(struct net_device *slave_dev, int addr)
{
struct dsa_port *dp = dsa_slave_to_port(slave_dev);
- struct dsa_slave_priv *p = netdev_priv(slave_dev);
struct dsa_switch *ds = dp->ds;
slave_dev->phydev = mdiobus_get_phy(ds->slave_mii_bus, addr);
@@ -1101,80 +1184,54 @@ static int dsa_slave_phy_connect(struct net_device *slave_dev, int addr)
return -ENODEV;
}
- /* Use already configured phy mode */
- if (p->phy_interface == PHY_INTERFACE_MODE_NA)
- p->phy_interface = slave_dev->phydev->interface;
-
- return phy_connect_direct(slave_dev, slave_dev->phydev,
- dsa_slave_adjust_link, p->phy_interface);
-}
-
-void dsa_port_phylink_mac_change(struct dsa_switch *ds, int port, bool up)
-{
+ return phylink_connect_phy(dp->pl, slave_dev->phydev);
}
-EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_change);
static int dsa_slave_phy_setup(struct net_device *slave_dev)
{
struct dsa_port *dp = dsa_slave_to_port(slave_dev);
- struct dsa_slave_priv *p = netdev_priv(slave_dev);
struct device_node *port_dn = dp->dn;
struct dsa_switch *ds = dp->ds;
- struct device_node *phy_dn;
- bool phy_is_fixed = false;
u32 phy_flags = 0;
int mode, ret;
mode = of_get_phy_mode(port_dn);
if (mode < 0)
mode = PHY_INTERFACE_MODE_NA;
- p->phy_interface = mode;
- phy_dn = of_parse_phandle(port_dn, "phy-handle", 0);
- if (!phy_dn && of_phy_is_fixed_link(port_dn)) {
- /* In the case of a fixed PHY, the DT node associated
- * to the fixed PHY is the Port DT node
- */
- ret = of_phy_register_fixed_link(port_dn);
- if (ret) {
- netdev_err(slave_dev, "failed to register fixed PHY: %d\n", ret);
- return ret;
- }
- phy_is_fixed = true;
- phy_dn = of_node_get(port_dn);
+ dp->pl = phylink_create(slave_dev, of_fwnode_handle(port_dn), mode,
+ &dsa_slave_phylink_mac_ops);
+ if (IS_ERR(dp->pl)) {
+ netdev_err(slave_dev,
+ "error creating PHYLINK: %ld\n", PTR_ERR(dp->pl));
+ return PTR_ERR(dp->pl);
}
+ /* Register only if the switch provides such a callback, since this
+ * callback takes precedence over polling the link GPIO in PHYLINK
+ * (see phylink_get_fixed_state).
+ */
+ if (ds->ops->phylink_fixed_state)
+ phylink_fixed_state_cb(dp->pl, dsa_slave_phylink_fixed_state);
+
if (ds->ops->get_phy_flags)
phy_flags = ds->ops->get_phy_flags(ds, dp->index);
- if (phy_dn) {
- slave_dev->phydev = of_phy_connect(slave_dev, phy_dn,
- dsa_slave_adjust_link,
- phy_flags,
- p->phy_interface);
- of_node_put(phy_dn);
- }
-
- if (slave_dev->phydev && phy_is_fixed)
- fixed_phy_set_link_update(slave_dev->phydev,
- dsa_slave_fixed_link_update);
-
- /* We could not connect to a designated PHY, so use the switch internal
- * MDIO bus instead
- */
- if (!slave_dev->phydev) {
+ ret = phylink_of_phy_connect(dp->pl, port_dn, phy_flags);
+ if (ret == -ENODEV) {
+ /* We could not connect to a designated PHY or SFP, so use the
+ * switch internal MDIO bus instead
+ */
ret = dsa_slave_phy_connect(slave_dev, dp->index);
if (ret) {
- netdev_err(slave_dev, "failed to connect to port %d: %d\n",
+ netdev_err(slave_dev,
+ "failed to connect to port %d: %d\n",
dp->index, ret);
- if (phy_is_fixed)
- of_phy_deregister_fixed_link(port_dn);
+ phylink_destroy(dp->pl);
return ret;
}
}
- phy_attached_info(slave_dev->phydev);
-
return 0;
}
@@ -1189,29 +1246,26 @@ static void dsa_slave_set_lockdep_class_one(struct net_device *dev,
int dsa_slave_suspend(struct net_device *slave_dev)
{
- struct dsa_slave_priv *p = netdev_priv(slave_dev);
+ struct dsa_port *dp = dsa_slave_to_port(slave_dev);
netif_device_detach(slave_dev);
- if (slave_dev->phydev) {
- phy_stop(slave_dev->phydev);
- p->old_pause = -1;
- p->old_link = -1;
- p->old_duplex = -1;
- phy_suspend(slave_dev->phydev);
- }
+ rtnl_lock();
+ phylink_stop(dp->pl);
+ rtnl_unlock();
return 0;
}
int dsa_slave_resume(struct net_device *slave_dev)
{
+ struct dsa_port *dp = dsa_slave_to_port(slave_dev);
+
netif_device_attach(slave_dev);
- if (slave_dev->phydev) {
- phy_resume(slave_dev->phydev);
- phy_start(slave_dev->phydev);
- }
+ rtnl_lock();
+ phylink_start(dp->pl);
+ rtnl_unlock();
return 0;
}
@@ -1276,11 +1330,6 @@ int dsa_slave_create(struct dsa_port *port)
p->dp = port;
INIT_LIST_HEAD(&p->mall_tc_list);
p->xmit = cpu_dp->tag_ops->xmit;
-
- p->old_pause = -1;
- p->old_link = -1;
- p->old_duplex = -1;
-
port->slave = slave_dev;
netif_carrier_off(slave_dev);
@@ -1303,9 +1352,10 @@ int dsa_slave_create(struct dsa_port *port)
return 0;
out_phy:
- phy_disconnect(slave_dev->phydev);
- if (of_phy_is_fixed_link(port->dn))
- of_phy_deregister_fixed_link(port->dn);
+ rtnl_lock();
+ phylink_disconnect_phy(p->dp->pl);
+ rtnl_unlock();
+ phylink_destroy(p->dp->pl);
out_free:
free_percpu(p->stats64);
free_netdev(slave_dev);
@@ -1317,17 +1367,15 @@ void dsa_slave_destroy(struct net_device *slave_dev)
{
struct dsa_port *dp = dsa_slave_to_port(slave_dev);
struct dsa_slave_priv *p = netdev_priv(slave_dev);
- struct device_node *port_dn = dp->dn;
netif_carrier_off(slave_dev);
- if (slave_dev->phydev) {
- phy_disconnect(slave_dev->phydev);
+ rtnl_lock();
+ phylink_disconnect_phy(dp->pl);
+ rtnl_unlock();
- if (of_phy_is_fixed_link(port_dn))
- of_phy_deregister_fixed_link(port_dn);
- }
dsa_slave_notify(slave_dev, DSA_PORT_UNREGISTER);
unregister_netdev(slave_dev);
+ phylink_destroy(dp->pl);
free_percpu(p->stats64);
free_netdev(slave_dev);
}
--
2.17.0
^ permalink raw reply related
* [PATCH net-next 9/9] net: dsa: bcm_sf2: Get rid of PHYLIB functions
From: Florian Fainelli @ 2018-05-05 19:04 UTC (permalink / raw)
To: netdev
Cc: Florian Fainelli, privat, andrew, vivien.didelot, davem,
rmk+kernel, sean.wang, Woojung.Huh, john, cphealy
In-Reply-To: <20180505190425.14378-1-f.fainelli@gmail.com>
Now that we have converted the bcm_sf2 driver to implement PHYLINK MAC
operations, we can remove the PHYLIB callbacks: adjust_link() and
fixed_link_update() which are no longer called by DSA.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/net/dsa/bcm_sf2.c | 149 --------------------------------------
1 file changed, 149 deletions(-)
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
index a20608b0329e..ac621f44237a 100644
--- a/drivers/net/dsa/bcm_sf2.c
+++ b/drivers/net/dsa/bcm_sf2.c
@@ -480,153 +480,6 @@ static u32 bcm_sf2_sw_get_phy_flags(struct dsa_switch *ds, int port)
return priv->hw_params.gphy_rev;
}
-static void bcm_sf2_sw_adjust_link(struct dsa_switch *ds, int port,
- struct phy_device *phydev)
-{
- struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
- struct ethtool_eee *p = &priv->dev->ports[port].eee;
- u32 id_mode_dis = 0, port_mode;
- const char *str = NULL;
- u32 reg, offset;
-
- if (priv->type == BCM7445_DEVICE_ID)
- offset = CORE_STS_OVERRIDE_GMIIP_PORT(port);
- else
- offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port);
-
- switch (phydev->interface) {
- case PHY_INTERFACE_MODE_RGMII:
- str = "RGMII (no delay)";
- id_mode_dis = 1;
- case PHY_INTERFACE_MODE_RGMII_TXID:
- if (!str)
- str = "RGMII (TX delay)";
- port_mode = EXT_GPHY;
- break;
- case PHY_INTERFACE_MODE_MII:
- str = "MII";
- port_mode = EXT_EPHY;
- break;
- case PHY_INTERFACE_MODE_REVMII:
- str = "Reverse MII";
- port_mode = EXT_REVMII;
- break;
- default:
- /* All other PHYs: internal and MoCA */
- goto force_link;
- }
-
- /* If the link is down, just disable the interface to conserve power */
- if (!phydev->link) {
- reg = reg_readl(priv, REG_RGMII_CNTRL_P(port));
- reg &= ~RGMII_MODE_EN;
- reg_writel(priv, reg, REG_RGMII_CNTRL_P(port));
- goto force_link;
- }
-
- /* Clear id_mode_dis bit, and the existing port mode, but
- * make sure we enable the RGMII block for data to pass
- */
- reg = reg_readl(priv, REG_RGMII_CNTRL_P(port));
- reg &= ~ID_MODE_DIS;
- reg &= ~(PORT_MODE_MASK << PORT_MODE_SHIFT);
- reg &= ~(RX_PAUSE_EN | TX_PAUSE_EN);
-
- reg |= port_mode | RGMII_MODE_EN;
- if (id_mode_dis)
- reg |= ID_MODE_DIS;
-
- if (phydev->pause) {
- if (phydev->asym_pause)
- reg |= TX_PAUSE_EN;
- reg |= RX_PAUSE_EN;
- }
-
- reg_writel(priv, reg, REG_RGMII_CNTRL_P(port));
-
- pr_info("Port %d configured for %s\n", port, str);
-
-force_link:
- /* Force link settings detected from the PHY */
- reg = SW_OVERRIDE;
- switch (phydev->speed) {
- case SPEED_1000:
- reg |= SPDSTS_1000 << SPEED_SHIFT;
- break;
- case SPEED_100:
- reg |= SPDSTS_100 << SPEED_SHIFT;
- break;
- }
-
- if (phydev->link)
- reg |= LINK_STS;
- if (phydev->duplex == DUPLEX_FULL)
- reg |= DUPLX_MODE;
-
- core_writel(priv, reg, offset);
-
- if (!phydev->is_pseudo_fixed_link)
- p->eee_enabled = b53_eee_init(ds, port, phydev);
-}
-
-static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port,
- struct fixed_phy_status *status)
-{
- struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
- u32 duplex, pause, offset;
- u32 reg;
-
- if (priv->type == BCM7445_DEVICE_ID)
- offset = CORE_STS_OVERRIDE_GMIIP_PORT(port);
- else
- offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port);
-
- duplex = core_readl(priv, CORE_DUPSTS);
- pause = core_readl(priv, CORE_PAUSESTS);
-
- status->link = 0;
-
- /* MoCA port is special as we do not get link status from CORE_LNKSTS,
- * which means that we need to force the link at the port override
- * level to get the data to flow. We do use what the interrupt handler
- * did determine before.
- *
- * For the other ports, we just force the link status, since this is
- * a fixed PHY device.
- */
- if (port == priv->moca_port) {
- status->link = priv->port_sts[port].link;
- /* For MoCA interfaces, also force a link down notification
- * since some version of the user-space daemon (mocad) use
- * cmd->autoneg to force the link, which messes up the PHY
- * state machine and make it go in PHY_FORCING state instead.
- */
- if (!status->link)
- netif_carrier_off(ds->ports[port].slave);
- status->duplex = 1;
- } else {
- status->link = 1;
- status->duplex = !!(duplex & (1 << port));
- }
-
- reg = core_readl(priv, offset);
- reg |= SW_OVERRIDE;
- if (status->link)
- reg |= LINK_STS;
- else
- reg &= ~LINK_STS;
- core_writel(priv, reg, offset);
-
- if ((pause & (1 << port)) &&
- (pause & (1 << (port + PAUSESTS_TX_PAUSE_SHIFT)))) {
- status->asym_pause = 1;
- status->pause = 1;
- }
-
- if (pause & (1 << port))
- status->pause = 1;
-}
-
static void bcm_sf2_sw_validate(struct dsa_switch *ds, int port,
unsigned long *supported,
struct phylink_link_state *state)
@@ -1054,8 +907,6 @@ static const struct dsa_switch_ops bcm_sf2_ops = {
.get_sset_count = b53_get_sset_count,
.get_ethtool_phy_stats = b53_get_ethtool_phy_stats,
.get_phy_flags = bcm_sf2_sw_get_phy_flags,
- .adjust_link = bcm_sf2_sw_adjust_link,
- .fixed_link_update = bcm_sf2_sw_fixed_link_update,
.phylink_validate = bcm_sf2_sw_validate,
.phylink_mac_config = bcm_sf2_sw_mac_config,
.phylink_mac_link_down = bcm_sf2_sw_mac_link_down,
--
2.17.0
^ permalink raw reply related
* [PATCH] isdn: eicon: fix a missing-check bug
From: Wenwen Wang @ 2018-05-05 19:32 UTC (permalink / raw)
To: Wenwen Wang
Cc: Kangjie Lu, Armin Schindler, Karsten Keil,
open list:ISDN SUBSYSTEM, open list
In divasmain.c, the function divas_write() firstly invokes the function
diva_xdi_open_adapter() to open the adapter that matches with the adapter
number provided by the user, and then invokes the function diva_xdi_write()
to perform the write operation using the matched adapter. The two functions
diva_xdi_open_adapter() and diva_xdi_write() are located in diva.c.
In diva_xdi_open_adapter(), the user command is copied to the object 'msg'
from the userspace pointer 'src' through the function pointer 'cp_fn',
which eventually calls copy_from_user() to do the copy. Then, the adapter
number 'msg.adapter' is used to find out a matched adapter from the
'adapter_queue'. A matched adapter will be returned if it is found.
Otherwise, NULL is returned to indicate the failure of the verification on
the adapter number.
As mentioned above, if a matched adapter is returned, the function
diva_xdi_write() is invoked to perform the write operation. In this
function, the user command is copied once again from the userspace pointer
'src', which is the same as the 'src' pointer in diva_xdi_open_adapter() as
both of them are from the 'buf' pointer in divas_write(). Similarly, the
copy is achieved through the function pointer 'cp_fn', which finally calls
copy_from_user(). After the successful copy, the corresponding command
processing handler of the matched adapter is invoked to perform the write
operation.
It is obvious that there are two copies here from userspace, one is in
diva_xdi_open_adapter(), and one is in diva_xdi_write(). Plus, both of
these two copies share the same source userspace pointer, i.e., the 'buf'
pointer in divas_write(). Given that a malicious userspace process can race
to change the content pointed by the 'buf' pointer, this can pose potential
security issues. For example, in the first copy, the user provides a valid
adapter number to pass the verification process and a valid adapter can be
found. Then the user can modify the adapter number to an invalid number.
This way, the user can bypass the verification process of the adapter
number and inject inconsistent data.
To avoid such issues, this patch adds a check after the second copy in the
function diva_xdi_write(). If the adapter number is not equal to the one
obtained in the first copy, (-4) will be returned to divas_write(), which
will then return an error code -EINVAL.
Signed-off-by: Wenwen Wang <wang6495@umn.edu>
---
drivers/isdn/hardware/eicon/diva.c | 6 +++++-
drivers/isdn/hardware/eicon/divasmain.c | 3 +++
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/isdn/hardware/eicon/diva.c b/drivers/isdn/hardware/eicon/diva.c
index 944a7f3..46cbf76 100644
--- a/drivers/isdn/hardware/eicon/diva.c
+++ b/drivers/isdn/hardware/eicon/diva.c
@@ -440,6 +440,7 @@ diva_xdi_write(void *adapter, void *os_handle, const void __user *src,
int length, divas_xdi_copy_from_user_fn_t cp_fn)
{
diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) adapter;
+ diva_xdi_um_cfg_cmd_t *p;
void *data;
if (a->xdi_mbox.status & DIVA_XDI_MBOX_BUSY) {
@@ -461,7 +462,10 @@ diva_xdi_write(void *adapter, void *os_handle, const void __user *src,
length = (*cp_fn) (os_handle, data, src, length);
if (length > 0) {
- if ((*(a->interface.cmd_proc))
+ p = (diva_xdi_um_cfg_cmd_t *) data;
+ if (a->controller != (int)p->adapter) {
+ length = -4;
+ } else if ((*(a->interface.cmd_proc))
(a, (diva_xdi_um_cfg_cmd_t *) data, length)) {
length = -3;
}
diff --git a/drivers/isdn/hardware/eicon/divasmain.c b/drivers/isdn/hardware/eicon/divasmain.c
index b9980e8..a03c658 100644
--- a/drivers/isdn/hardware/eicon/divasmain.c
+++ b/drivers/isdn/hardware/eicon/divasmain.c
@@ -614,6 +614,9 @@ static ssize_t divas_write(struct file *file, const char __user *buf,
case -3:
ret = -ENXIO;
break;
+ case -4:
+ ret = -EINVAL;
+ break;
}
DBG_TRC(("write: ret %d", ret));
return (ret);
--
2.7.4
^ permalink raw reply related
* Re: [PATCH v5 0/6] firmware_loader: cleanups for v4.18
From: Krzysztof Halasa @ 2018-05-05 20:26 UTC (permalink / raw)
To: Luis R. Rodriguez
Cc: gregkh, akpm, keescook, josh, teg, wagi, hdegoede, andresx7,
zohar, kubakici, shuah, mfuzzey, dhowells, pali.rohar, tiwai,
kvalo, arend.vanspriel, zajec5, nbroeking, markivx, broonie,
dmitry.torokhov, dwmw2, torvalds, Abhay_Salunke, bjorn.andersson,
jewalt, oneukum, cantabile.desu, ast, hare, jejb, martin.petersen,
davem, maco, arve, tkjos
In-Reply-To: <20180504195835.GU27853@wotan.suse.de>
"Luis R. Rodriguez" <mcgrof@kernel.org> writes:
> So we can nuke CONFIG_WANXL_BUILD_FIRMWARE now?
I'm uncertain I understand why do you want it, or maybe what are you
trying to do at all.
And what use would wanxlfw.S (the assembly source) have if the option is
removed?
>> It's more about delivering the .S source for the firmware, I guess.
>> Nobody is expected to build it. The fw is about 2.5 KB and is directly
>> linked with the driver.
>
> :P Future work I guess would be to just use the firmware API and stuff
> it into linux-firmware?
Who's going to make it happen?
The last time I checked (several years ago), wanXL worked. Who's going
to test it after the change?
I assume linux-firmware could include fw source and there would be means
to build the binary.
Just to be sure: the wanXL firmware has exactly nothing to do with FW
loader, nothing depends on it (nor the other way around), it's just
(with the rest of the wanXL code) an old piece of a driver for an old
card.
The question is, what do we gain by messing with it?
--
Krzysztof Halasa
^ permalink raw reply
* Re: [PATCH net-next] net: phy: sfp: handle cases where neither BR,min nor BR,max is given
From: Florian Fainelli @ 2018-05-05 20:35 UTC (permalink / raw)
To: Antoine Tenart, davem, linux
Cc: netdev, linux-kernel, thomas.petazzoni, maxime.chevallier,
gregory.clement, miquel.raynal, nadavh, stefanc, ymarkman, mw
In-Reply-To: <20180504152103.18152-1-antoine.tenart@bootlin.com>
On May 4, 2018 8:21:03 AM PDT, Antoine Tenart <antoine.tenart@bootlin.com> wrote:
>When computing the bitrate using values read from an SFP module EEPROM,
>we use the nominal BR plus BR,min and BR,max to determine the
>boundaries. But in some cases BR,min and BR,max aren't provided, which
>led the SFP code to end up having the nominal value for both the
>minimum
>and maximum bitrate values. When using a passive cable, the nominal
>value should be used as the maximum one, and there is no minimum one
>so we should use 0.
>
>Signed-off-by: Antoine Tenart <antoine.tenart@bootlin.com>
>---
>
>Hi Russell,
>
>I'm not completely sure about this patch as this case is not really
>specified in the specification. But the issue is there, and I've
>discuss
>this with others. It seemed logical (at least to us :)) to use the
>BR,nominal values as br_max and 0 as br_min when using a passive cable
>which only provides BR,nominal as this would be the highest rate at
>which the cable could work. And because it's passive, there should be
>no
>issues using it at a lower rate.
>
>I've tested this with one passive cable which only reports its
>BR,nominal (which was 10300) while it could be used when using
>1000baseX
>or 2500baseX modes.
Which SFP modules (vendor and model) exposed this out of curiosity? Russell and I already saw the Cotsworks modules having so e issues with checksums, so building a table of quirks would help. Thanks!
--
Florian
^ permalink raw reply
* Re: [PATCH net-next v2 03/13] net: phy: sfp: warn the user when no tx_disable pin is available
From: Florian Fainelli @ 2018-05-05 20:38 UTC (permalink / raw)
To: Andrew Lunn
Cc: Antoine Tenart, davem, kishon, linux, gregory.clement, jason,
sebastian.hesselbarth, netdev, linux-kernel, thomas.petazzoni,
maxime.chevallier, miquel.raynal, nadavh, stefanc, ymarkman, mw,
linux-arm-kernel
In-Reply-To: <20180504171425.GA17233@lunn.ch>
On May 4, 2018 10:14:25 AM PDT, Andrew Lunn <andrew@lunn.ch> wrote:
>On Fri, May 04, 2018 at 10:07:53AM -0700, Florian Fainelli wrote:
>> On 05/04/2018 06:56 AM, Antoine Tenart wrote:
>> > In case no Tx disable pin is available the SFP modules will always
>be
>> > emitting. This could be an issue when using modules using laser as
>their
>> > light source as we would have no way to disable it when the fiber
>is
>> > removed. This patch adds a warning when registering an SFP cage
>which do
>> > not have its tx_disable pin wired or available.
>>
>> Is this something that was done in a possibly earlier revision of a
>> given board design and which was finally fixed? Nothing wrong with
>the
>> patch, but this seems like a pretty serious board design mistake,
>that
>> needs to be addressed.
>
>Hi Florian
>
>Zii Devel B is like this. Only the "Signal Detect" pin is wired to a
>GPIO.
Good point, indeed. BTW what do you think about exposing the SFF's EEPROM and diagnostics through the standard ethtool operations even if we have to keep the description of the SFF as a fixed link in Device Tree because of the unfortunate wiring?
--
Florian
^ permalink raw reply
* Re: [PATCH net-next v2 03/13] net: phy: sfp: warn the user when no tx_disable pin is available
From: Andrew Lunn @ 2018-05-05 20:52 UTC (permalink / raw)
To: Florian Fainelli
Cc: maxime.chevallier, ymarkman, jason, Antoine Tenart, netdev,
gregory.clement, linux-kernel, linux, kishon, nadavh,
thomas.petazzoni, miquel.raynal, stefanc, mw, davem,
linux-arm-kernel, sebastian.hesselbarth
In-Reply-To: <44545AF4-64E3-4772-B5BA-43CCF2321025@gmail.com>
On Sat, May 05, 2018 at 01:38:31PM -0700, Florian Fainelli wrote:
> On May 4, 2018 10:14:25 AM PDT, Andrew Lunn <andrew@lunn.ch> wrote:
> >On Fri, May 04, 2018 at 10:07:53AM -0700, Florian Fainelli wrote:
> >> On 05/04/2018 06:56 AM, Antoine Tenart wrote:
> >> > In case no Tx disable pin is available the SFP modules will always
> >be
> >> > emitting. This could be an issue when using modules using laser as
> >their
> >> > light source as we would have no way to disable it when the fiber
> >is
> >> > removed. This patch adds a warning when registering an SFP cage
> >which do
> >> > not have its tx_disable pin wired or available.
> >>
> >> Is this something that was done in a possibly earlier revision of a
> >> given board design and which was finally fixed? Nothing wrong with
> >the
> >> patch, but this seems like a pretty serious board design mistake,
> >that
> >> needs to be addressed.
> >
> >Hi Florian
> >
> >Zii Devel B is like this. Only the "Signal Detect" pin is wired to a
> >GPIO.
>
> Good point, indeed. BTW what do you think about exposing the SFF's
> EEPROM and diagnostics through the standard ethtool operations even
> if we have to keep the description of the SFF as a fixed link in
> Device Tree because of the unfortunate wiring?
I believe in Antoine case, all the control plane is broken. He cannot
read the EEPROM, nor any of the modules pins via GPIOs.
For Zii Devel B, the EEPROM is accessible, and so is the SD pin. What
is missing is transmit disable. So i would expose it as an SFF module.
Andrew
^ permalink raw reply
* [RFC PATCH 1/3] arcnet: com20020: Add memory map of com20020
From: Andrea Greco @ 2018-05-05 21:34 UTC (permalink / raw)
To: m.grzeschik
Cc: Andrea Greco, Rob Herring, Mark Rutland, netdev, devicetree,
linux-kernel
From: Andrea Greco <a.greco@4sigma.it>
Add support for com20022I/com20020, memory mapped chip version.
Support bus: Intel 80xx and Motorola 68xx.
Bus size: Only 8 bit bus size is supported.
Added related device tree bindings
Signed-off-by: Andrea Greco <a.greco@4sigma.it>
---
.../devicetree/bindings/net/smsc-com20020.txt | 23 +++
drivers/net/arcnet/Kconfig | 12 +-
drivers/net/arcnet/Makefile | 1 +
drivers/net/arcnet/arcdevice.h | 27 ++-
drivers/net/arcnet/com20020-membus.c | 191 +++++++++++++++++++++
drivers/net/arcnet/com20020.c | 9 +-
6 files changed, 253 insertions(+), 10 deletions(-)
create mode 100644 Documentation/devicetree/bindings/net/smsc-com20020.txt
create mode 100644 drivers/net/arcnet/com20020-membus.c
diff --git a/Documentation/devicetree/bindings/net/smsc-com20020.txt b/Documentation/devicetree/bindings/net/smsc-com20020.txt
new file mode 100644
index 000000000000..39c5b19c55af
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/smsc-com20020.txt
@@ -0,0 +1,23 @@
+SMSC com20020, com20022I
+
+timeout: Arcnet timeout, checkout datashet
+clockp: Clock Prescaler, checkout datashet
+clockm: Clock multiplier, checkout datasheet
+
+phy-reset-gpios: Chip reset ppin
+phy-irq-gpios: Chip irq pin
+
+com20020_A@0 {
+ compatible = "smsc,com20020";
+
+ timeout = <0x3>;
+ backplane = <0x0>;
+
+ clockp = <0x0>;
+ clockm = <0x3>;
+
+ phy-reset-gpios = <&gpio3 21 GPIO_ACTIVE_LOW>;
+ phy-irq-gpios = <&gpio2 10 GPIO_ACTIVE_LOW>;
+
+ status = "okay";
+};
diff --git a/drivers/net/arcnet/Kconfig b/drivers/net/arcnet/Kconfig
index 39bd16f3f86d..d39faf45be1e 100644
--- a/drivers/net/arcnet/Kconfig
+++ b/drivers/net/arcnet/Kconfig
@@ -3,7 +3,7 @@
#
menuconfig ARCNET
- depends on NETDEVICES && (ISA || PCI || PCMCIA)
+ depends on NETDEVICES
tristate "ARCnet support"
---help---
If you have a network card of this type, say Y and check out the
@@ -129,5 +129,15 @@ config ARCNET_COM20020_CS
To compile this driver as a module, choose M here: the module will be
called com20020_cs. If unsure, say N.
+config ARCNET_COM20020_MEMORY_BUS
+ bool "Support for COM20020 on external memory"
+ depends on ARCNET_COM20020 && !(ARCNET_COM20020_PCI || ARCNET_COM20020_ISA || ARCNET_COM20020_CS)
+ help
+ Say Y here if on your custom board mount com20020 or friends.
+
+ Com20022I support arcnet bus 10Mbitps.
+ This driver support only 8bit, and DMA is not supported is attached on your board at external interface bus.
+ Supported bus Intel80xx / Motorola 68xx.
+ This driver not work with other com20020 module: PCI or PCMCIA compiled as [M].
endif # ARCNET
diff --git a/drivers/net/arcnet/Makefile b/drivers/net/arcnet/Makefile
index 53525e8ea130..19425c1e06f4 100644
--- a/drivers/net/arcnet/Makefile
+++ b/drivers/net/arcnet/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_ARCNET_COM20020) += com20020.o
obj-$(CONFIG_ARCNET_COM20020_ISA) += com20020-isa.o
obj-$(CONFIG_ARCNET_COM20020_PCI) += com20020-pci.o
obj-$(CONFIG_ARCNET_COM20020_CS) += com20020_cs.o
+obj-$(CONFIG_ARCNET_COM20020_MEMORY_BUS) += com20020-membus.o
diff --git a/drivers/net/arcnet/arcdevice.h b/drivers/net/arcnet/arcdevice.h
index d09b2b46ab63..16c608269cca 100644
--- a/drivers/net/arcnet/arcdevice.h
+++ b/drivers/net/arcnet/arcdevice.h
@@ -201,7 +201,7 @@ struct ArcProto {
void (*rx)(struct net_device *dev, int bufnum,
struct archdr *pkthdr, int length);
int (*build_header)(struct sk_buff *skb, struct net_device *dev,
- unsigned short ethproto, uint8_t daddr);
+ unsigned short ethproto, uint8_t daddr);
/* these functions return '1' if the skb can now be freed */
int (*prepare_tx)(struct net_device *dev, struct archdr *pkt,
@@ -326,9 +326,9 @@ struct arcnet_local {
void (*recontrigger) (struct net_device * dev, int enable);
void (*copy_to_card)(struct net_device *dev, int bufnum,
- int offset, void *buf, int count);
+ int offset, void *buf, int count);
void (*copy_from_card)(struct net_device *dev, int bufnum,
- int offset, void *buf, int count);
+ int offset, void *buf, int count);
} hw;
void __iomem *mem_start; /* pointer to ioremap'ed MMIO */
@@ -360,7 +360,7 @@ struct net_device *alloc_arcdev(const char *name);
int arcnet_open(struct net_device *dev);
int arcnet_close(struct net_device *dev);
netdev_tx_t arcnet_send_packet(struct sk_buff *skb,
- struct net_device *dev);
+ struct net_device *dev);
void arcnet_timeout(struct net_device *dev);
/* I/O equivalents */
@@ -371,7 +371,23 @@ void arcnet_timeout(struct net_device *dev);
#define BUS_ALIGN 1
#endif
-/* addr and offset allow register like names to define the actual IO address.
+#ifdef CONFIG_ARCNET_COM20020_MEMORY_BUS
+#define arcnet_inb(addr, offset) \
+ ioread8((void __iomem *)(addr) + BUS_ALIGN * (offset))
+
+#define arcnet_outb(value, addr, offset) \
+ iowrite8(value, (void __iomem *)(addr) + BUS_ALIGN * (offset))
+
+#define arcnet_insb(addr, offset, buffer, count) \
+ ioread8_rep((void __iomem *) \
+ (addr) + BUS_ALIGN * (offset), buffer, count)
+
+#define arcnet_outsb(addr, offset, buffer, count) \
+ iowrite8_rep((void __iomem *) \
+ (addr) + BUS_ALIGN * (offset), buffer, count)
+#else
+/**
+ * addr and offset allow register like names to define the actual IO address.
* A configuration option multiplies the offset for alignment.
*/
#define arcnet_inb(addr, offset) \
@@ -388,6 +404,7 @@ void arcnet_timeout(struct net_device *dev);
readb((addr) + (offset))
#define arcnet_writeb(value, addr, offset) \
writeb(value, (addr) + (offset))
+#endif
#endif /* __KERNEL__ */
#endif /* _LINUX_ARCDEVICE_H */
diff --git a/drivers/net/arcnet/com20020-membus.c b/drivers/net/arcnet/com20020-membus.c
new file mode 100644
index 000000000000..6e4a2f3a84f7
--- /dev/null
+++ b/drivers/net/arcnet/com20020-membus.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/* Linux ARCnet driver for com 20020.
+ *
+ * This datasheet:
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/200223vrevc.pdf
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/20020.pdf
+ *
+ * This driver support:
+ * - com20020,
+ * - com20022
+ * - com20022I-3v3
+ *
+ * This driver support only, 8bit read and write.
+ * DMA is not supported by this driver.
+ */
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
+#include <linux/sizes.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/random.h>
+
+#include <linux/delay.h>
+#include "arcdevice.h"
+#include "com20020.h"
+
+#define VERSION "arcnet: COM20020 MEMORY BUS support loaded.\n"
+
+static int com20020_probe(struct platform_device *pdev)
+{
+ struct device_node *np;
+ struct net_device *dev;
+ struct arcnet_local *lp;
+ struct resource res, *iores;
+ int ret, phy_reset, err;
+ u32 timeout, backplane, clockp, clockm;
+ void __iomem *ioaddr;
+
+ np = pdev->dev.of_node;
+
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (of_address_to_resource(np, 0, &res))
+ return -EINVAL;
+
+ ret = of_property_read_u32(np, "timeout", &timeout);
+ if (ret) {
+ dev_err(&pdev->dev, "timeout is required param");
+ return ret;
+ }
+
+ ret = of_property_read_u32(np, "backplane", &backplane);
+ if (ret) {
+ dev_err(&pdev->dev, "backplane is required param");
+ return ret;
+ }
+
+ ret = of_property_read_u32(np, "clockp", &clockp);
+ if (ret) {
+ dev_err(&pdev->dev, "clockp is required param");
+ return ret;
+ }
+
+ ret = of_property_read_u32(np, "clockm", &clockm);
+ if (ret) {
+ dev_err(&pdev->dev, "clockm is required param");
+ return ret;
+ }
+
+ phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0);
+ if (phy_reset == -EPROBE_DEFER) {
+ return phy_reset;
+ } else if (!gpio_is_valid(phy_reset)) {
+ dev_err(&pdev->dev, "phy-reset-gpios not valid !");
+ return 0;
+ }
+
+ err = devm_gpio_request_one(&pdev->dev, phy_reset, GPIOF_OUT_INIT_LOW,
+ "arcnet-phy-reset");
+ if (err) {
+ dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err);
+ return err;
+ }
+
+ dev = alloc_arcdev(NULL);// Let autoassign name arc%d
+ dev->netdev_ops = &com20020_netdev_ops;
+ lp = netdev_priv(dev);
+
+ lp->card_flags = ARC_CAN_10MBIT;/* pretend all of them can 10Mbit */
+
+ // Force address to 0
+ // Will be set by user with `ip set dev arc0 address YOUR_NODE_ID`
+ dev->dev_addr[0] = 0;
+
+ // request to system this memory region
+ if (!devm_request_mem_region(&pdev->dev, res.start, resource_size(&res),
+ lp->card_name))
+ return -EBUSY;
+
+ ioaddr = devm_ioremap(&pdev->dev, iores->start, resource_size(iores));
+ if (!ioaddr) {
+ dev_err(&pdev->dev, "ioremap fallied\n");
+ return -ENOMEM;
+ }
+
+ // Reset time is 5 * xTalFreq, minimal xtal is 10Mhz
+ // (5 * 1000) / 10Mhz = 500ns
+
+ gpio_set_value_cansleep(phy_reset, 0);
+ ndelay(500);
+ gpio_set_value_cansleep(phy_reset, 1);
+ ndelay(500);
+
+ /* Dummy access after Reset
+ * ARCNET controller needs
+ * this access to detect bustype
+ */
+ arcnet_outb(0x00, ioaddr, COM20020_REG_W_COMMAND);
+ arcnet_inb(ioaddr, COM20020_REG_R_DIAGSTAT);
+
+ dev->base_addr = (unsigned long)ioaddr;
+ get_random_bytes(dev->dev_addr, sizeof(u8));
+
+ dev->irq = of_get_named_gpio(np, "phy-irq-gpios", 0);
+ if (dev->irq == -EPROBE_DEFER) {
+ return dev->irq;
+ } else if (!gpio_is_valid(dev->irq)) {
+ dev_err(&pdev->dev, "phy-irq-gpios not valid !");
+ return 0;
+ }
+ dev->irq = gpio_to_irq(dev->irq);
+
+ lp->backplane = backplane;
+ lp->clockp = clockp & 7;
+ lp->clockm = clockm & 3;
+ lp->timeout = timeout;
+ lp->hw.owner = THIS_MODULE;
+
+ if (arcnet_inb(ioaddr, COM20020_REG_R_STATUS) == 0xFF) {
+ ret = -EIO;
+ goto err_release_mem;
+ }
+
+ if (com20020_check(dev)) {
+ ret = -EIO;
+ goto err_release_mem;
+ }
+
+ ret = com20020_found(dev, IRQF_TRIGGER_FALLING);
+ if (ret)
+ goto err_release_mem;
+
+ dev_dbg(&pdev->dev, "probe Done\n");
+ return 0;
+
+err_release_mem:
+ devm_iounmap(&pdev->dev, (void __iomem *)ioaddr);
+ devm_release_mem_region(&pdev->dev, res.start, resource_size(&res));
+ dev_err(&pdev->dev, "probe failed!\n");
+ return ret;
+}
+
+static const struct of_device_id of_com20020_match[] = {
+ { .compatible = "smsc,com20020", },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, of_com20020_match);
+
+static struct platform_driver of_com20020_driver = {
+ .driver = {
+ .name = "com20020-memory-bus",
+ .of_match_table = of_com20020_match,
+ },
+ .probe = com20020_probe,
+};
+
+static int com20020_init(void)
+{
+ return platform_driver_register(&of_com20020_driver);
+}
+late_initcall(com20020_init);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/arcnet/com20020.c b/drivers/net/arcnet/com20020.c
index 78043a9c5981..f09ea77dd6a8 100644
--- a/drivers/net/arcnet/com20020.c
+++ b/drivers/net/arcnet/com20020.c
@@ -43,7 +43,7 @@
#include "com20020.h"
static const char * const clockrates[] = {
- "XXXXXXX", "XXXXXXXX", "XXXXXX", "2.5 Mb/s",
+ "10 Mb/s", "XXXXXXXX", "XXXXXX", "2.5 Mb/s",
"1.25Mb/s", "625 Kb/s", "312.5 Kb/s", "156.25 Kb/s",
"Reserved", "Reserved", "Reserved"
};
@@ -391,9 +391,10 @@ static void com20020_set_mc_list(struct net_device *dev)
}
}
-#if defined(CONFIG_ARCNET_COM20020_PCI_MODULE) || \
- defined(CONFIG_ARCNET_COM20020_ISA_MODULE) || \
- defined(CONFIG_ARCNET_COM20020_CS_MODULE)
+#if defined(CONFIG_ARCNET_COM20020_PCI_MODULE) || \
+ defined(CONFIG_ARCNET_COM20020_ISA_MODULE) || \
+ defined(CONFIG_ARCNET_COM20020_CS_MODULE) || \
+ defined(CONFIG_ARCNET_COM20020_MEMORY_BUS)
EXPORT_SYMBOL(com20020_check);
EXPORT_SYMBOL(com20020_found);
EXPORT_SYMBOL(com20020_netdev_ops);
--
2.14.3
^ permalink raw reply related
* [RFC PATCH 3/3] arcnet: com20020: Add ethtool support
From: Andrea Greco @ 2018-05-05 21:35 UTC (permalink / raw)
To: m.grzeschik; +Cc: Andrea Greco, netdev, linux-kernel
From: Andrea Greco <a.greco@4sigma.it>
Setup ethtols for export com20020 diag register
Signed-off-by: Andrea Greco <a.greco@4sigma.it>
---
drivers/net/arcnet/com20020-isa.c | 1 +
drivers/net/arcnet/com20020-membus.c | 1 +
drivers/net/arcnet/com20020.c | 29 +++++++++++++++++++++++++++++
drivers/net/arcnet/com20020.h | 1 +
drivers/net/arcnet/com20020_cs.c | 1 +
include/uapi/linux/if_arcnet.h | 6 ++++++
6 files changed, 39 insertions(+)
diff --git a/drivers/net/arcnet/com20020-isa.c b/drivers/net/arcnet/com20020-isa.c
index 38fa60ddaf2e..44ab6dcccb58 100644
--- a/drivers/net/arcnet/com20020-isa.c
+++ b/drivers/net/arcnet/com20020-isa.c
@@ -154,6 +154,7 @@ static int __init com20020_init(void)
dev->dev_addr[0] = node;
dev->netdev_ops = &com20020_netdev_ops;
+ dev->ethtool_ops = &com20020_ethtool_ops;
lp = netdev_priv(dev);
lp->backplane = backplane;
diff --git a/drivers/net/arcnet/com20020-membus.c b/drivers/net/arcnet/com20020-membus.c
index 6e4a2f3a84f7..9eead734a3cf 100644
--- a/drivers/net/arcnet/com20020-membus.c
+++ b/drivers/net/arcnet/com20020-membus.c
@@ -91,6 +91,7 @@ static int com20020_probe(struct platform_device *pdev)
dev = alloc_arcdev(NULL);// Let autoassign name arc%d
dev->netdev_ops = &com20020_netdev_ops;
+ dev->ethtool_ops = &com20020_ethtool_ops;
lp = netdev_priv(dev);
lp->card_flags = ARC_CAN_10MBIT;/* pretend all of them can 10Mbit */
diff --git a/drivers/net/arcnet/com20020.c b/drivers/net/arcnet/com20020.c
index abd32ed8ec9b..2089b45e81c8 100644
--- a/drivers/net/arcnet/com20020.c
+++ b/drivers/net/arcnet/com20020.c
@@ -201,6 +201,34 @@ const struct net_device_ops com20020_netdev_ops = {
.ndo_set_rx_mode = com20020_set_mc_list,
};
+static int com20020_ethtool_regs_len(struct net_device *netdev)
+{
+ return sizeof(struct com20020_ethtool_regs);
+}
+
+static void com20020_ethtool_regs_read(struct net_device *dev,
+ struct ethtool_regs *regs, void *p)
+{
+ struct arcnet_local *lp;
+ struct com20020_ethtool_regs *com_reg;
+
+ lp = netdev_priv(dev);
+ memset(p, 0, sizeof(struct com20020_ethtool_regs));
+
+ regs->version = 1;
+
+ com_reg = p;
+
+ com_reg->status = lp->hw.status(dev) & 0xFF;
+ com_reg->diag_register = (lp->hw.status(dev) >> 8) & 0xFF;
+ com_reg->reconf_count = lp->num_recons;
+}
+
+const struct ethtool_ops com20020_ethtool_ops = {
+ .get_regs = com20020_ethtool_regs_read,
+ .get_regs_len = com20020_ethtool_regs_len,
+};
+
/* Set up the struct net_device associated with this card. Called after
* probing succeeds.
*/
@@ -402,6 +430,7 @@ static void com20020_set_mc_list(struct net_device *dev)
EXPORT_SYMBOL(com20020_check);
EXPORT_SYMBOL(com20020_found);
EXPORT_SYMBOL(com20020_netdev_ops);
+EXPORT_SYMBOL(com20020_ethtool_ops);
#endif
MODULE_LICENSE("GPL");
diff --git a/drivers/net/arcnet/com20020.h b/drivers/net/arcnet/com20020.h
index 0bcc5d0a6903..a1024c8f8a1f 100644
--- a/drivers/net/arcnet/com20020.h
+++ b/drivers/net/arcnet/com20020.h
@@ -31,6 +31,7 @@
int com20020_check(struct net_device *dev);
int com20020_found(struct net_device *dev, int shared);
extern const struct net_device_ops com20020_netdev_ops;
+extern const struct ethtool_ops com20020_ethtool_ops;
/* The number of low I/O ports used by the card. */
#define ARCNET_TOTAL_SIZE 8
diff --git a/drivers/net/arcnet/com20020_cs.c b/drivers/net/arcnet/com20020_cs.c
index cf607ffcf358..ae64f436fd54 100644
--- a/drivers/net/arcnet/com20020_cs.c
+++ b/drivers/net/arcnet/com20020_cs.c
@@ -233,6 +233,7 @@ static int com20020_config(struct pcmcia_device *link)
}
dev->irq = link->irq;
+ dev->ethtool_ops = &com20020_ethtool_ops;
ret = pcmcia_enable_device(link);
if (ret)
diff --git a/include/uapi/linux/if_arcnet.h b/include/uapi/linux/if_arcnet.h
index 683878036d76..790c0fa7386d 100644
--- a/include/uapi/linux/if_arcnet.h
+++ b/include/uapi/linux/if_arcnet.h
@@ -127,4 +127,10 @@ struct archdr {
} soft;
};
+struct com20020_ethtool_regs {
+ __u8 status;
+ __u8 diag_register;
+ __u32 reconf_count;
+};
+
#endif /* _LINUX_IF_ARCNET_H */
--
2.14.3
^ permalink raw reply related
* Re: [PATCH 2/8] rhashtable: remove nulls_base and related code.
From: NeilBrown @ 2018-05-05 21:37 UTC (permalink / raw)
To: Herbert Xu; +Cc: Thomas Graf, netdev, linux-kernel
In-Reply-To: <20180505091208.tnsxi6hdpjn456yz@gondor.apana.org.au>
[-- Attachment #1: Type: text/plain, Size: 2289 bytes --]
On Sat, May 05 2018, Herbert Xu wrote:
> On Fri, May 04, 2018 at 01:54:14PM +1000, NeilBrown wrote:
>> This "feature" is unused, undocumented, and untested and so
>> doesn't really belong. If a use for the nulls marker
>> is found, all this code would need to be reviewed to
>> ensure it works as required. It would be just as easy to
>> just add the code if/when it is needed instead.
>>
>> This patch actually fixes a bug too. The table resizing allows a
>> table to grow to 2^31 buckets, but the hash is truncated to 27 bits -
>> any growth beyond 2^27 is wasteful an ineffective.
>>
>> This patch result in NULLS_MARKER(0) being used for all chains,
>> and leave the use of rht_is_a_null() to test for it.
>>
>> Signed-off-by: NeilBrown <neilb@suse.com>
>
> I disagree. This is a fundamental requirement for the use of
> rhashtable in certain networking systems such as TCP/UDP. So
> we know that there will be a use for this.
I can see no evidence that this is required for anything, as it isn't
use and I'm fairly sure that in it's current form - it cannot be used.
Based on my best guess about how you might intend to use it, I suspect
it would be simpler to store the address of the bucket head in the nuls
rather than the hash and a magic number. This would make it just as
easy to detect when a search reaches the end of the wrong chain, which I
presume is the purpose.
I would find that useful myself - if the search would repeat when that
happened - as I could then use SLAB_TYPESAFE_BY_RCU.
Were we to take this approach, all the code I've removed here would
still need to be removed.
>
> As to the bug fix, please separate it out of the patch and resubmit.
I don't know how to do that. I don't know what is safe to change
without "breaking" the nulls_base code because that code is undocumented and
unused, so unmaintainable.
In general the kernel has, I believe, a policy against keeping unused
interfaces. While that isn't firm and universal, is seems to apply
particularly well to unusable interfaces.
Thanks,
NeilBrown
>
> Thanks,
> --
> Email: Herbert Xu <herbert@gondor.apana.org.au>
> Home Page: http://gondor.apana.org.au/~herbert/
> PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]
^ permalink raw reply
* [RFC PATCH 2/3] arcnet: com20020: Fixup missing SLOWARB bit
From: Andrea Greco @ 2018-05-05 21:37 UTC (permalink / raw)
To: m.grzeschik; +Cc: Andrea Greco, netdev, linux-kernel
From: Andrea Greco <a.greco@4sigma.it>
If com20020 clock is major of 40Mhz SLOWARB bit is requested.
Signed-off-by: Andrea Greco <a.greco@4sigma.it>
---
drivers/net/arcnet/com20020.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/net/arcnet/com20020.c b/drivers/net/arcnet/com20020.c
index f09ea77dd6a8..abd32ed8ec9b 100644
--- a/drivers/net/arcnet/com20020.c
+++ b/drivers/net/arcnet/com20020.c
@@ -102,6 +102,10 @@ int com20020_check(struct net_device *dev)
lp->setup = lp->clockm ? 0 : (lp->clockp << 1);
lp->setup2 = (lp->clockm << 4) | 8;
+ // If clock is major of 40Mhz, SLOWARB bit must be set
+ if (lp->clockm > 1)
+ lp->setup2 |= SLOWARB;
+
/* CHECK: should we do this for SOHARD cards ? */
/* Enable P1Mode for backplane mode */
lp->setup = lp->setup | P1MODE;
--
2.14.3
^ permalink raw reply related
* Re: [PATCH 3/8] rhashtable: use cmpxchg() to protect ->future_tbl.
From: NeilBrown @ 2018-05-05 21:45 UTC (permalink / raw)
To: Herbert Xu; +Cc: Thomas Graf, netdev, linux-kernel
In-Reply-To: <20180505092725.rlwn77d3yhknspdw@gondor.apana.org.au>
[-- Attachment #1: Type: text/plain, Size: 1169 bytes --]
On Sat, May 05 2018, Herbert Xu wrote:
> On Fri, May 04, 2018 at 01:54:14PM +1000, NeilBrown wrote:
>> Rather than borrowing one of the bucket locks to
>> protect ->future_tbl updates, use cmpxchg().
>> This gives more freedom to change how bucket locking
>> is implemented.
>>
>> Signed-off-by: NeilBrown <neilb@suse.com>
>
> This looks nice.
>
>> - spin_unlock_bh(old_tbl->locks);
>> + rcu_assign_pointer(tmp, new_tbl);
>
> Do we need this barrier since cmpxchg is supposed to provide memory
> barrier semantics?
It's hard to find documentation even for what cmpxchg() is meant do, let
alone what barriers is provides, but there does seem to be something
hidden in Documentation/core-api/atomic_ops.rst which suggests full
barrier semantics if the comparison succeeds. I'll replace the
rcu_assign_pointer with a comment saying why it isn't needed.
Thanks,
NeilBrown
>
>> + if (cmpxchg(&old_tbl->future_tbl, NULL, tmp) != NULL)
>> + return -EEXIST;
>
> Thanks,
> --
> Email: Herbert Xu <herbert@gondor.apana.org.au>
> Home Page: http://gondor.apana.org.au/~herbert/
> PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]
^ permalink raw reply
* Re: [PATCH 4/8] rhashtable: fix race in nested_table_alloc()
From: NeilBrown @ 2018-05-05 21:48 UTC (permalink / raw)
To: Herbert Xu; +Cc: Thomas Graf, netdev, linux-kernel
In-Reply-To: <20180505092907.2qa3scf6bzvubmtt@gondor.apana.org.au>
[-- Attachment #1: Type: text/plain, Size: 948 bytes --]
On Sat, May 05 2018, Herbert Xu wrote:
> On Fri, May 04, 2018 at 01:54:14PM +1000, NeilBrown wrote:
>> If two threads run nested_table_alloc() at the same time
>> they could both allocate a new table.
>> Best case is that one of them will never be freed, leaking memory.
>> Worst case is hat entry get stored there before it leaks,
>> and the are lost from the table.
>>
>> So use cmpxchg to detect the race and free the unused table.
>>
>> Fixes: da20420f83ea ("rhashtable: Add nested tables")
>> Cc: stable@vger.kernel.org # 4.11+
>> Signed-off-by: NeilBrown <neilb@suse.com>
>
> What about the spinlock that's meant to be held around this
> operation?
The spinlock protects 2 or more buckets. The nested table contains at
least 512 buckets, maybe more.
It is quite possible for two insertions into 2 different buckets to both
get their spinlock and both try to instantiate the same nested table.
Thanks,
NeilBrown
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]
^ permalink raw reply
* Re: [PATCH 1/8] rhashtable: silence RCU warning in rhashtable_test.
From: NeilBrown @ 2018-05-05 21:49 UTC (permalink / raw)
To: Herbert Xu; +Cc: Thomas Graf, netdev, linux-kernel
In-Reply-To: <20180505091046.bd74tk75oieaasi5@gondor.apana.org.au>
[-- Attachment #1: Type: text/plain, Size: 847 bytes --]
On Sat, May 05 2018, Herbert Xu wrote:
> On Fri, May 04, 2018 at 01:54:14PM +1000, NeilBrown wrote:
>> print_ht in rhashtable_test calls rht_dereference() with neither
>> RCU protection or the mutex. This triggers an RCU warning.
>> So take the mutex to silence the warning.
>>
>> Signed-off-by: NeilBrown <neilb@suse.com>
>
> I don't think the mutex is actually needed but since we don't
> have rht_dereference_raw and this is just test code:
I agrees there is no functional need for the mutex. It just needed to
silence the warning, so that other warnings are easier to see.
>
> Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
thanks,
NeilBrown
> --
> Email: Herbert Xu <herbert@gondor.apana.org.au>
> Home Page: http://gondor.apana.org.au/~herbert/
> PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]
^ permalink raw reply
* Re: [PATCH 6/8] rhashtable: further improve stability of rhashtable_walk
From: NeilBrown @ 2018-05-05 21:50 UTC (permalink / raw)
To: Herbert Xu; +Cc: Thomas Graf, netdev, linux-kernel
In-Reply-To: <20180505094212.hpqjyv5ijibviuwh@gondor.apana.org.au>
[-- Attachment #1: Type: text/plain, Size: 2307 bytes --]
On Sat, May 05 2018, Herbert Xu wrote:
> On Fri, May 04, 2018 at 01:54:14PM +1000, NeilBrown wrote:
>> If the sequence:
>> obj = rhashtable_walk_next(iter);
>> rhashtable_walk_stop(iter);
>> rhashtable_remove_fast(ht, &obj->head, params);
>> rhashtable_walk_start(iter);
>>
>> races with another thread inserting or removing
>> an object on the same hash chain, a subsequent
>> rhashtable_walk_next() is not guaranteed to get the "next"
>> object. It is possible that an object could be
>> repeated, or missed.
>>
>> This can be made more reliable by keeping the objects in a hash chain
>> sorted by memory address. A subsequent rhashtable_walk_next()
>> call can reliably find the correct position in the list, and thus
>> find the 'next' object.
>>
>> It is not possible (certainly not so easy) to achieve this with an
>> rhltable as keeping the hash chain in order is not so easy. When the
>> first object with a given key is removed, it is replaced in the chain
>> with the next object with the same key, and the address of that
>> object may not be correctly ordered.
>> No current user of rhltable_walk_enter() calls
>> rhashtable_walk_start() more than once, so no current code
>> could benefit from a more reliable walk of rhltables.
>>
>> This patch only attempts to improve walks for rhashtables.
>> - a new object is always inserted after the last object with a
>> smaller address, or at the start
>> - when rhashtable_walk_start() is called, it records that 'p' is not
>> 'safe', meaning that it cannot be dereferenced. The revalidation
>> that was previously done here is moved to rhashtable_walk_next()
>> - when rhashtable_walk_next() is called while p is not NULL and not
>> safe, it walks the chain looking for the first object with an
>> address greater than p and returns that. If there is none, it moves
>> to the next hash chain.
>>
>> Signed-off-by: NeilBrown <neilb@suse.com>
>
> I'm a bit torn on this. On the hand this is definitely an improvement
> over the status quo. On the other this does not work on rhltable and
> we do have a way of fixing it for both rhashtable and rhltable.
Do we? How could we fix it for both rhashtable and rhltable?
Thanks,
NeilBrown
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]
^ permalink raw reply
* Re: [PATCH 8/8] rhashtable: don't hold lock on first table throughout insertion.
From: NeilBrown @ 2018-05-05 22:00 UTC (permalink / raw)
To: Herbert Xu; +Cc: Thomas Graf, netdev, linux-kernel
In-Reply-To: <20180505094117.pl7b6bbk6mtyri6d@gondor.apana.org.au>
[-- Attachment #1: Type: text/plain, Size: 2302 bytes --]
On Sat, May 05 2018, Herbert Xu wrote:
> On Fri, May 04, 2018 at 01:54:14PM +1000, NeilBrown wrote:
>> rhashtable_try_insert() currently hold a lock on the bucket in
>> the first table, while also locking buckets in subsequent tables.
>> This is unnecessary and looks like a hold-over from some earlier
>> version of the implementation.
>>
>> As insert and remove always lock a bucket in each table in turn, and
>> as insert only inserts in the final table, there cannot be any races
>> that are not covered by simply locking a bucket in each table in turn.
>>
>> When an insert call reaches that last table it can be sure that there
>> is no match entry in any other table as it has searched them all, and
>> insertion never happens anywhere but in the last table. The fact that
>> code tests for the existence of future_tbl while holding a lock on
>> the relevant bucket ensures that two threads inserting the same key
>> will make compatible decisions about which is the "last" table.
>>
>> This simplifies the code and allows the ->rehash field to be
>> discarded.
>> We still need a way to ensure that a dead bucket_table is never
>> re-linked by rhashtable_walk_stop(). This can be achieved by
>> setting the ->size to 1. This still allows lookup code to work (and
>> correctly not find anything) but can never happen on an active bucket
>> table (as the minimum size is 4).
>>
>> Signed-off-by: NeilBrown <neilb@suse.com>
>
> I'm not convinced this is safe. There can be multiple tables
> in existence. Unless you hold the lock on the first table, what
> is to stop two parallel inserters each from inserting into their
> "last" tables which may not be the same table?
The insert function must (and does) take the lock on the bucket before
testing if there is a "next" table.
If one inserter finds that it has locked the "last" table (because there
is no next) and successfully inserts, then the other inserter cannot
have locked that table yet, else it would have inserted. When it does,
it will find what the first inserter inserted.
Thanks,
NeilBrown
>
> Cheers,
> --
> Email: Herbert Xu <herbert@gondor.apana.org.au>
> Home Page: http://gondor.apana.org.au/~herbert/
> PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]
^ permalink raw reply
* Re: WARNING in kernfs_add_one
From: Greg KH @ 2018-05-05 22:07 UTC (permalink / raw)
To: linux-wireless, Eric Dumazet
Cc: netdev, syzbot, linux-kernel, syzkaller-bugs, tj
In-Reply-To: <095bc684-0029-0b2e-5e28-23376174ca02@gmail.com>
On Sat, May 05, 2018 at 10:43:45AM -0700, Eric Dumazet wrote:
>
>
> On 05/05/2018 09:40 AM, Greg KH wrote:
> > On Sat, May 05, 2018 at 08:47:02AM -0700, syzbot wrote:
> >> Hello,
> >>
> >> syzbot found the following crash on:
> >>
> >> HEAD commit: 8fb11a9a8d51 net/ipv6: rename rt6_next to fib6_next
> >> git tree: net-next
> >> console output: https://syzkaller.appspot.com/x/log.txt?x=14b27237800000
> >> kernel config: https://syzkaller.appspot.com/x/.config?x=c416c61f3cd96be
> >> dashboard link: https://syzkaller.appspot.com/bug?extid=df47f81c226b31d89fb1
> >> compiler: gcc (GCC) 8.0.1 20180413 (experimental)
> >> syzkaller repro:https://syzkaller.appspot.com/x/repro.syz?x=172fb3e7800000
> >> C reproducer: https://syzkaller.appspot.com/x/repro.c?x=16552e57800000
> >>
> >> IMPORTANT: if you fix the bug, please add the following tag to the commit:
> >> Reported-by: syzbot+df47f81c226b31d89fb1@syzkaller.appspotmail.com
> >>
> >> RBP: 00007fff808f3e10 R08: 0000000000000002 R09: 00007fff80003534
> >> R10: 0000000000000000 R11: 0000000000000246 R12: ffffffffffffffff
> >> R13: 0000000000000006 R14: 0000000000000000 R15: 0000000000000000
> >> ------------[ cut here ]------------
> >> kernfs: ns required in 'ieee80211' for 'phy3'
> >
> > That's interesting, this looks like a netfilter bug (adding netdev to
> > the report here.)
>
>
> I do not see anything netfilter related here.
>
> More likely wireless territory
Ugh, that's what I get for writing emails before coffee in the
morning...
Yes, you are right, this looks like a wireless issue.
Now cc: linux-wireless.
> > Yes, we can "tone down" the kernfs warning to just be an error message
> > in the log, but there might be something worse going on here.
> >
> > Network developers, any idea? Rest of the callback chain is here:
> >
> >
> >> WARNING: CPU: 0 PID: 4538 at fs/kernfs/dir.c:759 kernfs_add_one+0x406/0x4d0
> >> fs/kernfs/dir.c:758
> >> Kernel panic - not syncing: panic_on_warn set ...
> >>
> >> CPU: 0 PID: 4538 Comm: syz-executor486 Not tainted 4.17.0-rc3+ #33
> >> Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS
> >> Google 01/01/2011
> >> Call Trace:
> >> __dump_stack lib/dump_stack.c:77 [inline]
> >> dump_stack+0x1b9/0x294 lib/dump_stack.c:113
> >> panic+0x22f/0x4de kernel/panic.c:184
> >> __warn.cold.8+0x163/0x1b3 kernel/panic.c:536
> >> report_bug+0x252/0x2d0 lib/bug.c:186
> >> fixup_bug arch/x86/kernel/traps.c:178 [inline]
> >> do_error_trap+0x1de/0x490 arch/x86/kernel/traps.c:296
> >> do_invalid_op+0x1b/0x20 arch/x86/kernel/traps.c:315
> >> invalid_op+0x14/0x20 arch/x86/entry/entry_64.S:992
> >> RIP: 0010:kernfs_add_one+0x406/0x4d0 fs/kernfs/dir.c:758
> >> RSP: 0018:ffff8801ca9eece0 EFLAGS: 00010286
> >> RAX: 000000000000002d RBX: ffffffff87d5cee0 RCX: ffffffff8160ba7d
> >> RDX: 0000000000000000 RSI: ffffffff81610731 RDI: ffff8801ca9ee840
> >> RBP: ffff8801ca9eed20 R08: ffff8801d9538500 R09: 0000000000000006
> >> R10: ffff8801d9538500 R11: 0000000000000000 R12: ffff8801ad1cb6c0
> >> R13: ffffffff885da640 R14: 0000000000000020 R15: 0000000000000000
> >> kernfs_create_link+0x112/0x180 fs/kernfs/symlink.c:41
> >> sysfs_do_create_link_sd.isra.2+0x90/0x130 fs/sysfs/symlink.c:43
> >> sysfs_do_create_link fs/sysfs/symlink.c:79 [inline]
> >> sysfs_create_link+0x65/0xc0 fs/sysfs/symlink.c:91
> >> device_add_class_symlinks drivers/base/core.c:1612 [inline]
> >> device_add+0x7a0/0x16d0 drivers/base/core.c:1810
> >> wiphy_register+0x178a/0x2430 net/wireless/core.c:806
> >> ieee80211_register_hw+0x13cd/0x35d0 net/mac80211/main.c:1047
> >> mac80211_hwsim_new_radio+0x1d9b/0x3410
> >> drivers/net/wireless/mac80211_hwsim.c:2772
> >> hwsim_new_radio_nl+0x7a7/0xa60 drivers/net/wireless/mac80211_hwsim.c:3246
> >> genl_family_rcv_msg+0x889/0x1120 net/netlink/genetlink.c:599
> >> genl_rcv_msg+0xc6/0x170 net/netlink/genetlink.c:624
> >> netlink_rcv_skb+0x172/0x440 net/netlink/af_netlink.c:2448
> >> genl_rcv+0x28/0x40 net/netlink/genetlink.c:635
> >> netlink_unicast_kernel net/netlink/af_netlink.c:1310 [inline]
> >> netlink_unicast+0x58b/0x740 net/netlink/af_netlink.c:1336
> >> netlink_sendmsg+0x9f0/0xfa0 net/netlink/af_netlink.c:1901
> >> sock_sendmsg_nosec net/socket.c:629 [inline]
> >> sock_sendmsg+0xd5/0x120 net/socket.c:639
> >> ___sys_sendmsg+0x805/0x940 net/socket.c:2117
> >> __sys_sendmsg+0x115/0x270 net/socket.c:2155
> >> __do_sys_sendmsg net/socket.c:2164 [inline]
> >> __se_sys_sendmsg net/socket.c:2162 [inline]
> >> __x64_sys_sendmsg+0x78/0xb0 net/socket.c:2162
> >> do_syscall_64+0x1b1/0x800 arch/x86/entry/common.c:287
> >> entry_SYSCALL_64_after_hwframe+0x49/0xbe
> >> RIP: 0033:0x4404c9
> >> RSP: 002b:00007fff808f3e08 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
> >> RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00000000004404c9
> >> RDX: 0000000000000000 RSI: 0000000020b3dfc8 RDI: 0000000000000005
> >> RBP: 00007fff808f3e10 R08: 0000000000000002 R09: 00007fff80003534
> >> R10: 0000000000000000 R11: 0000000000000246 R12: ffffffffffffffff
> >> R13: 0000000000000006 R14: 0000000000000000 R15: 0000000000000000
> >> Dumping ftrace buffer:
> >> (ftrace buffer empty)
> >> Kernel Offset: disabled
> >> Rebooting in 86400 seconds..
> >
> >
Any ideas?
thanks,
greg k-h
> >
^ permalink raw reply
* [PATCH bpf-next v5 0/4] Hash support for sock
From: John Fastabend @ 2018-05-05 23:25 UTC (permalink / raw)
To: borkmann, ast; +Cc: netdev, John Fastabend
In the original sockmap implementation we got away with using an
array similar to devmap. However, unlike devmap where an ifindex
has a nice 1:1 function into the map we have found some use cases
with sockets that need to be referenced using longer keys.
This series adds support for a sockhash map reusing as much of
the sockmap code as possible. I made the decision to add sockhash
specific helpers vs trying to generalize the existing helpers
because (a) they have sockmap in the name and (b) the keys are
different types. I prefer to be explicit here rather than play
type games or do something else tricky.
To test this we duplicate all the sockmap testing except swap out
the sockmap with a sockhash.
v2: fix file stats and add v2 tag
v3: move tool updates into test patch, move bpftool updates into
its own patch, and fixup the test patch stats to catch the
renamed file and provide only diffs +/- on that.
v4: Add documentation to UAPI bpf.h
v5: Add documentation to tools UAPI bpf.h
Just a note I pushed Dave's Acks through v4 into v5 due to small
size of change.
John Fastabend (4):
bpf: sockmap, refactor sockmap routines to work with hashmap
bpf: sockmap, add hash map support
bpf: bpftool, support for sockhash
bpf: selftest additions for SOCKHASH
include/linux/bpf.h | 8 +
include/linux/bpf_types.h | 1 +
include/linux/filter.h | 3 +-
include/net/tcp.h | 3 +-
include/uapi/linux/bpf.h | 6 +-
kernel/bpf/core.c | 1 +
kernel/bpf/sockmap.c | 638 ++++++++++++++++++---
kernel/bpf/verifier.c | 14 +-
net/core/filter.c | 89 ++-
tools/bpf/bpftool/map.c | 1 +
tools/include/uapi/linux/bpf.h | 6 +-
tools/testing/selftests/bpf/Makefile | 3 +-
tools/testing/selftests/bpf/test_sockhash_kern.c | 4 +
tools/testing/selftests/bpf/test_sockmap.c | 27 +-
.../{test_sockmap_kern.c => test_sockmap_kern.h} | 6 +-
15 files changed, 695 insertions(+), 115 deletions(-)
create mode 100644 tools/testing/selftests/bpf/test_sockhash_kern.c
rename tools/testing/selftests/bpf/{test_sockmap_kern.c => test_sockmap_kern.h} (98%)
--
1.9.1
^ permalink raw reply
* [PATCH bpf-next v5 1/4] bpf: sockmap, refactor sockmap routines to work with hashmap
From: John Fastabend @ 2018-05-05 23:25 UTC (permalink / raw)
To: borkmann, ast; +Cc: netdev, John Fastabend
In-Reply-To: <1525562710-11603-1-git-send-email-john.fastabend@gmail.com>
This patch only refactors the existing sockmap code. This will allow
much of the psock initialization code path and bpf helper codes to
work for both sockmap bpf map types that are backed by an array, the
currently supported type, and the new hash backed bpf map type
sockhash.
Most the fallout comes from three changes,
- Pushing bpf programs into an independent structure so we
can use it from the htab struct in the next patch.
- Generalizing helpers to use void *key instead of the hardcoded
u32.
- Instead of passing map/key through the metadata we now do
the lookup inline. This avoids storing the key in the metadata
which will be useful when keys can be longer than 4 bytes. We
rename the sk pointers to sk_redir at this point as well to
avoid any confusion between the current sk pointer and the
redirect pointer sk_redir.
Signed-off-by: John Fastabend <john.fastabend@gmail.com>
Acked-by: David S. Miller <davem@davemloft.net>
---
include/linux/filter.h | 3 +-
include/net/tcp.h | 3 +-
kernel/bpf/sockmap.c | 148 +++++++++++++++++++++++++++++--------------------
net/core/filter.c | 31 +++--------
4 files changed, 98 insertions(+), 87 deletions(-)
diff --git a/include/linux/filter.h b/include/linux/filter.h
index da7e165..9dbcb9d 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -515,9 +515,8 @@ struct sk_msg_buff {
int sg_end;
struct scatterlist sg_data[MAX_SKB_FRAGS];
bool sg_copy[MAX_SKB_FRAGS];
- __u32 key;
__u32 flags;
- struct bpf_map *map;
+ struct sock *sk_redir;
struct sk_buff *skb;
struct list_head list;
};
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 833154e..089185a 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -814,9 +814,8 @@ struct tcp_skb_cb {
#endif
} header; /* For incoming skbs */
struct {
- __u32 key;
__u32 flags;
- struct bpf_map *map;
+ struct sock *sk_redir;
void *data_end;
} bpf;
};
diff --git a/kernel/bpf/sockmap.c b/kernel/bpf/sockmap.c
index 801273b..4eef5b1 100644
--- a/kernel/bpf/sockmap.c
+++ b/kernel/bpf/sockmap.c
@@ -48,14 +48,18 @@
#define SOCK_CREATE_FLAG_MASK \
(BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)
-struct bpf_stab {
- struct bpf_map map;
- struct sock **sock_map;
+struct bpf_sock_progs {
struct bpf_prog *bpf_tx_msg;
struct bpf_prog *bpf_parse;
struct bpf_prog *bpf_verdict;
};
+struct bpf_stab {
+ struct bpf_map map;
+ struct sock **sock_map;
+ struct bpf_sock_progs progs;
+};
+
enum smap_psock_state {
SMAP_TX_RUNNING,
};
@@ -460,7 +464,7 @@ static int free_curr_sg(struct sock *sk, struct sk_msg_buff *md)
static int bpf_map_msg_verdict(int _rc, struct sk_msg_buff *md)
{
return ((_rc == SK_PASS) ?
- (md->map ? __SK_REDIRECT : __SK_PASS) :
+ (md->sk_redir ? __SK_REDIRECT : __SK_PASS) :
__SK_DROP);
}
@@ -1091,7 +1095,7 @@ static int smap_verdict_func(struct smap_psock *psock, struct sk_buff *skb)
* when we orphan the skb so that we don't have the possibility
* to reference a stale map.
*/
- TCP_SKB_CB(skb)->bpf.map = NULL;
+ TCP_SKB_CB(skb)->bpf.sk_redir = NULL;
skb->sk = psock->sock;
bpf_compute_data_pointers(skb);
preempt_disable();
@@ -1101,7 +1105,7 @@ static int smap_verdict_func(struct smap_psock *psock, struct sk_buff *skb)
/* Moving return codes from UAPI namespace into internal namespace */
return rc == SK_PASS ?
- (TCP_SKB_CB(skb)->bpf.map ? __SK_REDIRECT : __SK_PASS) :
+ (TCP_SKB_CB(skb)->bpf.sk_redir ? __SK_REDIRECT : __SK_PASS) :
__SK_DROP;
}
@@ -1371,7 +1375,6 @@ static int smap_init_sock(struct smap_psock *psock,
}
static void smap_init_progs(struct smap_psock *psock,
- struct bpf_stab *stab,
struct bpf_prog *verdict,
struct bpf_prog *parse)
{
@@ -1449,14 +1452,13 @@ static void smap_gc_work(struct work_struct *w)
kfree(psock);
}
-static struct smap_psock *smap_init_psock(struct sock *sock,
- struct bpf_stab *stab)
+static struct smap_psock *smap_init_psock(struct sock *sock, int node)
{
struct smap_psock *psock;
psock = kzalloc_node(sizeof(struct smap_psock),
GFP_ATOMIC | __GFP_NOWARN,
- stab->map.numa_node);
+ node);
if (!psock)
return ERR_PTR(-ENOMEM);
@@ -1661,40 +1663,26 @@ static int sock_map_delete_elem(struct bpf_map *map, void *key)
* - sock_map must use READ_ONCE and (cmp)xchg operations
* - BPF verdict/parse programs must use READ_ONCE and xchg operations
*/
-static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops,
- struct bpf_map *map,
- void *key, u64 flags)
+
+static int __sock_map_ctx_update_elem(struct bpf_map *map,
+ struct bpf_sock_progs *progs,
+ struct sock *sock,
+ struct sock **map_link,
+ void *key)
{
- struct bpf_stab *stab = container_of(map, struct bpf_stab, map);
- struct smap_psock_map_entry *e = NULL;
struct bpf_prog *verdict, *parse, *tx_msg;
- struct sock *osock, *sock;
+ struct smap_psock_map_entry *e = NULL;
struct smap_psock *psock;
- u32 i = *(u32 *)key;
bool new = false;
int err;
- if (unlikely(flags > BPF_EXIST))
- return -EINVAL;
-
- if (unlikely(i >= stab->map.max_entries))
- return -E2BIG;
-
- sock = READ_ONCE(stab->sock_map[i]);
- if (flags == BPF_EXIST && !sock)
- return -ENOENT;
- else if (flags == BPF_NOEXIST && sock)
- return -EEXIST;
-
- sock = skops->sk;
-
/* 1. If sock map has BPF programs those will be inherited by the
* sock being added. If the sock is already attached to BPF programs
* this results in an error.
*/
- verdict = READ_ONCE(stab->bpf_verdict);
- parse = READ_ONCE(stab->bpf_parse);
- tx_msg = READ_ONCE(stab->bpf_tx_msg);
+ verdict = READ_ONCE(progs->bpf_verdict);
+ parse = READ_ONCE(progs->bpf_parse);
+ tx_msg = READ_ONCE(progs->bpf_tx_msg);
if (parse && verdict) {
/* bpf prog refcnt may be zero if a concurrent attach operation
@@ -1702,11 +1690,11 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops,
* we increment the refcnt. If this is the case abort with an
* error.
*/
- verdict = bpf_prog_inc_not_zero(stab->bpf_verdict);
+ verdict = bpf_prog_inc_not_zero(progs->bpf_verdict);
if (IS_ERR(verdict))
return PTR_ERR(verdict);
- parse = bpf_prog_inc_not_zero(stab->bpf_parse);
+ parse = bpf_prog_inc_not_zero(progs->bpf_parse);
if (IS_ERR(parse)) {
bpf_prog_put(verdict);
return PTR_ERR(parse);
@@ -1714,7 +1702,7 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops,
}
if (tx_msg) {
- tx_msg = bpf_prog_inc_not_zero(stab->bpf_tx_msg);
+ tx_msg = bpf_prog_inc_not_zero(progs->bpf_tx_msg);
if (IS_ERR(tx_msg)) {
if (verdict)
bpf_prog_put(verdict);
@@ -1747,7 +1735,7 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops,
goto out_progs;
}
} else {
- psock = smap_init_psock(sock, stab);
+ psock = smap_init_psock(sock, map->numa_node);
if (IS_ERR(psock)) {
err = PTR_ERR(psock);
goto out_progs;
@@ -1762,7 +1750,6 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops,
err = -ENOMEM;
goto out_progs;
}
- e->entry = &stab->sock_map[i];
/* 3. At this point we have a reference to a valid psock that is
* running. Attach any BPF programs needed.
@@ -1779,7 +1766,7 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops,
err = smap_init_sock(psock, sock);
if (err)
goto out_free;
- smap_init_progs(psock, stab, verdict, parse);
+ smap_init_progs(psock, verdict, parse);
smap_start_sock(psock, sock);
}
@@ -1788,19 +1775,12 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops,
* it with. Because we can only have a single set of programs if
* old_sock has a strp we can stop it.
*/
- list_add_tail(&e->list, &psock->maps);
- write_unlock_bh(&sock->sk_callback_lock);
-
- osock = xchg(&stab->sock_map[i], sock);
- if (osock) {
- struct smap_psock *opsock = smap_psock_sk(osock);
-
- write_lock_bh(&osock->sk_callback_lock);
- smap_list_remove(opsock, &stab->sock_map[i]);
- smap_release_sock(opsock, osock);
- write_unlock_bh(&osock->sk_callback_lock);
+ if (map_link) {
+ e->entry = map_link;
+ list_add_tail(&e->list, &psock->maps);
}
- return 0;
+ write_unlock_bh(&sock->sk_callback_lock);
+ return err;
out_free:
smap_release_sock(psock, sock);
out_progs:
@@ -1815,23 +1795,69 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops,
return err;
}
-int sock_map_prog(struct bpf_map *map, struct bpf_prog *prog, u32 type)
+static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops,
+ struct bpf_map *map,
+ void *key, u64 flags)
{
struct bpf_stab *stab = container_of(map, struct bpf_stab, map);
+ struct bpf_sock_progs *progs = &stab->progs;
+ struct sock *osock, *sock;
+ u32 i = *(u32 *)key;
+ int err;
+
+ if (unlikely(flags > BPF_EXIST))
+ return -EINVAL;
+
+ if (unlikely(i >= stab->map.max_entries))
+ return -E2BIG;
+
+ sock = READ_ONCE(stab->sock_map[i]);
+ if (flags == BPF_EXIST && !sock)
+ return -ENOENT;
+ else if (flags == BPF_NOEXIST && sock)
+ return -EEXIST;
+
+ sock = skops->sk;
+ err = __sock_map_ctx_update_elem(map, progs, sock, &stab->sock_map[i],
+ key);
+ if (err)
+ goto out;
+
+ osock = xchg(&stab->sock_map[i], sock);
+ if (osock) {
+ struct smap_psock *opsock = smap_psock_sk(osock);
+
+ write_lock_bh(&osock->sk_callback_lock);
+ smap_list_remove(opsock, &stab->sock_map[i]);
+ smap_release_sock(opsock, osock);
+ write_unlock_bh(&osock->sk_callback_lock);
+ }
+out:
+ return 0;
+}
+
+int sock_map_prog(struct bpf_map *map, struct bpf_prog *prog, u32 type)
+{
+ struct bpf_sock_progs *progs;
struct bpf_prog *orig;
- if (unlikely(map->map_type != BPF_MAP_TYPE_SOCKMAP))
+ if (map->map_type == BPF_MAP_TYPE_SOCKMAP) {
+ struct bpf_stab *stab = container_of(map, struct bpf_stab, map);
+
+ progs = &stab->progs;
+ } else {
return -EINVAL;
+ }
switch (type) {
case BPF_SK_MSG_VERDICT:
- orig = xchg(&stab->bpf_tx_msg, prog);
+ orig = xchg(&progs->bpf_tx_msg, prog);
break;
case BPF_SK_SKB_STREAM_PARSER:
- orig = xchg(&stab->bpf_parse, prog);
+ orig = xchg(&progs->bpf_parse, prog);
break;
case BPF_SK_SKB_STREAM_VERDICT:
- orig = xchg(&stab->bpf_verdict, prog);
+ orig = xchg(&progs->bpf_verdict, prog);
break;
default:
return -EOPNOTSUPP;
@@ -1880,16 +1906,18 @@ static int sock_map_update_elem(struct bpf_map *map,
static void sock_map_release(struct bpf_map *map)
{
struct bpf_stab *stab = container_of(map, struct bpf_stab, map);
+ struct bpf_sock_progs *progs;
struct bpf_prog *orig;
- orig = xchg(&stab->bpf_parse, NULL);
+ progs = &stab->progs;
+ orig = xchg(&progs->bpf_parse, NULL);
if (orig)
bpf_prog_put(orig);
- orig = xchg(&stab->bpf_verdict, NULL);
+ orig = xchg(&progs->bpf_verdict, NULL);
if (orig)
bpf_prog_put(orig);
- orig = xchg(&stab->bpf_tx_msg, NULL);
+ orig = xchg(&progs->bpf_tx_msg, NULL);
if (orig)
bpf_prog_put(orig);
}
diff --git a/net/core/filter.c b/net/core/filter.c
index 6877426..14c6bb2 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -2079,9 +2079,10 @@ int skb_do_redirect(struct sk_buff *skb)
if (unlikely(flags & ~(BPF_F_INGRESS)))
return SK_DROP;
- tcb->bpf.key = key;
tcb->bpf.flags = flags;
- tcb->bpf.map = map;
+ tcb->bpf.sk_redir = __sock_map_lookup_elem(map, key);
+ if (!tcb->bpf.sk_redir)
+ return SK_DROP;
return SK_PASS;
}
@@ -2089,16 +2090,8 @@ int skb_do_redirect(struct sk_buff *skb)
struct sock *do_sk_redirect_map(struct sk_buff *skb)
{
struct tcp_skb_cb *tcb = TCP_SKB_CB(skb);
- struct sock *sk = NULL;
-
- if (tcb->bpf.map) {
- sk = __sock_map_lookup_elem(tcb->bpf.map, tcb->bpf.key);
- tcb->bpf.key = 0;
- tcb->bpf.map = NULL;
- }
-
- return sk;
+ return tcb->bpf.sk_redir;
}
static const struct bpf_func_proto bpf_sk_redirect_map_proto = {
@@ -2118,25 +2111,17 @@ struct sock *do_sk_redirect_map(struct sk_buff *skb)
if (unlikely(flags & ~(BPF_F_INGRESS)))
return SK_DROP;
- msg->key = key;
msg->flags = flags;
- msg->map = map;
+ msg->sk_redir = __sock_map_lookup_elem(map, key);
+ if (!msg->sk_redir)
+ return SK_DROP;
return SK_PASS;
}
struct sock *do_msg_redirect_map(struct sk_msg_buff *msg)
{
- struct sock *sk = NULL;
-
- if (msg->map) {
- sk = __sock_map_lookup_elem(msg->map, msg->key);
-
- msg->key = 0;
- msg->map = NULL;
- }
-
- return sk;
+ return msg->sk_redir;
}
static const struct bpf_func_proto bpf_msg_redirect_map_proto = {
--
1.9.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox