/root/src/xen/xen/common/rangeset.c
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * rangeset.c |
3 | | * |
4 | | * Creation, maintenance and automatic destruction of per-domain sets of |
5 | | * numeric ranges. |
6 | | * |
7 | | * Copyright (c) 2005, K A Fraser |
8 | | */ |
9 | | |
10 | | #include <xen/sched.h> |
11 | | #include <xen/errno.h> |
12 | | #include <xen/rangeset.h> |
13 | | #include <xsm/xsm.h> |
14 | | |
15 | | /* An inclusive range [s,e] and pointer to next range in ascending order. */ |
16 | | struct range { |
17 | | struct list_head list; |
18 | | unsigned long s, e; |
19 | | }; |
20 | | |
21 | | struct rangeset { |
22 | | /* Owning domain and threaded list of rangesets. */ |
23 | | struct list_head rangeset_list; |
24 | | struct domain *domain; |
25 | | |
26 | | /* Ordered list of ranges contained in this set, and protecting lock. */ |
27 | | struct list_head range_list; |
28 | | |
29 | | /* Number of ranges that can be allocated */ |
30 | | long nr_ranges; |
31 | | rwlock_t lock; |
32 | | |
33 | | /* Pretty-printing name. */ |
34 | | char name[32]; |
35 | | |
36 | | /* RANGESETF flags. */ |
37 | | unsigned int flags; |
38 | | }; |
39 | | |
40 | | /***************************** |
41 | | * Private range functions hide the underlying linked-list implemnetation. |
42 | | */ |
43 | | |
44 | | /* Find highest range lower than or containing s. NULL if no such range. */ |
45 | | static struct range *find_range( |
46 | | struct rangeset *r, unsigned long s) |
47 | 2.44M | { |
48 | 2.44M | struct range *x = NULL, *y; |
49 | 2.44M | |
50 | 2.44M | list_for_each_entry ( y, &r->range_list, list ) |
51 | 7.48M | { |
52 | 7.48M | if ( y->s > s ) |
53 | 946k | break; |
54 | 6.54M | x = y; |
55 | 6.54M | } |
56 | 2.44M | |
57 | 2.44M | return x; |
58 | 2.44M | } |
59 | | |
60 | | /* Return the lowest range in the set r, or NULL if r is empty. */ |
61 | | static struct range *first_range( |
62 | | struct rangeset *r) |
63 | 935 | { |
64 | 935 | if ( list_empty(&r->range_list) ) |
65 | 225 | return NULL; |
66 | 710 | return list_entry(r->range_list.next, struct range, list); |
67 | 935 | } |
68 | | |
69 | | /* Return range following x in ascending order, or NULL if x is the highest. */ |
70 | | static struct range *next_range( |
71 | | struct rangeset *r, struct range *x) |
72 | 388 | { |
73 | 388 | if ( x->list.next == &r->range_list ) |
74 | 323 | return NULL; |
75 | 65 | return list_entry(x->list.next, struct range, list); |
76 | 388 | } |
77 | | |
78 | | /* Insert range y after range x in r. Insert as first range if x is NULL. */ |
79 | | static void insert_range( |
80 | | struct rangeset *r, struct range *x, struct range *y) |
81 | 341 | { |
82 | 341 | list_add(&y->list, (x != NULL) ? &x->list : &r->range_list); |
83 | 341 | } |
84 | | |
85 | | /* Remove a range from its list and free it. */ |
86 | | static void destroy_range( |
87 | | struct rangeset *r, struct range *x) |
88 | 318 | { |
89 | 318 | r->nr_ranges++; |
90 | 318 | |
91 | 318 | list_del(&x->list); |
92 | 318 | xfree(x); |
93 | 318 | } |
94 | | |
95 | | /* Allocate a new range */ |
96 | | static struct range *alloc_range( |
97 | | struct rangeset *r) |
98 | 341 | { |
99 | 341 | struct range *x; |
100 | 341 | |
101 | 341 | if ( r->nr_ranges == 0 ) |
102 | 0 | return NULL; |
103 | 341 | |
104 | 341 | x = xmalloc(struct range); |
105 | 341 | if ( x ) |
106 | 341 | --r->nr_ranges; |
107 | 341 | |
108 | 341 | return x; |
109 | 341 | } |
110 | | |
111 | | /***************************** |
112 | | * Core public functions |
113 | | */ |
114 | | |
115 | | int rangeset_add_range( |
116 | | struct rangeset *r, unsigned long s, unsigned long e) |
117 | 379 | { |
118 | 379 | struct range *x, *y; |
119 | 379 | int rc = 0; |
120 | 379 | |
121 | 379 | ASSERT(s <= e); |
122 | 379 | |
123 | 379 | write_lock(&r->lock); |
124 | 379 | |
125 | 379 | x = find_range(r, s); |
126 | 379 | y = find_range(r, e); |
127 | 379 | |
128 | 379 | if ( x == y ) |
129 | 379 | { |
130 | 379 | if ( (x == NULL) || ((x->e < s) && ((x->e + 1) != s)) ) |
131 | 289 | { |
132 | 289 | x = alloc_range(r); |
133 | 289 | if ( x == NULL ) |
134 | 0 | { |
135 | 0 | rc = -ENOMEM; |
136 | 0 | goto out; |
137 | 0 | } |
138 | 289 | |
139 | 289 | x->s = s; |
140 | 289 | x->e = e; |
141 | 289 | |
142 | 289 | insert_range(r, y, x); |
143 | 289 | } |
144 | 90 | else if ( x->e < e ) |
145 | 42 | x->e = e; |
146 | 379 | } |
147 | 379 | else |
148 | 0 | { |
149 | 0 | if ( x == NULL ) |
150 | 0 | { |
151 | 0 | x = first_range(r); |
152 | 0 | x->s = s; |
153 | 0 | } |
154 | 0 | else if ( (x->e < s) && ((x->e + 1) != s) ) |
155 | 0 | { |
156 | 0 | x = next_range(r, x); |
157 | 0 | x->s = s; |
158 | 0 | } |
159 | 0 | |
160 | 0 | x->e = (y->e > e) ? y->e : e; |
161 | 0 |
|
162 | 0 | for ( ; ; ) |
163 | 0 | { |
164 | 0 | y = next_range(r, x); |
165 | 0 | if ( (y == NULL) || (y->e > x->e) ) |
166 | 0 | break; |
167 | 0 | destroy_range(r, y); |
168 | 0 | } |
169 | 0 | } |
170 | 379 | |
171 | 379 | y = next_range(r, x); |
172 | 379 | if ( (y != NULL) && ((x->e + 1) == y->s) ) |
173 | 0 | { |
174 | 0 | x->e = y->e; |
175 | 0 | destroy_range(r, y); |
176 | 0 | } |
177 | 379 | |
178 | 379 | out: |
179 | 379 | write_unlock(&r->lock); |
180 | 379 | return rc; |
181 | 379 | } |
182 | | |
183 | | int rangeset_remove_range( |
184 | | struct rangeset *r, unsigned long s, unsigned long e) |
185 | 120 | { |
186 | 120 | struct range *x, *y, *t; |
187 | 120 | int rc = 0; |
188 | 120 | |
189 | 120 | ASSERT(s <= e); |
190 | 120 | |
191 | 120 | write_lock(&r->lock); |
192 | 120 | |
193 | 120 | x = find_range(r, s); |
194 | 120 | y = find_range(r, e); |
195 | 120 | |
196 | 120 | if ( x == y ) |
197 | 119 | { |
198 | 119 | if ( (x == NULL) || (x->e < s) ) |
199 | 12 | goto out; |
200 | 119 | |
201 | 107 | if ( (x->s < s) && (x->e > e) ) |
202 | 52 | { |
203 | 52 | y = alloc_range(r); |
204 | 52 | if ( y == NULL ) |
205 | 0 | { |
206 | 0 | rc = -ENOMEM; |
207 | 0 | goto out; |
208 | 0 | } |
209 | 52 | |
210 | 52 | y->s = e + 1; |
211 | 52 | y->e = x->e; |
212 | 52 | x->e = s - 1; |
213 | 52 | |
214 | 52 | insert_range(r, x, y); |
215 | 52 | } |
216 | 55 | else if ( (x->s == s) && (x->e <= e) ) |
217 | 0 | destroy_range(r, x); |
218 | 55 | else if ( x->s == s ) |
219 | 44 | x->s = e + 1; |
220 | 11 | else if ( x->e <= e ) |
221 | 11 | x->e = s - 1; |
222 | 107 | } |
223 | 120 | else |
224 | 1 | { |
225 | 1 | if ( x == NULL ) |
226 | 0 | x = first_range(r); |
227 | 1 | |
228 | 1 | if ( x->s < s ) |
229 | 1 | { |
230 | 1 | x->e = s - 1; |
231 | 1 | x = next_range(r, x); |
232 | 1 | } |
233 | 1 | |
234 | 1 | while ( x != y ) |
235 | 0 | { |
236 | 0 | t = x; |
237 | 0 | x = next_range(r, x); |
238 | 0 | destroy_range(r, t); |
239 | 0 | } |
240 | 1 | |
241 | 1 | x->s = e + 1; |
242 | 1 | if ( x->s > x->e ) |
243 | 0 | destroy_range(r, x); |
244 | 1 | } |
245 | 120 | |
246 | 120 | out: |
247 | 120 | write_unlock(&r->lock); |
248 | 120 | return rc; |
249 | 120 | } |
250 | | |
251 | | bool_t rangeset_contains_range( |
252 | | struct rangeset *r, unsigned long s, unsigned long e) |
253 | 578k | { |
254 | 578k | struct range *x; |
255 | 578k | bool_t contains; |
256 | 578k | |
257 | 578k | ASSERT(s <= e); |
258 | 578k | |
259 | 578k | read_lock(&r->lock); |
260 | 578k | x = find_range(r, s); |
261 | 575k | contains = (x && (x->e >= e)); |
262 | 578k | read_unlock(&r->lock); |
263 | 578k | |
264 | 578k | return contains; |
265 | 578k | } |
266 | | |
267 | | bool_t rangeset_overlaps_range( |
268 | | struct rangeset *r, unsigned long s, unsigned long e) |
269 | 1.86M | { |
270 | 1.86M | struct range *x; |
271 | 1.86M | bool_t overlaps; |
272 | 1.86M | |
273 | 1.86M | ASSERT(s <= e); |
274 | 1.86M | |
275 | 1.86M | read_lock(&r->lock); |
276 | 1.86M | x = find_range(r, e); |
277 | 1.85M | overlaps = (x && (s <= x->e)); |
278 | 1.86M | read_unlock(&r->lock); |
279 | 1.86M | |
280 | 1.86M | return overlaps; |
281 | 1.86M | } |
282 | | |
283 | | int rangeset_report_ranges( |
284 | | struct rangeset *r, unsigned long s, unsigned long e, |
285 | | int (*cb)(unsigned long s, unsigned long e, void *), void *ctxt) |
286 | 1 | { |
287 | 1 | struct range *x; |
288 | 1 | int rc = 0; |
289 | 1 | |
290 | 1 | read_lock(&r->lock); |
291 | 1 | |
292 | 9 | for ( x = first_range(r); x && (x->s <= e) && !rc; x = next_range(r, x) ) |
293 | 8 | if ( x->e >= s ) |
294 | 8 | rc = cb(max(x->s, s), min(x->e, e), ctxt); |
295 | 1 | |
296 | 1 | read_unlock(&r->lock); |
297 | 1 | |
298 | 1 | return rc; |
299 | 1 | } |
300 | | |
301 | | int rangeset_consume_ranges( |
302 | | struct rangeset *r, |
303 | | int (*cb)(unsigned long s, unsigned long e, void *, unsigned long *c), |
304 | | void *ctxt) |
305 | 616 | { |
306 | 616 | int rc = 0; |
307 | 616 | |
308 | 616 | write_lock(&r->lock); |
309 | 934 | while ( !rangeset_is_empty(r) ) |
310 | 709 | { |
311 | 709 | unsigned long consumed = 0; |
312 | 709 | struct range *x = first_range(r); |
313 | 709 | |
314 | 709 | rc = cb(x->s, x->e, ctxt, &consumed); |
315 | 709 | |
316 | 709 | ASSERT(consumed <= x->e - x->s + 1); |
317 | 709 | x->s += consumed; |
318 | 709 | if ( x->s > x->e ) |
319 | 318 | destroy_range(r, x); |
320 | 709 | |
321 | 709 | if ( rc ) |
322 | 391 | break; |
323 | 709 | } |
324 | 616 | write_unlock(&r->lock); |
325 | 616 | |
326 | 616 | return rc; |
327 | 616 | } |
328 | | |
329 | | int rangeset_add_singleton( |
330 | | struct rangeset *r, unsigned long s) |
331 | 93 | { |
332 | 93 | return rangeset_add_range(r, s, s); |
333 | 93 | } |
334 | | |
335 | | int rangeset_remove_singleton( |
336 | | struct rangeset *r, unsigned long s) |
337 | 0 | { |
338 | 0 | return rangeset_remove_range(r, s, s); |
339 | 0 | } |
340 | | |
341 | | bool_t rangeset_contains_singleton( |
342 | | struct rangeset *r, unsigned long s) |
343 | 578k | { |
344 | 578k | return rangeset_contains_range(r, s, s); |
345 | 578k | } |
346 | | |
347 | | bool_t rangeset_is_empty( |
348 | | const struct rangeset *r) |
349 | 1.01k | { |
350 | 1.01k | return ((r == NULL) || list_empty(&r->range_list)); |
351 | 1.01k | } |
352 | | |
353 | | struct rangeset *rangeset_new( |
354 | | struct domain *d, char *name, unsigned int flags) |
355 | 238 | { |
356 | 238 | struct rangeset *r; |
357 | 238 | |
358 | 238 | r = xmalloc(struct rangeset); |
359 | 238 | if ( r == NULL ) |
360 | 0 | return NULL; |
361 | 238 | |
362 | 238 | rwlock_init(&r->lock); |
363 | 238 | INIT_LIST_HEAD(&r->range_list); |
364 | 238 | r->nr_ranges = -1; |
365 | 238 | |
366 | 238 | BUG_ON(flags & ~RANGESETF_prettyprint_hex); |
367 | 238 | r->flags = flags; |
368 | 238 | |
369 | 238 | if ( name != NULL ) |
370 | 13 | { |
371 | 13 | safe_strcpy(r->name, name); |
372 | 13 | } |
373 | 238 | else |
374 | 225 | { |
375 | 225 | snprintf(r->name, sizeof(r->name), "(no name)"); |
376 | 225 | } |
377 | 238 | |
378 | 238 | if ( (r->domain = d) != NULL ) |
379 | 12 | { |
380 | 12 | spin_lock(&d->rangesets_lock); |
381 | 12 | list_add(&r->rangeset_list, &d->rangesets); |
382 | 12 | spin_unlock(&d->rangesets_lock); |
383 | 12 | } |
384 | 238 | |
385 | 238 | return r; |
386 | 238 | } |
387 | | |
388 | | void rangeset_destroy( |
389 | | struct rangeset *r) |
390 | 225 | { |
391 | 225 | struct range *x; |
392 | 225 | |
393 | 225 | if ( r == NULL ) |
394 | 0 | return; |
395 | 225 | |
396 | 225 | if ( r->domain != NULL ) |
397 | 0 | { |
398 | 0 | spin_lock(&r->domain->rangesets_lock); |
399 | 0 | list_del(&r->rangeset_list); |
400 | 0 | spin_unlock(&r->domain->rangesets_lock); |
401 | 0 | } |
402 | 225 | |
403 | 225 | while ( (x = first_range(r)) != NULL ) |
404 | 0 | destroy_range(r, x); |
405 | 225 | |
406 | 225 | xfree(r); |
407 | 225 | } |
408 | | |
409 | | void rangeset_limit( |
410 | | struct rangeset *r, unsigned int limit) |
411 | 0 | { |
412 | 0 | r->nr_ranges = limit; |
413 | 0 | } |
414 | | |
415 | | void rangeset_domain_initialise( |
416 | | struct domain *d) |
417 | 5 | { |
418 | 5 | INIT_LIST_HEAD(&d->rangesets); |
419 | 5 | spin_lock_init(&d->rangesets_lock); |
420 | 5 | } |
421 | | |
422 | | void rangeset_domain_destroy( |
423 | | struct domain *d) |
424 | 0 | { |
425 | 0 | struct rangeset *r; |
426 | 0 |
|
427 | 0 | while ( !list_empty(&d->rangesets) ) |
428 | 0 | { |
429 | 0 | r = list_entry(d->rangesets.next, struct rangeset, rangeset_list); |
430 | 0 |
|
431 | 0 | BUG_ON(r->domain != d); |
432 | 0 | r->domain = NULL; |
433 | 0 | list_del(&r->rangeset_list); |
434 | 0 |
|
435 | 0 | rangeset_destroy(r); |
436 | 0 | } |
437 | 0 | } |
438 | | |
439 | | void rangeset_swap(struct rangeset *a, struct rangeset *b) |
440 | 0 | { |
441 | 0 | LIST_HEAD(tmp); |
442 | 0 |
|
443 | 0 | if ( a < b ) |
444 | 0 | { |
445 | 0 | write_lock(&a->lock); |
446 | 0 | write_lock(&b->lock); |
447 | 0 | } |
448 | 0 | else |
449 | 0 | { |
450 | 0 | write_lock(&b->lock); |
451 | 0 | write_lock(&a->lock); |
452 | 0 | } |
453 | 0 |
|
454 | 0 | list_splice_init(&a->range_list, &tmp); |
455 | 0 | list_splice_init(&b->range_list, &a->range_list); |
456 | 0 | list_splice(&tmp, &b->range_list); |
457 | 0 |
|
458 | 0 | write_unlock(&a->lock); |
459 | 0 | write_unlock(&b->lock); |
460 | 0 | } |
461 | | |
462 | | /***************************** |
463 | | * Pretty-printing functions |
464 | | */ |
465 | | |
466 | | static void print_limit(struct rangeset *r, unsigned long s) |
467 | 0 | { |
468 | 0 | printk((r->flags & RANGESETF_prettyprint_hex) ? "%lx" : "%lu", s); |
469 | 0 | } |
470 | | |
471 | | void rangeset_printk( |
472 | | struct rangeset *r) |
473 | 0 | { |
474 | 0 | int nr_printed = 0; |
475 | 0 | struct range *x; |
476 | 0 |
|
477 | 0 | read_lock(&r->lock); |
478 | 0 |
|
479 | 0 | printk("%-10s {", r->name); |
480 | 0 |
|
481 | 0 | for ( x = first_range(r); x != NULL; x = next_range(r, x) ) |
482 | 0 | { |
483 | 0 | if ( nr_printed++ ) |
484 | 0 | printk(","); |
485 | 0 | printk(" "); |
486 | 0 | print_limit(r, x->s); |
487 | 0 | if ( x->s != x->e ) |
488 | 0 | { |
489 | 0 | printk("-"); |
490 | 0 | print_limit(r, x->e); |
491 | 0 | } |
492 | 0 | } |
493 | 0 |
|
494 | 0 | printk(" }"); |
495 | 0 |
|
496 | 0 | read_unlock(&r->lock); |
497 | 0 | } |
498 | | |
499 | | void rangeset_domain_printk( |
500 | | struct domain *d) |
501 | 0 | { |
502 | 0 | struct rangeset *r; |
503 | 0 |
|
504 | 0 | printk("Rangesets belonging to domain %u:\n", d->domain_id); |
505 | 0 |
|
506 | 0 | spin_lock(&d->rangesets_lock); |
507 | 0 |
|
508 | 0 | if ( list_empty(&d->rangesets) ) |
509 | 0 | printk(" None\n"); |
510 | 0 |
|
511 | 0 | list_for_each_entry ( r, &d->rangesets, rangeset_list ) |
512 | 0 | { |
513 | 0 | printk(" "); |
514 | 0 | rangeset_printk(r); |
515 | 0 | printk("\n"); |
516 | 0 | } |
517 | 0 |
|
518 | 0 | spin_unlock(&d->rangesets_lock); |
519 | 0 | } |
520 | | |
521 | | /* |
522 | | * Local variables: |
523 | | * mode: C |
524 | | * c-file-style: "BSD" |
525 | | * c-basic-offset: 4 |
526 | | * tab-width: 4 |
527 | | * indent-tabs-mode: nil |
528 | | * End: |
529 | | */ |