Skip to content

Commit 85d7426

Browse files
authored
Merge pull request #273 from rhvgoyal/overlay-quota
BACKPORT: Overlay2 Quota Support
2 parents 638a809 + 2f4cee4 commit 85d7426

7 files changed

Lines changed: 452 additions & 22 deletions

File tree

daemon/graphdriver/overlay/overlay.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ func (d *Driver) Put(id string) error {
377377
if count := d.ctr.Decrement(mountpoint); count > 0 {
378378
return nil
379379
}
380-
if err := syscall.Unmount(mountpoint, 0); err != nil {
380+
if err := syscall.Unmount(mountpoint, syscall.MNT_DETACH); err != nil {
381381
logrus.Debugf("Failed to unmount %s overlay: %v", id, err)
382382
}
383383
return nil

daemon/graphdriver/overlay2/overlay.go

Lines changed: 86 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/docker/docker/pkg/parsers"
2525
"github.com/docker/docker/pkg/parsers/kernel"
2626
"github.com/docker/docker/pkg/system"
27+
"github.com/docker/go-units"
2728
"github.com/opencontainers/runc/libcontainer/label"
2829
)
2930

@@ -75,15 +76,25 @@ const (
7576
idLength = 26
7677
)
7778

79+
type overlayOptions struct {
80+
overrideKernelCheck bool
81+
quota graphdriver.Quota
82+
}
83+
7884
// Driver contains information about the home directory and the list of active mounts that are created using this driver.
7985
type Driver struct {
80-
home string
81-
uidMaps []idtools.IDMap
82-
gidMaps []idtools.IDMap
83-
ctr *graphdriver.RefCounter
86+
home string
87+
uidMaps []idtools.IDMap
88+
gidMaps []idtools.IDMap
89+
ctr *graphdriver.RefCounter
90+
quotaCtl *graphdriver.QuotaCtl
91+
options overlayOptions
8492
}
8593

86-
var backingFs = "<unknown>"
94+
var (
95+
backingFs = "<unknown>"
96+
projectQuotaSupported = false
97+
)
8798

8899
func init() {
89100
graphdriver.Register(driverName, Init)
@@ -149,13 +160,24 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
149160
uidMaps: uidMaps,
150161
gidMaps: gidMaps,
151162
ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)),
163+
options: *opts,
152164
}
153165

154-
return d, nil
155-
}
166+
if backingFs == "xfs" {
167+
// Try to enable project quota support over xfs.
168+
if d.quotaCtl, err = graphdriver.NewQuotaCtl(home); err == nil {
169+
projectQuotaSupported = true
170+
} else if opts.quota.Size > 0 {
171+
return nil, fmt.Errorf("Storage option overlay2.size not supported. Filesystem does not support Project Quota: %v", err)
172+
}
173+
} else if opts.quota.Size > 0 {
174+
// if xfs is not the backing fs then error out if the storage-opt overlay2.size is used.
175+
return nil, fmt.Errorf("Storage Option overlay2.size only supported for backingFS XFS. Found %v", backingFs)
176+
}
156177

157-
type overlayOptions struct {
158-
overrideKernelCheck bool
178+
logrus.Debugf("backingFs=%s, projectQuotaSupported=%v", backingFs, projectQuotaSupported)
179+
180+
return d, nil
159181
}
160182

161183
func parseOptions(options []string) (*overlayOptions, error) {
@@ -172,8 +194,14 @@ func parseOptions(options []string) (*overlayOptions, error) {
172194
if err != nil {
173195
return nil, err
174196
}
197+
case "overlay2.size":
198+
size, err := units.RAMInBytes(val)
199+
if err != nil {
200+
return nil, err
201+
}
202+
o.quota.Size = uint64(size)
175203
default:
176-
return nil, fmt.Errorf("overlay2: Unknown option %s\n", key)
204+
return nil, fmt.Errorf("overlay2: unknown option %s", key)
177205
}
178206
}
179207
return o, nil
@@ -253,11 +281,22 @@ func (d *Driver) CreateReadWrite(id, parent, mountLabel string, storageOpt map[s
253281
// Create is used to create the upper, lower, and merge directories required for overlay fs for a given id.
254282
// The parent filesystem is used to configure these directories for the overlay.
255283
func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]string) (retErr error) {
284+
if len(storageOpt) != 0 && !projectQuotaSupported {
285+
return fmt.Errorf("--storage-opt is supported only for overlay over xfs with 'pquota' mount option")
286+
}
256287

257-
if len(storageOpt) != 0 {
258-
return fmt.Errorf("--storage-opt is not supported for overlay")
288+
if storageOpt == nil {
289+
storageOpt = map[string]string{}
259290
}
260291

292+
if _, ok := storageOpt["size"]; !ok {
293+
storageOpt["size"] = strconv.FormatUint(d.options.quota.Size, 10)
294+
}
295+
return d.create(id, parent, storageOpt)
296+
}
297+
298+
func (d *Driver) create(id, parent string, storageOpt map[string]string) (retErr error) {
299+
261300
dir := d.dir(id)
262301

263302
rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
@@ -278,6 +317,20 @@ func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]str
278317
}
279318
}()
280319

320+
if len(storageOpt) > 0 {
321+
driver := &Driver{}
322+
if err := d.parseStorageOpt(storageOpt, driver); err != nil {
323+
return err
324+
}
325+
326+
if driver.options.quota.Size > 0 {
327+
// Set container disk quota limit
328+
if err := d.quotaCtl.SetQuota(dir, driver.options.quota); err != nil {
329+
return err
330+
}
331+
}
332+
}
333+
281334
if err := idtools.MkdirAs(path.Join(dir, "diff"), 0755, rootUID, rootGID); err != nil {
282335
return err
283336
}
@@ -317,6 +370,26 @@ func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]str
317370
return nil
318371
}
319372

373+
// Parse overlay storage options
374+
func (d *Driver) parseStorageOpt(storageOpt map[string]string, driver *Driver) error {
375+
// Read size to set the disk project quota per container
376+
for key, val := range storageOpt {
377+
key := strings.ToLower(key)
378+
switch key {
379+
case "size":
380+
size, err := units.RAMInBytes(val)
381+
if err != nil {
382+
return err
383+
}
384+
driver.options.quota.Size = uint64(size)
385+
default:
386+
return fmt.Errorf("Unknown option %s", key)
387+
}
388+
}
389+
390+
return nil
391+
}
392+
320393
func (d *Driver) getLower(parent string) (string, error) {
321394
parentDir := d.dir(parent)
322395

@@ -440,7 +513,7 @@ func (d *Driver) Put(id string) error {
440513
if count := d.ctr.Decrement(mountpoint); count > 0 {
441514
return nil
442515
}
443-
if err := syscall.Unmount(mountpoint, 0); err != nil {
516+
if err := syscall.Unmount(mountpoint, syscall.MNT_DETACH); err != nil {
444517
logrus.Debugf("Failed to unmount %s overlay: %v", id, err)
445518
}
446519
return nil

0 commit comments

Comments
 (0)