From 08c42330ce5c3dbeb4176356b23a488fd323ca82 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Mon, 18 Nov 2013 11:33:07 +0100 Subject: [PATCH 1/3] devmapper: Update device-mapper-tool This makes the device mapper tool work again and adds new features to get pool status, device status and to list all devices. Upstream-commit: 80aecc70141f3e7b3138752bf3f0c33b9a273714 Component: engine --- .../engine/graphdriver/devmapper/deviceset.go | 121 +++++++++++++++--- .../docker-device-tool/device_tool.go | 91 ++++++++++--- .../engine/graphdriver/devmapper/driver.go | 2 +- 3 files changed, 176 insertions(+), 38 deletions(-) diff --git a/components/engine/graphdriver/devmapper/deviceset.go b/components/engine/graphdriver/devmapper/deviceset.go index 227bc8ad8c..4e34a92e62 100644 --- a/components/engine/graphdriver/devmapper/deviceset.go +++ b/components/engine/graphdriver/devmapper/deviceset.go @@ -57,6 +57,16 @@ type Status struct { MetadataLoopback string Data DiskUsage Metadata DiskUsage + SectorSize uint64 +} + +type DevStatus struct { + DeviceId int + Size uint64 + TransactionId uint64 + SizeInSectors uint64 + MappedSectors uint64 + HighestMappedSector uint64 } func getDevName(name string) string { @@ -357,13 +367,24 @@ func minor(device uint64) uint64 { return (device & 0xff) | ((device >> 12) & 0xfff00) } -func (devices *DeviceSet) initDevmapper() error { +func (devices *DeviceSet) initDevmapper(doInit bool) error { logInit(devices) // Make sure the sparse images exist in /devicemapper/data and // /devicemapper/metadata - createdLoopback := !devices.hasImage("data") || !devices.hasImage("metadata") + hasData := devices.hasImage("data") + hasMetadata := devices.hasImage("metadata") + + if !doInit && !hasData { + return fmt.Errorf("Looback data file not found %s") + } + + if !doInit && !hasMetadata { + return fmt.Errorf("Looback metadata file not found %s") + } + + createdLoopback := !hasData || !hasMetadata data, err := devices.ensureImage("data", DefaultDataLoopbackSize) if err != nil { utils.Debugf("Error device ensureImage (data): %s\n", err) @@ -438,9 +459,11 @@ func (devices *DeviceSet) initDevmapper() error { } // Setup the base image - if err := devices.setupBaseImage(); err != nil { - utils.Debugf("Error device setupBaseImage: %s\n", err) - return err + if doInit { + if err := devices.setupBaseImage(); err != nil { + utils.Debugf("Error device setupBaseImage: %s\n", err) + return err + } } return nil @@ -757,6 +780,69 @@ func (devices *DeviceSet) setInitialized(hash string) error { return nil } +func (devices *DeviceSet) List() []string { + devices.Lock() + defer devices.Unlock() + + ids := make([]string, len(devices.Devices)) + i := 0 + for k := range devices.Devices { + ids[i] = k + i++ + } + return ids +} + +func (devices *DeviceSet) deviceStatus(devName string) (sizeInSectors, mappedSectors, highestMappedSector uint64, err error) { + var params string + _, sizeInSectors, _, params, err = getStatus(devName) + if err != nil { + return + } + if _, err = fmt.Sscanf(params, "%d %d", &mappedSectors, &highestMappedSector); err == nil { + return + } + return +} + +func (devices *DeviceSet) GetDeviceStatus(hash string) (*DevStatus, error) { + devices.Lock() + defer devices.Unlock() + + info := devices.Devices[hash] + if info == nil { + return nil, fmt.Errorf("No device %s", hash) + } + + status := &DevStatus{ + DeviceId: info.DeviceId, + Size: info.Size, + TransactionId: info.TransactionId, + } + + if err := devices.activateDeviceIfNeeded(hash); err != nil { + return nil, fmt.Errorf("Error activating devmapper device for '%s': %s", hash, err) + } + + if sizeInSectors, mappedSectors, highestMappedSector, err := devices.deviceStatus(info.DevName()); err != nil { + return nil, err + } else { + status.SizeInSectors = sizeInSectors + status.MappedSectors = mappedSectors + status.HighestMappedSector = highestMappedSector + } + + return status, nil +} + +func (devices *DeviceSet) poolStatus() (totalSizeInSectors, transactionId, dataUsed, dataTotal, metadataUsed, metadataTotal uint64, err error) { + var params string + if _, totalSizeInSectors, _, params, err = getStatus(devices.getPoolName()); err == nil { + _, err = fmt.Sscanf(params, "%d %d/%d %d/%d", &transactionId, &metadataUsed, &metadataTotal, &dataUsed, &dataTotal) + } + return +} + func (devices *DeviceSet) Status() *Status { devices.Lock() defer devices.Unlock() @@ -767,26 +853,25 @@ func (devices *DeviceSet) Status() *Status { status.DataLoopback = path.Join(devices.loopbackDir(), "data") status.MetadataLoopback = path.Join(devices.loopbackDir(), "metadata") - _, totalSizeInSectors, _, params, err := getStatus(devices.getPoolName()) + totalSizeInSectors, _, dataUsed, dataTotal, metadataUsed, metadataTotal, err := devices.poolStatus() if err == nil { - var transactionId, dataUsed, dataTotal, metadataUsed, metadataTotal uint64 - if _, err := fmt.Sscanf(params, "%d %d/%d %d/%d", &transactionId, &metadataUsed, &metadataTotal, &dataUsed, &dataTotal); err == nil { - // Convert from blocks to bytes - blockSizeInSectors := totalSizeInSectors / dataTotal + // Convert from blocks to bytes + blockSizeInSectors := totalSizeInSectors / dataTotal - status.Data.Used = dataUsed * blockSizeInSectors * 512 - status.Data.Total = dataTotal * blockSizeInSectors * 512 + status.Data.Used = dataUsed * blockSizeInSectors * 512 + status.Data.Total = dataTotal * blockSizeInSectors * 512 - // metadata blocks are always 4k - status.Metadata.Used = metadataUsed * 4096 - status.Metadata.Total = metadataTotal * 4096 - } + // metadata blocks are always 4k + status.Metadata.Used = metadataUsed * 4096 + status.Metadata.Total = metadataTotal * 4096 + + status.SectorSize = blockSizeInSectors * 512 } return status } -func NewDeviceSet(root string) (*DeviceSet, error) { +func NewDeviceSet(root string, doInit bool) (*DeviceSet, error) { SetDevDir("/dev") devices := &DeviceSet{ @@ -795,7 +880,7 @@ func NewDeviceSet(root string) (*DeviceSet, error) { activeMounts: make(map[string]int), } - if err := devices.initDevmapper(); err != nil { + if err := devices.initDevmapper(doInit); err != nil { return nil, err } diff --git a/components/engine/graphdriver/devmapper/docker-device-tool/device_tool.go b/components/engine/graphdriver/devmapper/docker-device-tool/device_tool.go index f66762da5e..bd7831c0c5 100644 --- a/components/engine/graphdriver/devmapper/docker-device-tool/device_tool.go +++ b/components/engine/graphdriver/devmapper/docker-device-tool/device_tool.go @@ -1,59 +1,112 @@ package main import ( + "flag" "fmt" - "github.com/dotcloud/docker/devmapper" + "github.com/dotcloud/docker/graphdriver/devmapper" "os" + "path" + "sort" ) func usage() { - fmt.Printf("Usage: %s [snap new-id base-id] | [remove id] | [mount id mountpoint]\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "Usage: %s [status] | [list] | [device id] | [snap new-id base-id] | [remove id] | [mount id mountpoint]\n", os.Args[0]) + flag.PrintDefaults() os.Exit(1) } func main() { - devices := devmapper.NewDeviceSet("/var/lib/docker") + root := flag.String("r", "/var/lib/docker", "Docker root dir") + flDebug := flag.Bool("D", false, "Debug mode") - if len(os.Args) < 2 { + flag.Parse() + + if *flDebug { + os.Setenv("DEBUG", "1") + } + + if flag.NArg() < 1 { usage() } - cmd := os.Args[1] - if cmd == "snap" { - if len(os.Args) < 4 { + args := flag.Args() + + home := path.Join(*root, "devicemapper") + devices, err := devmapper.NewDeviceSet(home, false) + if err != nil { + fmt.Println("Can't initialize device mapper: ", err) + os.Exit(1) + } + + switch args[0] { + case "status": + status := devices.Status() + fmt.Printf("Pool name: %s\n", status.PoolName) + fmt.Printf("Data Loopback file: %s\n", status.DataLoopback) + fmt.Printf("Metadata Loopback file: %s\n", status.MetadataLoopback) + fmt.Printf("Sector size: %d\n", status.SectorSize) + fmt.Printf("Data use: %d of %d (%.1f %%)\n", status.Data.Used, status.Data.Total, 100.0*float64(status.Data.Used)/float64(status.Data.Total)) + fmt.Printf("Metadata use: %d of %d (%.1f %%)\n", status.Metadata.Used, status.Metadata.Total, 100.0*float64(status.Metadata.Used)/float64(status.Metadata.Total)) + break + case "list": + ids := devices.List() + sort.Strings(ids) + for _, id := range ids { + fmt.Println(id) + } + break + case "device": + if flag.NArg() < 2 { + usage() + } + status, err := devices.GetDeviceStatus(args[1]) + if err != nil { + fmt.Println("Can't get device info: ", err) + os.Exit(1) + } + fmt.Printf("Id: %d\n", status.DeviceId) + fmt.Printf("Size: %d\n", status.Size) + fmt.Printf("Transaction Id: %d\n", status.TransactionId) + fmt.Printf("Size in Sectors: %d\n", status.SizeInSectors) + fmt.Printf("Mapped Sectors: %d\n", status.MappedSectors) + fmt.Printf("Highest Mapped Sector: %d\n", status.HighestMappedSector) + break + case "snap": + if flag.NArg() < 3 { usage() } - err := devices.AddDevice(os.Args[2], os.Args[3]) + err := devices.AddDevice(args[1], args[2]) if err != nil { fmt.Println("Can't create snap device: ", err) os.Exit(1) } - } else if cmd == "remove" { - if len(os.Args) < 3 { + break + case "remove": + if flag.NArg() < 2 { usage() } - err := devices.RemoveDevice(os.Args[2]) + err := devices.RemoveDevice(args[1]) if err != nil { fmt.Println("Can't remove device: ", err) os.Exit(1) } - } else if cmd == "mount" { - if len(os.Args) < 4 { + break + case "mount": + if flag.NArg() < 3 { usage() } - err := devices.MountDevice(os.Args[2], os.Args[3]) + err := devices.MountDevice(args[1], args[2], false) if err != nil { fmt.Println("Can't create snap device: ", err) os.Exit(1) } - } else { - fmt.Printf("Unknown command %s\n", cmd) - if len(os.Args) < 4 { - usage() - } + break + default: + fmt.Printf("Unknown command %s\n", args[0]) + usage() os.Exit(1) } diff --git a/components/engine/graphdriver/devmapper/driver.go b/components/engine/graphdriver/devmapper/driver.go index 315b7b091e..8e94b7bbc0 100644 --- a/components/engine/graphdriver/devmapper/driver.go +++ b/components/engine/graphdriver/devmapper/driver.go @@ -22,7 +22,7 @@ type Driver struct { } func Init(home string) (graphdriver.Driver, error) { - deviceSet, err := NewDeviceSet(home) + deviceSet, err := NewDeviceSet(home, true) if err != nil { return nil, err } From 90396dc0fc8a88fbb248ea169b22a8089f109ea1 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Mon, 18 Nov 2013 12:12:04 +0100 Subject: [PATCH 2/3] devicemapper tool: Add support for pool resizing Upstream-commit: a0224e61b48e1dd1378cb955596fba1937e99068 Component: engine --- .../engine/graphdriver/devmapper/deviceset.go | 66 ++++++++++ .../engine/graphdriver/devmapper/devmapper.go | 113 +++++++++++++++--- .../devmapper/devmapper_wrapper.go | 12 ++ .../docker-device-tool/device_tool.go | 57 ++++++++- 4 files changed, 228 insertions(+), 20 deletions(-) diff --git a/components/engine/graphdriver/devmapper/deviceset.go b/components/engine/graphdriver/devmapper/deviceset.go index 4e34a92e62..e24840634d 100644 --- a/components/engine/graphdriver/devmapper/deviceset.go +++ b/components/engine/graphdriver/devmapper/deviceset.go @@ -367,6 +367,72 @@ func minor(device uint64) uint64 { return (device & 0xff) | ((device >> 12) & 0xfff00) } +func (devices *DeviceSet) ResizePool(size int64) error { + dirname := devices.loopbackDir() + datafilename := path.Join(dirname, "data") + metadatafilename := path.Join(dirname, "metadata") + + datafile, err := os.OpenFile(datafilename, os.O_RDWR, 0) + if datafile == nil { + return err + } + defer datafile.Close() + + fi, err := datafile.Stat() + if fi == nil { + return err + } + + if fi.Size() > size { + return fmt.Errorf("Can't shrink file") + } + + dataloopback := FindLoopDeviceFor(datafile) + if dataloopback == nil { + return fmt.Errorf("Unable to find loopback mount for: %s", datafilename) + } + defer dataloopback.Close() + + metadatafile, err := os.OpenFile(metadatafilename, os.O_RDWR, 0) + if metadatafile == nil { + return err + } + defer metadatafile.Close() + + metadataloopback := FindLoopDeviceFor(metadatafile) + if metadataloopback == nil { + return fmt.Errorf("Unable to find loopback mount for: %s", metadatafilename) + } + defer metadataloopback.Close() + + // Grow loopback file + if err := datafile.Truncate(size); err != nil { + return fmt.Errorf("Unable to grow loopback file: %s", err) + } + + // Reload size for loopback device + if err := LoopbackSetCapacity(dataloopback); err != nil { + return fmt.Errorf("Unable to update loopback capacity: %s", err) + } + + // Suspend the pool + if err := suspendDevice(devices.getPoolName()); err != nil { + return fmt.Errorf("Unable to suspend pool: %s", err) + } + + // Reload with the new block sizes + if err := reloadPool(devices.getPoolName(), dataloopback, metadataloopback); err != nil { + return fmt.Errorf("Unable to reload pool: %s", err) + } + + // Resume the pool + if err := resumeDevice(devices.getPoolName()); err != nil { + return fmt.Errorf("Unable to resume pool: %s", err) + } + + return nil +} + func (devices *DeviceSet) initDevmapper(doInit bool) error { logInit(devices) diff --git a/components/engine/graphdriver/devmapper/devmapper.go b/components/engine/graphdriver/devmapper/devmapper.go index 54e21adddb..6901ba9a6e 100644 --- a/components/engine/graphdriver/devmapper/devmapper.go +++ b/components/engine/graphdriver/devmapper/devmapper.go @@ -6,6 +6,7 @@ import ( "github.com/dotcloud/docker/utils" "os" "runtime" + "syscall" ) type DevmapperLogger interface { @@ -40,25 +41,27 @@ const ( ) var ( - ErrTaskRun = errors.New("dm_task_run failed") - ErrTaskSetName = errors.New("dm_task_set_name failed") - ErrTaskSetMessage = errors.New("dm_task_set_message failed") - ErrTaskSetAddNode = errors.New("dm_task_set_add_node failed") - ErrTaskSetRo = errors.New("dm_task_set_ro failed") - ErrTaskAddTarget = errors.New("dm_task_add_target failed") - ErrTaskSetSector = errors.New("dm_task_set_sector failed") - ErrTaskGetInfo = errors.New("dm_task_get_info failed") - ErrTaskGetDriverVersion = errors.New("dm_task_get_driver_version failed") - ErrTaskSetCookie = errors.New("dm_task_set_cookie failed") - ErrNilCookie = errors.New("cookie ptr can't be nil") - ErrAttachLoopbackDevice = errors.New("loopback mounting failed") - ErrGetBlockSize = errors.New("Can't get block size") - ErrUdevWait = errors.New("wait on udev cookie failed") - ErrSetDevDir = errors.New("dm_set_dev_dir failed") - ErrGetLibraryVersion = errors.New("dm_get_library_version failed") - ErrCreateRemoveTask = errors.New("Can't create task of type DeviceRemove") - ErrRunRemoveDevice = errors.New("running removeDevice failed") - ErrInvalidAddNode = errors.New("Invalide AddNoce type") + ErrTaskRun = errors.New("dm_task_run failed") + ErrTaskSetName = errors.New("dm_task_set_name failed") + ErrTaskSetMessage = errors.New("dm_task_set_message failed") + ErrTaskSetAddNode = errors.New("dm_task_set_add_node failed") + ErrTaskSetRo = errors.New("dm_task_set_ro failed") + ErrTaskAddTarget = errors.New("dm_task_add_target failed") + ErrTaskSetSector = errors.New("dm_task_set_sector failed") + ErrTaskGetInfo = errors.New("dm_task_get_info failed") + ErrTaskGetDriverVersion = errors.New("dm_task_get_driver_version failed") + ErrTaskSetCookie = errors.New("dm_task_set_cookie failed") + ErrNilCookie = errors.New("cookie ptr can't be nil") + ErrAttachLoopbackDevice = errors.New("loopback mounting failed") + ErrGetBlockSize = errors.New("Can't get block size") + ErrUdevWait = errors.New("wait on udev cookie failed") + ErrSetDevDir = errors.New("dm_set_dev_dir failed") + ErrGetLibraryVersion = errors.New("dm_get_library_version failed") + ErrCreateRemoveTask = errors.New("Can't create task of type DeviceRemove") + ErrRunRemoveDevice = errors.New("running removeDevice failed") + ErrInvalidAddNode = errors.New("Invalide AddNoce type") + ErrGetLoopbackBackingFile = errors.New("Unable to get loopback backing file") + ErrLoopbackSetCapacity = errors.New("Unable set loopback capacity") ) type ( @@ -186,6 +189,55 @@ func AttachLoopDevice(filename string) (*os.File, error) { return os.NewFile(uintptr(fd), res), nil } +func getLoopbackBackingFile(file *os.File) (uint64, uint64, error) { + dev, inode, err := dmGetLoopbackBackingFile(file.Fd()) + if err != 0 { + return 0, 0, ErrGetLoopbackBackingFile + } + return dev, inode, nil +} + +func LoopbackSetCapacity(file *os.File) error { + err := dmLoopbackSetCapacity(file.Fd()) + if err != 0 { + return ErrLoopbackSetCapacity + } + return nil +} + +func FindLoopDeviceFor(file *os.File) *os.File { + stat, err := file.Stat() + if err != nil { + return nil + } + targetInode := stat.Sys().(*syscall.Stat_t).Ino + targetDevice := stat.Sys().(*syscall.Stat_t).Dev + + for i := 0; true; i++ { + path := fmt.Sprintf("/dev/loop%d", i) + + file, err := os.OpenFile(path, os.O_RDWR, 0) + if err != nil { + if os.IsNotExist(err) { + return nil + } + + // Ignore all errors until the first not-exist + // we want to continue looking for the file + continue + } + + dev, inode, err := getLoopbackBackingFile(file) + if err == nil && dev == targetDevice && inode == targetInode { + return file + } + + file.Close() + } + + return nil +} + func UdevWait(cookie uint) error { if res := DmUdevWait(cookie); res != 1 { utils.Debugf("Failed to wait on udev cookie %d", cookie) @@ -276,6 +328,29 @@ func createPool(poolName string, dataFile *os.File, metadataFile *os.File) error return nil } +func reloadPool(poolName string, dataFile *os.File, metadataFile *os.File) error { + task, err := createTask(DeviceReload, poolName) + if task == nil { + return err + } + + size, err := GetBlockDeviceSize(dataFile) + if err != nil { + return fmt.Errorf("Can't get data size") + } + + params := metadataFile.Name() + " " + dataFile.Name() + " 128 32768" + if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil { + return fmt.Errorf("Can't add target") + } + + if err := task.Run(); err != nil { + return fmt.Errorf("Error running DeviceCreate") + } + + return nil +} + func createTask(t TaskType, name string) (*Task, error) { task := TaskCreate(t) if task == nil { diff --git a/components/engine/graphdriver/devmapper/devmapper_wrapper.go b/components/engine/graphdriver/devmapper/devmapper_wrapper.go index fcd125e6c5..f9a7d039cf 100644 --- a/components/engine/graphdriver/devmapper/devmapper_wrapper.go +++ b/components/engine/graphdriver/devmapper/devmapper_wrapper.go @@ -239,6 +239,18 @@ func dmTaskAddTargetFct(task *CDmTask, C.uint64_t(start), C.uint64_t(size), Cttype, Cparams)) } +func dmGetLoopbackBackingFile(fd uintptr) (uint64, uint64, syscall.Errno) { + var lo64 C.struct_loop_info64 + _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.LOOP_GET_STATUS64, + uintptr(unsafe.Pointer(&lo64))) + return uint64(lo64.lo_device), uint64(lo64.lo_inode), err +} + +func dmLoopbackSetCapacity(fd uintptr) syscall.Errno { + _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.LOOP_SET_CAPACITY, 0) + return err +} + func dmGetBlockSizeFct(fd uintptr) (int64, syscall.Errno) { var size int64 _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.BLKGETSIZE64, diff --git a/components/engine/graphdriver/devmapper/docker-device-tool/device_tool.go b/components/engine/graphdriver/devmapper/docker-device-tool/device_tool.go index bd7831c0c5..4d1ee0cea5 100644 --- a/components/engine/graphdriver/devmapper/docker-device-tool/device_tool.go +++ b/components/engine/graphdriver/devmapper/docker-device-tool/device_tool.go @@ -7,14 +7,51 @@ import ( "os" "path" "sort" + "strconv" + "strings" ) func usage() { - fmt.Fprintf(os.Stderr, "Usage: %s [status] | [list] | [device id] | [snap new-id base-id] | [remove id] | [mount id mountpoint]\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "Usage: %s [status] | [list] | [device id] | [resize new-pool-size] | [snap new-id base-id] | [remove id] | [mount id mountpoint]\n", os.Args[0]) flag.PrintDefaults() os.Exit(1) } +func byteSizeFromString(arg string) (int64, error) { + digits := "" + rest := "" + last := strings.LastIndexAny(arg, "0123456789") + if last >= 0 { + digits = arg[:last+1] + rest = arg[last+1:] + } + + val, err := strconv.ParseInt(digits, 10, 64) + if err != nil { + return val, err + } + + rest = strings.ToLower(strings.TrimSpace(rest)) + + var multiplier int64 = 1 + switch rest { + case "": + multiplier = 1 + case "k", "kb": + multiplier = 1024 + case "m", "mb": + multiplier = 1024 * 1024 + case "g", "gb": + multiplier = 1024 * 1024 * 1024 + case "t", "tb": + multiplier = 1024 * 1024 * 1024 * 1024 + default: + return 0, fmt.Errorf("Unknown size unit: %s", rest) + } + + return val * multiplier, nil +} + func main() { root := flag.String("r", "/var/lib/docker", "Docker root dir") flDebug := flag.Bool("D", false, "Debug mode") @@ -70,6 +107,24 @@ func main() { fmt.Printf("Size in Sectors: %d\n", status.SizeInSectors) fmt.Printf("Mapped Sectors: %d\n", status.MappedSectors) fmt.Printf("Highest Mapped Sector: %d\n", status.HighestMappedSector) + break + case "resize": + if flag.NArg() < 2 { + usage() + } + + size, err := byteSizeFromString(args[1]) + if err != nil { + fmt.Println("Invalid size: ", err) + os.Exit(1) + } + + err = devices.ResizePool(size) + if err != nil { + fmt.Println("Error resizeing pool: ", err) + os.Exit(1) + } + break case "snap": if flag.NArg() < 3 { From 339d427535ec7307fbcb5c9917ab571a92d1e582 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 19 Nov 2013 10:03:04 +0100 Subject: [PATCH 3/3] Move docker-device-tool to contrib Upstream-commit: 9415c2b1f08ecd168ab5a620a80a5bed34df8adb Component: engine --- .../devmapper => contrib}/docker-device-tool/device_tool.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename components/engine/{graphdriver/devmapper => contrib}/docker-device-tool/device_tool.go (100%) diff --git a/components/engine/graphdriver/devmapper/docker-device-tool/device_tool.go b/components/engine/contrib/docker-device-tool/device_tool.go similarity index 100% rename from components/engine/graphdriver/devmapper/docker-device-tool/device_tool.go rename to components/engine/contrib/docker-device-tool/device_tool.go