diff mbox series

[RFC,01/13] net: dsa: add plumbing for custom netdev statistics

Message ID 20201017213611.2557565-2-vladimir.oltean@nxp.com
State RFC
Delegated to: David Miller
Headers show
Series Generic TX reallocation for DSA | expand

Commit Message

Vladimir Oltean Oct. 17, 2020, 9:35 p.m. UTC
DSA needs to push a header onto every packet on TX, and this might cause
reallocation under certain scenarios, which might affect, for example,
performance.

But reallocated packets are not standardized in struct pcpu_sw_netstats,
struct net_device_stats or anywhere else, it seems, so we need to roll
our own extra netdevice statistics and expose them to ethtool.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
 net/dsa/dsa_priv.h |  9 +++++++++
 net/dsa/slave.c    | 25 ++++++++++++++++++++++---
 2 files changed, 31 insertions(+), 3 deletions(-)

Comments

Florian Fainelli Oct. 17, 2020, 10:13 p.m. UTC | #1
On 10/17/2020 2:35 PM, Vladimir Oltean wrote:
> DSA needs to push a header onto every packet on TX, and this might cause
> reallocation under certain scenarios, which might affect, for example,
> performance.
> 
> But reallocated packets are not standardized in struct pcpu_sw_netstats,
> struct net_device_stats or anywhere else, it seems, so we need to roll
> our own extra netdevice statistics and expose them to ethtool.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> ---
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Andrew Lunn Oct. 17, 2020, 11:15 p.m. UTC | #2
> +/* Driver statistics, other than those in struct rtnl_link_stats64.
> + * These are collected per-CPU and aggregated by ethtool.
> + */
> +struct dsa_slave_stats {
> +	__u64			tx_reallocs;
> +	struct u64_stats_sync	syncp;
> +} __aligned(1 * sizeof(u64));

The convention seems to be to use a prefix of pcpu_,
e.g. pcpu_sw_netstats, pcpu_lstats.

Also, why __u64? Neither pcpu_sw_netstats nor pcpu_lstats use __u64.

      Andrew
Vladimir Oltean Oct. 18, 2020, 12:23 a.m. UTC | #3
On Sun, Oct 18, 2020 at 01:15:51AM +0200, Andrew Lunn wrote:
> > +/* Driver statistics, other than those in struct rtnl_link_stats64.
> > + * These are collected per-CPU and aggregated by ethtool.
> > + */
> > +struct dsa_slave_stats {
> > +	__u64			tx_reallocs;
> > +	struct u64_stats_sync	syncp;
> > +} __aligned(1 * sizeof(u64));
> 
> The convention seems to be to use a prefix of pcpu_,
> e.g. pcpu_sw_netstats, pcpu_lstats.

I find the "pcpu_sw_netstats" to be long and useless. I can easily see
it's percpu based on its usage, I don't need to have it in its name.

> Also, why __u64? Neither pcpu_sw_netstats nor pcpu_lstats use __u64.

Ok, I'll confess I stole the beginning from the dpaa2-eth driver prior
to commit d70446ee1f40 ("dpaa2-eth: send a scatter-gather FD instead of
realloc-ing"), since I knew it used to implement this counter. Then I
combined with what was already there for the standard statistics in DSA.

But to be honest, what I dislike a little bit is that we have 2
structures now. For example, netronome nfp has created one struct
nfp_repr_pcpu_stats that holds everything, even if it duplicates some
counters found elsewhere. But I think that's a bit easier to digest from
a maintainability point of view, what do you think?
Heiner Kallweit Oct. 18, 2020, 12:02 p.m. UTC | #4
On 17.10.2020 23:35, Vladimir Oltean wrote:
> DSA needs to push a header onto every packet on TX, and this might cause
> reallocation under certain scenarios, which might affect, for example,
> performance.
> 
> But reallocated packets are not standardized in struct pcpu_sw_netstats,
> struct net_device_stats or anywhere else, it seems, so we need to roll
> our own extra netdevice statistics and expose them to ethtool.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> ---
>  net/dsa/dsa_priv.h |  9 +++++++++
>  net/dsa/slave.c    | 25 ++++++++++++++++++++++---
>  2 files changed, 31 insertions(+), 3 deletions(-)
> 
> diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
> index 12998bf04e55..d39db7500cdd 100644
> --- a/net/dsa/dsa_priv.h
> +++ b/net/dsa/dsa_priv.h
> @@ -73,12 +73,21 @@ struct dsa_notifier_mtu_info {
>  	int mtu;
>  };
>  
> +/* Driver statistics, other than those in struct rtnl_link_stats64.
> + * These are collected per-CPU and aggregated by ethtool.
> + */
> +struct dsa_slave_stats {
> +	__u64			tx_reallocs;
> +	struct u64_stats_sync	syncp;
> +} __aligned(1 * sizeof(u64));
> +

Wouldn't a simple unsigned long (like in struct net_device_stats) be
sufficient here? This would make handling the counter much simpler.
And as far as I understand we talk about a packet counter that is
touched in certain scenarios only.

>  struct dsa_slave_priv {
>  	/* Copy of CPU port xmit for faster access in slave transmit hot path */
>  	struct sk_buff *	(*xmit)(struct sk_buff *skb,
>  					struct net_device *dev);
>  
>  	struct pcpu_sw_netstats	__percpu *stats64;
> +	struct dsa_slave_stats	__percpu *extra_stats;
>  
>  	struct gro_cells	gcells;
>  
> diff --git a/net/dsa/slave.c b/net/dsa/slave.c
> index 3bc5ca40c9fb..d4326940233c 100644
> --- a/net/dsa/slave.c
> +++ b/net/dsa/slave.c
> @@ -668,9 +668,10 @@ static void dsa_slave_get_strings(struct net_device *dev,
>  		strncpy(data + len, "tx_bytes", len);
>  		strncpy(data + 2 * len, "rx_packets", len);
>  		strncpy(data + 3 * len, "rx_bytes", len);
> +		strncpy(data + 4 * len, "tx_reallocs", len);
>  		if (ds->ops->get_strings)
>  			ds->ops->get_strings(ds, dp->index, stringset,
> -					     data + 4 * len);
> +					     data + 5 * len);
>  	}
>  }
>  
> @@ -682,11 +683,13 @@ static void dsa_slave_get_ethtool_stats(struct net_device *dev,
>  	struct dsa_slave_priv *p = netdev_priv(dev);
>  	struct dsa_switch *ds = dp->ds;
>  	struct pcpu_sw_netstats *s;
> +	struct dsa_slave_stats *e;
>  	unsigned int start;
>  	int i;
>  
>  	for_each_possible_cpu(i) {
>  		u64 tx_packets, tx_bytes, rx_packets, rx_bytes;
> +		u64 tx_reallocs;
>  
>  		s = per_cpu_ptr(p->stats64, i);
>  		do {
> @@ -696,13 +699,21 @@ static void dsa_slave_get_ethtool_stats(struct net_device *dev,
>  			rx_packets = s->rx_packets;
>  			rx_bytes = s->rx_bytes;
>  		} while (u64_stats_fetch_retry_irq(&s->syncp, start));
> +
> +		e = per_cpu_ptr(p->extra_stats, i);
> +		do {
> +			start = u64_stats_fetch_begin_irq(&e->syncp);
> +			tx_reallocs	= e->tx_reallocs;
> +		} while (u64_stats_fetch_retry_irq(&e->syncp, start));
> +
>  		data[0] += tx_packets;
>  		data[1] += tx_bytes;
>  		data[2] += rx_packets;
>  		data[3] += rx_bytes;
> +		data[4] += tx_reallocs;
>  	}
>  	if (ds->ops->get_ethtool_stats)
> -		ds->ops->get_ethtool_stats(ds, dp->index, data + 4);
> +		ds->ops->get_ethtool_stats(ds, dp->index, data + 5);
>  }
>  
>  static int dsa_slave_get_sset_count(struct net_device *dev, int sset)
> @@ -713,7 +724,7 @@ static int dsa_slave_get_sset_count(struct net_device *dev, int sset)
>  	if (sset == ETH_SS_STATS) {
>  		int count;
>  
> -		count = 4;
> +		count = 5;
>  		if (ds->ops->get_sset_count)
>  			count += ds->ops->get_sset_count(ds, dp->index, sset);
>  
> @@ -1806,6 +1817,12 @@ int dsa_slave_create(struct dsa_port *port)
>  		free_netdev(slave_dev);
>  		return -ENOMEM;
>  	}
> +	p->extra_stats = netdev_alloc_pcpu_stats(struct dsa_slave_stats);
> +	if (!p->extra_stats) {
> +		free_percpu(p->stats64);
> +		free_netdev(slave_dev);
> +		return -ENOMEM;
> +	}
>  
>  	ret = gro_cells_init(&p->gcells, slave_dev);
>  	if (ret)
> @@ -1864,6 +1881,7 @@ int dsa_slave_create(struct dsa_port *port)
>  out_gcells:
>  	gro_cells_destroy(&p->gcells);
>  out_free:
> +	free_percpu(p->extra_stats);
>  	free_percpu(p->stats64);
>  	free_netdev(slave_dev);
>  	port->slave = NULL;
> @@ -1886,6 +1904,7 @@ void dsa_slave_destroy(struct net_device *slave_dev)
>  	dsa_slave_notify(slave_dev, DSA_PORT_UNREGISTER);
>  	phylink_destroy(dp->pl);
>  	gro_cells_destroy(&p->gcells);
> +	free_percpu(p->extra_stats);
>  	free_percpu(p->stats64);
>  	free_netdev(slave_dev);
>  }
>
Vladimir Oltean Oct. 18, 2020, 12:16 p.m. UTC | #5
On Sun, Oct 18, 2020 at 02:02:46PM +0200, Heiner Kallweit wrote:
> Wouldn't a simple unsigned long (like in struct net_device_stats) be
> sufficient here? This would make handling the counter much simpler.
> And as far as I understand we talk about a packet counter that is
> touched in certain scenarios only.

I don't understand, in what sense 'sufficient'? This counter is exported
to ethtool which works with u64 values, how would an unsigned long,
which is u32 on 32-bit systems, help?
Heiner Kallweit Oct. 18, 2020, 1:09 p.m. UTC | #6
On 18.10.2020 14:16, Vladimir Oltean wrote:
> On Sun, Oct 18, 2020 at 02:02:46PM +0200, Heiner Kallweit wrote:
>> Wouldn't a simple unsigned long (like in struct net_device_stats) be
>> sufficient here? This would make handling the counter much simpler.
>> And as far as I understand we talk about a packet counter that is
>> touched in certain scenarios only.
> 
> I don't understand, in what sense 'sufficient'? This counter is exported
> to ethtool which works with u64 values, how would an unsigned long,
> which is u32 on 32-bit systems, help?
> 
Sufficient for me means that it's unlikely that a 32 bit counter will
overflow. Many drivers use the 32 bit counters (on a 32bit system) in
net_device_stats for infrequent events like rx/tx errors, and 64bit
counters only for things like rx/tx bytes, which are more likely to
overflow.
Vladimir Oltean Oct. 18, 2020, 1:48 p.m. UTC | #7
On Sun, Oct 18, 2020 at 03:09:32PM +0200, Heiner Kallweit wrote:
> On 18.10.2020 14:16, Vladimir Oltean wrote:
> > On Sun, Oct 18, 2020 at 02:02:46PM +0200, Heiner Kallweit wrote:
> >> Wouldn't a simple unsigned long (like in struct net_device_stats) be
> >> sufficient here? This would make handling the counter much simpler.
> >> And as far as I understand we talk about a packet counter that is
> >> touched in certain scenarios only.
> > 
> > I don't understand, in what sense 'sufficient'? This counter is exported
> > to ethtool which works with u64 values, how would an unsigned long,
> > which is u32 on 32-bit systems, help?
> > 
> Sufficient for me means that it's unlikely that a 32 bit counter will
> overflow. Many drivers use the 32 bit counters (on a 32bit system) in
> net_device_stats for infrequent events like rx/tx errors, and 64bit
> counters only for things like rx/tx bytes, which are more likely to
> overflow.

2^32 = 4,294,967,296 = 4 billion packets
Considering that every packet that needs TX timestamping must be
reallocated, protocols such as IEEE 802.1AS will trigger 5 reallocs per
second. So time synchronization alone (background traffic, by all
accounts) will make this counter overflow in 27 years.
Every packet flooded or multicast by the bridge will also trigger
reallocs. In this case it is not difficult to imagine that overflows
might occur sooner.

Even if the above wasn't true. What becomes easier when I make the
counter an unsigned long? I need to make arch-dependent casts between an
unsigned long an an u64 when I expose the counter to ethtool, and there
it ends up being u64 too, doesn't it?
Heiner Kallweit Oct. 18, 2020, 2:13 p.m. UTC | #8
On 18.10.2020 15:48, Vladimir Oltean wrote:
> On Sun, Oct 18, 2020 at 03:09:32PM +0200, Heiner Kallweit wrote:
>> On 18.10.2020 14:16, Vladimir Oltean wrote:
>>> On Sun, Oct 18, 2020 at 02:02:46PM +0200, Heiner Kallweit wrote:
>>>> Wouldn't a simple unsigned long (like in struct net_device_stats) be
>>>> sufficient here? This would make handling the counter much simpler.
>>>> And as far as I understand we talk about a packet counter that is
>>>> touched in certain scenarios only.
>>>
>>> I don't understand, in what sense 'sufficient'? This counter is exported
>>> to ethtool which works with u64 values, how would an unsigned long,
>>> which is u32 on 32-bit systems, help?
>>>
>> Sufficient for me means that it's unlikely that a 32 bit counter will
>> overflow. Many drivers use the 32 bit counters (on a 32bit system) in
>> net_device_stats for infrequent events like rx/tx errors, and 64bit
>> counters only for things like rx/tx bytes, which are more likely to
>> overflow.
> 
> 2^32 = 4,294,967,296 = 4 billion packets
> Considering that every packet that needs TX timestamping must be
> reallocated, protocols such as IEEE 802.1AS will trigger 5 reallocs per
> second. So time synchronization alone (background traffic, by all
> accounts) will make this counter overflow in 27 years.
> Every packet flooded or multicast by the bridge will also trigger
> reallocs. In this case it is not difficult to imagine that overflows
> might occur sooner.
> 
> Even if the above wasn't true. What becomes easier when I make the
> counter an unsigned long? I need to make arch-dependent casts between an
> unsigned long an an u64 when I expose the counter to ethtool, and there
> it ends up being u64 too, doesn't it?
> 

Access to unsigned long should be atomic, so you could avoid the
following and access the counter directly (like other drivers do it
with the net_device_stats members). On a side note, because I'm also
just dealing with it: this_cpu_ptr() is safe only if preemption is
disabled. Is this the case here? Else there's get_cpu_ptr/put_cpu_ptr.
Also, if irq's aren't disabled there might be a need to use
u64_stats_update_begin_irqsave() et al. See:
2695578b896a ("net: usbnet: fix potential deadlock on 32bit hosts")
But I don't know dsa good enough to say whether this is applicable
here.

+	e = this_cpu_ptr(p->extra_stats);
+	u64_stats_update_begin(&e->syncp);
+	e->tx_reallocs++;
+	u64_stats_update_end(&e->syncp);

+		e = per_cpu_ptr(p->extra_stats, i);
+		do {
+			start = u64_stats_fetch_begin_irq(&e->syncp);
+			tx_reallocs	= e->tx_reallocs;
+		} while (u64_stats_fetch_retry_irq(&e->syncp, start));
Jakub Kicinski Oct. 18, 2020, 4:49 p.m. UTC | #9
On Sun, 18 Oct 2020 00:35:59 +0300 Vladimir Oltean wrote:
> DSA needs to push a header onto every packet on TX, and this might cause
> reallocation under certain scenarios, which might affect, for example,
> performance.
> 
> But reallocated packets are not standardized in struct pcpu_sw_netstats,
> struct net_device_stats or anywhere else, it seems, so we need to roll
> our own extra netdevice statistics and expose them to ethtool.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>

Could you consider adding "driver" stats under RTM_GETSTATS, 
or a similar new structured interface over ethtool?

Looks like the statistic in question has pretty clear semantics,
and may be more broadly useful.

> +/* Driver statistics, other than those in struct rtnl_link_stats64.
> + * These are collected per-CPU and aggregated by ethtool.
> + */
> +struct dsa_slave_stats {
> +	__u64			tx_reallocs;

s/__u/u/

> +	struct u64_stats_sync	syncp;
> +} __aligned(1 * sizeof(u64));

Why aligned to u64? Compiler should pick a reasonable alignment here 
by itself.
Andrew Lunn Oct. 18, 2020, 5:36 p.m. UTC | #10
On Sun, Oct 18, 2020 at 09:49:51AM -0700, Jakub Kicinski wrote:
> > +	struct u64_stats_sync	syncp;
> > +} __aligned(1 * sizeof(u64));
> 
> Why aligned to u64? Compiler should pick a reasonable alignment here 
> by itself.

Hi Jakub

I wondered that as well. But:

struct gnet_stats_basic_cpu {
        struct gnet_stats_basic_packed bstats;
        struct u64_stats_sync syncp;
} __aligned(2 * sizeof(u64));

/* often modified stats are per-CPU, other are shared (netdev->stats) */
struct pcpu_sw_netstats {
        u64     rx_packets;
        u64     rx_bytes;
        u64     tx_packets;
        u64     tx_bytes;
        struct u64_stats_sync   syncp;
} __aligned(4 * sizeof(u64));

struct pcpu_lstats {
        u64_stats_t packets;
        u64_stats_t bytes;
        struct u64_stats_sync syncp;
} __aligned(2 * sizeof(u64));

Cargo cult or is there a real need?

      Andrew
Jakub Kicinski Oct. 18, 2020, 6:26 p.m. UTC | #11
On Sun, 18 Oct 2020 19:36:49 +0200 Andrew Lunn wrote:
> On Sun, Oct 18, 2020 at 09:49:51AM -0700, Jakub Kicinski wrote:
> > > +	struct u64_stats_sync	syncp;
> > > +} __aligned(1 * sizeof(u64));  
> > 
> > Why aligned to u64? Compiler should pick a reasonable alignment here 
> > by itself.  
> 
> Hi Jakub
> 
> I wondered that as well. But:
> 
> struct gnet_stats_basic_cpu {
>         struct gnet_stats_basic_packed bstats;
>         struct u64_stats_sync syncp;
> } __aligned(2 * sizeof(u64));
> 
> /* often modified stats are per-CPU, other are shared (netdev->stats) */
> struct pcpu_sw_netstats {
>         u64     rx_packets;
>         u64     rx_bytes;
>         u64     tx_packets;
>         u64     tx_bytes;
>         struct u64_stats_sync   syncp;
> } __aligned(4 * sizeof(u64));
> 
> struct pcpu_lstats {
>         u64_stats_t packets;
>         u64_stats_t bytes;
>         struct u64_stats_sync syncp;
> } __aligned(2 * sizeof(u64));
> 
> Cargo cult or is there a real need?

Hm, looks like the intent is to enforce power of two alignment 
to prevent the structure from spanning cache lines. Doesn't make 
any difference for 1 counter, but I guess we can keep the style 
for consistency.
Jakub Kicinski Oct. 18, 2020, 6:33 p.m. UTC | #12
On Sun, 18 Oct 2020 11:26:53 -0700 Jakub Kicinski wrote:
> Hm, looks like the intent is to enforce power of two alignment 
> to prevent the structure from spanning cache lines. Doesn't make 
> any difference for 1 counter, but I guess we can keep the style 
> for consistency.

I take that back, looks like seqcount_t is 4B, so it will make
a difference, don't mind me :)
Vladimir Oltean Oct. 18, 2020, 10:58 p.m. UTC | #13
On Sun, Oct 18, 2020 at 04:13:28PM +0200, Heiner Kallweit wrote:
> On a side note, because I'm also just dealing with it: this_cpu_ptr()
> is safe only if preemption is disabled. Is this the case here? Else
> there's get_cpu_ptr/put_cpu_ptr.

lockdep would shout about using smp_processor_id() in preemptible
context? Because it doesn't do that, and never did, but I don't really
understand why, coming to think of it.

> Also, if irq's aren't disabled there might be a need to use
> u64_stats_update_begin_irqsave() et al. See: 2695578b896a ("net:
> usbnet: fix potential deadlock on 32bit hosts") But I don't know dsa
> good enough to say whether this is applicable here.

DSA xmit and receive path do not run from hardirq context, just softirq,
so I believe the issue reported to Eric there does not apply here.
Florian Fainelli Oct. 18, 2020, 11:11 p.m. UTC | #14
On 10/18/2020 3:58 PM, Vladimir Oltean wrote:
> On Sun, Oct 18, 2020 at 04:13:28PM +0200, Heiner Kallweit wrote:
>> On a side note, because I'm also just dealing with it: this_cpu_ptr()
>> is safe only if preemption is disabled. Is this the case here? Else
>> there's get_cpu_ptr/put_cpu_ptr.
> 
> lockdep would shout about using smp_processor_id() in preemptible
> context? Because it doesn't do that, and never did, but I don't really
> understand why, coming to think of it.
> 
>> Also, if irq's aren't disabled there might be a need to use
>> u64_stats_update_begin_irqsave() et al. See: 2695578b896a ("net:
>> usbnet: fix potential deadlock on 32bit hosts") But I don't know dsa
>> good enough to say whether this is applicable here.
> 
> DSA xmit and receive path do not run from hardirq context, just softirq,
> so I believe the issue reported to Eric there does not apply here.

How about when used as a netconsole? We do support netconsole over DSA 
interfaces.
Vladimir Oltean Oct. 19, 2020, 12:21 a.m. UTC | #15
On Sun, Oct 18, 2020 at 04:11:14PM -0700, Florian Fainelli wrote:
> How about when used as a netconsole? We do support netconsole over DSA
> interfaces.

How? Who is supposed to bring up the master interface, and when?
Florian Fainelli Oct. 19, 2020, 3:49 a.m. UTC | #16
On 10/18/2020 5:21 PM, Vladimir Oltean wrote:
> On Sun, Oct 18, 2020 at 04:11:14PM -0700, Florian Fainelli wrote:
>> How about when used as a netconsole? We do support netconsole over DSA
>> interfaces.
> 
> How? Who is supposed to bring up the master interface, and when?
> 

You are right that this appears not to work when configured on the 
kernel command line:

[    6.836910] netpoll: netconsole: local port 4444
[    6.841553] netpoll: netconsole: local IPv4 address 192.168.1.10
[    6.847582] netpoll: netconsole: interface 'gphy'
[    6.852305] netpoll: netconsole: remote port 9353
[    6.857030] netpoll: netconsole: remote IPv4 address 192.168.1.254
[    6.863233] netpoll: netconsole: remote ethernet address 
b8:ac:6f:80:af:7e
[    6.870134] netpoll: netconsole: device gphy not up yet, forcing it
[    6.876428] netpoll: netconsole: failed to open gphy
[    6.881412] netconsole: cleaning up

looking at my test notes from 2015 when it was added, I had only tested 
dynamic netconsole while the network devices have already been brought 
up which is why I did not catch it. Let me see if I can fix that somehow.
Andrew Lunn Oct. 19, 2020, 12:05 p.m. UTC | #17
On Sun, Oct 18, 2020 at 08:49:31PM -0700, Florian Fainelli wrote:
> 
> 
> On 10/18/2020 5:21 PM, Vladimir Oltean wrote:
> > On Sun, Oct 18, 2020 at 04:11:14PM -0700, Florian Fainelli wrote:
> > > How about when used as a netconsole? We do support netconsole over DSA
> > > interfaces.
> > 
> > How? Who is supposed to bring up the master interface, and when?
> > 
> 
> You are right that this appears not to work when configured on the kernel
> command line:
> 
> [    6.836910] netpoll: netconsole: local port 4444
> [    6.841553] netpoll: netconsole: local IPv4 address 192.168.1.10
> [    6.847582] netpoll: netconsole: interface 'gphy'
> [    6.852305] netpoll: netconsole: remote port 9353
> [    6.857030] netpoll: netconsole: remote IPv4 address 192.168.1.254
> [    6.863233] netpoll: netconsole: remote ethernet address
> b8:ac:6f:80:af:7e
> [    6.870134] netpoll: netconsole: device gphy not up yet, forcing it
> [    6.876428] netpoll: netconsole: failed to open gphy
> [    6.881412] netconsole: cleaning up
> 
> looking at my test notes from 2015 when it was added, I had only tested
> dynamic netconsole while the network devices have already been brought up
> which is why I did not catch it. Let me see if I can fix that somehow.

Hi Florian

NFS root used to work, so there must be some code in the kernel to
bring the master interface up. Might just need copy/pasting.

      Andrew
Florian Fainelli Oct. 19, 2020, 4:27 p.m. UTC | #18
On 10/19/20 5:05 AM, Andrew Lunn wrote:
> On Sun, Oct 18, 2020 at 08:49:31PM -0700, Florian Fainelli wrote:
>>
>>
>> On 10/18/2020 5:21 PM, Vladimir Oltean wrote:
>>> On Sun, Oct 18, 2020 at 04:11:14PM -0700, Florian Fainelli wrote:
>>>> How about when used as a netconsole? We do support netconsole over DSA
>>>> interfaces.
>>>
>>> How? Who is supposed to bring up the master interface, and when?
>>>
>>
>> You are right that this appears not to work when configured on the kernel
>> command line:
>>
>> [    6.836910] netpoll: netconsole: local port 4444
>> [    6.841553] netpoll: netconsole: local IPv4 address 192.168.1.10
>> [    6.847582] netpoll: netconsole: interface 'gphy'
>> [    6.852305] netpoll: netconsole: remote port 9353
>> [    6.857030] netpoll: netconsole: remote IPv4 address 192.168.1.254
>> [    6.863233] netpoll: netconsole: remote ethernet address
>> b8:ac:6f:80:af:7e
>> [    6.870134] netpoll: netconsole: device gphy not up yet, forcing it
>> [    6.876428] netpoll: netconsole: failed to open gphy
>> [    6.881412] netconsole: cleaning up
>>
>> looking at my test notes from 2015 when it was added, I had only tested
>> dynamic netconsole while the network devices have already been brought up
>> which is why I did not catch it. Let me see if I can fix that somehow.
> 
> Hi Florian
> 
> NFS root used to work, so there must be some code in the kernel to
> bring the master interface up. Might just need copy/pasting.

This is a tiny bit different because netconsole goes through netpoll
which is responsible for doing the interface configuration. Unlike root
over NFS, this does not utilize net/ipv4/ipconfig.c, so the existing DSA
checks in that file are not used. The same "cure" could be applied, but
I am not sure if it will be accepted, we shall see.
diff mbox series

Patch

diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index 12998bf04e55..d39db7500cdd 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -73,12 +73,21 @@  struct dsa_notifier_mtu_info {
 	int mtu;
 };
 
+/* Driver statistics, other than those in struct rtnl_link_stats64.
+ * These are collected per-CPU and aggregated by ethtool.
+ */
+struct dsa_slave_stats {
+	__u64			tx_reallocs;
+	struct u64_stats_sync	syncp;
+} __aligned(1 * sizeof(u64));
+
 struct dsa_slave_priv {
 	/* Copy of CPU port xmit for faster access in slave transmit hot path */
 	struct sk_buff *	(*xmit)(struct sk_buff *skb,
 					struct net_device *dev);
 
 	struct pcpu_sw_netstats	__percpu *stats64;
+	struct dsa_slave_stats	__percpu *extra_stats;
 
 	struct gro_cells	gcells;
 
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 3bc5ca40c9fb..d4326940233c 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -668,9 +668,10 @@  static void dsa_slave_get_strings(struct net_device *dev,
 		strncpy(data + len, "tx_bytes", len);
 		strncpy(data + 2 * len, "rx_packets", len);
 		strncpy(data + 3 * len, "rx_bytes", len);
+		strncpy(data + 4 * len, "tx_reallocs", len);
 		if (ds->ops->get_strings)
 			ds->ops->get_strings(ds, dp->index, stringset,
-					     data + 4 * len);
+					     data + 5 * len);
 	}
 }
 
@@ -682,11 +683,13 @@  static void dsa_slave_get_ethtool_stats(struct net_device *dev,
 	struct dsa_slave_priv *p = netdev_priv(dev);
 	struct dsa_switch *ds = dp->ds;
 	struct pcpu_sw_netstats *s;
+	struct dsa_slave_stats *e;
 	unsigned int start;
 	int i;
 
 	for_each_possible_cpu(i) {
 		u64 tx_packets, tx_bytes, rx_packets, rx_bytes;
+		u64 tx_reallocs;
 
 		s = per_cpu_ptr(p->stats64, i);
 		do {
@@ -696,13 +699,21 @@  static void dsa_slave_get_ethtool_stats(struct net_device *dev,
 			rx_packets = s->rx_packets;
 			rx_bytes = s->rx_bytes;
 		} while (u64_stats_fetch_retry_irq(&s->syncp, start));
+
+		e = per_cpu_ptr(p->extra_stats, i);
+		do {
+			start = u64_stats_fetch_begin_irq(&e->syncp);
+			tx_reallocs	= e->tx_reallocs;
+		} while (u64_stats_fetch_retry_irq(&e->syncp, start));
+
 		data[0] += tx_packets;
 		data[1] += tx_bytes;
 		data[2] += rx_packets;
 		data[3] += rx_bytes;
+		data[4] += tx_reallocs;
 	}
 	if (ds->ops->get_ethtool_stats)
-		ds->ops->get_ethtool_stats(ds, dp->index, data + 4);
+		ds->ops->get_ethtool_stats(ds, dp->index, data + 5);
 }
 
 static int dsa_slave_get_sset_count(struct net_device *dev, int sset)
@@ -713,7 +724,7 @@  static int dsa_slave_get_sset_count(struct net_device *dev, int sset)
 	if (sset == ETH_SS_STATS) {
 		int count;
 
-		count = 4;
+		count = 5;
 		if (ds->ops->get_sset_count)
 			count += ds->ops->get_sset_count(ds, dp->index, sset);
 
@@ -1806,6 +1817,12 @@  int dsa_slave_create(struct dsa_port *port)
 		free_netdev(slave_dev);
 		return -ENOMEM;
 	}
+	p->extra_stats = netdev_alloc_pcpu_stats(struct dsa_slave_stats);
+	if (!p->extra_stats) {
+		free_percpu(p->stats64);
+		free_netdev(slave_dev);
+		return -ENOMEM;
+	}
 
 	ret = gro_cells_init(&p->gcells, slave_dev);
 	if (ret)
@@ -1864,6 +1881,7 @@  int dsa_slave_create(struct dsa_port *port)
 out_gcells:
 	gro_cells_destroy(&p->gcells);
 out_free:
+	free_percpu(p->extra_stats);
 	free_percpu(p->stats64);
 	free_netdev(slave_dev);
 	port->slave = NULL;
@@ -1886,6 +1904,7 @@  void dsa_slave_destroy(struct net_device *slave_dev)
 	dsa_slave_notify(slave_dev, DSA_PORT_UNREGISTER);
 	phylink_destroy(dp->pl);
 	gro_cells_destroy(&p->gcells);
+	free_percpu(p->extra_stats);
 	free_percpu(p->stats64);
 	free_netdev(slave_dev);
 }