[RFC 0/2] netconsole and networking co-existence

Commit d9506cd41ce98e10a29c25c4766878367103bb2d attempts to fix ping in netconsole [1] but IIUC the fix is incomplete (at least in our config).
[1] https://patchwork.ozlabs.org/project/uboot/patch/20201221034439.2747170-1-yl...
Two issues were identified:
Firstly that the logic of eth_halt() in the various network operations would break an already running netconsole.
Secondly, netconsole uses the net output buffer which means that any nested network operation (such as arp or ping) will cause an overwrite of the netconsole data.
Marked as RFC since not all network protocols have been tested, and not all nc configurations have been tested (e.g. broadcast address).
Tested against ge_bx50v3_defconfig (i.MX6) using the fec.
Tested against 2020.10 050acee119b3757fee3bd128f55d720fdd9bb890, using a configuration that includes CONFIG_NETCONSOLE, CONFIG_CMD_NFS, and CONFIG_CMD_PING.
A sample session is as follows:
Configure (via serial console)
=> env set ethaddr ca:fe:de:ca:f0:11 ; env set ipaddr 192.168.120.49 ; setenv stdout serial,nc; setenv stdin serial,nc => env set ncip 192.168.120.1
Invoke net console
./netconsole 192.168.120.49
Invoke ping (via serial console)
=> ping 192.168.120.1 Using ethernet@2188000 device host 192.168.120.1 is alive => _
Output on net console
Board out port: 6666 Board in port: 6666 NOTE: the interrupt signal (normally ^C) has been remapped to ^T ncb: not found
=> ping 192.168.120.1 Using ethernet@2188000 device host 192.168.120.1 is alive =>
Invoke ping (via netconsole) for an IP address that is not reachable. Type ^C to abort. The ^C is not echoed.
=> ping 192.168.120.2 Using ethernet@2188000 device
Abort ping failed; host 192.168.120.2 is not alive => _
Output on serial console
=> ping 192.168.120.2 Using ethernet@2188000 device
Abort ping failed; host 192.168.120.2 is not alive =>
The following tcpdump fragment captures the typing of the letters "ok" in the net console. There was approximately 3 seconds between each letter.
> sudo tcpdump -i enx001a9f0c4f04 -n host 192.168.120.49 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on enx001a9f0c4f04, link-type EN10MB (Ethernet), capture size 262144 bytes 14:20:05.856372 IP 192.168.120.1.53891 > 192.168.120.49.6666: UDP, length 1 'o' from host (nc stdin) to target 14:20:05.856684 ARP, Request who-has 192.168.120.1 tell 192.168.120.49, length 46 14:20:05.856709 ARP, Reply 192.168.120.1 is-at 00:1a:9f:0c:4f:04, length 28 14:20:05.856981 IP 192.168.120.49.6666 > 192.168.120.1.6666: UDP, length 1 'o' from target (stdout) to host 14:20:06.626637 ARP, Request who-has 192.168.120.49 tell 192.168.120.1, length 28 14:20:06.626915 ARP, Reply 192.168.120.49 is-at ca:fe:de:ca:f0:11, length 46 14:20:09.364465 IP 192.168.120.1.53891 > 192.168.120.49.6666: UDP, length 1 'k' from host to target 14:20:09.364776 ARP, Request who-has 192.168.120.1 tell 192.168.120.49, length 46 14:20:09.364794 ARP, Reply 192.168.120.1 is-at 00:1a:9f:0c:4f:04, length 28 14:20:09.365041 IP 192.168.120.49.6666 > 192.168.120.1.6666: UDP, length 1 'k' from target to host
The following tcpdump fragment captures the typing of the letters "ok" in the serial console. There was approximately 3 seconds between each letter.
14:22:48.225004 ARP, Request who-has 192.168.120.1 tell 192.168.120.49, length 46 14:22:48.225032 ARP, Reply 192.168.120.1 is-at 00:1a:9f:0c:4f:04, length 28 14:22:48.225291 IP 192.168.120.49.6666 > 192.168.120.1.6666: UDP, length 1 'o' from target (stdout) to host 14:22:51.777002 ARP, Request who-has 192.168.120.1 tell 192.168.120.49, length 46 14:22:51.777031 ARP, Reply 192.168.120.1 is-at 00:1a:9f:0c:4f:04, length 28 14:22:51.777251 IP 192.168.120.49.6666 > 192.168.120.1.6666: UDP, length 1 'k' from target to host
The following tcpdump fragment captures ping to a known host. (Not shown: the command "ping 192.168.120.1")
14:23:56.873103 ARP, Request who-has 192.168.120.1 tell 192.168.120.49, length 46 14:23:56.873131 ARP, Reply 192.168.120.1 is-at 00:1a:9f:0c:4f:04, length 28 14:23:56.873426 IP 192.168.120.49.6666 > 192.168.120.1.6666: UDP, length 1 '\n' 14:23:56.876020 IP 192.168.120.49.6666 > 192.168.120.1.6666: UDP, length 30 "Using ethernet@2188000 device" 14:23:56.876021 ARP, Request who-has 192.168.120.1 tell 192.168.120.49, length 46 14:23:56.876060 ARP, Reply 192.168.120.1 is-at 00:1a:9f:0c:4f:04, length 28 14:23:56.876354 IP 192.168.120.49 > 192.168.120.1: ICMP echo request, id 0, seq 5, length 8 14:23:56.876385 IP 192.168.120.1 > 192.168.120.49: ICMP echo reply, id 0, seq 5, length 8 14:23:56.879037 IP 192.168.120.49.6666 > 192.168.120.1.6666: UDP, length 28 "host 192.168.120.1 is alive" 14:23:56.879272 IP 192.168.120.49.6666 > 192.168.120.1.6666: UDP, length 3 "=> " 14:24:01.890653 ARP, Request who-has 192.168.120.49 tell 192.168.120.1, length 28 14:24:01.890933 ARP, Reply 192.168.120.49 is-at ca:fe:de:ca:f0:11, length 46
And, finally, the following tcpdump fragment captures ping to an unknown host. (Not shown: the command "ping 192.168.120.2")
14:35:57.680889 ARP, Request who-has 192.168.120.2 tell 192.168.120.49, length 46 14:36:02.681255 ARP, Request who-has 192.168.120.2 tell 192.168.120.49, length 46 14:36:02.786656 ARP, Request who-has 192.168.120.49 tell 192.168.120.1, length 28 14:36:02.786953 ARP, Reply 192.168.120.49 is-at ca:fe:de:ca:f0:11, length 46 14:36:04.154698 IP 192.168.120.1.53891 > 192.168.120.49.6666: UDP, length 1 ^C 14:36:04.155671 IP 192.168.120.49.6666 > 192.168.120.1.6666: UDP, length 7 "Abort" 14:36:04.159672 IP 192.168.120.49.6666 > 192.168.120.1.6666: UDP, length 45 "ping failed; host 192.168.120.2 is not alive" 14:36:04.159878 IP 192.168.120.49.6666 > 192.168.120.1.6666: UDP, length 3 "=> "
Ian Ray (2): net: net_up, net_down netconsole and networking co-existence
drivers/net/netconsole.c | 45 +++++++------------ include/net.h | 28 ++++-------- net/arp.c | 7 ++- net/arp.h | 1 + net/net.c | 111 ++++++++++++++++++++++++++++++++++------------- net/ping.c | 4 +- net/tftp.c | 4 -- net/wol.c | 1 - 8 files changed, 111 insertions(+), 90 deletions(-)
-- 2.10.1

Calls made to eth_halt() by network operations (ping, nfs, tftp, etc.) break netconsole if it is already running.
* Maintain the network up / down state based on a reference count, using new APIs net_up() and net_down().
Note that when network is brought up by netconsole client, then network will remain up until reboot (because there is no way to stop netconsole itself).
* Remove net_loop_last_protocol, eth_is_on_demand_init(), and eth_set_last_protocol(). This functionality is replaced by net_up() / net_down().
* Replace usage of eth_init(), eth_halt() with net_up() and net_down() in net.c, ping.c, tftp.c, and netconsole.c.
Signed-off-by: Ian Ray ian.ray@ge.com --- drivers/net/netconsole.c | 26 +++-------------- include/net.h | 23 ++------------- net/net.c | 75 +++++++++++++++++++++++++++++++++--------------- net/ping.c | 1 - net/tftp.c | 4 --- net/wol.c | 1 - 6 files changed, 59 insertions(+), 71 deletions(-)
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index f1d0630..b6d2e22 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -27,11 +27,6 @@ static short nc_out_port; /* target output port */ static short nc_in_port; /* source input port */ static const char *output_packet; /* used by first send udp */ static int output_packet_len; -/* - * Start with a default last protocol. - * We are only interested in NETCONS or not. - */ -enum proto_t net_loop_last_protocol = BOOTP;
static void nc_wait_arp_handler(uchar *pkt, unsigned dest, struct in_addr sip, unsigned src, @@ -177,7 +172,6 @@ static void nc_send_packet(const char *buf, int len) #else struct eth_device *eth; #endif - int inited = 0; uchar *pkt; uchar *ether; struct in_addr ip; @@ -200,29 +194,17 @@ static void nc_send_packet(const char *buf, int len) return; }
- if (!eth_is_active(eth)) { - if (eth_is_on_demand_init()) { - if (eth_init() < 0) - return; - eth_set_last_protocol(NETCONS); - } else { - eth_init_state_only(); - } - - inited = 1; + if (net_up(NETCONS) < 0) { + return; } + pkt = (uchar *)net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE; memcpy(pkt, buf, len); ether = nc_ether; ip = nc_ip; net_send_udp_packet(ether, ip, nc_out_port, nc_in_port, len);
- if (inited) { - if (eth_is_on_demand_init()) - eth_halt(); - else - eth_halt_state_only(); - } + net_down(); }
static int nc_stdio_start(struct stdio_dev *dev) diff --git a/include/net.h b/include/net.h index 1bf9867..cc6be60 100644 --- a/include/net.h +++ b/include/net.h @@ -599,6 +599,9 @@ int net_loop(enum proto_t); /* Load failed. Start again. */ int net_start_again(void);
+int net_up(enum proto_t protocol); +int net_down(void); + /* Get size of the ethernet header when we send */ int net_eth_hdr_size(void);
@@ -706,26 +709,6 @@ int nc_input_packet(uchar *pkt, struct in_addr src_ip, unsigned dest_port, unsigned src_port, unsigned len); #endif
-static __always_inline int eth_is_on_demand_init(void) -{ -#if defined(CONFIG_NETCONSOLE) && !defined(CONFIG_SPL_BUILD) - extern enum proto_t net_loop_last_protocol; - - return net_loop_last_protocol != NETCONS; -#else - return 1; -#endif -} - -static inline void eth_set_last_protocol(int protocol) -{ -#if defined(CONFIG_NETCONSOLE) && !defined(CONFIG_SPL_BUILD) - extern enum proto_t net_loop_last_protocol; - - net_loop_last_protocol = protocol; -#endif -} - /* * Check if autoload is enabled. If so, use either NFS or TFTP to download * the boot file. diff --git a/net/net.c b/net/net.c index 28d9eeb..45d4f19 100644 --- a/net/net.c +++ b/net/net.c @@ -404,6 +404,51 @@ void net_init(void) * Main network processing loop. */
+static int up_ref; +static bool up; +static bool keep_up; + +/// Bring net up/active if needed. +int net_up(enum proto_t protocol) +{ + int ret; + if (!up) { + eth_halt(); + eth_set_current(); + ret = eth_init(); + if (ret < 0) { + debug_cond(DEBUG_INT_STATE, "net_up failed\n"); + eth_halt(); + return ret; + } + up = true; + } else if (up_ref == 0) { + eth_init_state_only(); + } + up_ref++; + if (protocol == NETCONS) { + keep_up = true; + } + return 0; +} + +/// Set net inactive if no clients remain. +int net_down(void) +{ + if (up_ref > 0) { + up_ref--; + if (up_ref == 0) { + if (keep_up) { + eth_halt_state_only(); + } else { + up = false; + eth_halt(); + } + } + } + return 0; +} + int net_loop(enum proto_t protocol) { int ret = -EINVAL; @@ -420,16 +465,9 @@ int net_loop(enum proto_t protocol)
bootstage_mark_name(BOOTSTAGE_ID_ETH_START, "eth_start"); net_init(); - if (eth_is_on_demand_init() || protocol != NETCONS) { - eth_halt(); - eth_set_current(); - ret = eth_init(); - if (ret < 0) { - eth_halt(); - return ret; - } - } else { - eth_init_state_only(); + ret = net_up(protocol); + if (ret < 0) { + return ret; } restart: #ifdef CONFIG_USB_KEYBOARD @@ -448,7 +486,7 @@ restart: switch (net_check_prereq(protocol)) { case 1: /* network not configured */ - eth_halt(); + net_down(); net_set_state(prev_net_state); return -ENODEV;
@@ -589,9 +627,7 @@ restart: net_arp_wait_packet_ip.s_addr = 0;
net_cleanup_loop(); - eth_halt(); - /* Invalidate the last protocol */ - eth_set_last_protocol(BOOTP); + net_down();
puts("\nAbort\n"); /* include a debug print as well incase the debug @@ -647,12 +683,7 @@ restart: env_set_hex("filesize", net_boot_file_size); env_set_hex("fileaddr", image_load_addr); } - if (protocol != NETCONS) - eth_halt(); - else - eth_halt_state_only(); - - eth_set_last_protocol(protocol); + net_down();
ret = net_boot_file_size; debug_cond(DEBUG_INT_STATE, "--- net_loop Success!\n"); @@ -660,8 +691,7 @@ restart:
case NETLOOP_FAIL: net_cleanup_loop(); - /* Invalidate the last protocol */ - eth_set_last_protocol(BOOTP); + net_down(); debug_cond(DEBUG_INT_STATE, "--- net_loop Fail!\n"); ret = -ENONET; goto done; @@ -719,7 +749,6 @@ int net_start_again(void) }
if ((!retry_forever) && (net_try_count > retrycnt)) { - eth_halt(); net_set_state(NETLOOP_FAIL); /* * We don't provide a way for the protocol to return an error, diff --git a/net/ping.c b/net/ping.c index 0e33660..2c92806 100644 --- a/net/ping.c +++ b/net/ping.c @@ -64,7 +64,6 @@ static int ping_send(void)
static void ping_timeout_handler(void) { - eth_halt(); net_set_state(NETLOOP_FAIL); /* we did not get the reply */ }
diff --git a/net/tftp.c b/net/tftp.c index 84e970b..dcc387b 100644 --- a/net/tftp.c +++ b/net/tftp.c @@ -652,7 +652,6 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip, net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
if (store_block(tftp_cur_block - 1, pkt + 2, len)) { - eth_halt(); net_set_state(NETLOOP_FAIL); break; } @@ -680,7 +679,6 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip, case TFTP_ERR_FILE_NOT_FOUND: case TFTP_ERR_ACCESS_DENIED: puts("Not retrying...\n"); - eth_halt(); net_set_state(NETLOOP_FAIL); break; case TFTP_ERR_UNDEFINED: @@ -829,7 +827,6 @@ void tftp_start(enum proto_t protocol) #endif { if (tftp_init_load_addr()) { - eth_halt(); net_set_state(NETLOOP_FAIL); puts("\nTFTP error: "); puts("trying to overwrite reserved memory...\n"); @@ -885,7 +882,6 @@ void tftp_start_server(void) tftp_filename[0] = 0;
if (tftp_init_load_addr()) { - eth_halt(); net_set_state(NETLOOP_FAIL); puts("\nTFTP error: trying to overwrite reserved memory...\n"); return; diff --git a/net/wol.c b/net/wol.c index 0a62566..cd4e67e 100644 --- a/net/wol.c +++ b/net/wol.c @@ -85,7 +85,6 @@ void wol_set_timeout(ulong timeout)
static void wol_timeout_handler(void) { - eth_halt(); net_set_state(NETLOOP_FAIL); }

On Mon, May 3, 2021 at 2:55 PM Ian Ray ian.ray@ge.com wrote:
Calls made to eth_halt() by network operations (ping, nfs, tftp, etc.) break netconsole if it is already running.
- Maintain the network up / down state based on a reference count, using new APIs net_up() and net_down().
Note that when network is brought up by netconsole client, then network will remain up until reboot (because there is no way to stop netconsole itself).
Remove net_loop_last_protocol, eth_is_on_demand_init(), and eth_set_last_protocol(). This functionality is replaced by net_up() / net_down().
Replace usage of eth_init(), eth_halt() with net_up() and net_down() in net.c, ping.c, tftp.c, and netconsole.c.
Signed-off-by: Ian Ray ian.ray@ge.com
drivers/net/netconsole.c | 26 +++-------------- include/net.h | 23 ++------------- net/net.c | 75 +++++++++++++++++++++++++++++++++--------------- net/ping.c | 1 - net/tftp.c | 4 --- net/wol.c | 1 - 6 files changed, 59 insertions(+), 71 deletions(-)
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index f1d0630..b6d2e22 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -27,11 +27,6 @@ static short nc_out_port; /* target output port */ static short nc_in_port; /* source input port */ static const char *output_packet; /* used by first send udp */ static int output_packet_len; -/*
- Start with a default last protocol.
- We are only interested in NETCONS or not.
- */
-enum proto_t net_loop_last_protocol = BOOTP;
static void nc_wait_arp_handler(uchar *pkt, unsigned dest, struct in_addr sip, unsigned src, @@ -177,7 +172,6 @@ static void nc_send_packet(const char *buf, int len) #else struct eth_device *eth; #endif
int inited = 0; uchar *pkt; uchar *ether; struct in_addr ip;
@@ -200,29 +194,17 @@ static void nc_send_packet(const char *buf, int len) return; }
if (!eth_is_active(eth)) {
if (eth_is_on_demand_init()) {
if (eth_init() < 0)
return;
eth_set_last_protocol(NETCONS);
} else {
eth_init_state_only();
}
inited = 1;
if (net_up(NETCONS) < 0) {
return; }
pkt = (uchar *)net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE; memcpy(pkt, buf, len); ether = nc_ether; ip = nc_ip; net_send_udp_packet(ether, ip, nc_out_port, nc_in_port, len);
if (inited) {
if (eth_is_on_demand_init())
eth_halt();
else
eth_halt_state_only();
}
net_down();
}
static int nc_stdio_start(struct stdio_dev *dev) diff --git a/include/net.h b/include/net.h index 1bf9867..cc6be60 100644 --- a/include/net.h +++ b/include/net.h @@ -599,6 +599,9 @@ int net_loop(enum proto_t); /* Load failed. Start again. */ int net_start_again(void);
+int net_up(enum proto_t protocol); +int net_down(void);
/* Get size of the ethernet header when we send */ int net_eth_hdr_size(void);
@@ -706,26 +709,6 @@ int nc_input_packet(uchar *pkt, struct in_addr src_ip, unsigned dest_port, unsigned src_port, unsigned len); #endif
-static __always_inline int eth_is_on_demand_init(void) -{ -#if defined(CONFIG_NETCONSOLE) && !defined(CONFIG_SPL_BUILD)
extern enum proto_t net_loop_last_protocol;
return net_loop_last_protocol != NETCONS;
-#else
return 1;
-#endif -}
-static inline void eth_set_last_protocol(int protocol) -{ -#if defined(CONFIG_NETCONSOLE) && !defined(CONFIG_SPL_BUILD)
extern enum proto_t net_loop_last_protocol;
net_loop_last_protocol = protocol;
-#endif -}
/*
- Check if autoload is enabled. If so, use either NFS or TFTP to download
- the boot file.
diff --git a/net/net.c b/net/net.c index 28d9eeb..45d4f19 100644 --- a/net/net.c +++ b/net/net.c @@ -404,6 +404,51 @@ void net_init(void)
Main network processing loop.
*/
+static int up_ref; +static bool up; +static bool keep_up;
+/// Bring net up/active if needed. +int net_up(enum proto_t protocol) +{
int ret;
if (!up) {
eth_halt();
eth_set_current();
ret = eth_init();
if (ret < 0) {
debug_cond(DEBUG_INT_STATE, "net_up failed\n");
eth_halt();
return ret;
}
up = true;
} else if (up_ref == 0) {
eth_init_state_only();
}
up_ref++;
if (protocol == NETCONS) {
keep_up = true;
}
return 0;
+}
+/// Set net inactive if no clients remain. +int net_down(void) +{
if (up_ref > 0) {
up_ref--;
if (up_ref == 0) {
if (keep_up) {
eth_halt_state_only();
} else {
up = false;
eth_halt();
}
}
}
return 0;
+}
int net_loop(enum proto_t protocol) { int ret = -EINVAL; @@ -420,16 +465,9 @@ int net_loop(enum proto_t protocol)
bootstage_mark_name(BOOTSTAGE_ID_ETH_START, "eth_start"); net_init();
if (eth_is_on_demand_init() || protocol != NETCONS) {
eth_halt();
eth_set_current();
ret = eth_init();
if (ret < 0) {
eth_halt();
return ret;
}
} else {
eth_init_state_only();
ret = net_up(protocol);
if (ret < 0) {
return ret; }
restart: #ifdef CONFIG_USB_KEYBOARD @@ -448,7 +486,7 @@ restart: switch (net_check_prereq(protocol)) { case 1: /* network not configured */
eth_halt();
net_down(); net_set_state(prev_net_state); return -ENODEV;
@@ -589,9 +627,7 @@ restart: net_arp_wait_packet_ip.s_addr = 0;
net_cleanup_loop();
eth_halt();
/* Invalidate the last protocol */
eth_set_last_protocol(BOOTP);
net_down(); puts("\nAbort\n"); /* include a debug print as well incase the debug
@@ -647,12 +683,7 @@ restart: env_set_hex("filesize", net_boot_file_size); env_set_hex("fileaddr", image_load_addr); }
if (protocol != NETCONS)
eth_halt();
else
eth_halt_state_only();
eth_set_last_protocol(protocol);
net_down(); ret = net_boot_file_size; debug_cond(DEBUG_INT_STATE, "--- net_loop Success!\n");
@@ -660,8 +691,7 @@ restart:
case NETLOOP_FAIL: net_cleanup_loop();
/* Invalidate the last protocol */
eth_set_last_protocol(BOOTP);
net_down(); debug_cond(DEBUG_INT_STATE, "--- net_loop Fail!\n"); ret = -ENONET; goto done;
@@ -719,7 +749,6 @@ int net_start_again(void) }
if ((!retry_forever) && (net_try_count > retrycnt)) {
eth_halt(); net_set_state(NETLOOP_FAIL); /* * We don't provide a way for the protocol to return an error,
diff --git a/net/ping.c b/net/ping.c index 0e33660..2c92806 100644 --- a/net/ping.c +++ b/net/ping.c @@ -64,7 +64,6 @@ static int ping_send(void)
static void ping_timeout_handler(void) {
eth_halt(); net_set_state(NETLOOP_FAIL); /* we did not get the reply */
}
diff --git a/net/tftp.c b/net/tftp.c index 84e970b..dcc387b 100644 --- a/net/tftp.c +++ b/net/tftp.c @@ -652,7 +652,6 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip, net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
if (store_block(tftp_cur_block - 1, pkt + 2, len)) {
eth_halt(); net_set_state(NETLOOP_FAIL); break; }
@@ -680,7 +679,6 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip, case TFTP_ERR_FILE_NOT_FOUND: case TFTP_ERR_ACCESS_DENIED: puts("Not retrying...\n");
eth_halt(); net_set_state(NETLOOP_FAIL); break; case TFTP_ERR_UNDEFINED:
@@ -829,7 +827,6 @@ void tftp_start(enum proto_t protocol) #endif { if (tftp_init_load_addr()) {
eth_halt(); net_set_state(NETLOOP_FAIL); puts("\nTFTP error: "); puts("trying to overwrite reserved memory...\n");
@@ -885,7 +882,6 @@ void tftp_start_server(void) tftp_filename[0] = 0;
if (tftp_init_load_addr()) {
eth_halt(); net_set_state(NETLOOP_FAIL); puts("\nTFTP error: trying to overwrite reserved memory...\n"); return;
diff --git a/net/wol.c b/net/wol.c index 0a62566..cd4e67e 100644 --- a/net/wol.c +++ b/net/wol.c @@ -85,7 +85,6 @@ void wol_set_timeout(ulong timeout)
static void wol_timeout_handler(void) {
eth_halt(); net_set_state(NETLOOP_FAIL);
}
-- 2.10.1
I like the idea, I think that network protocols should call net_down() when they're done, however I'm not sure we need reference count. net_down() can be a wrapper around eth_halt(), We can just check if netconsole is running and call it eth_halt() only if its' not.

On Wed, May 05, 2021 at 05:02:21AM +0300, Ramon Fried wrote:
On Mon, May 3, 2021 at 2:55 PM Ian Ray ian.ray@ge.com wrote:
Calls made to eth_halt() by network operations (ping, nfs, tftp, etc.) break netconsole if it is already running.
- Maintain the network up / down state based on a reference count, using new APIs net_up() and net_down().
Note that when network is brought up by netconsole client, then network will remain up until reboot (because there is no way to stop netconsole itself).
Remove net_loop_last_protocol, eth_is_on_demand_init(), and eth_set_last_protocol(). This functionality is replaced by net_up() / net_down().
Replace usage of eth_init(), eth_halt() with net_up() and net_down() in net.c, ping.c, tftp.c, and netconsole.c.
Signed-off-by: Ian Ray ian.ray@ge.com
drivers/net/netconsole.c | 26 +++-------------- include/net.h | 23 ++------------- net/net.c | 75 +++++++++++++++++++++++++++++++++--------------- net/ping.c | 1 - net/tftp.c | 4 --- net/wol.c | 1 - 6 files changed, 59 insertions(+), 71 deletions(-)
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index f1d0630..b6d2e22 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -27,11 +27,6 @@ static short nc_out_port; /* target output port */ static short nc_in_port; /* source input port */ static const char *output_packet; /* used by first send udp */ static int output_packet_len; -/*
- Start with a default last protocol.
- We are only interested in NETCONS or not.
- */
-enum proto_t net_loop_last_protocol = BOOTP;
static void nc_wait_arp_handler(uchar *pkt, unsigned dest, struct in_addr sip, unsigned src, @@ -177,7 +172,6 @@ static void nc_send_packet(const char *buf, int len) #else struct eth_device *eth; #endif
int inited = 0; uchar *pkt; uchar *ether; struct in_addr ip;
@@ -200,29 +194,17 @@ static void nc_send_packet(const char *buf, int len) return; }
if (!eth_is_active(eth)) {
if (eth_is_on_demand_init()) {
if (eth_init() < 0)
return;
eth_set_last_protocol(NETCONS);
} else {
eth_init_state_only();
}
inited = 1;
if (net_up(NETCONS) < 0) {
return; }
pkt = (uchar *)net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE; memcpy(pkt, buf, len); ether = nc_ether; ip = nc_ip; net_send_udp_packet(ether, ip, nc_out_port, nc_in_port, len);
if (inited) {
if (eth_is_on_demand_init())
eth_halt();
else
eth_halt_state_only();
}
net_down();
}
static int nc_stdio_start(struct stdio_dev *dev) diff --git a/include/net.h b/include/net.h index 1bf9867..cc6be60 100644 --- a/include/net.h +++ b/include/net.h @@ -599,6 +599,9 @@ int net_loop(enum proto_t); /* Load failed. Start again. */ int net_start_again(void);
+int net_up(enum proto_t protocol); +int net_down(void);
/* Get size of the ethernet header when we send */ int net_eth_hdr_size(void);
@@ -706,26 +709,6 @@ int nc_input_packet(uchar *pkt, struct in_addr src_ip, unsigned dest_port, unsigned src_port, unsigned len); #endif
-static __always_inline int eth_is_on_demand_init(void) -{ -#if defined(CONFIG_NETCONSOLE) && !defined(CONFIG_SPL_BUILD)
extern enum proto_t net_loop_last_protocol;
return net_loop_last_protocol != NETCONS;
-#else
return 1;
-#endif -}
-static inline void eth_set_last_protocol(int protocol) -{ -#if defined(CONFIG_NETCONSOLE) && !defined(CONFIG_SPL_BUILD)
extern enum proto_t net_loop_last_protocol;
net_loop_last_protocol = protocol;
-#endif -}
/*
- Check if autoload is enabled. If so, use either NFS or TFTP to download
- the boot file.
diff --git a/net/net.c b/net/net.c index 28d9eeb..45d4f19 100644 --- a/net/net.c +++ b/net/net.c @@ -404,6 +404,51 @@ void net_init(void)
Main network processing loop.
*/
+static int up_ref; +static bool up; +static bool keep_up;
+/// Bring net up/active if needed. +int net_up(enum proto_t protocol) +{
int ret;
if (!up) {
eth_halt();
eth_set_current();
ret = eth_init();
if (ret < 0) {
debug_cond(DEBUG_INT_STATE, "net_up failed\n");
eth_halt();
return ret;
}
up = true;
} else if (up_ref == 0) {
eth_init_state_only();
}
up_ref++;
if (protocol == NETCONS) {
keep_up = true;
}
return 0;
+}
+/// Set net inactive if no clients remain. +int net_down(void) +{
if (up_ref > 0) {
up_ref--;
if (up_ref == 0) {
if (keep_up) {
eth_halt_state_only();
} else {
up = false;
eth_halt();
}
}
}
return 0;
+}
int net_loop(enum proto_t protocol) { int ret = -EINVAL; @@ -420,16 +465,9 @@ int net_loop(enum proto_t protocol)
bootstage_mark_name(BOOTSTAGE_ID_ETH_START, "eth_start"); net_init();
if (eth_is_on_demand_init() || protocol != NETCONS) {
eth_halt();
eth_set_current();
ret = eth_init();
if (ret < 0) {
eth_halt();
return ret;
}
} else {
eth_init_state_only();
ret = net_up(protocol);
if (ret < 0) {
return ret; }
restart: #ifdef CONFIG_USB_KEYBOARD @@ -448,7 +486,7 @@ restart: switch (net_check_prereq(protocol)) { case 1: /* network not configured */
eth_halt();
net_down(); net_set_state(prev_net_state); return -ENODEV;
@@ -589,9 +627,7 @@ restart: net_arp_wait_packet_ip.s_addr = 0;
net_cleanup_loop();
eth_halt();
/* Invalidate the last protocol */
eth_set_last_protocol(BOOTP);
net_down(); puts("\nAbort\n"); /* include a debug print as well incase the debug
@@ -647,12 +683,7 @@ restart: env_set_hex("filesize", net_boot_file_size); env_set_hex("fileaddr", image_load_addr); }
if (protocol != NETCONS)
eth_halt();
else
eth_halt_state_only();
eth_set_last_protocol(protocol);
net_down(); ret = net_boot_file_size; debug_cond(DEBUG_INT_STATE, "--- net_loop Success!\n");
@@ -660,8 +691,7 @@ restart:
case NETLOOP_FAIL: net_cleanup_loop();
/* Invalidate the last protocol */
eth_set_last_protocol(BOOTP);
net_down(); debug_cond(DEBUG_INT_STATE, "--- net_loop Fail!\n"); ret = -ENONET; goto done;
@@ -719,7 +749,6 @@ int net_start_again(void) }
if ((!retry_forever) && (net_try_count > retrycnt)) {
eth_halt(); net_set_state(NETLOOP_FAIL); /* * We don't provide a way for the protocol to return an error,
diff --git a/net/ping.c b/net/ping.c index 0e33660..2c92806 100644 --- a/net/ping.c +++ b/net/ping.c @@ -64,7 +64,6 @@ static int ping_send(void)
static void ping_timeout_handler(void) {
eth_halt(); net_set_state(NETLOOP_FAIL); /* we did not get the reply */
}
diff --git a/net/tftp.c b/net/tftp.c index 84e970b..dcc387b 100644 --- a/net/tftp.c +++ b/net/tftp.c @@ -652,7 +652,6 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip, net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
if (store_block(tftp_cur_block - 1, pkt + 2, len)) {
eth_halt(); net_set_state(NETLOOP_FAIL); break; }
@@ -680,7 +679,6 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip, case TFTP_ERR_FILE_NOT_FOUND: case TFTP_ERR_ACCESS_DENIED: puts("Not retrying...\n");
eth_halt(); net_set_state(NETLOOP_FAIL); break; case TFTP_ERR_UNDEFINED:
@@ -829,7 +827,6 @@ void tftp_start(enum proto_t protocol) #endif { if (tftp_init_load_addr()) {
eth_halt(); net_set_state(NETLOOP_FAIL); puts("\nTFTP error: "); puts("trying to overwrite reserved memory...\n");
@@ -885,7 +882,6 @@ void tftp_start_server(void) tftp_filename[0] = 0;
if (tftp_init_load_addr()) {
eth_halt(); net_set_state(NETLOOP_FAIL); puts("\nTFTP error: trying to overwrite reserved memory...\n"); return;
diff --git a/net/wol.c b/net/wol.c index 0a62566..cd4e67e 100644 --- a/net/wol.c +++ b/net/wol.c @@ -85,7 +85,6 @@ void wol_set_timeout(ulong timeout)
static void wol_timeout_handler(void) {
eth_halt(); net_set_state(NETLOOP_FAIL);
}
-- 2.10.1
I like the idea, I think that network protocols should call net_down()
I'm not sure I understand this comment.
My (possibly incomplete) understanding of the design is as follows:
1. A command causes the net_loop() to be entered, and returns when the command has completed (successfully or otherwise).
2. net/net.c calls protocol-specific entry points, which in turn assign handler functions and send a UDP packet.
3. Handler functions are called on receive or timeout, and the protocol implementation is responsible for creating new packets or terminating the net_loop() by calling net_set_state().
Currently the net is brought _down_ by individual protocols (e.g. net/ping.c, net/tftp.c) on failure (shortly before *or* after calling net_set_state()) and of course by net/net.c on success.
In the RFC I tried to simplify this such that: net is brought down in one place net/net.c (essentially on exit from net_loop) -- but sadly drivers/net/netconsole.c needs to do this too. Maybe that could be improved.
Why do you think that network protocols should call net_down() ?
when they're done, however I'm not sure we need reference count.
The reference count does perhaps feel over-engineered. We would need to be *sure* that there are no overlapping scenarios.
net_down() can be a wrapper around eth_halt(), We can just check if netconsole is running and call it eth_halt() only if its' not.
Thanks for the feedback!

The network stack does not appear to be designed to handle simultaneous netconsole and network operations (such as ping, nfs, and tftp) because of re-use of the netconsole output buffer.
* Add new net_send_udp_packet2, net_send_ip_packet2 APIs.
* Teach netconsole to use a private TX buffer. This is needed in order to avoid overwriting the ICMP ECHO REQUEST if a ping is started while netconsole is active.
Signed-off-by: Ian Ray ian.ray@ge.com --- drivers/net/netconsole.c | 19 +++++++++++-------- include/net.h | 5 +++++ net/arp.c | 7 +++++-- net/arp.h | 1 + net/net.c | 36 ++++++++++++++++++++++++++++-------- net/ping.c | 3 ++- 6 files changed, 52 insertions(+), 19 deletions(-)
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index b6d2e22..ef65d75 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -27,6 +27,8 @@ static short nc_out_port; /* target output port */ static short nc_in_port; /* source input port */ static const char *output_packet; /* used by first send udp */ static int output_packet_len; +static uchar nc_pkt_buf[(PKTBUFSRX+1) * PKTSIZE_ALIGN + PKTALIGN]; +static uchar *nc_tx_packet;
static void nc_wait_arp_handler(uchar *pkt, unsigned dest, struct in_addr sip, unsigned src, @@ -120,10 +122,10 @@ void nc_start(void) /* send arp request */ uchar *pkt; net_set_arp_handler(nc_wait_arp_handler); - pkt = (uchar *)net_tx_packet + net_eth_hdr_size() + + pkt = (uchar *)nc_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE; memcpy(pkt, output_packet, output_packet_len); - net_send_udp_packet(nc_ether, nc_ip, nc_out_port, nc_in_port, + net_send_udp_packet2(nc_tx_packet, nc_ether, nc_ip, nc_out_port, nc_in_port, output_packet_len); } } @@ -173,8 +175,6 @@ static void nc_send_packet(const char *buf, int len) struct eth_device *eth; #endif uchar *pkt; - uchar *ether; - struct in_addr ip;
debug_cond(DEBUG_DEV_PKT, "output: "%*.*s"\n", len, len, buf);
@@ -198,11 +198,9 @@ static void nc_send_packet(const char *buf, int len) return; }
- pkt = (uchar *)net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE; + pkt = (uchar *)nc_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE; memcpy(pkt, buf, len); - ether = nc_ether; - ip = nc_ip; - net_send_udp_packet(ether, ip, nc_out_port, nc_in_port, len); + net_send_udp_packet2(nc_tx_packet, nc_ether, nc_ip, nc_out_port, nc_in_port, len);
net_down(); } @@ -218,6 +216,11 @@ static int nc_stdio_start(struct stdio_dev *dev) if (retval != 0) return retval;
+ if (nc_tx_packet == NULL) { + nc_tx_packet = &nc_pkt_buf[0] + (PKTALIGN - 1); + nc_tx_packet -= (ulong)nc_tx_packet % PKTALIGN; + } + /* * Initialize the static IP settings and buffer pointers * incase we call net_send_udp_packet before net_loop diff --git a/include/net.h b/include/net.h index cc6be60..74f7ce1 100644 --- a/include/net.h +++ b/include/net.h @@ -694,9 +694,14 @@ static inline void net_send_packet(uchar *pkt, int len) * @param sport Source UDP port * @param payload_len Length of data after the UDP header */ +int net_send_ip_packet2(uchar *pkt, uchar *ether, struct in_addr dest, int dport, int sport, + int payload_len, int proto, u8 action, u32 tcp_seq_num, + u32 tcp_ack_num); int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport, int payload_len, int proto, u8 action, u32 tcp_seq_num, u32 tcp_ack_num); +int net_send_udp_packet2(uchar *pkt, uchar *ether, struct in_addr dest, int dport, + int sport, int payload_len); int net_send_udp_packet(uchar *ether, struct in_addr dest, int dport, int sport, int payload_len);
diff --git a/net/arp.c b/net/arp.c index 1d06ed2..f19958f 100644 --- a/net/arp.c +++ b/net/arp.c @@ -35,6 +35,7 @@ struct in_addr net_arp_wait_packet_ip; static struct in_addr net_arp_wait_reply_ip; /* MAC address of waiting packet's destination */ uchar *arp_wait_packet_ethaddr; +uchar *arp_wait_tx_packet; int arp_wait_tx_packet_size; ulong arp_wait_timer_start; int arp_wait_try; @@ -47,6 +48,7 @@ void arp_init(void) arp_wait_packet_ethaddr = NULL; net_arp_wait_packet_ip.s_addr = 0; net_arp_wait_reply_ip.s_addr = 0; + arp_wait_tx_packet = NULL; arp_wait_tx_packet_size = 0; arp_tx_packet = &arp_tx_packet_buf[0] + (PKTALIGN - 1); arp_tx_packet -= (ulong)arp_tx_packet % PKTALIGN; @@ -222,12 +224,13 @@ void arp_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip, int len)
/* set the mac address in the waiting packet's header and transmit it */ - memcpy(((struct ethernet_hdr *)net_tx_packet)->et_dest, + memcpy(((struct ethernet_hdr *)arp_wait_tx_packet)->et_dest, &arp->ar_sha, ARP_HLEN); - net_send_packet(net_tx_packet, arp_wait_tx_packet_size); + net_send_packet(arp_wait_tx_packet, arp_wait_tx_packet_size);
/* no arp request pending now */ net_arp_wait_packet_ip.s_addr = 0; + arp_wait_tx_packet = NULL; arp_wait_tx_packet_size = 0; arp_wait_packet_ethaddr = NULL; } diff --git a/net/arp.h b/net/arp.h index 25b3c00..8154ebc 100644 --- a/net/arp.h +++ b/net/arp.h @@ -17,6 +17,7 @@ extern struct in_addr net_arp_wait_packet_ip; /* MAC address of waiting packet's destination */ extern uchar *arp_wait_packet_ethaddr; +extern uchar *arp_wait_tx_packet; extern int arp_wait_tx_packet_size; extern ulong arp_wait_timer_start; extern int arp_wait_try; diff --git a/net/net.c b/net/net.c index 45d4f19..b1dba80 100644 --- a/net/net.c +++ b/net/net.c @@ -855,17 +855,38 @@ int net_send_udp_packet(uchar *ether, struct in_addr dest, int dport, int sport, IPPROTO_UDP, 0, 0, 0); }
+int net_send_udp_packet2(uchar *pkt, uchar *ether, struct in_addr dest, int dport, int sport, + int payload_len) +{ + return net_send_ip_packet2(pkt, ether, dest, dport, sport, payload_len, + IPPROTO_UDP, 0, 0, 0); +} + +/// Sends net_tx_packet + int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport, int payload_len, int proto, u8 action, u32 tcp_seq_num, u32 tcp_ack_num) { - uchar *pkt; + return net_send_ip_packet2(net_tx_packet, ether, dest, dport, sport, + payload_len, + proto, action, tcp_seq_num, tcp_ack_num); +} + +/// Global variables / state. +/// - Uses const net_bcast_ethaddr if destination is broadcast address +/// - net_set_ether() checks net_our_vlan +/// - If ether is null, fills net_arp_wait_packet_ip and does ARP + +int net_send_ip_packet2(uchar *pkt, uchar *ether, struct in_addr dest, int dport, int sport, + int payload_len, int proto, u8 action, u32 tcp_seq_num, + u32 tcp_ack_num) +{ int eth_hdr_size; int pkt_hdr_size;
- /* make sure the net_tx_packet is initialized (net_init() was called) */ - assert(net_tx_packet != NULL); - if (net_tx_packet == NULL) + assert(pkt != NULL); + if (pkt == NULL) return -1;
/* convert to new style broadcast */ @@ -876,8 +897,6 @@ int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport, if (dest.s_addr == 0xFFFFFFFF) ether = (uchar *)net_bcast_ethaddr;
- pkt = (uchar *)net_tx_packet; - eth_hdr_size = net_set_ether(pkt, ether, PROT_IP);
switch (proto) { @@ -898,7 +917,8 @@ int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport, net_arp_wait_packet_ip = dest; arp_wait_packet_ethaddr = ether;
- /* size of the waiting packet */ + /* waiting packet */ + arp_wait_tx_packet = pkt; arp_wait_tx_packet_size = pkt_hdr_size + payload_len;
/* and do the ARP request */ @@ -909,7 +929,7 @@ int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport, } else { debug_cond(DEBUG_DEV_PKT, "sending UDP to %pI4/%pM\n", &dest, ether); - net_send_packet(net_tx_packet, pkt_hdr_size + payload_len); + net_send_packet(pkt, pkt_hdr_size + payload_len); return 0; /* transmitted */ } } diff --git a/net/ping.c b/net/ping.c index 2c92806..74c4726 100644 --- a/net/ping.c +++ b/net/ping.c @@ -52,7 +52,8 @@ static int ping_send(void)
set_icmp_header(pkt, net_ping_ip);
- /* size of the waiting packet */ + /* waiting packet */ + arp_wait_tx_packet = net_tx_packet; arp_wait_tx_packet_size = eth_hdr_size + IP_ICMP_HDR_SIZE;
/* and do the ARP request */
participants (2)
-
Ian Ray
-
Ramon Fried