[roll] Roll fuchsia [netstack3] Optimize timer dispatcher memory use
Mitigate a memory leak in the `fuchsia_async::{Send,Local}Executor`
caused by its inability to properly cleanup abandoned timers
(`fuchsia_async::Timer`). This change does not fix the root-cause of
the issue in the executor but mitigates its problems in Netstack3
until a more global fix is ready.
Previously, the timer dispatcher created a `fuchsia_async::Timer` for
every timer that was scheduled (through the core). This resulted in an
entry being created in the `fuchsia_async::{Send,Local}Executor`'s
`TimerHeap` per scheduled timer. However, the executor does not properly
cleanup timers that have since been abandoned (e.g. aborted) and the
`TimerHeap` will instead keep the entry for the abandoned timer in its
heap until the timer's originally scheduled instant is reached. This
can eventually lead to unbounded memory growth/a leak.
Netstack3 will now defer creating a `fuchsia_async::Timer` with the
executor. It will instead lazily create the timer with the executor
only for the instant that the next timer is going to fire. That is,
if 2 timers are scheduled where A is scheduled to fire before B, then
a timer will be scheduled with the executor for A and only after it
fires will a timer be scheduled with the executor for B. This is
advantageous because if timer B were to be rescheduled, no existing
`fuchsia_async::Timer` exists that could be leaked.
Netstack3 will also group timers together based on the time the timers
are scheduled to fire at. This change effectively reduces the timer
granularity from nanoseconds to milliseconds and rounds up all instants
that a timer is expected to fire to the nearest millisecond. More
concretely, given 3 timers that fire at 0.1ms, 0.7ms and 1.1ms, the
first two timers will be scheduled to fire at 1ms and the second at 2ms.
This allows less thrash with `fuchsia_async::Timer`s registered with the
executor when soon-to-fire timers are quickly scheduled then later
de/re-scheduled (e.g. TCP socket timers for retransmission timeouts
that are rescheduled on each incoming TCP segment). See comments inline
for why we allow rounding instants up but not down, and why the chosen
granularity is milliseconds.
With an emulator running a debug `core_with_netstack3.x64` build and the
below command, I see the memory growth after ~11 hours has significantly
reduced from 24MiB to 0.01MiB.
```
while true; do ffx profile memory | grep -A6 ' netstack' | grep scudo; sleep 1; done
```
Logs showing the (almost negligible) memory growth:
```
$ # Start
$ ffx profile memory | grep -A6 ' netstack'
Process name: netstack.cm
Process koid: 19072
Private: 1.81 MiB
PSS: 7.60 MiB (Proportional Set Size)
Total: 17.79 MiB (Private + Shared unscaled)
Private Scaled Total
[scudo] 1.18 MiB 1.18 MiB 1.18 MiB
$ date
Fri Apr 21 10:03:36 PM PDT 2023
$
$
$ # End
$ date
Sat Apr 22 09:13:34 AM PDT 2023
$ ffx profile memory | grep -A6 ' netstack'
Process name: netstack.cm
Process koid: 19072
Private: 1.82 MiB
PSS: 7.61 MiB (Proportional Set Size)
Total: 17.80 MiB (Private + Shared unscaled)
Private Scaled Total
[scudo] 1.19 MiB 1.19 MiB 1.19 MiB
```
Original-Bug: 125301
Original-Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/842918
Original-Revision: 6723baee8d4c2b47a0b5ade2c48e146850e7492f
GitOrigin-RevId: a36c1dc36565444a370c73f638cd7d674c6a1fb0
Change-Id: If3d679bdceab54ab2f0d2732ee3a8bc2908b768c
This repository contains Fuchsia's Global Integration manifest files.
All changes should be made to the internal version of this repository. Our infrastructure automatically updates this version when the internal one changes.
Currently all changes must be made by a Google employee. Non-Google employees wishing to make a change can ask for assistance via the IRC channel #fuchsia on Freenode.
First install Jiri.
Next run:
$ jiri init $ jiri import minimal https://fuchsia.googlesource.com/integration $ jiri update
Third party projects should have their own subdirectory in ./third_party.