diff --git a/pkg/aws/ec2/ec2.go b/pkg/aws/ec2/ec2.go index d84bc7c7f7575..965dfecfbcc22 100644 --- a/pkg/aws/ec2/ec2.go +++ b/pkg/aws/ec2/ec2.go @@ -752,12 +752,21 @@ func (c *Client) DeleteNetworkInterface(ctx context.Context, eniID string) error // AttachNetworkInterface attaches a previously created ENI to an instance func (c *Client) AttachNetworkInterface(ctx context.Context, index int32, instanceID, eniID string) (string, error) { + return c.AttachNetworkInterfaceWithQueues(ctx, index, instanceID, eniID, nil) +} + +// AttachNetworkInterfaceWithQueues attaches a previously created ENI to an instance with optional ENA queue count +func (c *Client) AttachNetworkInterfaceWithQueues(ctx context.Context, index int32, instanceID, eniID string, enaQueueCount *int32) (string, error) { input := &ec2.AttachNetworkInterfaceInput{ DeviceIndex: aws.Int32(index), InstanceId: aws.String(instanceID), NetworkInterfaceId: aws.String(eniID), } + if enaQueueCount != nil { + input.EnaQueueCount = enaQueueCount + } + c.limiter.Limit(ctx, AttachNetworkInterface) sinceStart := spanstat.Start() output, err := c.ec2Client.AttachNetworkInterface(ctx, input) diff --git a/pkg/aws/ec2/mock/mock.go b/pkg/aws/ec2/mock/mock.go index 32a5c917f781d..87c13b40b6211 100644 --- a/pkg/aws/ec2/mock/mock.go +++ b/pkg/aws/ec2/mock/mock.go @@ -369,6 +369,10 @@ func (e *API) DeleteNetworkInterface(ctx context.Context, eniID string) error { } func (e *API) AttachNetworkInterface(ctx context.Context, index int32, instanceID, eniID string) (string, error) { + return e.AttachNetworkInterfaceWithQueues(ctx, index, instanceID, eniID, nil) +} + +func (e *API) AttachNetworkInterfaceWithQueues(ctx context.Context, index int32, instanceID, eniID string, enaQueueCount *int32) (string, error) { e.rateLimit() e.delaySim.Delay(AttachNetworkInterface) diff --git a/pkg/aws/eni/instances.go b/pkg/aws/eni/instances.go index 61be3c11e199c..9c9d15f8c88c6 100644 --- a/pkg/aws/eni/instances.go +++ b/pkg/aws/eni/instances.go @@ -35,6 +35,7 @@ type EC2API interface { GetDetachedNetworkInterfaces(ctx context.Context, tags ipamTypes.Tags, maxResults int32) ([]string, error) CreateNetworkInterface(ctx context.Context, toAllocate int32, subnetID, desc string, groups []string, allocatePrefixes bool) (string, *eniTypes.ENI, error) AttachNetworkInterface(ctx context.Context, index int32, instanceID, eniID string) (string, error) + AttachNetworkInterfaceWithQueues(ctx context.Context, index int32, instanceID, eniID string, enaQueueCount *int32) (string, error) DeleteNetworkInterface(ctx context.Context, eniID string) error ModifyNetworkInterface(ctx context.Context, eniID, attachmentID string, deleteOnTermination bool) error AssignPrivateIpAddresses(ctx context.Context, eniID string, addresses int32) ([]string, error) diff --git a/pkg/aws/eni/limits/limits.go b/pkg/aws/eni/limits/limits.go index bc4fcfbeb27eb..1e3b27cc52fdd 100644 --- a/pkg/aws/eni/limits/limits.go +++ b/pkg/aws/eni/limits/limits.go @@ -117,13 +117,17 @@ func (l *LimitsGetter) updateFromEC2API(ctx context.Context, api ec2API) error { ipv6PerAdapter := aws.ToInt32(instanceTypeInfo.NetworkInfo.Ipv6AddressesPerInterface) hypervisorType := instanceTypeInfo.Hypervisor isBareMetal := aws.ToBool(instanceTypeInfo.BareMetal) + vCpus := int(aws.ToInt32(instanceTypeInfo.VCpuInfo.DefaultVCpus)) + supportsFlexibleEnaQueues := instanceTypeInfo.NetworkInfo.FlexibleEnaQueuesSupport == ec2_types.FlexibleEnaQueuesSupportSupported l.m[instanceType] = ipamTypes.Limits{ - Adapters: int(adapterLimit), - IPv4: int(ipv4PerAdapter), - IPv6: int(ipv6PerAdapter), - HypervisorType: string(hypervisorType), - IsBareMetal: isBareMetal, + Adapters: int(adapterLimit), + IPv4: int(ipv4PerAdapter), + IPv6: int(ipv6PerAdapter), + HypervisorType: string(hypervisorType), + IsBareMetal: isBareMetal, + VCpus: vCpus, + SupportsFlexibleEnaQueues: supportsFlexibleEnaQueues, } } l.lastUpdate = time.Now() diff --git a/pkg/aws/eni/node.go b/pkg/aws/eni/node.go index 0d38f7e9eab04..036a7ae0eb5cf 100644 --- a/pkg/aws/eni/node.go +++ b/pkg/aws/eni/node.go @@ -521,9 +521,22 @@ func (n *Node) CreateInterface(ctx context.Context, allocation *ipam.AllocationA eni.Subnet.CIDR = subnet.CIDR.String() } + // Determine ENA queue count if the instance supports flexible ENA queues + var enaQueueCount *int32 + limits, limitsAvailable = n.getLimits() + if limitsAvailable && limits.SupportsFlexibleEnaQueues && limits.VCpus > 0 { + // Set queue count to match vCPU count, but cap at reasonable maximum + queueCount := limits.VCpus + if queueCount > 128 { // AWS maximum queue count + queueCount = 128 + } + enaQueueCount = aws.Int32(int32(queueCount)) + scopedLog.Debug("Configuring ENA queues", "queue-count", queueCount, "vcpus", limits.VCpus) + } + var attachmentID string for range maxAttachRetries { - attachmentID, err = n.manager.api.AttachNetworkInterface(ctx, index, n.node.InstanceID(), eniID) + attachmentID, err = n.manager.api.AttachNetworkInterfaceWithQueues(ctx, index, n.node.InstanceID(), eniID, enaQueueCount) // The index is already in use, this can happen if the local // list of ENIs is oudated. Retry the attachment to avoid diff --git a/pkg/ipam/types/types.go b/pkg/ipam/types/types.go index 1b333c93f1dba..75e98448c464c 100644 --- a/pkg/ipam/types/types.go +++ b/pkg/ipam/types/types.go @@ -28,6 +28,12 @@ type Limits struct { // IsBareMetal tracks whether an instance is a bare metal instance or not IsBareMetal bool + + // VCpus is the number of virtual CPUs for the instance type + VCpus int + + // SupportsFlexibleEnaQueues indicates if the instance type supports flexible ENA queue configuration + SupportsFlexibleEnaQueues bool } // AllocationIP is an IP which is available for allocation, or already @@ -370,11 +376,8 @@ func (in *Subnet) DeepEqual(other *Subnet) bool { if in.AvailableIPv6Addresses != other.AvailableIPv6Addresses { return false } - if ((in.Tags != nil) && (other.Tags != nil)) || ((in.Tags == nil) != (other.Tags == nil)) { - in, other := &in.Tags, &other.Tags - if !in.DeepEqual(other) { - return false - } + if !in.Tags.DeepEqual(&other.Tags) { + return false } return true