Message ID | 20180515154033.19899-21-kwolf@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 15 May 2018 at 16:40, Kevin Wolf <kwolf@redhat.com> wrote: > From: Alberto Garcia <berto@igalia.com> > > The L2 and refcount caches have default sizes that can be overridden > using the l2-cache-size and refcount-cache-size (an additional > parameter named cache-size sets the combined size of both caches). Hi; Coverity raises a nit about this patch (CID 1391229): > diff --git a/block/qcow2.c b/block/qcow2.c > index 2f36e632f9..6d532470a8 100644 > --- a/block/qcow2.c > +++ b/block/qcow2.c > @@ -802,23 +802,30 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, > } else if (refcount_cache_size_set) { > *l2_cache_size = combined_cache_size - *refcount_cache_size; > } else { > - *refcount_cache_size = combined_cache_size > - / (DEFAULT_L2_REFCOUNT_SIZE_RATIO + 1); > - *l2_cache_size = combined_cache_size - *refcount_cache_size; > + uint64_t virtual_disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; > + uint64_t max_l2_cache = virtual_disk_size / (s->cluster_size / 8); > + uint64_t min_refcount_cache = > + (uint64_t) MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; Here we have a (uint64_t) cast that ensures we're doing a 64x64 multiply rather than a 32x32 one... > + > + /* Assign as much memory as possible to the L2 cache, and > + * use the remainder for the refcount cache */ > + if (combined_cache_size >= max_l2_cache + min_refcount_cache) { > + *l2_cache_size = max_l2_cache; > + *refcount_cache_size = combined_cache_size - *l2_cache_size; > + } else { > + *refcount_cache_size = > + MIN(combined_cache_size, min_refcount_cache); > + *l2_cache_size = combined_cache_size - *refcount_cache_size; > + } > } > } else { > - if (!l2_cache_size_set && !refcount_cache_size_set) { > + if (!l2_cache_size_set) { > *l2_cache_size = MAX(DEFAULT_L2_CACHE_BYTE_SIZE, > (uint64_t)DEFAULT_L2_CACHE_CLUSTERS > * s->cluster_size); > - *refcount_cache_size = *l2_cache_size > - / DEFAULT_L2_REFCOUNT_SIZE_RATIO; > - } else if (!l2_cache_size_set) { > - *l2_cache_size = *refcount_cache_size > - * DEFAULT_L2_REFCOUNT_SIZE_RATIO; > - } else if (!refcount_cache_size_set) { > - *refcount_cache_size = *l2_cache_size > - / DEFAULT_L2_REFCOUNT_SIZE_RATIO; > + } > + if (!refcount_cache_size_set) { > + *refcount_cache_size = MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; ...but in the else clause down here, we don't have the cast, and Coverity complains that we evaluate a 32-bit result and then put it in a 64-bit variable. Should this have the (uint64_t) cast as well ? > } > } > thanks -- PMM
Am 25.05.2018 um 19:10 hat Peter Maydell geschrieben: > On 15 May 2018 at 16:40, Kevin Wolf <kwolf@redhat.com> wrote: > > From: Alberto Garcia <berto@igalia.com> > > > > The L2 and refcount caches have default sizes that can be overridden > > using the l2-cache-size and refcount-cache-size (an additional > > parameter named cache-size sets the combined size of both caches). > > Hi; Coverity raises a nit about this patch (CID 1391229): CCing Berto as the patch author. > > diff --git a/block/qcow2.c b/block/qcow2.c > > index 2f36e632f9..6d532470a8 100644 > > --- a/block/qcow2.c > > +++ b/block/qcow2.c > > @@ -802,23 +802,30 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, > > } else if (refcount_cache_size_set) { > > *l2_cache_size = combined_cache_size - *refcount_cache_size; > > } else { > > - *refcount_cache_size = combined_cache_size > > - / (DEFAULT_L2_REFCOUNT_SIZE_RATIO + 1); > > - *l2_cache_size = combined_cache_size - *refcount_cache_size; > > + uint64_t virtual_disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; > > + uint64_t max_l2_cache = virtual_disk_size / (s->cluster_size / 8); > > + uint64_t min_refcount_cache = > > + (uint64_t) MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; > > Here we have a (uint64_t) cast that ensures we're doing a 64x64 multiply > rather than a 32x32 one... > > > + > > + /* Assign as much memory as possible to the L2 cache, and > > + * use the remainder for the refcount cache */ > > + if (combined_cache_size >= max_l2_cache + min_refcount_cache) { > > + *l2_cache_size = max_l2_cache; > > + *refcount_cache_size = combined_cache_size - *l2_cache_size; > > + } else { > > + *refcount_cache_size = > > + MIN(combined_cache_size, min_refcount_cache); > > + *l2_cache_size = combined_cache_size - *refcount_cache_size; > > + } > > } > > } else { > > - if (!l2_cache_size_set && !refcount_cache_size_set) { > > + if (!l2_cache_size_set) { > > *l2_cache_size = MAX(DEFAULT_L2_CACHE_BYTE_SIZE, > > (uint64_t)DEFAULT_L2_CACHE_CLUSTERS > > * s->cluster_size); > > - *refcount_cache_size = *l2_cache_size > > - / DEFAULT_L2_REFCOUNT_SIZE_RATIO; > > - } else if (!l2_cache_size_set) { > > - *l2_cache_size = *refcount_cache_size > > - * DEFAULT_L2_REFCOUNT_SIZE_RATIO; > > - } else if (!refcount_cache_size_set) { > > - *refcount_cache_size = *l2_cache_size > > - / DEFAULT_L2_REFCOUNT_SIZE_RATIO; > > + } > > + if (!refcount_cache_size_set) { > > + *refcount_cache_size = MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; > > ...but in the else clause down here, we don't have the cast, and > Coverity complains that we evaluate a 32-bit result and then > put it in a 64-bit variable. Should this have the (uint64_t) > cast as well ? It's a false positive, MIN_REFCOUNT_CACHE_SIZE is 4 and s->cluster_size is at most 2 MB, so this will never overflow. I guess we can change the code anyway to silence it? Kevin
On Mon 28 May 2018 10:38:55 AM CEST, Kevin Wolf wrote: >> > + if (!refcount_cache_size_set) { >> > + *refcount_cache_size = MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; >> >> ...but in the else clause down here, we don't have the cast, and >> Coverity complains that we evaluate a 32-bit result and then put it >> in a 64-bit variable. Should this have the (uint64_t) cast as well ? I suppose that's not because we put a 32-bit result in a 64-bit variable, but because it could theoretically overflow (if s->cluster_size > INT32_MAX / 4). > It's a false positive, MIN_REFCOUNT_CACHE_SIZE is 4 and > s->cluster_size is at most 2 MB, so this will never overflow. > > I guess we can change the code anyway to silence it? Same opinion here, no problem with adding a cast to silence the warning. Berto
On 28 May 2018 at 09:58, Alberto Garcia <berto@igalia.com> wrote: > On Mon 28 May 2018 10:38:55 AM CEST, Kevin Wolf wrote: >>> > + if (!refcount_cache_size_set) { >>> > + *refcount_cache_size = MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; >>> >>> ...but in the else clause down here, we don't have the cast, and >>> Coverity complains that we evaluate a 32-bit result and then put it >>> in a 64-bit variable. Should this have the (uint64_t) cast as well ? > > I suppose that's not because we put a 32-bit result in a 64-bit > variable, but because it could theoretically overflow (if > s->cluster_size > INT32_MAX / 4). Well, coverity warns because it's one of those things that could be correct code, or could be the author tripping over one of C's less-than-obvious traps. 32x32->32 multiplies are just as susceptible to overflow, but the heuristic Coverity uses is "calculated a 32-bit multiply result and put it in a 64-bit variable", on the assumption that the type of the destination implies that whatever you're calculating could well be that big, and "truncate the result of my multiply even though it would fit in the destination" is a bit unexpected. Coverity's wrong quite a bit on this one, naturally, but it's usually easier to shut it up on the wrong guesses for the benefit of the times when it turns out to be right. thanks -- PMM
On Mon 28 May 2018 03:49:07 PM CEST, Peter Maydell wrote: > On 28 May 2018 at 09:58, Alberto Garcia <berto@igalia.com> wrote: >> On Mon 28 May 2018 10:38:55 AM CEST, Kevin Wolf wrote: >>>> > + if (!refcount_cache_size_set) { >>>> > + *refcount_cache_size = MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; >>>> >>>> ...but in the else clause down here, we don't have the cast, and >>>> Coverity complains that we evaluate a 32-bit result and then put it >>>> in a 64-bit variable. Should this have the (uint64_t) cast as well ? >> >> I suppose that's not because we put a 32-bit result in a 64-bit >> variable, but because it could theoretically overflow (if >> s->cluster_size > INT32_MAX / 4). > > Well, coverity warns because it's one of those things that could be > correct code, or could be the author tripping over one of C's > less-than-obvious traps. 32x32->32 multiplies are just as susceptible > to overflow, but the heuristic Coverity uses is "calculated a 32-bit > multiply result and put it in a 64-bit variable", on the assumption > that the type of the destination implies that whatever you're > calculating could well be that big, and "truncate the result of my > multiply even though it would fit in the destination" is a bit > unexpected. Coverity's wrong quite a bit on this one, naturally, but > it's usually easier to shut it up on the wrong guesses for the benefit > of the times when it turns out to be right. Yes, it's a fair warning. I'll send a patch. Berto
diff --git a/block/qcow2.h b/block/qcow2.h index adf5c3950f..01b5250415 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -77,10 +77,6 @@ #define DEFAULT_L2_CACHE_CLUSTERS 8 /* clusters */ #define DEFAULT_L2_CACHE_BYTE_SIZE 1048576 /* bytes */ -/* The refblock cache needs only a fourth of the L2 cache size to cover as many - * clusters */ -#define DEFAULT_L2_REFCOUNT_SIZE_RATIO 4 - #define DEFAULT_CLUSTER_SIZE 65536 diff --git a/block/qcow2.c b/block/qcow2.c index 2f36e632f9..6d532470a8 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -802,23 +802,30 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, } else if (refcount_cache_size_set) { *l2_cache_size = combined_cache_size - *refcount_cache_size; } else { - *refcount_cache_size = combined_cache_size - / (DEFAULT_L2_REFCOUNT_SIZE_RATIO + 1); - *l2_cache_size = combined_cache_size - *refcount_cache_size; + uint64_t virtual_disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; + uint64_t max_l2_cache = virtual_disk_size / (s->cluster_size / 8); + uint64_t min_refcount_cache = + (uint64_t) MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; + + /* Assign as much memory as possible to the L2 cache, and + * use the remainder for the refcount cache */ + if (combined_cache_size >= max_l2_cache + min_refcount_cache) { + *l2_cache_size = max_l2_cache; + *refcount_cache_size = combined_cache_size - *l2_cache_size; + } else { + *refcount_cache_size = + MIN(combined_cache_size, min_refcount_cache); + *l2_cache_size = combined_cache_size - *refcount_cache_size; + } } } else { - if (!l2_cache_size_set && !refcount_cache_size_set) { + if (!l2_cache_size_set) { *l2_cache_size = MAX(DEFAULT_L2_CACHE_BYTE_SIZE, (uint64_t)DEFAULT_L2_CACHE_CLUSTERS * s->cluster_size); - *refcount_cache_size = *l2_cache_size - / DEFAULT_L2_REFCOUNT_SIZE_RATIO; - } else if (!l2_cache_size_set) { - *l2_cache_size = *refcount_cache_size - * DEFAULT_L2_REFCOUNT_SIZE_RATIO; - } else if (!refcount_cache_size_set) { - *refcount_cache_size = *l2_cache_size - / DEFAULT_L2_REFCOUNT_SIZE_RATIO; + } + if (!refcount_cache_size_set) { + *refcount_cache_size = MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; } } diff --git a/tests/qemu-iotests/137.out b/tests/qemu-iotests/137.out index e28e1eadba..96724a6c33 100644 --- a/tests/qemu-iotests/137.out +++ b/tests/qemu-iotests/137.out @@ -22,7 +22,7 @@ refcount-cache-size may not exceed cache-size L2 cache size too big L2 cache entry size must be a power of two between 512 and the cluster size (65536) L2 cache entry size must be a power of two between 512 and the cluster size (65536) -L2 cache size too big +Refcount cache size too big Conflicting values for qcow2 options 'overlap-check' ('constant') and 'overlap-check.template' ('all') Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all