Merge pull request #310 from kolyshkin/19.03-quota-map

[19.03] backport projectquota: protect concurrent map access (ENGCORE-920)
This commit is contained in:
Andrew Hsu 2019-08-07 16:58:28 -07:00 committed by GitHub
commit 6b7330dcd4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 88 additions and 38 deletions

View file

@ -2,14 +2,6 @@
package copy // import "github.com/docker/docker/daemon/graphdriver/copy"
/*
#include <linux/fs.h>
#ifndef FICLONE
#define FICLONE _IOW(0x94, 9, int)
#endif
*/
import "C"
import (
"container/list"
"fmt"
@ -50,7 +42,7 @@ func copyRegular(srcPath, dstPath string, fileinfo os.FileInfo, copyWithFileRang
defer dstFile.Close()
if *copyWithFileClone {
_, _, err = unix.Syscall(unix.SYS_IOCTL, dstFile.Fd(), C.FICLONE, srcFile.Fd())
err = fiClone(srcFile, dstFile)
if err == nil {
return nil
}

View file

@ -0,0 +1,22 @@
// +build linux,cgo
package copy // import "github.com/docker/docker/daemon/graphdriver/copy"
/*
#include <linux/fs.h>
#ifndef FICLONE
#define FICLONE _IOW(0x94, 9, int)
#endif
*/
import "C"
import (
"os"
"golang.org/x/sys/unix"
)
func fiClone(srcFile, dstFile *os.File) error {
_, _, err := unix.Syscall(unix.SYS_IOCTL, dstFile.Fd(), C.FICLONE, srcFile.Fd())
return err
}

View file

@ -0,0 +1,13 @@
// +build linux,!cgo
package copy // import "github.com/docker/docker/daemon/graphdriver/copy"
import (
"os"
"golang.org/x/sys/unix"
)
func fiClone(srcFile, dstFile *os.File) error {
return unix.ENOSYS
}

View file

@ -1,4 +1,4 @@
// +build linux
// +build linux,!exclude_disk_quota
//
// projectquota.go - implements XFS project quota controls
@ -63,19 +63,6 @@ import (
"golang.org/x/sys/unix"
)
// Quota limit params - currently we only control blocks hard limit
type Quota struct {
Size uint64
}
// Control - Context to be used by storage driver (e.g. overlay)
// who wants to apply project quotas to container dirs
type Control struct {
backingFsBlockDev string
nextProjectID uint32
quotas map[string]uint32
}
// NewControl - initialize project quota support.
// Test to make sure that quota can be set on a test dir and find
// the first project id to be used for the next container create.
@ -166,9 +153,11 @@ func NewControl(basePath string) (*Control, error) {
// SetQuota - assign a unique project id to directory and set the quota limits
// for that project id
func (q *Control) SetQuota(targetPath string, quota Quota) error {
q.RLock()
projectID, ok := q.quotas[targetPath]
q.RUnlock()
if !ok {
q.Lock()
projectID = q.nextProjectID
//
@ -176,11 +165,12 @@ func (q *Control) SetQuota(targetPath string, quota Quota) error {
//
err := setProjectID(targetPath, projectID)
if err != nil {
q.Unlock()
return err
}
q.quotas[targetPath] = projectID
q.nextProjectID++
q.Unlock()
}
//
@ -217,8 +207,9 @@ func setProjectQuota(backingFsBlockDev string, projectID uint32, quota Quota) er
// GetQuota - get the quota limits of a directory that was configured with SetQuota
func (q *Control) GetQuota(targetPath string, quota *Quota) error {
q.RLock()
projectID, ok := q.quotas[targetPath]
q.RUnlock()
if !ok {
return errors.Errorf("quota not found for path: %s", targetPath)
}
@ -289,6 +280,8 @@ func setProjectID(targetPath string, projectID uint32) error {
// findNextProjectID - find the next project id to be used for containers
// by scanning driver home directory to find used project ids
func (q *Control) findNextProjectID(home string) error {
q.Lock()
defer q.Unlock()
files, err := ioutil.ReadDir(home)
if err != nil {
return errors.Errorf("read directory failed: %s", home)

View file

@ -0,0 +1,18 @@
// +build linux,exclude_disk_quota
package quota // import "github.com/docker/docker/daemon/graphdriver/quota"
func NewControl(basePath string) (*Control, error) {
return nil, ErrQuotaNotSupported
}
// SetQuota - assign a unique project id to directory and set the quota limits
// for that project id
func (q *Control) SetQuota(targetPath string, quota Quota) error {
return ErrQuotaNotSupported
}
// GetQuota - get the quota limits of a directory that was configured with SetQuota
func (q *Control) GetQuota(targetPath string, quota *Quota) error {
return ErrQuotaNotSupported
}

View file

@ -0,0 +1,19 @@
// +build linux
package quota // import "github.com/docker/docker/daemon/graphdriver/quota"
import "sync"
// Quota limit params - currently we only control blocks hard limit
type Quota struct {
Size uint64
}
// Control - Context to be used by storage driver (e.g. overlay)
// who wants to apply project quotas to container dirs
type Control struct {
backingFsBlockDev string
sync.RWMutex // protect nextProjectID and quotas map
nextProjectID uint32
quotas map[string]uint32
}

View file

@ -9,13 +9,9 @@ import (
"strings"
"github.com/opencontainers/runc/libcontainer/system"
"golang.org/x/sys/unix"
)
/*
#include <unistd.h>
*/
import "C"
// platformNewStatsCollector performs platform specific initialisation of the
// Collector structure.
func platformNewStatsCollector(s *Collector) {
@ -70,13 +66,10 @@ func (s *Collector) getSystemCPUUsage() (uint64, error) {
}
func (s *Collector) getNumberOnlineCPUs() (uint32, error) {
i, err := C.sysconf(C._SC_NPROCESSORS_ONLN)
// According to POSIX - errno is undefined after successful
// sysconf, and can be non-zero in several cases, so look for
// error in returned value not in errno.
// (https://sourceware.org/bugzilla/show_bug.cgi?id=21536)
if i == -1 {
var cpuset unix.CPUSet
err := unix.SchedGetaffinity(0, &cpuset)
if err != nil {
return 0, err
}
return uint32(i), nil
return uint32(cpuset.Count()), nil
}