xref: /linux/kernel/range.c (revision b296a6d5)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
227811d8cSYinghai Lu /*
327811d8cSYinghai Lu  * Range add and subtract
427811d8cSYinghai Lu  */
527811d8cSYinghai Lu #include <linux/init.h>
6*b296a6d5SAndy Shevchenko #include <linux/minmax.h>
7*b296a6d5SAndy Shevchenko #include <linux/printk.h>
827811d8cSYinghai Lu #include <linux/sort.h>
905418815SYinghai Lu #include <linux/string.h>
1027811d8cSYinghai Lu #include <linux/range.h>
1127811d8cSYinghai Lu 
add_range(struct range * range,int az,int nr_range,u64 start,u64 end)1227811d8cSYinghai Lu int add_range(struct range *range, int az, int nr_range, u64 start, u64 end)
1327811d8cSYinghai Lu {
14e9a0064aSYinghai Lu 	if (start >= end)
1527811d8cSYinghai Lu 		return nr_range;
1627811d8cSYinghai Lu 
1727811d8cSYinghai Lu 	/* Out of slots: */
1827811d8cSYinghai Lu 	if (nr_range >= az)
1927811d8cSYinghai Lu 		return nr_range;
2027811d8cSYinghai Lu 
2127811d8cSYinghai Lu 	range[nr_range].start = start;
2227811d8cSYinghai Lu 	range[nr_range].end = end;
2327811d8cSYinghai Lu 
2427811d8cSYinghai Lu 	nr_range++;
2527811d8cSYinghai Lu 
2627811d8cSYinghai Lu 	return nr_range;
2727811d8cSYinghai Lu }
2827811d8cSYinghai Lu 
add_range_with_merge(struct range * range,int az,int nr_range,u64 start,u64 end)2927811d8cSYinghai Lu int add_range_with_merge(struct range *range, int az, int nr_range,
3027811d8cSYinghai Lu 		     u64 start, u64 end)
3127811d8cSYinghai Lu {
3227811d8cSYinghai Lu 	int i;
3327811d8cSYinghai Lu 
34e9a0064aSYinghai Lu 	if (start >= end)
3527811d8cSYinghai Lu 		return nr_range;
3627811d8cSYinghai Lu 
3705418815SYinghai Lu 	/* get new start/end: */
3827811d8cSYinghai Lu 	for (i = 0; i < nr_range; i++) {
3927811d8cSYinghai Lu 		u64 common_start, common_end;
4027811d8cSYinghai Lu 
4127811d8cSYinghai Lu 		if (!range[i].end)
4227811d8cSYinghai Lu 			continue;
4327811d8cSYinghai Lu 
4427811d8cSYinghai Lu 		common_start = max(range[i].start, start);
4527811d8cSYinghai Lu 		common_end = min(range[i].end, end);
46e9a0064aSYinghai Lu 		if (common_start > common_end)
4727811d8cSYinghai Lu 			continue;
4827811d8cSYinghai Lu 
4905418815SYinghai Lu 		/* new start/end, will add it back at last */
5005418815SYinghai Lu 		start = min(range[i].start, start);
5105418815SYinghai Lu 		end = max(range[i].end, end);
5227811d8cSYinghai Lu 
5305418815SYinghai Lu 		memmove(&range[i], &range[i + 1],
5405418815SYinghai Lu 			(nr_range - (i + 1)) * sizeof(range[i]));
5505418815SYinghai Lu 		range[nr_range - 1].start = 0;
5605418815SYinghai Lu 		range[nr_range - 1].end   = 0;
5705418815SYinghai Lu 		nr_range--;
5805418815SYinghai Lu 		i--;
5927811d8cSYinghai Lu 	}
6027811d8cSYinghai Lu 
6127811d8cSYinghai Lu 	/* Need to add it: */
6227811d8cSYinghai Lu 	return add_range(range, az, nr_range, start, end);
6327811d8cSYinghai Lu }
6427811d8cSYinghai Lu 
subtract_range(struct range * range,int az,u64 start,u64 end)6527811d8cSYinghai Lu void subtract_range(struct range *range, int az, u64 start, u64 end)
6627811d8cSYinghai Lu {
6727811d8cSYinghai Lu 	int i, j;
6827811d8cSYinghai Lu 
69e9a0064aSYinghai Lu 	if (start >= end)
7027811d8cSYinghai Lu 		return;
7127811d8cSYinghai Lu 
7227811d8cSYinghai Lu 	for (j = 0; j < az; j++) {
7327811d8cSYinghai Lu 		if (!range[j].end)
7427811d8cSYinghai Lu 			continue;
7527811d8cSYinghai Lu 
7627811d8cSYinghai Lu 		if (start <= range[j].start && end >= range[j].end) {
7727811d8cSYinghai Lu 			range[j].start = 0;
7827811d8cSYinghai Lu 			range[j].end = 0;
7927811d8cSYinghai Lu 			continue;
8027811d8cSYinghai Lu 		}
8127811d8cSYinghai Lu 
8227811d8cSYinghai Lu 		if (start <= range[j].start && end < range[j].end &&
83e9a0064aSYinghai Lu 		    range[j].start < end) {
84e9a0064aSYinghai Lu 			range[j].start = end;
8527811d8cSYinghai Lu 			continue;
8627811d8cSYinghai Lu 		}
8727811d8cSYinghai Lu 
8827811d8cSYinghai Lu 
8927811d8cSYinghai Lu 		if (start > range[j].start && end >= range[j].end &&
90e9a0064aSYinghai Lu 		    range[j].end > start) {
91e9a0064aSYinghai Lu 			range[j].end = start;
9227811d8cSYinghai Lu 			continue;
9327811d8cSYinghai Lu 		}
9427811d8cSYinghai Lu 
9527811d8cSYinghai Lu 		if (start > range[j].start && end < range[j].end) {
9627811d8cSYinghai Lu 			/* Find the new spare: */
9727811d8cSYinghai Lu 			for (i = 0; i < az; i++) {
9827811d8cSYinghai Lu 				if (range[i].end == 0)
9927811d8cSYinghai Lu 					break;
10027811d8cSYinghai Lu 			}
10127811d8cSYinghai Lu 			if (i < az) {
10227811d8cSYinghai Lu 				range[i].end = range[j].end;
103e9a0064aSYinghai Lu 				range[i].start = end;
10427811d8cSYinghai Lu 			} else {
1057fba2c27SLin Feng 				pr_err("%s: run out of slot in ranges\n",
1067fba2c27SLin Feng 					__func__);
10727811d8cSYinghai Lu 			}
108e9a0064aSYinghai Lu 			range[j].end = start;
10927811d8cSYinghai Lu 			continue;
11027811d8cSYinghai Lu 		}
11127811d8cSYinghai Lu 	}
11227811d8cSYinghai Lu }
11327811d8cSYinghai Lu 
cmp_range(const void * x1,const void * x2)11427811d8cSYinghai Lu static int cmp_range(const void *x1, const void *x2)
11527811d8cSYinghai Lu {
11627811d8cSYinghai Lu 	const struct range *r1 = x1;
11727811d8cSYinghai Lu 	const struct range *r2 = x2;
11827811d8cSYinghai Lu 
119fc7f0dd3SLouis Langholtz 	if (r1->start < r2->start)
120fc7f0dd3SLouis Langholtz 		return -1;
121fc7f0dd3SLouis Langholtz 	if (r1->start > r2->start)
122fc7f0dd3SLouis Langholtz 		return 1;
123fc7f0dd3SLouis Langholtz 	return 0;
12427811d8cSYinghai Lu }
12527811d8cSYinghai Lu 
clean_sort_range(struct range * range,int az)12627811d8cSYinghai Lu int clean_sort_range(struct range *range, int az)
12727811d8cSYinghai Lu {
128834b4038SAlexey Khoroshilov 	int i, j, k = az - 1, nr_range = az;
12927811d8cSYinghai Lu 
13027811d8cSYinghai Lu 	for (i = 0; i < k; i++) {
13127811d8cSYinghai Lu 		if (range[i].end)
13227811d8cSYinghai Lu 			continue;
13327811d8cSYinghai Lu 		for (j = k; j > i; j--) {
13427811d8cSYinghai Lu 			if (range[j].end) {
13527811d8cSYinghai Lu 				k = j;
13627811d8cSYinghai Lu 				break;
13727811d8cSYinghai Lu 			}
13827811d8cSYinghai Lu 		}
13927811d8cSYinghai Lu 		if (j == i)
14027811d8cSYinghai Lu 			break;
14127811d8cSYinghai Lu 		range[i].start = range[k].start;
14227811d8cSYinghai Lu 		range[i].end   = range[k].end;
14327811d8cSYinghai Lu 		range[k].start = 0;
14427811d8cSYinghai Lu 		range[k].end   = 0;
14527811d8cSYinghai Lu 		k--;
14627811d8cSYinghai Lu 	}
14727811d8cSYinghai Lu 	/* count it */
14827811d8cSYinghai Lu 	for (i = 0; i < az; i++) {
14927811d8cSYinghai Lu 		if (!range[i].end) {
15027811d8cSYinghai Lu 			nr_range = i;
15127811d8cSYinghai Lu 			break;
15227811d8cSYinghai Lu 		}
15327811d8cSYinghai Lu 	}
15427811d8cSYinghai Lu 
15527811d8cSYinghai Lu 	/* sort them */
15627811d8cSYinghai Lu 	sort(range, nr_range, sizeof(struct range), cmp_range, NULL);
15727811d8cSYinghai Lu 
15827811d8cSYinghai Lu 	return nr_range;
15927811d8cSYinghai Lu }
16027811d8cSYinghai Lu 
sort_range(struct range * range,int nr_range)16127811d8cSYinghai Lu void sort_range(struct range *range, int nr_range)
16227811d8cSYinghai Lu {
16327811d8cSYinghai Lu 	/* sort them */
16427811d8cSYinghai Lu 	sort(range, nr_range, sizeof(struct range), cmp_range, NULL);
16527811d8cSYinghai Lu }
166