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:
commit
6b7330dcd4
7 changed files with 88 additions and 38 deletions
|
@ -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
|
||||
}
|
||||
|
|
22
daemon/graphdriver/copy/copy_cgo.go
Normal file
22
daemon/graphdriver/copy/copy_cgo.go
Normal 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
|
||||
}
|
13
daemon/graphdriver/copy/copy_nocgo.go
Normal file
13
daemon/graphdriver/copy/copy_nocgo.go
Normal 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
|
||||
}
|
|
@ -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)
|
||||
|
|
18
daemon/graphdriver/quota/projectquota_unsupported.go
Normal file
18
daemon/graphdriver/quota/projectquota_unsupported.go
Normal 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
|
||||
}
|
19
daemon/graphdriver/quota/types.go
Normal file
19
daemon/graphdriver/quota/types.go
Normal 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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue