Re: [PATCH v6 3/6] x86/e820: Refactor range_update and range_remove

Daniel Gutson
2022-02-08T22:26:43Z

On Thu, Feb 3, 2022 at 1:44 PM Martin Fernandez
<martin.fernandez@eclypsium.com> wrote:
>
> __e820__range_update and e820__range_remove had a very similar
> implementation with a few lines different from each other, the lines
> that actually perform the modification over the e820_table. The
> similiraties were found in the checks for the different cases on how
> each entry intersects with the given range (if it does at all). These
> checks were very presice and error prone so it was not a good idea to
> have them in both places.
>
> I propose a refactor of those functions, given that I need to create a
> similar one for this patchset.
>
> Add a function to modify a E820 table in a given range. This
> modification is done backed up by two helper structs:
> e820_entry_updater and e820_*_data.
>
> The first one, e820_entry_updater, carries 3 callbacks which function
> as the actions to take on the table.
>
> The other one, e820_*_data carries information needed by the
> callbacks, for example in the case of range_update it will carry the
> type that we are targeting.
>
> Signed-off-by: Martin Fernandez <martin.fernandez@eclypsium.com>
> ---
> arch/x86/kernel/e820.c | 383 ++++++++++++++++++++++++++++++-----------
> 1 file changed, 283 insertions(+), 100 deletions(-)
>
> diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c
> index bc0657f0deed..89b78c6b345b 100644
> --- a/arch/x86/kernel/e820.c
> +++ b/arch/x86/kernel/e820.c
> @@ -459,144 +459,327 @@ static int __init append_e820_table(struct boot_e820_entry *entries, u32 nr_entr
> return __append_e820_table(entries, nr_entries);
> }
>
> +/**
> + * e820_entry_updater - Helper type for __e820__handle_range_update().
> + * @should_update: Return true if @entry needs to be updated, false
> + * otherwise.
> + * @update: Apply desired actions to an @entry that is inside the
> + * range and satisfies @should_update.
> + * @new: Create new entry in the table with information gathered from
> + * @original and @data.
> + *
> + * Each function corresponds to an action that
> + * __e820__handle_range_update() does. Callbacks need to cast @data back
> + * to the corresponding type.
> + */
> +struct e820_entry_updater {
> + bool (*should_update)(const struct e820_entry *entry, const void *data);
> + void (*update)(struct e820_entry *entry, const void *data);
> + void (*new)(struct e820_table *table, u64 new_start, u64 new_size,
> + const struct e820_entry *original, const void *data);
> +};
> +
> +/**
> + * e820_remove_data - Helper type for e820__range_remove().
> + * @old_type: old_type parameter of e820__range_remove().
> + * @check_type: check_type parameter of e820__range_remove().
> + *
> + * This is intended to be used as the @data argument for the
> + * e820_entry_updater callbacks.
> + */
> +struct e820_remover_data {
> + enum e820_type old_type;
> + bool check_type;
> +};
> +
> +/**
> + * e820_type_updater_data - Helper type for __e820__range_update().
> + * @old_type: old_type parameter of __e820__range_update().
> + * @new_type: new_type parameter of __e820__range_update().
> + *
> + * This is intended to be used as the @data argument for the
> + * e820_entry_updater callbacks.
> + */
> +struct e820_type_updater_data {
> + enum e820_type old_type;
> + enum e820_type new_type;
> +};
> +
> +/**
> + * __e820__handle_intersected_range_update() - Helper function for
> + * __e820__handle_range_update().
> + * @table: Target e820_table.
> + * @start: Start of the range.
> + * @size: Size of the range.
> + * @entry: Current entry that __e820__handle_range_update() was
> + * looking into.
> + * @updater: updater parameter of __e820__handle_range_update().
> + * @data: data parameter of __e820__handle_range_update().
> + *
> + * Helper for __e820__handle_range_update to handle the case where
> + * neither the entry completely covers the range nor the range
> + * completely covers the entry.
> + *
> + * Return: The updated size.
> + */
> static u64 __init
> -__e820__range_update(struct e820_table *table, u64 start, u64 size, enum e820_type old_type, enum e820_type new_type)
> +__e820__handle_intersected_range_update(struct e820_table *table,
> + u64 start,
> + u64 size,
> + struct e820_entry *entry,
> + const struct e820_entry_updater *updater,
> + const void *data)
> {
> u64 end;
> - unsigned int i;
> - u64 real_updated_size = 0;
> -
> - BUG_ON(old_type == new_type);
> + u64 entry_end = entry->addr + entry->size;
> + u64 inner_start;
> + u64 inner_end;
> + u64 updated_size = 0;
>
> if (size > (ULLONG_MAX - start))
> size = ULLONG_MAX - start;
>
> end = start + size;
> - printk(KERN_DEBUG "e820: update [mem %#010Lx-%#010Lx] ", start, end - 1);
> - e820_print_type(old_type);
> - pr_cont(" ==> ");
> - e820_print_type(new_type);
> - pr_cont("\n");
> -
> - for (i = 0; i < table->nr_entries; i++) {
> - struct e820_entry *entry = &table->entries[i];
> - u64 final_start, final_end;
> - u64 entry_end;
> + inner_start = max(start, entry->addr);
> + inner_end = min(end, entry_end);
> +
> + /* Range and entry do intersect and... */
> + if (inner_start < inner_end) {
> + /* Entry is on the left */
> + if (entry->addr < inner_start) {
> + /* Resize current entry */
> + entry->size = inner_start - entry->addr;
> + /* Entry is on the right */
> + } else {
> + /* Resize and move current section */
> + entry->addr = inner_end;
> + entry->size = entry_end - inner_end;
> + }
> + /* Create new entry with intersected region */
> + updater->new(table, inner_start, inner_end - inner_start, entry, data);
>
> - if (entry->type != old_type)
> - continue;
> + updated_size += inner_end - inner_start;
> + } /* Else: [start, end) doesn't cover entry */
>
> - entry_end = entry->addr + entry->size;
> + return updated_size;
> +}
>
> - /* Completely covered by new range? */
> - if (entry->addr >= start && entry_end <= end) {
> - entry->type = new_type;
> - real_updated_size += entry->size;
> - continue;
> - }
> +/** __e820__handle_range_update(): Helper function to update a address
> + * range in a e820_table
> + * @table: e820_table that we want to modify.
> + * @start: Start of the range.
> + * @size: Size of the range.
> + * @updater: Callbacks to modify the table.
> + * @data: Information to modify the table.
> + *
> + * Update the table @table in [@start, @start + @size) doing the
> + * actions given in @updater.
> + *
> + * Return: The updated size.
> + */
> +static u64 __init
> +__e820__handle_range_update(struct e820_table *table,
> + u64 start,
> + u64 size,
> + const struct e820_entry_updater *updater,
> + const void *data)
> +{
> + u64 updated_size = 0;
> + u64 end;
> + unsigned int i;
>
> - /* New range is completely covered? */
> - if (entry->addr < start && entry_end > end) {
> - __e820__range_add(table, start, size, new_type);
> - __e820__range_add(table, end, entry_end - end, entry->type);
> - entry->size = start - entry->addr;
> - real_updated_size += size;
> - continue;
> - }
> + if (size > (ULLONG_MAX - start))
> + size = ULLONG_MAX - start;
>
> - /* Partially covered: */
> - final_start = max(start, entry->addr);
> - final_end = min(end, entry_end);
> - if (final_start >= final_end)
> - continue;
> + end = start + size;
>
> - __e820__range_add(table, final_start, final_end - final_start, new_type);
> + for (i = 0; i < table->nr_entries; i++) {
> + struct e820_entry *entry = &table->entries[i];
> + u64 entry_end = entry->addr + entry->size;
> +
> + if (updater->should_update(data, entry)) {
> + /* Range completely covers entry */
> + if (entry->addr >= start && entry_end <= end) {
> + updater->update(entry, data);
> + updated_size += entry->size;
> + /* Entry completely covers range */
> + } else if (start > entry->addr && end < entry_end) {
> + /* Resize current entry */
> + entry->size = start - entry->addr;
> +
> + /* Create new entry with intersection region */
> + updater->new(table, start, size, entry, data);
> +
> + /*
> + * Create a new entry for the leftover
> + * of the current entry
> + */
> + __e820__range_add(table, end, entry_end - end,
> + entry->type);
> +
> + updated_size += size;
> + } else {
> + updated_size =
> + __e820__handle_intersected_range_update(table, start, size,
> + entry, updater, data);
> + }
> + }
> + }
>
> - real_updated_size += final_end - final_start;
> + return updated_size;
> +}
>
> - /*
> - * Left range could be head or tail, so need to update
> - * its size first:
> - */
> - entry->size -= final_end - final_start;
> - if (entry->addr < final_start)
> - continue;
> +static bool __init type_updater__should_update(const struct e820_entry *entry,
> + const void *data)
> +{
> + struct e820_type_updater_data *type_updater_data =
> + (struct e820_type_updater_data *)data;

Please preserve const correctness. You are removing the const qualifier.

>
> - entry->addr = final_end;
> - }
> - return real_updated_size;
> + return entry->type == type_updater_data->old_type;
> }
>
> -u64 __init e820__range_update(u64 start, u64 size, enum e820_type old_type, enum e820_type new_type)
> +static void __init type_updater__update(struct e820_entry *entry,
> + const void *data)
> {
> - return __e820__range_update(e820_table, start, size, old_type, new_type);
> + struct e820_type_updater_data *type_updater_data =
> + (struct e820_type_updater_data *)data;
> +
> + entry->type = type_updater_data->new_type;
> }
>
> -static u64 __init e820__range_update_kexec(u64 start, u64 size, enum e820_type old_type, enum e820_type new_type)
> +static void __init type_updater__new(struct e820_table *table, u64 new_start,
> + u64 new_size,
> + const struct e820_entry *original,
> + const void *data)
> {
> - return __e820__range_update(e820_table_kexec, start, size, old_type, new_type);
> + struct e820_type_updater_data *type_updater_data =
> + (struct e820_type_updater_data *)data;
> +
> + __e820__range_add(table, new_start, new_size,
> + type_updater_data->new_type);
> }
>
> -/* Remove a range of memory from the E820 table: */
> -u64 __init e820__range_remove(u64 start, u64 size, enum e820_type old_type, bool check_type)
> +static u64 __init __e820__range_update(struct e820_table *table, u64 start,
> + u64 size, enum e820_type old_type,
> + enum e820_type new_type)
> {
> - int i;
> - u64 end;
> - u64 real_removed_size = 0;
> + struct e820_entry_updater updater = {
> + .should_update = type_updater__should_update,
> + .update = type_updater__update,
> + .new = type_updater__new
> + };
>
> - if (size > (ULLONG_MAX - start))
> - size = ULLONG_MAX - start;
> + struct e820_type_updater_data data = {
> + .old_type = old_type,
> + .new_type = new_type
> + };
>
> - end = start + size;
> - printk(KERN_DEBUG "e820: remove [mem %#010Lx-%#010Lx] ", start, end - 1);
> - if (check_type)
> - e820_print_type(old_type);
> + BUG_ON(old_type == new_type);
> +
> + printk(KERN_DEBUG "e820: update [mem %#018Lx-%#018Lx] ", start,
> + start + size - 1);
> + e820_print_type(old_type);
> + pr_cont(" ==> ");
> + e820_print_type(new_type);
> pr_cont("\n");
>
> - for (i = 0; i < e820_table->nr_entries; i++) {
> - struct e820_entry *entry = &e820_table->entries[i];
> - u64 final_start, final_end;
> - u64 entry_end;
> + return __e820__handle_range_update(table, start, size, &updater, &data);
> +}
>
> - if (check_type && entry->type != old_type)
> - continue;
> +static bool __init remover__should_update(const struct e820_entry *entry,
> + const void *data)
> +{
> + struct e820_remover_data *remover_data =
> + (struct e820_remover_data *)data;
>
> - entry_end = entry->addr + entry->size;
> + return !remover_data->check_type ||
> + entry->type == remover_data->old_type;
> +}
>
> - /* Completely covered? */
> - if (entry->addr >= start && entry_end <= end) {
> - real_removed_size += entry->size;
> - memset(entry, 0, sizeof(*entry));
> - continue;
> - }
> +static void __init remover__update(struct e820_entry *entry, const void *data)
> +{
> + memset(entry, 0, sizeof(*entry));
> +}
>
> - /* Is the new range completely covered? */
> - if (entry->addr < start && entry_end > end) {
> - e820__range_add(end, entry_end - end, entry->type);
> - entry->size = start - entry->addr;
> - real_removed_size += size;
> - continue;
> - }
> +static void __init remover__new(struct e820_table *table, u64 new_start,
> + u64 new_size, const struct e820_entry *original,
> + const void *data)
> +{
> +}
>
> - /* Partially covered: */
> - final_start = max(start, entry->addr);
> - final_end = min(end, entry_end);
> - if (final_start >= final_end)
> - continue;
> +/**
> + * e820__range_remove() - Remove an address range from e820_table.
> + * @start: Start of the address range.
> + * @size: Size of the address range.
> + * @old_type: Type of the entries that we want to remove.
> + * @check_type: Bool to decide if ignore @old_type or not.
> + *
> + * Remove [@start, @start + @size) from e820_table. If @check_type is
> + * true remove only entries with type @old_type.
> + *
> + * Return: The size removed.
> + */
> +u64 __init e820__range_remove(u64 start, u64 size, enum e820_type old_type,
> + bool check_type)
> +{
> + struct e820_entry_updater updater = {
> + .should_update = remover__should_update,
> + .update = remover__update,
> + .new = remover__new
> + };
> +
> + struct e820_remover_data data = {
> + .check_type = check_type,
> + .old_type = old_type
> + };
> +
> + printk(KERN_DEBUG "e820: remove [mem %#018Lx-%#018Lx] ", start,
> + start + size - 1);
> + if (check_type)
> + e820_print_type(old_type);
> + pr_cont("\n");
>
> - real_removed_size += final_end - final_start;
> + return __e820__handle_range_update(e820_table, start, size, &updater,
> + &data);
> +}
>
> - /*
> - * Left range could be head or tail, so need to update
> - * the size first:
> - */
> - entry->size -= final_end - final_start;
> - if (entry->addr < final_start)
> - continue;
> +/**
> + * e820__range_update() - Update the type of a given address range in
> + * e820_table.
> + * @start: Start of the range.
> + * @size: Size of the range.
> + * @old_type: Type that we want to change.
> + * @new_type: New type to replace @old_type.
> + *
> + * Update type of addresses in [@start, @start + @size) from @old_type
> + * to @new_type in e820_table.
> + *
> + * Return: The size updated.
> + */
> +u64 __init e820__range_update(u64 start, u64 size, enum e820_type old_type,
> + enum e820_type new_type)
> +{
> + return __e820__range_update(e820_table, start, size, old_type, new_type);
> +}
>
> - entry->addr = final_end;
> - }
> - return real_removed_size;
> +/**
> + * e820__range_update_kexec() - Update the type of a given address
> + * range in e820_table_kexec.
> + * @start: Start of the range.
> + * @size: Size of the range.
> + * @old_type: Type that we want to change.
> + * @new_type: New type to replace @old_type.
> + *
> + * Update type of addresses in [@start, @start + @size) from @old_type
> + * to @new_type in e820_table_kexec.
> + *
> + * Return: The size updated.
> + */
> +static u64 __init e820__range_update_kexec(u64 start, u64 size,
> + enum e820_type old_type,
> + enum e820_type new_type)
> +{
> + return __e820__range_update(e820_table_kexec, start, size, old_type, new_type);
> }
>
> void __init e820__update_table_print(void)
> --
> 2.30.2
>