Skip to content
This repository has been archived by the owner on May 12, 2021. It is now read-only.

Commit

Permalink
virtcontainers: Implement function to get the pmem DeviceInfo
Browse files Browse the repository at this point in the history
Implement function to get the pmem `DeviceInfo` from a volume.
`PmemDeviceInfo` return a new `DeviceInfo` object if a volume has a loop device
as backend and the backing file for such loop device contains the PFN signature,
needed to enable DAX in the guest.

Signed-off-by: Julio Montes <[email protected]>
  • Loading branch information
Julio Montes committed Mar 20, 2020
1 parent 9ff44db commit ee941e5
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 0 deletions.
8 changes: 8 additions & 0 deletions virtcontainers/device/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ type DeviceInfo struct {
Major int64
Minor int64

// Pmem enabled persistent memory. Use HostPath as backing file
// for a nvdimm device in the guest.
Pmem bool

// FileMode permission bits for the device.
FileMode os.FileMode

Expand Down Expand Up @@ -169,6 +173,10 @@ type BlockDrive struct {

// ReadOnly sets the device file readonly
ReadOnly bool

// Pmem enables persistent memory. Use File as backing file
// for a nvdimm device in the guest
Pmem bool
}

// VFIODeviceType indicates VFIO device type
Expand Down
116 changes: 116 additions & 0 deletions virtcontainers/device/config/pmem.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright (c) 2020 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//

package config

import (
"fmt"
"os"
"syscall"

"github.com/kata-containers/runtime/virtcontainers/utils"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)

const (
// This signature is defined in the linux NVDIMM driver.
// devices or backing files with this signature can be used
// as pmem (persistent memory) devices in the guest.
pfnSignature = "NVDIMM_PFN_INFO"

// offset in the backing file where the signature should be
pfnSignatureOffset = int64(4 * 1024)
)

var (
pmemLog = logrus.WithField("source", "virtcontainers/device/config")
)

// PmemDeviceInfo returns a DeviceInfo if a loop device
// is mounted on source, and the backing file of the loop device
// has the PFN signature.
func PmemDeviceInfo(source, destination string) (*DeviceInfo, error) {
stat := syscall.Stat_t{}
err := syscall.Stat(source, &stat)
if err != nil {
return nil, err
}

// device object is still incomplete,
// but it can be used to fetch the backing file
device := &DeviceInfo{
ContainerPath: destination,
DevType: "b",
Major: int64(unix.Major(stat.Dev)),
Minor: int64(unix.Minor(stat.Dev)),
Pmem: true,
DriverOptions: make(map[string]string),
}

pmemLog.WithFields(
logrus.Fields{
"major": device.Major,
"minor": device.Minor,
}).Debug("looking for backing file")

device.HostPath, err = getBackingFile(*device)
if err != nil {
return nil, err
}

pmemLog.WithField("backing-file", device.HostPath).
Debug("backing file found: looking for PFN signature")

if !hasPFNSignature(device.HostPath) {
return nil, fmt.Errorf("backing file %v has not PFN signature", device.HostPath)
}

_, fstype, err := utils.GetDevicePathAndFsType(source)
if err != nil {
pmemLog.WithError(err).WithField("mount-point", source).Warn("failed to get fstype: using ext4")
fstype = "ext4"
}

pmemLog.WithField("fstype", fstype).Debug("filesystem for mount point")
device.DriverOptions["fstype"] = fstype

return device, nil
}

// returns true if the file/device path has the PFN signature
// required to use it as PMEM device and enable DAX.
// See [1] to know more about the PFN signature.
//
// [1] - https://github.com/kata-containers/osbuilder/blob/master/image-builder/nsdax.gpl.c
func hasPFNSignature(path string) bool {
f, err := os.Open(path)
if err != nil {
pmemLog.WithError(err).Error("Could not get PFN signature")
return false
}
defer f.Close()

signatureLen := len(pfnSignature)
signature := make([]byte, signatureLen)

l, err := f.ReadAt(signature, pfnSignatureOffset)
if err != nil {
pmemLog.WithError(err).Debug("Could not read pfn signature")
return false
}

pmemLog.WithFields(logrus.Fields{
"path": path,
"signature": string(signature),
}).Debug("got signature")

if l != signatureLen {
pmemLog.WithField("read-bytes", l).Debug("Incomplete signature")
return false
}

return pfnSignature == string(signature)
}
49 changes: 49 additions & 0 deletions virtcontainers/device/config/pmem_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) 2020 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//

package config

import (
"io/ioutil"
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
)

func createPFNFile(assert *assert.Assertions, dir string) string {
pfnPath := filepath.Join(dir, "pfn")
file, err := os.Create(pfnPath)
assert.NoError(err)
defer file.Close()

l, err := file.WriteAt([]byte(pfnSignature), pfnSignatureOffset)
assert.NoError(err)
assert.Equal(len(pfnSignature), l)

return pfnPath
}

func TestHasPFNSignature(t *testing.T) {
assert := assert.New(t)

b := hasPFNSignature("/abc/xyz/123/sw")
assert.False(b)

f, err := ioutil.TempFile("", "pfn")
assert.NoError(err)
f.Close()
defer os.Remove(f.Name())

b = hasPFNSignature(f.Name())
assert.False(b)

pfnFile := createPFNFile(assert, os.TempDir())
defer os.Remove(pfnFile)

b = hasPFNSignature(pfnFile)
assert.True(b)
}

0 comments on commit ee941e5

Please sign in to comment.