rbd: write max 1gb per WriteSame() operation

It seems that writing more than 1 GiB per WriteSame() operation causes
an EINVAL (22) "Invalid argument" error. Splitting the writes in blocks
of maximum 1 GiB should prevent that from happening.

Not all volumes are of a size that is the multiple of the stripe-size.
WriteSame() needs to write full blocks of data, so in case there is a
small left-over, it will be filled with WriteAt().

Signed-off-by: Niels de Vos <ndevos@redhat.com>
This commit is contained in:
Niels de Vos 2021-03-09 14:06:43 +01:00 committed by mergify[bot]
parent 06d5d8f23a
commit fe0f169875

View File

@ -333,15 +333,52 @@ func (rv *rbdVolume) allocate(offset uint64) error {
return err return err
} }
// zeroBlock is the stripe-period: size of the object-size multiplied // blockSize is the stripe-period: size of the object-size multiplied
// by the stripe-count // by the stripe-count
zeroBlock := make([]byte, sc*(1<<st.Order)) blockSize := sc * (1 << st.Order)
zeroBlock := make([]byte, blockSize)
// the actual size of the image as available in the pool, can be // the actual size of the image as available in the pool, can be
// marginally different from the requested image size // marginally different from the requested image size
_, err = image.WriteSame(offset, st.Size-offset, zeroBlock, rados.OpFlagNone) size := st.Size - offset
return err // In case the remaining space on the volume is smaller than blockSize,
// write a partial block with WriteAt() after this loop.
for size > blockSize {
writeSize := size
// write a maximum of 1GB per WriteSame() call
if size > helpers.GiB {
writeSize = helpers.GiB
}
// round down to the size of a zeroBlock
if (writeSize % blockSize) != 0 {
writeSize = (writeSize / blockSize) * blockSize
}
_, err = image.WriteSame(offset, writeSize, zeroBlock,
rados.OpFlagNone)
if err != nil {
return fmt.Errorf("failed to allocate %d/%d bytes at "+
"offset %d: %w", writeSize, blockSize, offset, err)
}
// write succeeded
size -= writeSize
offset += writeSize
}
// write the last remaining bytes, in case the image size can not be
// written with the optimal blockSize
if size != 0 {
_, err = image.WriteAt(zeroBlock[:size], int64(offset))
if err != nil {
return fmt.Errorf("failed to allocate %d bytes at "+
"offset %d: %w", size, offset, err)
}
}
return nil
} }
// isInUse checks if there is a watcher on the image. It returns true if there // isInUse checks if there is a watcher on the image. It returns true if there