On Thu, Feb 3, 2022 at 1:44 PM Martin Fernandez <martin.fernandez@xxxxxxxxxxxxx> 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@xxxxxxxxxxxxx> > --- > 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 >