diff options
author | Jiri Pirko <jiri@mellanox.com> | 2018-11-14 03:22:28 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-11-15 17:43:43 -0500 |
commit | 0a020d416d0af0b0c782e2a8363896e756e9121e (patch) | |
tree | 2bd2d93bf7fdddceb76f0450c1063db9ea3776a3 | |
parent | 7dc5a0eeea185e5f3af3f97a86e76419791cdd60 (diff) |
lib: introduce initial implementation of object aggregation manager
This lib tracks objects which could be of two types:
1) root object
2) nested object - with a "delta" which differentiates it from
the associated root object
The objects are tracked by a hashtable and reference-counted. User is
responsible of implementing callbacks to create/destroy root entity
related to each root object and callback to create/destroy nested object
delta.
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | MAINTAINERS | 8 | ||||
-rw-r--r-- | include/linux/objagg.h | 46 | ||||
-rw-r--r-- | include/trace/events/objagg.h | 228 | ||||
-rw-r--r-- | lib/Kconfig | 3 | ||||
-rw-r--r-- | lib/Kconfig.debug | 10 | ||||
-rw-r--r-- | lib/Makefile | 2 | ||||
-rw-r--r-- | lib/objagg.c | 501 | ||||
-rw-r--r-- | lib/test_objagg.c | 835 |
8 files changed, 1633 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index e110e327bf38..3bd775ba51ce 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -10679,6 +10679,14 @@ L: linux-nfc@lists.01.org (moderated for non-subscribers) | |||
10679 | S: Supported | 10679 | S: Supported |
10680 | F: drivers/nfc/nxp-nci | 10680 | F: drivers/nfc/nxp-nci |
10681 | 10681 | ||
10682 | OBJAGG | ||
10683 | M: Jiri Pirko <jiri@mellanox.com> | ||
10684 | L: netdev@vger.kernel.org | ||
10685 | S: Supported | ||
10686 | F: lib/objagg.c | ||
10687 | F: lib/test_objagg.c | ||
10688 | F: include/linux/objagg.h | ||
10689 | |||
10682 | OBJTOOL | 10690 | OBJTOOL |
10683 | M: Josh Poimboeuf <jpoimboe@redhat.com> | 10691 | M: Josh Poimboeuf <jpoimboe@redhat.com> |
10684 | M: Peter Zijlstra <peterz@infradead.org> | 10692 | M: Peter Zijlstra <peterz@infradead.org> |
diff --git a/include/linux/objagg.h b/include/linux/objagg.h new file mode 100644 index 000000000000..34f38c186ea0 --- /dev/null +++ b/include/linux/objagg.h | |||
@@ -0,0 +1,46 @@ | |||
1 | /* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */ | ||
2 | /* Copyright (c) 2018 Mellanox Technologies. All rights reserved */ | ||
3 | |||
4 | #ifndef _OBJAGG_H | ||
5 | #define _OBJAGG_H | ||
6 | |||
7 | struct objagg_ops { | ||
8 | size_t obj_size; | ||
9 | void * (*delta_create)(void *priv, void *parent_obj, void *obj); | ||
10 | void (*delta_destroy)(void *priv, void *delta_priv); | ||
11 | void * (*root_create)(void *priv, void *obj); | ||
12 | void (*root_destroy)(void *priv, void *root_priv); | ||
13 | }; | ||
14 | |||
15 | struct objagg; | ||
16 | struct objagg_obj; | ||
17 | |||
18 | const void *objagg_obj_root_priv(const struct objagg_obj *objagg_obj); | ||
19 | const void *objagg_obj_delta_priv(const struct objagg_obj *objagg_obj); | ||
20 | const void *objagg_obj_raw(const struct objagg_obj *objagg_obj); | ||
21 | |||
22 | struct objagg_obj *objagg_obj_get(struct objagg *objagg, void *obj); | ||
23 | void objagg_obj_put(struct objagg *objagg, struct objagg_obj *objagg_obj); | ||
24 | struct objagg *objagg_create(const struct objagg_ops *ops, void *priv); | ||
25 | void objagg_destroy(struct objagg *objagg); | ||
26 | |||
27 | struct objagg_obj_stats { | ||
28 | unsigned int user_count; | ||
29 | unsigned int delta_user_count; /* includes delta object users */ | ||
30 | }; | ||
31 | |||
32 | struct objagg_obj_stats_info { | ||
33 | struct objagg_obj_stats stats; | ||
34 | struct objagg_obj *objagg_obj; /* associated object */ | ||
35 | bool is_root; | ||
36 | }; | ||
37 | |||
38 | struct objagg_stats { | ||
39 | unsigned int stats_info_count; | ||
40 | struct objagg_obj_stats_info stats_info[]; | ||
41 | }; | ||
42 | |||
43 | const struct objagg_stats *objagg_stats_get(struct objagg *objagg); | ||
44 | void objagg_stats_put(const struct objagg_stats *objagg_stats); | ||
45 | |||
46 | #endif | ||
diff --git a/include/trace/events/objagg.h b/include/trace/events/objagg.h new file mode 100644 index 000000000000..fcec0fc9eb0c --- /dev/null +++ b/include/trace/events/objagg.h | |||
@@ -0,0 +1,228 @@ | |||
1 | /* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */ | ||
2 | /* Copyright (c) 2018 Mellanox Technologies. All rights reserved */ | ||
3 | |||
4 | #undef TRACE_SYSTEM | ||
5 | #define TRACE_SYSTEM objagg | ||
6 | |||
7 | #if !defined(__TRACE_OBJAGG_H) || defined(TRACE_HEADER_MULTI_READ) | ||
8 | #define __TRACE_OBJAGG_H | ||
9 | |||
10 | #include <linux/tracepoint.h> | ||
11 | |||
12 | struct objagg; | ||
13 | struct objagg_obj; | ||
14 | |||
15 | TRACE_EVENT(objagg_create, | ||
16 | TP_PROTO(const struct objagg *objagg), | ||
17 | |||
18 | TP_ARGS(objagg), | ||
19 | |||
20 | TP_STRUCT__entry( | ||
21 | __field(const void *, objagg) | ||
22 | ), | ||
23 | |||
24 | TP_fast_assign( | ||
25 | __entry->objagg = objagg; | ||
26 | ), | ||
27 | |||
28 | TP_printk("objagg %p", __entry->objagg) | ||
29 | ); | ||
30 | |||
31 | TRACE_EVENT(objagg_destroy, | ||
32 | TP_PROTO(const struct objagg *objagg), | ||
33 | |||
34 | TP_ARGS(objagg), | ||
35 | |||
36 | TP_STRUCT__entry( | ||
37 | __field(const void *, objagg) | ||
38 | ), | ||
39 | |||
40 | TP_fast_assign( | ||
41 | __entry->objagg = objagg; | ||
42 | ), | ||
43 | |||
44 | TP_printk("objagg %p", __entry->objagg) | ||
45 | ); | ||
46 | |||
47 | TRACE_EVENT(objagg_obj_create, | ||
48 | TP_PROTO(const struct objagg *objagg, | ||
49 | const struct objagg_obj *obj), | ||
50 | |||
51 | TP_ARGS(objagg, obj), | ||
52 | |||
53 | TP_STRUCT__entry( | ||
54 | __field(const void *, objagg) | ||
55 | __field(const void *, obj) | ||
56 | ), | ||
57 | |||
58 | TP_fast_assign( | ||
59 | __entry->objagg = objagg; | ||
60 | __entry->obj = obj; | ||
61 | ), | ||
62 | |||
63 | TP_printk("objagg %p, obj %p", __entry->objagg, __entry->obj) | ||
64 | ); | ||
65 | |||
66 | TRACE_EVENT(objagg_obj_destroy, | ||
67 | TP_PROTO(const struct objagg *objagg, | ||
68 | const struct objagg_obj *obj), | ||
69 | |||
70 | TP_ARGS(objagg, obj), | ||
71 | |||
72 | TP_STRUCT__entry( | ||
73 | __field(const void *, objagg) | ||
74 | __field(const void *, obj) | ||
75 | ), | ||
76 | |||
77 | TP_fast_assign( | ||
78 | __entry->objagg = objagg; | ||
79 | __entry->obj = obj; | ||
80 | ), | ||
81 | |||
82 | TP_printk("objagg %p, obj %p", __entry->objagg, __entry->obj) | ||
83 | ); | ||
84 | |||
85 | TRACE_EVENT(objagg_obj_get, | ||
86 | TP_PROTO(const struct objagg *objagg, | ||
87 | const struct objagg_obj *obj, | ||
88 | unsigned int refcount), | ||
89 | |||
90 | TP_ARGS(objagg, obj, refcount), | ||
91 | |||
92 | TP_STRUCT__entry( | ||
93 | __field(const void *, objagg) | ||
94 | __field(const void *, obj) | ||
95 | __field(unsigned int, refcount) | ||
96 | ), | ||
97 | |||
98 | TP_fast_assign( | ||
99 | __entry->objagg = objagg; | ||
100 | __entry->obj = obj; | ||
101 | __entry->refcount = refcount; | ||
102 | ), | ||
103 | |||
104 | TP_printk("objagg %p, obj %p, refcount %u", | ||
105 | __entry->objagg, __entry->obj, __entry->refcount) | ||
106 | ); | ||
107 | |||
108 | TRACE_EVENT(objagg_obj_put, | ||
109 | TP_PROTO(const struct objagg *objagg, | ||
110 | const struct objagg_obj *obj, | ||
111 | unsigned int refcount), | ||
112 | |||
113 | TP_ARGS(objagg, obj, refcount), | ||
114 | |||
115 | TP_STRUCT__entry( | ||
116 | __field(const void *, objagg) | ||
117 | __field(const void *, obj) | ||
118 | __field(unsigned int, refcount) | ||
119 | ), | ||
120 | |||
121 | TP_fast_assign( | ||
122 | __entry->objagg = objagg; | ||
123 | __entry->obj = obj; | ||
124 | __entry->refcount = refcount; | ||
125 | ), | ||
126 | |||
127 | TP_printk("objagg %p, obj %p, refcount %u", | ||
128 | __entry->objagg, __entry->obj, __entry->refcount) | ||
129 | ); | ||
130 | |||
131 | TRACE_EVENT(objagg_obj_parent_assign, | ||
132 | TP_PROTO(const struct objagg *objagg, | ||
133 | const struct objagg_obj *obj, | ||
134 | const struct objagg_obj *parent, | ||
135 | unsigned int parent_refcount), | ||
136 | |||
137 | TP_ARGS(objagg, obj, parent, parent_refcount), | ||
138 | |||
139 | TP_STRUCT__entry( | ||
140 | __field(const void *, objagg) | ||
141 | __field(const void *, obj) | ||
142 | __field(const void *, parent) | ||
143 | __field(unsigned int, parent_refcount) | ||
144 | ), | ||
145 | |||
146 | TP_fast_assign( | ||
147 | __entry->objagg = objagg; | ||
148 | __entry->obj = obj; | ||
149 | __entry->parent = parent; | ||
150 | __entry->parent_refcount = parent_refcount; | ||
151 | ), | ||
152 | |||
153 | TP_printk("objagg %p, obj %p, parent %p, parent_refcount %u", | ||
154 | __entry->objagg, __entry->obj, | ||
155 | __entry->parent, __entry->parent_refcount) | ||
156 | ); | ||
157 | |||
158 | TRACE_EVENT(objagg_obj_parent_unassign, | ||
159 | TP_PROTO(const struct objagg *objagg, | ||
160 | const struct objagg_obj *obj, | ||
161 | const struct objagg_obj *parent, | ||
162 | unsigned int parent_refcount), | ||
163 | |||
164 | TP_ARGS(objagg, obj, parent, parent_refcount), | ||
165 | |||
166 | TP_STRUCT__entry( | ||
167 | __field(const void *, objagg) | ||
168 | __field(const void *, obj) | ||
169 | __field(const void *, parent) | ||
170 | __field(unsigned int, parent_refcount) | ||
171 | ), | ||
172 | |||
173 | TP_fast_assign( | ||
174 | __entry->objagg = objagg; | ||
175 | __entry->obj = obj; | ||
176 | __entry->parent = parent; | ||
177 | __entry->parent_refcount = parent_refcount; | ||
178 | ), | ||
179 | |||
180 | TP_printk("objagg %p, obj %p, parent %p, parent_refcount %u", | ||
181 | __entry->objagg, __entry->obj, | ||
182 | __entry->parent, __entry->parent_refcount) | ||
183 | ); | ||
184 | |||
185 | TRACE_EVENT(objagg_obj_root_create, | ||
186 | TP_PROTO(const struct objagg *objagg, | ||
187 | const struct objagg_obj *obj), | ||
188 | |||
189 | TP_ARGS(objagg, obj), | ||
190 | |||
191 | TP_STRUCT__entry( | ||
192 | __field(const void *, objagg) | ||
193 | __field(const void *, obj) | ||
194 | ), | ||
195 | |||
196 | TP_fast_assign( | ||
197 | __entry->objagg = objagg; | ||
198 | __entry->obj = obj; | ||
199 | ), | ||
200 | |||
201 | TP_printk("objagg %p, obj %p", | ||
202 | __entry->objagg, __entry->obj) | ||
203 | ); | ||
204 | |||
205 | TRACE_EVENT(objagg_obj_root_destroy, | ||
206 | TP_PROTO(const struct objagg *objagg, | ||
207 | const struct objagg_obj *obj), | ||
208 | |||
209 | TP_ARGS(objagg, obj), | ||
210 | |||
211 | TP_STRUCT__entry( | ||
212 | __field(const void *, objagg) | ||
213 | __field(const void *, obj) | ||
214 | ), | ||
215 | |||
216 | TP_fast_assign( | ||
217 | __entry->objagg = objagg; | ||
218 | __entry->obj = obj; | ||
219 | ), | ||
220 | |||
221 | TP_printk("objagg %p, obj %p", | ||
222 | __entry->objagg, __entry->obj) | ||
223 | ); | ||
224 | |||
225 | #endif /* __TRACE_OBJAGG_H */ | ||
226 | |||
227 | /* This part must be outside protection */ | ||
228 | #include <trace/define_trace.h> | ||
diff --git a/lib/Kconfig b/lib/Kconfig index a9965f4af4dd..7dbbcfe9cd90 100644 --- a/lib/Kconfig +++ b/lib/Kconfig | |||
@@ -624,3 +624,6 @@ config GENERIC_LIB_CMPDI2 | |||
624 | 624 | ||
625 | config GENERIC_LIB_UCMPDI2 | 625 | config GENERIC_LIB_UCMPDI2 |
626 | bool | 626 | bool |
627 | |||
628 | config OBJAGG | ||
629 | tristate "objagg" if COMPILE_TEST | ||
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 1af29b8224fd..b3c91b9e32f8 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug | |||
@@ -1976,6 +1976,16 @@ config TEST_MEMCAT_P | |||
1976 | 1976 | ||
1977 | If unsure, say N. | 1977 | If unsure, say N. |
1978 | 1978 | ||
1979 | config TEST_OBJAGG | ||
1980 | tristate "Perform selftest on object aggreration manager" | ||
1981 | default n | ||
1982 | depends on OBJAGG | ||
1983 | help | ||
1984 | Enable this option to test object aggregation manager on boot | ||
1985 | (or module load). | ||
1986 | |||
1987 | If unsure, say N. | ||
1988 | |||
1979 | endif # RUNTIME_TESTING_MENU | 1989 | endif # RUNTIME_TESTING_MENU |
1980 | 1990 | ||
1981 | config MEMTEST | 1991 | config MEMTEST |
diff --git a/lib/Makefile b/lib/Makefile index db06d1237898..f5262d30bfe6 100644 --- a/lib/Makefile +++ b/lib/Makefile | |||
@@ -75,6 +75,7 @@ obj-$(CONFIG_TEST_PARMAN) += test_parman.o | |||
75 | obj-$(CONFIG_TEST_KMOD) += test_kmod.o | 75 | obj-$(CONFIG_TEST_KMOD) += test_kmod.o |
76 | obj-$(CONFIG_TEST_DEBUG_VIRTUAL) += test_debug_virtual.o | 76 | obj-$(CONFIG_TEST_DEBUG_VIRTUAL) += test_debug_virtual.o |
77 | obj-$(CONFIG_TEST_MEMCAT_P) += test_memcat_p.o | 77 | obj-$(CONFIG_TEST_MEMCAT_P) += test_memcat_p.o |
78 | obj-$(CONFIG_TEST_OBJAGG) += test_objagg.o | ||
78 | 79 | ||
79 | ifeq ($(CONFIG_DEBUG_KOBJECT),y) | 80 | ifeq ($(CONFIG_DEBUG_KOBJECT),y) |
80 | CFLAGS_kobject.o += -DDEBUG | 81 | CFLAGS_kobject.o += -DDEBUG |
@@ -274,3 +275,4 @@ obj-$(CONFIG_GENERIC_LIB_LSHRDI3) += lshrdi3.o | |||
274 | obj-$(CONFIG_GENERIC_LIB_MULDI3) += muldi3.o | 275 | obj-$(CONFIG_GENERIC_LIB_MULDI3) += muldi3.o |
275 | obj-$(CONFIG_GENERIC_LIB_CMPDI2) += cmpdi2.o | 276 | obj-$(CONFIG_GENERIC_LIB_CMPDI2) += cmpdi2.o |
276 | obj-$(CONFIG_GENERIC_LIB_UCMPDI2) += ucmpdi2.o | 277 | obj-$(CONFIG_GENERIC_LIB_UCMPDI2) += ucmpdi2.o |
278 | obj-$(CONFIG_OBJAGG) += objagg.o | ||
diff --git a/lib/objagg.c b/lib/objagg.c new file mode 100644 index 000000000000..c9b457a91153 --- /dev/null +++ b/lib/objagg.c | |||
@@ -0,0 +1,501 @@ | |||
1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 | ||
2 | /* Copyright (c) 2018 Mellanox Technologies. All rights reserved */ | ||
3 | |||
4 | #include <linux/module.h> | ||
5 | #include <linux/slab.h> | ||
6 | #include <linux/rhashtable.h> | ||
7 | #include <linux/list.h> | ||
8 | #include <linux/sort.h> | ||
9 | #include <linux/objagg.h> | ||
10 | |||
11 | #define CREATE_TRACE_POINTS | ||
12 | #include <trace/events/objagg.h> | ||
13 | |||
14 | struct objagg { | ||
15 | const struct objagg_ops *ops; | ||
16 | void *priv; | ||
17 | struct rhashtable obj_ht; | ||
18 | struct rhashtable_params ht_params; | ||
19 | struct list_head obj_list; | ||
20 | unsigned int obj_count; | ||
21 | }; | ||
22 | |||
23 | struct objagg_obj { | ||
24 | struct rhash_head ht_node; /* member of objagg->obj_ht */ | ||
25 | struct list_head list; /* member of objagg->obj_list */ | ||
26 | struct objagg_obj *parent; /* if the object is nested, this | ||
27 | * holds pointer to parent, otherwise NULL | ||
28 | */ | ||
29 | union { | ||
30 | void *delta_priv; /* user delta private */ | ||
31 | void *root_priv; /* user root private */ | ||
32 | }; | ||
33 | unsigned int refcount; /* counts number of users of this object | ||
34 | * including nested objects | ||
35 | */ | ||
36 | struct objagg_obj_stats stats; | ||
37 | unsigned long obj[0]; | ||
38 | }; | ||
39 | |||
40 | static unsigned int objagg_obj_ref_inc(struct objagg_obj *objagg_obj) | ||
41 | { | ||
42 | return ++objagg_obj->refcount; | ||
43 | } | ||
44 | |||
45 | static unsigned int objagg_obj_ref_dec(struct objagg_obj *objagg_obj) | ||
46 | { | ||
47 | return --objagg_obj->refcount; | ||
48 | } | ||
49 | |||
50 | static void objagg_obj_stats_inc(struct objagg_obj *objagg_obj) | ||
51 | { | ||
52 | objagg_obj->stats.user_count++; | ||
53 | objagg_obj->stats.delta_user_count++; | ||
54 | if (objagg_obj->parent) | ||
55 | objagg_obj->parent->stats.delta_user_count++; | ||
56 | } | ||
57 | |||
58 | static void objagg_obj_stats_dec(struct objagg_obj *objagg_obj) | ||
59 | { | ||
60 | objagg_obj->stats.user_count--; | ||
61 | objagg_obj->stats.delta_user_count--; | ||
62 | if (objagg_obj->parent) | ||
63 | objagg_obj->parent->stats.delta_user_count--; | ||
64 | } | ||
65 | |||
66 | static bool objagg_obj_is_root(const struct objagg_obj *objagg_obj) | ||
67 | { | ||
68 | /* Nesting is not supported, so we can use ->parent | ||
69 | * to figure out if the object is root. | ||
70 | */ | ||
71 | return !objagg_obj->parent; | ||
72 | } | ||
73 | |||
74 | /** | ||
75 | * objagg_obj_root_priv - obtains root private for an object | ||
76 | * @objagg_obj: objagg object instance | ||
77 | * | ||
78 | * Note: all locking must be provided by the caller. | ||
79 | * | ||
80 | * Either the object is root itself when the private is returned | ||
81 | * directly, or the parent is root and its private is returned | ||
82 | * instead. | ||
83 | * | ||
84 | * Returns a user private root pointer. | ||
85 | */ | ||
86 | const void *objagg_obj_root_priv(const struct objagg_obj *objagg_obj) | ||
87 | { | ||
88 | if (objagg_obj_is_root(objagg_obj)) | ||
89 | return objagg_obj->root_priv; | ||
90 | WARN_ON(!objagg_obj_is_root(objagg_obj->parent)); | ||
91 | return objagg_obj->parent->root_priv; | ||
92 | } | ||
93 | EXPORT_SYMBOL(objagg_obj_root_priv); | ||
94 | |||
95 | /** | ||
96 | * objagg_obj_delta_priv - obtains delta private for an object | ||
97 | * @objagg_obj: objagg object instance | ||
98 | * | ||
99 | * Note: all locking must be provided by the caller. | ||
100 | * | ||
101 | * Returns user private delta pointer or NULL in case the passed | ||
102 | * object is root. | ||
103 | */ | ||
104 | const void *objagg_obj_delta_priv(const struct objagg_obj *objagg_obj) | ||
105 | { | ||
106 | if (objagg_obj_is_root(objagg_obj)) | ||
107 | return NULL; | ||
108 | return objagg_obj->delta_priv; | ||
109 | } | ||
110 | EXPORT_SYMBOL(objagg_obj_delta_priv); | ||
111 | |||
112 | /** | ||
113 | * objagg_obj_raw - obtains object user private pointer | ||
114 | * @objagg_obj: objagg object instance | ||
115 | * | ||
116 | * Note: all locking must be provided by the caller. | ||
117 | * | ||
118 | * Returns user private pointer as was passed to objagg_obj_get() by "obj" arg. | ||
119 | */ | ||
120 | const void *objagg_obj_raw(const struct objagg_obj *objagg_obj) | ||
121 | { | ||
122 | return objagg_obj->obj; | ||
123 | } | ||
124 | EXPORT_SYMBOL(objagg_obj_raw); | ||
125 | |||
126 | static struct objagg_obj *objagg_obj_lookup(struct objagg *objagg, void *obj) | ||
127 | { | ||
128 | return rhashtable_lookup_fast(&objagg->obj_ht, obj, objagg->ht_params); | ||
129 | } | ||
130 | |||
131 | static int objagg_obj_parent_assign(struct objagg *objagg, | ||
132 | struct objagg_obj *objagg_obj, | ||
133 | struct objagg_obj *parent) | ||
134 | { | ||
135 | void *delta_priv; | ||
136 | |||
137 | delta_priv = objagg->ops->delta_create(objagg->priv, parent->obj, | ||
138 | objagg_obj->obj); | ||
139 | if (IS_ERR(delta_priv)) | ||
140 | return PTR_ERR(delta_priv); | ||
141 | |||
142 | /* User returned a delta private, that means that | ||
143 | * our object can be aggregated into the parent. | ||
144 | */ | ||
145 | objagg_obj->parent = parent; | ||
146 | objagg_obj->delta_priv = delta_priv; | ||
147 | objagg_obj_ref_inc(objagg_obj->parent); | ||
148 | trace_objagg_obj_parent_assign(objagg, objagg_obj, | ||
149 | parent, | ||
150 | parent->refcount); | ||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | static int objagg_obj_parent_lookup_assign(struct objagg *objagg, | ||
155 | struct objagg_obj *objagg_obj) | ||
156 | { | ||
157 | struct objagg_obj *objagg_obj_cur; | ||
158 | int err; | ||
159 | |||
160 | list_for_each_entry(objagg_obj_cur, &objagg->obj_list, list) { | ||
161 | /* Nesting is not supported. In case the object | ||
162 | * is not root, it cannot be assigned as parent. | ||
163 | */ | ||
164 | if (!objagg_obj_is_root(objagg_obj_cur)) | ||
165 | continue; | ||
166 | err = objagg_obj_parent_assign(objagg, objagg_obj, | ||
167 | objagg_obj_cur); | ||
168 | if (!err) | ||
169 | return 0; | ||
170 | } | ||
171 | return -ENOENT; | ||
172 | } | ||
173 | |||
174 | static void __objagg_obj_put(struct objagg *objagg, | ||
175 | struct objagg_obj *objagg_obj); | ||
176 | |||
177 | static void objagg_obj_parent_unassign(struct objagg *objagg, | ||
178 | struct objagg_obj *objagg_obj) | ||
179 | { | ||
180 | trace_objagg_obj_parent_unassign(objagg, objagg_obj, | ||
181 | objagg_obj->parent, | ||
182 | objagg_obj->parent->refcount); | ||
183 | objagg->ops->delta_destroy(objagg->priv, objagg_obj->delta_priv); | ||
184 | __objagg_obj_put(objagg, objagg_obj->parent); | ||
185 | } | ||
186 | |||
187 | static int objagg_obj_root_create(struct objagg *objagg, | ||
188 | struct objagg_obj *objagg_obj) | ||
189 | { | ||
190 | objagg_obj->root_priv = objagg->ops->root_create(objagg->priv, | ||
191 | objagg_obj->obj); | ||
192 | if (IS_ERR(objagg_obj->root_priv)) | ||
193 | return PTR_ERR(objagg_obj->root_priv); | ||
194 | |||
195 | trace_objagg_obj_root_create(objagg, objagg_obj); | ||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | static void objagg_obj_root_destroy(struct objagg *objagg, | ||
200 | struct objagg_obj *objagg_obj) | ||
201 | { | ||
202 | trace_objagg_obj_root_destroy(objagg, objagg_obj); | ||
203 | objagg->ops->root_destroy(objagg->priv, objagg_obj->root_priv); | ||
204 | } | ||
205 | |||
206 | static int objagg_obj_init(struct objagg *objagg, | ||
207 | struct objagg_obj *objagg_obj) | ||
208 | { | ||
209 | int err; | ||
210 | |||
211 | /* Try to find if the object can be aggregated under an existing one. */ | ||
212 | err = objagg_obj_parent_lookup_assign(objagg, objagg_obj); | ||
213 | if (!err) | ||
214 | return 0; | ||
215 | /* If aggregation is not possible, make the object a root. */ | ||
216 | return objagg_obj_root_create(objagg, objagg_obj); | ||
217 | } | ||
218 | |||
219 | static void objagg_obj_fini(struct objagg *objagg, | ||
220 | struct objagg_obj *objagg_obj) | ||
221 | { | ||
222 | if (!objagg_obj_is_root(objagg_obj)) | ||
223 | objagg_obj_parent_unassign(objagg, objagg_obj); | ||
224 | else | ||
225 | objagg_obj_root_destroy(objagg, objagg_obj); | ||
226 | } | ||
227 | |||
228 | static struct objagg_obj *objagg_obj_create(struct objagg *objagg, void *obj) | ||
229 | { | ||
230 | struct objagg_obj *objagg_obj; | ||
231 | int err; | ||
232 | |||
233 | objagg_obj = kzalloc(sizeof(*objagg_obj) + objagg->ops->obj_size, | ||
234 | GFP_KERNEL); | ||
235 | if (!objagg_obj) | ||
236 | return ERR_PTR(-ENOMEM); | ||
237 | objagg_obj_ref_inc(objagg_obj); | ||
238 | memcpy(objagg_obj->obj, obj, objagg->ops->obj_size); | ||
239 | |||
240 | err = objagg_obj_init(objagg, objagg_obj); | ||
241 | if (err) | ||
242 | goto err_obj_init; | ||
243 | |||
244 | err = rhashtable_insert_fast(&objagg->obj_ht, &objagg_obj->ht_node, | ||
245 | objagg->ht_params); | ||
246 | if (err) | ||
247 | goto err_ht_insert; | ||
248 | list_add(&objagg_obj->list, &objagg->obj_list); | ||
249 | objagg->obj_count++; | ||
250 | trace_objagg_obj_create(objagg, objagg_obj); | ||
251 | |||
252 | return objagg_obj; | ||
253 | |||
254 | err_ht_insert: | ||
255 | objagg_obj_fini(objagg, objagg_obj); | ||
256 | err_obj_init: | ||
257 | kfree(objagg_obj); | ||
258 | return ERR_PTR(err); | ||
259 | } | ||
260 | |||
261 | static struct objagg_obj *__objagg_obj_get(struct objagg *objagg, void *obj) | ||
262 | { | ||
263 | struct objagg_obj *objagg_obj; | ||
264 | |||
265 | /* First, try to find the object exactly as user passed it, | ||
266 | * perhaps it is already in use. | ||
267 | */ | ||
268 | objagg_obj = objagg_obj_lookup(objagg, obj); | ||
269 | if (objagg_obj) { | ||
270 | objagg_obj_ref_inc(objagg_obj); | ||
271 | return objagg_obj; | ||
272 | } | ||
273 | |||
274 | return objagg_obj_create(objagg, obj); | ||
275 | } | ||
276 | |||
277 | /** | ||
278 | * objagg_obj_get - gets an object within objagg instance | ||
279 | * @objagg: objagg instance | ||
280 | * @obj: user-specific private object pointer | ||
281 | * | ||
282 | * Note: all locking must be provided by the caller. | ||
283 | * | ||
284 | * Size of the "obj" memory is specified in "objagg->ops". | ||
285 | * | ||
286 | * There are 3 main options this function wraps: | ||
287 | * 1) The object according to "obj" already exist. In that case | ||
288 | * the reference counter is incrementes and the object is returned. | ||
289 | * 2) The object does not exist, but it can be aggregated within | ||
290 | * another object. In that case, user ops->delta_create() is called | ||
291 | * to obtain delta data and a new object is created with returned | ||
292 | * user-delta private pointer. | ||
293 | * 3) The object does not exist and cannot be aggregated into | ||
294 | * any of the existing objects. In that case, user ops->root_create() | ||
295 | * is called to create the root and a new object is created with | ||
296 | * returned user-root private pointer. | ||
297 | * | ||
298 | * Returns a pointer to objagg object instance in case of success, | ||
299 | * otherwise it returns pointer error using ERR_PTR macro. | ||
300 | */ | ||
301 | struct objagg_obj *objagg_obj_get(struct objagg *objagg, void *obj) | ||
302 | { | ||
303 | struct objagg_obj *objagg_obj; | ||
304 | |||
305 | objagg_obj = __objagg_obj_get(objagg, obj); | ||
306 | if (IS_ERR(objagg_obj)) | ||
307 | return objagg_obj; | ||
308 | objagg_obj_stats_inc(objagg_obj); | ||
309 | trace_objagg_obj_get(objagg, objagg_obj, objagg_obj->refcount); | ||
310 | return objagg_obj; | ||
311 | } | ||
312 | EXPORT_SYMBOL(objagg_obj_get); | ||
313 | |||
314 | static void objagg_obj_destroy(struct objagg *objagg, | ||
315 | struct objagg_obj *objagg_obj) | ||
316 | { | ||
317 | trace_objagg_obj_destroy(objagg, objagg_obj); | ||
318 | --objagg->obj_count; | ||
319 | list_del(&objagg_obj->list); | ||
320 | rhashtable_remove_fast(&objagg->obj_ht, &objagg_obj->ht_node, | ||
321 | objagg->ht_params); | ||
322 | objagg_obj_fini(objagg, objagg_obj); | ||
323 | kfree(objagg_obj); | ||
324 | } | ||
325 | |||
326 | static void __objagg_obj_put(struct objagg *objagg, | ||
327 | struct objagg_obj *objagg_obj) | ||
328 | { | ||
329 | if (!objagg_obj_ref_dec(objagg_obj)) | ||
330 | objagg_obj_destroy(objagg, objagg_obj); | ||
331 | } | ||
332 | |||
333 | /** | ||
334 | * objagg_obj_put - puts an object within objagg instance | ||
335 | * @objagg: objagg instance | ||
336 | * @objagg_obj: objagg object instance | ||
337 | * | ||
338 | * Note: all locking must be provided by the caller. | ||
339 | * | ||
340 | * Symmetric to objagg_obj_get(). | ||
341 | */ | ||
342 | void objagg_obj_put(struct objagg *objagg, struct objagg_obj *objagg_obj) | ||
343 | { | ||
344 | trace_objagg_obj_put(objagg, objagg_obj, objagg_obj->refcount); | ||
345 | objagg_obj_stats_dec(objagg_obj); | ||
346 | __objagg_obj_put(objagg, objagg_obj); | ||
347 | } | ||
348 | EXPORT_SYMBOL(objagg_obj_put); | ||
349 | |||
350 | /** | ||
351 | * objagg_create - creates a new objagg instance | ||
352 | * @ops: user-specific callbacks | ||
353 | * @priv: pointer to a private data passed to the ops | ||
354 | * | ||
355 | * Note: all locking must be provided by the caller. | ||
356 | * | ||
357 | * The purpose of the library is to provide an infrastructure to | ||
358 | * aggregate user-specified objects. Library does not care about the type | ||
359 | * of the object. User fills-up ops which take care of the specific | ||
360 | * user object manipulation. | ||
361 | * | ||
362 | * As a very stupid example, consider integer numbers. For example | ||
363 | * number 8 as a root object. That can aggregate number 9 with delta 1, | ||
364 | * number 10 with delta 2, etc. This example is implemented as | ||
365 | * a part of a testing module in test_objagg.c file. | ||
366 | * | ||
367 | * Each objagg instance contains multiple trees. Each tree node is | ||
368 | * represented by "an object". In the current implementation there can be | ||
369 | * only roots and leafs nodes. Leaf nodes are called deltas. | ||
370 | * But in general, this can be easily extended for intermediate nodes. | ||
371 | * In that extension, a delta would be associated with all non-root | ||
372 | * nodes. | ||
373 | * | ||
374 | * Returns a pointer to newly created objagg instance in case of success, | ||
375 | * otherwise it returns pointer error using ERR_PTR macro. | ||
376 | */ | ||
377 | struct objagg *objagg_create(const struct objagg_ops *ops, void *priv) | ||
378 | { | ||
379 | struct objagg *objagg; | ||
380 | int err; | ||
381 | |||
382 | if (WARN_ON(!ops || !ops->root_create || !ops->root_destroy || | ||
383 | !ops->delta_create || !ops->delta_destroy)) | ||
384 | return ERR_PTR(-EINVAL); | ||
385 | objagg = kzalloc(sizeof(*objagg), GFP_KERNEL); | ||
386 | if (!objagg) | ||
387 | return ERR_PTR(-ENOMEM); | ||
388 | objagg->ops = ops; | ||
389 | objagg->priv = priv; | ||
390 | INIT_LIST_HEAD(&objagg->obj_list); | ||
391 | |||
392 | objagg->ht_params.key_len = ops->obj_size; | ||
393 | objagg->ht_params.key_offset = offsetof(struct objagg_obj, obj); | ||
394 | objagg->ht_params.head_offset = offsetof(struct objagg_obj, ht_node); | ||
395 | |||
396 | err = rhashtable_init(&objagg->obj_ht, &objagg->ht_params); | ||
397 | if (err) | ||
398 | goto err_rhashtable_init; | ||
399 | |||
400 | trace_objagg_create(objagg); | ||
401 | return objagg; | ||
402 | |||
403 | err_rhashtable_init: | ||
404 | kfree(objagg); | ||
405 | return ERR_PTR(err); | ||
406 | } | ||
407 | EXPORT_SYMBOL(objagg_create); | ||
408 | |||
409 | /** | ||
410 | * objagg_destroy - destroys a new objagg instance | ||
411 | * @objagg: objagg instance | ||
412 | * | ||
413 | * Note: all locking must be provided by the caller. | ||
414 | */ | ||
415 | void objagg_destroy(struct objagg *objagg) | ||
416 | { | ||
417 | trace_objagg_destroy(objagg); | ||
418 | WARN_ON(!list_empty(&objagg->obj_list)); | ||
419 | rhashtable_destroy(&objagg->obj_ht); | ||
420 | kfree(objagg); | ||
421 | } | ||
422 | EXPORT_SYMBOL(objagg_destroy); | ||
423 | |||
424 | static int objagg_stats_info_sort_cmp_func(const void *a, const void *b) | ||
425 | { | ||
426 | const struct objagg_obj_stats_info *stats_info1 = a; | ||
427 | const struct objagg_obj_stats_info *stats_info2 = b; | ||
428 | |||
429 | if (stats_info1->is_root != stats_info2->is_root) | ||
430 | return stats_info2->is_root - stats_info1->is_root; | ||
431 | if (stats_info1->stats.delta_user_count != | ||
432 | stats_info2->stats.delta_user_count) | ||
433 | return stats_info2->stats.delta_user_count - | ||
434 | stats_info1->stats.delta_user_count; | ||
435 | return stats_info2->stats.user_count - stats_info1->stats.user_count; | ||
436 | } | ||
437 | |||
438 | /** | ||
439 | * objagg_stats_get - obtains stats of the objagg instance | ||
440 | * @objagg: objagg instance | ||
441 | * | ||
442 | * Note: all locking must be provided by the caller. | ||
443 | * | ||
444 | * The returned structure contains statistics of all object | ||
445 | * currently in use, ordered by following rules: | ||
446 | * 1) Root objects are always on lower indexes than the rest. | ||
447 | * 2) Objects with higher delta user count are always on lower | ||
448 | * indexes. | ||
449 | * 3) In case more objects have the same delta user count, | ||
450 | * the objects are ordered by user count. | ||
451 | * | ||
452 | * Returns a pointer to stats instance in case of success, | ||
453 | * otherwise it returns pointer error using ERR_PTR macro. | ||
454 | */ | ||
455 | const struct objagg_stats *objagg_stats_get(struct objagg *objagg) | ||
456 | { | ||
457 | struct objagg_stats *objagg_stats; | ||
458 | struct objagg_obj *objagg_obj; | ||
459 | size_t alloc_size; | ||
460 | int i; | ||
461 | |||
462 | alloc_size = sizeof(*objagg_stats) + | ||
463 | sizeof(objagg_stats->stats_info[0]) * objagg->obj_count; | ||
464 | objagg_stats = kzalloc(alloc_size, GFP_KERNEL); | ||
465 | if (!objagg_stats) | ||
466 | return ERR_PTR(-ENOMEM); | ||
467 | |||
468 | i = 0; | ||
469 | list_for_each_entry(objagg_obj, &objagg->obj_list, list) { | ||
470 | memcpy(&objagg_stats->stats_info[i].stats, &objagg_obj->stats, | ||
471 | sizeof(objagg_stats->stats_info[0].stats)); | ||
472 | objagg_stats->stats_info[i].objagg_obj = objagg_obj; | ||
473 | objagg_stats->stats_info[i].is_root = | ||
474 | objagg_obj_is_root(objagg_obj); | ||
475 | i++; | ||
476 | } | ||
477 | objagg_stats->stats_info_count = i; | ||
478 | |||
479 | sort(objagg_stats->stats_info, objagg_stats->stats_info_count, | ||
480 | sizeof(struct objagg_obj_stats_info), | ||
481 | objagg_stats_info_sort_cmp_func, NULL); | ||
482 | |||
483 | return objagg_stats; | ||
484 | } | ||
485 | EXPORT_SYMBOL(objagg_stats_get); | ||
486 | |||
487 | /** | ||
488 | * objagg_stats_puts - puts stats of the objagg instance | ||
489 | * @objagg_stats: objagg instance stats | ||
490 | * | ||
491 | * Note: all locking must be provided by the caller. | ||
492 | */ | ||
493 | void objagg_stats_put(const struct objagg_stats *objagg_stats) | ||
494 | { | ||
495 | kfree(objagg_stats); | ||
496 | } | ||
497 | EXPORT_SYMBOL(objagg_stats_put); | ||
498 | |||
499 | MODULE_LICENSE("Dual BSD/GPL"); | ||
500 | MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>"); | ||
501 | MODULE_DESCRIPTION("Object aggregation manager"); | ||
diff --git a/lib/test_objagg.c b/lib/test_objagg.c new file mode 100644 index 000000000000..aac5d8e8800c --- /dev/null +++ b/lib/test_objagg.c | |||
@@ -0,0 +1,835 @@ | |||
1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 | ||
2 | /* Copyright (c) 2018 Mellanox Technologies. All rights reserved */ | ||
3 | |||
4 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
5 | |||
6 | #include <linux/kernel.h> | ||
7 | #include <linux/module.h> | ||
8 | #include <linux/slab.h> | ||
9 | #include <linux/random.h> | ||
10 | #include <linux/objagg.h> | ||
11 | |||
12 | struct tokey { | ||
13 | unsigned int id; | ||
14 | }; | ||
15 | |||
16 | #define NUM_KEYS 32 | ||
17 | |||
18 | static int key_id_index(unsigned int key_id) | ||
19 | { | ||
20 | if (key_id >= NUM_KEYS) { | ||
21 | WARN_ON(1); | ||
22 | return 0; | ||
23 | } | ||
24 | return key_id; | ||
25 | } | ||
26 | |||
27 | #define BUF_LEN 128 | ||
28 | |||
29 | struct world { | ||
30 | unsigned int root_count; | ||
31 | unsigned int delta_count; | ||
32 | char next_root_buf[BUF_LEN]; | ||
33 | struct objagg_obj *objagg_objs[NUM_KEYS]; | ||
34 | unsigned int key_refs[NUM_KEYS]; | ||
35 | }; | ||
36 | |||
37 | struct root { | ||
38 | struct tokey key; | ||
39 | char buf[BUF_LEN]; | ||
40 | }; | ||
41 | |||
42 | struct delta { | ||
43 | unsigned int key_id_diff; | ||
44 | }; | ||
45 | |||
46 | static struct objagg_obj *world_obj_get(struct world *world, | ||
47 | struct objagg *objagg, | ||
48 | unsigned int key_id) | ||
49 | { | ||
50 | struct objagg_obj *objagg_obj; | ||
51 | struct tokey key; | ||
52 | int err; | ||
53 | |||
54 | key.id = key_id; | ||
55 | objagg_obj = objagg_obj_get(objagg, &key); | ||
56 | if (IS_ERR(objagg_obj)) { | ||
57 | pr_err("Key %u: Failed to get object.\n", key_id); | ||
58 | return objagg_obj; | ||
59 | } | ||
60 | if (!world->key_refs[key_id_index(key_id)]) { | ||
61 | world->objagg_objs[key_id_index(key_id)] = objagg_obj; | ||
62 | } else if (world->objagg_objs[key_id_index(key_id)] != objagg_obj) { | ||
63 | pr_err("Key %u: God another object for the same key.\n", | ||
64 | key_id); | ||
65 | err = -EINVAL; | ||
66 | goto err_key_id_check; | ||
67 | } | ||
68 | world->key_refs[key_id_index(key_id)]++; | ||
69 | return objagg_obj; | ||
70 | |||
71 | err_key_id_check: | ||
72 | objagg_obj_put(objagg, objagg_obj); | ||
73 | return ERR_PTR(err); | ||
74 | } | ||
75 | |||
76 | static void world_obj_put(struct world *world, struct objagg *objagg, | ||
77 | unsigned int key_id) | ||
78 | { | ||
79 | struct objagg_obj *objagg_obj; | ||
80 | |||
81 | if (!world->key_refs[key_id_index(key_id)]) | ||
82 | return; | ||
83 | objagg_obj = world->objagg_objs[key_id_index(key_id)]; | ||
84 | objagg_obj_put(objagg, objagg_obj); | ||
85 | world->key_refs[key_id_index(key_id)]--; | ||
86 | } | ||
87 | |||
88 | #define MAX_KEY_ID_DIFF 5 | ||
89 | |||
90 | static void *delta_create(void *priv, void *parent_obj, void *obj) | ||
91 | { | ||
92 | struct tokey *parent_key = parent_obj; | ||
93 | struct world *world = priv; | ||
94 | struct tokey *key = obj; | ||
95 | int diff = key->id - parent_key->id; | ||
96 | struct delta *delta; | ||
97 | |||
98 | if (diff < 0 || diff > MAX_KEY_ID_DIFF) | ||
99 | return ERR_PTR(-EINVAL); | ||
100 | |||
101 | delta = kzalloc(sizeof(*delta), GFP_KERNEL); | ||
102 | if (!delta) | ||
103 | return ERR_PTR(-ENOMEM); | ||
104 | delta->key_id_diff = diff; | ||
105 | world->delta_count++; | ||
106 | return delta; | ||
107 | } | ||
108 | |||
109 | static void delta_destroy(void *priv, void *delta_priv) | ||
110 | { | ||
111 | struct delta *delta = delta_priv; | ||
112 | struct world *world = priv; | ||
113 | |||
114 | world->delta_count--; | ||
115 | kfree(delta); | ||
116 | } | ||
117 | |||
118 | static void *root_create(void *priv, void *obj) | ||
119 | { | ||
120 | struct world *world = priv; | ||
121 | struct tokey *key = obj; | ||
122 | struct root *root; | ||
123 | |||
124 | root = kzalloc(sizeof(*root), GFP_KERNEL); | ||
125 | if (!root) | ||
126 | return ERR_PTR(-ENOMEM); | ||
127 | memcpy(&root->key, key, sizeof(root->key)); | ||
128 | memcpy(root->buf, world->next_root_buf, sizeof(root->buf)); | ||
129 | world->root_count++; | ||
130 | return root; | ||
131 | } | ||
132 | |||
133 | static void root_destroy(void *priv, void *root_priv) | ||
134 | { | ||
135 | struct root *root = root_priv; | ||
136 | struct world *world = priv; | ||
137 | |||
138 | world->root_count--; | ||
139 | kfree(root); | ||
140 | } | ||
141 | |||
142 | static int test_nodelta_obj_get(struct world *world, struct objagg *objagg, | ||
143 | unsigned int key_id, bool should_create_root) | ||
144 | { | ||
145 | unsigned int orig_root_count = world->root_count; | ||
146 | struct objagg_obj *objagg_obj; | ||
147 | const struct root *root; | ||
148 | int err; | ||
149 | |||
150 | if (should_create_root) | ||
151 | prandom_bytes(world->next_root_buf, | ||
152 | sizeof(world->next_root_buf)); | ||
153 | |||
154 | objagg_obj = world_obj_get(world, objagg, key_id); | ||
155 | if (IS_ERR(objagg_obj)) { | ||
156 | pr_err("Key %u: Failed to get object.\n", key_id); | ||
157 | return PTR_ERR(objagg_obj); | ||
158 | } | ||
159 | if (should_create_root) { | ||
160 | if (world->root_count != orig_root_count + 1) { | ||
161 | pr_err("Key %u: Root was not created\n", key_id); | ||
162 | err = -EINVAL; | ||
163 | goto err_check_root_count; | ||
164 | } | ||
165 | } else { | ||
166 | if (world->root_count != orig_root_count) { | ||
167 | pr_err("Key %u: Root was incorrectly created\n", | ||
168 | key_id); | ||
169 | err = -EINVAL; | ||
170 | goto err_check_root_count; | ||
171 | } | ||
172 | } | ||
173 | root = objagg_obj_root_priv(objagg_obj); | ||
174 | if (root->key.id != key_id) { | ||
175 | pr_err("Key %u: Root has unexpected key id\n", key_id); | ||
176 | err = -EINVAL; | ||
177 | goto err_check_key_id; | ||
178 | } | ||
179 | if (should_create_root && | ||
180 | memcmp(world->next_root_buf, root->buf, sizeof(root->buf))) { | ||
181 | pr_err("Key %u: Buffer does not match the expected content\n", | ||
182 | key_id); | ||
183 | err = -EINVAL; | ||
184 | goto err_check_buf; | ||
185 | } | ||
186 | return 0; | ||
187 | |||
188 | err_check_buf: | ||
189 | err_check_key_id: | ||
190 | err_check_root_count: | ||
191 | objagg_obj_put(objagg, objagg_obj); | ||
192 | return err; | ||
193 | } | ||
194 | |||
195 | static int test_nodelta_obj_put(struct world *world, struct objagg *objagg, | ||
196 | unsigned int key_id, bool should_destroy_root) | ||
197 | { | ||
198 | unsigned int orig_root_count = world->root_count; | ||
199 | |||
200 | world_obj_put(world, objagg, key_id); | ||
201 | |||
202 | if (should_destroy_root) { | ||
203 | if (world->root_count != orig_root_count - 1) { | ||
204 | pr_err("Key %u: Root was not destroyed\n", key_id); | ||
205 | return -EINVAL; | ||
206 | } | ||
207 | } else { | ||
208 | if (world->root_count != orig_root_count) { | ||
209 | pr_err("Key %u: Root was incorrectly destroyed\n", | ||
210 | key_id); | ||
211 | return -EINVAL; | ||
212 | } | ||
213 | } | ||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | static int check_stats_zero(struct objagg *objagg) | ||
218 | { | ||
219 | const struct objagg_stats *stats; | ||
220 | int err = 0; | ||
221 | |||
222 | stats = objagg_stats_get(objagg); | ||
223 | if (IS_ERR(stats)) | ||
224 | return PTR_ERR(stats); | ||
225 | |||
226 | if (stats->stats_info_count != 0) { | ||
227 | pr_err("Stats: Object count is not zero while it should be\n"); | ||
228 | err = -EINVAL; | ||
229 | } | ||
230 | |||
231 | objagg_stats_put(stats); | ||
232 | return err; | ||
233 | } | ||
234 | |||
235 | static int check_stats_nodelta(struct objagg *objagg) | ||
236 | { | ||
237 | const struct objagg_stats *stats; | ||
238 | int i; | ||
239 | int err; | ||
240 | |||
241 | stats = objagg_stats_get(objagg); | ||
242 | if (IS_ERR(stats)) | ||
243 | return PTR_ERR(stats); | ||
244 | |||
245 | if (stats->stats_info_count != NUM_KEYS) { | ||
246 | pr_err("Stats: Unexpected object count (%u expected, %u returned)\n", | ||
247 | NUM_KEYS, stats->stats_info_count); | ||
248 | err = -EINVAL; | ||
249 | goto stats_put; | ||
250 | } | ||
251 | |||
252 | for (i = 0; i < stats->stats_info_count; i++) { | ||
253 | if (stats->stats_info[i].stats.user_count != 2) { | ||
254 | pr_err("Stats: incorrect user count\n"); | ||
255 | err = -EINVAL; | ||
256 | goto stats_put; | ||
257 | } | ||
258 | if (stats->stats_info[i].stats.delta_user_count != 2) { | ||
259 | pr_err("Stats: incorrect delta user count\n"); | ||
260 | err = -EINVAL; | ||
261 | goto stats_put; | ||
262 | } | ||
263 | } | ||
264 | err = 0; | ||
265 | |||
266 | stats_put: | ||
267 | objagg_stats_put(stats); | ||
268 | return err; | ||
269 | } | ||
270 | |||
271 | static void *delta_create_dummy(void *priv, void *parent_obj, void *obj) | ||
272 | { | ||
273 | return ERR_PTR(-EOPNOTSUPP); | ||
274 | } | ||
275 | |||
276 | static void delta_destroy_dummy(void *priv, void *delta_priv) | ||
277 | { | ||
278 | } | ||
279 | |||
280 | static const struct objagg_ops nodelta_ops = { | ||
281 | .obj_size = sizeof(struct tokey), | ||
282 | .delta_create = delta_create_dummy, | ||
283 | .delta_destroy = delta_destroy_dummy, | ||
284 | .root_create = root_create, | ||
285 | .root_destroy = root_destroy, | ||
286 | }; | ||
287 | |||
288 | static int test_nodelta(void) | ||
289 | { | ||
290 | struct world world = {}; | ||
291 | struct objagg *objagg; | ||
292 | int i; | ||
293 | int err; | ||
294 | |||
295 | objagg = objagg_create(&nodelta_ops, &world); | ||
296 | if (IS_ERR(objagg)) | ||
297 | return PTR_ERR(objagg); | ||
298 | |||
299 | err = check_stats_zero(objagg); | ||
300 | if (err) | ||
301 | goto err_stats_first_zero; | ||
302 | |||
303 | /* First round of gets, the root objects should be created */ | ||
304 | for (i = 0; i < NUM_KEYS; i++) { | ||
305 | err = test_nodelta_obj_get(&world, objagg, i, true); | ||
306 | if (err) | ||
307 | goto err_obj_first_get; | ||
308 | } | ||
309 | |||
310 | /* Do the second round of gets, all roots are already created, | ||
311 | * make sure that no new root is created | ||
312 | */ | ||
313 | for (i = 0; i < NUM_KEYS; i++) { | ||
314 | err = test_nodelta_obj_get(&world, objagg, i, false); | ||
315 | if (err) | ||
316 | goto err_obj_second_get; | ||
317 | } | ||
318 | |||
319 | err = check_stats_nodelta(objagg); | ||
320 | if (err) | ||
321 | goto err_stats_nodelta; | ||
322 | |||
323 | for (i = NUM_KEYS - 1; i >= 0; i--) { | ||
324 | err = test_nodelta_obj_put(&world, objagg, i, false); | ||
325 | if (err) | ||
326 | goto err_obj_first_put; | ||
327 | } | ||
328 | for (i = NUM_KEYS - 1; i >= 0; i--) { | ||
329 | err = test_nodelta_obj_put(&world, objagg, i, true); | ||
330 | if (err) | ||
331 | goto err_obj_second_put; | ||
332 | } | ||
333 | |||
334 | err = check_stats_zero(objagg); | ||
335 | if (err) | ||
336 | goto err_stats_second_zero; | ||
337 | |||
338 | objagg_destroy(objagg); | ||
339 | return 0; | ||
340 | |||
341 | err_stats_nodelta: | ||
342 | err_obj_first_put: | ||
343 | err_obj_second_get: | ||
344 | for (i--; i >= 0; i--) | ||
345 | world_obj_put(&world, objagg, i); | ||
346 | |||
347 | i = NUM_KEYS; | ||
348 | err_obj_first_get: | ||
349 | err_obj_second_put: | ||
350 | for (i--; i >= 0; i--) | ||
351 | world_obj_put(&world, objagg, i); | ||
352 | err_stats_first_zero: | ||
353 | err_stats_second_zero: | ||
354 | objagg_destroy(objagg); | ||
355 | return err; | ||
356 | } | ||
357 | |||
358 | static const struct objagg_ops delta_ops = { | ||
359 | .obj_size = sizeof(struct tokey), | ||
360 | .delta_create = delta_create, | ||
361 | .delta_destroy = delta_destroy, | ||
362 | .root_create = root_create, | ||
363 | .root_destroy = root_destroy, | ||
364 | }; | ||
365 | |||
366 | enum action { | ||
367 | ACTION_GET, | ||
368 | ACTION_PUT, | ||
369 | }; | ||
370 | |||
371 | enum expect_delta { | ||
372 | EXPECT_DELTA_SAME, | ||
373 | EXPECT_DELTA_INC, | ||
374 | EXPECT_DELTA_DEC, | ||
375 | }; | ||
376 | |||
377 | enum expect_root { | ||
378 | EXPECT_ROOT_SAME, | ||
379 | EXPECT_ROOT_INC, | ||
380 | EXPECT_ROOT_DEC, | ||
381 | }; | ||
382 | |||
383 | struct expect_stats_info { | ||
384 | struct objagg_obj_stats stats; | ||
385 | bool is_root; | ||
386 | unsigned int key_id; | ||
387 | }; | ||
388 | |||
389 | struct expect_stats { | ||
390 | unsigned int info_count; | ||
391 | struct expect_stats_info info[NUM_KEYS]; | ||
392 | }; | ||
393 | |||
394 | struct action_item { | ||
395 | unsigned int key_id; | ||
396 | enum action action; | ||
397 | enum expect_delta expect_delta; | ||
398 | enum expect_root expect_root; | ||
399 | struct expect_stats expect_stats; | ||
400 | }; | ||
401 | |||
402 | #define EXPECT_STATS(count, ...) \ | ||
403 | { \ | ||
404 | .info_count = count, \ | ||
405 | .info = { __VA_ARGS__ } \ | ||
406 | } | ||
407 | |||
408 | #define ROOT(key_id, user_count, delta_user_count) \ | ||
409 | {{user_count, delta_user_count}, true, key_id} | ||
410 | |||
411 | #define DELTA(key_id, user_count) \ | ||
412 | {{user_count, user_count}, false, key_id} | ||
413 | |||
414 | static const struct action_item action_items[] = { | ||
415 | { | ||
416 | 1, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_INC, | ||
417 | EXPECT_STATS(1, ROOT(1, 1, 1)), | ||
418 | }, /* r: 1 d: */ | ||
419 | { | ||
420 | 7, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_INC, | ||
421 | EXPECT_STATS(2, ROOT(1, 1, 1), ROOT(7, 1, 1)), | ||
422 | }, /* r: 1, 7 d: */ | ||
423 | { | ||
424 | 3, ACTION_GET, EXPECT_DELTA_INC, EXPECT_ROOT_SAME, | ||
425 | EXPECT_STATS(3, ROOT(1, 1, 2), ROOT(7, 1, 1), | ||
426 | DELTA(3, 1)), | ||
427 | }, /* r: 1, 7 d: 3^1 */ | ||
428 | { | ||
429 | 5, ACTION_GET, EXPECT_DELTA_INC, EXPECT_ROOT_SAME, | ||
430 | EXPECT_STATS(4, ROOT(1, 1, 3), ROOT(7, 1, 1), | ||
431 | DELTA(3, 1), DELTA(5, 1)), | ||
432 | }, /* r: 1, 7 d: 3^1, 5^1 */ | ||
433 | { | ||
434 | 3, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME, | ||
435 | EXPECT_STATS(4, ROOT(1, 1, 4), ROOT(7, 1, 1), | ||
436 | DELTA(3, 2), DELTA(5, 1)), | ||
437 | }, /* r: 1, 7 d: 3^1, 3^1, 5^1 */ | ||
438 | { | ||
439 | 1, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME, | ||
440 | EXPECT_STATS(4, ROOT(1, 2, 5), ROOT(7, 1, 1), | ||
441 | DELTA(3, 2), DELTA(5, 1)), | ||
442 | }, /* r: 1, 1, 7 d: 3^1, 3^1, 5^1 */ | ||
443 | { | ||
444 | 30, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_INC, | ||
445 | EXPECT_STATS(5, ROOT(1, 2, 5), ROOT(7, 1, 1), ROOT(30, 1, 1), | ||
446 | DELTA(3, 2), DELTA(5, 1)), | ||
447 | }, /* r: 1, 1, 7, 30 d: 3^1, 3^1, 5^1 */ | ||
448 | { | ||
449 | 8, ACTION_GET, EXPECT_DELTA_INC, EXPECT_ROOT_SAME, | ||
450 | EXPECT_STATS(6, ROOT(1, 2, 5), ROOT(7, 1, 2), ROOT(30, 1, 1), | ||
451 | DELTA(3, 2), DELTA(5, 1), DELTA(8, 1)), | ||
452 | }, /* r: 1, 1, 7, 30 d: 3^1, 3^1, 5^1, 8^7 */ | ||
453 | { | ||
454 | 8, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME, | ||
455 | EXPECT_STATS(6, ROOT(1, 2, 5), ROOT(7, 1, 3), ROOT(30, 1, 1), | ||
456 | DELTA(3, 2), DELTA(8, 2), DELTA(5, 1)), | ||
457 | }, /* r: 1, 1, 7, 30 d: 3^1, 3^1, 5^1, 8^7, 8^7 */ | ||
458 | { | ||
459 | 3, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME, | ||
460 | EXPECT_STATS(6, ROOT(1, 2, 4), ROOT(7, 1, 3), ROOT(30, 1, 1), | ||
461 | DELTA(8, 2), DELTA(3, 1), DELTA(5, 1)), | ||
462 | }, /* r: 1, 1, 7, 30 d: 3^1, 5^1, 8^7, 8^7 */ | ||
463 | { | ||
464 | 3, ACTION_PUT, EXPECT_DELTA_DEC, EXPECT_ROOT_SAME, | ||
465 | EXPECT_STATS(5, ROOT(1, 2, 3), ROOT(7, 1, 3), ROOT(30, 1, 1), | ||
466 | DELTA(8, 2), DELTA(5, 1)), | ||
467 | }, /* r: 1, 1, 7, 30 d: 5^1, 8^7, 8^7 */ | ||
468 | { | ||
469 | 1, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME, | ||
470 | EXPECT_STATS(5, ROOT(7, 1, 3), ROOT(1, 1, 2), ROOT(30, 1, 1), | ||
471 | DELTA(8, 2), DELTA(5, 1)), | ||
472 | }, /* r: 1, 7, 30 d: 5^1, 8^7, 8^7 */ | ||
473 | { | ||
474 | 1, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME, | ||
475 | EXPECT_STATS(5, ROOT(7, 1, 3), ROOT(30, 1, 1), ROOT(1, 0, 1), | ||
476 | DELTA(8, 2), DELTA(5, 1)), | ||
477 | }, /* r: 7, 30 d: 5^1, 8^7, 8^7 */ | ||
478 | { | ||
479 | 5, ACTION_PUT, EXPECT_DELTA_DEC, EXPECT_ROOT_DEC, | ||
480 | EXPECT_STATS(3, ROOT(7, 1, 3), ROOT(30, 1, 1), | ||
481 | DELTA(8, 2)), | ||
482 | }, /* r: 7, 30 d: 8^7, 8^7 */ | ||
483 | { | ||
484 | 5, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_INC, | ||
485 | EXPECT_STATS(4, ROOT(7, 1, 3), ROOT(30, 1, 1), ROOT(5, 1, 1), | ||
486 | DELTA(8, 2)), | ||
487 | }, /* r: 7, 30, 5 d: 8^7, 8^7 */ | ||
488 | { | ||
489 | 6, ACTION_GET, EXPECT_DELTA_INC, EXPECT_ROOT_SAME, | ||
490 | EXPECT_STATS(5, ROOT(7, 1, 3), ROOT(5, 1, 2), ROOT(30, 1, 1), | ||
491 | DELTA(8, 2), DELTA(6, 1)), | ||
492 | }, /* r: 7, 30, 5 d: 8^7, 8^7, 6^5 */ | ||
493 | { | ||
494 | 8, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME, | ||
495 | EXPECT_STATS(5, ROOT(7, 1, 4), ROOT(5, 1, 2), ROOT(30, 1, 1), | ||
496 | DELTA(8, 3), DELTA(6, 1)), | ||
497 | }, /* r: 7, 30, 5 d: 8^7, 8^7, 8^7, 6^5 */ | ||
498 | { | ||
499 | 8, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME, | ||
500 | EXPECT_STATS(5, ROOT(7, 1, 3), ROOT(5, 1, 2), ROOT(30, 1, 1), | ||
501 | DELTA(8, 2), DELTA(6, 1)), | ||
502 | }, /* r: 7, 30, 5 d: 8^7, 8^7, 6^5 */ | ||
503 | { | ||
504 | 8, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME, | ||
505 | EXPECT_STATS(5, ROOT(7, 1, 2), ROOT(5, 1, 2), ROOT(30, 1, 1), | ||
506 | DELTA(8, 1), DELTA(6, 1)), | ||
507 | }, /* r: 7, 30, 5 d: 8^7, 6^5 */ | ||
508 | { | ||
509 | 8, ACTION_PUT, EXPECT_DELTA_DEC, EXPECT_ROOT_SAME, | ||
510 | EXPECT_STATS(4, ROOT(5, 1, 2), ROOT(7, 1, 1), ROOT(30, 1, 1), | ||
511 | DELTA(6, 1)), | ||
512 | }, /* r: 7, 30, 5 d: 6^5 */ | ||
513 | { | ||
514 | 8, ACTION_GET, EXPECT_DELTA_INC, EXPECT_ROOT_SAME, | ||
515 | EXPECT_STATS(5, ROOT(5, 1, 3), ROOT(7, 1, 1), ROOT(30, 1, 1), | ||
516 | DELTA(6, 1), DELTA(8, 1)), | ||
517 | }, /* r: 7, 30, 5 d: 6^5, 8^5 */ | ||
518 | { | ||
519 | 7, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_DEC, | ||
520 | EXPECT_STATS(4, ROOT(5, 1, 3), ROOT(30, 1, 1), | ||
521 | DELTA(6, 1), DELTA(8, 1)), | ||
522 | }, /* r: 30, 5 d: 6^5, 8^5 */ | ||
523 | { | ||
524 | 30, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_DEC, | ||
525 | EXPECT_STATS(3, ROOT(5, 1, 3), | ||
526 | DELTA(6, 1), DELTA(8, 1)), | ||
527 | }, /* r: 5 d: 6^5, 8^5 */ | ||
528 | { | ||
529 | 5, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME, | ||
530 | EXPECT_STATS(3, ROOT(5, 0, 2), | ||
531 | DELTA(6, 1), DELTA(8, 1)), | ||
532 | }, /* r: d: 6^5, 8^5 */ | ||
533 | { | ||
534 | 6, ACTION_PUT, EXPECT_DELTA_DEC, EXPECT_ROOT_SAME, | ||
535 | EXPECT_STATS(2, ROOT(5, 0, 1), | ||
536 | DELTA(8, 1)), | ||
537 | }, /* r: d: 6^5 */ | ||
538 | { | ||
539 | 8, ACTION_PUT, EXPECT_DELTA_DEC, EXPECT_ROOT_DEC, | ||
540 | EXPECT_STATS(0, ), | ||
541 | }, /* r: d: */ | ||
542 | }; | ||
543 | |||
544 | static int check_expect(struct world *world, | ||
545 | const struct action_item *action_item, | ||
546 | unsigned int orig_delta_count, | ||
547 | unsigned int orig_root_count) | ||
548 | { | ||
549 | unsigned int key_id = action_item->key_id; | ||
550 | |||
551 | switch (action_item->expect_delta) { | ||
552 | case EXPECT_DELTA_SAME: | ||
553 | if (orig_delta_count != world->delta_count) { | ||
554 | pr_err("Key %u: Delta count changed while expected to remain the same.\n", | ||
555 | key_id); | ||
556 | return -EINVAL; | ||
557 | } | ||
558 | break; | ||
559 | case EXPECT_DELTA_INC: | ||
560 | if (WARN_ON(action_item->action == ACTION_PUT)) | ||
561 | return -EINVAL; | ||
562 | if (orig_delta_count + 1 != world->delta_count) { | ||
563 | pr_err("Key %u: Delta count was not incremented.\n", | ||
564 | key_id); | ||
565 | return -EINVAL; | ||
566 | } | ||
567 | break; | ||
568 | case EXPECT_DELTA_DEC: | ||
569 | if (WARN_ON(action_item->action == ACTION_GET)) | ||
570 | return -EINVAL; | ||
571 | if (orig_delta_count - 1 != world->delta_count) { | ||
572 | pr_err("Key %u: Delta count was not decremented.\n", | ||
573 | key_id); | ||
574 | return -EINVAL; | ||
575 | } | ||
576 | break; | ||
577 | } | ||
578 | |||
579 | switch (action_item->expect_root) { | ||
580 | case EXPECT_ROOT_SAME: | ||
581 | if (orig_root_count != world->root_count) { | ||
582 | pr_err("Key %u: Root count changed while expected to remain the same.\n", | ||
583 | key_id); | ||
584 | return -EINVAL; | ||
585 | } | ||
586 | break; | ||
587 | case EXPECT_ROOT_INC: | ||
588 | if (WARN_ON(action_item->action == ACTION_PUT)) | ||
589 | return -EINVAL; | ||
590 | if (orig_root_count + 1 != world->root_count) { | ||
591 | pr_err("Key %u: Root count was not incremented.\n", | ||
592 | key_id); | ||
593 | return -EINVAL; | ||
594 | } | ||
595 | break; | ||
596 | case EXPECT_ROOT_DEC: | ||
597 | if (WARN_ON(action_item->action == ACTION_GET)) | ||
598 | return -EINVAL; | ||
599 | if (orig_root_count - 1 != world->root_count) { | ||
600 | pr_err("Key %u: Root count was not decremented.\n", | ||
601 | key_id); | ||
602 | return -EINVAL; | ||
603 | } | ||
604 | } | ||
605 | |||
606 | return 0; | ||
607 | } | ||
608 | |||
609 | static unsigned int obj_to_key_id(struct objagg_obj *objagg_obj) | ||
610 | { | ||
611 | const struct tokey *root_key; | ||
612 | const struct delta *delta; | ||
613 | unsigned int key_id; | ||
614 | |||
615 | root_key = objagg_obj_root_priv(objagg_obj); | ||
616 | key_id = root_key->id; | ||
617 | delta = objagg_obj_delta_priv(objagg_obj); | ||
618 | if (delta) | ||
619 | key_id += delta->key_id_diff; | ||
620 | return key_id; | ||
621 | } | ||
622 | |||
623 | static int | ||
624 | check_expect_stats_nums(const struct objagg_obj_stats_info *stats_info, | ||
625 | const struct expect_stats_info *expect_stats_info, | ||
626 | const char **errmsg) | ||
627 | { | ||
628 | if (stats_info->is_root != expect_stats_info->is_root) { | ||
629 | if (errmsg) | ||
630 | *errmsg = "Incorrect root/delta indication"; | ||
631 | return -EINVAL; | ||
632 | } | ||
633 | if (stats_info->stats.user_count != | ||
634 | expect_stats_info->stats.user_count) { | ||
635 | if (errmsg) | ||
636 | *errmsg = "Incorrect user count"; | ||
637 | return -EINVAL; | ||
638 | } | ||
639 | if (stats_info->stats.delta_user_count != | ||
640 | expect_stats_info->stats.delta_user_count) { | ||
641 | if (errmsg) | ||
642 | *errmsg = "Incorrect delta user count"; | ||
643 | return -EINVAL; | ||
644 | } | ||
645 | return 0; | ||
646 | } | ||
647 | |||
648 | static int | ||
649 | check_expect_stats_key_id(const struct objagg_obj_stats_info *stats_info, | ||
650 | const struct expect_stats_info *expect_stats_info, | ||
651 | const char **errmsg) | ||
652 | { | ||
653 | if (obj_to_key_id(stats_info->objagg_obj) != | ||
654 | expect_stats_info->key_id) { | ||
655 | if (errmsg) | ||
656 | *errmsg = "incorrect key id"; | ||
657 | return -EINVAL; | ||
658 | } | ||
659 | return 0; | ||
660 | } | ||
661 | |||
662 | static int check_expect_stats_neigh(const struct objagg_stats *stats, | ||
663 | const struct expect_stats *expect_stats, | ||
664 | int pos) | ||
665 | { | ||
666 | int i; | ||
667 | int err; | ||
668 | |||
669 | for (i = pos - 1; i >= 0; i--) { | ||
670 | err = check_expect_stats_nums(&stats->stats_info[i], | ||
671 | &expect_stats->info[pos], NULL); | ||
672 | if (err) | ||
673 | break; | ||
674 | err = check_expect_stats_key_id(&stats->stats_info[i], | ||
675 | &expect_stats->info[pos], NULL); | ||
676 | if (!err) | ||
677 | return 0; | ||
678 | } | ||
679 | for (i = pos + 1; i < stats->stats_info_count; i++) { | ||
680 | err = check_expect_stats_nums(&stats->stats_info[i], | ||
681 | &expect_stats->info[pos], NULL); | ||
682 | if (err) | ||
683 | break; | ||
684 | err = check_expect_stats_key_id(&stats->stats_info[i], | ||
685 | &expect_stats->info[pos], NULL); | ||
686 | if (!err) | ||
687 | return 0; | ||
688 | } | ||
689 | return -EINVAL; | ||
690 | } | ||
691 | |||
692 | static int __check_expect_stats(const struct objagg_stats *stats, | ||
693 | const struct expect_stats *expect_stats, | ||
694 | const char **errmsg) | ||
695 | { | ||
696 | int i; | ||
697 | int err; | ||
698 | |||
699 | if (stats->stats_info_count != expect_stats->info_count) { | ||
700 | *errmsg = "Unexpected object count"; | ||
701 | return -EINVAL; | ||
702 | } | ||
703 | |||
704 | for (i = 0; i < stats->stats_info_count; i++) { | ||
705 | err = check_expect_stats_nums(&stats->stats_info[i], | ||
706 | &expect_stats->info[i], errmsg); | ||
707 | if (err) | ||
708 | return err; | ||
709 | err = check_expect_stats_key_id(&stats->stats_info[i], | ||
710 | &expect_stats->info[i], errmsg); | ||
711 | if (err) { | ||
712 | /* It is possible that one of the neighbor stats with | ||
713 | * same numbers have the correct key id, so check it | ||
714 | */ | ||
715 | err = check_expect_stats_neigh(stats, expect_stats, i); | ||
716 | if (err) | ||
717 | return err; | ||
718 | } | ||
719 | } | ||
720 | return 0; | ||
721 | } | ||
722 | |||
723 | static int check_expect_stats(struct objagg *objagg, | ||
724 | const struct expect_stats *expect_stats, | ||
725 | const char **errmsg) | ||
726 | { | ||
727 | const struct objagg_stats *stats; | ||
728 | int err; | ||
729 | |||
730 | stats = objagg_stats_get(objagg); | ||
731 | if (IS_ERR(stats)) | ||
732 | return PTR_ERR(stats); | ||
733 | err = __check_expect_stats(stats, expect_stats, errmsg); | ||
734 | objagg_stats_put(stats); | ||
735 | return err; | ||
736 | } | ||
737 | |||
738 | static int test_delta_action_item(struct world *world, | ||
739 | struct objagg *objagg, | ||
740 | const struct action_item *action_item, | ||
741 | bool inverse) | ||
742 | { | ||
743 | unsigned int orig_delta_count = world->delta_count; | ||
744 | unsigned int orig_root_count = world->root_count; | ||
745 | unsigned int key_id = action_item->key_id; | ||
746 | enum action action = action_item->action; | ||
747 | struct objagg_obj *objagg_obj; | ||
748 | const char *errmsg; | ||
749 | int err; | ||
750 | |||
751 | if (inverse) | ||
752 | action = action == ACTION_GET ? ACTION_PUT : ACTION_GET; | ||
753 | |||
754 | switch (action) { | ||
755 | case ACTION_GET: | ||
756 | objagg_obj = world_obj_get(world, objagg, key_id); | ||
757 | if (IS_ERR(objagg_obj)) | ||
758 | return PTR_ERR(objagg_obj); | ||
759 | break; | ||
760 | case ACTION_PUT: | ||
761 | world_obj_put(world, objagg, key_id); | ||
762 | break; | ||
763 | } | ||
764 | |||
765 | if (inverse) | ||
766 | return 0; | ||
767 | err = check_expect(world, action_item, | ||
768 | orig_delta_count, orig_root_count); | ||
769 | if (err) | ||
770 | goto errout; | ||
771 | |||
772 | err = check_expect_stats(objagg, &action_item->expect_stats, &errmsg); | ||
773 | if (err) { | ||
774 | pr_err("Key %u: Stats: %s\n", action_item->key_id, errmsg); | ||
775 | goto errout; | ||
776 | } | ||
777 | |||
778 | return 0; | ||
779 | |||
780 | errout: | ||
781 | /* This can only happen when action is not inversed. | ||
782 | * So in case of an error, cleanup by doing inverse action. | ||
783 | */ | ||
784 | test_delta_action_item(world, objagg, action_item, true); | ||
785 | return err; | ||
786 | } | ||
787 | |||
788 | static int test_delta(void) | ||
789 | { | ||
790 | struct world world = {}; | ||
791 | struct objagg *objagg; | ||
792 | int i; | ||
793 | int err; | ||
794 | |||
795 | objagg = objagg_create(&delta_ops, &world); | ||
796 | if (IS_ERR(objagg)) | ||
797 | return PTR_ERR(objagg); | ||
798 | |||
799 | for (i = 0; i < ARRAY_SIZE(action_items); i++) { | ||
800 | err = test_delta_action_item(&world, objagg, | ||
801 | &action_items[i], false); | ||
802 | if (err) | ||
803 | goto err_do_action_item; | ||
804 | } | ||
805 | |||
806 | objagg_destroy(objagg); | ||
807 | return 0; | ||
808 | |||
809 | err_do_action_item: | ||
810 | for (i--; i >= 0; i--) | ||
811 | test_delta_action_item(&world, objagg, &action_items[i], true); | ||
812 | |||
813 | objagg_destroy(objagg); | ||
814 | return err; | ||
815 | } | ||
816 | |||
817 | static int __init test_objagg_init(void) | ||
818 | { | ||
819 | int err; | ||
820 | |||
821 | err = test_nodelta(); | ||
822 | if (err) | ||
823 | return err; | ||
824 | return test_delta(); | ||
825 | } | ||
826 | |||
827 | static void __exit test_objagg_exit(void) | ||
828 | { | ||
829 | } | ||
830 | |||
831 | module_init(test_objagg_init); | ||
832 | module_exit(test_objagg_exit); | ||
833 | MODULE_LICENSE("Dual BSD/GPL"); | ||
834 | MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>"); | ||
835 | MODULE_DESCRIPTION("Test module for objagg"); | ||