Skip to content

Commit daef95d

Browse files
committed
lxd/device: add support for network device limits.priority option
This is a replacement feature for per-instance limits.network.priority option. New approach does not require netprio cgroup to be suppored (it's from legacy cgroup v1) and also it allows to set priority for virtual machine instances. Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@canonical.com>
1 parent d23780c commit daef95d

File tree

7 files changed

+73
-3
lines changed

7 files changed

+73
-3
lines changed

doc/.wordlist.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,3 +307,4 @@ Zettabyte
307307
ZFS
308308
zpool
309309
zpools
310+
qdisc

doc/reference/devices_nic.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ Key | Type | Default | Managed | Description
8888
`limits.egress` | string | - | no | I/O limit in bit/s for outgoing traffic (various suffixes supported, see {ref}`instances-limit-units`)
8989
`limits.ingress` | string | - | no | I/O limit in bit/s for incoming traffic (various suffixes supported, see {ref}`instances-limit-units`)
9090
`limits.max` | string | - | no | I/O limit in bit/s for both incoming and outgoing traffic (same as setting both `limits.ingress` and `limits.egress`)
91+
`limits.priority` | integer | - | The `skb->priority` value (32-bit unsigned integer) for outgoing traffic, to be used by the kernel queuing discipline (qdisc) to prioritize network packets (The effect of this value depends on the particular qdisc implementation, for example, `SKBPRIO` or `QFQ`. Consult the kernel qdisc documentation before setting this value.)
9192
`maas.subnet.ipv4` | string | - | yes | MAAS IPv4 subnet to register the instance in
9293
`maas.subnet.ipv6` | string | - | yes | MAAS IPv6 subnet to register the instance in
9394
`mtu` | integer | parent MTU | yes | The MTU of the new interface
@@ -354,6 +355,7 @@ Key | Type | Default | Description
354355
`limits.egress` | string | - | I/O limit in bit/s for outgoing traffic (various suffixes supported, see {ref}`instances-limit-units`)
355356
`limits.ingress` | string | - | I/O limit in bit/s for incoming traffic (various suffixes supported, see {ref}`instances-limit-units`)
356357
`limits.max` | string | - | I/O limit in bit/s for both incoming and outgoing traffic (same as setting both `limits.ingress` and `limits.egress`)
358+
`limits.priority` | integer | - | The `skb->priority` value (32-bit unsigned integer) for outgoing traffic, to be used by the kernel queuing discipline (qdisc) to prioritize network packets (The effect of this value depends on the particular qdisc implementation, for example, `SKBPRIO` or `QFQ`. Consult the kernel qdisc documentation before setting this value.)
357359
`mtu` | integer | kernel assigned | The MTU of the new interface
358360
`name` | string | kernel assigned | The name of the interface inside the instance
359361
`queue.tx.length` | integer | - | The transmit queue length for the NIC
@@ -442,6 +444,7 @@ Key | Type | Default | Description
442444
`limits.egress` | string | - | I/O limit in bit/s for outgoing traffic (various suffixes supported, see {ref}`instances-limit-units`)
443445
`limits.ingress` | string | - | I/O limit in bit/s for incoming traffic (various suffixes supported, see {ref}`instances-limit-units`)
444446
`limits.max` | string | - | I/O limit in bit/s for both incoming and outgoing traffic (same as setting both `limits.ingress` and `limits.egress`)
447+
`limits.priority` | integer | - | The `skb->priority` value (32-bit unsigned integer) for outgoing traffic, to be used by the kernel queuing discipline (qdisc) to prioritize network packets (The effect of this value depends on the particular qdisc implementation, for example, `SKBPRIO` or `QFQ`. Consult the kernel qdisc documentation before setting this value.)
445448
`mtu` | integer | parent MTU | The MTU of the new interface
446449
`name` | string | kernel assigned | The name of the interface inside the instance
447450
`parent` | string | - | The name of the host device to join the instance to

lxd/device/device_utils_network.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,44 @@ func networkSetupHostVethLimits(d *deviceCommon, oldConfig deviceConfig.Device,
560560
}
561561
}
562562

563+
var networkPriority uint64
564+
if d.config["limits.priority"] != "" {
565+
networkPriority, err = strconv.ParseUint(d.config["limits.priority"], 10, 32)
566+
if err != nil {
567+
return fmt.Errorf("Failed to parse limits.priority %q: %w", d.config["limits.priority"], err)
568+
}
569+
}
570+
571+
if oldConfig != nil && oldConfig["limits.priority"] != d.config["limits.priority"] {
572+
err = d.state.Firewall.InstanceClearNetPrio(d.inst.Project().Name, d.inst.Name(), veth)
573+
if err != nil {
574+
return err
575+
}
576+
}
577+
578+
if oldConfig == nil || (oldConfig != nil && oldConfig["limits.priority"] != d.config["limits.priority"]) {
579+
if networkPriority != 0 {
580+
if bridged && d.state.Firewall.String() == "xtables" {
581+
return fmt.Errorf("Failed to setup instance device network priority. The xtables firewall driver does not support required functionality.")
582+
}
583+
584+
err = d.state.Firewall.InstanceSetupNetPrio(d.inst.Project().Name, d.inst.Name(), veth, uint32(networkPriority))
585+
if err != nil {
586+
return fmt.Errorf("Failed to setup instance device network priority: %w", err)
587+
}
588+
}
589+
}
590+
591+
return nil
592+
}
593+
594+
// networkClearHostVethLimits clears any network rate limits to the veth device specified in the config.
595+
func networkClearHostVethLimits(d *deviceCommon) error {
596+
err := d.state.Firewall.InstanceClearNetPrio(d.inst.Project().Name, d.inst.Name(), d.config["host_name"])
597+
if err != nil {
598+
return err
599+
}
600+
563601
return nil
564602
}
565603

lxd/device/nic.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ func nicValidationRules(requiredFields []string, optionalFields []string, instCo
2626
"limits.ingress": validate.IsAny,
2727
"limits.egress": validate.IsAny,
2828
"limits.max": validate.IsAny,
29+
"limits.priority": validate.Optional(validate.IsUint32),
2930
"security.mac_filtering": validate.IsAny,
3031
"security.ipv4_filtering": validate.IsAny,
3132
"security.ipv6_filtering": validate.IsAny,

lxd/device/nic_bridged.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ func (d *nicBridged) validateConfig(instConf instance.ConfigReader) error {
7575
"limits.ingress",
7676
"limits.egress",
7777
"limits.max",
78+
"limits.priority",
7879
"ipv4.address",
7980
"ipv6.address",
8081
"ipv4.routes",
@@ -456,7 +457,7 @@ func (d *nicBridged) UpdatableFields(oldDevice Type) []string {
456457
return []string{}
457458
}
458459

459-
return []string{"limits.ingress", "limits.egress", "limits.max", "ipv4.routes", "ipv6.routes", "ipv4.routes.external", "ipv6.routes.external", "ipv4.address", "ipv6.address", "security.mac_filtering", "security.ipv4_filtering", "security.ipv6_filtering"}
460+
return []string{"limits.ingress", "limits.egress", "limits.max", "limits.priority", "ipv4.routes", "ipv6.routes", "ipv4.routes.external", "ipv6.routes.external", "ipv4.address", "ipv6.address", "security.mac_filtering", "security.ipv4_filtering", "security.ipv6_filtering"}
460461
}
461462

462463
// Add is run when a device is added to a non-snapshot instance whether or not the instance is running.
@@ -800,6 +801,14 @@ func (d *nicBridged) Stop() (*deviceConfig.RunConfig, error) {
800801
return nil, err
801802
}
802803

804+
// Populate device config with volatile fields (hwaddr and host_name) if needed.
805+
networkVethFillFromVolatile(d.config, d.volatileGet())
806+
807+
err = networkClearHostVethLimits(&d.deviceCommon)
808+
if err != nil {
809+
return nil, err
810+
}
811+
803812
// Setup post-stop actions.
804813
runConf := deviceConfig.RunConfig{
805814
PostHooks: []func() error{d.postStop},

lxd/device/nic_p2p.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ func (d *nicP2P) validateConfig(instConf instance.ConfigReader) error {
3737
"limits.ingress",
3838
"limits.egress",
3939
"limits.max",
40+
"limits.priority",
4041
"ipv4.routes",
4142
"ipv6.routes",
4243
"boot.priority",
@@ -67,7 +68,7 @@ func (d *nicP2P) UpdatableFields(oldDevice Type) []string {
6768
return []string{}
6869
}
6970

70-
return []string{"limits.ingress", "limits.egress", "limits.max", "ipv4.routes", "ipv6.routes"}
71+
return []string{"limits.ingress", "limits.egress", "limits.max", "limits.priority", "ipv4.routes", "ipv6.routes"}
7172
}
7273

7374
// Start is run when the device is added to a running instance or instance is starting up.
@@ -199,6 +200,14 @@ func (d *nicP2P) Update(oldDevices deviceConfig.Devices, isRunning bool) error {
199200

200201
// Stop is run when the device is removed from the instance.
201202
func (d *nicP2P) Stop() (*deviceConfig.RunConfig, error) {
203+
// Populate device config with volatile fields (hwaddr and host_name) if needed.
204+
networkVethFillFromVolatile(d.config, d.volatileGet())
205+
206+
err := networkClearHostVethLimits(&d.deviceCommon)
207+
if err != nil {
208+
return nil, err
209+
}
210+
202211
runConf := deviceConfig.RunConfig{
203212
PostHooks: []func() error{d.postStop},
204213
}

lxd/device/nic_routed.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func (d *nicRouted) UpdatableFields(oldDevice Type) []string {
4343
return []string{}
4444
}
4545

46-
return []string{"limits.ingress", "limits.egress", "limits.max"}
46+
return []string{"limits.ingress", "limits.egress", "limits.max", "limits.priority"}
4747
}
4848

4949
// validateConfig checks the supplied config for correctness.
@@ -69,6 +69,7 @@ func (d *nicRouted) validateConfig(instConf instance.ConfigReader) error {
6969
"limits.ingress",
7070
"limits.egress",
7171
"limits.max",
72+
"limits.priority",
7273
"ipv4.gateway",
7374
"ipv6.gateway",
7475
"ipv4.routes",
@@ -588,6 +589,14 @@ func (d *nicRouted) Update(oldDevices deviceConfig.Devices, isRunning bool) erro
588589

589590
// Stop is run when the device is removed from the instance.
590591
func (d *nicRouted) Stop() (*deviceConfig.RunConfig, error) {
592+
// Populate device config with volatile fields (hwaddr and host_name) if needed.
593+
networkVethFillFromVolatile(d.config, d.volatileGet())
594+
595+
err := networkClearHostVethLimits(&d.deviceCommon)
596+
if err != nil {
597+
return nil, err
598+
}
599+
591600
runConf := deviceConfig.RunConfig{
592601
PostHooks: []func() error{d.postStop},
593602
}

0 commit comments

Comments
 (0)