xref: /linux-6.15/kernel/trace/tracing_map.c (revision c193707d)
108d43a5fSTom Zanussi /*
208d43a5fSTom Zanussi  * tracing_map - lock-free map for tracing
308d43a5fSTom Zanussi  *
408d43a5fSTom Zanussi  * This program is free software; you can redistribute it and/or modify
508d43a5fSTom Zanussi  * it under the terms of the GNU General Public License as published by
608d43a5fSTom Zanussi  * the Free Software Foundation; either version 2 of the License, or
708d43a5fSTom Zanussi  * (at your option) any later version.
808d43a5fSTom Zanussi  *
908d43a5fSTom Zanussi  * This program is distributed in the hope that it will be useful,
1008d43a5fSTom Zanussi  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1108d43a5fSTom Zanussi  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1208d43a5fSTom Zanussi  * GNU General Public License for more details.
1308d43a5fSTom Zanussi  *
1408d43a5fSTom Zanussi  * Copyright (C) 2015 Tom Zanussi <[email protected]>
1508d43a5fSTom Zanussi  *
1608d43a5fSTom Zanussi  * tracing_map implementation inspired by lock-free map algorithms
1708d43a5fSTom Zanussi  * originated by Dr. Cliff Click:
1808d43a5fSTom Zanussi  *
1908d43a5fSTom Zanussi  * http://www.azulsystems.com/blog/cliff/2007-03-26-non-blocking-hashtable
2008d43a5fSTom Zanussi  * http://www.azulsystems.com/events/javaone_2007/2007_LockFreeHash.pdf
2108d43a5fSTom Zanussi  */
2208d43a5fSTom Zanussi 
2308d43a5fSTom Zanussi #include <linux/vmalloc.h>
2408d43a5fSTom Zanussi #include <linux/jhash.h>
2508d43a5fSTom Zanussi #include <linux/slab.h>
2608d43a5fSTom Zanussi #include <linux/sort.h>
2708d43a5fSTom Zanussi 
2808d43a5fSTom Zanussi #include "tracing_map.h"
2908d43a5fSTom Zanussi #include "trace.h"
3008d43a5fSTom Zanussi 
3108d43a5fSTom Zanussi /*
3208d43a5fSTom Zanussi  * NOTE: For a detailed description of the data structures used by
3308d43a5fSTom Zanussi  * these functions (such as tracing_map_elt) please see the overview
3408d43a5fSTom Zanussi  * of tracing_map data structures at the beginning of tracing_map.h.
3508d43a5fSTom Zanussi  */
3608d43a5fSTom Zanussi 
3708d43a5fSTom Zanussi /**
3808d43a5fSTom Zanussi  * tracing_map_update_sum - Add a value to a tracing_map_elt's sum field
3908d43a5fSTom Zanussi  * @elt: The tracing_map_elt
4008d43a5fSTom Zanussi  * @i: The index of the given sum associated with the tracing_map_elt
4108d43a5fSTom Zanussi  * @n: The value to add to the sum
4208d43a5fSTom Zanussi  *
4308d43a5fSTom Zanussi  * Add n to sum i associated with the specified tracing_map_elt
4408d43a5fSTom Zanussi  * instance.  The index i is the index returned by the call to
4508d43a5fSTom Zanussi  * tracing_map_add_sum_field() when the tracing map was set up.
4608d43a5fSTom Zanussi  */
4708d43a5fSTom Zanussi void tracing_map_update_sum(struct tracing_map_elt *elt, unsigned int i, u64 n)
4808d43a5fSTom Zanussi {
4908d43a5fSTom Zanussi 	atomic64_add(n, &elt->fields[i].sum);
5008d43a5fSTom Zanussi }
5108d43a5fSTom Zanussi 
5208d43a5fSTom Zanussi /**
5308d43a5fSTom Zanussi  * tracing_map_read_sum - Return the value of a tracing_map_elt's sum field
5408d43a5fSTom Zanussi  * @elt: The tracing_map_elt
5508d43a5fSTom Zanussi  * @i: The index of the given sum associated with the tracing_map_elt
5608d43a5fSTom Zanussi  *
5708d43a5fSTom Zanussi  * Retrieve the value of the sum i associated with the specified
5808d43a5fSTom Zanussi  * tracing_map_elt instance.  The index i is the index returned by the
5908d43a5fSTom Zanussi  * call to tracing_map_add_sum_field() when the tracing map was set
6008d43a5fSTom Zanussi  * up.
6108d43a5fSTom Zanussi  *
6208d43a5fSTom Zanussi  * Return: The sum associated with field i for elt.
6308d43a5fSTom Zanussi  */
6408d43a5fSTom Zanussi u64 tracing_map_read_sum(struct tracing_map_elt *elt, unsigned int i)
6508d43a5fSTom Zanussi {
6608d43a5fSTom Zanussi 	return (u64)atomic64_read(&elt->fields[i].sum);
6708d43a5fSTom Zanussi }
6808d43a5fSTom Zanussi 
6908d43a5fSTom Zanussi int tracing_map_cmp_string(void *val_a, void *val_b)
7008d43a5fSTom Zanussi {
7108d43a5fSTom Zanussi 	char *a = val_a;
7208d43a5fSTom Zanussi 	char *b = val_b;
7308d43a5fSTom Zanussi 
7408d43a5fSTom Zanussi 	return strcmp(a, b);
7508d43a5fSTom Zanussi }
7608d43a5fSTom Zanussi 
7708d43a5fSTom Zanussi int tracing_map_cmp_none(void *val_a, void *val_b)
7808d43a5fSTom Zanussi {
7908d43a5fSTom Zanussi 	return 0;
8008d43a5fSTom Zanussi }
8108d43a5fSTom Zanussi 
8208d43a5fSTom Zanussi static int tracing_map_cmp_atomic64(void *val_a, void *val_b)
8308d43a5fSTom Zanussi {
8408d43a5fSTom Zanussi 	u64 a = atomic64_read((atomic64_t *)val_a);
8508d43a5fSTom Zanussi 	u64 b = atomic64_read((atomic64_t *)val_b);
8608d43a5fSTom Zanussi 
8708d43a5fSTom Zanussi 	return (a > b) ? 1 : ((a < b) ? -1 : 0);
8808d43a5fSTom Zanussi }
8908d43a5fSTom Zanussi 
9008d43a5fSTom Zanussi #define DEFINE_TRACING_MAP_CMP_FN(type)					\
9108d43a5fSTom Zanussi static int tracing_map_cmp_##type(void *val_a, void *val_b)		\
9208d43a5fSTom Zanussi {									\
9308d43a5fSTom Zanussi 	type a = *(type *)val_a;					\
9408d43a5fSTom Zanussi 	type b = *(type *)val_b;					\
9508d43a5fSTom Zanussi 									\
9608d43a5fSTom Zanussi 	return (a > b) ? 1 : ((a < b) ? -1 : 0);			\
9708d43a5fSTom Zanussi }
9808d43a5fSTom Zanussi 
9908d43a5fSTom Zanussi DEFINE_TRACING_MAP_CMP_FN(s64);
10008d43a5fSTom Zanussi DEFINE_TRACING_MAP_CMP_FN(u64);
10108d43a5fSTom Zanussi DEFINE_TRACING_MAP_CMP_FN(s32);
10208d43a5fSTom Zanussi DEFINE_TRACING_MAP_CMP_FN(u32);
10308d43a5fSTom Zanussi DEFINE_TRACING_MAP_CMP_FN(s16);
10408d43a5fSTom Zanussi DEFINE_TRACING_MAP_CMP_FN(u16);
10508d43a5fSTom Zanussi DEFINE_TRACING_MAP_CMP_FN(s8);
10608d43a5fSTom Zanussi DEFINE_TRACING_MAP_CMP_FN(u8);
10708d43a5fSTom Zanussi 
10808d43a5fSTom Zanussi tracing_map_cmp_fn_t tracing_map_cmp_num(int field_size,
10908d43a5fSTom Zanussi 					 int field_is_signed)
11008d43a5fSTom Zanussi {
11108d43a5fSTom Zanussi 	tracing_map_cmp_fn_t fn = tracing_map_cmp_none;
11208d43a5fSTom Zanussi 
11308d43a5fSTom Zanussi 	switch (field_size) {
11408d43a5fSTom Zanussi 	case 8:
11508d43a5fSTom Zanussi 		if (field_is_signed)
11608d43a5fSTom Zanussi 			fn = tracing_map_cmp_s64;
11708d43a5fSTom Zanussi 		else
11808d43a5fSTom Zanussi 			fn = tracing_map_cmp_u64;
11908d43a5fSTom Zanussi 		break;
12008d43a5fSTom Zanussi 	case 4:
12108d43a5fSTom Zanussi 		if (field_is_signed)
12208d43a5fSTom Zanussi 			fn = tracing_map_cmp_s32;
12308d43a5fSTom Zanussi 		else
12408d43a5fSTom Zanussi 			fn = tracing_map_cmp_u32;
12508d43a5fSTom Zanussi 		break;
12608d43a5fSTom Zanussi 	case 2:
12708d43a5fSTom Zanussi 		if (field_is_signed)
12808d43a5fSTom Zanussi 			fn = tracing_map_cmp_s16;
12908d43a5fSTom Zanussi 		else
13008d43a5fSTom Zanussi 			fn = tracing_map_cmp_u16;
13108d43a5fSTom Zanussi 		break;
13208d43a5fSTom Zanussi 	case 1:
13308d43a5fSTom Zanussi 		if (field_is_signed)
13408d43a5fSTom Zanussi 			fn = tracing_map_cmp_s8;
13508d43a5fSTom Zanussi 		else
13608d43a5fSTom Zanussi 			fn = tracing_map_cmp_u8;
13708d43a5fSTom Zanussi 		break;
13808d43a5fSTom Zanussi 	}
13908d43a5fSTom Zanussi 
14008d43a5fSTom Zanussi 	return fn;
14108d43a5fSTom Zanussi }
14208d43a5fSTom Zanussi 
14308d43a5fSTom Zanussi static int tracing_map_add_field(struct tracing_map *map,
14408d43a5fSTom Zanussi 				 tracing_map_cmp_fn_t cmp_fn)
14508d43a5fSTom Zanussi {
14608d43a5fSTom Zanussi 	int ret = -EINVAL;
14708d43a5fSTom Zanussi 
14808d43a5fSTom Zanussi 	if (map->n_fields < TRACING_MAP_FIELDS_MAX) {
14908d43a5fSTom Zanussi 		ret = map->n_fields;
15008d43a5fSTom Zanussi 		map->fields[map->n_fields++].cmp_fn = cmp_fn;
15108d43a5fSTom Zanussi 	}
15208d43a5fSTom Zanussi 
15308d43a5fSTom Zanussi 	return ret;
15408d43a5fSTom Zanussi }
15508d43a5fSTom Zanussi 
15608d43a5fSTom Zanussi /**
15708d43a5fSTom Zanussi  * tracing_map_add_sum_field - Add a field describing a tracing_map sum
15808d43a5fSTom Zanussi  * @map: The tracing_map
15908d43a5fSTom Zanussi  *
16008d43a5fSTom Zanussi  * Add a sum field to the key and return the index identifying it in
16108d43a5fSTom Zanussi  * the map and associated tracing_map_elts.  This is the index used
16208d43a5fSTom Zanussi  * for instance to update a sum for a particular tracing_map_elt using
16308d43a5fSTom Zanussi  * tracing_map_update_sum() or reading it via tracing_map_read_sum().
16408d43a5fSTom Zanussi  *
16508d43a5fSTom Zanussi  * Return: The index identifying the field in the map and associated
1663b772b96STom Zanussi  * tracing_map_elts, or -EINVAL on error.
16708d43a5fSTom Zanussi  */
16808d43a5fSTom Zanussi int tracing_map_add_sum_field(struct tracing_map *map)
16908d43a5fSTom Zanussi {
17008d43a5fSTom Zanussi 	return tracing_map_add_field(map, tracing_map_cmp_atomic64);
17108d43a5fSTom Zanussi }
17208d43a5fSTom Zanussi 
17308d43a5fSTom Zanussi /**
17408d43a5fSTom Zanussi  * tracing_map_add_key_field - Add a field describing a tracing_map key
17508d43a5fSTom Zanussi  * @map: The tracing_map
17608d43a5fSTom Zanussi  * @offset: The offset within the key
17708d43a5fSTom Zanussi  * @cmp_fn: The comparison function that will be used to sort on the key
17808d43a5fSTom Zanussi  *
17908d43a5fSTom Zanussi  * Let the map know there is a key and that if it's used as a sort key
18008d43a5fSTom Zanussi  * to use cmp_fn.
18108d43a5fSTom Zanussi  *
18208d43a5fSTom Zanussi  * A key can be a subset of a compound key; for that purpose, the
18308d43a5fSTom Zanussi  * offset param is used to describe where within the the compound key
18408d43a5fSTom Zanussi  * the key referenced by this key field resides.
18508d43a5fSTom Zanussi  *
18608d43a5fSTom Zanussi  * Return: The index identifying the field in the map and associated
1873b772b96STom Zanussi  * tracing_map_elts, or -EINVAL on error.
18808d43a5fSTom Zanussi  */
18908d43a5fSTom Zanussi int tracing_map_add_key_field(struct tracing_map *map,
19008d43a5fSTom Zanussi 			      unsigned int offset,
19108d43a5fSTom Zanussi 			      tracing_map_cmp_fn_t cmp_fn)
19208d43a5fSTom Zanussi 
19308d43a5fSTom Zanussi {
19408d43a5fSTom Zanussi 	int idx = tracing_map_add_field(map, cmp_fn);
19508d43a5fSTom Zanussi 
19608d43a5fSTom Zanussi 	if (idx < 0)
19708d43a5fSTom Zanussi 		return idx;
19808d43a5fSTom Zanussi 
19908d43a5fSTom Zanussi 	map->fields[idx].offset = offset;
20008d43a5fSTom Zanussi 
20108d43a5fSTom Zanussi 	map->key_idx[map->n_keys++] = idx;
20208d43a5fSTom Zanussi 
20308d43a5fSTom Zanussi 	return idx;
20408d43a5fSTom Zanussi }
20508d43a5fSTom Zanussi 
20608d43a5fSTom Zanussi void tracing_map_array_clear(struct tracing_map_array *a)
20708d43a5fSTom Zanussi {
20808d43a5fSTom Zanussi 	unsigned int i;
20908d43a5fSTom Zanussi 
21008d43a5fSTom Zanussi 	if (!a->pages)
21108d43a5fSTom Zanussi 		return;
21208d43a5fSTom Zanussi 
21308d43a5fSTom Zanussi 	for (i = 0; i < a->n_pages; i++)
21408d43a5fSTom Zanussi 		memset(a->pages[i], 0, PAGE_SIZE);
21508d43a5fSTom Zanussi }
21608d43a5fSTom Zanussi 
21708d43a5fSTom Zanussi void tracing_map_array_free(struct tracing_map_array *a)
21808d43a5fSTom Zanussi {
21908d43a5fSTom Zanussi 	unsigned int i;
22008d43a5fSTom Zanussi 
22108d43a5fSTom Zanussi 	if (!a)
22208d43a5fSTom Zanussi 		return;
22308d43a5fSTom Zanussi 
224475bb3c6SChunyu Hu 	if (!a->pages)
225475bb3c6SChunyu Hu 		goto free;
22608d43a5fSTom Zanussi 
22708d43a5fSTom Zanussi 	for (i = 0; i < a->n_pages; i++) {
22808d43a5fSTom Zanussi 		if (!a->pages[i])
22908d43a5fSTom Zanussi 			break;
23008d43a5fSTom Zanussi 		free_page((unsigned long)a->pages[i]);
23108d43a5fSTom Zanussi 	}
232475bb3c6SChunyu Hu 
233475bb3c6SChunyu Hu 	kfree(a->pages);
234475bb3c6SChunyu Hu 
235475bb3c6SChunyu Hu  free:
236475bb3c6SChunyu Hu 	kfree(a);
23708d43a5fSTom Zanussi }
23808d43a5fSTom Zanussi 
23908d43a5fSTom Zanussi struct tracing_map_array *tracing_map_array_alloc(unsigned int n_elts,
24008d43a5fSTom Zanussi 						  unsigned int entry_size)
24108d43a5fSTom Zanussi {
24208d43a5fSTom Zanussi 	struct tracing_map_array *a;
24308d43a5fSTom Zanussi 	unsigned int i;
24408d43a5fSTom Zanussi 
24508d43a5fSTom Zanussi 	a = kzalloc(sizeof(*a), GFP_KERNEL);
24608d43a5fSTom Zanussi 	if (!a)
24708d43a5fSTom Zanussi 		return NULL;
24808d43a5fSTom Zanussi 
24908d43a5fSTom Zanussi 	a->entry_size_shift = fls(roundup_pow_of_two(entry_size) - 1);
25008d43a5fSTom Zanussi 	a->entries_per_page = PAGE_SIZE / (1 << a->entry_size_shift);
25108d43a5fSTom Zanussi 	a->n_pages = n_elts / a->entries_per_page;
25208d43a5fSTom Zanussi 	if (!a->n_pages)
25308d43a5fSTom Zanussi 		a->n_pages = 1;
25408d43a5fSTom Zanussi 	a->entry_shift = fls(a->entries_per_page) - 1;
25508d43a5fSTom Zanussi 	a->entry_mask = (1 << a->entry_shift) - 1;
25608d43a5fSTom Zanussi 
25708d43a5fSTom Zanussi 	a->pages = kcalloc(a->n_pages, sizeof(void *), GFP_KERNEL);
25808d43a5fSTom Zanussi 	if (!a->pages)
25908d43a5fSTom Zanussi 		goto free;
26008d43a5fSTom Zanussi 
26108d43a5fSTom Zanussi 	for (i = 0; i < a->n_pages; i++) {
26208d43a5fSTom Zanussi 		a->pages[i] = (void *)get_zeroed_page(GFP_KERNEL);
26308d43a5fSTom Zanussi 		if (!a->pages[i])
26408d43a5fSTom Zanussi 			goto free;
26508d43a5fSTom Zanussi 	}
26608d43a5fSTom Zanussi  out:
26708d43a5fSTom Zanussi 	return a;
26808d43a5fSTom Zanussi  free:
26908d43a5fSTom Zanussi 	tracing_map_array_free(a);
27008d43a5fSTom Zanussi 	a = NULL;
27108d43a5fSTom Zanussi 
27208d43a5fSTom Zanussi 	goto out;
27308d43a5fSTom Zanussi }
27408d43a5fSTom Zanussi 
27508d43a5fSTom Zanussi static void tracing_map_elt_clear(struct tracing_map_elt *elt)
27608d43a5fSTom Zanussi {
27708d43a5fSTom Zanussi 	unsigned i;
27808d43a5fSTom Zanussi 
27908d43a5fSTom Zanussi 	for (i = 0; i < elt->map->n_fields; i++)
28008d43a5fSTom Zanussi 		if (elt->fields[i].cmp_fn == tracing_map_cmp_atomic64)
28108d43a5fSTom Zanussi 			atomic64_set(&elt->fields[i].sum, 0);
28208d43a5fSTom Zanussi 
28308d43a5fSTom Zanussi 	if (elt->map->ops && elt->map->ops->elt_clear)
28408d43a5fSTom Zanussi 		elt->map->ops->elt_clear(elt);
28508d43a5fSTom Zanussi }
28608d43a5fSTom Zanussi 
28708d43a5fSTom Zanussi static void tracing_map_elt_init_fields(struct tracing_map_elt *elt)
28808d43a5fSTom Zanussi {
28908d43a5fSTom Zanussi 	unsigned int i;
29008d43a5fSTom Zanussi 
29108d43a5fSTom Zanussi 	tracing_map_elt_clear(elt);
29208d43a5fSTom Zanussi 
29308d43a5fSTom Zanussi 	for (i = 0; i < elt->map->n_fields; i++) {
29408d43a5fSTom Zanussi 		elt->fields[i].cmp_fn = elt->map->fields[i].cmp_fn;
29508d43a5fSTom Zanussi 
29608d43a5fSTom Zanussi 		if (elt->fields[i].cmp_fn != tracing_map_cmp_atomic64)
29708d43a5fSTom Zanussi 			elt->fields[i].offset = elt->map->fields[i].offset;
29808d43a5fSTom Zanussi 	}
29908d43a5fSTom Zanussi }
30008d43a5fSTom Zanussi 
30108d43a5fSTom Zanussi static void tracing_map_elt_free(struct tracing_map_elt *elt)
30208d43a5fSTom Zanussi {
30308d43a5fSTom Zanussi 	if (!elt)
30408d43a5fSTom Zanussi 		return;
30508d43a5fSTom Zanussi 
30608d43a5fSTom Zanussi 	if (elt->map->ops && elt->map->ops->elt_free)
30708d43a5fSTom Zanussi 		elt->map->ops->elt_free(elt);
30808d43a5fSTom Zanussi 	kfree(elt->fields);
30908d43a5fSTom Zanussi 	kfree(elt->key);
31008d43a5fSTom Zanussi 	kfree(elt);
31108d43a5fSTom Zanussi }
31208d43a5fSTom Zanussi 
31308d43a5fSTom Zanussi static struct tracing_map_elt *tracing_map_elt_alloc(struct tracing_map *map)
31408d43a5fSTom Zanussi {
31508d43a5fSTom Zanussi 	struct tracing_map_elt *elt;
31608d43a5fSTom Zanussi 	int err = 0;
31708d43a5fSTom Zanussi 
31808d43a5fSTom Zanussi 	elt = kzalloc(sizeof(*elt), GFP_KERNEL);
31908d43a5fSTom Zanussi 	if (!elt)
32008d43a5fSTom Zanussi 		return ERR_PTR(-ENOMEM);
32108d43a5fSTom Zanussi 
32208d43a5fSTom Zanussi 	elt->map = map;
32308d43a5fSTom Zanussi 
32408d43a5fSTom Zanussi 	elt->key = kzalloc(map->key_size, GFP_KERNEL);
32508d43a5fSTom Zanussi 	if (!elt->key) {
32608d43a5fSTom Zanussi 		err = -ENOMEM;
32708d43a5fSTom Zanussi 		goto free;
32808d43a5fSTom Zanussi 	}
32908d43a5fSTom Zanussi 
33008d43a5fSTom Zanussi 	elt->fields = kcalloc(map->n_fields, sizeof(*elt->fields), GFP_KERNEL);
33108d43a5fSTom Zanussi 	if (!elt->fields) {
33208d43a5fSTom Zanussi 		err = -ENOMEM;
33308d43a5fSTom Zanussi 		goto free;
33408d43a5fSTom Zanussi 	}
33508d43a5fSTom Zanussi 
33608d43a5fSTom Zanussi 	tracing_map_elt_init_fields(elt);
33708d43a5fSTom Zanussi 
33808d43a5fSTom Zanussi 	if (map->ops && map->ops->elt_alloc) {
33908d43a5fSTom Zanussi 		err = map->ops->elt_alloc(elt);
34008d43a5fSTom Zanussi 		if (err)
34108d43a5fSTom Zanussi 			goto free;
34208d43a5fSTom Zanussi 	}
34308d43a5fSTom Zanussi 	return elt;
34408d43a5fSTom Zanussi  free:
34508d43a5fSTom Zanussi 	tracing_map_elt_free(elt);
34608d43a5fSTom Zanussi 
34708d43a5fSTom Zanussi 	return ERR_PTR(err);
34808d43a5fSTom Zanussi }
34908d43a5fSTom Zanussi 
35008d43a5fSTom Zanussi static struct tracing_map_elt *get_free_elt(struct tracing_map *map)
35108d43a5fSTom Zanussi {
35208d43a5fSTom Zanussi 	struct tracing_map_elt *elt = NULL;
35308d43a5fSTom Zanussi 	int idx;
35408d43a5fSTom Zanussi 
35508d43a5fSTom Zanussi 	idx = atomic_inc_return(&map->next_elt);
35608d43a5fSTom Zanussi 	if (idx < map->max_elts) {
35708d43a5fSTom Zanussi 		elt = *(TRACING_MAP_ELT(map->elts, idx));
35808d43a5fSTom Zanussi 		if (map->ops && map->ops->elt_init)
35908d43a5fSTom Zanussi 			map->ops->elt_init(elt);
36008d43a5fSTom Zanussi 	}
36108d43a5fSTom Zanussi 
36208d43a5fSTom Zanussi 	return elt;
36308d43a5fSTom Zanussi }
36408d43a5fSTom Zanussi 
36508d43a5fSTom Zanussi static void tracing_map_free_elts(struct tracing_map *map)
36608d43a5fSTom Zanussi {
36708d43a5fSTom Zanussi 	unsigned int i;
36808d43a5fSTom Zanussi 
36908d43a5fSTom Zanussi 	if (!map->elts)
37008d43a5fSTom Zanussi 		return;
37108d43a5fSTom Zanussi 
3726e4cf657STom Zanussi 	for (i = 0; i < map->max_elts; i++) {
37308d43a5fSTom Zanussi 		tracing_map_elt_free(*(TRACING_MAP_ELT(map->elts, i)));
3746e4cf657STom Zanussi 		*(TRACING_MAP_ELT(map->elts, i)) = NULL;
3756e4cf657STom Zanussi 	}
37608d43a5fSTom Zanussi 
37708d43a5fSTom Zanussi 	tracing_map_array_free(map->elts);
3786e4cf657STom Zanussi 	map->elts = NULL;
37908d43a5fSTom Zanussi }
38008d43a5fSTom Zanussi 
38108d43a5fSTom Zanussi static int tracing_map_alloc_elts(struct tracing_map *map)
38208d43a5fSTom Zanussi {
38308d43a5fSTom Zanussi 	unsigned int i;
38408d43a5fSTom Zanussi 
38508d43a5fSTom Zanussi 	map->elts = tracing_map_array_alloc(map->max_elts,
38608d43a5fSTom Zanussi 					    sizeof(struct tracing_map_elt *));
38708d43a5fSTom Zanussi 	if (!map->elts)
38808d43a5fSTom Zanussi 		return -ENOMEM;
38908d43a5fSTom Zanussi 
39008d43a5fSTom Zanussi 	for (i = 0; i < map->max_elts; i++) {
39108d43a5fSTom Zanussi 		*(TRACING_MAP_ELT(map->elts, i)) = tracing_map_elt_alloc(map);
3926e4cf657STom Zanussi 		if (IS_ERR(*(TRACING_MAP_ELT(map->elts, i)))) {
3936e4cf657STom Zanussi 			*(TRACING_MAP_ELT(map->elts, i)) = NULL;
39408d43a5fSTom Zanussi 			tracing_map_free_elts(map);
39508d43a5fSTom Zanussi 
39608d43a5fSTom Zanussi 			return -ENOMEM;
39708d43a5fSTom Zanussi 		}
39808d43a5fSTom Zanussi 	}
39908d43a5fSTom Zanussi 
40008d43a5fSTom Zanussi 	return 0;
40108d43a5fSTom Zanussi }
40208d43a5fSTom Zanussi 
40308d43a5fSTom Zanussi static inline bool keys_match(void *key, void *test_key, unsigned key_size)
40408d43a5fSTom Zanussi {
40508d43a5fSTom Zanussi 	bool match = true;
40608d43a5fSTom Zanussi 
40708d43a5fSTom Zanussi 	if (memcmp(key, test_key, key_size))
40808d43a5fSTom Zanussi 		match = false;
40908d43a5fSTom Zanussi 
41008d43a5fSTom Zanussi 	return match;
41108d43a5fSTom Zanussi }
41208d43a5fSTom Zanussi 
41308d43a5fSTom Zanussi static inline struct tracing_map_elt *
41408d43a5fSTom Zanussi __tracing_map_insert(struct tracing_map *map, void *key, bool lookup_only)
41508d43a5fSTom Zanussi {
41608d43a5fSTom Zanussi 	u32 idx, key_hash, test_key;
417cbf4100eSVedang Patel 	int dup_try = 0;
41808d43a5fSTom Zanussi 	struct tracing_map_entry *entry;
419cbf4100eSVedang Patel 	struct tracing_map_elt *val;
42008d43a5fSTom Zanussi 
42108d43a5fSTom Zanussi 	key_hash = jhash(key, map->key_size, 0);
42208d43a5fSTom Zanussi 	if (key_hash == 0)
42308d43a5fSTom Zanussi 		key_hash = 1;
42408d43a5fSTom Zanussi 	idx = key_hash >> (32 - (map->map_bits + 1));
42508d43a5fSTom Zanussi 
42608d43a5fSTom Zanussi 	while (1) {
42708d43a5fSTom Zanussi 		idx &= (map->map_size - 1);
42808d43a5fSTom Zanussi 		entry = TRACING_MAP_ENTRY(map->map, idx);
42908d43a5fSTom Zanussi 		test_key = entry->key;
43008d43a5fSTom Zanussi 
431cbf4100eSVedang Patel 		if (test_key && test_key == key_hash) {
432cbf4100eSVedang Patel 			val = READ_ONCE(entry->val);
433cbf4100eSVedang Patel 			if (val &&
434cbf4100eSVedang Patel 			    keys_match(key, val->key, map->key_size)) {
43583c07eccSTom Zanussi 				if (!lookup_only)
43608d43a5fSTom Zanussi 					atomic64_inc(&map->hits);
437cbf4100eSVedang Patel 				return val;
438cbf4100eSVedang Patel 			} else if (unlikely(!val)) {
439cbf4100eSVedang Patel 				/*
440cbf4100eSVedang Patel 				 * The key is present. But, val (pointer to elt
441cbf4100eSVedang Patel 				 * struct) is still NULL. which means some other
442cbf4100eSVedang Patel 				 * thread is in the process of inserting an
443cbf4100eSVedang Patel 				 * element.
444cbf4100eSVedang Patel 				 *
445cbf4100eSVedang Patel 				 * On top of that, it's key_hash is same as the
446cbf4100eSVedang Patel 				 * one being inserted right now. So, it's
447cbf4100eSVedang Patel 				 * possible that the element has the same
448cbf4100eSVedang Patel 				 * key as well.
449cbf4100eSVedang Patel 				 */
450cbf4100eSVedang Patel 
451cbf4100eSVedang Patel 				dup_try++;
452cbf4100eSVedang Patel 				if (dup_try > map->map_size) {
453cbf4100eSVedang Patel 					atomic64_inc(&map->drops);
454cbf4100eSVedang Patel 					break;
455cbf4100eSVedang Patel 				}
456cbf4100eSVedang Patel 				continue;
457cbf4100eSVedang Patel 			}
45808d43a5fSTom Zanussi 		}
45908d43a5fSTom Zanussi 
46008d43a5fSTom Zanussi 		if (!test_key) {
46108d43a5fSTom Zanussi 			if (lookup_only)
46208d43a5fSTom Zanussi 				break;
46308d43a5fSTom Zanussi 
46408d43a5fSTom Zanussi 			if (!cmpxchg(&entry->key, 0, key_hash)) {
46508d43a5fSTom Zanussi 				struct tracing_map_elt *elt;
46608d43a5fSTom Zanussi 
46708d43a5fSTom Zanussi 				elt = get_free_elt(map);
46808d43a5fSTom Zanussi 				if (!elt) {
46908d43a5fSTom Zanussi 					atomic64_inc(&map->drops);
47008d43a5fSTom Zanussi 					entry->key = 0;
47108d43a5fSTom Zanussi 					break;
47208d43a5fSTom Zanussi 				}
47308d43a5fSTom Zanussi 
47408d43a5fSTom Zanussi 				memcpy(elt->key, key, map->key_size);
47508d43a5fSTom Zanussi 				entry->val = elt;
47608d43a5fSTom Zanussi 				atomic64_inc(&map->hits);
47708d43a5fSTom Zanussi 
47808d43a5fSTom Zanussi 				return entry->val;
479cbf4100eSVedang Patel 			} else {
480cbf4100eSVedang Patel 				/*
481cbf4100eSVedang Patel 				 * cmpxchg() failed. Loop around once
482cbf4100eSVedang Patel 				 * more to check what key was inserted.
483cbf4100eSVedang Patel 				 */
484cbf4100eSVedang Patel 				dup_try++;
485cbf4100eSVedang Patel 				continue;
48608d43a5fSTom Zanussi 			}
48708d43a5fSTom Zanussi 		}
48808d43a5fSTom Zanussi 
48908d43a5fSTom Zanussi 		idx++;
49008d43a5fSTom Zanussi 	}
49108d43a5fSTom Zanussi 
49208d43a5fSTom Zanussi 	return NULL;
49308d43a5fSTom Zanussi }
49408d43a5fSTom Zanussi 
49508d43a5fSTom Zanussi /**
49608d43a5fSTom Zanussi  * tracing_map_insert - Insert key and/or retrieve val from a tracing_map
49708d43a5fSTom Zanussi  * @map: The tracing_map to insert into
49808d43a5fSTom Zanussi  * @key: The key to insert
49908d43a5fSTom Zanussi  *
50008d43a5fSTom Zanussi  * Inserts a key into a tracing_map and creates and returns a new
50108d43a5fSTom Zanussi  * tracing_map_elt for it, or if the key has already been inserted by
50208d43a5fSTom Zanussi  * a previous call, returns the tracing_map_elt already associated
50308d43a5fSTom Zanussi  * with it.  When the map was created, the number of elements to be
50408d43a5fSTom Zanussi  * allocated for the map was specified (internally maintained as
50508d43a5fSTom Zanussi  * 'max_elts' in struct tracing_map), and that number of
50608d43a5fSTom Zanussi  * tracing_map_elts was created by tracing_map_init().  This is the
50708d43a5fSTom Zanussi  * pre-allocated pool of tracing_map_elts that tracing_map_insert()
50808d43a5fSTom Zanussi  * will allocate from when adding new keys.  Once that pool is
50908d43a5fSTom Zanussi  * exhausted, tracing_map_insert() is useless and will return NULL to
51008d43a5fSTom Zanussi  * signal that state.  There are two user-visible tracing_map
51108d43a5fSTom Zanussi  * variables, 'hits' and 'drops', which are updated by this function.
51208d43a5fSTom Zanussi  * Every time an element is either successfully inserted or retrieved,
51308d43a5fSTom Zanussi  * the 'hits' value is incrememented.  Every time an element insertion
51408d43a5fSTom Zanussi  * fails, the 'drops' value is incremented.
51508d43a5fSTom Zanussi  *
51608d43a5fSTom Zanussi  * This is a lock-free tracing map insertion function implementing a
51708d43a5fSTom Zanussi  * modified form of Cliff Click's basic insertion algorithm.  It
51808d43a5fSTom Zanussi  * requires the table size be a power of two.  To prevent any
51908d43a5fSTom Zanussi  * possibility of an infinite loop we always make the internal table
52008d43a5fSTom Zanussi  * size double the size of the requested table size (max_elts * 2).
52108d43a5fSTom Zanussi  * Likewise, we never reuse a slot or resize or delete elements - when
52208d43a5fSTom Zanussi  * we've reached max_elts entries, we simply return NULL once we've
52308d43a5fSTom Zanussi  * run out of entries.  Readers can at any point in time traverse the
52408d43a5fSTom Zanussi  * tracing map and safely access the key/val pairs.
52508d43a5fSTom Zanussi  *
52608d43a5fSTom Zanussi  * Return: the tracing_map_elt pointer val associated with the key.
52708d43a5fSTom Zanussi  * If this was a newly inserted key, the val will be a newly allocated
52808d43a5fSTom Zanussi  * and associated tracing_map_elt pointer val.  If the key wasn't
52908d43a5fSTom Zanussi  * found and the pool of tracing_map_elts has been exhausted, NULL is
53008d43a5fSTom Zanussi  * returned and no further insertions will succeed.
53108d43a5fSTom Zanussi  */
53208d43a5fSTom Zanussi struct tracing_map_elt *tracing_map_insert(struct tracing_map *map, void *key)
53308d43a5fSTom Zanussi {
53408d43a5fSTom Zanussi 	return __tracing_map_insert(map, key, false);
53508d43a5fSTom Zanussi }
53608d43a5fSTom Zanussi 
53708d43a5fSTom Zanussi /**
53808d43a5fSTom Zanussi  * tracing_map_lookup - Retrieve val from a tracing_map
53908d43a5fSTom Zanussi  * @map: The tracing_map to perform the lookup on
54008d43a5fSTom Zanussi  * @key: The key to look up
54108d43a5fSTom Zanussi  *
54208d43a5fSTom Zanussi  * Looks up key in tracing_map and if found returns the matching
54308d43a5fSTom Zanussi  * tracing_map_elt.  This is a lock-free lookup; see
54408d43a5fSTom Zanussi  * tracing_map_insert() for details on tracing_map and how it works.
54508d43a5fSTom Zanussi  * Every time an element is retrieved, the 'hits' value is
54608d43a5fSTom Zanussi  * incrememented.  There is one user-visible tracing_map variable,
54708d43a5fSTom Zanussi  * 'hits', which is updated by this function.  Every time an element
54808d43a5fSTom Zanussi  * is successfully retrieved, the 'hits' value is incrememented.  The
54908d43a5fSTom Zanussi  * 'drops' value is never updated by this function.
55008d43a5fSTom Zanussi  *
55108d43a5fSTom Zanussi  * Return: the tracing_map_elt pointer val associated with the key.
55208d43a5fSTom Zanussi  * If the key wasn't found, NULL is returned.
55308d43a5fSTom Zanussi  */
55408d43a5fSTom Zanussi struct tracing_map_elt *tracing_map_lookup(struct tracing_map *map, void *key)
55508d43a5fSTom Zanussi {
55608d43a5fSTom Zanussi 	return __tracing_map_insert(map, key, true);
55708d43a5fSTom Zanussi }
55808d43a5fSTom Zanussi 
55908d43a5fSTom Zanussi /**
56008d43a5fSTom Zanussi  * tracing_map_destroy - Destroy a tracing_map
56108d43a5fSTom Zanussi  * @map: The tracing_map to destroy
56208d43a5fSTom Zanussi  *
56308d43a5fSTom Zanussi  * Frees a tracing_map along with its associated array of
56408d43a5fSTom Zanussi  * tracing_map_elts.
56508d43a5fSTom Zanussi  *
56608d43a5fSTom Zanussi  * Callers should make sure there are no readers or writers actively
56708d43a5fSTom Zanussi  * reading or inserting into the map before calling this.
56808d43a5fSTom Zanussi  */
56908d43a5fSTom Zanussi void tracing_map_destroy(struct tracing_map *map)
57008d43a5fSTom Zanussi {
57108d43a5fSTom Zanussi 	if (!map)
57208d43a5fSTom Zanussi 		return;
57308d43a5fSTom Zanussi 
57408d43a5fSTom Zanussi 	tracing_map_free_elts(map);
57508d43a5fSTom Zanussi 
57608d43a5fSTom Zanussi 	tracing_map_array_free(map->map);
57708d43a5fSTom Zanussi 	kfree(map);
57808d43a5fSTom Zanussi }
57908d43a5fSTom Zanussi 
58008d43a5fSTom Zanussi /**
58108d43a5fSTom Zanussi  * tracing_map_clear - Clear a tracing_map
58208d43a5fSTom Zanussi  * @map: The tracing_map to clear
58308d43a5fSTom Zanussi  *
58408d43a5fSTom Zanussi  * Resets the tracing map to a cleared or initial state.  The
58508d43a5fSTom Zanussi  * tracing_map_elts are all cleared, and the array of struct
58608d43a5fSTom Zanussi  * tracing_map_entry is reset to an initialized state.
58708d43a5fSTom Zanussi  *
58808d43a5fSTom Zanussi  * Callers should make sure there are no writers actively inserting
58908d43a5fSTom Zanussi  * into the map before calling this.
59008d43a5fSTom Zanussi  */
59108d43a5fSTom Zanussi void tracing_map_clear(struct tracing_map *map)
59208d43a5fSTom Zanussi {
59308d43a5fSTom Zanussi 	unsigned int i;
59408d43a5fSTom Zanussi 
59508d43a5fSTom Zanussi 	atomic_set(&map->next_elt, -1);
59608d43a5fSTom Zanussi 	atomic64_set(&map->hits, 0);
59708d43a5fSTom Zanussi 	atomic64_set(&map->drops, 0);
59808d43a5fSTom Zanussi 
59908d43a5fSTom Zanussi 	tracing_map_array_clear(map->map);
60008d43a5fSTom Zanussi 
60108d43a5fSTom Zanussi 	for (i = 0; i < map->max_elts; i++)
60208d43a5fSTom Zanussi 		tracing_map_elt_clear(*(TRACING_MAP_ELT(map->elts, i)));
60308d43a5fSTom Zanussi }
60408d43a5fSTom Zanussi 
60508d43a5fSTom Zanussi static void set_sort_key(struct tracing_map *map,
60608d43a5fSTom Zanussi 			 struct tracing_map_sort_key *sort_key)
60708d43a5fSTom Zanussi {
60808d43a5fSTom Zanussi 	map->sort_key = *sort_key;
60908d43a5fSTom Zanussi }
61008d43a5fSTom Zanussi 
61108d43a5fSTom Zanussi /**
61208d43a5fSTom Zanussi  * tracing_map_create - Create a lock-free map and element pool
61308d43a5fSTom Zanussi  * @map_bits: The size of the map (2 ** map_bits)
61408d43a5fSTom Zanussi  * @key_size: The size of the key for the map in bytes
61508d43a5fSTom Zanussi  * @ops: Optional client-defined tracing_map_ops instance
61608d43a5fSTom Zanussi  * @private_data: Client data associated with the map
61708d43a5fSTom Zanussi  *
61808d43a5fSTom Zanussi  * Creates and sets up a map to contain 2 ** map_bits number of
61908d43a5fSTom Zanussi  * elements (internally maintained as 'max_elts' in struct
62008d43a5fSTom Zanussi  * tracing_map).  Before using, map fields should be added to the map
62108d43a5fSTom Zanussi  * with tracing_map_add_sum_field() and tracing_map_add_key_field().
62208d43a5fSTom Zanussi  * tracing_map_init() should then be called to allocate the array of
62308d43a5fSTom Zanussi  * tracing_map_elts, in order to avoid allocating anything in the map
62408d43a5fSTom Zanussi  * insertion path.  The user-specified map size reflects the maximum
62508d43a5fSTom Zanussi  * number of elements that can be contained in the table requested by
62608d43a5fSTom Zanussi  * the user - internally we double that in order to keep the table
62708d43a5fSTom Zanussi  * sparse and keep collisions manageable.
62808d43a5fSTom Zanussi  *
62908d43a5fSTom Zanussi  * A tracing_map is a special-purpose map designed to aggregate or
63008d43a5fSTom Zanussi  * 'sum' one or more values associated with a specific object of type
63108d43a5fSTom Zanussi  * tracing_map_elt, which is attached by the map to a given key.
63208d43a5fSTom Zanussi  *
63308d43a5fSTom Zanussi  * tracing_map_create() sets up the map itself, and provides
63408d43a5fSTom Zanussi  * operations for inserting tracing_map_elts, but doesn't allocate the
63508d43a5fSTom Zanussi  * tracing_map_elts themselves, or provide a means for describing the
63608d43a5fSTom Zanussi  * keys or sums associated with the tracing_map_elts.  All
63708d43a5fSTom Zanussi  * tracing_map_elts for a given map have the same set of sums and
63808d43a5fSTom Zanussi  * keys, which are defined by the client using the functions
63908d43a5fSTom Zanussi  * tracing_map_add_key_field() and tracing_map_add_sum_field().  Once
64008d43a5fSTom Zanussi  * the fields are defined, the pool of elements allocated for the map
64108d43a5fSTom Zanussi  * can be created, which occurs when the client code calls
64208d43a5fSTom Zanussi  * tracing_map_init().
64308d43a5fSTom Zanussi  *
64408d43a5fSTom Zanussi  * When tracing_map_init() returns, tracing_map_elt elements can be
64508d43a5fSTom Zanussi  * inserted into the map using tracing_map_insert().  When called,
64608d43a5fSTom Zanussi  * tracing_map_insert() grabs a free tracing_map_elt from the pool, or
64708d43a5fSTom Zanussi  * finds an existing match in the map and in either case returns it.
64808d43a5fSTom Zanussi  * The client can then use tracing_map_update_sum() and
64908d43a5fSTom Zanussi  * tracing_map_read_sum() to update or read a given sum field for the
65008d43a5fSTom Zanussi  * tracing_map_elt.
65108d43a5fSTom Zanussi  *
65208d43a5fSTom Zanussi  * The client can at any point retrieve and traverse the current set
65308d43a5fSTom Zanussi  * of inserted tracing_map_elts in a tracing_map, via
65408d43a5fSTom Zanussi  * tracing_map_sort_entries().  Sorting can be done on any field,
65508d43a5fSTom Zanussi  * including keys.
65608d43a5fSTom Zanussi  *
65708d43a5fSTom Zanussi  * See tracing_map.h for a description of tracing_map_ops.
65808d43a5fSTom Zanussi  *
65908d43a5fSTom Zanussi  * Return: the tracing_map pointer if successful, ERR_PTR if not.
66008d43a5fSTom Zanussi  */
66108d43a5fSTom Zanussi struct tracing_map *tracing_map_create(unsigned int map_bits,
66208d43a5fSTom Zanussi 				       unsigned int key_size,
66308d43a5fSTom Zanussi 				       const struct tracing_map_ops *ops,
66408d43a5fSTom Zanussi 				       void *private_data)
66508d43a5fSTom Zanussi {
66608d43a5fSTom Zanussi 	struct tracing_map *map;
66708d43a5fSTom Zanussi 	unsigned int i;
66808d43a5fSTom Zanussi 
66908d43a5fSTom Zanussi 	if (map_bits < TRACING_MAP_BITS_MIN ||
67008d43a5fSTom Zanussi 	    map_bits > TRACING_MAP_BITS_MAX)
67108d43a5fSTom Zanussi 		return ERR_PTR(-EINVAL);
67208d43a5fSTom Zanussi 
67308d43a5fSTom Zanussi 	map = kzalloc(sizeof(*map), GFP_KERNEL);
67408d43a5fSTom Zanussi 	if (!map)
67508d43a5fSTom Zanussi 		return ERR_PTR(-ENOMEM);
67608d43a5fSTom Zanussi 
67708d43a5fSTom Zanussi 	map->map_bits = map_bits;
67808d43a5fSTom Zanussi 	map->max_elts = (1 << map_bits);
67908d43a5fSTom Zanussi 	atomic_set(&map->next_elt, -1);
68008d43a5fSTom Zanussi 
68108d43a5fSTom Zanussi 	map->map_size = (1 << (map_bits + 1));
68208d43a5fSTom Zanussi 	map->ops = ops;
68308d43a5fSTom Zanussi 
68408d43a5fSTom Zanussi 	map->private_data = private_data;
68508d43a5fSTom Zanussi 
68608d43a5fSTom Zanussi 	map->map = tracing_map_array_alloc(map->map_size,
68708d43a5fSTom Zanussi 					   sizeof(struct tracing_map_entry));
68808d43a5fSTom Zanussi 	if (!map->map)
68908d43a5fSTom Zanussi 		goto free;
69008d43a5fSTom Zanussi 
69108d43a5fSTom Zanussi 	map->key_size = key_size;
69208d43a5fSTom Zanussi 	for (i = 0; i < TRACING_MAP_KEYS_MAX; i++)
69308d43a5fSTom Zanussi 		map->key_idx[i] = -1;
69408d43a5fSTom Zanussi  out:
69508d43a5fSTom Zanussi 	return map;
69608d43a5fSTom Zanussi  free:
69708d43a5fSTom Zanussi 	tracing_map_destroy(map);
69808d43a5fSTom Zanussi 	map = ERR_PTR(-ENOMEM);
69908d43a5fSTom Zanussi 
70008d43a5fSTom Zanussi 	goto out;
70108d43a5fSTom Zanussi }
70208d43a5fSTom Zanussi 
70308d43a5fSTom Zanussi /**
70408d43a5fSTom Zanussi  * tracing_map_init - Allocate and clear a map's tracing_map_elts
70508d43a5fSTom Zanussi  * @map: The tracing_map to initialize
70608d43a5fSTom Zanussi  *
70708d43a5fSTom Zanussi  * Allocates a clears a pool of tracing_map_elts equal to the
70808d43a5fSTom Zanussi  * user-specified size of 2 ** map_bits (internally maintained as
70908d43a5fSTom Zanussi  * 'max_elts' in struct tracing_map).  Before using, the map fields
71008d43a5fSTom Zanussi  * should be added to the map with tracing_map_add_sum_field() and
71108d43a5fSTom Zanussi  * tracing_map_add_key_field().  tracing_map_init() should then be
71208d43a5fSTom Zanussi  * called to allocate the array of tracing_map_elts, in order to avoid
71308d43a5fSTom Zanussi  * allocating anything in the map insertion path.  The user-specified
71408d43a5fSTom Zanussi  * map size reflects the max number of elements requested by the user
71508d43a5fSTom Zanussi  * - internally we double that in order to keep the table sparse and
71608d43a5fSTom Zanussi  * keep collisions manageable.
71708d43a5fSTom Zanussi  *
71808d43a5fSTom Zanussi  * See tracing_map.h for a description of tracing_map_ops.
71908d43a5fSTom Zanussi  *
72008d43a5fSTom Zanussi  * Return: the tracing_map pointer if successful, ERR_PTR if not.
72108d43a5fSTom Zanussi  */
72208d43a5fSTom Zanussi int tracing_map_init(struct tracing_map *map)
72308d43a5fSTom Zanussi {
72408d43a5fSTom Zanussi 	int err;
72508d43a5fSTom Zanussi 
72608d43a5fSTom Zanussi 	if (map->n_fields < 2)
72708d43a5fSTom Zanussi 		return -EINVAL; /* need at least 1 key and 1 val */
72808d43a5fSTom Zanussi 
72908d43a5fSTom Zanussi 	err = tracing_map_alloc_elts(map);
73008d43a5fSTom Zanussi 	if (err)
73108d43a5fSTom Zanussi 		return err;
73208d43a5fSTom Zanussi 
73308d43a5fSTom Zanussi 	tracing_map_clear(map);
73408d43a5fSTom Zanussi 
73508d43a5fSTom Zanussi 	return err;
73608d43a5fSTom Zanussi }
73708d43a5fSTom Zanussi 
73808d43a5fSTom Zanussi static int cmp_entries_dup(const struct tracing_map_sort_entry **a,
73908d43a5fSTom Zanussi 			   const struct tracing_map_sort_entry **b)
74008d43a5fSTom Zanussi {
74108d43a5fSTom Zanussi 	int ret = 0;
74208d43a5fSTom Zanussi 
74308d43a5fSTom Zanussi 	if (memcmp((*a)->key, (*b)->key, (*a)->elt->map->key_size))
74408d43a5fSTom Zanussi 		ret = 1;
74508d43a5fSTom Zanussi 
74608d43a5fSTom Zanussi 	return ret;
74708d43a5fSTom Zanussi }
74808d43a5fSTom Zanussi 
74908d43a5fSTom Zanussi static int cmp_entries_sum(const struct tracing_map_sort_entry **a,
75008d43a5fSTom Zanussi 			   const struct tracing_map_sort_entry **b)
75108d43a5fSTom Zanussi {
75208d43a5fSTom Zanussi 	const struct tracing_map_elt *elt_a, *elt_b;
75308d43a5fSTom Zanussi 	struct tracing_map_sort_key *sort_key;
75408d43a5fSTom Zanussi 	struct tracing_map_field *field;
75508d43a5fSTom Zanussi 	tracing_map_cmp_fn_t cmp_fn;
75608d43a5fSTom Zanussi 	void *val_a, *val_b;
75708d43a5fSTom Zanussi 	int ret = 0;
75808d43a5fSTom Zanussi 
75908d43a5fSTom Zanussi 	elt_a = (*a)->elt;
76008d43a5fSTom Zanussi 	elt_b = (*b)->elt;
76108d43a5fSTom Zanussi 
76208d43a5fSTom Zanussi 	sort_key = &elt_a->map->sort_key;
76308d43a5fSTom Zanussi 
76408d43a5fSTom Zanussi 	field = &elt_a->fields[sort_key->field_idx];
76508d43a5fSTom Zanussi 	cmp_fn = field->cmp_fn;
76608d43a5fSTom Zanussi 
76708d43a5fSTom Zanussi 	val_a = &elt_a->fields[sort_key->field_idx].sum;
76808d43a5fSTom Zanussi 	val_b = &elt_b->fields[sort_key->field_idx].sum;
76908d43a5fSTom Zanussi 
77008d43a5fSTom Zanussi 	ret = cmp_fn(val_a, val_b);
77108d43a5fSTom Zanussi 	if (sort_key->descending)
77208d43a5fSTom Zanussi 		ret = -ret;
77308d43a5fSTom Zanussi 
77408d43a5fSTom Zanussi 	return ret;
77508d43a5fSTom Zanussi }
77608d43a5fSTom Zanussi 
77708d43a5fSTom Zanussi static int cmp_entries_key(const struct tracing_map_sort_entry **a,
77808d43a5fSTom Zanussi 			   const struct tracing_map_sort_entry **b)
77908d43a5fSTom Zanussi {
78008d43a5fSTom Zanussi 	const struct tracing_map_elt *elt_a, *elt_b;
78108d43a5fSTom Zanussi 	struct tracing_map_sort_key *sort_key;
78208d43a5fSTom Zanussi 	struct tracing_map_field *field;
78308d43a5fSTom Zanussi 	tracing_map_cmp_fn_t cmp_fn;
78408d43a5fSTom Zanussi 	void *val_a, *val_b;
78508d43a5fSTom Zanussi 	int ret = 0;
78608d43a5fSTom Zanussi 
78708d43a5fSTom Zanussi 	elt_a = (*a)->elt;
78808d43a5fSTom Zanussi 	elt_b = (*b)->elt;
78908d43a5fSTom Zanussi 
79008d43a5fSTom Zanussi 	sort_key = &elt_a->map->sort_key;
79108d43a5fSTom Zanussi 
79208d43a5fSTom Zanussi 	field = &elt_a->fields[sort_key->field_idx];
79308d43a5fSTom Zanussi 
79408d43a5fSTom Zanussi 	cmp_fn = field->cmp_fn;
79508d43a5fSTom Zanussi 
79608d43a5fSTom Zanussi 	val_a = elt_a->key + field->offset;
79708d43a5fSTom Zanussi 	val_b = elt_b->key + field->offset;
79808d43a5fSTom Zanussi 
79908d43a5fSTom Zanussi 	ret = cmp_fn(val_a, val_b);
80008d43a5fSTom Zanussi 	if (sort_key->descending)
80108d43a5fSTom Zanussi 		ret = -ret;
80208d43a5fSTom Zanussi 
80308d43a5fSTom Zanussi 	return ret;
80408d43a5fSTom Zanussi }
80508d43a5fSTom Zanussi 
80608d43a5fSTom Zanussi static void destroy_sort_entry(struct tracing_map_sort_entry *entry)
80708d43a5fSTom Zanussi {
80808d43a5fSTom Zanussi 	if (!entry)
80908d43a5fSTom Zanussi 		return;
81008d43a5fSTom Zanussi 
81108d43a5fSTom Zanussi 	if (entry->elt_copied)
81208d43a5fSTom Zanussi 		tracing_map_elt_free(entry->elt);
81308d43a5fSTom Zanussi 
81408d43a5fSTom Zanussi 	kfree(entry);
81508d43a5fSTom Zanussi }
81608d43a5fSTom Zanussi 
81708d43a5fSTom Zanussi /**
81808d43a5fSTom Zanussi  * tracing_map_destroy_sort_entries - Destroy an array of sort entries
81908d43a5fSTom Zanussi  * @entries: The entries to destroy
82008d43a5fSTom Zanussi  * @n_entries: The number of entries in the array
82108d43a5fSTom Zanussi  *
82208d43a5fSTom Zanussi  * Destroy the elements returned by a tracing_map_sort_entries() call.
82308d43a5fSTom Zanussi  */
82408d43a5fSTom Zanussi void tracing_map_destroy_sort_entries(struct tracing_map_sort_entry **entries,
82508d43a5fSTom Zanussi 				      unsigned int n_entries)
82608d43a5fSTom Zanussi {
82708d43a5fSTom Zanussi 	unsigned int i;
82808d43a5fSTom Zanussi 
82908d43a5fSTom Zanussi 	for (i = 0; i < n_entries; i++)
83008d43a5fSTom Zanussi 		destroy_sort_entry(entries[i]);
83108d43a5fSTom Zanussi 
83208d43a5fSTom Zanussi 	vfree(entries);
83308d43a5fSTom Zanussi }
83408d43a5fSTom Zanussi 
83508d43a5fSTom Zanussi static struct tracing_map_sort_entry *
83608d43a5fSTom Zanussi create_sort_entry(void *key, struct tracing_map_elt *elt)
83708d43a5fSTom Zanussi {
83808d43a5fSTom Zanussi 	struct tracing_map_sort_entry *sort_entry;
83908d43a5fSTom Zanussi 
84008d43a5fSTom Zanussi 	sort_entry = kzalloc(sizeof(*sort_entry), GFP_KERNEL);
84108d43a5fSTom Zanussi 	if (!sort_entry)
84208d43a5fSTom Zanussi 		return NULL;
84308d43a5fSTom Zanussi 
84408d43a5fSTom Zanussi 	sort_entry->key = key;
84508d43a5fSTom Zanussi 	sort_entry->elt = elt;
84608d43a5fSTom Zanussi 
84708d43a5fSTom Zanussi 	return sort_entry;
84808d43a5fSTom Zanussi }
84908d43a5fSTom Zanussi 
850*c193707dSVedang Patel static void detect_dups(struct tracing_map_sort_entry **sort_entries,
85108d43a5fSTom Zanussi 		      int n_entries, unsigned int key_size)
85208d43a5fSTom Zanussi {
85308d43a5fSTom Zanussi 	unsigned int dups = 0, total_dups = 0;
854*c193707dSVedang Patel 	int i;
85508d43a5fSTom Zanussi 	void *key;
85608d43a5fSTom Zanussi 
85708d43a5fSTom Zanussi 	if (n_entries < 2)
858*c193707dSVedang Patel 		return;
85908d43a5fSTom Zanussi 
86008d43a5fSTom Zanussi 	sort(sort_entries, n_entries, sizeof(struct tracing_map_sort_entry *),
86108d43a5fSTom Zanussi 	     (int (*)(const void *, const void *))cmp_entries_dup, NULL);
86208d43a5fSTom Zanussi 
86308d43a5fSTom Zanussi 	key = sort_entries[0]->key;
86408d43a5fSTom Zanussi 	for (i = 1; i < n_entries; i++) {
86508d43a5fSTom Zanussi 		if (!memcmp(sort_entries[i]->key, key, key_size)) {
86608d43a5fSTom Zanussi 			dups++; total_dups++;
86708d43a5fSTom Zanussi 			continue;
86808d43a5fSTom Zanussi 		}
86908d43a5fSTom Zanussi 		key = sort_entries[i]->key;
87008d43a5fSTom Zanussi 		dups = 0;
87108d43a5fSTom Zanussi 	}
87208d43a5fSTom Zanussi 
873*c193707dSVedang Patel 	WARN_ONCE(total_dups > 0,
874*c193707dSVedang Patel 		  "Duplicates detected: %d\n", total_dups);
87508d43a5fSTom Zanussi }
87608d43a5fSTom Zanussi 
87708d43a5fSTom Zanussi static bool is_key(struct tracing_map *map, unsigned int field_idx)
87808d43a5fSTom Zanussi {
87908d43a5fSTom Zanussi 	unsigned int i;
88008d43a5fSTom Zanussi 
88108d43a5fSTom Zanussi 	for (i = 0; i < map->n_keys; i++)
88208d43a5fSTom Zanussi 		if (map->key_idx[i] == field_idx)
88308d43a5fSTom Zanussi 			return true;
88408d43a5fSTom Zanussi 	return false;
88508d43a5fSTom Zanussi }
88608d43a5fSTom Zanussi 
88708d43a5fSTom Zanussi static void sort_secondary(struct tracing_map *map,
88808d43a5fSTom Zanussi 			   const struct tracing_map_sort_entry **entries,
88908d43a5fSTom Zanussi 			   unsigned int n_entries,
89008d43a5fSTom Zanussi 			   struct tracing_map_sort_key *primary_key,
89108d43a5fSTom Zanussi 			   struct tracing_map_sort_key *secondary_key)
89208d43a5fSTom Zanussi {
89308d43a5fSTom Zanussi 	int (*primary_fn)(const struct tracing_map_sort_entry **,
89408d43a5fSTom Zanussi 			  const struct tracing_map_sort_entry **);
89508d43a5fSTom Zanussi 	int (*secondary_fn)(const struct tracing_map_sort_entry **,
89608d43a5fSTom Zanussi 			    const struct tracing_map_sort_entry **);
89708d43a5fSTom Zanussi 	unsigned i, start = 0, n_sub = 1;
89808d43a5fSTom Zanussi 
89908d43a5fSTom Zanussi 	if (is_key(map, primary_key->field_idx))
90008d43a5fSTom Zanussi 		primary_fn = cmp_entries_key;
90108d43a5fSTom Zanussi 	else
90208d43a5fSTom Zanussi 		primary_fn = cmp_entries_sum;
90308d43a5fSTom Zanussi 
90408d43a5fSTom Zanussi 	if (is_key(map, secondary_key->field_idx))
90508d43a5fSTom Zanussi 		secondary_fn = cmp_entries_key;
90608d43a5fSTom Zanussi 	else
90708d43a5fSTom Zanussi 		secondary_fn = cmp_entries_sum;
90808d43a5fSTom Zanussi 
90908d43a5fSTom Zanussi 	for (i = 0; i < n_entries - 1; i++) {
91008d43a5fSTom Zanussi 		const struct tracing_map_sort_entry **a = &entries[i];
91108d43a5fSTom Zanussi 		const struct tracing_map_sort_entry **b = &entries[i + 1];
91208d43a5fSTom Zanussi 
91308d43a5fSTom Zanussi 		if (primary_fn(a, b) == 0) {
91408d43a5fSTom Zanussi 			n_sub++;
91508d43a5fSTom Zanussi 			if (i < n_entries - 2)
91608d43a5fSTom Zanussi 				continue;
91708d43a5fSTom Zanussi 		}
91808d43a5fSTom Zanussi 
91908d43a5fSTom Zanussi 		if (n_sub < 2) {
92008d43a5fSTom Zanussi 			start = i + 1;
92108d43a5fSTom Zanussi 			n_sub = 1;
92208d43a5fSTom Zanussi 			continue;
92308d43a5fSTom Zanussi 		}
92408d43a5fSTom Zanussi 
92508d43a5fSTom Zanussi 		set_sort_key(map, secondary_key);
92608d43a5fSTom Zanussi 		sort(&entries[start], n_sub,
92708d43a5fSTom Zanussi 		     sizeof(struct tracing_map_sort_entry *),
92808d43a5fSTom Zanussi 		     (int (*)(const void *, const void *))secondary_fn, NULL);
92908d43a5fSTom Zanussi 		set_sort_key(map, primary_key);
93008d43a5fSTom Zanussi 
93108d43a5fSTom Zanussi 		start = i + 1;
93208d43a5fSTom Zanussi 		n_sub = 1;
93308d43a5fSTom Zanussi 	}
93408d43a5fSTom Zanussi }
93508d43a5fSTom Zanussi 
93608d43a5fSTom Zanussi /**
93708d43a5fSTom Zanussi  * tracing_map_sort_entries - Sort the current set of tracing_map_elts in a map
93808d43a5fSTom Zanussi  * @map: The tracing_map
93908d43a5fSTom Zanussi  * @sort_key: The sort key to use for sorting
94008d43a5fSTom Zanussi  * @sort_entries: outval: pointer to allocated and sorted array of entries
94108d43a5fSTom Zanussi  *
94208d43a5fSTom Zanussi  * tracing_map_sort_entries() sorts the current set of entries in the
94308d43a5fSTom Zanussi  * map and returns the list of tracing_map_sort_entries containing
94408d43a5fSTom Zanussi  * them to the client in the sort_entries param.  The client can
94508d43a5fSTom Zanussi  * access the struct tracing_map_elt element of interest directly as
94608d43a5fSTom Zanussi  * the 'elt' field of a returned struct tracing_map_sort_entry object.
94708d43a5fSTom Zanussi  *
94808d43a5fSTom Zanussi  * The sort_key has only two fields: idx and descending.  'idx' refers
94908d43a5fSTom Zanussi  * to the index of the field added via tracing_map_add_sum_field() or
95008d43a5fSTom Zanussi  * tracing_map_add_key_field() when the tracing_map was initialized.
95108d43a5fSTom Zanussi  * 'descending' is a flag that if set reverses the sort order, which
95208d43a5fSTom Zanussi  * by default is ascending.
95308d43a5fSTom Zanussi  *
95408d43a5fSTom Zanussi  * The client should not hold on to the returned array but should use
95508d43a5fSTom Zanussi  * it and call tracing_map_destroy_sort_entries() when done.
95608d43a5fSTom Zanussi  *
95708d43a5fSTom Zanussi  * Return: the number of sort_entries in the struct tracing_map_sort_entry
95808d43a5fSTom Zanussi  * array, negative on error
95908d43a5fSTom Zanussi  */
96008d43a5fSTom Zanussi int tracing_map_sort_entries(struct tracing_map *map,
96108d43a5fSTom Zanussi 			     struct tracing_map_sort_key *sort_keys,
96208d43a5fSTom Zanussi 			     unsigned int n_sort_keys,
96308d43a5fSTom Zanussi 			     struct tracing_map_sort_entry ***sort_entries)
96408d43a5fSTom Zanussi {
96508d43a5fSTom Zanussi 	int (*cmp_entries_fn)(const struct tracing_map_sort_entry **,
96608d43a5fSTom Zanussi 			      const struct tracing_map_sort_entry **);
96708d43a5fSTom Zanussi 	struct tracing_map_sort_entry *sort_entry, **entries;
96808d43a5fSTom Zanussi 	int i, n_entries, ret;
96908d43a5fSTom Zanussi 
97008d43a5fSTom Zanussi 	entries = vmalloc(map->max_elts * sizeof(sort_entry));
97108d43a5fSTom Zanussi 	if (!entries)
97208d43a5fSTom Zanussi 		return -ENOMEM;
97308d43a5fSTom Zanussi 
97408d43a5fSTom Zanussi 	for (i = 0, n_entries = 0; i < map->map_size; i++) {
97508d43a5fSTom Zanussi 		struct tracing_map_entry *entry;
97608d43a5fSTom Zanussi 
97708d43a5fSTom Zanussi 		entry = TRACING_MAP_ENTRY(map->map, i);
97808d43a5fSTom Zanussi 
97908d43a5fSTom Zanussi 		if (!entry->key || !entry->val)
98008d43a5fSTom Zanussi 			continue;
98108d43a5fSTom Zanussi 
98208d43a5fSTom Zanussi 		entries[n_entries] = create_sort_entry(entry->val->key,
98308d43a5fSTom Zanussi 						       entry->val);
98408d43a5fSTom Zanussi 		if (!entries[n_entries++]) {
98508d43a5fSTom Zanussi 			ret = -ENOMEM;
98608d43a5fSTom Zanussi 			goto free;
98708d43a5fSTom Zanussi 		}
98808d43a5fSTom Zanussi 	}
98908d43a5fSTom Zanussi 
99008d43a5fSTom Zanussi 	if (n_entries == 0) {
99108d43a5fSTom Zanussi 		ret = 0;
99208d43a5fSTom Zanussi 		goto free;
99308d43a5fSTom Zanussi 	}
99408d43a5fSTom Zanussi 
99508d43a5fSTom Zanussi 	if (n_entries == 1) {
99608d43a5fSTom Zanussi 		*sort_entries = entries;
99708d43a5fSTom Zanussi 		return 1;
99808d43a5fSTom Zanussi 	}
99908d43a5fSTom Zanussi 
1000*c193707dSVedang Patel 	detect_dups(entries, n_entries, map->key_size);
100108d43a5fSTom Zanussi 
100208d43a5fSTom Zanussi 	if (is_key(map, sort_keys[0].field_idx))
100308d43a5fSTom Zanussi 		cmp_entries_fn = cmp_entries_key;
100408d43a5fSTom Zanussi 	else
100508d43a5fSTom Zanussi 		cmp_entries_fn = cmp_entries_sum;
100608d43a5fSTom Zanussi 
100708d43a5fSTom Zanussi 	set_sort_key(map, &sort_keys[0]);
100808d43a5fSTom Zanussi 
100908d43a5fSTom Zanussi 	sort(entries, n_entries, sizeof(struct tracing_map_sort_entry *),
101008d43a5fSTom Zanussi 	     (int (*)(const void *, const void *))cmp_entries_fn, NULL);
101108d43a5fSTom Zanussi 
101208d43a5fSTom Zanussi 	if (n_sort_keys > 1)
101308d43a5fSTom Zanussi 		sort_secondary(map,
101408d43a5fSTom Zanussi 			       (const struct tracing_map_sort_entry **)entries,
101508d43a5fSTom Zanussi 			       n_entries,
101608d43a5fSTom Zanussi 			       &sort_keys[0],
101708d43a5fSTom Zanussi 			       &sort_keys[1]);
101808d43a5fSTom Zanussi 
101908d43a5fSTom Zanussi 	*sort_entries = entries;
102008d43a5fSTom Zanussi 
102108d43a5fSTom Zanussi 	return n_entries;
102208d43a5fSTom Zanussi  free:
102308d43a5fSTom Zanussi 	tracing_map_destroy_sort_entries(entries, n_entries);
102408d43a5fSTom Zanussi 
102508d43a5fSTom Zanussi 	return ret;
102608d43a5fSTom Zanussi }
1027