diff mbox

[U-Boot] net: Improve the speed of netconsole

Message ID 1343160675-30148-1-git-send-email-joe.hershberger@ni.com
State Superseded
Delegated to: Joe Hershberger
Headers show

Commit Message

Joe Hershberger July 24, 2012, 8:11 p.m. UTC
Previously u-boot would initialize the network interface for every
network operation and then shut it down again.  This makes sense for
most operations where the network in not known to be needed soon after
the operation is complete.  In the case of netconsole, it will use the
network for every interaction with the shell or every printf.  This
means that the network is being reinitialized very often.  On many
devices, this intialization is very slow.

This patch checks for consecutive netconsole actions and leaves the
ethernet hardware initialized between them.  It will still behave the
same old way for all other network operations and any time another
network operation happens between netconsole operations.

Signed-off-by: Joe Hershberger <joe.hershberger@ni.com>
Cc: Stefano Babic <sbabic@denx.de>
---
 doc/README.NetConsole    |    3 +++
 drivers/net/netconsole.c |   26 ++++++++++++++++++++++----
 include/net.h            |    6 ++++++
 net/eth.c                |   14 ++++++++++++++
 net/net.c                |   44 +++++++++++++++++++++++++++++++++++++++-----
 5 files changed, 84 insertions(+), 9 deletions(-)

Comments

Stefano Babic July 25, 2012, 9:58 a.m. UTC | #1
On 24/07/2012 22:11, Joe Hershberger wrote:
> Previously u-boot would initialize the network interface for every
> network operation and then shut it down again.  This makes sense for
> most operations where the network in not known to be needed soon after
> the operation is complete.  In the case of netconsole, it will use the
> network for every interaction with the shell or every printf.  This
> means that the network is being reinitialized very often.  On many
> devices, this intialization is very slow.

Hi Joe,

agree with you - and in some cases, such as ethernet on USB, netconsole
is not yet useful.

Which is the status of the ethernet controller before starting the
kernel ? Up now we guarantee that the hardware is turned off before
kernel takes over. This patch changes this behavior, and I know from the
past (really with USB, we have a usb_stop before calling the kernel)
that this can be dangerous.

Maybe you can add a eth_halt() call into do_bootm().

Second general question is if the behavior of
CONFIG_NETCONSOLE_PERSIST_ETH should be always turned on when Netconsole
is activated. I mean, your patch maintain a high compatibility with the
past, but I suppose that everyone using netconsole likes the new
behavior that you are introducing and everyone will turn
CONFIG_NETCONSOLE_PERSIST_ETH on. Then we can simply use
CONFIG_NETCONSOLE as switch.

Best regards,
Stefano
Mike Frysinger July 25, 2012, 6:49 p.m. UTC | #2
On Tuesday 24 July 2012 16:11:15 Joe Hershberger wrote:
> --- a/drivers/net/netconsole.c
> +++ b/drivers/net/netconsole.c
> @@ -131,8 +131,17 @@ static void nc_send_packet(const char *buf, int len)
>  	}
> 
>  	if (eth->state != ETH_STATE_ACTIVE) {
> -		if (eth_init(gd->bd) < 0)
> -			return;
> +#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
> +		if (net_loop_last_protocol != NETCONS) {
> +#endif
> +			if (eth_init(gd->bd) < 0)
> +				return;
> +#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
> +			net_loop_last_protocol = NETCONS;
> +		} else {
> +			eth_init_state_only(gd->bd);
> +		}
> +#endif

seems to me these pre/post clauses should really get refactored someway.  the 
ifdef noise is significant here.

so in the header, something like:

static inline int eth_reinit_protocol(int protocol)
{
#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
	return protocol != NETCONS;
#else
	return 1;
#endif
}
static inline void eth_set_last_protocol(int protocol)
{
#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
	net_loop_last_protocol = protocol;
#endif
}
#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
extern enum proto_t net_loop_last_protocol;
#else
# define net_loop_last_protocol -1
#endif

then the source becomes the more manageable:

if (eth_reinit_protocol(net_loop_last_protocol)) {
	if (eth_init(gd->bd) < 0)
		return;
	eth_set_last_protocol(NETCONS);
} else
	eth_init_state_only(gd->bd);

> --- a/net/eth.c
> +++ b/net/eth.c
> 
> +#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
> +int eth_init_state_only(bd_t *bis)
> +{
> +	eth_current->state = ETH_STATE_ACTIVE;
> +
> +	return 0;
> +}
> +
> +void eth_halt_state_only(void)
> +{
> +	eth_current->state = ETH_STATE_PASSIVE;
> +}
> +#endif

these *really* should be static inlines in the global header.  they're so dirt 
simple, the overhead of the function call is probably much higher than the 
single memory store.
-mike
Joe Hershberger July 30, 2012, 9:08 p.m. UTC | #3
Hi Mike,

On Wed, Jul 25, 2012 at 1:49 PM, Mike Frysinger <vapier@gentoo.org> wrote:
> On Tuesday 24 July 2012 16:11:15 Joe Hershberger wrote:
>> --- a/drivers/net/netconsole.c
>> +++ b/drivers/net/netconsole.c
>> @@ -131,8 +131,17 @@ static void nc_send_packet(const char *buf, int len)
>>       }
>>
>>       if (eth->state != ETH_STATE_ACTIVE) {
>> -             if (eth_init(gd->bd) < 0)
>> -                     return;
>> +#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
>> +             if (net_loop_last_protocol != NETCONS) {
>> +#endif
>> +                     if (eth_init(gd->bd) < 0)
>> +                             return;
>> +#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
>> +                     net_loop_last_protocol = NETCONS;
>> +             } else {
>> +                     eth_init_state_only(gd->bd);
>> +             }
>> +#endif
>
> seems to me these pre/post clauses should really get refactored someway.  the
> ifdef noise is significant here.

I agree.

> so in the header, something like:
>
> static inline int eth_reinit_protocol(int protocol)
> {
> #ifdef CONFIG_NETCONSOLE_PERSIST_ETH
>         return protocol != NETCONS;
> #else
>         return 1;
> #endif
> }
> static inline void eth_set_last_protocol(int protocol)
> {
> #ifdef CONFIG_NETCONSOLE_PERSIST_ETH
>         net_loop_last_protocol = protocol;
> #endif
> }
> #ifdef CONFIG_NETCONSOLE_PERSIST_ETH
> extern enum proto_t net_loop_last_protocol;
> #else
> # define net_loop_last_protocol -1
> #endif
>
> then the source becomes the more manageable:
>
> if (eth_reinit_protocol(net_loop_last_protocol)) {
>         if (eth_init(gd->bd) < 0)
>                 return;
>         eth_set_last_protocol(NETCONS);
> } else
>         eth_init_state_only(gd->bd);

Much nicer.  With static inline it should compile to the same thing.

>> --- a/net/eth.c
>> +++ b/net/eth.c
>>
>> +#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
>> +int eth_init_state_only(bd_t *bis)
>> +{
>> +     eth_current->state = ETH_STATE_ACTIVE;
>> +
>> +     return 0;
>> +}
>> +
>> +void eth_halt_state_only(void)
>> +{
>> +     eth_current->state = ETH_STATE_PASSIVE;
>> +}
>> +#endif
>
> these *really* should be static inlines in the global header.  they're so dirt
> simple, the overhead of the function call is probably much higher than the
> single memory store.

I can do that, but I don't think it will save anything.  Since
eth_current is static, I would have to change it to eth_get_dev(), and
we're back to a function call.  Thoughts?

Thanks,
-Joe
Mike Frysinger Aug. 1, 2012, 4:35 p.m. UTC | #4
On Monday 30 July 2012 17:08:41 Joe Hershberger wrote:
> On Wed, Jul 25, 2012 at 1:49 PM, Mike Frysinger wrote:
> > On Tuesday 24 July 2012 16:11:15 Joe Hershberger wrote:
> >> --- a/net/eth.c
> >> +++ b/net/eth.c
> >> 
> >> +#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
> >> +int eth_init_state_only(bd_t *bis)
> >> +{
> >> +     eth_current->state = ETH_STATE_ACTIVE;
> >> +
> >> +     return 0;
> >> +}
> >> +
> >> +void eth_halt_state_only(void)
> >> +{
> >> +     eth_current->state = ETH_STATE_PASSIVE;
> >> +}
> >> +#endif
> > 
> > these *really* should be static inlines in the global header.  they're so
> > dirt simple, the overhead of the function call is probably much higher
> > than the single memory store.
> 
> I can do that, but I don't think it will save anything.  Since
> eth_current is static, I would have to change it to eth_get_dev(), and
> we're back to a function call.  Thoughts?

i wonder why eth_get_dev is an external func then :)
-mike
Joe Hershberger Aug. 1, 2012, 4:42 p.m. UTC | #5
Hi Mike,

On Wed, Aug 1, 2012 at 11:35 AM, Mike Frysinger <vapier@gentoo.org> wrote:
> On Monday 30 July 2012 17:08:41 Joe Hershberger wrote:
>> On Wed, Jul 25, 2012 at 1:49 PM, Mike Frysinger wrote:
>> > On Tuesday 24 July 2012 16:11:15 Joe Hershberger wrote:
>> >> --- a/net/eth.c
>> >> +++ b/net/eth.c
>> >>
>> >> +#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
>> >> +int eth_init_state_only(bd_t *bis)
>> >> +{
>> >> +     eth_current->state = ETH_STATE_ACTIVE;
>> >> +
>> >> +     return 0;
>> >> +}
>> >> +
>> >> +void eth_halt_state_only(void)
>> >> +{
>> >> +     eth_current->state = ETH_STATE_PASSIVE;
>> >> +}
>> >> +#endif
>> >
>> > these *really* should be static inlines in the global header.  they're so
>> > dirt simple, the overhead of the function call is probably much higher
>> > than the single memory store.
>>
>> I can do that, but I don't think it will save anything.  Since
>> eth_current is static, I would have to change it to eth_get_dev(), and
>> we're back to a function call.  Thoughts?
>
> i wonder why eth_get_dev is an external func then :)

An attempt at encapsulation I assume.  I guess I could just make it global.

-Joe
diff mbox

Patch

diff --git a/doc/README.NetConsole b/doc/README.NetConsole
index c8bcb90..7a20bf5 100644
--- a/doc/README.NetConsole
+++ b/doc/README.NetConsole
@@ -6,6 +6,9 @@  serial and network input/output devices by adjusting the 'stdin' and
 set either of these variables to "nc". Input and output can be
 switched independently.
 
+CONFIG_NETCONSOLE_PERSIST_ETH - Don't reinitialize the Ethernet driver
+		between consecutive calls to netconsole.
+
 We use an environment variable 'ncip' to set the IP address and the
 port of the destination. The format is <ip_addr>:<port>. If <port> is
 omitted, the value of 6666 is used. If the env var doesn't exist, the
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index 14243b8..0218302 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -131,8 +131,17 @@  static void nc_send_packet(const char *buf, int len)
 	}
 
 	if (eth->state != ETH_STATE_ACTIVE) {
-		if (eth_init(gd->bd) < 0)
-			return;
+#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
+		if (net_loop_last_protocol != NETCONS) {
+#endif
+			if (eth_init(gd->bd) < 0)
+				return;
+#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
+			net_loop_last_protocol = NETCONS;
+		} else {
+			eth_init_state_only(gd->bd);
+		}
+#endif
 		inited = 1;
 	}
 	pkt = (uchar *)NetTxPacket + NetEthHdrSize() + IP_UDP_HDR_SIZE;
@@ -141,8 +150,17 @@  static void nc_send_packet(const char *buf, int len)
 	ip = nc_ip;
 	NetSendUDPPacket(ether, ip, nc_port, nc_port, len);
 
-	if (inited)
-		eth_halt();
+	if (inited) {
+#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
+		if (net_loop_last_protocol != NETCONS) {
+#endif
+			eth_halt();
+#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
+		} else {
+			eth_halt_state_only();
+		}
+#endif
+	}
 }
 
 static int nc_start(void)
diff --git a/include/net.h b/include/net.h
index 6d2d6cd..0b75e10 100644
--- a/include/net.h
+++ b/include/net.h
@@ -151,6 +151,12 @@  extern int eth_rx(void);			/* Check for received packets */
 extern void eth_halt(void);			/* stop SCC */
 extern char *eth_get_name(void);		/* get name of current device */
 
+#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
+extern int eth_init_state_only(bd_t *bis);		/* Set active state */
+extern void eth_halt_state_only(void);		/* Set passive state */
+extern enum proto_t net_loop_last_protocol;
+#endif
+
 /*
  * Set the hardware address for an ethernet interface based on 'eth%daddr'
  * environment variable (or just 'ethaddr' if eth_number is 0).
diff --git a/net/eth.c b/net/eth.c
index d526264..54841b9 100644
--- a/net/eth.c
+++ b/net/eth.c
@@ -436,6 +436,20 @@  void eth_halt(void)
 	eth_current->state = ETH_STATE_PASSIVE;
 }
 
+#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
+int eth_init_state_only(bd_t *bis)
+{
+	eth_current->state = ETH_STATE_ACTIVE;
+
+	return 0;
+}
+
+void eth_halt_state_only(void)
+{
+	eth_current->state = ETH_STATE_PASSIVE;
+}
+#endif
+
 int eth_send(void *packet, int length)
 {
 	if (!eth_current)
diff --git a/net/net.c b/net/net.c
index 9de7d92..184c182 100644
--- a/net/net.c
+++ b/net/net.c
@@ -180,6 +180,14 @@  IPaddr_t	NetNtpServerIP;
 int		NetTimeOffset;
 #endif
 
+#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
+/*
+ * Start with a default last protocol.
+ * We are only interested in NETCONS or not.
+ */
+enum proto_t net_loop_last_protocol = BOOTP;
+#endif
+
 uchar PktBuf[(PKTBUFSRX+1) * PKTSIZE_ALIGN + PKTALIGN];
 
 /* Receive packet */
@@ -314,12 +322,20 @@  int NetLoop(enum proto_t protocol)
 
 	bootstage_mark_name(BOOTSTAGE_ID_ETH_START, "eth_start");
 	net_init();
-	eth_halt();
-	eth_set_current();
-	if (eth_init(bd) < 0) {
+#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
+	if (protocol != NETCONS || net_loop_last_protocol != NETCONS) {
+#endif
 		eth_halt();
-		return -1;
+		eth_set_current();
+		if (eth_init(bd) < 0) {
+			eth_halt();
+			return -1;
+		}
+#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
+	} else {
+		eth_init_state_only(bd);
 	}
+#endif
 
 restart:
 	memcpy(NetOurEther, eth_get_dev()->enetaddr, 6);
@@ -461,6 +477,10 @@  restart:
 
 			net_cleanup_loop();
 			eth_halt();
+#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
+			/* Invalidate the last protocol */
+			net_loop_last_protocol = BOOTP;
+#endif
 			puts("\nAbort\n");
 			/* include a debug print as well incase the debug
 			   messages are directed to stderr */
@@ -518,13 +538,27 @@  restart:
 				sprintf(buf, "%lX", (unsigned long)load_addr);
 				setenv("fileaddr", buf);
 			}
-			eth_halt();
+#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
+			if (protocol != NETCONS) {
+#endif
+				eth_halt();
+#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
+			} else {
+				eth_halt_state_only();
+			}
+			/* Remember what the last protocol was */
+			net_loop_last_protocol = protocol;
+#endif
 			ret = NetBootFileXferSize;
 			debug_cond(DEBUG_INT_STATE, "--- NetLoop Success!\n");
 			goto done;
 
 		case NETLOOP_FAIL:
 			net_cleanup_loop();
+#ifdef CONFIG_NETCONSOLE_PERSIST_ETH
+			/* Invalidate the last protocol */
+			net_loop_last_protocol = BOOTP;
+#endif
 			debug_cond(DEBUG_INT_STATE, "--- NetLoop Fail!\n");
 			goto done;