* Re: [PATCH net-next 00/10] udp: implement GRO support
From: David Miller @ 2018-11-07 20:03 UTC (permalink / raw)
To: willemdebruijn.kernel; +Cc: pabeni, netdev, willemb, steffen.klassert, subashab
In-Reply-To: <CAF=yD-JG2wsRgAMW7Vv10yQdSDPGAg0akDJwDY1t+ySwQC-L9A@mail.gmail.com>
From: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
Date: Wed, 7 Nov 2018 13:56:52 -0600
> For the series:
>
> Acked-by: Willem de Bruijn <willemb@google.com>
>
> (Let me know if I need to Ack each patch directly)
You do not.
This is why I like header postings so much, reviewers can just
reply to it to ACK the entire series.
^ permalink raw reply
* Re: [PATCH net-next 00/10] udp: implement GRO support
From: Willem de Bruijn @ 2018-11-07 19:56 UTC (permalink / raw)
To: Paolo Abeni
Cc: Network Development, David Miller, Willem de Bruijn,
steffen.klassert, Subash Abhinov Kasiviswanathan
In-Reply-To: <cover.1541588248.git.pabeni@redhat.com>
On Wed, Nov 7, 2018 at 5:43 AM Paolo Abeni <pabeni@redhat.com> wrote:
>
> This series implements GRO support for UDP sockets, as the RX counterpart
> of commit bec1f6f69736 ("udp: generate gso with UDP_SEGMENT").
> The core functionality is implemented by the second patch, introducing a new
> sockopt to enable UDP_GRO, while patch 3 implements support for passing the
> segment size to the user space via a new cmsg.
> UDP GRO performs a socket lookup for each ingress packets and aggregate datagram
> directed to UDP GRO enabled sockets with constant l4 tuple.
>
> UDP GRO packets can land on non GRO-enabled sockets, e.g. due to iptables NAT
> rules, and that could potentially confuse existing applications.
>
> The solution adopted here is to de-segment the GRO packet before enqueuing
> as needed. Since we must cope with packet reinsertion after de-segmentation,
> the relevant code is factored-out in ipv4 and ipv6 specific helpers and exposed
> to UDP usage.
>
> While the current code can probably be improved, this safeguard ,implemented in
> the patches 4-7, allows future enachements to enable UDP GSO offload on more
> virtual devices eventually even on forwarded packets.
>
> The last 4 for patches implement some performance and functional self-tests,
> re-using the existing udpgso infrastructure. The problematic scenario described
> above is explicitly tested.
>
> This revision of the series try to address the feedback provided by Willem and
> Subash on previous iteration.
>
> Paolo Abeni (10):
> udp: implement complete book-keeping for encap_needed
> udp: implement GRO for plain UDP sockets.
> udp: add support for UDP_GRO cmsg
> ip: factor out protocol delivery helper
> ipv6: factor out protocol delivery helper
> udp: cope with UDP GRO packet misdirection
> selftests: add GRO support to udp bench rx program
> selftests: add dummy xdp test helper
> selftests: add some benchmark for UDP GRO
> selftests: add functionals test for UDP GRO
>
> include/linux/udp.h | 25 ++-
> include/net/ip.h | 1 +
> include/net/ipv6.h | 2 +
> include/net/udp.h | 45 ++++-
> include/net/udp_tunnel.h | 6 +
> include/uapi/linux/udp.h | 1 +
> net/ipv4/ip_input.c | 73 ++++----
> net/ipv4/udp.c | 54 +++++-
> net/ipv4/udp_offload.c | 109 +++++++++---
> net/ipv6/ip6_input.c | 28 ++--
> net/ipv6/udp.c | 41 ++++-
> net/ipv6/udp_offload.c | 6 +-
> tools/testing/selftests/bpf/Makefile | 3 +-
> tools/testing/selftests/bpf/xdp_dummy.c | 13 ++
> tools/testing/selftests/net/Makefile | 1 +
> tools/testing/selftests/net/udpgro.sh | 148 +++++++++++++++++
> tools/testing/selftests/net/udpgro_bench.sh | 95 +++++++++++
> tools/testing/selftests/net/udpgso_bench.sh | 2 +-
> tools/testing/selftests/net/udpgso_bench_rx.c | 156 ++++++++++++++++--
> tools/testing/selftests/net/udpgso_bench_tx.c | 22 ++-
> 20 files changed, 708 insertions(+), 123 deletions(-)
> create mode 100644 tools/testing/selftests/bpf/xdp_dummy.c
> create mode 100755 tools/testing/selftests/net/udpgro.sh
> create mode 100755 tools/testing/selftests/net/udpgro_bench.sh
>
> --
> 2.17.2
>
For the series:
Acked-by: Willem de Bruijn <willemb@google.com>
(Let me know if I need to Ack each patch directly)
Looks great. Thanks for addressing all comments, Paolo. I applied
the series mbox from patchwork to the same commit as the previous
RFCs to be able to incrementally review with git diff.
^ permalink raw reply
* Re: [PATCH net-next 04/11] selftests: pmtu: Introduce tests for IPv4/IPv6 over VxLAN over IPv6
From: Stefano Brivio @ 2018-11-07 19:54 UTC (permalink / raw)
To: David Ahern; +Cc: David S. Miller, Sabrina Dubroca, Xin Long, netdev
In-Reply-To: <3f6578a6-623b-a5cc-68fd-29b25a4585e8@gmail.com>
On Wed, 7 Nov 2018 12:28:21 -0700
David Ahern <dsahern@gmail.com> wrote:
> On 11/6/18 2:39 PM, Stefano Brivio wrote:
> > Use a router between endpoints, implemented via namespaces, set a low MTU
> > between router and destination endpoint, exceed it and check PMTU value in
> > route exceptions.
> >
> > Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
> > Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
> > ---
> > This only introduces tests over VxLAN over IPv6 right now. I'll introduce
> > tests over IPv4 (they can be added trivially) once DF configuration support
> > is accepted into iproute2.
>
> you can add them now and wrapped in a 'does ip support the df option'
> check. That is needed regardless of order (kernel vs iproute2).
True, I thought about that, but then I also thought that if we end up
with a different syntax for the iproute2 command, that becomes ugly.
Then yes, the check would be there -- it's actually already there, that
|| return 1 after the ip-link command.
--
Stefano
^ permalink raw reply
* Re: [PATCH net-next 04/11] selftests: pmtu: Introduce tests for IPv4/IPv6 over VxLAN over IPv6
From: David Miller @ 2018-11-07 19:48 UTC (permalink / raw)
To: dsahern; +Cc: sbrivio, sd, lucien.xin, netdev
In-Reply-To: <3f6578a6-623b-a5cc-68fd-29b25a4585e8@gmail.com>
From: David Ahern <dsahern@gmail.com>
Date: Wed, 7 Nov 2018 12:28:21 -0700
> you can add them now and wrapped in a 'does ip support the df option'
> check. That is needed regardless of order (kernel vs iproute2).
Indeed, that's a good point.
^ permalink raw reply
* Re: [PATCH net-next 0/5] net: phy: improve and simplify phylib state machine
From: Andrew Lunn @ 2018-11-07 19:48 UTC (permalink / raw)
To: Heiner Kallweit; +Cc: Florian Fainelli, David Miller, netdev@vger.kernel.org
In-Reply-To: <922c223b-7bc0-e0ec-345d-2034b796af91@gmail.com>
On Wed, Nov 07, 2018 at 08:41:52PM +0100, Heiner Kallweit wrote:
> This patch series is based on two axioms:
>
> - During autoneg a PHY always reports the link being down
Hi Heiner
I think that is a risky assumption to make.
What happens if this assumption is incorrect?
Andrew
^ permalink raw reply
* [PATCH net-next 5/5] net: phy: use phy_check_link_status in more places in the state machine
From: Heiner Kallweit @ 2018-11-07 19:47 UTC (permalink / raw)
To: Florian Fainelli, Andrew Lunn, David Miller; +Cc: netdev@vger.kernel.org
In-Reply-To: <922c223b-7bc0-e0ec-345d-2034b796af91@gmail.com>
Use phy_check_link_status in more places in the state machine.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
drivers/net/phy/phy.c | 53 ++++---------------------------------------
1 file changed, 5 insertions(+), 48 deletions(-)
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 226824804..dd5bff955 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -945,17 +945,13 @@ void phy_state_machine(struct work_struct *work)
break;
case PHY_NOLINK:
+ case PHY_RUNNING:
if (!phy_polling_mode(phydev))
break;
-
- err = phy_read_status(phydev);
- if (err)
- break;
-
- if (phydev->link) {
- phydev->state = PHY_RUNNING;
- phy_link_up(phydev);
- }
+ /* fall through */
+ case PHY_CHANGELINK:
+ case PHY_RESUMING:
+ err = phy_check_link_status(phydev);
break;
case PHY_FORCING:
err = genphy_update_link(phydev);
@@ -971,32 +967,6 @@ void phy_state_machine(struct work_struct *work)
phy_link_down(phydev, false);
}
break;
- case PHY_RUNNING:
- if (!phy_polling_mode(phydev))
- break;
-
- err = phy_read_status(phydev);
- if (err)
- break;
-
- if (!phydev->link) {
- phydev->state = PHY_NOLINK;
- phy_link_down(phydev, true);
- }
- break;
- case PHY_CHANGELINK:
- err = phy_read_status(phydev);
- if (err)
- break;
-
- if (phydev->link) {
- phydev->state = PHY_RUNNING;
- phy_link_up(phydev);
- } else {
- phydev->state = PHY_NOLINK;
- phy_link_down(phydev, true);
- }
- break;
case PHY_HALTED:
if (phydev->link) {
phydev->link = 0;
@@ -1004,19 +974,6 @@ void phy_state_machine(struct work_struct *work)
do_suspend = true;
}
break;
- case PHY_RESUMING:
- err = phy_read_status(phydev);
- if (err)
- break;
-
- if (phydev->link) {
- phydev->state = PHY_RUNNING;
- phy_link_up(phydev);
- } else {
- phydev->state = PHY_NOLINK;
- phy_link_down(phydev, true);
- }
- break;
}
mutex_unlock(&phydev->lock);
--
2.19.1
^ permalink raw reply related
* [PATCH net-next 4/5] net: phy: remove state PHY_AN
From: Heiner Kallweit @ 2018-11-07 19:46 UTC (permalink / raw)
To: Florian Fainelli, Andrew Lunn, David Miller; +Cc: netdev@vger.kernel.org
In-Reply-To: <922c223b-7bc0-e0ec-345d-2034b796af91@gmail.com>
After the recent changes in the state machine state PHY_AN isn't used
any longer and can be removed.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
drivers/net/phy/phy.c | 27 ---------------------------
include/linux/phy.h | 19 +------------------
2 files changed, 1 insertion(+), 45 deletions(-)
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 87ed00030..226824804 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -50,7 +50,6 @@ static const char *phy_state_to_str(enum phy_state st)
PHY_STATE_STR(READY)
PHY_STATE_STR(PENDING)
PHY_STATE_STR(UP)
- PHY_STATE_STR(AN)
PHY_STATE_STR(RUNNING)
PHY_STATE_STR(NOLINK)
PHY_STATE_STR(FORCING)
@@ -944,32 +943,6 @@ void phy_state_machine(struct work_struct *work)
case PHY_UP:
needs_aneg = true;
- phydev->link_timeout = PHY_AN_TIMEOUT;
-
- break;
- case PHY_AN:
- err = phy_read_status(phydev);
- if (err < 0)
- break;
-
- /* If the link is down, give up on negotiation for now */
- if (!phydev->link) {
- phydev->state = PHY_NOLINK;
- phy_link_down(phydev, true);
- break;
- }
-
- /* Check if negotiation is done. Break if there's an error */
- err = phy_aneg_done(phydev);
- if (err < 0)
- break;
-
- /* If AN is done, we're running */
- if (err > 0) {
- phydev->state = PHY_RUNNING;
- phy_link_up(phydev);
- } else if (0 == phydev->link_timeout--)
- needs_aneg = true;
break;
case PHY_NOLINK:
if (!phy_polling_mode(phydev))
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 9e4d49ef4..2090277ea 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -178,7 +178,6 @@ static inline const char *phy_modes(phy_interface_t interface)
#define PHY_INIT_TIMEOUT 100000
#define PHY_STATE_TIME 1
#define PHY_FORCE_TIMEOUT 10
-#define PHY_AN_TIMEOUT 10
#define PHY_MAX_ADDR 32
@@ -297,24 +296,10 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr);
*
* UP: The PHY and attached device are ready to do work.
* Interrupts should be started here.
- * - timer moves to AN
- *
- * AN: The PHY is currently negotiating the link state. Link is
- * therefore down for now. phy_timer will set this state when it
- * detects the state is UP. config_aneg will set this state
- * whenever called with phydev->autoneg set to AUTONEG_ENABLE.
- * - If autonegotiation finishes, but there's no link, it sets
- * the state to NOLINK.
- * - If aneg finishes with link, it sets the state to RUNNING,
- * and calls adjust_link
- * - If autonegotiation did not finish after an arbitrary amount
- * of time, autonegotiation should be tried again if the PHY
- * supports "magic" autonegotiation (back to AN)
- * - If it didn't finish, and no magic_aneg, move to FORCING.
+ * - timer moves to NOLINK or RUNNING
*
* NOLINK: PHY is up, but not currently plugged in.
* - If the timer notes that the link comes back, we move to RUNNING
- * - config_aneg moves to AN
* - phy_stop moves to HALTED
*
* FORCING: PHY is being configured with forced settings
@@ -329,7 +314,6 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr);
* link state is polled every other cycle of this state machine,
* which makes it every other second)
* - irq will set CHANGELINK
- * - config_aneg will set AN
* - phy_stop moves to HALTED
*
* CHANGELINK: PHY experienced a change in link state
@@ -353,7 +337,6 @@ enum phy_state {
PHY_READY,
PHY_PENDING,
PHY_UP,
- PHY_AN,
PHY_RUNNING,
PHY_NOLINK,
PHY_FORCING,
--
2.19.1
^ permalink raw reply related
* [PATCH net-next 3/5] net: phy: add phy_check_link_status
From: Heiner Kallweit @ 2018-11-07 19:45 UTC (permalink / raw)
To: Florian Fainelli, Andrew Lunn, David Miller; +Cc: netdev@vger.kernel.org
In-Reply-To: <922c223b-7bc0-e0ec-345d-2034b796af91@gmail.com>
In few places in the state machine the state is set to PHY_RUNNING or
PHY_NOLINK after doing a phy_read_status(). So factor this out to
phy_check_link_status().
First use it in phy_start_aneg(): By setting the state to PHY_RUNNING
or PHY_NOLINK directly we can remove the code to handle the case that
we're using interrupts and aneg was finished already.
Definition of phy_link_up and phy_link_down needs to be moved because
they are called in the new function.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
drivers/net/phy/phy.c | 70 ++++++++++++++++++++++++-------------------
1 file changed, 40 insertions(+), 30 deletions(-)
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 14dffa0da..87ed00030 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -62,6 +62,17 @@ static const char *phy_state_to_str(enum phy_state st)
return NULL;
}
+static void phy_link_up(struct phy_device *phydev)
+{
+ phydev->phy_link_change(phydev, true, true);
+ phy_led_trigger_change_speed(phydev);
+}
+
+static void phy_link_down(struct phy_device *phydev, bool do_carrier)
+{
+ phydev->phy_link_change(phydev, false, do_carrier);
+ phy_led_trigger_change_speed(phydev);
+}
/**
* phy_print_status - Convenience function to print out the current phy status
@@ -493,6 +504,34 @@ static int phy_config_aneg(struct phy_device *phydev)
return genphy_config_aneg(phydev);
}
+/**
+ * phy_check_link_status - check link status and set state accordingly
+ * @phydev: the phy_device struct
+ *
+ * Description: Check for link and whether autoneg was triggered / is running
+ * and set state accordingly
+ */
+static int phy_check_link_status(struct phy_device *phydev)
+{
+ int err;
+
+ WARN_ON(!mutex_is_locked(&phydev->lock));
+
+ err = phy_read_status(phydev);
+ if (err)
+ return err;
+
+ if (phydev->link && phydev->state != PHY_RUNNING) {
+ phydev->state = PHY_RUNNING;
+ phy_link_up(phydev);
+ } else if (!phydev->link && phydev->state != PHY_NOLINK) {
+ phydev->state = PHY_NOLINK;
+ phy_link_down(phydev, true);
+ }
+
+ return 0;
+}
+
/**
* phy_start_aneg - start auto-negotiation for this PHY device
* @phydev: the phy_device struct
@@ -504,7 +543,6 @@ static int phy_config_aneg(struct phy_device *phydev)
*/
int phy_start_aneg(struct phy_device *phydev)
{
- bool trigger = 0;
int err;
if (!phydev->drv)
@@ -524,32 +562,16 @@ int phy_start_aneg(struct phy_device *phydev)
if (phydev->state != PHY_HALTED) {
if (AUTONEG_ENABLE == phydev->autoneg) {
- phydev->state = PHY_AN;
- phydev->link_timeout = PHY_AN_TIMEOUT;
+ err = phy_check_link_status(phydev);
} else {
phydev->state = PHY_FORCING;
phydev->link_timeout = PHY_FORCE_TIMEOUT;
}
}
- /* Re-schedule a PHY state machine to check PHY status because
- * negotiation may already be done and aneg interrupt may not be
- * generated.
- */
- if (!phy_polling_mode(phydev) && phydev->state == PHY_AN) {
- err = phy_aneg_done(phydev);
- if (err > 0) {
- trigger = true;
- err = 0;
- }
- }
-
out_unlock:
mutex_unlock(&phydev->lock);
- if (trigger)
- phy_trigger_machine(phydev);
-
return err;
}
EXPORT_SYMBOL(phy_start_aneg);
@@ -893,18 +915,6 @@ void phy_start(struct phy_device *phydev)
}
EXPORT_SYMBOL(phy_start);
-static void phy_link_up(struct phy_device *phydev)
-{
- phydev->phy_link_change(phydev, true, true);
- phy_led_trigger_change_speed(phydev);
-}
-
-static void phy_link_down(struct phy_device *phydev, bool do_carrier)
-{
- phydev->phy_link_change(phydev, false, do_carrier);
- phy_led_trigger_change_speed(phydev);
-}
-
/**
* phy_state_machine - Handle the state machine
* @work: work_struct that describes the work to be done
--
2.19.1
^ permalink raw reply related
* [PATCH net-next 2/5] net: phy: remove useless check in state machine case PHY_RESUMING
From: Heiner Kallweit @ 2018-11-07 19:44 UTC (permalink / raw)
To: Florian Fainelli, Andrew Lunn, David Miller; +Cc: netdev@vger.kernel.org
In-Reply-To: <922c223b-7bc0-e0ec-345d-2034b796af91@gmail.com>
If aneg isn't finished yet then the PHY reports the link as down.
There's no benefit in setting the state to PHY_AN because the next
state machine run would set the status to PHY_NOLINK anyway (except
in the meantime aneg has been finished and link is up). Therefore
we can set the state to PHY_RUNNING or PHY_NOLINK directly.
In addition change the do_carrier parameter in phy_link_down() to true.
If carrier was marked as up before (what should never be the case because
PHY was in state PHY_HALTED before) then we should mark it as down now.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
drivers/net/phy/phy.c | 13 +------------
1 file changed, 1 insertion(+), 12 deletions(-)
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 87c6d304c..14dffa0da 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -1022,17 +1022,6 @@ void phy_state_machine(struct work_struct *work)
}
break;
case PHY_RESUMING:
- if (AUTONEG_ENABLE == phydev->autoneg) {
- err = phy_aneg_done(phydev);
- if (err < 0) {
- break;
- } else if (!err) {
- phydev->state = PHY_AN;
- phydev->link_timeout = PHY_AN_TIMEOUT;
- break;
- }
- }
-
err = phy_read_status(phydev);
if (err)
break;
@@ -1042,7 +1031,7 @@ void phy_state_machine(struct work_struct *work)
phy_link_up(phydev);
} else {
phydev->state = PHY_NOLINK;
- phy_link_down(phydev, false);
+ phy_link_down(phydev, true);
}
break;
}
--
2.19.1
^ permalink raw reply related
* [PATCH net-next 1/5] net: phy: remove useless check in state machine case PHY_NOLINK
From: Heiner Kallweit @ 2018-11-07 19:43 UTC (permalink / raw)
To: Florian Fainelli, Andrew Lunn, David Miller; +Cc: netdev@vger.kernel.org
In-Reply-To: <922c223b-7bc0-e0ec-345d-2034b796af91@gmail.com>
If aneg is enabled and the PHY reports the link as up then definitely
aneg finished successfully. Therefore this check is useless and
can be removed.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
drivers/net/phy/phy.c | 11 -----------
1 file changed, 11 deletions(-)
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 476578746..87c6d304c 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -970,17 +970,6 @@ void phy_state_machine(struct work_struct *work)
break;
if (phydev->link) {
- if (AUTONEG_ENABLE == phydev->autoneg) {
- err = phy_aneg_done(phydev);
- if (err < 0)
- break;
-
- if (!err) {
- phydev->state = PHY_AN;
- phydev->link_timeout = PHY_AN_TIMEOUT;
- break;
- }
- }
phydev->state = PHY_RUNNING;
phy_link_up(phydev);
}
--
2.19.1
^ permalink raw reply related
* Re: [PATCH net-next 0/7] nfp: more set actions and notifier refactor
From: David Miller @ 2018-11-07 19:46 UTC (permalink / raw)
To: jakub.kicinski; +Cc: netdev, oss-drivers
In-Reply-To: <20181107010734.29935-1-jakub.kicinski@netronome.com>
From: Jakub Kicinski <jakub.kicinski@netronome.com>
Date: Tue, 6 Nov 2018 17:07:27 -0800
> This series brings updates to flower offload code. First Pieter adds
> support for setting TTL, ToS, Flow Label and Hop Limit fields in IPv4
> and IPv6 headers.
>
> Remaining 5 patches deal with factoring out netdev notifiers from flower
> code. We already have two instances, and more is coming, so it's time
> to move to one central notifier which then feeds individual feature
> handlers.
>
> I start that part by cleaning up the existing notifiers. Next a central
> notifier is added, and used by flower offloads.
Looks good, series applied, thanks Jakub.
^ permalink raw reply
* [PATCH net-next 0/5] net: phy: improve and simplify phylib state machine
From: Heiner Kallweit @ 2018-11-07 19:41 UTC (permalink / raw)
To: Florian Fainelli, Andrew Lunn, David Miller; +Cc: netdev@vger.kernel.org
This patch series is based on two axioms:
- During autoneg a PHY always reports the link being down
- Info in clause 22/45 registers doesn't allow to differentiate between
these two states:
1. Link is physically down
2. A link partner is connected and PHY is autonegotiating
In both cases "link up" and "aneg finished" bits aren't set.
One consequence is that having separate states PHY_NOLINK and PHY_AN
isn't needed.
By using these two axioms the state machine can be significantly
simplified.
Heiner Kallweit (5):
net: phy: remove useless check in state machine case PHY_NOLINK
net: phy: remove useless check in state machine case PHY_RESUMING
net: phy: add phy_check_link_status
net: phy: remove state PHY_AN
net: phy: use phy_check_link_status in more places in the state machine
drivers/net/phy/phy.c | 172 +++++++++++-------------------------------
include/linux/phy.h | 19 +----
2 files changed, 46 insertions(+), 145 deletions(-)
--
2.19.1
^ permalink raw reply
* [PATCH bpf-next 2/2] bpftool: support loading flow dissector
From: Stanislav Fomichev @ 2018-11-07 19:35 UTC (permalink / raw)
To: netdev, linux-kselftest, ast, daniel, shuah
Cc: jakub.kicinski, quentin.monnet, guro, jiong.wang, sdf,
bhole_prashant_q7, john.fastabend, jbenc, treeze.taeung, yhs, osk,
sandipan
In-Reply-To: <20181107193552.77894-1-sdf@google.com>
This commit adds support for loading/attaching/detaching flow
dissector program. The structure of the flow dissector program is
assumed to be the same as in the selftests:
* flow_dissector section with the main entry point
* a bunch of tail call progs
* a jmp_table map that is populated with the tail call progs
When `bpftool load` is called with a flow_dissector prog (i.e. when the
first section is flow_dissector of 'type flow_dissector' argument is
passed), we load and pin all the programs and build the jump table.
The last argument of `bpftool attach` is made optional for this use
case.
Example:
bpftool prog load tools/testing/selftests/bpf/bpf_flow.o \
/sys/fs/bpf/flow type flow_dissector
bpftool prog attach pinned /sys/fs/bpf/flow/flow_dissector/0 flow_dissector
Tested by using the above two lines to load the prog in
the test_flow_dissector.sh selftest.
Signed-off-by: Stanislav Fomichev <sdf@google.com>
---
.../bpftool/Documentation/bpftool-prog.rst | 16 ++-
tools/bpf/bpftool/common.c | 32 +++--
tools/bpf/bpftool/main.h | 1 +
tools/bpf/bpftool/prog.c | 135 +++++++++++++++---
4 files changed, 141 insertions(+), 43 deletions(-)
diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
index ac4e904b10fb..3caa9153435b 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
@@ -25,8 +25,8 @@ MAP COMMANDS
| **bpftool** **prog dump jited** *PROG* [{**file** *FILE* | **opcodes**}]
| **bpftool** **prog pin** *PROG* *FILE*
| **bpftool** **prog load** *OBJ* *FILE* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*]
-| **bpftool** **prog attach** *PROG* *ATTACH_TYPE* *MAP*
-| **bpftool** **prog detach** *PROG* *ATTACH_TYPE* *MAP*
+| **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*]
+| **bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*]
| **bpftool** **prog help**
|
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
@@ -39,7 +39,9 @@ MAP COMMANDS
| **cgroup/bind4** | **cgroup/bind6** | **cgroup/post_bind4** | **cgroup/post_bind6** |
| **cgroup/connect4** | **cgroup/connect6** | **cgroup/sendmsg4** | **cgroup/sendmsg6**
| }
-| *ATTACH_TYPE* := { **msg_verdict** | **skb_verdict** | **skb_parse** }
+| *ATTACH_TYPE* := {
+| | **msg_verdict** | **skb_verdict** | **skb_parse** | **flow_dissector**
+| }
DESCRIPTION
@@ -97,13 +99,13 @@ DESCRIPTION
contain a dot character ('.'), which is reserved for future
extensions of *bpffs*.
- **bpftool prog attach** *PROG* *ATTACH_TYPE* *MAP*
+ **bpftool prog attach** *PROG* *ATTACH_TYPE* [*MAP*]
Attach bpf program *PROG* (with type specified by *ATTACH_TYPE*)
- to the map *MAP*.
+ to the optional map *MAP*.
- **bpftool prog detach** *PROG* *ATTACH_TYPE* *MAP*
+ **bpftool prog detach** *PROG* *ATTACH_TYPE* [*MAP*]
Detach bpf program *PROG* (with type specified by *ATTACH_TYPE*)
- from the map *MAP*.
+ from the optional map *MAP*.
**bpftool prog help**
Print short help message.
diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c
index 25af85304ebe..963881142dfb 100644
--- a/tools/bpf/bpftool/common.c
+++ b/tools/bpf/bpftool/common.c
@@ -169,34 +169,24 @@ int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type)
return fd;
}
-int do_pin_fd(int fd, const char *name)
+int mount_bpffs_for_pin(const char *name)
{
char err_str[ERR_MAX_LEN];
char *file;
char *dir;
int err = 0;
- err = bpf_obj_pin(fd, name);
- if (!err)
- goto out;
-
file = malloc(strlen(name) + 1);
strcpy(file, name);
dir = dirname(file);
- if (errno != EPERM || is_bpffs(dir)) {
- p_err("can't pin the object (%s): %s", name, strerror(errno));
+ if (is_bpffs(dir)) {
+ /* nothing to do if already mounted */
goto out_free;
}
- /* Attempt to mount bpffs, then retry pinning. */
err = mnt_bpffs(dir, err_str, ERR_MAX_LEN);
- if (!err) {
- err = bpf_obj_pin(fd, name);
- if (err)
- p_err("can't pin the object (%s): %s", name,
- strerror(errno));
- } else {
+ if (err) {
err_str[ERR_MAX_LEN - 1] = '\0';
p_err("can't mount BPF file system to pin the object (%s): %s",
name, err_str);
@@ -204,10 +194,22 @@ int do_pin_fd(int fd, const char *name)
out_free:
free(file);
-out:
return err;
}
+int do_pin_fd(int fd, const char *name)
+{
+ int err = mount_bpffs_for_pin(name);
+
+ if (err) {
+ p_err("can't mount bpffs for pin %s: %s",
+ name, strerror(errno));
+ return err;
+ }
+
+ return bpf_obj_pin(fd, name);
+}
+
int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32))
{
unsigned int id;
diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
index 28322ace2856..1383824c9baf 100644
--- a/tools/bpf/bpftool/main.h
+++ b/tools/bpf/bpftool/main.h
@@ -129,6 +129,7 @@ const char *get_fd_type_name(enum bpf_obj_type type);
char *get_fdinfo(int fd, const char *key);
int open_obj_pinned(char *path);
int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type);
+int mount_bpffs_for_pin(const char *name);
int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32));
int do_pin_fd(int fd, const char *name);
diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
index 5302ee282409..f3a07ec3a444 100644
--- a/tools/bpf/bpftool/prog.c
+++ b/tools/bpf/bpftool/prog.c
@@ -81,6 +81,7 @@ static const char * const attach_type_strings[] = {
[BPF_SK_SKB_STREAM_PARSER] = "stream_parser",
[BPF_SK_SKB_STREAM_VERDICT] = "stream_verdict",
[BPF_SK_MSG_VERDICT] = "msg_verdict",
+ [BPF_FLOW_DISSECTOR] = "flow_dissector",
[__MAX_BPF_ATTACH_TYPE] = NULL,
};
@@ -724,9 +725,10 @@ int map_replace_compar(const void *p1, const void *p2)
static int do_attach(int argc, char **argv)
{
enum bpf_attach_type attach_type;
- int err, mapfd, progfd;
+ int err, progfd;
+ int mapfd = 0;
- if (!REQ_ARGS(5)) {
+ if (!REQ_ARGS(3)) {
p_err("too few parameters for map attach");
return -EINVAL;
}
@@ -741,10 +743,11 @@ static int do_attach(int argc, char **argv)
return -EINVAL;
}
NEXT_ARG();
-
- mapfd = map_parse_fd(&argc, &argv);
- if (mapfd < 0)
- return mapfd;
+ if (argc > 0) {
+ mapfd = map_parse_fd(&argc, &argv);
+ if (mapfd < 0)
+ return mapfd;
+ }
err = bpf_prog_attach(progfd, mapfd, attach_type, 0);
if (err) {
@@ -760,9 +763,10 @@ static int do_attach(int argc, char **argv)
static int do_detach(int argc, char **argv)
{
enum bpf_attach_type attach_type;
- int err, mapfd, progfd;
+ int err, progfd;
+ int mapfd = 0;
- if (!REQ_ARGS(5)) {
+ if (!REQ_ARGS(3)) {
p_err("too few parameters for map detach");
return -EINVAL;
}
@@ -777,10 +781,11 @@ static int do_detach(int argc, char **argv)
return -EINVAL;
}
NEXT_ARG();
-
- mapfd = map_parse_fd(&argc, &argv);
- if (mapfd < 0)
- return mapfd;
+ if (argc > 0) {
+ mapfd = map_parse_fd(&argc, &argv);
+ if (mapfd < 0)
+ return mapfd;
+ }
err = bpf_prog_detach2(progfd, mapfd, attach_type);
if (err) {
@@ -792,6 +797,56 @@ static int do_detach(int argc, char **argv)
jsonw_null(json_wtr);
return 0;
}
+
+/* Flow dissector consists of a main program and a jump table for each
+ * supported protocol. The assumption here is that the first prog is the main
+ * one and the other progs are used in the tail calls. In this routine we
+ * build the jump table for the non-main progs.
+ */
+static int build_flow_dissector_jmp_table(struct bpf_object *obj,
+ struct bpf_program *prog,
+ const char *jmp_table_map)
+{
+ struct bpf_map *jmp_table;
+ struct bpf_program *pos;
+ int i = 0;
+ int prog_fd, jmp_table_fd, fd;
+
+ prog_fd = bpf_program__fd(prog);
+ if (prog_fd < 0) {
+ p_err("failed to get fd of main prog");
+ return prog_fd;
+ }
+
+ jmp_table = bpf_object__find_map_by_name(obj, jmp_table_map);
+ if (jmp_table == NULL) {
+ p_err("failed to find '%s' map", jmp_table_map);
+ return -1;
+ }
+
+ jmp_table_fd = bpf_map__fd(jmp_table);
+ if (jmp_table_fd < 0) {
+ p_err("failed to get fd of jmp_table");
+ return jmp_table_fd;
+ }
+
+ bpf_object__for_each_program(pos, obj) {
+ fd = bpf_program__fd(pos);
+ if (fd < 0) {
+ p_err("failed to get fd of '%s'",
+ bpf_program__title(pos, false));
+ return fd;
+ }
+
+ if (fd != prog_fd) {
+ bpf_map_update_elem(jmp_table_fd, &i, &fd, BPF_ANY);
+ ++i;
+ }
+ }
+
+ return 0;
+}
+
static int do_load(int argc, char **argv)
{
enum bpf_attach_type expected_attach_type;
@@ -800,7 +855,7 @@ static int do_load(int argc, char **argv)
};
struct map_replace *map_replace = NULL;
unsigned int old_map_fds = 0;
- struct bpf_program *prog;
+ struct bpf_program *prog, *pos;
struct bpf_object *obj;
struct bpf_map *map;
const char *pinfile;
@@ -918,13 +973,19 @@ static int do_load(int argc, char **argv)
goto err_free_reuse_maps;
}
- prog = bpf_program__next(NULL, obj);
+ if (attr.prog_type == BPF_PROG_TYPE_FLOW_DISSECTOR) {
+ /* for the flow dissector type, the entry point is in the
+ * section flow_dissector; other progs are tail calls
+ */
+ prog = bpf_object__find_program_by_title(obj, "flow_dissector");
+ } else {
+ prog = bpf_program__next(NULL, obj);
+ }
if (!prog) {
p_err("object file doesn't contain any bpf program");
goto err_close_obj;
}
- bpf_program__set_ifindex(prog, ifindex);
if (attr.prog_type == BPF_PROG_TYPE_UNSPEC) {
const char *sec_name = bpf_program__title(prog, false);
@@ -936,8 +997,13 @@ static int do_load(int argc, char **argv)
goto err_close_obj;
}
}
- bpf_program__set_type(prog, attr.prog_type);
- bpf_program__set_expected_attach_type(prog, expected_attach_type);
+
+ bpf_object__for_each_program(pos, obj) {
+ bpf_program__set_ifindex(pos, ifindex);
+ bpf_program__set_type(pos, attr.prog_type);
+ bpf_program__set_expected_attach_type(pos,
+ expected_attach_type);
+ }
qsort(map_replace, old_map_fds, sizeof(*map_replace),
map_replace_compar);
@@ -1001,8 +1067,34 @@ static int do_load(int argc, char **argv)
goto err_close_obj;
}
- if (do_pin_fd(bpf_program__fd(prog), pinfile))
+ err = mount_bpffs_for_pin(pinfile);
+ if (err) {
+ p_err("failed to mount bpffs for pin '%s'", pinfile);
goto err_close_obj;
+ }
+
+ if (attr.prog_type == BPF_PROG_TYPE_FLOW_DISSECTOR) {
+ err = build_flow_dissector_jmp_table(obj, prog, "jmp_table");
+ if (err) {
+ p_err("failed to build flow dissector jump table");
+ goto err_close_obj;
+ }
+ /* flow dissector consist of multiple programs,
+ * we want to pin them all
+ */
+ err = bpf_object__pin(obj, pinfile);
+ if (err) {
+ p_err("failed to pin flow dissector object");
+ goto err_close_obj;
+ }
+ } else {
+ err = bpf_obj_pin(bpf_program__fd(prog), pinfile);
+ if (err) {
+ p_err("failed to pin program %s",
+ bpf_program__title(prog, false));
+ goto err_close_obj;
+ }
+ }
if (json_output)
jsonw_null(json_wtr);
@@ -1037,8 +1129,8 @@ static int do_help(int argc, char **argv)
" %s %s pin PROG FILE\n"
" %s %s load OBJ FILE [type TYPE] [dev NAME] \\\n"
" [map { idx IDX | name NAME } MAP]\n"
- " %s %s attach PROG ATTACH_TYPE MAP\n"
- " %s %s detach PROG ATTACH_TYPE MAP\n"
+ " %s %s attach PROG ATTACH_TYPE [MAP]\n"
+ " %s %s detach PROG ATTACH_TYPE [MAP]\n"
" %s %s help\n"
"\n"
" " HELP_SPEC_MAP "\n"
@@ -1050,7 +1142,8 @@ static int do_help(int argc, char **argv)
" cgroup/bind4 | cgroup/bind6 | cgroup/post_bind4 |\n"
" cgroup/post_bind6 | cgroup/connect4 | cgroup/connect6 |\n"
" cgroup/sendmsg4 | cgroup/sendmsg6 }\n"
- " ATTACH_TYPE := { msg_verdict | skb_verdict | skb_parse }\n"
+ " ATTACH_TYPE := { msg_verdict | skb_verdict | skb_parse |\n"
+ " flow_dissector }\n"
" " HELP_SPEC_OPTIONS "\n"
"",
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
--
2.19.1.930.g4563a0d9d0-goog
^ permalink raw reply related
* [PATCH bpf-next 1/2] selftests/bpf: rename flow dissector section to flow_dissector
From: Stanislav Fomichev @ 2018-11-07 19:35 UTC (permalink / raw)
To: netdev, linux-kselftest, ast, daniel, shuah
Cc: jakub.kicinski, quentin.monnet, guro, jiong.wang, sdf,
bhole_prashant_q7, john.fastabend, jbenc, treeze.taeung, yhs, osk,
sandipan
In-Reply-To: <20181107193552.77894-1-sdf@google.com>
Makes it compatible with the logic that derives program type
from section name in libbpf_prog_type_by_name.
Signed-off-by: Stanislav Fomichev <sdf@google.com>
---
tools/testing/selftests/bpf/bpf_flow.c | 2 +-
tools/testing/selftests/bpf/test_flow_dissector.sh | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/bpf/bpf_flow.c b/tools/testing/selftests/bpf/bpf_flow.c
index 107350a7821d..b9798f558ca7 100644
--- a/tools/testing/selftests/bpf/bpf_flow.c
+++ b/tools/testing/selftests/bpf/bpf_flow.c
@@ -116,7 +116,7 @@ static __always_inline int parse_eth_proto(struct __sk_buff *skb, __be16 proto)
return BPF_DROP;
}
-SEC("dissect")
+SEC("flow_dissector")
int _dissect(struct __sk_buff *skb)
{
if (!skb->vlan_present)
diff --git a/tools/testing/selftests/bpf/test_flow_dissector.sh b/tools/testing/selftests/bpf/test_flow_dissector.sh
index c0fb073b5eab..d23d4da66b83 100755
--- a/tools/testing/selftests/bpf/test_flow_dissector.sh
+++ b/tools/testing/selftests/bpf/test_flow_dissector.sh
@@ -59,7 +59,7 @@ else
fi
# Attach BPF program
-./flow_dissector_load -p bpf_flow.o -s dissect
+./flow_dissector_load -p bpf_flow.o -s flow_dissector
# Setup
tc qdisc add dev lo ingress
--
2.19.1.930.g4563a0d9d0-goog
^ permalink raw reply related
* [PATCH bpf-next 0/2] bpftool: support loading flow dissector
From: Stanislav Fomichev @ 2018-11-07 19:35 UTC (permalink / raw)
To: netdev, linux-kselftest, ast, daniel, shuah
Cc: jakub.kicinski, quentin.monnet, guro, jiong.wang, sdf,
bhole_prashant_q7, john.fastabend, jbenc, treeze.taeung, yhs, osk,
sandipan
This patch series adds support for loading and attaching flow dissector
programs from the bpftool:
* first patch fixes flow dissector section name in the selftests (so
libbpf auto-detection works)
* second patch adds actual support to the bpftool
See second patch for the description/details.
Stanislav Fomichev (2):
selftests/bpf: rename flow dissector section to flow_dissector
bpftool: support loading flow dissector
.../bpftool/Documentation/bpftool-prog.rst | 16 ++-
tools/bpf/bpftool/common.c | 32 +++--
tools/bpf/bpftool/main.h | 1 +
tools/bpf/bpftool/prog.c | 135 +++++++++++++++---
tools/testing/selftests/bpf/bpf_flow.c | 2 +-
.../selftests/bpf/test_flow_dissector.sh | 2 +-
6 files changed, 143 insertions(+), 45 deletions(-)
--
2.19.1.930.g4563a0d9d0-goog
^ permalink raw reply
* Re: [PATCH bpf-next v2 02/13] bpf: btf: Add BTF_KIND_FUNC and BTF_KIND_FUNC_PROTO
From: Edward Cree @ 2018-11-07 19:29 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: Martin Lau, Yonghong Song, Alexei Starovoitov,
daniel@iogearbox.net, netdev@vger.kernel.org, Kernel Team
In-Reply-To: <20181107005955.qwgbytqtziaggdve@ast-mbp>
On 07/11/18 00:59, Alexei Starovoitov wrote:
> Function name and function argument names are part of the same debug info.
> Splitting them makes no sense.
... except where combining them involves creating pain elsewhere.
Sure the function name *could* go in the type record, but there
still needs to be a separate function record in a functions
table (because types are not instances), and that being the
case the latter _may as well_ be where the name lives so that
multiple functions (and pointers to them) can all share the
same type record when the param names etc. all match.
> struct name and struct field names live in the same BTF record.
No, the struct _type_ name lives in the same record as the field
names, but an _instance_ name doesn't. I.e. in
struct foo {int x;} bar;
the BTF type record holds the names 'foo' and 'x', but not 'bar'
because that's not the name of the _type_. Indeed there isn't
room in the record for both 'foo' and 'bar' because there's only
one name_off field for the type.
And I argue that the name of a function is more like 'bar' than
'foo' here, not least from the point of view of which C namespace
they occupy.
> Similarly function name and function argument names should be
> in the same BTF record, so we can reuse most of the BTF validation
> and BTF parsing logic by doing so.
I think it's incredibly short-sighted to focus on 'what can most
easily be done with the existing implementation' when designing a
file format which is intended to have 'long legs'.
> assembler is not a high level language.
I never said it was.
> I believe it's a proper trade-off to make C easier to use
> in expense of some ugliness in your ebpf_asm.
Please respond to the arguments I make, rather than unrelated
arguments that you might imagine me making. Asm is merely a
cause of my present interest in BTF, it is not the lens
through which I see the whole thing.
> Let's keep 'nasty hack' claims out of this discussion.
> I find the current BTF design and KIND_FUNC addition to be elegant
> and appropriate.
Whereas I don't, and I don't feel like my core criticisms have
been addressed _at all_. The only answer I get to "BTF should
store type and instance information in separate records" is
"it's a debuginfo", no indication of why that's a meaningful
noun let alone why it implies they should be conflated in the
format.
And please explain what's "elegant" about how map types are
currently handled.
> BTF is not *type* only format. It's debug info format.
> Trying to make BTF into type only is not going to work.
> It's already more than type only as I showed earlier.
Again, as I have *repeatedly* said, I am not trying to remove
non-type information from BTF. I am just trying to organise
BTF to consist of separate _parts_ for types and instances,
rather than forcing both into the same Procrustean bed.
(I don't feel like we're making progress in understanding one
another here; maybe we should have a telephone discussion?
Sadly I'm not going to Plumbers, else that would be the
perfect place to thrash this out.)
-Ed
^ permalink raw reply
* Re: [PATCH net-next 07/11] selftests: pmtu: Introduce tests for IPv4/IPv6 over GENEVE over IPv6
From: David Ahern @ 2018-11-07 19:28 UTC (permalink / raw)
To: Stefano Brivio, David S. Miller; +Cc: Sabrina Dubroca, Xin Long, netdev
In-Reply-To: <30bc36eea991cd6f35418c9ddc4cf322ed99e6e9.1541533786.git.sbrivio@redhat.com>
On 11/6/18 2:39 PM, Stefano Brivio wrote:
> This only introduces tests over GENEVE over IPv6 right now. I'll introduce
> tests over IPv4 (they can be added trivially) once DF configuration support
> is accepted into iproute2.
same here.
^ permalink raw reply
* Re: [PATCH net-next 04/11] selftests: pmtu: Introduce tests for IPv4/IPv6 over VxLAN over IPv6
From: David Ahern @ 2018-11-07 19:28 UTC (permalink / raw)
To: Stefano Brivio, David S. Miller; +Cc: Sabrina Dubroca, Xin Long, netdev
In-Reply-To: <366b75ae560cc2d0b3a0f69b84d43b621c8fcce4.1541533786.git.sbrivio@redhat.com>
On 11/6/18 2:39 PM, Stefano Brivio wrote:
> Use a router between endpoints, implemented via namespaces, set a low MTU
> between router and destination endpoint, exceed it and check PMTU value in
> route exceptions.
>
> Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
> Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
> ---
> This only introduces tests over VxLAN over IPv6 right now. I'll introduce
> tests over IPv4 (they can be added trivially) once DF configuration support
> is accepted into iproute2.
you can add them now and wrapped in a 'does ip support the df option'
check. That is needed regardless of order (kernel vs iproute2).
^ permalink raw reply
* Re: [PATCH v4 1/3] net: emac: implement 802.1Q VLAN TX tagging support
From: David Miller @ 2018-11-07 19:23 UTC (permalink / raw)
To: chunkeey; +Cc: netdev, ivan, f.fainelli
In-Reply-To: <20181106.150954.2230387672447683694.davem@davemloft.net>
From: David Miller <davem@davemloft.net>
Date: Tue, 06 Nov 2018 15:09:54 -0800 (PST)
> From: Christian Lamparter <chunkeey@gmail.com>
> Date: Tue, 6 Nov 2018 23:27:49 +0100
>
>> @@ -1435,6 +1436,22 @@ static inline netdev_tx_t emac_xmit_finish(struct emac_instance *dev, int len)
>> return NETDEV_TX_OK;
>> }
>>
>> +static inline u16 emac_tx_vlan(struct emac_instance *dev, struct sk_buff *skb)
>> +{
>> + /* Handle VLAN TPID and TCI insert if this is a VLAN skb */
>> + if (emac_has_feature(dev, EMAC_FTR_HAS_VLAN_CTAG_TX) &&
>> + skb_vlan_tag_present(skb)) {
>> + struct emac_regs __iomem *p = dev->emacp;
>> +
>> + /* update the VLAN TCI */
>> + out_be32(&p->vtci, (u32)skb_vlan_tag_get(skb));
>
> Hmmm, how does this vtci register work?
I'm tossing your patches since you refuse to answer my question and
explain why this works properly.
Sorry.
^ permalink raw reply
* [net 13/14] i40e: restore NETIF_F_GSO_IPXIP[46] to netdev features
From: Jeff Kirsher @ 2018-11-07 19:16 UTC (permalink / raw)
To: davem; +Cc: Jacob Keller, netdev, nhorman, sassmann, Jeff Kirsher
In-Reply-To: <20181107191631.5072-1-jeffrey.t.kirsher@intel.com>
From: Jacob Keller <jacob.e.keller@intel.com>
Since commit bacd75cfac8a ("i40e/i40evf: Add capability exchange for
outer checksum", 2017-04-06) the i40e driver has not reported support
for IP-in-IP offloads. This likely occurred due to a bad rebase, as the
commit extracts hw_enc_features into its own variable. As part of this
change, it dropped the NETIF_F_FSO_IPXIP flags from the
netdev->hw_enc_features. This was unfortunately not caught during code
review.
Fix this by adding back the missing feature flags.
For reference, NETIF_F_GSO_IPXIP4 was added in commit 7e13318daa4a
("net: define gso types for IPx over IPv4 and IPv6", 2016-05-20),
replacing NETIF_F_GSO_IPIP and NETIF_F_GSO_SIT.
NETIF_F_GSO_IPXIP6 was added in commit bf2d1df39502 ("intel: Add support
for IPv6 IP-in-IP offload", 2016-05-20).
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/ethernet/intel/i40e/i40e_main.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index bc71a21c1dc2..3ff5ee49818b 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -12249,6 +12249,8 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
NETIF_F_GSO_GRE |
NETIF_F_GSO_GRE_CSUM |
NETIF_F_GSO_PARTIAL |
+ NETIF_F_GSO_IPXIP4 |
+ NETIF_F_GSO_IPXIP6 |
NETIF_F_GSO_UDP_TUNNEL |
NETIF_F_GSO_UDP_TUNNEL_CSUM |
NETIF_F_SCTP_CRC |
--
2.19.1
^ permalink raw reply related
* [net 14/14] i40e: enable NETIF_F_NTUPLE and NETIF_F_HW_TC at driver load
From: Jeff Kirsher @ 2018-11-07 19:16 UTC (permalink / raw)
To: davem; +Cc: Jacob Keller, netdev, nhorman, sassmann, Jeff Kirsher
In-Reply-To: <20181107191631.5072-1-jeffrey.t.kirsher@intel.com>
From: Jacob Keller <jacob.e.keller@intel.com>
The assignment of the feature flag NETIF_F_NTUPLE and NETIF_F_HW_TC
occurs prior to the initial setup of the local hw_features variable.
This means the features are set as user-changeable, but are not set in
the currently active feature list. This results in the features being
disabled at the driver's initial load.
Move the assignment after the initial assignment of hw_features, and
assign to the local variable. This ensures that NETIF_F_NTUPLE and
NETIF_F_HW_TC are marked as user-changeable, and also enables them by
default when the driver loads.
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/ethernet/intel/i40e/i40e_main.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 3ff5ee49818b..21c2688d6308 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -12268,13 +12268,13 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
/* record features VLANs can make use of */
netdev->vlan_features |= hw_enc_features | NETIF_F_TSO_MANGLEID;
- if (!(pf->flags & I40E_FLAG_MFP_ENABLED))
- netdev->hw_features |= NETIF_F_NTUPLE | NETIF_F_HW_TC;
-
hw_features = hw_enc_features |
NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_CTAG_RX;
+ if (!(pf->flags & I40E_FLAG_MFP_ENABLED))
+ hw_features |= NETIF_F_NTUPLE | NETIF_F_HW_TC;
+
netdev->hw_features |= hw_features;
netdev->features |= hw_features | NETIF_F_HW_VLAN_CTAG_FILTER;
--
2.19.1
^ permalink raw reply related
* [net 10/14] ice: Fix the bytecount sent to netdev_tx_sent_queue
From: Jeff Kirsher @ 2018-11-07 19:16 UTC (permalink / raw)
To: davem
Cc: Brett Creeley, netdev, nhorman, sassmann, Anirudh Venkataramanan,
Jeff Kirsher
In-Reply-To: <20181107191631.5072-1-jeffrey.t.kirsher@intel.com>
From: Brett Creeley <brett.creeley@intel.com>
Currently if the driver does a TSO offload the bytecount sent to
netdev_tx_sent_queue will be incorrect. This is because in ice_tso we
overwrite the initial value that we set in ice_tx_map. This creates a
mismatch between the Tx and Tx clean flow. In the Tx clean flow we
calculate the bytecount (called total_bytes) as we clean the
descriptors so the value used in the Tx clean path is correct. Fix this
by using += in ice_tso instead of =. This fixes the mismatch in
bytecount mentioned above.
Signed-off-by: Brett Creeley <brett.creeley@intel.com>
Signed-off-by: Anirudh Venkataramanan <anirudh.venkataramanan@intel.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/ethernet/intel/ice/ice_txrx.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.c b/drivers/net/ethernet/intel/ice/ice_txrx.c
index 3387c67c848d..fe5bbabbb41e 100644
--- a/drivers/net/ethernet/intel/ice/ice_txrx.c
+++ b/drivers/net/ethernet/intel/ice/ice_txrx.c
@@ -1520,7 +1520,7 @@ int ice_tso(struct ice_tx_buf *first, struct ice_tx_offload_params *off)
/* update gso_segs and bytecount */
first->gso_segs = skb_shinfo(skb)->gso_segs;
- first->bytecount = (first->gso_segs - 1) * off->header_len;
+ first->bytecount += (first->gso_segs - 1) * off->header_len;
cd_tso_len = skb->len - off->header_len;
cd_mss = skb_shinfo(skb)->gso_size;
--
2.19.1
^ permalink raw reply related
* [net 12/14] ice: Change req_speeds to be u16
From: Jeff Kirsher @ 2018-11-07 19:16 UTC (permalink / raw)
To: davem
Cc: Chinh T Cao, netdev, nhorman, sassmann, Anirudh Venkataramanan,
Jeff Kirsher
In-Reply-To: <20181107191631.5072-1-jeffrey.t.kirsher@intel.com>
From: Chinh T Cao <chinh.t.cao@intel.com>
Since the req_speeds field in struct ice_link_status is a u8,
req_speeds & ICE_AQ_LINK_SPEED_40GB always returns 0. This was caught
by a coverity scan.
Fix this by changing req_speeds to be u16.
Reported-by: Bruce Allan <bruce.w.allan@intel.com>
Signed-off-by: Chinh T Cao <chinh.t.cao@intel.com>
Signed-off-by: Anirudh Venkataramanan <anirudh.venkataramanan@intel.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/ethernet/intel/ice/ice_type.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h
index 12f9432abf11..f4dbc81c1988 100644
--- a/drivers/net/ethernet/intel/ice/ice_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_type.h
@@ -92,12 +92,12 @@ struct ice_link_status {
u64 phy_type_low;
u16 max_frame_size;
u16 link_speed;
+ u16 req_speeds;
u8 lse_ena; /* Link Status Event notification */
u8 link_info;
u8 an_info;
u8 ext_info;
u8 pacing;
- u8 req_speeds;
/* Refer to #define from module_type[ICE_MODULE_TYPE_TOTAL_BYTE] of
* ice_aqc_get_phy_caps structure
*/
--
2.19.1
^ permalink raw reply related
* [net 09/14] ice: Fix tx_timeout in PF driver
From: Jeff Kirsher @ 2018-11-07 19:16 UTC (permalink / raw)
To: davem
Cc: Brett Creeley, netdev, nhorman, sassmann, Anirudh Venkataramanan,
Jeff Kirsher
In-Reply-To: <20181107191631.5072-1-jeffrey.t.kirsher@intel.com>
From: Brett Creeley <brett.creeley@intel.com>
Prior to this commit the driver was running into tx_timeouts when a
queue was stressed enough. This was happening because the HW tail
and SW tail (NTU) were incorrectly out of sync. Consequently this was
causing the HW head to collide with the HW tail, which to the hardware
means that all descriptors posted for Tx have been processed.
Due to the Tx logic used in the driver SW tail and HW tail are allowed
to be out of sync. This is done as an optimization because it allows the
driver to write HW tail as infrequently as possible, while still
updating the SW tail index to keep track. However, there are situations
where this results in the tail never getting updated, resulting in Tx
timeouts.
Tx HW tail write condition:
if (netif_xmit_stopped(txring_txq(tx_ring) || !skb->xmit_more)
writel(sw_tail, tx_ring->tail);
An issue was found in the Tx logic that was causing the afore mentioned
condition for updating HW tail to never happen, causing tx_timeouts.
In ice_xmit_frame_ring we calculate how many descriptors we need for the
Tx transaction based on the skb the kernel hands us. This is then passed
into ice_maybe_stop_tx along with some extra padding to determine if we
have enough descriptors available for this transaction. If we don't then
we return -EBUSY to the stack, otherwise we move on and eventually
prepare the Tx descriptors accordingly in ice_tx_map and set
next_to_watch. In ice_tx_map we make another call to ice_maybe_stop_tx
with a value of MAX_SKB_FRAGS + 4. The key here is that this value is
possibly less than the value we sent in the first call to
ice_maybe_stop_tx in ice_xmit_frame_ring. Now, if the number of unused
descriptors is between MAX_SKB_FRAGS + 4 and the value used in the first
call to ice_maybe_stop_tx in ice_xmit_frame_ring then we do not update
the HW tail because of the "Tx HW tail write condition" above. This is
because in ice_maybe_stop_tx we return success from ice_maybe_stop_tx
instead of calling __ice_maybe_stop_tx and subsequently calling
netif_stop_subqueue, which sets the __QUEUE_STATE_DEV_XOFF bit. This
bit is then checked in the "Tx HW tail write condition" by calling
netif_xmit_stopped and subsequently updating HW tail if the
afore mentioned bit is set.
In ice_clean_tx_irq, if next_to_watch is not NULL, we end up cleaning
the descriptors that HW sets the DD bit on and we have the budget. The
HW head will eventually run into the HW tail in response to the
description in the paragraph above.
The next time through ice_xmit_frame_ring we make the initial call to
ice_maybe_stop_tx with another skb from the stack. This time we do not
have enough descriptors available and we return NETDEV_TX_BUSY to the
stack and end up setting next_to_watch to NULL.
This is where we are stuck. In ice_clean_tx_irq we never clean anything
because next_to_watch is always NULL and in ice_xmit_frame_ring we never
update HW tail because we already return NETDEV_TX_BUSY to the stack and
eventually we hit a tx_timeout.
This issue was fixed by making sure that the second call to
ice_maybe_stop_tx in ice_tx_map is passed a value that is >= the value
that was used on the initial call to ice_maybe_stop_tx in
ice_xmit_frame_ring. This was done by adding the following defines to
make the logic more clear and to reduce the chance of mucking this up
again:
ICE_CACHE_LINE_BYTES 64
ICE_DESCS_PER_CACHE_LINE (ICE_CACHE_LINE_BYTES / \
sizeof(struct ice_tx_desc))
ICE_DESCS_FOR_CTX_DESC 1
ICE_DESCS_FOR_SKB_DATA_PTR 1
The ICE_CACHE_LINE_BYTES being 64 is an assumption being made so we
don't have to figure this out on every pass through the Tx path. Instead
I added a sanity check in ice_probe to verify cache line size and print
a message if it's not 64 Bytes. This will make it easier to file issues
if they are seen when the cache line size is not 64 Bytes when reading
from the GLPCI_CNF2 register.
Signed-off-by: Brett Creeley <brett.creeley@intel.com>
Signed-off-by: Anirudh Venkataramanan <anirudh.venkataramanan@intel.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
.../net/ethernet/intel/ice/ice_hw_autogen.h | 2 ++
drivers/net/ethernet/intel/ice/ice_main.c | 18 ++++++++++++++++++
drivers/net/ethernet/intel/ice/ice_txrx.c | 9 +++++----
drivers/net/ethernet/intel/ice/ice_txrx.h | 17 +++++++++++++++--
4 files changed, 40 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_hw_autogen.h b/drivers/net/ethernet/intel/ice/ice_hw_autogen.h
index 5fdea6ec7675..596b9fb1c510 100644
--- a/drivers/net/ethernet/intel/ice/ice_hw_autogen.h
+++ b/drivers/net/ethernet/intel/ice/ice_hw_autogen.h
@@ -242,6 +242,8 @@
#define GLNVM_ULD 0x000B6008
#define GLNVM_ULD_CORER_DONE_M BIT(3)
#define GLNVM_ULD_GLOBR_DONE_M BIT(4)
+#define GLPCI_CNF2 0x000BE004
+#define GLPCI_CNF2_CACHELINE_SIZE_M BIT(1)
#define PF_FUNC_RID 0x0009E880
#define PF_FUNC_RID_FUNC_NUM_S 0
#define PF_FUNC_RID_FUNC_NUM_M ICE_M(0x7, 0)
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index 82f49dbd762c..333312a1d595 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -1994,6 +1994,22 @@ static int ice_init_interrupt_scheme(struct ice_pf *pf)
return 0;
}
+/**
+ * ice_verify_cacheline_size - verify driver's assumption of 64 Byte cache lines
+ * @pf: pointer to the PF structure
+ *
+ * There is no error returned here because the driver should be able to handle
+ * 128 Byte cache lines, so we only print a warning in case issues are seen,
+ * specifically with Tx.
+ */
+static void ice_verify_cacheline_size(struct ice_pf *pf)
+{
+ if (rd32(&pf->hw, GLPCI_CNF2) & GLPCI_CNF2_CACHELINE_SIZE_M)
+ dev_warn(&pf->pdev->dev,
+ "%d Byte cache line assumption is invalid, driver may have Tx timeouts!\n",
+ ICE_CACHE_LINE_BYTES);
+}
+
/**
* ice_probe - Device initialization routine
* @pdev: PCI device information struct
@@ -2144,6 +2160,8 @@ static int ice_probe(struct pci_dev *pdev,
/* since everything is good, start the service timer */
mod_timer(&pf->serv_tmr, round_jiffies(jiffies + pf->serv_tmr_period));
+ ice_verify_cacheline_size(pf);
+
return 0;
err_alloc_sw_unroll:
diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.c b/drivers/net/ethernet/intel/ice/ice_txrx.c
index 5dae968d853e..3387c67c848d 100644
--- a/drivers/net/ethernet/intel/ice/ice_txrx.c
+++ b/drivers/net/ethernet/intel/ice/ice_txrx.c
@@ -1556,15 +1556,15 @@ int ice_tso(struct ice_tx_buf *first, struct ice_tx_offload_params *off)
* magnitude greater than our largest possible GSO size.
*
* This would then be implemented as:
- * return (((size >> 12) * 85) >> 8) + 1;
+ * return (((size >> 12) * 85) >> 8) + ICE_DESCS_FOR_SKB_DATA_PTR;
*
* Since multiplication and division are commutative, we can reorder
* operations into:
- * return ((size * 85) >> 20) + 1;
+ * return ((size * 85) >> 20) + ICE_DESCS_FOR_SKB_DATA_PTR;
*/
static unsigned int ice_txd_use_count(unsigned int size)
{
- return ((size * 85) >> 20) + 1;
+ return ((size * 85) >> 20) + ICE_DESCS_FOR_SKB_DATA_PTR;
}
/**
@@ -1706,7 +1706,8 @@ ice_xmit_frame_ring(struct sk_buff *skb, struct ice_ring *tx_ring)
* + 1 desc for context descriptor,
* otherwise try next time
*/
- if (ice_maybe_stop_tx(tx_ring, count + 4 + 1)) {
+ if (ice_maybe_stop_tx(tx_ring, count + ICE_DESCS_PER_CACHE_LINE +
+ ICE_DESCS_FOR_CTX_DESC)) {
tx_ring->tx_stats.tx_busy++;
return NETDEV_TX_BUSY;
}
diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.h b/drivers/net/ethernet/intel/ice/ice_txrx.h
index 1d0f58bd389b..75d0eaf6c9dd 100644
--- a/drivers/net/ethernet/intel/ice/ice_txrx.h
+++ b/drivers/net/ethernet/intel/ice/ice_txrx.h
@@ -22,8 +22,21 @@
#define ICE_RX_BUF_WRITE 16 /* Must be power of 2 */
#define ICE_MAX_TXQ_PER_TXQG 128
-/* Tx Descriptors needed, worst case */
-#define DESC_NEEDED (MAX_SKB_FRAGS + 4)
+/* We are assuming that the cache line is always 64 Bytes here for ice.
+ * In order to make sure that is a correct assumption there is a check in probe
+ * to print a warning if the read from GLPCI_CNF2 tells us that the cache line
+ * size is 128 bytes. We do it this way because we do not want to read the
+ * GLPCI_CNF2 register or a variable containing the value on every pass through
+ * the Tx path.
+ */
+#define ICE_CACHE_LINE_BYTES 64
+#define ICE_DESCS_PER_CACHE_LINE (ICE_CACHE_LINE_BYTES / \
+ sizeof(struct ice_tx_desc))
+#define ICE_DESCS_FOR_CTX_DESC 1
+#define ICE_DESCS_FOR_SKB_DATA_PTR 1
+/* Tx descriptors needed, worst case */
+#define DESC_NEEDED (MAX_SKB_FRAGS + ICE_DESCS_FOR_CTX_DESC + \
+ ICE_DESCS_PER_CACHE_LINE + ICE_DESCS_FOR_SKB_DATA_PTR)
#define ICE_DESC_UNUSED(R) \
((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->count) + \
(R)->next_to_clean - (R)->next_to_use - 1)
--
2.19.1
^ permalink raw reply related
* [net 11/14] igb: shorten maximum PHC timecounter update interval
From: Jeff Kirsher @ 2018-11-07 19:16 UTC (permalink / raw)
To: davem
Cc: Miroslav Lichvar, netdev, nhorman, sassmann, Thomas Gleixner,
Jeff Kirsher
In-Reply-To: <20181107191631.5072-1-jeffrey.t.kirsher@intel.com>
From: Miroslav Lichvar <mlichvar@redhat.com>
The timecounter needs to be updated at least once per ~550 seconds in
order to avoid a 40-bit SYSTIM timestamp to be misinterpreted as an old
timestamp.
Since commit 500462a9de65 ("timers: Switch to a non-cascading wheel"),
scheduling of delayed work seems to be less accurate and a requested
delay of 540 seconds may actually be longer than 550 seconds. Also, the
PHC may be adjusted to run up to 6% faster than real time and the system
clock up to 10% slower. Shorten the delay to 360 seconds to be sure the
timecounter is updated in time.
This fixes an issue with HW timestamps on 82580/I350/I354 being off by
~1100 seconds for few seconds every ~9 minutes.
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
Acked-by: Jacob Keller <jacob.e.keller@intel.com>
Acked-by: Richard Cochran <richardcochran@gmail.com>
Tested-by: Aaron Brown <aaron.f.brown@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/ethernet/intel/igb/igb_ptp.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c
index 29ced6b74d36..2b95dc9c7a6a 100644
--- a/drivers/net/ethernet/intel/igb/igb_ptp.c
+++ b/drivers/net/ethernet/intel/igb/igb_ptp.c
@@ -53,13 +53,15 @@
* 2^40 * 10^-9 / 60 = 18.3 minutes.
*
* SYSTIM is converted to real time using a timecounter. As
- * timecounter_cyc2time() allows old timestamps, the timecounter
- * needs to be updated at least once per half of the SYSTIM interval.
- * Scheduling of delayed work is not very accurate, so we aim for 8
- * minutes to be sure the actual interval is shorter than 9.16 minutes.
+ * timecounter_cyc2time() allows old timestamps, the timecounter needs
+ * to be updated at least once per half of the SYSTIM interval.
+ * Scheduling of delayed work is not very accurate, and also the NIC
+ * clock can be adjusted to run up to 6% faster and the system clock
+ * up to 10% slower, so we aim for 6 minutes to be sure the actual
+ * interval in the NIC time is shorter than 9.16 minutes.
*/
-#define IGB_SYSTIM_OVERFLOW_PERIOD (HZ * 60 * 8)
+#define IGB_SYSTIM_OVERFLOW_PERIOD (HZ * 60 * 6)
#define IGB_PTP_TX_TIMEOUT (HZ * 15)
#define INCPERIOD_82576 BIT(E1000_TIMINCA_16NS_SHIFT)
#define INCVALUE_82576_MASK GENMASK(E1000_TIMINCA_16NS_SHIFT - 1, 0)
--
2.19.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