diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2022-09-04 08:20:29 +0200 |
---|---|---|
committer | Yu Watanabe <watanabe.yu+github@gmail.com> | 2022-09-08 22:35:40 +0200 |
commit | 0245e15afedb6a155b5da84821de39e71e0710b4 (patch) | |
tree | ca4cb18959602c6d1b51bece244e71917026cc78 /src/partition/repart.c | |
parent | repart: split out context_grow_partition_one() (diff) | |
download | systemd-0245e15afedb6a155b5da84821de39e71e0710b4.tar.xz systemd-0245e15afedb6a155b5da84821de39e71e0710b4.zip |
repart: make scale_by_weight() always succeed
Diffstat (limited to '')
-rw-r--r-- | src/partition/repart.c | 58 |
1 files changed, 26 insertions, 32 deletions
diff --git a/src/partition/repart.c b/src/partition/repart.c index 00f64ad438..02d7dc8e10 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -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) |