mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-06-22 13:38:26 +00:00
virtcontainers: Implement function to get the pmem DeviceInfo
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 <julio.montes@intel.com>
This commit is contained in:
parent
9ff44dba87
commit
ee941e5c56
@ -113,6 +113,10 @@ type DeviceInfo struct {
|
|||||||
Major int64
|
Major int64
|
||||||
Minor 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 permission bits for the device.
|
||||||
FileMode os.FileMode
|
FileMode os.FileMode
|
||||||
|
|
||||||
@ -169,6 +173,10 @@ type BlockDrive struct {
|
|||||||
|
|
||||||
// ReadOnly sets the device file readonly
|
// ReadOnly sets the device file readonly
|
||||||
ReadOnly bool
|
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
|
// VFIODeviceType indicates VFIO device type
|
||||||
|
116
virtcontainers/device/config/pmem.go
Normal file
116
virtcontainers/device/config/pmem.go
Normal file
@ -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
virtcontainers/device/config/pmem_test.go
Normal file
49
virtcontainers/device/config/pmem_test.go
Normal file
@ -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)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user