From 2dba8f17158acbe29f19dff3c4a56361bcd2b222 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Stachowski?= Date: Thu, 3 Aug 2017 10:21:46 +0200 Subject: [PATCH] Support for hugetlbfs in empty dir volume plugin --- pkg/api/types.go | 5 +-- pkg/volume/empty_dir/empty_dir.go | 45 +++++++++++++++++++++---- pkg/volume/empty_dir/empty_dir_linux.go | 7 +++- pkg/volume/empty_dir/empty_dir_test.go | 11 +++++- staging/src/k8s.io/api/core/v1/types.go | 5 +-- 5 files changed, 61 insertions(+), 12 deletions(-) diff --git a/pkg/api/types.go b/pkg/api/types.go index c6e57efe471..6cb987d0407 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -681,8 +681,9 @@ type EmptyDirVolumeSource struct { type StorageMedium string const ( - StorageMediumDefault StorageMedium = "" // use whatever the default is for the node - StorageMediumMemory StorageMedium = "Memory" // use memory (tmpfs) + StorageMediumDefault StorageMedium = "" // use whatever the default is for the node + StorageMediumMemory StorageMedium = "Memory" // use memory (tmpfs) + StorageMediumHugepages StorageMedium = "Hugepages" // use hugepages ) // Protocol defines network protocols supported for things like container ports. diff --git a/pkg/volume/empty_dir/empty_dir.go b/pkg/volume/empty_dir/empty_dir.go index 76da7c3cffe..575d0778a2e 100644 --- a/pkg/volume/empty_dir/empty_dir.go +++ b/pkg/volume/empty_dir/empty_dir.go @@ -104,9 +104,11 @@ func (plugin *emptyDirPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, opts vo func (plugin *emptyDirPlugin) newMounterInternal(spec *volume.Spec, pod *v1.Pod, mounter mount.Interface, mountDetector mountDetector, opts volume.VolumeOptions) (volume.Mounter, error) { medium := v1.StorageMediumDefault + if spec.Volume.EmptyDir != nil { // Support a non-specified source as EmptyDir. medium = spec.Volume.EmptyDir.Medium } + return &emptyDir{ pod: pod, volName: spec.Name(), @@ -159,8 +161,9 @@ type mountDetector interface { type storageMedium int const ( - mediumUnknown storageMedium = 0 // assume anything we don't explicitly handle is this - mediumMemory storageMedium = 1 // memory (e.g. tmpfs on linux) + mediumUnknown storageMedium = 0 // assume anything we don't explicitly handle is this + mediumMemory storageMedium = 1 // memory (e.g. tmpfs on linux) + mediumHugepages storageMedium = 2 // hugepages ) // EmptyDir volumes are temporary directories exposed to the pod. @@ -221,6 +224,8 @@ func (ed *emptyDir) SetUpAt(dir string, fsGroup *int64) error { err = ed.setupDir(dir) case v1.StorageMediumMemory: err = ed.setupTmpfs(dir) + case v1.StorageMediumHugepages: + err = ed.setupHugepages(dir) default: err = fmt.Errorf("unknown storage medium %q", ed.medium) } @@ -257,6 +262,29 @@ func (ed *emptyDir) setupTmpfs(dir string) error { return ed.mounter.Mount("tmpfs", dir, "tmpfs", nil /* options */) } +// setupHugepages creates a hugepage mount at the specified directory. +func (ed *emptyDir) setupHugepages(dir string) error { + if ed.mounter == nil { + return fmt.Errorf("memory storage requested, but mounter is nil") + } + if err := ed.setupDir(dir); err != nil { + return err + } + // Make SetUp idempotent. + medium, isMnt, err := ed.mountDetector.GetMountMedium(dir) + if err != nil { + return err + } + // If the directory is a mountpoint with medium memory, there is no + // work to do since we are already in the desired state. + if isMnt && medium == mediumHugepages { + return nil + } + + glog.V(3).Infof("pod %v: mounting hugepages for volume %v", ed.pod.UID, ed.volName) + return ed.mounter.Mount("nodev", dir, "hugetlbfs", []string{}) +} + // setupDir creates the directory with the default permissions specified by the perm constant. func (ed *emptyDir) setupDir(dir string) error { // Create the directory if it doesn't already exist. @@ -318,9 +346,14 @@ func (ed *emptyDir) TearDownAt(dir string) error { if err != nil { return err } - if isMnt && medium == mediumMemory { - ed.medium = v1.StorageMediumMemory - return ed.teardownTmpfs(dir) + if isMnt { + if medium == mediumMemory { + ed.medium = v1.StorageMediumMemory + return ed.teardownTmpfsOrHugetlbfs(dir) + } else if medium == mediumHugepages { + ed.medium = v1.StorageMediumHugepages + return ed.teardownTmpfsOrHugetlbfs(dir) + } } // assume StorageMediumDefault return ed.teardownDefault(dir) @@ -336,7 +369,7 @@ func (ed *emptyDir) teardownDefault(dir string) error { return nil } -func (ed *emptyDir) teardownTmpfs(dir string) error { +func (ed *emptyDir) teardownTmpfsOrHugetlbfs(dir string) error { if ed.mounter == nil { return fmt.Errorf("memory storage requested, but mounter is nil") } diff --git a/pkg/volume/empty_dir/empty_dir_linux.go b/pkg/volume/empty_dir/empty_dir_linux.go index b086429f9cb..630a49fc2e9 100644 --- a/pkg/volume/empty_dir/empty_dir_linux.go +++ b/pkg/volume/empty_dir/empty_dir_linux.go @@ -27,7 +27,10 @@ import ( ) // Defined by Linux - the type number for tmpfs mounts. -const linuxTmpfsMagic = 0x01021994 +const ( + linuxTmpfsMagic = 0x01021994 + linuxHugetlbfsMagic = 0x958458f6 +) // realMountDetector implements mountDetector in terms of syscalls. type realMountDetector struct { @@ -48,6 +51,8 @@ func (m *realMountDetector) GetMountMedium(path string) (storageMedium, bool, er glog.V(5).Infof("Statfs_t of %v: %+v", path, buf) if buf.Type == linuxTmpfsMagic { return mediumMemory, !notMnt, nil + } else if buf.Type == linuxHugetlbfsMagic { + return mediumHugepages, !notMnt, nil } return mediumUnknown, !notMnt, nil } diff --git a/pkg/volume/empty_dir/empty_dir_test.go b/pkg/volume/empty_dir/empty_dir_test.go index 31cc7f5a312..4a007a8e8a4 100644 --- a/pkg/volume/empty_dir/empty_dir_test.go +++ b/pkg/volume/empty_dir/empty_dir_test.go @@ -80,6 +80,15 @@ func TestPluginEmptyRootContext(t *testing.T) { expectedTeardownMounts: 0}) } +func TestPluginHugetlbfs(t *testing.T) { + doTestPlugin(t, pluginTestConfig{ + medium: v1.StorageMediumHugepages, + expectedSetupMounts: 1, + expectedTeardownMounts: 0, + shouldBeMountedBeforeTeardown: true, + }) +} + type pluginTestConfig struct { medium v1.StorageMedium idempotent bool @@ -165,7 +174,7 @@ func doTestPlugin(t *testing.T, config pluginTestConfig) { if e, a := config.expectedSetupMounts, len(physicalMounter.Log); e != a { t.Errorf("Expected %v physicalMounter calls during setup, got %v", e, a) } else if config.expectedSetupMounts == 1 && - (physicalMounter.Log[0].Action != mount.FakeActionMount || physicalMounter.Log[0].FSType != "tmpfs") { + (physicalMounter.Log[0].Action != mount.FakeActionMount || (physicalMounter.Log[0].FSType != "tmpfs" && physicalMounter.Log[0].FSType != "hugetlbfs")) { t.Errorf("Unexpected physicalMounter action during setup: %#v", physicalMounter.Log[0]) } physicalMounter.ResetLog() diff --git a/staging/src/k8s.io/api/core/v1/types.go b/staging/src/k8s.io/api/core/v1/types.go index 391736885d3..eef13a25a9f 100644 --- a/staging/src/k8s.io/api/core/v1/types.go +++ b/staging/src/k8s.io/api/core/v1/types.go @@ -943,8 +943,9 @@ type FlockerVolumeSource struct { type StorageMedium string const ( - StorageMediumDefault StorageMedium = "" // use whatever the default is for the node - StorageMediumMemory StorageMedium = "Memory" // use memory (tmpfs) + StorageMediumDefault StorageMedium = "" // use whatever the default is for the node + StorageMediumMemory StorageMedium = "Memory" // use memory (tmpfs) + StorageMediumHugepages StorageMedium = "HugePages" // use hugepages ) // Protocol defines network protocols supported for things like container ports.