Commit 9fc22d29 authored by Michael Stapelberg's avatar Michael Stapelberg Committed by Mikio Hara

net: update zoneCache on cache misses to cover appearing interfaces

performance differences are in measurement noise as per benchcmp:

benchmark                            old ns/op     new ns/op     delta
BenchmarkUDP6LinkLocalUnicast-12     5012          5009          -0.06%

Fixes #28535

Change-Id: Id022e2ed089ce8388a2398e755848ec94e77e653
Reviewed-on: https://go-review.googlesource.com/c/146941
Run-TryBot: Mikio Hara <mikioh.public.networking@gmail.com>
Reviewed-by: 's avatarMikio Hara <mikioh.public.networking@gmail.com>
parent 5848b6c9
......@@ -102,7 +102,7 @@ func Interfaces() ([]Interface, error) {
return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
}
if len(ift) != 0 {
zoneCache.update(ift)
zoneCache.update(ift, false)
}
return ift, nil
}
......@@ -159,7 +159,7 @@ func InterfaceByName(name string) (*Interface, error) {
return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
}
if len(ift) != 0 {
zoneCache.update(ift)
zoneCache.update(ift, false)
}
for _, ifi := range ift {
if name == ifi.Name {
......@@ -187,18 +187,21 @@ var zoneCache = ipv6ZoneCache{
toName: make(map[int]string),
}
func (zc *ipv6ZoneCache) update(ift []Interface) {
// update refreshes the network interface information if the cache was last
// updated more than 1 minute ago, or if force is set. It returns whether the
// cache was updated.
func (zc *ipv6ZoneCache) update(ift []Interface, force bool) (updated bool) {
zc.Lock()
defer zc.Unlock()
now := time.Now()
if zc.lastFetched.After(now.Add(-60 * time.Second)) {
return
if !force && zc.lastFetched.After(now.Add(-60*time.Second)) {
return false
}
zc.lastFetched = now
if len(ift) == 0 {
var err error
if ift, err = interfaceTable(0); err != nil {
return
return false
}
}
zc.toIndex = make(map[string]int, len(ift))
......@@ -209,16 +212,25 @@ func (zc *ipv6ZoneCache) update(ift []Interface) {
zc.toName[ifi.Index] = ifi.Name
}
}
return true
}
func (zc *ipv6ZoneCache) name(index int) string {
if index == 0 {
return ""
}
zoneCache.update(nil)
updated := zoneCache.update(nil, false)
zoneCache.RLock()
defer zoneCache.RUnlock()
name, ok := zoneCache.toName[index]
zoneCache.RUnlock()
if !ok {
if !updated {
zoneCache.update(nil, true)
zoneCache.RLock()
name, ok = zoneCache.toName[index]
zoneCache.RUnlock()
}
}
if !ok {
name = uitoa(uint(index))
}
......@@ -229,10 +241,18 @@ func (zc *ipv6ZoneCache) index(name string) int {
if name == "" {
return 0
}
zoneCache.update(nil)
updated := zoneCache.update(nil, false)
zoneCache.RLock()
defer zoneCache.RUnlock()
index, ok := zoneCache.toIndex[name]
zoneCache.RUnlock()
if !ok {
if !updated {
zoneCache.update(nil, true)
zoneCache.RLock()
index, ok = zoneCache.toIndex[name]
zoneCache.RUnlock()
}
}
if !ok {
index, _, _ = dtoi(name)
}
......
......@@ -7,6 +7,7 @@
package net
import (
"errors"
"fmt"
"os/exec"
"runtime"
......@@ -53,3 +54,7 @@ func (ti *testInterface) setPointToPoint(suffix int) error {
})
return nil
}
func (ti *testInterface) setLinkLocal(suffix int) error {
return errors.New("not yet implemented for BSD")
}
......@@ -35,6 +35,31 @@ func (ti *testInterface) setBroadcast(suffix int) error {
return nil
}
func (ti *testInterface) setLinkLocal(suffix int) error {
ti.name = fmt.Sprintf("gotest%d", suffix)
xname, err := exec.LookPath("ip")
if err != nil {
return err
}
ti.setupCmds = append(ti.setupCmds, &exec.Cmd{
Path: xname,
Args: []string{"ip", "link", "add", ti.name, "type", "dummy"},
})
ti.setupCmds = append(ti.setupCmds, &exec.Cmd{
Path: xname,
Args: []string{"ip", "address", "add", ti.local, "dev", ti.name},
})
ti.teardownCmds = append(ti.teardownCmds, &exec.Cmd{
Path: xname,
Args: []string{"ip", "address", "del", ti.local, "dev", ti.name},
})
ti.teardownCmds = append(ti.teardownCmds, &exec.Cmd{
Path: xname,
Args: []string{"ip", "link", "delete", ti.name, "type", "dummy"},
})
return nil
}
func (ti *testInterface) setPointToPoint(suffix int) error {
ti.name = fmt.Sprintf("gotest%d", suffix)
xname, err := exec.LookPath("ip")
......
......@@ -176,3 +176,37 @@ func TestInterfaceArrivalAndDeparture(t *testing.T) {
}
}
}
func TestInterfaceArrivalAndDepartureZoneCache(t *testing.T) {
if testing.Short() {
t.Skip("avoid external network")
}
if os.Getuid() != 0 {
t.Skip("must be root")
}
// Ensure zoneCache is filled:
_, _ = Listen("tcp", "[fe80::1%nonexistant]:0")
ti := &testInterface{local: "fe80::1"}
if err := ti.setLinkLocal(0); err != nil {
t.Skipf("test requires external command: %v", err)
}
if err := ti.setup(); err != nil {
t.Fatal(err)
}
defer ti.teardown()
time.Sleep(3 * time.Millisecond)
// If Listen fails (on Linux with “bind: invalid argument”), zoneCache was
// not updated when encountering a nonexistant interface:
ln, err := Listen("tcp", "[fe80::1%"+ti.name+"]:0")
if err != nil {
t.Fatal(err)
}
ln.Close()
if err := ti.teardown(); err != nil {
t.Fatal(err)
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment