Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/qat_plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ The QAT plugin can take a number of command line arguments, summarised in the fo
| -kernel-vf-drivers | string | Comma separated VF Device Driver of the QuickAssist Devices in the system. Devices supported: DH895xCC, C62x, C3xxx, 4xxx, C4xxx and D15xx (default: `c6xxvf,4xxxvf`) |
| -max-num-devices | int | maximum number of QAT devices to be provided to the QuickAssist device plugin (default: `32`) |
| -mode | string | plugin mode which can be either `dpdk` or `kernel` (default: `dpdk`) |
| -allocation-policy | string | 2 possible values: balanced and packed. Balanced mode spreads allocated QAT VF resources balanced among QAT PF devices, and packed mode packs one QAT PF device full of QAT VF resources before allocating resources from the next QAT PF. (There is no default.) |

The plugin also accepts a number of other arguments related to logging. Please use the `-h` option to see
the complete list of logging related options.
Expand Down
109 changes: 106 additions & 3 deletions cmd/qat_plugin/dpdkdrv/dpdkdrv.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ package dpdkdrv

import (
"bytes"
"flag"
"fmt"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -62,11 +64,54 @@ var qatDeviceDriver = map[string]string{
"6f55": "d15xxvf",
}

// swapBDF returns ["C1:B1:A1", "C2:B2:A2"], when the given parameter is ["A1:B1:C1", "A2:B2:C2"``].
func swapBDF(devstrings []string) []string {
result := make([]string, len(devstrings))

for n, dev := range devstrings {
tmp := strings.Split(dev, ":")
result[n] = fmt.Sprintf("%v:%v:%v", tmp[2], tmp[1], tmp[0])
}

return result
}

type preferredAllocationPolicyFunc func(*pluginapi.ContainerPreferredAllocationRequest) []string

// nonePolicy is used when no policy is specified.
func nonePolicy(req *pluginapi.ContainerPreferredAllocationRequest) []string {
deviceIds := req.AvailableDeviceIDs

return deviceIds[:req.AllocationSize]
}

// balancedPolicy is used for allocating QAT devices in balance.
func balancedPolicy(req *pluginapi.ContainerPreferredAllocationRequest) []string {
// make it "FDB" and string sort and change back to "BDF"
deviceIds := swapBDF(req.AvailableDeviceIDs)
sort.Strings(deviceIds)
deviceIds = swapBDF(deviceIds)

return deviceIds[:req.AllocationSize]
}

// packedPolicy is used for allocating QAT PF devices one by one.
func packedPolicy(req *pluginapi.ContainerPreferredAllocationRequest) []string {
deviceIds := req.AvailableDeviceIDs
sort.Strings(deviceIds)
deviceIds = deviceIds[:req.AllocationSize]

return deviceIds
}

// DevicePlugin represents vfio based QAT plugin.
type DevicePlugin struct {
scanTicker *time.Ticker
scanDone chan bool

// Note: If restarting the plugin with a new policy, the allocations for existing pods remain with old policy.
policy preferredAllocationPolicyFunc

pciDriverDir string
pciDeviceDir string
dpdkDriver string
Expand All @@ -75,7 +120,7 @@ type DevicePlugin struct {
}

// NewDevicePlugin returns new instance of vfio based QAT plugin.
func NewDevicePlugin(maxDevices int, kernelVfDrivers string, dpdkDriver string) (*DevicePlugin, error) {
func NewDevicePlugin(maxDevices int, kernelVfDrivers string, dpdkDriver string, preferredAllocationPolicy string) (*DevicePlugin, error) {
if !isValidDpdkDeviceDriver(dpdkDriver) {
return nil, errors.Errorf("wrong DPDK device driver: %s", dpdkDriver)
}
Expand All @@ -87,10 +132,42 @@ func NewDevicePlugin(maxDevices int, kernelVfDrivers string, dpdkDriver string)
}
}

return newDevicePlugin(pciDriverDirectory, pciDeviceDirectory, maxDevices, kernelDrivers, dpdkDriver), nil
allocationPolicyFunc := getAllocationPolicy(preferredAllocationPolicy)
if allocationPolicyFunc == nil {
return nil, errors.Errorf("wrong allocation policy: %s", preferredAllocationPolicy)
}

return newDevicePlugin(pciDriverDirectory, pciDeviceDirectory, maxDevices, kernelDrivers, dpdkDriver, allocationPolicyFunc), nil
}

func newDevicePlugin(pciDriverDir, pciDeviceDir string, maxDevices int, kernelVfDrivers []string, dpdkDriver string) *DevicePlugin {
//getAllocationPolicy returns a func that fits the policy given as a parameter. It returns nonePolicy when the flag is not set, and it returns nil when the policy is not valid value.
func getAllocationPolicy(preferredAllocationPolicy string) preferredAllocationPolicyFunc {
switch {
case !isFlagSet("allocation-policy"):
return nonePolicy
case preferredAllocationPolicy == "packed":
return packedPolicy
case preferredAllocationPolicy == "balanced":
return balancedPolicy
default:
return nil
}
}

// isFlagSet returns true when the flag that has the same name as the parameter is set.
func isFlagSet(name string) bool {
set := false

flag.Visit(func(f *flag.Flag) {
if f.Name == name {
set = true
}
})

return set
}

func newDevicePlugin(pciDriverDir, pciDeviceDir string, maxDevices int, kernelVfDrivers []string, dpdkDriver string, preferredAllocationPolicyFunc preferredAllocationPolicyFunc) *DevicePlugin {
return &DevicePlugin{
maxDevices: maxDevices,
pciDriverDir: pciDriverDir,
Expand All @@ -99,6 +176,7 @@ func newDevicePlugin(pciDriverDir, pciDeviceDir string, maxDevices int, kernelVf
dpdkDriver: dpdkDriver,
scanTicker: time.NewTicker(scanPeriod),
scanDone: make(chan bool, 1),
policy: preferredAllocationPolicyFunc,
}
}

Expand Down Expand Up @@ -143,6 +221,31 @@ func (dp *DevicePlugin) Scan(notifier dpapi.Notifier) error {
}
}

// Implement the PreferredAllocator interface.
func (dp *DevicePlugin) GetPreferredAllocation(rqt *pluginapi.PreferredAllocationRequest) (*pluginapi.PreferredAllocationResponse, error) {
response := &pluginapi.PreferredAllocationResponse{}

for _, req := range rqt.ContainerRequests {
// Add a security check here. This should never happen unless there occurs error in kubelet device plugin manager.
if req.AllocationSize > int32(len(req.AvailableDeviceIDs)) {
var err = errors.Errorf("AllocationSize (%d) is greater than the number of available device IDs (%d)", req.AllocationSize, len(req.AvailableDeviceIDs))
return nil, err
}

IDs := dp.policy(req)
klog.V(3).Infof("AvailableDeviceIDs: %q", req.AvailableDeviceIDs)
klog.V(3).Infof("AllocatedDeviceIDs: %q", IDs)

resp := &pluginapi.ContainerPreferredAllocationResponse{
DeviceIDs: IDs,
}

response.ContainerResponses = append(response.ContainerResponses, resp)
}

return response, nil
}

func (dp *DevicePlugin) getDpdkDevice(vfBdf string) (string, error) {
switch dp.dpdkDriver {
case igbUio:
Expand Down
44 changes: 43 additions & 1 deletion cmd/qat_plugin/dpdkdrv/dpdkdrv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"flag"
"os"
"path"
"reflect"
"testing"

"github.com/pkg/errors"
Expand Down Expand Up @@ -95,7 +96,7 @@ func TestNewDevicePlugin(t *testing.T) {
}
for _, tt := range tcases {
t.Run(tt.name, func(t *testing.T) {
_, err := NewDevicePlugin(1, tt.kernelVfDrivers, tt.dpdkDriver)
_, err := NewDevicePlugin(1, tt.kernelVfDrivers, tt.dpdkDriver, "")

if tt.expectedErr && err == nil {
t.Errorf("Test case '%s': expected error", tt.name)
Expand All @@ -119,6 +120,46 @@ func (n *fakeNotifier) Notify(newDeviceTree dpapi.DeviceTree) {
n.scanDone <- true
}

func TestGetPreferredAllocation(t *testing.T) {
rqt := &pluginapi.PreferredAllocationRequest{
ContainerRequests: []*pluginapi.ContainerPreferredAllocationRequest{
{
AvailableDeviceIDs: []string{"0000:03:00.4", "0000:04:00.1", "0000:05:00.3", "0000:05:00.4", "0000:05:00.1", "0000:04:00.0", "0000:04:00.4", "0000:06:00.4", "0000:04:00.2", "0000:03:00.1", "0000:05:00.0", "0000:05:00.2", "0000:04:00.3", "0000:03:00.2", "0000:06:00.0", "0000:06:00.3", "0000:03:00.3", "0000:03:00.0", "0000:06:00.1", "0000:06:00.2"},
AllocationSize: 4,
},
},
}

plugin := newDevicePlugin("", "", 4, []string{""}, "", nonePolicy)
response, _ := plugin.GetPreferredAllocation(rqt)

if !reflect.DeepEqual(response.ContainerResponses[0].DeviceIDs, []string{"0000:03:00.4", "0000:04:00.1", "0000:05:00.3", "0000:05:00.4"}) {
t.Error("Unexpected return value for balanced preferred allocation")
}

plugin = newDevicePlugin("", "", 4, []string{""}, "", packedPolicy)
response, _ = plugin.GetPreferredAllocation(rqt)

if !reflect.DeepEqual(response.ContainerResponses[0].DeviceIDs, []string{"0000:03:00.0", "0000:03:00.1", "0000:03:00.2", "0000:03:00.3"}) {
t.Error("Unexpected return value for balanced preferred allocation")
}

plugin = newDevicePlugin("", "", 4, []string{""}, "", balancedPolicy)
response, _ = plugin.GetPreferredAllocation(rqt)

if !reflect.DeepEqual(response.ContainerResponses[0].DeviceIDs, []string{"0000:03:00.0", "0000:04:00.0", "0000:05:00.0", "0000:06:00.0"}) {
t.Error("Unexpected return value for balanced preferred allocation")
}

rqt.ContainerRequests[0].AllocationSize = 32
plugin = newDevicePlugin("", "", 4, []string{""}, "", nil)
_, err := plugin.GetPreferredAllocation(rqt)

if err == nil {
t.Error("Unexpected nil value return for err when AllocationSize is greater than the number of available device IDs")
}
}

func TestScan(t *testing.T) {
tcases := []struct {
name string
Expand Down Expand Up @@ -405,6 +446,7 @@ func TestScan(t *testing.T) {
tt.maxDevNum,
tt.kernelVfDrivers,
tt.dpdkDriver,
nil,
)

fN := fakeNotifier{
Expand Down
3 changes: 2 additions & 1 deletion cmd/qat_plugin/qat_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,13 @@ func main() {

dpdkDriver := flag.String("dpdk-driver", "vfio-pci", "DPDK Device driver for configuring the QAT device")
kernelVfDrivers := flag.String("kernel-vf-drivers", "c6xxvf,4xxxvf", "Comma separated VF Device Driver of the QuickAssist Devices in the system. Devices supported: DH895xCC, C62x, C3xxx, C4xxx, 4xxx, and D15xx")
preferredAllocationPolicy := flag.String("allocation-policy", "", "Modes of allocating QAT devices: balanced and packed")
maxNumDevices := flag.Int("max-num-devices", 32, "maximum number of QAT devices to be provided to the QuickAssist device plugin")
flag.Parse()

switch *mode {
case "dpdk":
plugin, err = dpdkdrv.NewDevicePlugin(*maxNumDevices, *kernelVfDrivers, *dpdkDriver)
plugin, err = dpdkdrv.NewDevicePlugin(*maxNumDevices, *kernelVfDrivers, *dpdkDriver, *preferredAllocationPolicy)
case "kernel":
plugin = kerneldrv.NewDevicePlugin()
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ spec:
description: NodeSelector provides a simple way to constrain device
plugin pods to nodes with particular labels.
type: object
preferredAllocationPolicy:
description: PreferredAllocationPolicy sets the mode of allocating
QAT devices on a node. See documentation for detailed description
of the policies.
enum:
- balanced
- packed
type: string
type: object
status:
description: 'QatDevicePluginStatus defines the observed state of QatDevicePlugin.
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/deviceplugin/v1/qatdeviceplugin_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ type QatDevicePluginSpec struct {

// InitImage is a container image with a script that initialize devices.
InitImage string `json:"initImage,omitempty"`
// PreferredAllocationPolicy sets the mode of allocating QAT devices on a node.
// See documentation for detailed description of the policies.
// +kubebuilder:validation:Enum=balanced;packed
PreferredAllocationPolicy string `json:"preferredAllocationPolicy,omitempty"`

// DpdkDriver is a DPDK device driver for configuring the QAT device.
// +kubebuilder:validation:Enum=igb_uio;vfio-pci
Expand Down
4 changes: 4 additions & 0 deletions pkg/controllers/qat/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,5 +280,9 @@ func getPodArgs(qdp *devicepluginv1.QatDevicePlugin) []string {
args = append(args, "-max-num-devices", "32")
}

if qdp.Spec.PreferredAllocationPolicy != "" {
args = append(args, "-allocation-policy", qdp.Spec.PreferredAllocationPolicy)
}

return args
}
4 changes: 4 additions & 0 deletions test/envtest/qatdeviceplugin_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ var _ = Describe("QatDevicePlugin Controller", func() {
updatedDpdkDriver := "igb_uio"
updatedKernelVfDrivers := "c3xxxvf"
updatedMaxNumDevices := 16
updatedPreferredAllocationPolicy := "balanced"
updatedNodeSelector := map[string]string{"updated-qat-nodeselector": "true"}

fetched.Spec.Image = updatedImage
Expand All @@ -104,6 +105,7 @@ var _ = Describe("QatDevicePlugin Controller", func() {
fetched.Spec.DpdkDriver = updatedDpdkDriver
fetched.Spec.KernelVfDrivers = []devicepluginv1.KernelVfDriver{devicepluginv1.KernelVfDriver(updatedKernelVfDrivers)}
fetched.Spec.MaxNumDevices = updatedMaxNumDevices
fetched.Spec.PreferredAllocationPolicy = updatedPreferredAllocationPolicy
fetched.Spec.NodeSelector = updatedNodeSelector

Expect(k8sClient.Update(context.Background(), fetched)).Should(Succeed())
Expand All @@ -126,6 +128,8 @@ var _ = Describe("QatDevicePlugin Controller", func() {
updatedKernelVfDrivers,
"-max-num-devices",
strconv.Itoa(updatedMaxNumDevices),
"-allocation-policy",
updatedPreferredAllocationPolicy,
}

Expect(ds.Spec.Template.Spec.Containers[0].Args).Should(ConsistOf(expectArgs))
Expand Down