Re: [PATCH v2 12/13] xen/device_tree: Introduce function to merge overlapping intervals

2024-04-18 Thread Luca Fancellu


> On 18 Apr 2024, at 07:28, Jan Beulich  wrote:
> 
> On 09.04.2024 13:45, Luca Fancellu wrote:
>> --- a/xen/arch/x86/extable.c
>> +++ b/xen/arch/x86/extable.c
>> @@ -23,7 +23,8 @@ static inline unsigned long ex_cont(const struct 
>> exception_table_entry *x)
>> return EX_FIELD(x, cont);
>> }
>> 
>> -static int init_or_livepatch cf_check cmp_ex(const void *a, const void *b)
>> +static int init_or_livepatch cf_check cmp_ex(const void *a, const void *b,
>> + const void *data)
>> {
>> const struct exception_table_entry *l = a, *r = b;
>> unsigned long lip = ex_addr(l);
>> @@ -53,7 +54,7 @@ void init_or_livepatch sort_exception_table(struct 
>> exception_table_entry *start,
>>  const struct exception_table_entry *stop)
>> {
>> sort(start, stop - start,
>> - sizeof(struct exception_table_entry), cmp_ex, swap_ex);
>> + sizeof(struct exception_table_entry), cmp_ex, swap_ex, NULL);
>> }
> 
> Not the least because of this addition of an entirely useless parameter / 
> argument

Well it’s not useless in this patch, given that without it I couldn’t know the 
size of the address
element, however ...

> I'm not in favor of ...
> 
>> --- a/xen/include/xen/sort.h
>> +++ b/xen/include/xen/sort.h
>> @@ -23,8 +23,8 @@
>> extern gnu_inline
>> #endif
>> void sort(void *base, size_t num, size_t size,
>> -  int (*cmp)(const void *a, const void *b),
>> -  void (*swap)(void *a, void *b, size_t size))
>> +  int (*cmp)(const void *a, const void *b, const void *data),
>> +  void (*swap)(void *a, void *b, size_t size), const void *cmp_data)
>> {
> 
> ... this change. Consider you were doing this on a C library you cannot 
> change.
> You'd have to find a different solution anyway.

I get your point here, we should not change standard functions.

> And the way we have sort()
> right now is matching the C spec. The change to do renders things unexpected 
> to
> anyone wanting to use this function in a spec-compliant way. One approach may
> be to make an adjustment to data representation, such that the extra reference
> data is accessible through the pointers already being passed.
> 
> Jan
> 

Anyway in the end this patch was dropped for other reasons.

Cheers,
Luca

Re: [PATCH v2 12/13] xen/device_tree: Introduce function to merge overlapping intervals

2024-04-18 Thread Jan Beulich
On 09.04.2024 13:45, Luca Fancellu wrote:
> --- a/xen/arch/x86/extable.c
> +++ b/xen/arch/x86/extable.c
> @@ -23,7 +23,8 @@ static inline unsigned long ex_cont(const struct 
> exception_table_entry *x)
>   return EX_FIELD(x, cont);
>  }
>  
> -static int init_or_livepatch cf_check cmp_ex(const void *a, const void *b)
> +static int init_or_livepatch cf_check cmp_ex(const void *a, const void *b,
> + const void *data)
>  {
>   const struct exception_table_entry *l = a, *r = b;
>   unsigned long lip = ex_addr(l);
> @@ -53,7 +54,7 @@ void init_or_livepatch sort_exception_table(struct 
> exception_table_entry *start,
>   const struct exception_table_entry *stop)
>  {
>  sort(start, stop - start,
> - sizeof(struct exception_table_entry), cmp_ex, swap_ex);
> + sizeof(struct exception_table_entry), cmp_ex, swap_ex, NULL);
>  }

Not the least because of this addition of an entirely useless parameter / 
argument
I'm not in favor of ...

> --- a/xen/include/xen/sort.h
> +++ b/xen/include/xen/sort.h
> @@ -23,8 +23,8 @@
>  extern gnu_inline
>  #endif
>  void sort(void *base, size_t num, size_t size,
> -  int (*cmp)(const void *a, const void *b),
> -  void (*swap)(void *a, void *b, size_t size))
> +  int (*cmp)(const void *a, const void *b, const void *data),
> +  void (*swap)(void *a, void *b, size_t size), const void *cmp_data)
>  {

... this change. Consider you were doing this on a C library you cannot change.
You'd have to find a different solution anyway. And the way we have sort()
right now is matching the C spec. The change to do renders things unexpected to
anyone wanting to use this function in a spec-compliant way. One approach may
be to make an adjustment to data representation, such that the extra reference
data is accessible through the pointers already being passed.

Jan



Re: [PATCH v2 12/13] xen/device_tree: Introduce function to merge overlapping intervals

2024-04-11 Thread Luca Fancellu
> 
> I’ve just spotted an issue with the algorithm, the fix is this one:
> 
> diff --git a/xen/common/device_tree.c b/xen/common/device_tree.c
> index 24914a80d03b..262385a041a8 100644
> --- a/xen/common/device_tree.c
> +++ b/xen/common/device_tree.c
> @@ -2360,6 +2360,10 @@ int __init 
> dt_merge_overlapping_addr_size_intervals(__be32 *reg, int *nr_cells,
> __be32 *tmp_last_cell_size = last_cell + addrcells;
> 
> dt_set_cell(_last_cell_size, sizecells, new_size);
> +
> +/* Last interval updated, so the end is changed */
> +end_last = start_last + size_last;
> +
> /*
>  * This current interval is merged with the last one, so remove 
> this
>  * interval and shift left all the remaining elements
> 

Apologies, this is the fix:

diff --git a/xen/common/device_tree.c b/xen/common/device_tree.c
index 24914a80d03b..9a2f5b27aa9b 100644
--- a/xen/common/device_tree.c
+++ b/xen/common/device_tree.c
@@ -2360,6 +2360,10 @@ int __init 
dt_merge_overlapping_addr_size_intervals(__be32 *reg, int *nr_cells,
 __be32 *tmp_last_cell_size = last_cell + addrcells;
 
 dt_set_cell(_last_cell_size, sizecells, new_size);
+
+/* Last interval updated, so the end is changed */
+end_last = start_last + new_size;
+
 /*
  * This current interval is merged with the last one, so remove 
this
  * interval and shift left all the remaining elements

So instead of “size_last” -> “new_size”.

Sorry for the noise.

Cheers,
Luca



Re: [PATCH v2 12/13] xen/device_tree: Introduce function to merge overlapping intervals

2024-04-11 Thread Luca Fancellu


> On 9 Apr 2024, at 12:45, Luca Fancellu  wrote:
> 
> Introduce a function that given an array of cells containing
> (address,size) intervals, merges the overlapping ones, returning
> an array with no overlapping intervals.
> 
> The algorithm needs to sort the intervals by ascending order
> address, so the sort() function already included in the codebase
> is used, however in this case additional data is needed for the
> compare function, to be able to extract the address from the
> interval.
> So add one argument to the sort() function and its compare
> callback to have additional data and be able to pass, in this
> case, the address length. In case the argument is not needed,
> NULL can be provided.
> 
> Signed-off-by: Luca Fancellu 
> ---

Hi all,

I’ve just spotted an issue with the algorithm, the fix is this one:

diff --git a/xen/common/device_tree.c b/xen/common/device_tree.c
index 24914a80d03b..262385a041a8 100644
--- a/xen/common/device_tree.c
+++ b/xen/common/device_tree.c
@@ -2360,6 +2360,10 @@ int __init 
dt_merge_overlapping_addr_size_intervals(__be32 *reg, int *nr_cells,
 __be32 *tmp_last_cell_size = last_cell + addrcells;
 
 dt_set_cell(_last_cell_size, sizecells, new_size);
+
+/* Last interval updated, so the end is changed */
+end_last = start_last + size_last;
+
 /*
  * This current interval is merged with the last one, so remove 
this
  * interval and shift left all the remaining elements




Now, I would like to write something about the algorithm to ease the reviewers,
the problem is that we have some intervals and we would like to merge the 
overlapping
ones, a simple algorithm can be found here: 
https://www.interviewbit.com/blog/merge-intervals/

Limitation now is that when merging the intervals, we don’t want to exceed the 
space needed
to store the information, for example:

sizecells: 1 (meaning one __be32, 4 byte)
Int1: start 0x0, size 0x
Int2: start 0x,  size 0x1000

We can’t merge them because the new size would be over 4 byte.

During the development of this algorithm I’ve prototyped it in Python, I’ll 
attach my script here
so that it’s easier to understand:

#!/usr/bin/env python3

def merge_intervals_inplace(intervals, size_limit):
merged = intervals[:]
last_idx = 0
i = 1
count = len(merged)

if count == 1:
return merged

last_cell = merged[last_idx]
start_last = last_cell[0]
size_last = last_cell[1]
end_last = start_last + size_last

while i < count:

start_current = merged[i][0]
size_current = merged[i][1]
end_current = start_current + size_current
overlap = end_last >= start_current
new_size = max(end_last, end_current) - start_last
#print((f"last ({start_last},{end_last}),"
#   f" curr ({start_current},{end_current}),"
#   f" newsize: {new_size}"
#))

# If the current interval doesn't overlap with the last one, or even if
# they overlap but the computed new size would be over the imposed
# limit, then advance the last element by one position
if (not overlap) or (overlap and new_size > size_limit):
#print("advance last")
last_idx += 1
last_cell = merged[last_idx]
start_last = last_cell[0]
size_last = last_cell[1]
end_last = start_last + size_last
else:
#print("merge")
# Set new size for the last element, merging the last interval with
# the current one
merged[last_idx] = (start_last, new_size)
# Update last elem interval end
end_last = start_last + new_size
# The current interval (i) is merged with the last, so remove it and
# shift left all the remaining intervals
merged = merged[:i] + merged[i+1:]
# Now the array has less element since we merged two intervals
count -= 1
# Next iteration needs to start from the current index, skip
# increment
continue
i += 1

return merged


def print_interval(intervals):
print("[", end='')
for interval in intervals:
s = interval[0]
sz = interval[1]
print(f" ({s},{sz}) ", end='')
print("] -> ", end='')
print("[", end='')
for interval in intervals:
s = interval[0]
e = interval[0] + interval[1]
print(f" ({s},{e}) ", end='')
print("]")


def main(argv):
limit=20

# Array of intervals (start address, size)
#banks = [(0,2), (5,1), (0,10), (10,7), (2,6)]
banks = [(0,20), (20,5), (10,15), (5,15)]

for interval in banks:
if interval[1] > limit:
raise Exception(f"{interval} size > limit ({limit})")

# Sort by start address ascending order
banks.sort(key=lambda a: