Skip to content

Commit e0d983e

Browse files
committed
datapath: Increase the devices controller's netlink receive buffer
The devices controller subscribes to address, route, link and neighbor updates on netlink sockets with the kernel default receive buffer. Under high churn that buffer overflows with ENOBUFS, and any one socket erroring restarts all four subscriptions. Size all four explicitly: 4MiB by default, configurable with the new --netlink-buffer-size flag. Signed-off-by: Jared Ledvina <jared.ledvina@datadoghq.com>
1 parent 67dfc5a commit e0d983e

7 files changed

Lines changed: 35 additions & 15 deletions

File tree

Documentation/cmdref/cilium-agent.md

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Documentation/cmdref/cilium-agent_hive.md

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Documentation/cmdref/cilium-agent_hive_dot-graph.md

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/datapath/linux/devices_controller.go

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ func (c DevicesConfig) Flags(flags *pflag.FlagSet) {
6666
flags.StringSlice(option.Devices, []string{}, "List of devices facing cluster/external network (used for BPF NodePort, BPF masquerading and host firewall); supports '+' as wildcard in device name, e.g. 'eth+'; support '!' to exclude devices, e.g. '!eth+' excludes any device with prefix 'eth'. Note '!' says nothing about which ones to include. A device must match other criteria to be selected; The filters are matched in order and whatever matched first wins.")
6767

6868
flags.Bool(option.ForceDeviceDetection, false, "Forces the auto-detection of devices, even if specific devices are explicitly listed")
69+
70+
flags.Int(option.NetlinkBufferSize, defaults.NetlinkBufferSize, "Size (in bytes) of the netlink socket receive buffer used by the devices controller's address, route, link and neighbor subscriptions; a larger buffer reduces the chance of ENOBUFS errors and subscription restarts under high churn, and is capped by net.core.rmem_max")
6971
}
7072

7173
var (
@@ -93,6 +95,9 @@ type DevicesConfig struct {
9395
// ForceDeviceDetection forces the auto-detection of devices,
9496
// even if user-specific devices are explicitly listed.
9597
ForceDeviceDetection bool
98+
// NetlinkBufferSize is the receive buffer size in bytes requested for
99+
// the controller's netlink subscription sockets.
100+
NetlinkBufferSize int
96101
}
97102

98103
type devicesControllerParams struct {
@@ -141,7 +146,7 @@ func newDevicesController(lc cell.Lifecycle, p devicesControllerParams) (*device
141146
func (dc *devicesController) Start(startCtx cell.HookContext) error {
142147
if dc.params.NetlinkFuncs == nil {
143148
var err error
144-
dc.params.NetlinkFuncs, err = makeNetlinkFuncs()
149+
dc.params.NetlinkFuncs, err = makeNetlinkFuncs(dc.params.Config.NetlinkBufferSize)
145150
if err != nil {
146151
return err
147152
}
@@ -743,7 +748,7 @@ type netlinkFuncs struct {
743748

744749
// makeNetlinkFuncs returns a *netlinkFuncs containing netlink accessors to the
745750
// network namespace of the calling goroutine's OS thread.
746-
func makeNetlinkFuncs() (*netlinkFuncs, error) {
751+
func makeNetlinkFuncs(netlinkBufferSize int) (*netlinkFuncs, error) {
747752
netlinkHandle, err := safenetlink.NewHandle(&safenetlink.HandleConfig{NLFamilies: []int{unix.NETLINK_ROUTE}})
748753
if err != nil {
749754
return nil, fmt.Errorf("creating netlink handle: %w", err)
@@ -759,36 +764,40 @@ func makeNetlinkFuncs() (*netlinkFuncs, error) {
759764
h := vns.NsHandle(cur.FD())
760765
return safenetlink.RouteSubscribeWithOptions(ch, done,
761766
netlink.RouteSubscribeOptions{
762-
ListExisting: false,
763-
ErrorCallback: errorCallback,
764-
Namespace: &h,
767+
ListExisting: false,
768+
ErrorCallback: errorCallback,
769+
Namespace: &h,
770+
ReceiveBufferSize: netlinkBufferSize,
765771
})
766772
},
767773
AddrSubscribe: func(ch chan<- netlink.AddrUpdate, done <-chan struct{}, errorCallback func(error)) error {
768774
h := vns.NsHandle(cur.FD())
769775
return netlink.AddrSubscribeWithOptions(ch, done,
770776
netlink.AddrSubscribeOptions{
771-
ListExisting: false,
772-
ErrorCallback: errorCallback,
773-
Namespace: &h,
777+
ListExisting: false,
778+
ErrorCallback: errorCallback,
779+
Namespace: &h,
780+
ReceiveBufferSize: netlinkBufferSize,
774781
})
775782
},
776783
LinkSubscribe: func(ch chan<- netlink.LinkUpdate, done <-chan struct{}, errorCallback func(error)) error {
777784
h := vns.NsHandle(cur.FD())
778785
return safenetlink.LinkSubscribeWithOptions(ch, done,
779786
netlink.LinkSubscribeOptions{
780-
ListExisting: false,
781-
ErrorCallback: errorCallback,
782-
Namespace: &h,
787+
ListExisting: false,
788+
ErrorCallback: errorCallback,
789+
Namespace: &h,
790+
ReceiveBufferSize: netlinkBufferSize,
783791
})
784792
},
785793
NeighSubscribe: func(ch chan<- netlink.NeighUpdate, done <-chan struct{}, errorCallback func(error)) error {
786794
h := vns.NsHandle(cur.FD())
787795
return safenetlink.NeighSubscribeWithOptions(ch, done,
788796
netlink.NeighSubscribeOptions{
789-
ListExisting: false,
790-
ErrorCallback: errorCallback,
791-
Namespace: &h,
797+
ListExisting: false,
798+
ErrorCallback: errorCallback,
799+
Namespace: &h,
800+
ReceiveBufferSize: netlinkBufferSize,
792801
})
793802
},
794803
LinkList: func() ([]netlink.Link, error) {

pkg/datapath/linux/devices_controller_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"golang.org/x/sys/unix"
3232

3333
"github.com/cilium/cilium/pkg/datapath/tables"
34+
"github.com/cilium/cilium/pkg/defaults"
3435
"github.com/cilium/cilium/pkg/hive"
3536
"github.com/cilium/cilium/pkg/testutils"
3637
)
@@ -64,7 +65,7 @@ func TestPrivilegedDevicesControllerScript(t *testing.T) {
6465
DevicesControllerCell,
6566
cell.Provide(func() (*netlinkFuncs, error) {
6667
// Provide the normal netlink interface, restricted to the test network namespace.
67-
return makeNetlinkFuncs()
68+
return makeNetlinkFuncs(defaults.NetlinkBufferSize)
6869
}),
6970
)
7071

pkg/defaults/defaults.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ const (
2020
)
2121

2222
const (
23+
// NetlinkBufferSize is the default value for option.NetlinkBufferSize
24+
NetlinkBufferSize = 4 << 20 // 4MiB
25+
2326
// ClusterHealthPort is the default value for option.ClusterHealthPort
2427
ClusterHealthPort = 4240
2528

pkg/option/config.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ const (
112112
// Forces the auto-detection of devices, even if specific devices are explicitly listed
113113
ForceDeviceDetection = "force-device-detection"
114114

115+
// NetlinkBufferSize sets the size in bytes of the netlink socket receive
116+
// buffer used by the devices controller's subscriptions
117+
NetlinkBufferSize = "netlink-buffer-size"
118+
115119
// DirectRoutingDevice is the name of a device used to connect nodes in
116120
// direct routing mode (only required by BPF NodePort)
117121
DirectRoutingDevice = "direct-routing-device"

0 commit comments

Comments
 (0)