1bcea3f96SSteven Rostedt (VMware) // SPDX-License-Identifier: GPL-2.0
208d43a5fSTom Zanussi /*
308d43a5fSTom Zanussi * tracing_map - lock-free map for tracing
408d43a5fSTom Zanussi *
508d43a5fSTom Zanussi * Copyright (C) 2015 Tom Zanussi <[email protected]>
608d43a5fSTom Zanussi *
708d43a5fSTom Zanussi * tracing_map implementation inspired by lock-free map algorithms
808d43a5fSTom Zanussi * originated by Dr. Cliff Click:
908d43a5fSTom Zanussi *
1008d43a5fSTom Zanussi * http://www.azulsystems.com/blog/cliff/2007-03-26-non-blocking-hashtable
1108d43a5fSTom Zanussi * http://www.azulsystems.com/events/javaone_2007/2007_LockFreeHash.pdf
1208d43a5fSTom Zanussi */
1308d43a5fSTom Zanussi
1408d43a5fSTom Zanussi #include <linux/vmalloc.h>
1508d43a5fSTom Zanussi #include <linux/jhash.h>
1608d43a5fSTom Zanussi #include <linux/slab.h>
1708d43a5fSTom Zanussi #include <linux/sort.h>
18f25667e5SChen Jun #include <linux/kmemleak.h>
1908d43a5fSTom Zanussi
2008d43a5fSTom Zanussi #include "tracing_map.h"
2108d43a5fSTom Zanussi #include "trace.h"
2208d43a5fSTom Zanussi
2308d43a5fSTom Zanussi /*
2408d43a5fSTom Zanussi * NOTE: For a detailed description of the data structures used by
2508d43a5fSTom Zanussi * these functions (such as tracing_map_elt) please see the overview
2608d43a5fSTom Zanussi * of tracing_map data structures at the beginning of tracing_map.h.
2708d43a5fSTom Zanussi */
2808d43a5fSTom Zanussi
2908d43a5fSTom Zanussi /**
3008d43a5fSTom Zanussi * tracing_map_update_sum - Add a value to a tracing_map_elt's sum field
3108d43a5fSTom Zanussi * @elt: The tracing_map_elt
3208d43a5fSTom Zanussi * @i: The index of the given sum associated with the tracing_map_elt
3308d43a5fSTom Zanussi * @n: The value to add to the sum
3408d43a5fSTom Zanussi *
3508d43a5fSTom Zanussi * Add n to sum i associated with the specified tracing_map_elt
3608d43a5fSTom Zanussi * instance. The index i is the index returned by the call to
3708d43a5fSTom Zanussi * tracing_map_add_sum_field() when the tracing map was set up.
3808d43a5fSTom Zanussi */
tracing_map_update_sum(struct tracing_map_elt * elt,unsigned int i,u64 n)3908d43a5fSTom Zanussi void tracing_map_update_sum(struct tracing_map_elt *elt, unsigned int i, u64 n)
4008d43a5fSTom Zanussi {
4108d43a5fSTom Zanussi atomic64_add(n, &elt->fields[i].sum);
4208d43a5fSTom Zanussi }
4308d43a5fSTom Zanussi
4408d43a5fSTom Zanussi /**
4508d43a5fSTom Zanussi * tracing_map_read_sum - Return the value of a tracing_map_elt's sum field
4608d43a5fSTom Zanussi * @elt: The tracing_map_elt
4708d43a5fSTom Zanussi * @i: The index of the given sum associated with the tracing_map_elt
4808d43a5fSTom Zanussi *
4908d43a5fSTom Zanussi * Retrieve the value of the sum i associated with the specified
5008d43a5fSTom Zanussi * tracing_map_elt instance. The index i is the index returned by the
5108d43a5fSTom Zanussi * call to tracing_map_add_sum_field() when the tracing map was set
5208d43a5fSTom Zanussi * up.
5308d43a5fSTom Zanussi *
5408d43a5fSTom Zanussi * Return: The sum associated with field i for elt.
5508d43a5fSTom Zanussi */
tracing_map_read_sum(struct tracing_map_elt * elt,unsigned int i)5608d43a5fSTom Zanussi u64 tracing_map_read_sum(struct tracing_map_elt *elt, unsigned int i)
5708d43a5fSTom Zanussi {
5808d43a5fSTom Zanussi return (u64)atomic64_read(&elt->fields[i].sum);
5908d43a5fSTom Zanussi }
6008d43a5fSTom Zanussi
612734b629STom Zanussi /**
622734b629STom Zanussi * tracing_map_set_var - Assign a tracing_map_elt's variable field
632734b629STom Zanussi * @elt: The tracing_map_elt
642734b629STom Zanussi * @i: The index of the given variable associated with the tracing_map_elt
652734b629STom Zanussi * @n: The value to assign
662734b629STom Zanussi *
672734b629STom Zanussi * Assign n to variable i associated with the specified tracing_map_elt
682734b629STom Zanussi * instance. The index i is the index returned by the call to
692734b629STom Zanussi * tracing_map_add_var() when the tracing map was set up.
702734b629STom Zanussi */
tracing_map_set_var(struct tracing_map_elt * elt,unsigned int i,u64 n)712734b629STom Zanussi void tracing_map_set_var(struct tracing_map_elt *elt, unsigned int i, u64 n)
722734b629STom Zanussi {
732734b629STom Zanussi atomic64_set(&elt->vars[i], n);
742734b629STom Zanussi elt->var_set[i] = true;
752734b629STom Zanussi }
762734b629STom Zanussi
772734b629STom Zanussi /**
782734b629STom Zanussi * tracing_map_var_set - Return whether or not a variable has been set
792734b629STom Zanussi * @elt: The tracing_map_elt
802734b629STom Zanussi * @i: The index of the given variable associated with the tracing_map_elt
812734b629STom Zanussi *
822734b629STom Zanussi * Return true if the variable has been set, false otherwise. The
832734b629STom Zanussi * index i is the index returned by the call to tracing_map_add_var()
842734b629STom Zanussi * when the tracing map was set up.
852734b629STom Zanussi */
tracing_map_var_set(struct tracing_map_elt * elt,unsigned int i)862734b629STom Zanussi bool tracing_map_var_set(struct tracing_map_elt *elt, unsigned int i)
872734b629STom Zanussi {
882734b629STom Zanussi return elt->var_set[i];
892734b629STom Zanussi }
902734b629STom Zanussi
912734b629STom Zanussi /**
922734b629STom Zanussi * tracing_map_read_var - Return the value of a tracing_map_elt's variable field
932734b629STom Zanussi * @elt: The tracing_map_elt
942734b629STom Zanussi * @i: The index of the given variable associated with the tracing_map_elt
952734b629STom Zanussi *
962734b629STom Zanussi * Retrieve the value of the variable i associated with the specified
972734b629STom Zanussi * tracing_map_elt instance. The index i is the index returned by the
982734b629STom Zanussi * call to tracing_map_add_var() when the tracing map was set
992734b629STom Zanussi * up.
1002734b629STom Zanussi *
1012734b629STom Zanussi * Return: The variable value associated with field i for elt.
1022734b629STom Zanussi */
tracing_map_read_var(struct tracing_map_elt * elt,unsigned int i)1032734b629STom Zanussi u64 tracing_map_read_var(struct tracing_map_elt *elt, unsigned int i)
1042734b629STom Zanussi {
1052734b629STom Zanussi return (u64)atomic64_read(&elt->vars[i]);
1062734b629STom Zanussi }
1072734b629STom Zanussi
1082734b629STom Zanussi /**
1092734b629STom Zanussi * tracing_map_read_var_once - Return and reset a tracing_map_elt's variable field
1102734b629STom Zanussi * @elt: The tracing_map_elt
1112734b629STom Zanussi * @i: The index of the given variable associated with the tracing_map_elt
1122734b629STom Zanussi *
1132734b629STom Zanussi * Retrieve the value of the variable i associated with the specified
1142734b629STom Zanussi * tracing_map_elt instance, and reset the variable to the 'not set'
1152734b629STom Zanussi * state. The index i is the index returned by the call to
1162734b629STom Zanussi * tracing_map_add_var() when the tracing map was set up. The reset
1172734b629STom Zanussi * essentially makes the variable a read-once variable if it's only
1182734b629STom Zanussi * accessed using this function.
1192734b629STom Zanussi *
1202734b629STom Zanussi * Return: The variable value associated with field i for elt.
1212734b629STom Zanussi */
tracing_map_read_var_once(struct tracing_map_elt * elt,unsigned int i)1222734b629STom Zanussi u64 tracing_map_read_var_once(struct tracing_map_elt *elt, unsigned int i)
1232734b629STom Zanussi {
1242734b629STom Zanussi elt->var_set[i] = false;
1252734b629STom Zanussi return (u64)atomic64_read(&elt->vars[i]);
1262734b629STom Zanussi }
1272734b629STom Zanussi
tracing_map_cmp_string(void * val_a,void * val_b)12808d43a5fSTom Zanussi int tracing_map_cmp_string(void *val_a, void *val_b)
12908d43a5fSTom Zanussi {
13008d43a5fSTom Zanussi char *a = val_a;
13108d43a5fSTom Zanussi char *b = val_b;
13208d43a5fSTom Zanussi
13308d43a5fSTom Zanussi return strcmp(a, b);
13408d43a5fSTom Zanussi }
13508d43a5fSTom Zanussi
tracing_map_cmp_none(void * val_a,void * val_b)13608d43a5fSTom Zanussi int tracing_map_cmp_none(void *val_a, void *val_b)
13708d43a5fSTom Zanussi {
13808d43a5fSTom Zanussi return 0;
13908d43a5fSTom Zanussi }
14008d43a5fSTom Zanussi
tracing_map_cmp_atomic64(void * val_a,void * val_b)14108d43a5fSTom Zanussi static int tracing_map_cmp_atomic64(void *val_a, void *val_b)
14208d43a5fSTom Zanussi {
14308d43a5fSTom Zanussi u64 a = atomic64_read((atomic64_t *)val_a);
14408d43a5fSTom Zanussi u64 b = atomic64_read((atomic64_t *)val_b);
14508d43a5fSTom Zanussi
14608d43a5fSTom Zanussi return (a > b) ? 1 : ((a < b) ? -1 : 0);
14708d43a5fSTom Zanussi }
14808d43a5fSTom Zanussi
14908d43a5fSTom Zanussi #define DEFINE_TRACING_MAP_CMP_FN(type) \
15008d43a5fSTom Zanussi static int tracing_map_cmp_##type(void *val_a, void *val_b) \
15108d43a5fSTom Zanussi { \
152106f41f5SSteven Rostedt (VMware) type a = (type)(*(u64 *)val_a); \
153106f41f5SSteven Rostedt (VMware) type b = (type)(*(u64 *)val_b); \
15408d43a5fSTom Zanussi \
15508d43a5fSTom Zanussi return (a > b) ? 1 : ((a < b) ? -1 : 0); \
15608d43a5fSTom Zanussi }
15708d43a5fSTom Zanussi
15808d43a5fSTom Zanussi DEFINE_TRACING_MAP_CMP_FN(s64);
15908d43a5fSTom Zanussi DEFINE_TRACING_MAP_CMP_FN(u64);
16008d43a5fSTom Zanussi DEFINE_TRACING_MAP_CMP_FN(s32);
16108d43a5fSTom Zanussi DEFINE_TRACING_MAP_CMP_FN(u32);
16208d43a5fSTom Zanussi DEFINE_TRACING_MAP_CMP_FN(s16);
16308d43a5fSTom Zanussi DEFINE_TRACING_MAP_CMP_FN(u16);
16408d43a5fSTom Zanussi DEFINE_TRACING_MAP_CMP_FN(s8);
16508d43a5fSTom Zanussi DEFINE_TRACING_MAP_CMP_FN(u8);
16608d43a5fSTom Zanussi
tracing_map_cmp_num(int field_size,int field_is_signed)16708d43a5fSTom Zanussi tracing_map_cmp_fn_t tracing_map_cmp_num(int field_size,
16808d43a5fSTom Zanussi int field_is_signed)
16908d43a5fSTom Zanussi {
17008d43a5fSTom Zanussi tracing_map_cmp_fn_t fn = tracing_map_cmp_none;
17108d43a5fSTom Zanussi
17208d43a5fSTom Zanussi switch (field_size) {
17308d43a5fSTom Zanussi case 8:
17408d43a5fSTom Zanussi if (field_is_signed)
17508d43a5fSTom Zanussi fn = tracing_map_cmp_s64;
17608d43a5fSTom Zanussi else
17708d43a5fSTom Zanussi fn = tracing_map_cmp_u64;
17808d43a5fSTom Zanussi break;
17908d43a5fSTom Zanussi case 4:
18008d43a5fSTom Zanussi if (field_is_signed)
18108d43a5fSTom Zanussi fn = tracing_map_cmp_s32;
18208d43a5fSTom Zanussi else
18308d43a5fSTom Zanussi fn = tracing_map_cmp_u32;
18408d43a5fSTom Zanussi break;
18508d43a5fSTom Zanussi case 2:
18608d43a5fSTom Zanussi if (field_is_signed)
18708d43a5fSTom Zanussi fn = tracing_map_cmp_s16;
18808d43a5fSTom Zanussi else
18908d43a5fSTom Zanussi fn = tracing_map_cmp_u16;
19008d43a5fSTom Zanussi break;
19108d43a5fSTom Zanussi case 1:
19208d43a5fSTom Zanussi if (field_is_signed)
19308d43a5fSTom Zanussi fn = tracing_map_cmp_s8;
19408d43a5fSTom Zanussi else
19508d43a5fSTom Zanussi fn = tracing_map_cmp_u8;
19608d43a5fSTom Zanussi break;
19708d43a5fSTom Zanussi }
19808d43a5fSTom Zanussi
19908d43a5fSTom Zanussi return fn;
20008d43a5fSTom Zanussi }
20108d43a5fSTom Zanussi
tracing_map_add_field(struct tracing_map * map,tracing_map_cmp_fn_t cmp_fn)20208d43a5fSTom Zanussi static int tracing_map_add_field(struct tracing_map *map,
20308d43a5fSTom Zanussi tracing_map_cmp_fn_t cmp_fn)
20408d43a5fSTom Zanussi {
20508d43a5fSTom Zanussi int ret = -EINVAL;
20608d43a5fSTom Zanussi
20708d43a5fSTom Zanussi if (map->n_fields < TRACING_MAP_FIELDS_MAX) {
20808d43a5fSTom Zanussi ret = map->n_fields;
20908d43a5fSTom Zanussi map->fields[map->n_fields++].cmp_fn = cmp_fn;
21008d43a5fSTom Zanussi }
21108d43a5fSTom Zanussi
21208d43a5fSTom Zanussi return ret;
21308d43a5fSTom Zanussi }
21408d43a5fSTom Zanussi
21508d43a5fSTom Zanussi /**
21608d43a5fSTom Zanussi * tracing_map_add_sum_field - Add a field describing a tracing_map sum
21708d43a5fSTom Zanussi * @map: The tracing_map
21808d43a5fSTom Zanussi *
21908d43a5fSTom Zanussi * Add a sum field to the key and return the index identifying it in
22008d43a5fSTom Zanussi * the map and associated tracing_map_elts. This is the index used
22108d43a5fSTom Zanussi * for instance to update a sum for a particular tracing_map_elt using
22208d43a5fSTom Zanussi * tracing_map_update_sum() or reading it via tracing_map_read_sum().
22308d43a5fSTom Zanussi *
22408d43a5fSTom Zanussi * Return: The index identifying the field in the map and associated
2253b772b96STom Zanussi * tracing_map_elts, or -EINVAL on error.
22608d43a5fSTom Zanussi */
tracing_map_add_sum_field(struct tracing_map * map)22708d43a5fSTom Zanussi int tracing_map_add_sum_field(struct tracing_map *map)
22808d43a5fSTom Zanussi {
22908d43a5fSTom Zanussi return tracing_map_add_field(map, tracing_map_cmp_atomic64);
23008d43a5fSTom Zanussi }
23108d43a5fSTom Zanussi
23208d43a5fSTom Zanussi /**
2332734b629STom Zanussi * tracing_map_add_var - Add a field describing a tracing_map var
2342734b629STom Zanussi * @map: The tracing_map
2352734b629STom Zanussi *
2362734b629STom Zanussi * Add a var to the map and return the index identifying it in the map
2372734b629STom Zanussi * and associated tracing_map_elts. This is the index used for
2382734b629STom Zanussi * instance to update a var for a particular tracing_map_elt using
2392734b629STom Zanussi * tracing_map_update_var() or reading it via tracing_map_read_var().
2402734b629STom Zanussi *
2412734b629STom Zanussi * Return: The index identifying the var in the map and associated
2422734b629STom Zanussi * tracing_map_elts, or -EINVAL on error.
2432734b629STom Zanussi */
tracing_map_add_var(struct tracing_map * map)2442734b629STom Zanussi int tracing_map_add_var(struct tracing_map *map)
2452734b629STom Zanussi {
2462734b629STom Zanussi int ret = -EINVAL;
2472734b629STom Zanussi
2482734b629STom Zanussi if (map->n_vars < TRACING_MAP_VARS_MAX)
2492734b629STom Zanussi ret = map->n_vars++;
2502734b629STom Zanussi
2512734b629STom Zanussi return ret;
2522734b629STom Zanussi }
2532734b629STom Zanussi
2542734b629STom Zanussi /**
25508d43a5fSTom Zanussi * tracing_map_add_key_field - Add a field describing a tracing_map key
25608d43a5fSTom Zanussi * @map: The tracing_map
25708d43a5fSTom Zanussi * @offset: The offset within the key
25808d43a5fSTom Zanussi * @cmp_fn: The comparison function that will be used to sort on the key
25908d43a5fSTom Zanussi *
26008d43a5fSTom Zanussi * Let the map know there is a key and that if it's used as a sort key
26108d43a5fSTom Zanussi * to use cmp_fn.
26208d43a5fSTom Zanussi *
26308d43a5fSTom Zanussi * A key can be a subset of a compound key; for that purpose, the
2645c8c206eSRandy Dunlap * offset param is used to describe where within the compound key
26508d43a5fSTom Zanussi * the key referenced by this key field resides.
26608d43a5fSTom Zanussi *
26708d43a5fSTom Zanussi * Return: The index identifying the field in the map and associated
2683b772b96STom Zanussi * tracing_map_elts, or -EINVAL on error.
26908d43a5fSTom Zanussi */
tracing_map_add_key_field(struct tracing_map * map,unsigned int offset,tracing_map_cmp_fn_t cmp_fn)27008d43a5fSTom Zanussi int tracing_map_add_key_field(struct tracing_map *map,
27108d43a5fSTom Zanussi unsigned int offset,
27208d43a5fSTom Zanussi tracing_map_cmp_fn_t cmp_fn)
27308d43a5fSTom Zanussi
27408d43a5fSTom Zanussi {
27508d43a5fSTom Zanussi int idx = tracing_map_add_field(map, cmp_fn);
27608d43a5fSTom Zanussi
27708d43a5fSTom Zanussi if (idx < 0)
27808d43a5fSTom Zanussi return idx;
27908d43a5fSTom Zanussi
28008d43a5fSTom Zanussi map->fields[idx].offset = offset;
28108d43a5fSTom Zanussi
28208d43a5fSTom Zanussi map->key_idx[map->n_keys++] = idx;
28308d43a5fSTom Zanussi
28408d43a5fSTom Zanussi return idx;
28508d43a5fSTom Zanussi }
28608d43a5fSTom Zanussi
tracing_map_array_clear(struct tracing_map_array * a)287d013496fSJason Yan static void tracing_map_array_clear(struct tracing_map_array *a)
28808d43a5fSTom Zanussi {
28908d43a5fSTom Zanussi unsigned int i;
29008d43a5fSTom Zanussi
29108d43a5fSTom Zanussi if (!a->pages)
29208d43a5fSTom Zanussi return;
29308d43a5fSTom Zanussi
29408d43a5fSTom Zanussi for (i = 0; i < a->n_pages; i++)
29508d43a5fSTom Zanussi memset(a->pages[i], 0, PAGE_SIZE);
29608d43a5fSTom Zanussi }
29708d43a5fSTom Zanussi
tracing_map_array_free(struct tracing_map_array * a)298d013496fSJason Yan static void tracing_map_array_free(struct tracing_map_array *a)
29908d43a5fSTom Zanussi {
30008d43a5fSTom Zanussi unsigned int i;
30108d43a5fSTom Zanussi
30208d43a5fSTom Zanussi if (!a)
30308d43a5fSTom Zanussi return;
30408d43a5fSTom Zanussi
305475bb3c6SChunyu Hu if (!a->pages)
306475bb3c6SChunyu Hu goto free;
30708d43a5fSTom Zanussi
30808d43a5fSTom Zanussi for (i = 0; i < a->n_pages; i++) {
30908d43a5fSTom Zanussi if (!a->pages[i])
31008d43a5fSTom Zanussi break;
311f25667e5SChen Jun kmemleak_free(a->pages[i]);
31208d43a5fSTom Zanussi free_page((unsigned long)a->pages[i]);
31308d43a5fSTom Zanussi }
314475bb3c6SChunyu Hu
315475bb3c6SChunyu Hu kfree(a->pages);
316475bb3c6SChunyu Hu
317475bb3c6SChunyu Hu free:
318475bb3c6SChunyu Hu kfree(a);
31908d43a5fSTom Zanussi }
32008d43a5fSTom Zanussi
tracing_map_array_alloc(unsigned int n_elts,unsigned int entry_size)321d013496fSJason Yan static struct tracing_map_array *tracing_map_array_alloc(unsigned int n_elts,
32208d43a5fSTom Zanussi unsigned int entry_size)
32308d43a5fSTom Zanussi {
32408d43a5fSTom Zanussi struct tracing_map_array *a;
32508d43a5fSTom Zanussi unsigned int i;
32608d43a5fSTom Zanussi
32708d43a5fSTom Zanussi a = kzalloc(sizeof(*a), GFP_KERNEL);
32808d43a5fSTom Zanussi if (!a)
32908d43a5fSTom Zanussi return NULL;
33008d43a5fSTom Zanussi
33108d43a5fSTom Zanussi a->entry_size_shift = fls(roundup_pow_of_two(entry_size) - 1);
33208d43a5fSTom Zanussi a->entries_per_page = PAGE_SIZE / (1 << a->entry_size_shift);
33308d43a5fSTom Zanussi a->n_pages = n_elts / a->entries_per_page;
33408d43a5fSTom Zanussi if (!a->n_pages)
33508d43a5fSTom Zanussi a->n_pages = 1;
33608d43a5fSTom Zanussi a->entry_shift = fls(a->entries_per_page) - 1;
33708d43a5fSTom Zanussi a->entry_mask = (1 << a->entry_shift) - 1;
33808d43a5fSTom Zanussi
33908d43a5fSTom Zanussi a->pages = kcalloc(a->n_pages, sizeof(void *), GFP_KERNEL);
34008d43a5fSTom Zanussi if (!a->pages)
34108d43a5fSTom Zanussi goto free;
34208d43a5fSTom Zanussi
34308d43a5fSTom Zanussi for (i = 0; i < a->n_pages; i++) {
34408d43a5fSTom Zanussi a->pages[i] = (void *)get_zeroed_page(GFP_KERNEL);
34508d43a5fSTom Zanussi if (!a->pages[i])
34608d43a5fSTom Zanussi goto free;
347f25667e5SChen Jun kmemleak_alloc(a->pages[i], PAGE_SIZE, 1, GFP_KERNEL);
34808d43a5fSTom Zanussi }
34908d43a5fSTom Zanussi out:
35008d43a5fSTom Zanussi return a;
35108d43a5fSTom Zanussi free:
35208d43a5fSTom Zanussi tracing_map_array_free(a);
35308d43a5fSTom Zanussi a = NULL;
35408d43a5fSTom Zanussi
35508d43a5fSTom Zanussi goto out;
35608d43a5fSTom Zanussi }
35708d43a5fSTom Zanussi
tracing_map_elt_clear(struct tracing_map_elt * elt)35808d43a5fSTom Zanussi static void tracing_map_elt_clear(struct tracing_map_elt *elt)
35908d43a5fSTom Zanussi {
36008d43a5fSTom Zanussi unsigned i;
36108d43a5fSTom Zanussi
36208d43a5fSTom Zanussi for (i = 0; i < elt->map->n_fields; i++)
36308d43a5fSTom Zanussi if (elt->fields[i].cmp_fn == tracing_map_cmp_atomic64)
36408d43a5fSTom Zanussi atomic64_set(&elt->fields[i].sum, 0);
36508d43a5fSTom Zanussi
3662734b629STom Zanussi for (i = 0; i < elt->map->n_vars; i++) {
3672734b629STom Zanussi atomic64_set(&elt->vars[i], 0);
3682734b629STom Zanussi elt->var_set[i] = false;
3692734b629STom Zanussi }
3702734b629STom Zanussi
37108d43a5fSTom Zanussi if (elt->map->ops && elt->map->ops->elt_clear)
37208d43a5fSTom Zanussi elt->map->ops->elt_clear(elt);
37308d43a5fSTom Zanussi }
37408d43a5fSTom Zanussi
tracing_map_elt_init_fields(struct tracing_map_elt * elt)37508d43a5fSTom Zanussi static void tracing_map_elt_init_fields(struct tracing_map_elt *elt)
37608d43a5fSTom Zanussi {
37708d43a5fSTom Zanussi unsigned int i;
37808d43a5fSTom Zanussi
37908d43a5fSTom Zanussi tracing_map_elt_clear(elt);
38008d43a5fSTom Zanussi
38108d43a5fSTom Zanussi for (i = 0; i < elt->map->n_fields; i++) {
38208d43a5fSTom Zanussi elt->fields[i].cmp_fn = elt->map->fields[i].cmp_fn;
38308d43a5fSTom Zanussi
38408d43a5fSTom Zanussi if (elt->fields[i].cmp_fn != tracing_map_cmp_atomic64)
38508d43a5fSTom Zanussi elt->fields[i].offset = elt->map->fields[i].offset;
38608d43a5fSTom Zanussi }
38708d43a5fSTom Zanussi }
38808d43a5fSTom Zanussi
tracing_map_elt_free(struct tracing_map_elt * elt)38908d43a5fSTom Zanussi static void tracing_map_elt_free(struct tracing_map_elt *elt)
39008d43a5fSTom Zanussi {
39108d43a5fSTom Zanussi if (!elt)
39208d43a5fSTom Zanussi return;
39308d43a5fSTom Zanussi
39408d43a5fSTom Zanussi if (elt->map->ops && elt->map->ops->elt_free)
39508d43a5fSTom Zanussi elt->map->ops->elt_free(elt);
39608d43a5fSTom Zanussi kfree(elt->fields);
3972734b629STom Zanussi kfree(elt->vars);
3982734b629STom Zanussi kfree(elt->var_set);
39908d43a5fSTom Zanussi kfree(elt->key);
40008d43a5fSTom Zanussi kfree(elt);
40108d43a5fSTom Zanussi }
40208d43a5fSTom Zanussi
tracing_map_elt_alloc(struct tracing_map * map)40308d43a5fSTom Zanussi static struct tracing_map_elt *tracing_map_elt_alloc(struct tracing_map *map)
40408d43a5fSTom Zanussi {
40508d43a5fSTom Zanussi struct tracing_map_elt *elt;
40608d43a5fSTom Zanussi int err = 0;
40708d43a5fSTom Zanussi
40808d43a5fSTom Zanussi elt = kzalloc(sizeof(*elt), GFP_KERNEL);
40908d43a5fSTom Zanussi if (!elt)
41008d43a5fSTom Zanussi return ERR_PTR(-ENOMEM);
41108d43a5fSTom Zanussi
41208d43a5fSTom Zanussi elt->map = map;
41308d43a5fSTom Zanussi
41408d43a5fSTom Zanussi elt->key = kzalloc(map->key_size, GFP_KERNEL);
41508d43a5fSTom Zanussi if (!elt->key) {
41608d43a5fSTom Zanussi err = -ENOMEM;
41708d43a5fSTom Zanussi goto free;
41808d43a5fSTom Zanussi }
41908d43a5fSTom Zanussi
42008d43a5fSTom Zanussi elt->fields = kcalloc(map->n_fields, sizeof(*elt->fields), GFP_KERNEL);
42108d43a5fSTom Zanussi if (!elt->fields) {
42208d43a5fSTom Zanussi err = -ENOMEM;
42308d43a5fSTom Zanussi goto free;
42408d43a5fSTom Zanussi }
42508d43a5fSTom Zanussi
4262734b629STom Zanussi elt->vars = kcalloc(map->n_vars, sizeof(*elt->vars), GFP_KERNEL);
4272734b629STom Zanussi if (!elt->vars) {
4282734b629STom Zanussi err = -ENOMEM;
4292734b629STom Zanussi goto free;
4302734b629STom Zanussi }
4312734b629STom Zanussi
4322734b629STom Zanussi elt->var_set = kcalloc(map->n_vars, sizeof(*elt->var_set), GFP_KERNEL);
4332734b629STom Zanussi if (!elt->var_set) {
4342734b629STom Zanussi err = -ENOMEM;
4352734b629STom Zanussi goto free;
4362734b629STom Zanussi }
4372734b629STom Zanussi
43808d43a5fSTom Zanussi tracing_map_elt_init_fields(elt);
43908d43a5fSTom Zanussi
44008d43a5fSTom Zanussi if (map->ops && map->ops->elt_alloc) {
44108d43a5fSTom Zanussi err = map->ops->elt_alloc(elt);
44208d43a5fSTom Zanussi if (err)
44308d43a5fSTom Zanussi goto free;
44408d43a5fSTom Zanussi }
44508d43a5fSTom Zanussi return elt;
44608d43a5fSTom Zanussi free:
44708d43a5fSTom Zanussi tracing_map_elt_free(elt);
44808d43a5fSTom Zanussi
44908d43a5fSTom Zanussi return ERR_PTR(err);
45008d43a5fSTom Zanussi }
45108d43a5fSTom Zanussi
get_free_elt(struct tracing_map * map)45208d43a5fSTom Zanussi static struct tracing_map_elt *get_free_elt(struct tracing_map *map)
45308d43a5fSTom Zanussi {
45408d43a5fSTom Zanussi struct tracing_map_elt *elt = NULL;
45508d43a5fSTom Zanussi int idx;
45608d43a5fSTom Zanussi
457bcf86c01STze-nan Wu idx = atomic_fetch_add_unless(&map->next_elt, 1, map->max_elts);
45808d43a5fSTom Zanussi if (idx < map->max_elts) {
45908d43a5fSTom Zanussi elt = *(TRACING_MAP_ELT(map->elts, idx));
46008d43a5fSTom Zanussi if (map->ops && map->ops->elt_init)
46108d43a5fSTom Zanussi map->ops->elt_init(elt);
46208d43a5fSTom Zanussi }
46308d43a5fSTom Zanussi
46408d43a5fSTom Zanussi return elt;
46508d43a5fSTom Zanussi }
46608d43a5fSTom Zanussi
tracing_map_free_elts(struct tracing_map * map)46708d43a5fSTom Zanussi static void tracing_map_free_elts(struct tracing_map *map)
46808d43a5fSTom Zanussi {
46908d43a5fSTom Zanussi unsigned int i;
47008d43a5fSTom Zanussi
47108d43a5fSTom Zanussi if (!map->elts)
47208d43a5fSTom Zanussi return;
47308d43a5fSTom Zanussi
4746e4cf657STom Zanussi for (i = 0; i < map->max_elts; i++) {
47508d43a5fSTom Zanussi tracing_map_elt_free(*(TRACING_MAP_ELT(map->elts, i)));
4766e4cf657STom Zanussi *(TRACING_MAP_ELT(map->elts, i)) = NULL;
4776e4cf657STom Zanussi }
47808d43a5fSTom Zanussi
47908d43a5fSTom Zanussi tracing_map_array_free(map->elts);
4806e4cf657STom Zanussi map->elts = NULL;
48108d43a5fSTom Zanussi }
48208d43a5fSTom Zanussi
tracing_map_alloc_elts(struct tracing_map * map)48308d43a5fSTom Zanussi static int tracing_map_alloc_elts(struct tracing_map *map)
48408d43a5fSTom Zanussi {
48508d43a5fSTom Zanussi unsigned int i;
48608d43a5fSTom Zanussi
48708d43a5fSTom Zanussi map->elts = tracing_map_array_alloc(map->max_elts,
48808d43a5fSTom Zanussi sizeof(struct tracing_map_elt *));
48908d43a5fSTom Zanussi if (!map->elts)
49008d43a5fSTom Zanussi return -ENOMEM;
49108d43a5fSTom Zanussi
49208d43a5fSTom Zanussi for (i = 0; i < map->max_elts; i++) {
49308d43a5fSTom Zanussi *(TRACING_MAP_ELT(map->elts, i)) = tracing_map_elt_alloc(map);
4946e4cf657STom Zanussi if (IS_ERR(*(TRACING_MAP_ELT(map->elts, i)))) {
4956e4cf657STom Zanussi *(TRACING_MAP_ELT(map->elts, i)) = NULL;
49608d43a5fSTom Zanussi tracing_map_free_elts(map);
49708d43a5fSTom Zanussi
49808d43a5fSTom Zanussi return -ENOMEM;
49908d43a5fSTom Zanussi }
50008d43a5fSTom Zanussi }
50108d43a5fSTom Zanussi
50208d43a5fSTom Zanussi return 0;
50308d43a5fSTom Zanussi }
50408d43a5fSTom Zanussi
keys_match(void * key,void * test_key,unsigned key_size)50508d43a5fSTom Zanussi static inline bool keys_match(void *key, void *test_key, unsigned key_size)
50608d43a5fSTom Zanussi {
50708d43a5fSTom Zanussi bool match = true;
50808d43a5fSTom Zanussi
50908d43a5fSTom Zanussi if (memcmp(key, test_key, key_size))
51008d43a5fSTom Zanussi match = false;
51108d43a5fSTom Zanussi
51208d43a5fSTom Zanussi return match;
51308d43a5fSTom Zanussi }
51408d43a5fSTom Zanussi
51508d43a5fSTom Zanussi static inline struct tracing_map_elt *
__tracing_map_insert(struct tracing_map * map,void * key,bool lookup_only)51608d43a5fSTom Zanussi __tracing_map_insert(struct tracing_map *map, void *key, bool lookup_only)
51708d43a5fSTom Zanussi {
51808d43a5fSTom Zanussi u32 idx, key_hash, test_key;
519cbf4100eSVedang Patel int dup_try = 0;
52008d43a5fSTom Zanussi struct tracing_map_entry *entry;
521cbf4100eSVedang Patel struct tracing_map_elt *val;
52208d43a5fSTom Zanussi
52308d43a5fSTom Zanussi key_hash = jhash(key, map->key_size, 0);
52408d43a5fSTom Zanussi if (key_hash == 0)
52508d43a5fSTom Zanussi key_hash = 1;
52608d43a5fSTom Zanussi idx = key_hash >> (32 - (map->map_bits + 1));
52708d43a5fSTom Zanussi
52808d43a5fSTom Zanussi while (1) {
52908d43a5fSTom Zanussi idx &= (map->map_size - 1);
53008d43a5fSTom Zanussi entry = TRACING_MAP_ENTRY(map->map, idx);
53108d43a5fSTom Zanussi test_key = entry->key;
53208d43a5fSTom Zanussi
533cbf4100eSVedang Patel if (test_key && test_key == key_hash) {
534cbf4100eSVedang Patel val = READ_ONCE(entry->val);
535cbf4100eSVedang Patel if (val &&
536cbf4100eSVedang Patel keys_match(key, val->key, map->key_size)) {
53783c07eccSTom Zanussi if (!lookup_only)
53808d43a5fSTom Zanussi atomic64_inc(&map->hits);
539cbf4100eSVedang Patel return val;
540cbf4100eSVedang Patel } else if (unlikely(!val)) {
541cbf4100eSVedang Patel /*
542cbf4100eSVedang Patel * The key is present. But, val (pointer to elt
543cbf4100eSVedang Patel * struct) is still NULL. which means some other
544cbf4100eSVedang Patel * thread is in the process of inserting an
545cbf4100eSVedang Patel * element.
546cbf4100eSVedang Patel *
547cbf4100eSVedang Patel * On top of that, it's key_hash is same as the
548cbf4100eSVedang Patel * one being inserted right now. So, it's
549cbf4100eSVedang Patel * possible that the element has the same
550cbf4100eSVedang Patel * key as well.
551cbf4100eSVedang Patel */
552cbf4100eSVedang Patel
553cbf4100eSVedang Patel dup_try++;
554cbf4100eSVedang Patel if (dup_try > map->map_size) {
555cbf4100eSVedang Patel atomic64_inc(&map->drops);
556cbf4100eSVedang Patel break;
557cbf4100eSVedang Patel }
558cbf4100eSVedang Patel continue;
559cbf4100eSVedang Patel }
56008d43a5fSTom Zanussi }
56108d43a5fSTom Zanussi
56208d43a5fSTom Zanussi if (!test_key) {
56308d43a5fSTom Zanussi if (lookup_only)
56408d43a5fSTom Zanussi break;
56508d43a5fSTom Zanussi
56608d43a5fSTom Zanussi if (!cmpxchg(&entry->key, 0, key_hash)) {
56708d43a5fSTom Zanussi struct tracing_map_elt *elt;
56808d43a5fSTom Zanussi
56908d43a5fSTom Zanussi elt = get_free_elt(map);
57008d43a5fSTom Zanussi if (!elt) {
57108d43a5fSTom Zanussi atomic64_inc(&map->drops);
57208d43a5fSTom Zanussi entry->key = 0;
57308d43a5fSTom Zanussi break;
57408d43a5fSTom Zanussi }
57508d43a5fSTom Zanussi
57608d43a5fSTom Zanussi memcpy(elt->key, key, map->key_size);
5772b447606SPetr Pavlu /*
5782b447606SPetr Pavlu * Ensure the initialization is visible and
5792b447606SPetr Pavlu * publish the elt.
5802b447606SPetr Pavlu */
5812b447606SPetr Pavlu smp_wmb();
5822b447606SPetr Pavlu WRITE_ONCE(entry->val, elt);
58308d43a5fSTom Zanussi atomic64_inc(&map->hits);
58408d43a5fSTom Zanussi
58508d43a5fSTom Zanussi return entry->val;
586cbf4100eSVedang Patel } else {
587cbf4100eSVedang Patel /*
588cbf4100eSVedang Patel * cmpxchg() failed. Loop around once
589cbf4100eSVedang Patel * more to check what key was inserted.
590cbf4100eSVedang Patel */
591cbf4100eSVedang Patel dup_try++;
592cbf4100eSVedang Patel continue;
59308d43a5fSTom Zanussi }
59408d43a5fSTom Zanussi }
59508d43a5fSTom Zanussi
59608d43a5fSTom Zanussi idx++;
59708d43a5fSTom Zanussi }
59808d43a5fSTom Zanussi
59908d43a5fSTom Zanussi return NULL;
60008d43a5fSTom Zanussi }
60108d43a5fSTom Zanussi
60208d43a5fSTom Zanussi /**
60308d43a5fSTom Zanussi * tracing_map_insert - Insert key and/or retrieve val from a tracing_map
60408d43a5fSTom Zanussi * @map: The tracing_map to insert into
60508d43a5fSTom Zanussi * @key: The key to insert
60608d43a5fSTom Zanussi *
60708d43a5fSTom Zanussi * Inserts a key into a tracing_map and creates and returns a new
60808d43a5fSTom Zanussi * tracing_map_elt for it, or if the key has already been inserted by
60908d43a5fSTom Zanussi * a previous call, returns the tracing_map_elt already associated
61008d43a5fSTom Zanussi * with it. When the map was created, the number of elements to be
61108d43a5fSTom Zanussi * allocated for the map was specified (internally maintained as
61208d43a5fSTom Zanussi * 'max_elts' in struct tracing_map), and that number of
61308d43a5fSTom Zanussi * tracing_map_elts was created by tracing_map_init(). This is the
61408d43a5fSTom Zanussi * pre-allocated pool of tracing_map_elts that tracing_map_insert()
61508d43a5fSTom Zanussi * will allocate from when adding new keys. Once that pool is
61608d43a5fSTom Zanussi * exhausted, tracing_map_insert() is useless and will return NULL to
61708d43a5fSTom Zanussi * signal that state. There are two user-visible tracing_map
61808d43a5fSTom Zanussi * variables, 'hits' and 'drops', which are updated by this function.
61908d43a5fSTom Zanussi * Every time an element is either successfully inserted or retrieved,
6202b5894ccSQiujun Huang * the 'hits' value is incremented. Every time an element insertion
62108d43a5fSTom Zanussi * fails, the 'drops' value is incremented.
62208d43a5fSTom Zanussi *
62308d43a5fSTom Zanussi * This is a lock-free tracing map insertion function implementing a
62408d43a5fSTom Zanussi * modified form of Cliff Click's basic insertion algorithm. It
62508d43a5fSTom Zanussi * requires the table size be a power of two. To prevent any
62608d43a5fSTom Zanussi * possibility of an infinite loop we always make the internal table
62708d43a5fSTom Zanussi * size double the size of the requested table size (max_elts * 2).
62808d43a5fSTom Zanussi * Likewise, we never reuse a slot or resize or delete elements - when
62908d43a5fSTom Zanussi * we've reached max_elts entries, we simply return NULL once we've
63008d43a5fSTom Zanussi * run out of entries. Readers can at any point in time traverse the
63108d43a5fSTom Zanussi * tracing map and safely access the key/val pairs.
63208d43a5fSTom Zanussi *
63308d43a5fSTom Zanussi * Return: the tracing_map_elt pointer val associated with the key.
63408d43a5fSTom Zanussi * If this was a newly inserted key, the val will be a newly allocated
63508d43a5fSTom Zanussi * and associated tracing_map_elt pointer val. If the key wasn't
63608d43a5fSTom Zanussi * found and the pool of tracing_map_elts has been exhausted, NULL is
63708d43a5fSTom Zanussi * returned and no further insertions will succeed.
63808d43a5fSTom Zanussi */
tracing_map_insert(struct tracing_map * map,void * key)63908d43a5fSTom Zanussi struct tracing_map_elt *tracing_map_insert(struct tracing_map *map, void *key)
64008d43a5fSTom Zanussi {
64108d43a5fSTom Zanussi return __tracing_map_insert(map, key, false);
64208d43a5fSTom Zanussi }
64308d43a5fSTom Zanussi
64408d43a5fSTom Zanussi /**
64508d43a5fSTom Zanussi * tracing_map_lookup - Retrieve val from a tracing_map
64608d43a5fSTom Zanussi * @map: The tracing_map to perform the lookup on
64708d43a5fSTom Zanussi * @key: The key to look up
64808d43a5fSTom Zanussi *
64908d43a5fSTom Zanussi * Looks up key in tracing_map and if found returns the matching
65008d43a5fSTom Zanussi * tracing_map_elt. This is a lock-free lookup; see
65108d43a5fSTom Zanussi * tracing_map_insert() for details on tracing_map and how it works.
65208d43a5fSTom Zanussi * Every time an element is retrieved, the 'hits' value is
6532b5894ccSQiujun Huang * incremented. There is one user-visible tracing_map variable,
65408d43a5fSTom Zanussi * 'hits', which is updated by this function. Every time an element
6552b5894ccSQiujun Huang * is successfully retrieved, the 'hits' value is incremented. The
65608d43a5fSTom Zanussi * 'drops' value is never updated by this function.
65708d43a5fSTom Zanussi *
65808d43a5fSTom Zanussi * Return: the tracing_map_elt pointer val associated with the key.
65908d43a5fSTom Zanussi * If the key wasn't found, NULL is returned.
66008d43a5fSTom Zanussi */
tracing_map_lookup(struct tracing_map * map,void * key)66108d43a5fSTom Zanussi struct tracing_map_elt *tracing_map_lookup(struct tracing_map *map, void *key)
66208d43a5fSTom Zanussi {
66308d43a5fSTom Zanussi return __tracing_map_insert(map, key, true);
66408d43a5fSTom Zanussi }
66508d43a5fSTom Zanussi
66608d43a5fSTom Zanussi /**
66708d43a5fSTom Zanussi * tracing_map_destroy - Destroy a tracing_map
66808d43a5fSTom Zanussi * @map: The tracing_map to destroy
66908d43a5fSTom Zanussi *
67008d43a5fSTom Zanussi * Frees a tracing_map along with its associated array of
67108d43a5fSTom Zanussi * tracing_map_elts.
67208d43a5fSTom Zanussi *
67308d43a5fSTom Zanussi * Callers should make sure there are no readers or writers actively
67408d43a5fSTom Zanussi * reading or inserting into the map before calling this.
67508d43a5fSTom Zanussi */
tracing_map_destroy(struct tracing_map * map)67608d43a5fSTom Zanussi void tracing_map_destroy(struct tracing_map *map)
67708d43a5fSTom Zanussi {
67808d43a5fSTom Zanussi if (!map)
67908d43a5fSTom Zanussi return;
68008d43a5fSTom Zanussi
68108d43a5fSTom Zanussi tracing_map_free_elts(map);
68208d43a5fSTom Zanussi
68308d43a5fSTom Zanussi tracing_map_array_free(map->map);
68408d43a5fSTom Zanussi kfree(map);
68508d43a5fSTom Zanussi }
68608d43a5fSTom Zanussi
68708d43a5fSTom Zanussi /**
68808d43a5fSTom Zanussi * tracing_map_clear - Clear a tracing_map
68908d43a5fSTom Zanussi * @map: The tracing_map to clear
69008d43a5fSTom Zanussi *
69108d43a5fSTom Zanussi * Resets the tracing map to a cleared or initial state. The
69208d43a5fSTom Zanussi * tracing_map_elts are all cleared, and the array of struct
69308d43a5fSTom Zanussi * tracing_map_entry is reset to an initialized state.
69408d43a5fSTom Zanussi *
69508d43a5fSTom Zanussi * Callers should make sure there are no writers actively inserting
69608d43a5fSTom Zanussi * into the map before calling this.
69708d43a5fSTom Zanussi */
tracing_map_clear(struct tracing_map * map)69808d43a5fSTom Zanussi void tracing_map_clear(struct tracing_map *map)
69908d43a5fSTom Zanussi {
70008d43a5fSTom Zanussi unsigned int i;
70108d43a5fSTom Zanussi
702bcf86c01STze-nan Wu atomic_set(&map->next_elt, 0);
70308d43a5fSTom Zanussi atomic64_set(&map->hits, 0);
70408d43a5fSTom Zanussi atomic64_set(&map->drops, 0);
70508d43a5fSTom Zanussi
70608d43a5fSTom Zanussi tracing_map_array_clear(map->map);
70708d43a5fSTom Zanussi
70808d43a5fSTom Zanussi for (i = 0; i < map->max_elts; i++)
70908d43a5fSTom Zanussi tracing_map_elt_clear(*(TRACING_MAP_ELT(map->elts, i)));
71008d43a5fSTom Zanussi }
71108d43a5fSTom Zanussi
set_sort_key(struct tracing_map * map,struct tracing_map_sort_key * sort_key)71208d43a5fSTom Zanussi static void set_sort_key(struct tracing_map *map,
71308d43a5fSTom Zanussi struct tracing_map_sort_key *sort_key)
71408d43a5fSTom Zanussi {
71508d43a5fSTom Zanussi map->sort_key = *sort_key;
71608d43a5fSTom Zanussi }
71708d43a5fSTom Zanussi
71808d43a5fSTom Zanussi /**
71908d43a5fSTom Zanussi * tracing_map_create - Create a lock-free map and element pool
72008d43a5fSTom Zanussi * @map_bits: The size of the map (2 ** map_bits)
72108d43a5fSTom Zanussi * @key_size: The size of the key for the map in bytes
72208d43a5fSTom Zanussi * @ops: Optional client-defined tracing_map_ops instance
72308d43a5fSTom Zanussi * @private_data: Client data associated with the map
72408d43a5fSTom Zanussi *
72508d43a5fSTom Zanussi * Creates and sets up a map to contain 2 ** map_bits number of
72608d43a5fSTom Zanussi * elements (internally maintained as 'max_elts' in struct
72708d43a5fSTom Zanussi * tracing_map). Before using, map fields should be added to the map
72808d43a5fSTom Zanussi * with tracing_map_add_sum_field() and tracing_map_add_key_field().
72908d43a5fSTom Zanussi * tracing_map_init() should then be called to allocate the array of
73008d43a5fSTom Zanussi * tracing_map_elts, in order to avoid allocating anything in the map
73108d43a5fSTom Zanussi * insertion path. The user-specified map size reflects the maximum
73208d43a5fSTom Zanussi * number of elements that can be contained in the table requested by
73308d43a5fSTom Zanussi * the user - internally we double that in order to keep the table
73408d43a5fSTom Zanussi * sparse and keep collisions manageable.
73508d43a5fSTom Zanussi *
73608d43a5fSTom Zanussi * A tracing_map is a special-purpose map designed to aggregate or
73708d43a5fSTom Zanussi * 'sum' one or more values associated with a specific object of type
73808d43a5fSTom Zanussi * tracing_map_elt, which is attached by the map to a given key.
73908d43a5fSTom Zanussi *
74008d43a5fSTom Zanussi * tracing_map_create() sets up the map itself, and provides
74108d43a5fSTom Zanussi * operations for inserting tracing_map_elts, but doesn't allocate the
74208d43a5fSTom Zanussi * tracing_map_elts themselves, or provide a means for describing the
74308d43a5fSTom Zanussi * keys or sums associated with the tracing_map_elts. All
74408d43a5fSTom Zanussi * tracing_map_elts for a given map have the same set of sums and
74508d43a5fSTom Zanussi * keys, which are defined by the client using the functions
74608d43a5fSTom Zanussi * tracing_map_add_key_field() and tracing_map_add_sum_field(). Once
74708d43a5fSTom Zanussi * the fields are defined, the pool of elements allocated for the map
74808d43a5fSTom Zanussi * can be created, which occurs when the client code calls
74908d43a5fSTom Zanussi * tracing_map_init().
75008d43a5fSTom Zanussi *
75108d43a5fSTom Zanussi * When tracing_map_init() returns, tracing_map_elt elements can be
75208d43a5fSTom Zanussi * inserted into the map using tracing_map_insert(). When called,
75308d43a5fSTom Zanussi * tracing_map_insert() grabs a free tracing_map_elt from the pool, or
75408d43a5fSTom Zanussi * finds an existing match in the map and in either case returns it.
75508d43a5fSTom Zanussi * The client can then use tracing_map_update_sum() and
75608d43a5fSTom Zanussi * tracing_map_read_sum() to update or read a given sum field for the
75708d43a5fSTom Zanussi * tracing_map_elt.
75808d43a5fSTom Zanussi *
75908d43a5fSTom Zanussi * The client can at any point retrieve and traverse the current set
76008d43a5fSTom Zanussi * of inserted tracing_map_elts in a tracing_map, via
76108d43a5fSTom Zanussi * tracing_map_sort_entries(). Sorting can be done on any field,
76208d43a5fSTom Zanussi * including keys.
76308d43a5fSTom Zanussi *
76408d43a5fSTom Zanussi * See tracing_map.h for a description of tracing_map_ops.
76508d43a5fSTom Zanussi *
76608d43a5fSTom Zanussi * Return: the tracing_map pointer if successful, ERR_PTR if not.
76708d43a5fSTom Zanussi */
tracing_map_create(unsigned int map_bits,unsigned int key_size,const struct tracing_map_ops * ops,void * private_data)76808d43a5fSTom Zanussi struct tracing_map *tracing_map_create(unsigned int map_bits,
76908d43a5fSTom Zanussi unsigned int key_size,
77008d43a5fSTom Zanussi const struct tracing_map_ops *ops,
77108d43a5fSTom Zanussi void *private_data)
77208d43a5fSTom Zanussi {
77308d43a5fSTom Zanussi struct tracing_map *map;
77408d43a5fSTom Zanussi unsigned int i;
77508d43a5fSTom Zanussi
77608d43a5fSTom Zanussi if (map_bits < TRACING_MAP_BITS_MIN ||
77708d43a5fSTom Zanussi map_bits > TRACING_MAP_BITS_MAX)
77808d43a5fSTom Zanussi return ERR_PTR(-EINVAL);
77908d43a5fSTom Zanussi
78008d43a5fSTom Zanussi map = kzalloc(sizeof(*map), GFP_KERNEL);
78108d43a5fSTom Zanussi if (!map)
78208d43a5fSTom Zanussi return ERR_PTR(-ENOMEM);
78308d43a5fSTom Zanussi
78408d43a5fSTom Zanussi map->map_bits = map_bits;
78508d43a5fSTom Zanussi map->max_elts = (1 << map_bits);
786bcf86c01STze-nan Wu atomic_set(&map->next_elt, 0);
78708d43a5fSTom Zanussi
78808d43a5fSTom Zanussi map->map_size = (1 << (map_bits + 1));
78908d43a5fSTom Zanussi map->ops = ops;
79008d43a5fSTom Zanussi
79108d43a5fSTom Zanussi map->private_data = private_data;
79208d43a5fSTom Zanussi
79308d43a5fSTom Zanussi map->map = tracing_map_array_alloc(map->map_size,
79408d43a5fSTom Zanussi sizeof(struct tracing_map_entry));
79508d43a5fSTom Zanussi if (!map->map)
79608d43a5fSTom Zanussi goto free;
79708d43a5fSTom Zanussi
79808d43a5fSTom Zanussi map->key_size = key_size;
79908d43a5fSTom Zanussi for (i = 0; i < TRACING_MAP_KEYS_MAX; i++)
80008d43a5fSTom Zanussi map->key_idx[i] = -1;
80108d43a5fSTom Zanussi out:
80208d43a5fSTom Zanussi return map;
80308d43a5fSTom Zanussi free:
80408d43a5fSTom Zanussi tracing_map_destroy(map);
80508d43a5fSTom Zanussi map = ERR_PTR(-ENOMEM);
80608d43a5fSTom Zanussi
80708d43a5fSTom Zanussi goto out;
80808d43a5fSTom Zanussi }
80908d43a5fSTom Zanussi
81008d43a5fSTom Zanussi /**
81108d43a5fSTom Zanussi * tracing_map_init - Allocate and clear a map's tracing_map_elts
81208d43a5fSTom Zanussi * @map: The tracing_map to initialize
81308d43a5fSTom Zanussi *
81408d43a5fSTom Zanussi * Allocates a clears a pool of tracing_map_elts equal to the
81508d43a5fSTom Zanussi * user-specified size of 2 ** map_bits (internally maintained as
81608d43a5fSTom Zanussi * 'max_elts' in struct tracing_map). Before using, the map fields
81708d43a5fSTom Zanussi * should be added to the map with tracing_map_add_sum_field() and
81808d43a5fSTom Zanussi * tracing_map_add_key_field(). tracing_map_init() should then be
81908d43a5fSTom Zanussi * called to allocate the array of tracing_map_elts, in order to avoid
82008d43a5fSTom Zanussi * allocating anything in the map insertion path. The user-specified
82108d43a5fSTom Zanussi * map size reflects the max number of elements requested by the user
82208d43a5fSTom Zanussi * - internally we double that in order to keep the table sparse and
82308d43a5fSTom Zanussi * keep collisions manageable.
82408d43a5fSTom Zanussi *
82508d43a5fSTom Zanussi * See tracing_map.h for a description of tracing_map_ops.
82608d43a5fSTom Zanussi *
82708d43a5fSTom Zanussi * Return: the tracing_map pointer if successful, ERR_PTR if not.
82808d43a5fSTom Zanussi */
tracing_map_init(struct tracing_map * map)82908d43a5fSTom Zanussi int tracing_map_init(struct tracing_map *map)
83008d43a5fSTom Zanussi {
83108d43a5fSTom Zanussi int err;
83208d43a5fSTom Zanussi
83308d43a5fSTom Zanussi if (map->n_fields < 2)
83408d43a5fSTom Zanussi return -EINVAL; /* need at least 1 key and 1 val */
83508d43a5fSTom Zanussi
83608d43a5fSTom Zanussi err = tracing_map_alloc_elts(map);
83708d43a5fSTom Zanussi if (err)
83808d43a5fSTom Zanussi return err;
83908d43a5fSTom Zanussi
84008d43a5fSTom Zanussi tracing_map_clear(map);
84108d43a5fSTom Zanussi
84208d43a5fSTom Zanussi return err;
84308d43a5fSTom Zanussi }
84408d43a5fSTom Zanussi
cmp_entries_dup(const void * A,const void * B)8457ce1bb83SKalesh Singh static int cmp_entries_dup(const void *A, const void *B)
84608d43a5fSTom Zanussi {
8477ce1bb83SKalesh Singh const struct tracing_map_sort_entry *a, *b;
84808d43a5fSTom Zanussi
8497ce1bb83SKalesh Singh a = *(const struct tracing_map_sort_entry **)A;
8507ce1bb83SKalesh Singh b = *(const struct tracing_map_sort_entry **)B;
8517ce1bb83SKalesh Singh
852*e63fbd5fSKuan-Wei Chiu return memcmp(a->key, b->key, a->elt->map->key_size);
85308d43a5fSTom Zanussi }
85408d43a5fSTom Zanussi
cmp_entries_sum(const void * A,const void * B)8557ce1bb83SKalesh Singh static int cmp_entries_sum(const void *A, const void *B)
85608d43a5fSTom Zanussi {
85708d43a5fSTom Zanussi const struct tracing_map_elt *elt_a, *elt_b;
8587ce1bb83SKalesh Singh const struct tracing_map_sort_entry *a, *b;
85908d43a5fSTom Zanussi struct tracing_map_sort_key *sort_key;
86008d43a5fSTom Zanussi struct tracing_map_field *field;
86108d43a5fSTom Zanussi tracing_map_cmp_fn_t cmp_fn;
86208d43a5fSTom Zanussi void *val_a, *val_b;
86308d43a5fSTom Zanussi int ret = 0;
86408d43a5fSTom Zanussi
8657ce1bb83SKalesh Singh a = *(const struct tracing_map_sort_entry **)A;
8667ce1bb83SKalesh Singh b = *(const struct tracing_map_sort_entry **)B;
8677ce1bb83SKalesh Singh
8687ce1bb83SKalesh Singh elt_a = a->elt;
8697ce1bb83SKalesh Singh elt_b = b->elt;
87008d43a5fSTom Zanussi
87108d43a5fSTom Zanussi sort_key = &elt_a->map->sort_key;
87208d43a5fSTom Zanussi
87308d43a5fSTom Zanussi field = &elt_a->fields[sort_key->field_idx];
87408d43a5fSTom Zanussi cmp_fn = field->cmp_fn;
87508d43a5fSTom Zanussi
87608d43a5fSTom Zanussi val_a = &elt_a->fields[sort_key->field_idx].sum;
87708d43a5fSTom Zanussi val_b = &elt_b->fields[sort_key->field_idx].sum;
87808d43a5fSTom Zanussi
87908d43a5fSTom Zanussi ret = cmp_fn(val_a, val_b);
88008d43a5fSTom Zanussi if (sort_key->descending)
88108d43a5fSTom Zanussi ret = -ret;
88208d43a5fSTom Zanussi
88308d43a5fSTom Zanussi return ret;
88408d43a5fSTom Zanussi }
88508d43a5fSTom Zanussi
cmp_entries_key(const void * A,const void * B)8867ce1bb83SKalesh Singh static int cmp_entries_key(const void *A, const void *B)
88708d43a5fSTom Zanussi {
88808d43a5fSTom Zanussi const struct tracing_map_elt *elt_a, *elt_b;
8897ce1bb83SKalesh Singh const struct tracing_map_sort_entry *a, *b;
89008d43a5fSTom Zanussi struct tracing_map_sort_key *sort_key;
89108d43a5fSTom Zanussi struct tracing_map_field *field;
89208d43a5fSTom Zanussi tracing_map_cmp_fn_t cmp_fn;
89308d43a5fSTom Zanussi void *val_a, *val_b;
89408d43a5fSTom Zanussi int ret = 0;
89508d43a5fSTom Zanussi
8967ce1bb83SKalesh Singh a = *(const struct tracing_map_sort_entry **)A;
8977ce1bb83SKalesh Singh b = *(const struct tracing_map_sort_entry **)B;
8987ce1bb83SKalesh Singh
8997ce1bb83SKalesh Singh elt_a = a->elt;
9007ce1bb83SKalesh Singh elt_b = b->elt;
90108d43a5fSTom Zanussi
90208d43a5fSTom Zanussi sort_key = &elt_a->map->sort_key;
90308d43a5fSTom Zanussi
90408d43a5fSTom Zanussi field = &elt_a->fields[sort_key->field_idx];
90508d43a5fSTom Zanussi
90608d43a5fSTom Zanussi cmp_fn = field->cmp_fn;
90708d43a5fSTom Zanussi
90808d43a5fSTom Zanussi val_a = elt_a->key + field->offset;
90908d43a5fSTom Zanussi val_b = elt_b->key + field->offset;
91008d43a5fSTom Zanussi
91108d43a5fSTom Zanussi ret = cmp_fn(val_a, val_b);
91208d43a5fSTom Zanussi if (sort_key->descending)
91308d43a5fSTom Zanussi ret = -ret;
91408d43a5fSTom Zanussi
91508d43a5fSTom Zanussi return ret;
91608d43a5fSTom Zanussi }
91708d43a5fSTom Zanussi
destroy_sort_entry(struct tracing_map_sort_entry * entry)91808d43a5fSTom Zanussi static void destroy_sort_entry(struct tracing_map_sort_entry *entry)
91908d43a5fSTom Zanussi {
92008d43a5fSTom Zanussi if (!entry)
92108d43a5fSTom Zanussi return;
92208d43a5fSTom Zanussi
92308d43a5fSTom Zanussi if (entry->elt_copied)
92408d43a5fSTom Zanussi tracing_map_elt_free(entry->elt);
92508d43a5fSTom Zanussi
92608d43a5fSTom Zanussi kfree(entry);
92708d43a5fSTom Zanussi }
92808d43a5fSTom Zanussi
92908d43a5fSTom Zanussi /**
93008d43a5fSTom Zanussi * tracing_map_destroy_sort_entries - Destroy an array of sort entries
93108d43a5fSTom Zanussi * @entries: The entries to destroy
93208d43a5fSTom Zanussi * @n_entries: The number of entries in the array
93308d43a5fSTom Zanussi *
93408d43a5fSTom Zanussi * Destroy the elements returned by a tracing_map_sort_entries() call.
93508d43a5fSTom Zanussi */
tracing_map_destroy_sort_entries(struct tracing_map_sort_entry ** entries,unsigned int n_entries)93608d43a5fSTom Zanussi void tracing_map_destroy_sort_entries(struct tracing_map_sort_entry **entries,
93708d43a5fSTom Zanussi unsigned int n_entries)
93808d43a5fSTom Zanussi {
93908d43a5fSTom Zanussi unsigned int i;
94008d43a5fSTom Zanussi
94108d43a5fSTom Zanussi for (i = 0; i < n_entries; i++)
94208d43a5fSTom Zanussi destroy_sort_entry(entries[i]);
94308d43a5fSTom Zanussi
94408d43a5fSTom Zanussi vfree(entries);
94508d43a5fSTom Zanussi }
94608d43a5fSTom Zanussi
94708d43a5fSTom Zanussi static struct tracing_map_sort_entry *
create_sort_entry(void * key,struct tracing_map_elt * elt)94808d43a5fSTom Zanussi create_sort_entry(void *key, struct tracing_map_elt *elt)
94908d43a5fSTom Zanussi {
95008d43a5fSTom Zanussi struct tracing_map_sort_entry *sort_entry;
95108d43a5fSTom Zanussi
95208d43a5fSTom Zanussi sort_entry = kzalloc(sizeof(*sort_entry), GFP_KERNEL);
95308d43a5fSTom Zanussi if (!sort_entry)
95408d43a5fSTom Zanussi return NULL;
95508d43a5fSTom Zanussi
95608d43a5fSTom Zanussi sort_entry->key = key;
95708d43a5fSTom Zanussi sort_entry->elt = elt;
95808d43a5fSTom Zanussi
95908d43a5fSTom Zanussi return sort_entry;
96008d43a5fSTom Zanussi }
96108d43a5fSTom Zanussi
detect_dups(struct tracing_map_sort_entry ** sort_entries,int n_entries,unsigned int key_size)962c193707dSVedang Patel static void detect_dups(struct tracing_map_sort_entry **sort_entries,
96308d43a5fSTom Zanussi int n_entries, unsigned int key_size)
96408d43a5fSTom Zanussi {
965ed87277fSChen Zhongjin unsigned int total_dups = 0;
966c193707dSVedang Patel int i;
96708d43a5fSTom Zanussi void *key;
96808d43a5fSTom Zanussi
96908d43a5fSTom Zanussi if (n_entries < 2)
970c193707dSVedang Patel return;
97108d43a5fSTom Zanussi
97208d43a5fSTom Zanussi sort(sort_entries, n_entries, sizeof(struct tracing_map_sort_entry *),
97308d43a5fSTom Zanussi (int (*)(const void *, const void *))cmp_entries_dup, NULL);
97408d43a5fSTom Zanussi
97508d43a5fSTom Zanussi key = sort_entries[0]->key;
97608d43a5fSTom Zanussi for (i = 1; i < n_entries; i++) {
97708d43a5fSTom Zanussi if (!memcmp(sort_entries[i]->key, key, key_size)) {
978ed87277fSChen Zhongjin total_dups++;
97908d43a5fSTom Zanussi continue;
98008d43a5fSTom Zanussi }
98108d43a5fSTom Zanussi key = sort_entries[i]->key;
98208d43a5fSTom Zanussi }
98308d43a5fSTom Zanussi
984c193707dSVedang Patel WARN_ONCE(total_dups > 0,
985c193707dSVedang Patel "Duplicates detected: %d\n", total_dups);
98608d43a5fSTom Zanussi }
98708d43a5fSTom Zanussi
is_key(struct tracing_map * map,unsigned int field_idx)98808d43a5fSTom Zanussi static bool is_key(struct tracing_map *map, unsigned int field_idx)
98908d43a5fSTom Zanussi {
99008d43a5fSTom Zanussi unsigned int i;
99108d43a5fSTom Zanussi
99208d43a5fSTom Zanussi for (i = 0; i < map->n_keys; i++)
99308d43a5fSTom Zanussi if (map->key_idx[i] == field_idx)
99408d43a5fSTom Zanussi return true;
99508d43a5fSTom Zanussi return false;
99608d43a5fSTom Zanussi }
99708d43a5fSTom Zanussi
sort_secondary(struct tracing_map * map,const struct tracing_map_sort_entry ** entries,unsigned int n_entries,struct tracing_map_sort_key * primary_key,struct tracing_map_sort_key * secondary_key)99808d43a5fSTom Zanussi static void sort_secondary(struct tracing_map *map,
99908d43a5fSTom Zanussi const struct tracing_map_sort_entry **entries,
100008d43a5fSTom Zanussi unsigned int n_entries,
100108d43a5fSTom Zanussi struct tracing_map_sort_key *primary_key,
100208d43a5fSTom Zanussi struct tracing_map_sort_key *secondary_key)
100308d43a5fSTom Zanussi {
10047ce1bb83SKalesh Singh int (*primary_fn)(const void *, const void *);
10057ce1bb83SKalesh Singh int (*secondary_fn)(const void *, const void *);
100608d43a5fSTom Zanussi unsigned i, start = 0, n_sub = 1;
100708d43a5fSTom Zanussi
100808d43a5fSTom Zanussi if (is_key(map, primary_key->field_idx))
100908d43a5fSTom Zanussi primary_fn = cmp_entries_key;
101008d43a5fSTom Zanussi else
101108d43a5fSTom Zanussi primary_fn = cmp_entries_sum;
101208d43a5fSTom Zanussi
101308d43a5fSTom Zanussi if (is_key(map, secondary_key->field_idx))
101408d43a5fSTom Zanussi secondary_fn = cmp_entries_key;
101508d43a5fSTom Zanussi else
101608d43a5fSTom Zanussi secondary_fn = cmp_entries_sum;
101708d43a5fSTom Zanussi
101808d43a5fSTom Zanussi for (i = 0; i < n_entries - 1; i++) {
101908d43a5fSTom Zanussi const struct tracing_map_sort_entry **a = &entries[i];
102008d43a5fSTom Zanussi const struct tracing_map_sort_entry **b = &entries[i + 1];
102108d43a5fSTom Zanussi
102208d43a5fSTom Zanussi if (primary_fn(a, b) == 0) {
102308d43a5fSTom Zanussi n_sub++;
102408d43a5fSTom Zanussi if (i < n_entries - 2)
102508d43a5fSTom Zanussi continue;
102608d43a5fSTom Zanussi }
102708d43a5fSTom Zanussi
102808d43a5fSTom Zanussi if (n_sub < 2) {
102908d43a5fSTom Zanussi start = i + 1;
103008d43a5fSTom Zanussi n_sub = 1;
103108d43a5fSTom Zanussi continue;
103208d43a5fSTom Zanussi }
103308d43a5fSTom Zanussi
103408d43a5fSTom Zanussi set_sort_key(map, secondary_key);
103508d43a5fSTom Zanussi sort(&entries[start], n_sub,
103608d43a5fSTom Zanussi sizeof(struct tracing_map_sort_entry *),
103708d43a5fSTom Zanussi (int (*)(const void *, const void *))secondary_fn, NULL);
103808d43a5fSTom Zanussi set_sort_key(map, primary_key);
103908d43a5fSTom Zanussi
104008d43a5fSTom Zanussi start = i + 1;
104108d43a5fSTom Zanussi n_sub = 1;
104208d43a5fSTom Zanussi }
104308d43a5fSTom Zanussi }
104408d43a5fSTom Zanussi
104508d43a5fSTom Zanussi /**
104608d43a5fSTom Zanussi * tracing_map_sort_entries - Sort the current set of tracing_map_elts in a map
104708d43a5fSTom Zanussi * @map: The tracing_map
1048adaa0a9fSYang Li * @sort_keys: The sort key to use for sorting
1049adaa0a9fSYang Li * @n_sort_keys: hitcount, always have at least one
105008d43a5fSTom Zanussi * @sort_entries: outval: pointer to allocated and sorted array of entries
105108d43a5fSTom Zanussi *
105208d43a5fSTom Zanussi * tracing_map_sort_entries() sorts the current set of entries in the
105308d43a5fSTom Zanussi * map and returns the list of tracing_map_sort_entries containing
105408d43a5fSTom Zanussi * them to the client in the sort_entries param. The client can
105508d43a5fSTom Zanussi * access the struct tracing_map_elt element of interest directly as
105608d43a5fSTom Zanussi * the 'elt' field of a returned struct tracing_map_sort_entry object.
105708d43a5fSTom Zanussi *
105808d43a5fSTom Zanussi * The sort_key has only two fields: idx and descending. 'idx' refers
105908d43a5fSTom Zanussi * to the index of the field added via tracing_map_add_sum_field() or
106008d43a5fSTom Zanussi * tracing_map_add_key_field() when the tracing_map was initialized.
106108d43a5fSTom Zanussi * 'descending' is a flag that if set reverses the sort order, which
106208d43a5fSTom Zanussi * by default is ascending.
106308d43a5fSTom Zanussi *
106408d43a5fSTom Zanussi * The client should not hold on to the returned array but should use
106508d43a5fSTom Zanussi * it and call tracing_map_destroy_sort_entries() when done.
106608d43a5fSTom Zanussi *
106708d43a5fSTom Zanussi * Return: the number of sort_entries in the struct tracing_map_sort_entry
106808d43a5fSTom Zanussi * array, negative on error
106908d43a5fSTom Zanussi */
tracing_map_sort_entries(struct tracing_map * map,struct tracing_map_sort_key * sort_keys,unsigned int n_sort_keys,struct tracing_map_sort_entry *** sort_entries)107008d43a5fSTom Zanussi int tracing_map_sort_entries(struct tracing_map *map,
107108d43a5fSTom Zanussi struct tracing_map_sort_key *sort_keys,
107208d43a5fSTom Zanussi unsigned int n_sort_keys,
107308d43a5fSTom Zanussi struct tracing_map_sort_entry ***sort_entries)
107408d43a5fSTom Zanussi {
10757ce1bb83SKalesh Singh int (*cmp_entries_fn)(const void *, const void *);
107608d43a5fSTom Zanussi struct tracing_map_sort_entry *sort_entry, **entries;
107708d43a5fSTom Zanussi int i, n_entries, ret;
107808d43a5fSTom Zanussi
107942bc47b3SKees Cook entries = vmalloc(array_size(sizeof(sort_entry), map->max_elts));
108008d43a5fSTom Zanussi if (!entries)
108108d43a5fSTom Zanussi return -ENOMEM;
108208d43a5fSTom Zanussi
108308d43a5fSTom Zanussi for (i = 0, n_entries = 0; i < map->map_size; i++) {
108408d43a5fSTom Zanussi struct tracing_map_entry *entry;
108508d43a5fSTom Zanussi
108608d43a5fSTom Zanussi entry = TRACING_MAP_ENTRY(map->map, i);
108708d43a5fSTom Zanussi
108808d43a5fSTom Zanussi if (!entry->key || !entry->val)
108908d43a5fSTom Zanussi continue;
109008d43a5fSTom Zanussi
109108d43a5fSTom Zanussi entries[n_entries] = create_sort_entry(entry->val->key,
109208d43a5fSTom Zanussi entry->val);
109308d43a5fSTom Zanussi if (!entries[n_entries++]) {
109408d43a5fSTom Zanussi ret = -ENOMEM;
109508d43a5fSTom Zanussi goto free;
109608d43a5fSTom Zanussi }
109708d43a5fSTom Zanussi }
109808d43a5fSTom Zanussi
109908d43a5fSTom Zanussi if (n_entries == 0) {
110008d43a5fSTom Zanussi ret = 0;
110108d43a5fSTom Zanussi goto free;
110208d43a5fSTom Zanussi }
110308d43a5fSTom Zanussi
110408d43a5fSTom Zanussi if (n_entries == 1) {
110508d43a5fSTom Zanussi *sort_entries = entries;
110608d43a5fSTom Zanussi return 1;
110708d43a5fSTom Zanussi }
110808d43a5fSTom Zanussi
1109c193707dSVedang Patel detect_dups(entries, n_entries, map->key_size);
111008d43a5fSTom Zanussi
111108d43a5fSTom Zanussi if (is_key(map, sort_keys[0].field_idx))
111208d43a5fSTom Zanussi cmp_entries_fn = cmp_entries_key;
111308d43a5fSTom Zanussi else
111408d43a5fSTom Zanussi cmp_entries_fn = cmp_entries_sum;
111508d43a5fSTom Zanussi
111608d43a5fSTom Zanussi set_sort_key(map, &sort_keys[0]);
111708d43a5fSTom Zanussi
111808d43a5fSTom Zanussi sort(entries, n_entries, sizeof(struct tracing_map_sort_entry *),
111908d43a5fSTom Zanussi (int (*)(const void *, const void *))cmp_entries_fn, NULL);
112008d43a5fSTom Zanussi
112108d43a5fSTom Zanussi if (n_sort_keys > 1)
112208d43a5fSTom Zanussi sort_secondary(map,
112308d43a5fSTom Zanussi (const struct tracing_map_sort_entry **)entries,
112408d43a5fSTom Zanussi n_entries,
112508d43a5fSTom Zanussi &sort_keys[0],
112608d43a5fSTom Zanussi &sort_keys[1]);
112708d43a5fSTom Zanussi
112808d43a5fSTom Zanussi *sort_entries = entries;
112908d43a5fSTom Zanussi
113008d43a5fSTom Zanussi return n_entries;
113108d43a5fSTom Zanussi free:
113208d43a5fSTom Zanussi tracing_map_destroy_sort_entries(entries, n_entries);
113308d43a5fSTom Zanussi
113408d43a5fSTom Zanussi return ret;
113508d43a5fSTom Zanussi }
1136