2 * Copyright (c) 2014, Cisco Systems, Inc. All rights reserved.
4 * This software is available to you under a choice of one of two
5 * licenses. You may choose to be licensed under the terms of the GNU
6 * General Public License (GPL) Version 2, available from the file
7 * COPYING in the main directory of this source tree, or the
10 * Redistribution and use in source and binary forms, with or
11 * without modification, are permitted provided that the following
14 * - Redistributions of source code must retain the above
15 * copyright notice, this list of conditions and the following
18 * - Redistributions in binary form must reproduce the above
19 * copyright notice, this list of conditions and the following
20 * disclaimer in the documentation and/or other materials
21 * provided with the distribution.
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
34 #include <linux/init.h>
35 #include <linux/list.h>
36 #include <linux/slab.h>
37 #include <linux/list_sort.h>
39 #include <linux/interval_tree_generic.h>
40 #include "usnic_uiom_interval_tree.h"
42 #define START(node) ((node)->start)
43 #define LAST(node) ((node)->last)
45 #define MAKE_NODE(node, start, end, ref_cnt, flags, err, err_out) \
47 node = usnic_uiom_interval_node_alloc(start, \
48 end, ref_cnt, flags); \
55 #define MARK_FOR_ADD(node, list) (list_add_tail(&node->link, list))
57 #define MAKE_NODE_AND_APPEND(node, start, end, ref_cnt, flags, err, \
60 MAKE_NODE(node, start, end, \
61 ref_cnt, flags, err, \
63 MARK_FOR_ADD(node, list); \
66 #define FLAGS_EQUAL(flags1, flags2, mask) \
67 (((flags1) & (mask)) == ((flags2) & (mask)))
69 static struct usnic_uiom_interval_node
*
70 usnic_uiom_interval_node_alloc(long int start
, long int last
, int ref_cnt
,
73 struct usnic_uiom_interval_node
*interval
= kzalloc(sizeof(*interval
),
78 interval
->start
= start
;
79 interval
->last
= last
;
80 interval
->flags
= flags
;
81 interval
->ref_cnt
= ref_cnt
;
86 static int interval_cmp(void *priv
, const struct list_head
*a
,
87 const struct list_head
*b
)
89 struct usnic_uiom_interval_node
*node_a
, *node_b
;
91 node_a
= list_entry(a
, struct usnic_uiom_interval_node
, link
);
92 node_b
= list_entry(b
, struct usnic_uiom_interval_node
, link
);
95 if (node_a
->start
< node_b
->start
)
97 else if (node_a
->start
> node_b
->start
)
104 find_intervals_intersection_sorted(struct rb_root_cached
*root
,
105 unsigned long start
, unsigned long last
,
106 struct list_head
*list
)
108 struct usnic_uiom_interval_node
*node
;
110 INIT_LIST_HEAD(list
);
112 for (node
= usnic_uiom_interval_tree_iter_first(root
, start
, last
);
114 node
= usnic_uiom_interval_tree_iter_next(node
, start
, last
))
115 list_add_tail(&node
->link
, list
);
117 list_sort(NULL
, list
, interval_cmp
);
120 int usnic_uiom_get_intervals_diff(unsigned long start
, unsigned long last
,
121 int flags
, int flag_mask
,
122 struct rb_root_cached
*root
,
123 struct list_head
*diff_set
)
125 struct usnic_uiom_interval_node
*interval
, *tmp
;
127 long int pivot
= start
;
128 LIST_HEAD(intersection_set
);
130 INIT_LIST_HEAD(diff_set
);
132 find_intervals_intersection_sorted(root
, start
, last
,
135 list_for_each_entry(interval
, &intersection_set
, link
) {
136 if (pivot
< interval
->start
) {
137 MAKE_NODE_AND_APPEND(tmp
, pivot
, interval
->start
- 1,
138 1, flags
, err
, err_out
,
140 pivot
= interval
->start
;
144 * Invariant: Set [start, pivot] is either in diff_set or root,
148 if (pivot
> interval
->last
) {
150 } else if (pivot
<= interval
->last
&&
151 FLAGS_EQUAL(interval
->flags
, flags
,
153 pivot
= interval
->last
+ 1;
158 MAKE_NODE_AND_APPEND(tmp
, pivot
, last
, 1, flags
, err
, err_out
,
164 list_for_each_entry_safe(interval
, tmp
, diff_set
, link
) {
165 list_del(&interval
->link
);
172 void usnic_uiom_put_interval_set(struct list_head
*intervals
)
174 struct usnic_uiom_interval_node
*interval
, *tmp
;
175 list_for_each_entry_safe(interval
, tmp
, intervals
, link
)
179 int usnic_uiom_insert_interval(struct rb_root_cached
*root
, unsigned long start
,
180 unsigned long last
, int flags
)
182 struct usnic_uiom_interval_node
*interval
, *tmp
;
183 unsigned long istart
, ilast
;
184 int iref_cnt
, iflags
;
185 unsigned long lpivot
= start
;
188 LIST_HEAD(intersection_set
);
190 find_intervals_intersection_sorted(root
, start
, last
,
193 list_for_each_entry(interval
, &intersection_set
, link
) {
195 * Invariant - lpivot is the left edge of next interval to be
198 istart
= interval
->start
;
199 ilast
= interval
->last
;
200 iref_cnt
= interval
->ref_cnt
;
201 iflags
= interval
->flags
;
203 if (istart
< lpivot
) {
204 MAKE_NODE_AND_APPEND(tmp
, istart
, lpivot
- 1, iref_cnt
,
205 iflags
, err
, err_out
, &to_add
);
206 } else if (istart
> lpivot
) {
207 MAKE_NODE_AND_APPEND(tmp
, lpivot
, istart
- 1, 1, flags
,
208 err
, err_out
, &to_add
);
215 MAKE_NODE_AND_APPEND(tmp
, lpivot
, last
, iref_cnt
+ 1,
216 iflags
| flags
, err
, err_out
,
218 MAKE_NODE_AND_APPEND(tmp
, last
+ 1, ilast
, iref_cnt
,
219 iflags
, err
, err_out
, &to_add
);
221 MAKE_NODE_AND_APPEND(tmp
, lpivot
, ilast
, iref_cnt
+ 1,
222 iflags
| flags
, err
, err_out
,
230 MAKE_NODE_AND_APPEND(tmp
, lpivot
, last
, 1, flags
, err
, err_out
,
233 list_for_each_entry_safe(interval
, tmp
, &intersection_set
, link
) {
234 usnic_uiom_interval_tree_remove(interval
, root
);
238 list_for_each_entry(interval
, &to_add
, link
)
239 usnic_uiom_interval_tree_insert(interval
, root
);
244 list_for_each_entry_safe(interval
, tmp
, &to_add
, link
)
250 void usnic_uiom_remove_interval(struct rb_root_cached
*root
,
251 unsigned long start
, unsigned long last
,
252 struct list_head
*removed
)
254 struct usnic_uiom_interval_node
*interval
;
256 for (interval
= usnic_uiom_interval_tree_iter_first(root
, start
, last
);
258 interval
= usnic_uiom_interval_tree_iter_next(interval
,
261 if (--interval
->ref_cnt
== 0)
262 list_add_tail(&interval
->link
, removed
);
265 list_for_each_entry(interval
, removed
, link
)
266 usnic_uiom_interval_tree_remove(interval
, root
);
269 INTERVAL_TREE_DEFINE(struct usnic_uiom_interval_node
, rb
,
270 unsigned long, __subtree_last
,
271 START
, LAST
, , usnic_uiom_interval_tree
)