repart: make scale_by_weight() always succeed
This commit is contained in:
+26
-32
@@ -696,20 +696,24 @@ overflow_sum:
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "Combined weight of partition exceeds unsigned 64bit range, refusing.");
|
||||
}
|
||||
|
||||
static int scale_by_weight(uint64_t value, uint64_t weight, uint64_t weight_sum, uint64_t *ret) {
|
||||
static uint64_t scale_by_weight(uint64_t value, uint64_t weight, uint64_t weight_sum) {
|
||||
assert(weight_sum >= weight);
|
||||
assert(ret);
|
||||
|
||||
if (weight == 0) {
|
||||
*ret = 0;
|
||||
return 0;
|
||||
for (;;) {
|
||||
if (weight == 0)
|
||||
return 0;
|
||||
if (weight == weight_sum)
|
||||
return value;
|
||||
if (value <= UINT64_MAX / weight)
|
||||
return value * weight / weight_sum;
|
||||
|
||||
/* Rescale weight and weight_sum to make not the calculation overflow. To satisfy the
|
||||
* following conditions, 'weight_sum' is rounded up but 'weight' is rounded down:
|
||||
* - the sum of scale_by_weight() for all weights must not be larger than the input value,
|
||||
* - scale_by_weight() must not be larger than the ideal value (i.e. calculated with uint128_t). */
|
||||
weight_sum = DIV_ROUND_UP(weight_sum, 2);
|
||||
weight /= 2;
|
||||
}
|
||||
|
||||
if (value > UINT64_MAX / weight)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "Scaling by weight of partition exceeds unsigned 64bit range, refusing.");
|
||||
|
||||
*ret = value * weight / weight_sum;
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef enum GrowPartitionPhase {
|
||||
@@ -725,17 +729,17 @@ typedef enum GrowPartitionPhase {
|
||||
_GROW_PARTITION_PHASE_MAX,
|
||||
} GrowPartitionPhase;
|
||||
|
||||
static int context_grow_partitions_phase(
|
||||
static bool context_grow_partitions_phase(
|
||||
Context *context,
|
||||
FreeArea *a,
|
||||
GrowPartitionPhase phase,
|
||||
uint64_t *span,
|
||||
uint64_t *weight_sum) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(context);
|
||||
assert(a);
|
||||
assert(span);
|
||||
assert(weight_sum);
|
||||
|
||||
/* Now let's look at the intended weights and adjust them taking the minimum space assignments into
|
||||
* account. i.e. if a partition has a small weight but a high minimum space value set it should not
|
||||
@@ -754,9 +758,7 @@ static int context_grow_partitions_phase(
|
||||
|
||||
/* Calculate how much this space this partition needs if everyone would get
|
||||
* the weight based share */
|
||||
r = scale_by_weight(*span, p->weight, *weight_sum, &share);
|
||||
if (r < 0)
|
||||
return r;
|
||||
share = scale_by_weight(*span, p->weight, *weight_sum);
|
||||
|
||||
rsz = partition_min_size(context, p);
|
||||
xsz = partition_max_size(context, p);
|
||||
@@ -798,16 +800,14 @@ static int context_grow_partitions_phase(
|
||||
}
|
||||
|
||||
if (try_again)
|
||||
return 0; /* try again */
|
||||
return false; /* try again */
|
||||
}
|
||||
|
||||
if (p->new_padding == UINT64_MAX) {
|
||||
bool charge = false, try_again = false;
|
||||
uint64_t share, rsz, xsz;
|
||||
|
||||
r = scale_by_weight(*span, p->padding_weight, *weight_sum, &share);
|
||||
if (r < 0)
|
||||
return r;
|
||||
share = scale_by_weight(*span, p->padding_weight, *weight_sum);
|
||||
|
||||
rsz = partition_min_padding(p);
|
||||
xsz = partition_max_padding(p);
|
||||
@@ -829,11 +829,11 @@ static int context_grow_partitions_phase(
|
||||
}
|
||||
|
||||
if (try_again)
|
||||
return 0; /* try again */
|
||||
return false; /* try again */
|
||||
}
|
||||
}
|
||||
|
||||
return 1; /* done */
|
||||
return true; /* done */
|
||||
}
|
||||
|
||||
static void context_grow_partition_one(Context *context, FreeArea *a, Partition *p, uint64_t *span) {
|
||||
@@ -888,15 +888,9 @@ static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) {
|
||||
span += round_up_size(a->after->offset + a->after->current_size, context->grain_size) - a->after->offset;
|
||||
}
|
||||
|
||||
for (GrowPartitionPhase phase = 0; phase < _GROW_PARTITION_PHASE_MAX;) {
|
||||
r = context_grow_partitions_phase(context, a, phase, &span, &weight_sum);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) /* not done yet, re-run this phase */
|
||||
continue;
|
||||
|
||||
phase++; /* got to next phase */
|
||||
}
|
||||
for (GrowPartitionPhase phase = 0; phase < _GROW_PARTITION_PHASE_MAX;)
|
||||
if (context_grow_partitions_phase(context, a, phase, &span, &weight_sum))
|
||||
phase++; /* go to the next phase */
|
||||
|
||||
/* We still have space left over? Donate to preceding partition if we have one */
|
||||
if (span > 0 && a->after)
|
||||
|
||||
Reference in New Issue
Block a user