summaryrefslogtreecommitdiffstats
path: root/src/partition/repart.c
diff options
context:
space:
mode:
authorYu Watanabe <watanabe.yu+github@gmail.com>2022-09-04 08:20:29 +0200
committerYu Watanabe <watanabe.yu+github@gmail.com>2022-09-08 22:35:40 +0200
commit0245e15afedb6a155b5da84821de39e71e0710b4 (patch)
treeca4cb18959602c6d1b51bece244e71917026cc78 /src/partition/repart.c
parentrepart: split out context_grow_partition_one() (diff)
downloadsystemd-0245e15afedb6a155b5da84821de39e71e0710b4.tar.xz
systemd-0245e15afedb6a155b5da84821de39e71e0710b4.zip
repart: make scale_by_weight() always succeed
Diffstat (limited to '')
-rw-r--r--src/partition/repart.c58
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)