Browse code

Merge pull request #20786 from rhvgoyal/min-free-space

devmapper: Add a new option dm.min_free_space_percent

Vincent Batts authored on 2016/03/15 09:10:43
Showing 3 changed files
... ...
@@ -43,11 +43,12 @@ var (
43 43
 	// We retry device removal so many a times that even error messages
44 44
 	// will fill up console during normal operation. So only log Fatal
45 45
 	// messages by default.
46
-	logLevel                     = devicemapper.LogLevelFatal
47
-	driverDeferredRemovalSupport = false
48
-	enableDeferredRemoval        = false
49
-	enableDeferredDeletion       = false
50
-	userBaseSize                 = false
46
+	logLevel                            = devicemapper.LogLevelFatal
47
+	driverDeferredRemovalSupport        = false
48
+	enableDeferredRemoval               = false
49
+	enableDeferredDeletion              = false
50
+	userBaseSize                        = false
51
+	defaultMinFreeSpacePercent   uint32 = 10
51 52
 )
52 53
 
53 54
 const deviceSetMetaFile string = "deviceset-metadata"
... ...
@@ -122,6 +123,7 @@ type DeviceSet struct {
122 122
 	deletionWorkerTicker  *time.Ticker
123 123
 	uidMaps               []idtools.IDMap
124 124
 	gidMaps               []idtools.IDMap
125
+	minFreeSpacePercent   uint32 //min free space percentage in thinpool
125 126
 }
126 127
 
127 128
 // DiskUsage contains information about disk usage and is used when reporting Status of a device.
... ...
@@ -753,6 +755,38 @@ func (devices *DeviceSet) getNextFreeDeviceID() (int, error) {
753 753
 	return 0, fmt.Errorf("devmapper: Unable to find a free device ID")
754 754
 }
755 755
 
756
+func (devices *DeviceSet) poolHasFreeSpace() error {
757
+	if devices.minFreeSpacePercent == 0 {
758
+		return nil
759
+	}
760
+
761
+	_, _, dataUsed, dataTotal, metadataUsed, metadataTotal, err := devices.poolStatus()
762
+	if err != nil {
763
+		return err
764
+	}
765
+
766
+	minFreeData := (dataTotal * uint64(devices.minFreeSpacePercent)) / 100
767
+	if minFreeData < 1 {
768
+		minFreeData = 1
769
+	}
770
+	dataFree := dataTotal - dataUsed
771
+	if dataFree < minFreeData {
772
+		return fmt.Errorf("devmapper: Thin Pool has %v free data blocks which is less than minimum required %v free data blocks. Create more free space in thin pool or use dm.min_free_space option to change behavior", (dataTotal - dataUsed), minFreeData)
773
+	}
774
+
775
+	minFreeMetadata := (metadataTotal * uint64(devices.minFreeSpacePercent)) / 100
776
+	if minFreeMetadata < 1 {
777
+		minFreeData = 1
778
+	}
779
+
780
+	metadataFree := metadataTotal - metadataUsed
781
+	if metadataFree < minFreeMetadata {
782
+		return fmt.Errorf("devmapper: Thin Pool has %v free metadata blocks which is less than minimum required %v free metadata blocks. Create more free metadata space in thin pool or use dm.min_free_space option to change behavior", (metadataTotal - metadataUsed), minFreeMetadata)
783
+	}
784
+
785
+	return nil
786
+}
787
+
756 788
 func (devices *DeviceSet) createRegisterDevice(hash string) (*devInfo, error) {
757 789
 	devices.Lock()
758 790
 	defer devices.Unlock()
... ...
@@ -809,6 +843,10 @@ func (devices *DeviceSet) createRegisterDevice(hash string) (*devInfo, error) {
809 809
 }
810 810
 
811 811
 func (devices *DeviceSet) createRegisterSnapDevice(hash string, baseInfo *devInfo) error {
812
+	if err := devices.poolHasFreeSpace(); err != nil {
813
+		return err
814
+	}
815
+
812 816
 	deviceID, err := devices.getNextFreeDeviceID()
813 817
 	if err != nil {
814 818
 		return err
... ...
@@ -2437,6 +2475,7 @@ func NewDeviceSet(root string, doInit bool, options []string, uidMaps, gidMaps [
2437 2437
 		deletionWorkerTicker:  time.NewTicker(time.Second * 30),
2438 2438
 		uidMaps:               uidMaps,
2439 2439
 		gidMaps:               gidMaps,
2440
+		minFreeSpacePercent:   defaultMinFreeSpacePercent,
2440 2441
 	}
2441 2442
 
2442 2443
 	foundBlkDiscard := false
... ...
@@ -2512,6 +2551,22 @@ func NewDeviceSet(root string, doInit bool, options []string, uidMaps, gidMaps [
2512 2512
 				return nil, err
2513 2513
 			}
2514 2514
 
2515
+		case "dm.min_free_space":
2516
+			if !strings.HasSuffix(val, "%") {
2517
+				return nil, fmt.Errorf("devmapper: Option dm.min_free_space requires %% suffix")
2518
+			}
2519
+
2520
+			valstring := strings.TrimSuffix(val, "%")
2521
+			minFreeSpacePercent, err := strconv.ParseUint(valstring, 10, 32)
2522
+			if err != nil {
2523
+				return nil, err
2524
+			}
2525
+
2526
+			if minFreeSpacePercent >= 100 {
2527
+				return nil, fmt.Errorf("devmapper: Invalid value %v for option dm.min_free_space", val)
2528
+			}
2529
+
2530
+			devices.minFreeSpacePercent = uint32(minFreeSpacePercent)
2515 2531
 		default:
2516 2532
 			return nil, fmt.Errorf("devmapper: Unknown option %s\n", key)
2517 2533
 		}
... ...
@@ -438,6 +438,32 @@ options for `zfs` start with `zfs`.
438 438
     when unintentional leaking of mount point happens across multiple mount
439 439
     namespaces.
440 440
 
441
+*  `dm.min_free_space`
442
+
443
+    Specifies the min free space percent in thin pool require for new device
444
+    creation to succeed. This check applies to both free data space as well
445
+    as free metadata space. Valid values are from 0% - 99%. Value 0% disables
446
+    free space checking logic. If user does not specify a value for this optoin,
447
+    then default value for this option is 10%.
448
+
449
+    Whenever a new thin pool device is created (during docker pull or
450
+    during container creation), docker will check minimum free space is
451
+    available as specified by this parameter. If that is not the case, then
452
+    device creation will fail and docker operation will fail.
453
+
454
+    One will have to create more free space in thin pool to recover from the
455
+    error. Either delete some of the images and containers from thin pool and
456
+    create free space or add more storage to thin pool.
457
+
458
+    For lvm thin pool, one can add more storage to volume group container thin
459
+    pool and that should automatically resolve it. If loop devices are being
460
+    used, then stop docker, grow the size of loop files and restart docker and
461
+    that should resolve the issue.
462
+
463
+    Example use:
464
+
465
+        $ docker daemon --storage-opt dm.min_free_space_percent=10%
466
+
441 467
 Currently supported options of `zfs`:
442 468
 
443 469
 * `zfs.fsname`
... ...
@@ -475,6 +475,30 @@ By default docker will pick up the zfs filesystem where docker graph
475 475
 
476 476
 Example use: `docker daemon -s zfs --storage-opt zfs.fsname=zroot/docker`
477 477
 
478
+#### dm.min_free_space
479
+
480
+Specifies the min free space percent in thin pool require for new device
481
+creation to succeed. This check applies to both free data space as well
482
+as free metadata space. Valid values are from 0% - 99%. Value 0% disables
483
+free space checking logic. If user does not specify a value for this optoin,
484
+then default value for this option is 10%.
485
+
486
+Whenever a new thin pool device is created (during docker pull or
487
+during container creation), docker will check minimum free space is
488
+available as specified by this parameter. If that is not the case, then
489
+device creation will fail and docker operation will fail.
490
+
491
+One will have to create more free space in thin pool to recover from the
492
+error. Either delete some of the images and containers from thin pool and
493
+create free space or add more storage to thin pool.
494
+
495
+For lvm thin pool, one can add more storage to volume group container thin
496
+pool and that should automatically resolve it. If loop devices are being
497
+used, then stop docker, grow the size of loop files and restart docker and
498
+that should resolve the issue.
499
+
500
+Example use: `docker daemon --storage-opt dm.min_free_space_percent=10%`
501
+
478 502
 # CLUSTER STORE OPTIONS
479 503
 
480 504
 The daemon uses libkv to advertise