Discard all data on devicemapper devices when deleting them
This works around the fact that deleting a device in a thin pool doesn't discard the free space. Unfortunately even this is not perfect, as it seems discards are respected only for blocks that has never been shared in the thin device code. However, this has been fixed in the upstream kernel device-mapper tree: http://git.kernel.org/cgit/linux/kernel/git/device-mapper/linux-dm.git/commit/?h=for-next&id=0ab1c92ff748b745c1ed7cde31bb37ad2c5f901a When this hits the kernel I belive this will fully return space for removed images/containers to the host FS. For now it only helps partially (which is better than nothing). Docker-DCO-1.1-Signed-off-by: Alexander Larsson <alexl@redhat.com> (github: alexlarsson)
This commit is contained in:
parent
07f62f199f
commit
93e120e7d6
5 changed files with 49 additions and 0 deletions
|
@ -568,6 +568,15 @@ func (devices *DeviceSet) removeDevice(hash string) error {
|
|||
return fmt.Errorf("hash %s doesn't exists", hash)
|
||||
}
|
||||
|
||||
// This is a workaround for the kernel not discarding block so
|
||||
// on the thin pool when we remove a thinp device, so we do it
|
||||
// manually
|
||||
if err := devices.activateDeviceIfNeeded(hash); err == nil {
|
||||
if err := BlockDeviceDiscard(info.DevName()); err != nil {
|
||||
utils.Debugf("Error discarding block on device: %s (ignoring)\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
devinfo, _ := getInfo(info.Name())
|
||||
if devinfo != nil && devinfo.Exists != 0 {
|
||||
if err := removeDevice(info.Name()); err != nil {
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"fmt"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"runtime"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type DevmapperLogger interface {
|
||||
|
@ -288,6 +289,29 @@ func GetBlockDeviceSize(file *osFile) (uint64, error) {
|
|||
return uint64(size), nil
|
||||
}
|
||||
|
||||
func BlockDeviceDiscard(path string) error {
|
||||
file, err := osOpenFile(path, osORdWr, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
size, err := GetBlockDeviceSize(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ioctlBlkDiscard(file.Fd(), 0, size); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Without this sometimes the remove of the device that happens after
|
||||
// discard fails with EBUSY.
|
||||
syscall.Sync()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// This is the programmatic example of "dmsetup create"
|
||||
func createPool(poolName string, dataFile, metadataFile *osFile) error {
|
||||
task, err := createTask(DeviceCreate, poolName)
|
||||
|
|
|
@ -66,6 +66,7 @@ type (
|
|||
// IOCTL consts
|
||||
const (
|
||||
BlkGetSize64 = C.BLKGETSIZE64
|
||||
BlkDiscard = C.BLKDISCARD
|
||||
|
||||
LoopSetFd = C.LOOP_SET_FD
|
||||
LoopCtlGetFree = C.LOOP_CTL_GET_FREE
|
||||
|
|
|
@ -641,6 +641,10 @@ func TestDriverRemove(t *testing.T) {
|
|||
"DmTaskSetMessage",
|
||||
"DmTaskCreate",
|
||||
"DmTaskGetInfo",
|
||||
"DmTaskSetCookie",
|
||||
"DmTaskSetTarget",
|
||||
"DmTaskSetAddNode",
|
||||
"DmUdevWait",
|
||||
"Mounted",
|
||||
"sysUnmount",
|
||||
)
|
||||
|
|
|
@ -58,3 +58,14 @@ func ioctlBlkGetSize64(fd uintptr) (int64, error) {
|
|||
}
|
||||
return size, nil
|
||||
}
|
||||
|
||||
func ioctlBlkDiscard(fd uintptr, offset, length uint64) error {
|
||||
var r [2]uint64
|
||||
r[0] = offset
|
||||
r[1] = length
|
||||
|
||||
if _, _, err := sysSyscall(sysSysIoctl, fd, BlkDiscard, uintptr(unsafe.Pointer(&r[0]))); err != 0 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue