From fe0f1698754d0a841461e18a9f5ba951153a6b3c Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Tue, 9 Mar 2021 14:06:43 +0100 Subject: [PATCH] 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 --- internal/rbd/rbd_util.go | 45 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/internal/rbd/rbd_util.go b/internal/rbd/rbd_util.go index 293997720..7dcf76523 100644 --- a/internal/rbd/rbd_util.go +++ b/internal/rbd/rbd_util.go @@ -333,15 +333,52 @@ func (rv *rbdVolume) allocate(offset uint64) error { 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 - zeroBlock := make([]byte, sc*(1< 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