Skip to content

Commit

Permalink
F 1 Binding a device by NIC (#476)
Browse files Browse the repository at this point in the history
* [devices] move devices package from crimea to nff-go
* [devices] add a devbind example
  • Loading branch information
Marcus Schiesser authored and aregm committed Sep 21, 2018
1 parent 3d0869a commit 8bb091f
Show file tree
Hide file tree
Showing 9 changed files with 766 additions and 1 deletion.
75 changes: 75 additions & 0 deletions devices/bind.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright 2018 Intel Corporation.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package devices helps to query DPDK compatibles devices and to bind/unbind drivers
package devices

// Device is a DPDK compatible device and should be able to bind, unbind and
// probe.
type Device interface {
// Binds a driver to the device
Bind(driver string) error
// Unbinds the current driver from the device
Unbind() error
// Returns the name of the driver that is currently bound
CurrentDriver() (string, error)
// Probes the currently bound driver and checks if there is an error
Probe() error
// Returns the ID of the device
ID() string
}

// New returns a corresponding device by given input
func New(input string) (Device, error) {
switch {
case IsPciID.Match([]byte(input)):
return NewDeviceByPciID(input)
case IsUUID.Match([]byte(input)):
return NewDeviceByVmbusID(input)
default:
return NewDeviceByNicName(input)
}
}

// NewDeviceByPciID returns a PCI device by given PCI ID
func NewDeviceByPciID(pciID string) (Device, error) {
device, err := GetPciDeviceByPciID(pciID)
if err != nil {
return nil, err
}

return device, nil
}

// NewDeviceByVmbusID returns a VMBus device by given UUID
func NewDeviceByVmbusID(uuid string) (Device, error) {
device, err := GetVmbusDeviceByUUID(uuid)
if err != nil {
return nil, err
}

return device, nil
}

// NewDeviceByNicName returns a device by given NIC name, e.g. eth0.
func NewDeviceByNicName(nicName string) (Device, error) {
devID, err := GetDeviceID(nicName)
if err != nil {
return nil, err
}

device, err := newDevice(devID)
if err != nil {
return nil, err
}

return device, nil
}

func newDevice(id string) (Device, error) {
if IsPciID.Match([]byte(id)) {
return GetPciDeviceByPciID(id)
}
return GetVmbusDeviceByUUID(id)
}
112 changes: 112 additions & 0 deletions devices/consts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright 2018 Intel Corporation.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package devices helps to query DPDK compatibles devices and to bind/unbind drivers
package devices

import (
"fmt"
"regexp"
)

// Driver names
const (
DriverHvNetvcs = "hv_netvcs"
DriverUioPciGeneric = "uio_pci_generic"
DriverIgUio = "ig_uio"
DriverVfioPci = "vfio-pci"
DriverUioHvGeneric = "uio_hv_generic"
)

// Path to PCI
const (
PathSysPciDevices = "/sys/bus/pci/devices"
PathSysPciDrivers = "/sys/bus/pci/drivers"
PathSysPciDriverProbe = "/sys/bus/pci/drivers_probe"
)

// Path to VMBus
const (
PathSysVmbusDevices = "/sys/bus/vmbus/devices"
PathSysVmbusDrivers = "/sys/bus/vmbus/drivers"
)

// Path to net
const (
PathSysClassNet = "/sys/class/net"
)

// Regular expressions for PCI-ID and UUID
var (
IsPciID *regexp.Regexp
IsUUID *regexp.Regexp
)

// DPDK related drivers
var (
DefaultDpdkDriver = DriverUioPciGeneric
DpdkDrivers = [...]string{DriverUioPciGeneric, DriverIgUio, DriverVfioPci, DriverUioHvGeneric}
DpdkPciDrivers = [...]string{DriverUioPciGeneric, DriverIgUio, DriverVfioPci}
DpdkVmbusDrivers = [...]string{DriverUioHvGeneric}
)

type stringBuilder string

func (s stringBuilder) With(args ...interface{}) string {
return fmt.Sprintf(string(s), args...)
}

var (
pathSysPciDevicesBind stringBuilder = PathSysPciDevices + "/%s/driver/bind"
pathSysPciDevicesUnbind stringBuilder = PathSysPciDevices + "/%s/driver/unbind"
pathSysPciDevicesOverrideDriver stringBuilder = PathSysPciDevices + "/%s/driver_override"

pathSysPciDriversBind stringBuilder = PathSysPciDrivers + "/%s/bind"
pathSysPciDriversUnbind stringBuilder = PathSysPciDrivers + "/%s/unbind"
pathSysPciDriversNewID stringBuilder = PathSysPciDrivers + "/%s/new_id"
)

var (
pathSysVmbusDriversBind stringBuilder = PathSysVmbusDrivers + "/%s/bind"
pathSysVmbusDriversUnbind stringBuilder = PathSysVmbusDrivers + "/%s/unbind"
pathSysVmbusDriversNewID stringBuilder = PathSysVmbusDrivers + "/%s/new_id"
)

var (
pathSysClassNetDeviceDriver stringBuilder = PathSysClassNet + "/%s/device/driver"
pathSysClassNetDevice stringBuilder = PathSysClassNet + "/%s/device"
)

var (
vmbusDeviceStringer stringBuilder = "ID: %s\nClass:\t%s\nDriver:\t%s"
pciDeviceStringer stringBuilder = "ID: %s\nClass:\t%s\nVendor:\t%s\nDevice:\t%s\nDriver:\t%s"
)

var (
// for ethtool output
rPciIDForEthtool *regexp.Regexp

// for lspci output
rPciClass *regexp.Regexp
rPciVendor *regexp.Regexp
rPciDevice *regexp.Regexp
rPciDriver *regexp.Regexp

// for find output
rVmbusDriver *regexp.Regexp
)

func init() {
rPciIDForEthtool = regexp.MustCompile("bus-info:\\s([0-9:.]+)")

rPciClass = regexp.MustCompile("[Cc]lass:\\s([0-9a-zA-Z]{4})")
rPciVendor = regexp.MustCompile("[Vv]endor:\\s([0-9a-zA-Z]{4})")
rPciDevice = regexp.MustCompile("[Dd]evice:\\s([0-9a-zA-Z]{4})")
rPciDriver = regexp.MustCompile("[Dd]river:\\s(\\S+)")

rVmbusDriver = regexp.MustCompile("/sys/bus/vmbus/drivers/(\\S+)/")

IsPciID = regexp.MustCompile("^\\d{4}:\\d{2}:\\d{2}.\\d$")
IsUUID = regexp.MustCompile("^[[:xdigit:]]{8}-[[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}$")
}
21 changes: 21 additions & 0 deletions devices/errno.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2018 Intel Corporation.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package devices helps to query DPDK compatibles devices and to bind/unbind drivers
package devices

import (
"errors"
)

// Errors of devices package
var (
ErrNoBoundDriver = errors.New("no driver is bound to the device")
ErrAlreadyBoundDriver = errors.New("device has already bound the selected driver")
ErrBind = errors.New("fail to bind the driver")
ErrUnbind = errors.New("fail to unbind the driver")
ErrUnsupportedDriver = errors.New("unsupported DPDK driver")
ErrNotProbe = errors.New("device doesn't support 'drive_probe'")
ErrKernelModuleNotLoaded = errors.New("kernel module is not loaded")
)
98 changes: 98 additions & 0 deletions devices/misc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright 2018 Intel Corporation.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package devices helps to query DPDK compatibles devices and to bind/unbind drivers
package devices

import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
)

var defaultTimeoutLimitation = 5 * time.Second

// FindDefaultDpdkDriver returns a default DPDK driver that the given NIC can
// use.
func FindDefaultDpdkDriver(nicName string) string {
driver, err := readlinkBaseCmd(pathSysClassNetDeviceDriver.With(nicName))
if err != nil {
return DefaultDpdkDriver
}
switch driver {
case DriverHvNetvcs:
return DriverUioHvGeneric
default:
return DefaultDpdkDriver
}
}

// GetDeviceID returns the device ID of given NIC name.
func GetDeviceID(nicName string) (string, error) {
// DEV_ID=$(basename $(readlink /sys/class/net/<nicName>/device))
return readlinkBaseCmd(pathSysClassNetDevice.With(nicName))
}

// IsModuleLoaded checks if the kernel has already loaded the driver or not.
func IsModuleLoaded(driver string) bool {
output, err := cmdOutputWithTimeout(defaultTimeoutLimitation, "lsmod")
if err != nil {
// Can't run lsmod, return false
return false
}
lines := strings.Split(string(output), "\n")
for _, line := range lines {
if checkModuleLine(line, driver) {
return true
}
}
return false
}

func writeToTargetWithData(sysfs string, flag int, mode os.FileMode, data string) error {
writer, err := os.OpenFile(sysfs, flag, mode)
if err != nil {
return fmt.Errorf("OpenFile failed: %s", err.Error())
}
defer writer.Close()

_, err = writer.Write([]byte(data))
if err != nil {
return fmt.Errorf("WriteFile failed: %s", err.Error())
}

return nil
}

func readlinkBaseCmd(path string) (string, error) {
output, err := cmdOutputWithTimeout(defaultTimeoutLimitation, "readlink", path)
if err != nil {
return "", fmt.Errorf("Cmd Execute readlink failed: %s", err.Error())
}
outputStr := strings.Trim(string(output), "\n")
result := filepath.Base(outputStr)
return result, nil
}

func checkModuleLine(line, driver string) bool {
if !strings.Contains(line, driver) {
return false
}
elements := strings.Split(line, " ")
return elements[0] == driver
}

func cmdOutputWithTimeout(duration time.Duration, cmd string, parameters ...string) ([]byte, error) {
ctx, cancel := context.WithTimeout(context.Background(), duration)
defer cancel()
output, err := exec.CommandContext(ctx, cmd, parameters...).Output()
if err != nil {
return nil, err
}
return output, nil
}
Loading

0 comments on commit 8bb091f

Please sign in to comment.