Hi James,

On 26/08/2026 07:29, James Hilliard wrote:
> Each lwIP command currently creates the only netif and removes it when
> the command exits. A second client therefore replaces the first client's
> interface, and the first exit can stop Ethernet while the second is active.
> 
> Add a reference-counted runtime attachment which owns one Ethernet
> device and netif. All attached clients use the global poll function, so
> lwIP dispatches packets to every registered PCB. A DHCP attachment may
> temporarily own interface addressing; the environment configuration is
> restored when it leaves.
> 
> Migrate the existing commands and clean up their PCBs, callbacks and
> timeouts before detaching. Wget now aborts an interrupted request and
> releases its TLS configuration, while DHCP leaves its acquired address
> configured.
> 
> Signed-off-by: James Hilliard <[email protected]>
> ---
>  cmd/lwip/ping.c     |  41 ++++-----
>  cmd/lwip/sntp.c     |  19 ++--
>  include/net-lwip.h  |  42 +++++++--
>  net/lwip/dhcp.c     |  57 ++++--------
>  net/lwip/dns.c      |  24 ++---
>  net/lwip/net-lwip.c | 248 
> ++++++++++++++++++++++++++++++++++++++++++++--------
>  net/lwip/nfs.c      |  25 ++----
>  net/lwip/tftp.c     |  54 +++++-------
>  net/lwip/wget.c     |  70 ++++++++-------
>  9 files changed, 372 insertions(+), 208 deletions(-)
> 
> diff --git a/cmd/lwip/ping.c b/cmd/lwip/ping.c
> index 98fa8e22bce..9e159106f62 100644
> --- a/cmd/lwip/ping.c
> +++ b/cmd/lwip/ping.c
> @@ -116,30 +116,23 @@ static void ping_send(void *arg)
>       }
>  }
>  
> -static int ping_loop(struct udevice *udev, const ip_addr_t *addr)
> +static int ping_loop(struct net_lwip_ctx *net, const ip_addr_t *addr)
>  {
>       struct ping_ctx ctx = {};
> -     struct netif *netif;
>       int ret;
>  
> -     netif = net_lwip_new_netif(udev);
> -     if (!netif)
> -             return -ENODEV;
> -
> -     printf("Using %s device\n", udev->name);
> +     printf("Using %s device\n", net->dev->name);
>  
>       ret = ping_raw_init(&ctx);
> -     if (ret < 0) {
> -             net_lwip_remove_netif(netif);
> +     if (ret < 0)
>               return ret;
> -     }
>  
>       ctx.target = *addr;
>  
>       ping_send(&ctx);
>  
>       do {
> -             net_lwip_rx(udev, netif);
> +             net_lwip_poll();
>               if (ctx.alive)
>                       break;
>               if (ctrlc()) {
> @@ -151,8 +144,6 @@ static int ping_loop(struct udevice *udev, const 
> ip_addr_t *addr)
>       sys_untimeout(ping_send, &ctx);
>       ping_raw_stop(&ctx);
>  
> -     net_lwip_remove_netif(netif);
> -
>       if (ctx.alive)
>               return 0;
>  
> @@ -162,6 +153,7 @@ static int ping_loop(struct udevice *udev, const 
> ip_addr_t *addr)
>  
>  int do_ping(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
>  {
> +     struct net_lwip_ctx net = {};
>       ip_addr_t addr;
>       int ret;
>  
> @@ -174,13 +166,22 @@ int do_ping(struct cmd_tbl *cmdtp, int flag, int argc, 
> char *const argv[])
>       net_try_count = 1;
>  
>       do {
> -             if (net_lwip_eth_start() == 0) {
> -                     ret = ping_loop(eth_get_dev(), &addr);
> -                     net_lwip_eth_stop();
> -                     if (ret == 0)
> -                             return CMD_RET_SUCCESS;
> -             }
> -     } while (net_start_again() == 0);
> +             ret = net_lwip_start(&net, NET_LWIP_ADDR_ENV);
> +             if (!ret)
> +                     break;
> +     } while (!net_start_again());
> +     if (ret)
> +             return CMD_RET_FAILURE;
> +
> +     do {
> +             ret = ping_loop(&net, &addr);
> +             if (!ret)
> +                     break;
> +     } while (!net_lwip_restart(&net));
> +
> +     net_lwip_stop(&net);
> +     if (!ret)
> +             return CMD_RET_SUCCESS;
>  
>       return CMD_RET_FAILURE;
>  }
> diff --git a/cmd/lwip/sntp.c b/cmd/lwip/sntp.c
> index 584151ba7d1..2801f66866a 100644
> --- a/cmd/lwip/sntp.c
> +++ b/cmd/lwip/sntp.c
> @@ -54,14 +54,8 @@ static bool ntp_server_known(void)
>       return false;
>  }
>  
> -static int sntp_loop(struct udevice *udev, ip_addr_t *srvip)
> +static int sntp_loop(struct net_lwip_ctx *net, ip_addr_t *srvip)
>  {
> -     struct netif *netif;
> -
> -     netif = net_lwip_new_netif(udev);
> -     if (!netif)
> -             return -1;
> -
>       sntp_state = NOT_DONE;
>  
>       sntp_setoperatingmode(SNTP_OPMODE_POLL);
> @@ -71,7 +65,6 @@ static int sntp_loop(struct udevice *udev, ip_addr_t *srvip)
>       } else {
>               if (!ntp_server_known()) {
>                       log_err("error: ntpserverip not set\n");
> -                     net_lwip_remove_netif(netif);
>                       return -1;
>               }
>       }
> @@ -79,7 +72,7 @@ static int sntp_loop(struct udevice *udev, ip_addr_t *srvip)
>  
>       sys_timeout(SNTP_TIMEOUT, no_response, NULL);
>       while (sntp_state == NOT_DONE) {
> -             net_lwip_rx(udev, netif);
> +             net_lwip_poll();
>               if (ctrlc()) {
>                       printf("\nAbort\n");
>                       sntp_state = ABORTED;
> @@ -89,7 +82,6 @@ static int sntp_loop(struct udevice *udev, ip_addr_t *srvip)
>       sys_untimeout(no_response, NULL);
>  
>       sntp_stop();
> -     net_lwip_remove_netif(netif);
>  
>       if (sntp_state == SUCCESS)
>               return 0;
> @@ -99,6 +91,7 @@ static int sntp_loop(struct udevice *udev, ip_addr_t *srvip)
>  
>  int do_sntp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
>  {
> +     struct net_lwip_ctx net = {};
>       ip_addr_t *srvip;
>       char *server;
>       ip_addr_t ipaddr;
> @@ -125,16 +118,16 @@ int do_sntp(struct cmd_tbl *cmdtp, int flag, int argc, 
> char *const argv[])
>               return CMD_RET_USAGE;
>       }
>  
> -     if (net_lwip_eth_start() < 0)
> +     if (net_lwip_start(&net, NET_LWIP_ADDR_ENV))
>               return CMD_RET_FAILURE;
>  
> -     if (sntp_loop(eth_get_dev(), srvip) < 0)
> +     if (sntp_loop(&net, srvip) < 0)
>               goto out;
>  
>       ret = CMD_RET_SUCCESS;
>  
>  out:
> -     net_lwip_eth_stop();
> +     net_lwip_stop(&net);
>  
>       return ret;
>  }
> diff --git a/include/net-lwip.h b/include/net-lwip.h
> index 8e59a2299e0..0d0394fce14 100644
> --- a/include/net-lwip.h
> +++ b/include/net-lwip.h
> @@ -6,6 +6,35 @@
>  #include <lwip/ip4.h>
>  #include <lwip/netif.h>
>  
> +struct udevice;
> +
> +/**
> + * enum net_lwip_addr_mode - Initial address configuration for a client
> + * @NET_LWIP_ADDR_ENV: Configure the interface from the environment
> + * @NET_LWIP_ADDR_NONE: Start with an unconfigured IPv4 interface
> + */
> +enum net_lwip_addr_mode {
> +     NET_LWIP_ADDR_ENV,
> +     NET_LWIP_ADDR_NONE,
> +};

I do see a problem with this: attaching a NET_LWIP_ADDR_NONE client
(typically, DHCP) while a NET_LWIP_ADDR_ENV client is active can change
the network configuration underneath that client. If the other client is
netconsole/UDP bound to IP_ADDR_ANY this may be tolerable, but for TCP
clients it can break an established connection. The failure mode is not
a clean disconnect but a connection which can stall or eventually time
out.

I think NET_LWIP_ADDR_NONE therefore needs to have explicit address
ownership semantics and should not be allowed to coexist with arbitrary
NET_LWIP_ADDR_ENV clients. How about introducing three modes like so:

/**
 * enum net_lwip_addr_mode - IPv4 requirements of a runtime client
 * @NET_LWIP_ADDR_ENV_STRICT:
 *      Use environment addressing and require it to remain stable while the
 *      client is attached.
 * @NET_LWIP_ADDR_ENV_FLEXIBLE:
 *      Use environment addressing, but permit another client to temporarily
 *      change or clear the shared netif's IPv4 configuration. The client's
 *      protocol state must remain valid across such changes; communication
 *      may be temporarily unavailable. Intended for clients such as
 *      netconsole which use connectionless UDP bound to IP_ADDR_ANY.
 * @NET_LWIP_ADDR_NONE:
 *      Temporarily own the shared netif's IPv4 configuration and start with
 *      no IPv4 address. Intended for DHCP. This mode may coexist only with
 *      NET_LWIP_ADDR_ENV_FLEXIBLE clients.
 */
enum net_lwip_addr_mode {
        NET_LWIP_ADDR_ENV_STRICT,
        NET_LWIP_ADDR_ENV_FLEXIBLE,
        NET_LWIP_ADDR_NONE,
};

The net_lwip_runtime struct would need a 'unsigned int strict_env_users;'
and a few tests would need to be adjusted, but the overall impact should be
small. What do you think?

> +
> +/**
> + * struct net_lwip_ctx - Attachment to the shared lwIP runtime
> + * @dev: Ethernet device used by the runtime
> + * @netif: Shared lwIP network interface
> + * @addr_mode: Address mode requested by this client
> + *
> + * Clients must zero-initialize this structure before passing it to
> + * net_lwip_start(). Multiple active clients share @dev and @netif. A client
> + * must remove its callbacks and protocol control blocks before calling
> + * net_lwip_stop().
> + */
> +struct net_lwip_ctx {
> +     struct udevice *dev;
> +     struct netif *netif;
> +     enum net_lwip_addr_mode addr_mode;
> +};
> +
>  /* HTTPS authentication mode */
>  enum auth_mode {
>       AUTH_NONE,
> @@ -34,13 +63,12 @@ static inline int eth_is_on_demand_init(void)
>  int eth_init_state_only(void); /* Set active state */
>  
>  int net_lwip_dns_init(void);
> -int net_lwip_eth_start(void);
> -void net_lwip_eth_stop(void);
> -struct netif *net_lwip_new_netif(struct udevice *udev);
> -struct netif *net_lwip_new_netif_noip(struct udevice *udev);
> -void net_lwip_remove_netif(struct netif *netif);
> -struct netif *net_lwip_get_netif(void);
> -int net_lwip_rx(struct udevice *udev, struct netif *netif);
> +int net_lwip_start(struct net_lwip_ctx *ctx,
> +                enum net_lwip_addr_mode addr_mode);
> +void net_lwip_stop(struct net_lwip_ctx *ctx);
> +int net_lwip_restart(struct net_lwip_ctx *ctx);
> +int net_lwip_refresh(struct net_lwip_ctx *ctx);
> +int net_lwip_poll(void);
>  int net_lwip_dns_resolve(char *name_or_ip, ip_addr_t *ip);
>  
>  /**
> diff --git a/net/lwip/dhcp.c b/net/lwip/dhcp.c
> index a5e2e7d4da0..0494e3981d5 100644
> --- a/net/lwip/dhcp.c
> +++ b/net/lwip/dhcp.c
> @@ -11,7 +11,6 @@
>  #include <lwip/apps/sntp.h>
>  #include <lwip/dhcp.h>
>  #include <lwip/dns.h>
> -#include <lwip/timeouts.h>
>  #include <net.h>
>  #include <time.h>
>  
> @@ -22,34 +21,23 @@
>  static char boot_file_name[DHCP_BOOT_FILE_LEN];
>  #endif
>  
> -static void call_lwip_dhcp_fine_tmr(void *ctx)
> -{
> -     dhcp_fine_tmr();
> -     sys_timeout(DHCP_FINE_TIMER_MSECS, call_lwip_dhcp_fine_tmr, NULL);
> -}
> -
> -static int dhcp_loop(struct udevice *udev)
> +static int dhcp_loop(struct net_lwip_ctx *net)
>  {
>       char ipstr[] = "ipaddr\0\0\0";
>       char maskstr[] = "netmask\0\0\0";
>       char gwstr[] = "gatewayip\0\0\0";
>       const ip_addr_t *ntpserverip;
>       unsigned long start;
> -     struct netif *netif;
>       struct dhcp *dhcp;
> -     bool bound;
> +     bool bound = false;
>       int idx;
>  
> -     idx = dev_seq(udev);
> +     idx = dev_seq(net->dev);
>       if (idx < 0 || idx > 99) {
>               log_err("unexpected idx %d\n", idx);
>               return CMD_RET_FAILURE;
>       }
>  
> -     netif = net_lwip_new_netif_noip(udev);
> -     if (!netif)
> -             return CMD_RET_FAILURE;
> -
>       /*
>        * Request the DHCP stack to parse and store the NTP servers for
>        * eventual use by the SNTP command
> @@ -59,15 +47,13 @@ static int dhcp_loop(struct udevice *udev)
>  
>       start = get_timer(0);
>  
> -     if (dhcp_start(netif))
> +     if (dhcp_start(net->netif))
>               return CMD_RET_FAILURE;
>  
> -     call_lwip_dhcp_fine_tmr(NULL);
> -
>       /* Wait for DHCP to complete */
>       do {
> -             net_lwip_rx(udev, netif);
> -             bound = dhcp_supplied_address(netif);
> +             net_lwip_poll();
> +             bound = dhcp_supplied_address(net->netif);
>               if (bound)
>                       break;
>               if (ctrlc()) {
> @@ -77,14 +63,10 @@ static int dhcp_loop(struct udevice *udev)
>               mdelay(1);
>       } while (get_timer(start) < DHCP_TIMEOUT_MS);
>  
> -     sys_untimeout(call_lwip_dhcp_fine_tmr, NULL);
> -
> -     if (!bound) {
> -             net_lwip_remove_netif(netif);
> +     if (!bound)
>               return CMD_RET_FAILURE;
> -     }
>  
> -     dhcp = netif_dhcp_data(netif);
> +     dhcp = netif_dhcp_data(net->netif);
>  
>       env_set("bootfile", dhcp->boot_file_name);
>  
> @@ -123,26 +105,18 @@ static int dhcp_loop(struct udevice *udev)
>       printf("DHCP client bound to address %pI4 (%lu ms)\n",
>              &dhcp->offered_ip_addr, get_timer(start));
>  
> -     net_lwip_remove_netif(netif);
>       return CMD_RET_SUCCESS;
>  }
>  
>  int do_dhcp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
>  {
> +     struct net_lwip_ctx net = {};
>       int ret;
> -     struct udevice *dev;
>  
> -     if (net_lwip_eth_start() < 0)
> +     if (net_lwip_start(&net, NET_LWIP_ADDR_NONE))
>               return CMD_RET_FAILURE;
>  
> -     dev = eth_get_dev();
> -     if (!dev) {
> -             log_err("No network device\n");
> -             ret = CMD_RET_FAILURE;
> -             goto out;
> -     }
> -
> -     ret = dhcp_loop(dev);
> +     ret = dhcp_loop(&net);
>       if (ret)
>               goto out;
>  
> @@ -156,7 +130,14 @@ int do_dhcp(struct cmd_tbl *cmdtp, int flag, int argc, 
> char *const argv[])
>       ret = CMD_RET_SUCCESS;
>  
>  out:
> -     net_lwip_eth_stop();
> +     if (net.netif) {
> +             if (dhcp_supplied_address(net.netif))
> +                     dhcp_stop_without_release(net.netif);
> +             else
> +                     dhcp_release_and_stop(net.netif);
> +             dhcp_cleanup(net.netif);
> +     }
> +     net_lwip_stop(&net);
>  
>       return ret;
>  }
> diff --git a/net/lwip/dns.c b/net/lwip/dns.c
> index b620b0611d6..15bc06797c2 100644
> --- a/net/lwip/dns.c
> +++ b/net/lwip/dns.c
> @@ -34,24 +34,18 @@ static void dns_cb(const char *name, const ip_addr_t 
> *ipaddr, void *arg)
>       ip_addr_set(&dns_cb_arg->host_ipaddr, ipaddr);
>  }
>  
> -static int dns_loop(struct udevice *udev, const char *name, const char *var)
> +static int dns_loop(struct net_lwip_ctx *net, const char *name,
> +                 const char *var)
>  {
>       struct dns_cb_arg dns_cb_arg = { };
> -     struct netif *netif;
>       const char *ipstr;
>       ip_addr_t ipaddr;
>       ulong start;
>       int ret;
>  
> -     netif = net_lwip_new_netif(udev);
> -     if (!netif)
> +     if (net_lwip_dns_init())
>               return CMD_RET_FAILURE;
>  
> -     if (net_lwip_dns_init()) {
> -             net_lwip_remove_netif(netif);
> -             return CMD_RET_FAILURE;
> -     }
> -
>       dns_cb_arg.done = false;
>  
>       ret = dns_gethostbyname(name, &ipaddr, dns_cb, &dns_cb_arg);
> @@ -62,7 +56,7 @@ static int dns_loop(struct udevice *udev, const char *name, 
> const char *var)
>               start = get_timer(0);
>               sys_timeout(DNS_RESEND_MS, do_dns_tmr, NULL);
>               do {
> -                     net_lwip_rx(udev, netif);
> +                     net_lwip_poll();
>                       if (dns_cb_arg.done)
>                               break;
>                       if (ctrlc()) {
> @@ -71,10 +65,9 @@ static int dns_loop(struct udevice *udev, const char 
> *name, const char *var)
>                       }
>               } while (get_timer(start) < DNS_TIMEOUT_MS);
>               sys_untimeout(do_dns_tmr, NULL);
> +             dns_cancel(dns_cb, &dns_cb_arg);
>       }
>  
> -     net_lwip_remove_netif(netif);
> -
>       if (dns_cb_arg.done && !ip_addr_isany(&dns_cb_arg.host_ipaddr)) {
>               ipstr = ipaddr_ntoa(&dns_cb_arg.host_ipaddr);
>               if (var)
> @@ -89,6 +82,7 @@ static int dns_loop(struct udevice *udev, const char *name, 
> const char *var)
>  
>  int do_dns(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
>  {
> +     struct net_lwip_ctx net = {};
>       char *name;
>       char *var = NULL;
>       int ret;
> @@ -101,12 +95,12 @@ int do_dns(struct cmd_tbl *cmdtp, int flag, int argc, 
> char *const argv[])
>       if (argc == 3)
>               var = argv[2];
>  
> -     if (net_lwip_eth_start() < 0)
> +     if (net_lwip_start(&net, NET_LWIP_ADDR_ENV))
>               return CMD_RET_FAILURE;
>  
> -     ret = dns_loop(eth_get_dev(), name, var);
> +     ret = dns_loop(&net, name, var);
>  
> -     net_lwip_eth_stop();
> +     net_lwip_stop(&net);
>  
>       return ret;
>  }
> diff --git a/net/lwip/net-lwip.c b/net/lwip/net-lwip.c
> index 8f8f9d69020..4c012bfb1e3 100644
> --- a/net/lwip/net-lwip.c
> +++ b/net/lwip/net-lwip.c
> @@ -31,7 +31,14 @@ void (*push_packet)(void *, int len) = 0;
>  int net_try_count;
>  static int net_restarted;
>  int net_restart_wrap;
> -static int net_lwip_eth_started;
> +static struct {
> +     struct udevice *dev;
> +     struct netif *netif;
> +     unsigned int users;
> +     unsigned int env_users;
> +     unsigned int no_addr_users;
> +     bool polling;
> +} net_lwip_runtime;
>  static uchar net_pkt_buf[(PKTBUFSRX) * PKTSIZE_ALIGN + PKTALIGN]
>       __aligned(PKTALIGN);
>  const u8 net_bcast_ethaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
> @@ -107,19 +114,6 @@ static void eth_init_rings(void)
>               net_rx_packets[i] = net_pkt_buf + i  * PKTSIZE_ALIGN;
>  }
>  
> -struct netif *net_lwip_get_netif(void)
> -{
> -     struct netif *netif, *found = NULL;
> -
> -     NETIF_FOREACH(netif) {
> -             if (!found)
> -                     found = netif;
> -             else
> -                     printf("Error: more than one netif in lwIP\n");
> -     }
> -     return found;
> -}
> -
>  static int get_udev_ipv4_info(struct udevice *dev, ip4_addr_t *ip,
>                             ip4_addr_t *mask, ip4_addr_t *gw)
>  {
> @@ -196,19 +190,15 @@ int net_lwip_dns_init(void)
>  /*
>   * Initialize the network stack if needed and start the current device if 
> valid
>   */
> -int net_lwip_eth_start(void)
> +static int net_lwip_eth_start(void)
>  {
>       int ret;
>  
> -     if (net_lwip_eth_started++ > 0)
> -             return 0;
> -
>       net_init();
>       eth_halt();
>       eth_set_current();
>       ret = eth_init();
>       if (ret < 0) {
> -             net_lwip_eth_started--;
>               eth_halt();
>               return ret;
>       }
> @@ -216,14 +206,8 @@ int net_lwip_eth_start(void)
>       return 0;
>  }
>  
> -void net_lwip_eth_stop(void)
> +static void net_lwip_eth_stop(void)
>  {
> -     if (!net_lwip_eth_started)
> -             return;
> -
> -     if (--net_lwip_eth_started)
> -             return;
> -
>       eth_halt();
>  }
>  
> @@ -243,8 +227,6 @@ static struct netif *new_netif(struct udevice *udev, bool 
> with_ip)
>               return NULL;
>       }
>  
> -     netif_remove(net_lwip_get_netif());
> -
>       ip4_addr_set_zero(&ip);
>       ip4_addr_set_zero(&mask);
>       ip4_addr_set_zero(&gw);
> @@ -287,20 +269,188 @@ static struct netif *new_netif(struct udevice *udev, 
> bool with_ip)
>       return netif;
>  }
>  
> -struct netif *net_lwip_new_netif(struct udevice *udev)
> +static void net_lwip_remove_netif(struct netif *netif)
>  {
> -     return new_netif(udev, true);
> +     netif_remove(netif);
> +     free(netif);
>  }
>  
> -struct netif *net_lwip_new_netif_noip(struct udevice *udev)
> +static int net_lwip_configure(enum net_lwip_addr_mode addr_mode)
>  {
> -     return new_netif(udev, false);
> +     ip4_addr_t ip, mask, gw;
> +
> +     if (addr_mode == NET_LWIP_ADDR_ENV) {
> +             if (get_udev_ipv4_info(net_lwip_runtime.dev, &ip, &mask, &gw))
> +                     return -EINVAL;
> +     } else {
> +             ip4_addr_set_zero(&ip);
> +             ip4_addr_set_zero(&mask);
> +             ip4_addr_set_zero(&gw);
> +     }
> +
> +     if (!ip4_addr_cmp(netif_ip4_addr(net_lwip_runtime.netif), &ip) ||
> +         !ip4_addr_cmp(netif_ip4_netmask(net_lwip_runtime.netif), &mask) ||
> +         !ip4_addr_cmp(netif_ip4_gw(net_lwip_runtime.netif), &gw))
> +             netif_set_addr(net_lwip_runtime.netif, &ip, &mask, &gw);
> +
> +     return 0;
>  }
>  
> -void net_lwip_remove_netif(struct netif *netif)
> +/**
> + * net_lwip_start - Attach a client to the shared lwIP runtime
> + * @ctx: Zero-initialized client attachment
> + * @addr_mode: Initial IPv4 address configuration requested by the client
> + *
> + * The first client starts the selected Ethernet device and creates the lwIP
> + * network interface. Later clients share both resources. Address-less 
> clients
> + * take priority while active so DHCP can configure the shared interface.
> + *
> + * Return: 0 on success, or a negative error code.
> + */
> +int net_lwip_start(struct net_lwip_ctx *ctx,
> +                enum net_lwip_addr_mode addr_mode)
>  {
> -     netif_remove(netif);
> -     free(netif);
> +     struct netif *netif;
> +     int ret;
> +
> +     if (!ctx)
> +             return -EINVAL;
> +     if (ctx->netif || ctx->dev)
> +             return -EBUSY;
> +     if (addr_mode != NET_LWIP_ADDR_ENV &&
> +         addr_mode != NET_LWIP_ADDR_NONE)
> +             return -EINVAL;
> +
> +     if (!net_lwip_runtime.users) {
> +             ret = net_lwip_eth_start();
> +             if (ret)
> +                     return ret;
> +
> +             net_lwip_runtime.dev = eth_get_dev();
> +             netif = new_netif(net_lwip_runtime.dev,
> +                               addr_mode == NET_LWIP_ADDR_ENV);
> +             if (!netif) {
> +                     net_lwip_runtime.dev = NULL;
> +                     net_lwip_eth_stop();
> +                     return -ENODEV;
> +             }
> +             net_lwip_runtime.netif = netif;
> +     } else if (addr_mode == NET_LWIP_ADDR_NONE &&
> +                !net_lwip_runtime.no_addr_users) {
> +             ret = net_lwip_configure(NET_LWIP_ADDR_NONE);
> +             if (ret)
> +                     return ret;
> +     } else if (addr_mode == NET_LWIP_ADDR_ENV &&
> +                !net_lwip_runtime.no_addr_users) {
> +             ret = net_lwip_configure(NET_LWIP_ADDR_ENV);
> +             if (ret)
> +                     return ret;
> +     }
> +
> +     net_lwip_runtime.users++;
> +     if (addr_mode == NET_LWIP_ADDR_ENV)
> +             net_lwip_runtime.env_users++;
> +     else
> +             net_lwip_runtime.no_addr_users++;
> +
> +     ctx->dev = net_lwip_runtime.dev;
> +     ctx->netif = net_lwip_runtime.netif;
> +     ctx->addr_mode = addr_mode;
> +
> +     return 0;
> +}
> +
> +/**
> + * net_lwip_stop - Detach a client from the shared lwIP runtime
> + * @ctx: Active client attachment
> + *
> + * The final client removes the lwIP interface and stops Ethernet. When the
> + * last address-less client leaves, environment addressing is restored for
> + * any clients which remain attached. Callers must first remove every lwIP
> + * callback and protocol control block owned by @ctx. This function must not
> + * be called from a callback dispatched by net_lwip_poll().
> + */
> +void net_lwip_stop(struct net_lwip_ctx *ctx)
> +{
> +     if (!ctx || ctx->netif != net_lwip_runtime.netif ||
> +         ctx->dev != net_lwip_runtime.dev || !net_lwip_runtime.users)
> +             return;
> +
> +     if (ctx->addr_mode == NET_LWIP_ADDR_ENV)
> +             net_lwip_runtime.env_users--;
> +     else
> +             net_lwip_runtime.no_addr_users--;
> +     net_lwip_runtime.users--;
> +
> +     ctx->dev = NULL;
> +     ctx->netif = NULL;
> +
> +     if (!net_lwip_runtime.users) {
> +             net_lwip_remove_netif(net_lwip_runtime.netif);
> +             net_lwip_runtime.netif = NULL;
> +             net_lwip_runtime.dev = NULL;
> +             net_lwip_eth_stop();
> +             return;
> +     }
> +
> +     if (!net_lwip_runtime.no_addr_users &&
> +         net_lwip_runtime.env_users &&
> +         net_lwip_configure(NET_LWIP_ADDR_ENV))
> +             log_err("Failed to restore lwIP interface addressing\n");
> +}
> +
> +/**
> + * net_lwip_restart - Restart an exclusively held lwIP runtime
> + * @ctx: Active client attachment
> + *
> + * Stop the current interface, select the next interface according to the
> + * normal network retry policy and attach @ctx to the replacement interface.
> + * A shared runtime cannot be restarted without disrupting other clients.
> + *
> + * Return: 0 on success, -EBUSY if other clients are attached, or another
> + * negative error code.
> + */
> +int net_lwip_restart(struct net_lwip_ctx *ctx)
> +{
> +     enum net_lwip_addr_mode addr_mode;
> +     int ret;
> +
> +     if (!ctx || ctx->netif != net_lwip_runtime.netif ||
> +         ctx->dev != net_lwip_runtime.dev || !net_lwip_runtime.users)
> +             return -EINVAL;
> +     if (net_lwip_runtime.users != 1)
> +             return -EBUSY;
> +
> +     addr_mode = ctx->addr_mode;
> +     net_lwip_stop(ctx);
> +
> +     ret = net_start_again();
> +     if (ret)
> +             return ret;
> +
> +     return net_lwip_start(ctx, addr_mode);
> +}
> +
> +/**
> + * net_lwip_refresh - Refresh environment addressing for the shared interface
> + * @ctx: Active environment-addressed client attachment
> + *
> + * Address-less clients take priority, so refreshes are deferred until the
> + * last such client detaches.
> + *
> + * Return: 0 on success, or a negative error code.
> + */
> +int net_lwip_refresh(struct net_lwip_ctx *ctx)
> +{
> +     if (!ctx || ctx->netif != net_lwip_runtime.netif ||
> +         ctx->dev != net_lwip_runtime.dev ||
> +         ctx->addr_mode != NET_LWIP_ADDR_ENV)
> +             return -EINVAL;
> +
> +     if (net_lwip_runtime.no_addr_users)
> +             return 0;
> +
> +     return net_lwip_configure(NET_LWIP_ADDR_ENV);
>  }
>  
>  /*
> @@ -343,7 +493,7 @@ static struct pbuf *alloc_pbuf_and_copy(uchar *data, int 
> len)
>       return p;
>  }
>  
> -int net_lwip_rx(struct udevice *udev, struct netif *netif)
> +static int net_lwip_rx(struct udevice *udev, struct netif *netif)
>  {
>       struct pbuf *pbuf;
>       uchar *packet;
> @@ -387,6 +537,32 @@ int net_lwip_rx(struct udevice *udev, struct netif 
> *netif)
>       return len;
>  }
>  
> +/**
> + * net_lwip_poll - Service the shared lwIP runtime
> + *
> + * Run lwIP timers, schedule other U-Boot work and dispatch received packets
> + * to all registered lwIP protocol control blocks. Reentrant calls are 
> rejected
> + * so protocol callbacks may safely invoke code which attempts to poll.
> + *
> + * Return: Receive status, -ENODEV with no active clients, or -EBUSY when a
> + * poll is already in progress.
> + */
> +int net_lwip_poll(void)
> +{
> +     int ret;
> +
> +     if (!net_lwip_runtime.users)
> +             return -ENODEV;
> +     if (net_lwip_runtime.polling)
> +             return -EBUSY;
> +
> +     net_lwip_runtime.polling = true;
> +     ret = net_lwip_rx(net_lwip_runtime.dev, net_lwip_runtime.netif);
> +     net_lwip_runtime.polling = false;
> +
> +     return ret;
> +}
> +
>  /**
>   * net_lwip_dns_resolve() - find IP address from name or IP
>   *
> diff --git a/net/lwip/nfs.c b/net/lwip/nfs.c
> index 4cc36373fdd..2b7ee22ccc5 100644
> --- a/net/lwip/nfs.c
> +++ b/net/lwip/nfs.c
> @@ -98,10 +98,9 @@ static int nfs_timeout_check(void)
>       return 1;
>  }
>  
> -static int nfs_loop(struct udevice *udev, ulong addr, char *fname,
> +static int nfs_loop(struct net_lwip_ctx *net, ulong addr, char *fname,
>                   ip_addr_t srvip)
>  {
> -     struct netif *netif;
>       int ret;
>  
>       nfs_download_state = NETLOOP_FAIL;
> @@ -110,16 +109,12 @@ static int nfs_loop(struct udevice *udev, ulong addr, 
> char *fname,
>       if (!fname || addr == 0)
>               return -1;
>  
> -     netif = net_lwip_new_netif(udev);
> -     if (!netif)
> -             return -1;
> -
>       strlcpy(nfs_path_buff, fname, sizeof(nfs_path_buff));
>  
>       nfs_filename = nfs_basename(nfs_path_buff);
>       nfs_path     = nfs_dirname(nfs_path_buff);
>  
> -     printf("Using %s device\n", udev->name);
> +     printf("Using %s device\n", net->dev->name);
>  
>       printf("File transfer via NFS from server %s; our IP address is %s\n",
>              ipaddr_ntoa(&srvip), env_get("ipaddr"));
> @@ -139,7 +134,6 @@ static int nfs_loop(struct udevice *udev, ulong addr, 
> char *fname,
>  
>       ret = nfs_udp_init(&sess_ctx);
>       if (ret < 0) {
> -             net_lwip_remove_netif(netif);
>               debug("Failed to init network interface, aborting for error = 
> %d\n", ret);
>               return ret;
>       }
> @@ -152,7 +146,7 @@ static int nfs_loop(struct udevice *udev, ulong addr, 
> char *fname,
>  
>       timer_start = get_timer(0);
>       do {
> -             net_lwip_rx(udev, netif);
> +             net_lwip_poll();
>               if (net_state != NETLOOP_CONTINUE)
>                       break;
>               if (ctrlc()) {
> @@ -165,7 +159,8 @@ static int nfs_loop(struct udevice *udev, ulong addr, 
> char *fname,
>       } while (true);
>       debug("%s: Loop exit at %lu\n", __func__, get_timer(0));
>  
> -     net_lwip_remove_netif(netif);
> +     udp_remove(sess_ctx.pcb);
> +     sess_ctx.pcb = NULL;
>  
>       if (net_state == NETLOOP_SUCCESS) {
>               ret = 0;
> @@ -186,8 +181,8 @@ static int nfs_loop(struct udevice *udev, ulong addr, 
> char *fname,
>  
>  int do_nfs(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
>  {
> +     struct net_lwip_ctx net = {};
>       int ret = CMD_RET_SUCCESS;
> -     bool started = false;
>       char *arg = NULL;
>       char *words[2] = { };
>       char *fname = NULL;
> @@ -278,17 +273,15 @@ int do_nfs(struct cmd_tbl *cmdtp, int flag, int argc, 
> char *const argv[])
>               goto out;
>       }
>  
> -     if (net_lwip_eth_start() < 0) {
> +     if (net_lwip_start(&net, NET_LWIP_ADDR_ENV)) {
>               ret = CMD_RET_FAILURE;
>               goto out;
>       }
> -     started = true;
>  
> -     if (nfs_loop(eth_get_dev(), laddr, fname, srvip) < 0)
> +     if (nfs_loop(&net, laddr, fname, srvip) < 0)
>               ret = CMD_RET_FAILURE;
>  out:
> -     if (started)
> -             net_lwip_eth_stop();
> +     net_lwip_stop(&net);
>       if (arg != net_boot_file_name)
>               free(arg);
>       return ret;
> diff --git a/net/lwip/tftp.c b/net/lwip/tftp.c
> index d6a9f29a260..59bae93ab35 100644
> --- a/net/lwip/tftp.c
> +++ b/net/lwip/tftp.c
> @@ -226,11 +226,10 @@ static void no_response(void *arg)
>       ctx->done = FAILURE;
>  }
>  
> -static int tftp_loop(struct udevice *udev, ulong addr, char *fname,
> +static int tftp_loop(struct net_lwip_ctx *net, ulong addr, char *fname,
>                    ip_addr_t srvip, uint16_t srvport)
>  {
>       int blksize = CONFIG_TFTP_BLOCKSIZE;
> -     struct netif *netif;
>       struct tftp_ctx ctx;
>       const char *ep;
>       err_t err;
> @@ -241,10 +240,6 @@ static int tftp_loop(struct udevice *udev, ulong addr, 
> char *fname,
>       if (!srvport)
>               srvport = TFTP_PORT;
>  
> -     netif = net_lwip_new_netif(udev);
> -     if (!netif)
> -             return -1;
> -
>       ctx.done = NOT_DONE;
>       ctx.size = 0;
>       ctx.block_count = 0;
> @@ -254,7 +249,7 @@ static int tftp_loop(struct udevice *udev, ulong addr, 
> char *fname,
>       ctx.wrq_accepted = false;
>       ctx.fname[0] = '\0';
>  
> -     printf("Using %s device\n", udev->name);
> +     printf("Using %s device\n", net->dev->name);
>       printf("TFTP from server %s; our IP address is %s\n",
>              ipaddr_ntoa(&srvip), env_get("ipaddr"));
>       printf("Filename '%s'.\n", fname);
> @@ -262,8 +257,10 @@ static int tftp_loop(struct udevice *udev, ulong addr, 
> char *fname,
>       printf("Loading: ");
>  
>       err = tftp_init_client(&tftp_context);
> -     if (!(err == ERR_OK || err == ERR_USE))
> +     if (err != ERR_OK) {
>               log_err("tftp_init_client err: %d\n", err);
> +             return -1;
> +     }
>  
>       ep = env_get("tftpblocksize");
>       if (ep)
> @@ -275,13 +272,13 @@ static int tftp_loop(struct udevice *udev, ulong addr, 
> char *fname,
>       /* might return different errors, like routing problems */
>       if (err != ERR_OK) {
>               printf("tftp_get() error %d\n", err);
> -             net_lwip_remove_netif(netif);
> +             tftp_cleanup();
>               return -1;
>       }
>  
>       sys_timeout(NO_RSP_TIMEOUT_MS, no_response, &ctx);
>       while (!ctx.done) {
> -             net_lwip_rx(udev, netif);
> +             net_lwip_poll();
>               if (ctrlc()) {
>                       printf("\nAbort\n");
>                       ctx.done = ABORTED;
> @@ -292,8 +289,6 @@ static int tftp_loop(struct udevice *udev, ulong addr, 
> char *fname,
>  
>       tftp_cleanup();
>  
> -     net_lwip_remove_netif(netif);
> -
>       if (ctx.done == SUCCESS) {
>               if (env_set_hex("fileaddr", addr)) {
>                       log_err("fileaddr not updated\n");
> @@ -318,9 +313,8 @@ static void no_request(void *arg)
>       ctx->done = FAILURE;
>  }
>  
> -static int tftpsrv_loop(struct udevice *udev, ulong addr)
> +static int tftpsrv_loop(struct net_lwip_ctx *net, ulong addr)
>  {
> -     struct netif *netif;
>       struct tftp_ctx ctx;
>       const char *ipaddr;
>       int ret = -1;
> @@ -335,16 +329,12 @@ static int tftpsrv_loop(struct udevice *udev, ulong 
> addr)
>               return -1;
>       }
>  
> -     netif = net_lwip_new_netif(udev);
> -     if (!netif)
> -             return -1;
> -
>       memset(&ctx, 0, sizeof(ctx));
>       ctx.done = NOT_DONE;
>       ctx.daddr = addr;
>       ctx.is_server = true;
>  
> -     printf("Using %s device\n", udev->name);
> +     printf("Using %s device\n", net->dev->name);
>       printf("Listening for TFTP transfer on %s\n", ipaddr);
>       printf("Load address: 0x%lx\n", ctx.daddr);
>  
> @@ -352,13 +342,13 @@ static int tftpsrv_loop(struct udevice *udev, ulong 
> addr)
>       err = tftp_init_server(&tftp_context);
>       if (err != ERR_OK) {
>               log_err("tftp_init_server err: %d\n", err);
> -             goto out_remove_netif;
> +             goto out;
>       }
>  
>       ctx.start_time = get_timer(0);
>       sys_timeout(TFTPSRV_LISTEN_TIMEOUT_MS, no_request, &ctx);
>       while (!ctx.done) {
> -             net_lwip_rx(udev, netif);
> +             net_lwip_poll();
>               if (ctrlc()) {
>                       printf("\nAbort\n");
>                       ctx.done = ABORTED;
> @@ -373,22 +363,22 @@ static int tftpsrv_loop(struct udevice *udev, ulong 
> addr)
>       if (ctx.done == SUCCESS) {
>               if (env_set_hex("fileaddr", addr)) {
>                       log_err("fileaddr not updated\n");
> -                     goto out_remove_netif;
> +                     goto out;
>               }
>               efi_set_bootdev("Net", "", ctx.fname, map_sysmem(addr, 0),
>                               ctx.size);
>               ret = 0;
>       }
>  
> -out_remove_netif:
> +out:
>       tftpsrv_active_ctx = NULL;
> -     net_lwip_remove_netif(netif);
>  
>       return ret;
>  }
>  
>  int do_tftpsrv(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
>  {
> +     struct net_lwip_ctx net = {};
>       int ret = CMD_RET_SUCCESS;
>       char *end;
>       ulong laddr;
> @@ -421,25 +411,25 @@ int do_tftpsrv(struct cmd_tbl *cmdtp, int flag, int 
> argc, char *const argv[])
>               goto out;
>       }
>  
> -     if (net_lwip_eth_start() < 0) {
> +     if (net_lwip_start(&net, NET_LWIP_ADDR_ENV)) {
>               ret = CMD_RET_FAILURE;
>               goto out;
>       }
>  
> -     if (tftpsrv_loop(eth_get_dev(), laddr) < 0)
> +     if (tftpsrv_loop(&net, laddr) < 0)
>               ret = CMD_RET_FAILURE;
>       else
>               image_load_addr = laddr;
> -     net_lwip_eth_stop();
>  
>  out:
> +     net_lwip_stop(&net);
>       return ret;
>  }
>  
>  int do_tftpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
>  {
> +     struct net_lwip_ctx net = {};
>       int ret = CMD_RET_SUCCESS;
> -     bool started = false;
>       char *arg = NULL;
>       char *words[3] = { };
>       char *fname = NULL;
> @@ -540,19 +530,17 @@ int do_tftpb(struct cmd_tbl *cmdtp, int flag, int argc, 
> char *const argv[])
>               goto out;
>       }
>  
> -     if (net_lwip_eth_start() < 0) {
> +     if (net_lwip_start(&net, NET_LWIP_ADDR_ENV)) {
>               ret = CMD_RET_FAILURE;
>               goto out;
>       }
> -     started = true;
>  
> -     if (tftp_loop(eth_get_dev(), laddr, fname, srvip, port) < 0)
> +     if (tftp_loop(&net, laddr, fname, srvip, port) < 0)
>               ret = CMD_RET_FAILURE;
>       else
>               image_load_addr = laddr;
>  out:
> -     if (started)
> -             net_lwip_eth_stop();
> +     net_lwip_stop(&net);
>       if (arg != net_boot_file_name)
>               free(arg);
>       return ret;
> diff --git a/net/lwip/wget.c b/net/lwip/wget.c
> index e8930da410b..c9af3c7420f 100644
> --- a/net/lwip/wget.c
> +++ b/net/lwip/wget.c
> @@ -39,6 +39,7 @@ struct wget_ctx {
>       ulong content_len;
>       ulong hash_count;
>       enum done_state done;
> +     bool aborted;
>  };
>  
>  static void wget_lwip_fill_info(struct pbuf *hdr, u16_t hdr_len, u32_t 
> hdr_cont_len)
> @@ -229,6 +230,10 @@ static void httpc_result_cb(void *arg, httpc_result_t 
> httpc_result,
>               ctx->done = FAILURE;
>               return;
>       }
> +     if (httpc_result == HTTPC_RESULT_LOCAL_ABORT && ctx->aborted) {
> +             ctx->done = FAILURE;
> +             return;
> +     }
>  
>       if (httpc_result != HTTPC_RESULT_OK) {
>               log_err("\nHTTP client error %d\n", httpc_result);
> @@ -293,13 +298,15 @@ static err_t httpc_headers_done_cb(httpc_state_t 
> *connection, void *arg, struct
>  #endif
>  
>  static int wget_handle_request(struct wget_ctx *ctx, bool is_https,
> -                            struct udevice *udev, struct netif *netif)
> +                            struct net_lwip_ctx *net)
>  {
>  #if CONFIG_IS_ENABLED(WGET_HTTPS)
> +     struct altcp_tls_config *tls_config = NULL;
>       altcp_allocator_t tls_allocator;
>  #endif
>       httpc_connection_t conn;
>       httpc_state_t *state;
> +     err_t err;
>       int ret;
>  
>       /* if URL with hostname init dns */
> @@ -348,11 +355,11 @@ static int wget_handle_request(struct wget_ctx *ctx, 
> bool is_https,
>                       printf("HTTPS connections not authenticated\n");
>               }
>               tls_allocator.alloc = &altcp_tls_alloc;
> -             tls_allocator.arg =
> -                     altcp_tls_create_config_client(ca, ca_sz,
> -                                                    ctx->server_name);
> +             tls_config = altcp_tls_create_config_client(ca, ca_sz,
> +                                                         ctx->server_name);
> +             tls_allocator.arg = tls_config;
>  
> -             if (!tls_allocator.arg) {
> +             if (!tls_config) {
>                       log_err("error: Cannot create a TLS connection\n");
>                       return -ENODEV;
>               }
> @@ -363,33 +370,44 @@ static int wget_handle_request(struct wget_ctx *ctx, 
> bool is_https,
>  
>       conn.result_fn = httpc_result_cb;
>       conn.headers_done_fn = httpc_headers_done_cb;
> -     if (httpc_get_file_dns(ctx->server_name, ctx->port, ctx->path, &conn,
> -                            httpc_recv_cb, ctx, &state)) {
> -             return -ENODEV;
> +     err = httpc_get_file_dns(ctx->server_name, ctx->port, ctx->path, &conn,
> +                              httpc_recv_cb, ctx, &state);
> +     if (err) {
> +             ret = -ENODEV;
> +             goto out;
>       }
>  
>       errno = 0;
>  
>       while (!ctx->done) {
> -             net_lwip_rx(udev, netif);
> -             if (ctrlc())
> +             net_lwip_poll();
> +             if (!ctx->done && ctrlc()) {
> +                     ctx->aborted = true;
> +                     httpc_abort(state);
>                       break;
> +             }
>       }
>  
> -     if (ctx->done == SUCCESS)
> -             return 0;
> -
> -     if (errno == EPERM && !wget_info->silent)
> -             printf("Certificate verification failed\n");
> +     if (ctx->done == SUCCESS) {
> +             ret = 0;
> +     } else {
> +             if (errno == EPERM && !wget_info->silent)
> +                     printf("Certificate verification failed\n");
> +             ret = -errno ?: -EIO;
> +     }
>  
> -     return -errno ?: -EIO;
> +out:
> +#if CONFIG_IS_ENABLED(WGET_HTTPS)
> +     if (tls_config)
> +             altcp_tls_free_config(tls_config);
> +#endif
> +     return ret;
>  }
>  
>  int wget_do_request(ulong dst_addr, char *uri)
>  {
> -     struct udevice *udev;
> +     struct net_lwip_ctx net = {};
>       struct wget_ctx ctx;
> -     struct netif *netif;
>       bool is_https;
>       int ret;
>  
> @@ -401,30 +419,22 @@ int wget_do_request(ulong dst_addr, char *uri)
>       ctx.start_time = 0;
>       ctx.content_len = 0;
>       ctx.hash_count = 0;
> +     ctx.aborted = false;
>  
>       ret = parse_url(uri, ctx.server_name, &ctx.port, &ctx.path, &is_https);
>       if (ret)
>               return ret;
>  
> -     ret = net_lwip_eth_start();
> +     ret = net_lwip_start(&net, NET_LWIP_ADDR_ENV);
>       if (ret)
>               return ret;
>  
>       if (!wget_info)
>               wget_info = &default_wget_info;
>  
> -     udev = eth_get_dev();
> -
> -     netif = net_lwip_new_netif(udev);
> -     if (!netif) {
> -             net_lwip_eth_stop();
> -             return -ENODEV;
> -     }
> -
> -     ret = wget_handle_request(&ctx, is_https, udev, netif);
> +     ret = wget_handle_request(&ctx, is_https, &net);
>  
> -     net_lwip_remove_netif(netif);
> -     net_lwip_eth_stop();
> +     net_lwip_stop(&net);
>  
>       return ret;
>  }
> 

Reply via email to