devmapper: Save and restore NextDeviceId in a file

The way thin-pool right now is designed, user space is supposed to keep
track of what device ids have already been used. If user space tries to
create a new thin/snap device and device id has already been used, thin
pool retuns -EEXIST.

Upon receiving -EEXIST, current docker implementation simply tries the
NextDeviceId++ and keeps on doing this till it finds a free device id.

This approach has two issues.

- It is little suboptimal.
- If device id already exists, current kenrel implementation spits out
  a messsage on console.

[17991.140135] device-mapper: thin: Creation of new snapshot 33 of device 3 failed.

Here kenrel is trying to tell user that device id 33 has already been used.
And this shows up for every device id docker tries till it reaches a point
where device ids are not used. So if there are thousands of container and
one is trying to create a new container after fresh docker start, expect
thousands of such warnings to flood console.

This patch saves the NextDeviceId in a file in
/var/lib/docker/devmapper/metadata/deviceset-metadata and reads it back
when docker starts. This way we don't retry lots of device ids which 
have already been used. 

There might be some device ids which are free but we will get back to them
once device numbers wrap around (24bit limit on device ids).

This patch should cut down on number of kernel warnings.

Notice that I am creating a deviceset metadata file which is a global file
for this pool. So down the line if we need to save more data we should be
able to do that.

Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
This commit is contained in:
Vivek Goyal 2014-11-05 09:25:02 -05:00
parent 8e9a18039b
commit 8c9e5e5e05

View file

@ -62,25 +62,25 @@ type MetaData struct {
}
type DeviceSet struct {
MetaData
sync.Mutex // Protects Devices map and serializes calls into libdevmapper
root string
devicePrefix string
TransactionId uint64
NewTransactionId uint64
NextDeviceId int
MetaData `json:"-"`
sync.Mutex `json:"-"` // Protects Devices map and serializes calls into libdevmapper
root string `json:"-"`
devicePrefix string `json:"-"`
TransactionId uint64 `json:"-"`
NewTransactionId uint64 `json:"-"`
NextDeviceId int `json:"next_device_id"`
// Options
dataLoopbackSize int64
metaDataLoopbackSize int64
baseFsSize uint64
filesystem string
mountOptions string
mkfsArgs []string
dataDevice string
metadataDevice string
doBlkDiscard bool
thinpBlockSize uint32
dataLoopbackSize int64 `json:"-"`
metaDataLoopbackSize int64 `json:"-"`
baseFsSize uint64 `json:"-"`
filesystem string `json:"-"`
mountOptions string `json:"-"`
mkfsArgs []string `json:"-"`
dataDevice string `json:"-"`
metadataDevice string `json:"-"`
doBlkDiscard bool `json:"-"`
thinpBlockSize uint32 `json:"-"`
}
type DiskUsage struct {
@ -138,6 +138,10 @@ func (devices *DeviceSet) metadataFile(info *DevInfo) string {
return path.Join(devices.metadataDir(), file)
}
func (devices *DeviceSet) deviceSetMetaFile() string {
return path.Join(devices.metadataDir(), "deviceset-metadata")
}
func (devices *DeviceSet) oldMetadataFile() string {
return path.Join(devices.loopbackDir(), "json")
}
@ -545,6 +549,34 @@ func (devices *DeviceSet) ResizePool(size int64) error {
return nil
}
func (devices *DeviceSet) loadDeviceSetMetaData() error {
jsonData, err := ioutil.ReadFile(devices.deviceSetMetaFile())
if err != nil {
return nil
}
if err := json.Unmarshal(jsonData, devices); err != nil {
return nil
}
return nil
}
func (devices *DeviceSet) saveDeviceSetMetaData() error {
jsonData, err := json.Marshal(devices)
if err != nil {
return fmt.Errorf("Error encoding metadata to json: %s", err)
}
err = devices.writeMetaFile(jsonData, devices.deviceSetMetaFile())
if err != nil {
return err
}
return nil
}
func (devices *DeviceSet) initDevmapper(doInit bool) error {
logInit(devices)
@ -676,6 +708,10 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
}
}
// Right now this loads only NextDeviceId. If there is more metatadata
// down the line, we might have to move it earlier.
devices.loadDeviceSetMetaData()
// Setup the base image
if doInit {
if err := devices.setupBaseImage(); err != nil {
@ -955,6 +991,8 @@ func (devices *DeviceSet) Shutdown() error {
if err := devices.deactivatePool(); err != nil {
log.Debugf("Shutdown deactivate pool , error: %s", err)
}
devices.saveDeviceSetMetaData()
devices.Unlock()
return nil