@@ -2,7 +2,7 @@ use alloc::boxed::Box;
22#[ cfg( feature = "dns" ) ]
33use alloc:: vec:: Vec ;
44use core:: future;
5- use core:: sync:: atomic:: { AtomicU16 , Ordering } ;
5+ use core:: sync:: atomic:: { AtomicBool , AtomicU16 , Ordering } ;
66use core:: task:: Poll ;
77
88use hermit_sync:: InterruptTicketMutex ;
@@ -79,6 +79,8 @@ static LOCAL_ENDPOINT: AtomicU16 = AtomicU16::new(0);
7979pub ( crate ) static NIC : InterruptTicketMutex < NetworkState < ' _ > > =
8080 InterruptTicketMutex :: new ( NetworkState :: Missing ) ;
8181
82+ static NETWORK_POLLER_STARTED : AtomicBool = AtomicBool :: new ( false ) ;
83+
8284type MaybePcapDevice = cfg_select ! {
8385 feature = "write-pcap-file" => {
8486 smoltcp:: phy:: PcapWriter <NetworkDevice , crate :: executor:: device:: pcap_writer:: FileSink >
@@ -138,17 +140,26 @@ pub(crate) fn now() -> Instant {
138140 Instant :: from_micros_const ( systemtime:: now_micros ( ) . try_into ( ) . unwrap ( ) )
139141}
140142
143+ /// Whether DHCP has configured the *current* network stack.
144+ ///
145+ /// A new DHCP socket reports [`dhcpv4::Event::Deconfigured`] on its first poll,
146+ /// which must not tear down the configuration the stack was created with.
147+ #[ cfg( feature = "dhcpv4" ) ]
148+ static DHCP_WAS_EVER_CONFIGURED : AtomicBool = AtomicBool :: new ( false ) ;
149+
141150#[ cfg( feature = "dhcpv4" ) ]
142151async fn dhcpv4_run ( ) {
143- let mut was_ever_configured = false ;
144152 future:: poll_fn ( |cx| {
145153 let Some ( mut guard) = NIC . try_lock ( ) else {
146154 // FIXME: only wake when progress can be made
147155 cx. waker ( ) . wake_by_ref ( ) ;
148156 return Poll :: Pending ;
149157 } ;
150158
151- let nic = guard. as_nic_mut ( ) . unwrap ( ) ;
159+ let Ok ( nic) = guard. as_nic_mut ( ) else {
160+ NETWORK_WAKER . lock ( ) . register ( cx. waker ( ) ) ;
161+ return Poll :: Pending ;
162+ } ;
152163 let dhcp_handle = nic. dhcp_handle ;
153164 let socket = nic. sockets . get_mut :: < dhcpv4:: Socket < ' _ > > ( dhcp_handle) ;
154165
@@ -192,10 +203,10 @@ async fn dhcpv4_run() {
192203 nic. dns_handle = Some ( nic. sockets . add ( dns_socket) ) ;
193204 }
194205
195- was_ever_configured = true ;
206+ DHCP_WAS_EVER_CONFIGURED . store ( true , Ordering :: Relaxed ) ;
196207 }
197208 Some ( dhcpv4:: Event :: Deconfigured ) => {
198- if !was_ever_configured {
209+ if !DHCP_WAS_EVER_CONFIGURED . load ( Ordering :: Relaxed ) {
199210 // If there is a default configuration, we do not want to reset it. If there is not, there is not a need to reset it.
200211 return Poll :: Pending ;
201212 }
@@ -244,7 +255,7 @@ pub(crate) fn wake_network_waker() {
244255}
245256
246257async fn network_run ( ) {
247- future:: poll_fn ( |cx| {
258+ future:: poll_fn ( |cx| -> Poll < ( ) > {
248259 let Some ( mut guard) = NIC . try_lock ( ) else {
249260 // FIXME: only wake when progress can be made
250261 cx. waker ( ) . wake_by_ref ( ) ;
@@ -253,7 +264,8 @@ async fn network_run() {
253264 } ;
254265
255266 let NetworkState :: Initialized ( nic) = & mut * guard else {
256- return Poll :: Ready ( ( ) ) ;
267+ NETWORK_WAKER . lock ( ) . register ( cx. waker ( ) ) ;
268+ return Poll :: Pending ;
257269 } ;
258270
259271 let now = now ( ) ;
@@ -285,7 +297,10 @@ pub(crate) async fn get_query_result(query: QueryHandle) -> io::Result<Vec<IpAdd
285297 return Poll :: Pending ;
286298 } ;
287299
288- let nic = guard. as_nic_mut ( ) . unwrap ( ) ;
300+ let Ok ( nic) = guard. as_nic_mut ( ) else {
301+ NETWORK_WAKER . lock ( ) . register ( cx. waker ( ) ) ;
302+ return Poll :: Pending ;
303+ } ;
289304 let socket = nic. get_mut_dns_socket ( ) ?;
290305 match socket. get_query_result ( query) {
291306 Ok ( addrs) => {
@@ -309,6 +324,103 @@ pub(crate) async fn get_query_result(query: QueryHandle) -> io::Result<Vec<IpAdd
309324 . await
310325}
311326
327+ fn spawn_network_executor_tasks_once ( ) {
328+ if NETWORK_POLLER_STARTED
329+ . compare_exchange ( false , true , Ordering :: SeqCst , Ordering :: SeqCst )
330+ . is_ok ( )
331+ {
332+ spawn ( network_run ( ) ) ;
333+ #[ cfg( feature = "dhcpv4" ) ]
334+ spawn ( dhcpv4_run ( ) ) ;
335+ }
336+ }
337+
338+ /// Fully tears down the current [`NIC`] state, returns the hardware driver to
339+ /// [`super::device::NETWORK_DEVICE`] when applicable, and rebuilds the stack
340+ /// the same way as [`init`].
341+ ///
342+ /// Handles of sockets that were open before the call do not refer to a valid
343+ /// socket afterwards.
344+ #[ cfg( feature = "snapshot" ) ]
345+ pub ( crate ) fn reinit ( ) {
346+ LOCAL_ENDPOINT . store ( start_endpoint ( ) , Ordering :: Relaxed ) ;
347+ #[ cfg( feature = "dhcpv4" ) ]
348+ DHCP_WAS_EVER_CONFIGURED . store ( false , Ordering :: Relaxed ) ;
349+
350+ let mut guard = NIC . lock ( ) ;
351+ let old = core:: mem:: replace ( & mut * guard, NetworkState :: Missing ) ;
352+
353+ if let NetworkState :: Initialized ( nic_box) = old {
354+ cfg_select ! {
355+ any(
356+ all( target_arch = "riscv64" , feature = "gem-net" , not( feature = "pci" ) ) ,
357+ feature = "rtl8139" ,
358+ feature = "virtio-net" ,
359+ ) => {
360+ let dev = recycle_hardware_device( * nic_box) ;
361+ * super :: device:: NETWORK_DEVICE . lock( ) = Some ( dev) ;
362+ }
363+ _ => {
364+ drop( nic_box) ;
365+ }
366+ }
367+ }
368+
369+ * guard = NetworkInterface :: create ( ) ;
370+
371+ if let NetworkState :: Initialized ( _) = & * guard {
372+ spawn_network_executor_tasks_once ( ) ;
373+ }
374+
375+ drop ( guard) ;
376+ wake_network_waker ( ) ;
377+ }
378+
379+ #[ cfg( all(
380+ feature = "snapshot" ,
381+ any(
382+ all( target_arch = "riscv64" , feature = "gem-net" , not( feature = "pci" ) ) ,
383+ feature = "rtl8139" ,
384+ feature = "virtio-net" ,
385+ ) ,
386+ ) ) ]
387+ fn recycle_hardware_device ( nic : NetworkInterface < ' _ > ) -> NetworkDevice {
388+ use smoltcp:: phy:: { Device , RxToken } ;
389+
390+ /// Upper bound for the packets dropped by a single [`recycle_hardware_device`] call.
391+ const DRAIN_LIMIT : usize = 256 ;
392+
393+ let NetworkInterface {
394+ iface,
395+ sockets,
396+ device,
397+ ..
398+ } = nic;
399+ drop ( ( iface, sockets) ) ;
400+ let mut device = {
401+ #[ cfg( feature = "net-trace" ) ]
402+ let device = device. into_inner ( ) ;
403+ device
404+ } ;
405+
406+ // The sockets these packets belong to are gone, so drop what the device
407+ // still holds instead of feeding it to the new interface.
408+ let now = now ( ) ;
409+ let mut dropped = 0 ;
410+ while dropped < DRAIN_LIMIT {
411+ let Some ( ( rx, _tx) ) = device. receive ( now) else {
412+ break ;
413+ } ;
414+ rx. consume ( |_| ( ) ) ;
415+ dropped += 1 ;
416+ }
417+ if dropped > 0 {
418+ debug ! ( "Dropped {dropped} packets that were received before the reinit" ) ;
419+ }
420+
421+ device
422+ }
423+
312424pub ( crate ) fn init ( ) {
313425 info ! ( "Try to initialize network!" ) ;
314426
@@ -320,9 +432,7 @@ pub(crate) fn init() {
320432 * guard = NetworkInterface :: create ( ) ;
321433
322434 if let NetworkState :: Initialized ( _) = & mut * guard {
323- spawn ( network_run ( ) ) ;
324- #[ cfg( feature = "dhcpv4" ) ]
325- spawn ( dhcpv4_run ( ) ) ;
435+ spawn_network_executor_tasks_once ( ) ;
326436 }
327437}
328438
0 commit comments