162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/kernel.h>
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/random.h>
1062306a36Sopenharmony_ci#include <linux/objagg.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistruct tokey {
1362306a36Sopenharmony_ci	unsigned int id;
1462306a36Sopenharmony_ci};
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define NUM_KEYS 32
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic int key_id_index(unsigned int key_id)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	if (key_id >= NUM_KEYS) {
2162306a36Sopenharmony_ci		WARN_ON(1);
2262306a36Sopenharmony_ci		return 0;
2362306a36Sopenharmony_ci	}
2462306a36Sopenharmony_ci	return key_id;
2562306a36Sopenharmony_ci}
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define BUF_LEN 128
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistruct world {
3062306a36Sopenharmony_ci	unsigned int root_count;
3162306a36Sopenharmony_ci	unsigned int delta_count;
3262306a36Sopenharmony_ci	char next_root_buf[BUF_LEN];
3362306a36Sopenharmony_ci	struct objagg_obj *objagg_objs[NUM_KEYS];
3462306a36Sopenharmony_ci	unsigned int key_refs[NUM_KEYS];
3562306a36Sopenharmony_ci};
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistruct root {
3862306a36Sopenharmony_ci	struct tokey key;
3962306a36Sopenharmony_ci	char buf[BUF_LEN];
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistruct delta {
4362306a36Sopenharmony_ci	unsigned int key_id_diff;
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic struct objagg_obj *world_obj_get(struct world *world,
4762306a36Sopenharmony_ci					struct objagg *objagg,
4862306a36Sopenharmony_ci					unsigned int key_id)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct objagg_obj *objagg_obj;
5162306a36Sopenharmony_ci	struct tokey key;
5262306a36Sopenharmony_ci	int err;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	key.id = key_id;
5562306a36Sopenharmony_ci	objagg_obj = objagg_obj_get(objagg, &key);
5662306a36Sopenharmony_ci	if (IS_ERR(objagg_obj)) {
5762306a36Sopenharmony_ci		pr_err("Key %u: Failed to get object.\n", key_id);
5862306a36Sopenharmony_ci		return objagg_obj;
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci	if (!world->key_refs[key_id_index(key_id)]) {
6162306a36Sopenharmony_ci		world->objagg_objs[key_id_index(key_id)] = objagg_obj;
6262306a36Sopenharmony_ci	} else if (world->objagg_objs[key_id_index(key_id)] != objagg_obj) {
6362306a36Sopenharmony_ci		pr_err("Key %u: God another object for the same key.\n",
6462306a36Sopenharmony_ci		       key_id);
6562306a36Sopenharmony_ci		err = -EINVAL;
6662306a36Sopenharmony_ci		goto err_key_id_check;
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci	world->key_refs[key_id_index(key_id)]++;
6962306a36Sopenharmony_ci	return objagg_obj;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cierr_key_id_check:
7262306a36Sopenharmony_ci	objagg_obj_put(objagg, objagg_obj);
7362306a36Sopenharmony_ci	return ERR_PTR(err);
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic void world_obj_put(struct world *world, struct objagg *objagg,
7762306a36Sopenharmony_ci			  unsigned int key_id)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct objagg_obj *objagg_obj;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	if (!world->key_refs[key_id_index(key_id)])
8262306a36Sopenharmony_ci		return;
8362306a36Sopenharmony_ci	objagg_obj = world->objagg_objs[key_id_index(key_id)];
8462306a36Sopenharmony_ci	objagg_obj_put(objagg, objagg_obj);
8562306a36Sopenharmony_ci	world->key_refs[key_id_index(key_id)]--;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci#define MAX_KEY_ID_DIFF 5
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic bool delta_check(void *priv, const void *parent_obj, const void *obj)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	const struct tokey *parent_key = parent_obj;
9362306a36Sopenharmony_ci	const struct tokey *key = obj;
9462306a36Sopenharmony_ci	int diff = key->id - parent_key->id;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	return diff >= 0 && diff <= MAX_KEY_ID_DIFF;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic void *delta_create(void *priv, void *parent_obj, void *obj)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	struct tokey *parent_key = parent_obj;
10262306a36Sopenharmony_ci	struct world *world = priv;
10362306a36Sopenharmony_ci	struct tokey *key = obj;
10462306a36Sopenharmony_ci	int diff = key->id - parent_key->id;
10562306a36Sopenharmony_ci	struct delta *delta;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (!delta_check(priv, parent_obj, obj))
10862306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	delta = kzalloc(sizeof(*delta), GFP_KERNEL);
11162306a36Sopenharmony_ci	if (!delta)
11262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
11362306a36Sopenharmony_ci	delta->key_id_diff = diff;
11462306a36Sopenharmony_ci	world->delta_count++;
11562306a36Sopenharmony_ci	return delta;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic void delta_destroy(void *priv, void *delta_priv)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct delta *delta = delta_priv;
12162306a36Sopenharmony_ci	struct world *world = priv;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	world->delta_count--;
12462306a36Sopenharmony_ci	kfree(delta);
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic void *root_create(void *priv, void *obj, unsigned int id)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct world *world = priv;
13062306a36Sopenharmony_ci	struct tokey *key = obj;
13162306a36Sopenharmony_ci	struct root *root;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	root = kzalloc(sizeof(*root), GFP_KERNEL);
13462306a36Sopenharmony_ci	if (!root)
13562306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
13662306a36Sopenharmony_ci	memcpy(&root->key, key, sizeof(root->key));
13762306a36Sopenharmony_ci	memcpy(root->buf, world->next_root_buf, sizeof(root->buf));
13862306a36Sopenharmony_ci	world->root_count++;
13962306a36Sopenharmony_ci	return root;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic void root_destroy(void *priv, void *root_priv)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct root *root = root_priv;
14562306a36Sopenharmony_ci	struct world *world = priv;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	world->root_count--;
14862306a36Sopenharmony_ci	kfree(root);
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic int test_nodelta_obj_get(struct world *world, struct objagg *objagg,
15262306a36Sopenharmony_ci				unsigned int key_id, bool should_create_root)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	unsigned int orig_root_count = world->root_count;
15562306a36Sopenharmony_ci	struct objagg_obj *objagg_obj;
15662306a36Sopenharmony_ci	const struct root *root;
15762306a36Sopenharmony_ci	int err;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (should_create_root)
16062306a36Sopenharmony_ci		get_random_bytes(world->next_root_buf,
16162306a36Sopenharmony_ci			      sizeof(world->next_root_buf));
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	objagg_obj = world_obj_get(world, objagg, key_id);
16462306a36Sopenharmony_ci	if (IS_ERR(objagg_obj)) {
16562306a36Sopenharmony_ci		pr_err("Key %u: Failed to get object.\n", key_id);
16662306a36Sopenharmony_ci		return PTR_ERR(objagg_obj);
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci	if (should_create_root) {
16962306a36Sopenharmony_ci		if (world->root_count != orig_root_count + 1) {
17062306a36Sopenharmony_ci			pr_err("Key %u: Root was not created\n", key_id);
17162306a36Sopenharmony_ci			err = -EINVAL;
17262306a36Sopenharmony_ci			goto err_check_root_count;
17362306a36Sopenharmony_ci		}
17462306a36Sopenharmony_ci	} else {
17562306a36Sopenharmony_ci		if (world->root_count != orig_root_count) {
17662306a36Sopenharmony_ci			pr_err("Key %u: Root was incorrectly created\n",
17762306a36Sopenharmony_ci			       key_id);
17862306a36Sopenharmony_ci			err = -EINVAL;
17962306a36Sopenharmony_ci			goto err_check_root_count;
18062306a36Sopenharmony_ci		}
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci	root = objagg_obj_root_priv(objagg_obj);
18362306a36Sopenharmony_ci	if (root->key.id != key_id) {
18462306a36Sopenharmony_ci		pr_err("Key %u: Root has unexpected key id\n", key_id);
18562306a36Sopenharmony_ci		err = -EINVAL;
18662306a36Sopenharmony_ci		goto err_check_key_id;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci	if (should_create_root &&
18962306a36Sopenharmony_ci	    memcmp(world->next_root_buf, root->buf, sizeof(root->buf))) {
19062306a36Sopenharmony_ci		pr_err("Key %u: Buffer does not match the expected content\n",
19162306a36Sopenharmony_ci		       key_id);
19262306a36Sopenharmony_ci		err = -EINVAL;
19362306a36Sopenharmony_ci		goto err_check_buf;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci	return 0;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cierr_check_buf:
19862306a36Sopenharmony_cierr_check_key_id:
19962306a36Sopenharmony_cierr_check_root_count:
20062306a36Sopenharmony_ci	objagg_obj_put(objagg, objagg_obj);
20162306a36Sopenharmony_ci	return err;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic int test_nodelta_obj_put(struct world *world, struct objagg *objagg,
20562306a36Sopenharmony_ci				unsigned int key_id, bool should_destroy_root)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	unsigned int orig_root_count = world->root_count;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	world_obj_put(world, objagg, key_id);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if (should_destroy_root) {
21262306a36Sopenharmony_ci		if (world->root_count != orig_root_count - 1) {
21362306a36Sopenharmony_ci			pr_err("Key %u: Root was not destroyed\n", key_id);
21462306a36Sopenharmony_ci			return -EINVAL;
21562306a36Sopenharmony_ci		}
21662306a36Sopenharmony_ci	} else {
21762306a36Sopenharmony_ci		if (world->root_count != orig_root_count) {
21862306a36Sopenharmony_ci			pr_err("Key %u: Root was incorrectly destroyed\n",
21962306a36Sopenharmony_ci			       key_id);
22062306a36Sopenharmony_ci			return -EINVAL;
22162306a36Sopenharmony_ci		}
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci	return 0;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic int check_stats_zero(struct objagg *objagg)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	const struct objagg_stats *stats;
22962306a36Sopenharmony_ci	int err = 0;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	stats = objagg_stats_get(objagg);
23262306a36Sopenharmony_ci	if (IS_ERR(stats))
23362306a36Sopenharmony_ci		return PTR_ERR(stats);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (stats->stats_info_count != 0) {
23662306a36Sopenharmony_ci		pr_err("Stats: Object count is not zero while it should be\n");
23762306a36Sopenharmony_ci		err = -EINVAL;
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	objagg_stats_put(stats);
24162306a36Sopenharmony_ci	return err;
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic int check_stats_nodelta(struct objagg *objagg)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	const struct objagg_stats *stats;
24762306a36Sopenharmony_ci	int i;
24862306a36Sopenharmony_ci	int err;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	stats = objagg_stats_get(objagg);
25162306a36Sopenharmony_ci	if (IS_ERR(stats))
25262306a36Sopenharmony_ci		return PTR_ERR(stats);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (stats->stats_info_count != NUM_KEYS) {
25562306a36Sopenharmony_ci		pr_err("Stats: Unexpected object count (%u expected, %u returned)\n",
25662306a36Sopenharmony_ci		       NUM_KEYS, stats->stats_info_count);
25762306a36Sopenharmony_ci		err = -EINVAL;
25862306a36Sopenharmony_ci		goto stats_put;
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	for (i = 0; i < stats->stats_info_count; i++) {
26262306a36Sopenharmony_ci		if (stats->stats_info[i].stats.user_count != 2) {
26362306a36Sopenharmony_ci			pr_err("Stats: incorrect user count\n");
26462306a36Sopenharmony_ci			err = -EINVAL;
26562306a36Sopenharmony_ci			goto stats_put;
26662306a36Sopenharmony_ci		}
26762306a36Sopenharmony_ci		if (stats->stats_info[i].stats.delta_user_count != 2) {
26862306a36Sopenharmony_ci			pr_err("Stats: incorrect delta user count\n");
26962306a36Sopenharmony_ci			err = -EINVAL;
27062306a36Sopenharmony_ci			goto stats_put;
27162306a36Sopenharmony_ci		}
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci	err = 0;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistats_put:
27662306a36Sopenharmony_ci	objagg_stats_put(stats);
27762306a36Sopenharmony_ci	return err;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic bool delta_check_dummy(void *priv, const void *parent_obj,
28162306a36Sopenharmony_ci			      const void *obj)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	return false;
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic void *delta_create_dummy(void *priv, void *parent_obj, void *obj)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	return ERR_PTR(-EOPNOTSUPP);
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic void delta_destroy_dummy(void *priv, void *delta_priv)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic const struct objagg_ops nodelta_ops = {
29662306a36Sopenharmony_ci	.obj_size = sizeof(struct tokey),
29762306a36Sopenharmony_ci	.delta_check = delta_check_dummy,
29862306a36Sopenharmony_ci	.delta_create = delta_create_dummy,
29962306a36Sopenharmony_ci	.delta_destroy = delta_destroy_dummy,
30062306a36Sopenharmony_ci	.root_create = root_create,
30162306a36Sopenharmony_ci	.root_destroy = root_destroy,
30262306a36Sopenharmony_ci};
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic int test_nodelta(void)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	struct world world = {};
30762306a36Sopenharmony_ci	struct objagg *objagg;
30862306a36Sopenharmony_ci	int i;
30962306a36Sopenharmony_ci	int err;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	objagg = objagg_create(&nodelta_ops, NULL, &world);
31262306a36Sopenharmony_ci	if (IS_ERR(objagg))
31362306a36Sopenharmony_ci		return PTR_ERR(objagg);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	err = check_stats_zero(objagg);
31662306a36Sopenharmony_ci	if (err)
31762306a36Sopenharmony_ci		goto err_stats_first_zero;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	/* First round of gets, the root objects should be created */
32062306a36Sopenharmony_ci	for (i = 0; i < NUM_KEYS; i++) {
32162306a36Sopenharmony_ci		err = test_nodelta_obj_get(&world, objagg, i, true);
32262306a36Sopenharmony_ci		if (err)
32362306a36Sopenharmony_ci			goto err_obj_first_get;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	/* Do the second round of gets, all roots are already created,
32762306a36Sopenharmony_ci	 * make sure that no new root is created
32862306a36Sopenharmony_ci	 */
32962306a36Sopenharmony_ci	for (i = 0; i < NUM_KEYS; i++) {
33062306a36Sopenharmony_ci		err = test_nodelta_obj_get(&world, objagg, i, false);
33162306a36Sopenharmony_ci		if (err)
33262306a36Sopenharmony_ci			goto err_obj_second_get;
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	err = check_stats_nodelta(objagg);
33662306a36Sopenharmony_ci	if (err)
33762306a36Sopenharmony_ci		goto err_stats_nodelta;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	for (i = NUM_KEYS - 1; i >= 0; i--) {
34062306a36Sopenharmony_ci		err = test_nodelta_obj_put(&world, objagg, i, false);
34162306a36Sopenharmony_ci		if (err)
34262306a36Sopenharmony_ci			goto err_obj_first_put;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci	for (i = NUM_KEYS - 1; i >= 0; i--) {
34562306a36Sopenharmony_ci		err = test_nodelta_obj_put(&world, objagg, i, true);
34662306a36Sopenharmony_ci		if (err)
34762306a36Sopenharmony_ci			goto err_obj_second_put;
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	err = check_stats_zero(objagg);
35162306a36Sopenharmony_ci	if (err)
35262306a36Sopenharmony_ci		goto err_stats_second_zero;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	objagg_destroy(objagg);
35562306a36Sopenharmony_ci	return 0;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cierr_stats_nodelta:
35862306a36Sopenharmony_cierr_obj_first_put:
35962306a36Sopenharmony_cierr_obj_second_get:
36062306a36Sopenharmony_ci	for (i--; i >= 0; i--)
36162306a36Sopenharmony_ci		world_obj_put(&world, objagg, i);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	i = NUM_KEYS;
36462306a36Sopenharmony_cierr_obj_first_get:
36562306a36Sopenharmony_cierr_obj_second_put:
36662306a36Sopenharmony_ci	for (i--; i >= 0; i--)
36762306a36Sopenharmony_ci		world_obj_put(&world, objagg, i);
36862306a36Sopenharmony_cierr_stats_first_zero:
36962306a36Sopenharmony_cierr_stats_second_zero:
37062306a36Sopenharmony_ci	objagg_destroy(objagg);
37162306a36Sopenharmony_ci	return err;
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic const struct objagg_ops delta_ops = {
37562306a36Sopenharmony_ci	.obj_size = sizeof(struct tokey),
37662306a36Sopenharmony_ci	.delta_check = delta_check,
37762306a36Sopenharmony_ci	.delta_create = delta_create,
37862306a36Sopenharmony_ci	.delta_destroy = delta_destroy,
37962306a36Sopenharmony_ci	.root_create = root_create,
38062306a36Sopenharmony_ci	.root_destroy = root_destroy,
38162306a36Sopenharmony_ci};
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cienum action {
38462306a36Sopenharmony_ci	ACTION_GET,
38562306a36Sopenharmony_ci	ACTION_PUT,
38662306a36Sopenharmony_ci};
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cienum expect_delta {
38962306a36Sopenharmony_ci	EXPECT_DELTA_SAME,
39062306a36Sopenharmony_ci	EXPECT_DELTA_INC,
39162306a36Sopenharmony_ci	EXPECT_DELTA_DEC,
39262306a36Sopenharmony_ci};
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cienum expect_root {
39562306a36Sopenharmony_ci	EXPECT_ROOT_SAME,
39662306a36Sopenharmony_ci	EXPECT_ROOT_INC,
39762306a36Sopenharmony_ci	EXPECT_ROOT_DEC,
39862306a36Sopenharmony_ci};
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistruct expect_stats_info {
40162306a36Sopenharmony_ci	struct objagg_obj_stats stats;
40262306a36Sopenharmony_ci	bool is_root;
40362306a36Sopenharmony_ci	unsigned int key_id;
40462306a36Sopenharmony_ci};
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistruct expect_stats {
40762306a36Sopenharmony_ci	unsigned int info_count;
40862306a36Sopenharmony_ci	struct expect_stats_info info[NUM_KEYS];
40962306a36Sopenharmony_ci};
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cistruct action_item {
41262306a36Sopenharmony_ci	unsigned int key_id;
41362306a36Sopenharmony_ci	enum action action;
41462306a36Sopenharmony_ci	enum expect_delta expect_delta;
41562306a36Sopenharmony_ci	enum expect_root expect_root;
41662306a36Sopenharmony_ci	struct expect_stats expect_stats;
41762306a36Sopenharmony_ci};
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci#define EXPECT_STATS(count, ...)		\
42062306a36Sopenharmony_ci{						\
42162306a36Sopenharmony_ci	.info_count = count,			\
42262306a36Sopenharmony_ci	.info = { __VA_ARGS__ }			\
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci#define ROOT(key_id, user_count, delta_user_count)	\
42662306a36Sopenharmony_ci	{{user_count, delta_user_count}, true, key_id}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci#define DELTA(key_id, user_count)			\
42962306a36Sopenharmony_ci	{{user_count, user_count}, false, key_id}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic const struct action_item action_items[] = {
43262306a36Sopenharmony_ci	{
43362306a36Sopenharmony_ci		1, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_INC,
43462306a36Sopenharmony_ci		EXPECT_STATS(1, ROOT(1, 1, 1)),
43562306a36Sopenharmony_ci	},	/* r: 1			d: */
43662306a36Sopenharmony_ci	{
43762306a36Sopenharmony_ci		7, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_INC,
43862306a36Sopenharmony_ci		EXPECT_STATS(2, ROOT(1, 1, 1), ROOT(7, 1, 1)),
43962306a36Sopenharmony_ci	},	/* r: 1, 7		d: */
44062306a36Sopenharmony_ci	{
44162306a36Sopenharmony_ci		3, ACTION_GET, EXPECT_DELTA_INC, EXPECT_ROOT_SAME,
44262306a36Sopenharmony_ci		EXPECT_STATS(3, ROOT(1, 1, 2), ROOT(7, 1, 1),
44362306a36Sopenharmony_ci				DELTA(3, 1)),
44462306a36Sopenharmony_ci	},	/* r: 1, 7		d: 3^1 */
44562306a36Sopenharmony_ci	{
44662306a36Sopenharmony_ci		5, ACTION_GET, EXPECT_DELTA_INC, EXPECT_ROOT_SAME,
44762306a36Sopenharmony_ci		EXPECT_STATS(4, ROOT(1, 1, 3), ROOT(7, 1, 1),
44862306a36Sopenharmony_ci				DELTA(3, 1), DELTA(5, 1)),
44962306a36Sopenharmony_ci	},	/* r: 1, 7		d: 3^1, 5^1 */
45062306a36Sopenharmony_ci	{
45162306a36Sopenharmony_ci		3, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
45262306a36Sopenharmony_ci		EXPECT_STATS(4, ROOT(1, 1, 4), ROOT(7, 1, 1),
45362306a36Sopenharmony_ci				DELTA(3, 2), DELTA(5, 1)),
45462306a36Sopenharmony_ci	},	/* r: 1, 7		d: 3^1, 3^1, 5^1 */
45562306a36Sopenharmony_ci	{
45662306a36Sopenharmony_ci		1, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
45762306a36Sopenharmony_ci		EXPECT_STATS(4, ROOT(1, 2, 5), ROOT(7, 1, 1),
45862306a36Sopenharmony_ci				DELTA(3, 2), DELTA(5, 1)),
45962306a36Sopenharmony_ci	},	/* r: 1, 1, 7		d: 3^1, 3^1, 5^1 */
46062306a36Sopenharmony_ci	{
46162306a36Sopenharmony_ci		30, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_INC,
46262306a36Sopenharmony_ci		EXPECT_STATS(5, ROOT(1, 2, 5), ROOT(7, 1, 1), ROOT(30, 1, 1),
46362306a36Sopenharmony_ci				DELTA(3, 2), DELTA(5, 1)),
46462306a36Sopenharmony_ci	},	/* r: 1, 1, 7, 30	d: 3^1, 3^1, 5^1 */
46562306a36Sopenharmony_ci	{
46662306a36Sopenharmony_ci		8, ACTION_GET, EXPECT_DELTA_INC, EXPECT_ROOT_SAME,
46762306a36Sopenharmony_ci		EXPECT_STATS(6, ROOT(1, 2, 5), ROOT(7, 1, 2), ROOT(30, 1, 1),
46862306a36Sopenharmony_ci				DELTA(3, 2), DELTA(5, 1), DELTA(8, 1)),
46962306a36Sopenharmony_ci	},	/* r: 1, 1, 7, 30	d: 3^1, 3^1, 5^1, 8^7 */
47062306a36Sopenharmony_ci	{
47162306a36Sopenharmony_ci		8, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
47262306a36Sopenharmony_ci		EXPECT_STATS(6, ROOT(1, 2, 5), ROOT(7, 1, 3), ROOT(30, 1, 1),
47362306a36Sopenharmony_ci				DELTA(3, 2), DELTA(8, 2), DELTA(5, 1)),
47462306a36Sopenharmony_ci	},	/* r: 1, 1, 7, 30	d: 3^1, 3^1, 5^1, 8^7, 8^7 */
47562306a36Sopenharmony_ci	{
47662306a36Sopenharmony_ci		3, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
47762306a36Sopenharmony_ci		EXPECT_STATS(6, ROOT(1, 2, 4), ROOT(7, 1, 3), ROOT(30, 1, 1),
47862306a36Sopenharmony_ci				DELTA(8, 2), DELTA(3, 1), DELTA(5, 1)),
47962306a36Sopenharmony_ci	},	/* r: 1, 1, 7, 30	d: 3^1, 5^1, 8^7, 8^7 */
48062306a36Sopenharmony_ci	{
48162306a36Sopenharmony_ci		3, ACTION_PUT, EXPECT_DELTA_DEC, EXPECT_ROOT_SAME,
48262306a36Sopenharmony_ci		EXPECT_STATS(5, ROOT(1, 2, 3), ROOT(7, 1, 3), ROOT(30, 1, 1),
48362306a36Sopenharmony_ci				DELTA(8, 2), DELTA(5, 1)),
48462306a36Sopenharmony_ci	},	/* r: 1, 1, 7, 30	d: 5^1, 8^7, 8^7 */
48562306a36Sopenharmony_ci	{
48662306a36Sopenharmony_ci		1, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
48762306a36Sopenharmony_ci		EXPECT_STATS(5, ROOT(7, 1, 3), ROOT(1, 1, 2), ROOT(30, 1, 1),
48862306a36Sopenharmony_ci				DELTA(8, 2), DELTA(5, 1)),
48962306a36Sopenharmony_ci	},	/* r: 1, 7, 30		d: 5^1, 8^7, 8^7 */
49062306a36Sopenharmony_ci	{
49162306a36Sopenharmony_ci		1, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
49262306a36Sopenharmony_ci		EXPECT_STATS(5, ROOT(7, 1, 3), ROOT(30, 1, 1), ROOT(1, 0, 1),
49362306a36Sopenharmony_ci				DELTA(8, 2), DELTA(5, 1)),
49462306a36Sopenharmony_ci	},	/* r: 7, 30		d: 5^1, 8^7, 8^7 */
49562306a36Sopenharmony_ci	{
49662306a36Sopenharmony_ci		5, ACTION_PUT, EXPECT_DELTA_DEC, EXPECT_ROOT_DEC,
49762306a36Sopenharmony_ci		EXPECT_STATS(3, ROOT(7, 1, 3), ROOT(30, 1, 1),
49862306a36Sopenharmony_ci				DELTA(8, 2)),
49962306a36Sopenharmony_ci	},	/* r: 7, 30		d: 8^7, 8^7 */
50062306a36Sopenharmony_ci	{
50162306a36Sopenharmony_ci		5, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_INC,
50262306a36Sopenharmony_ci		EXPECT_STATS(4, ROOT(7, 1, 3), ROOT(30, 1, 1), ROOT(5, 1, 1),
50362306a36Sopenharmony_ci				DELTA(8, 2)),
50462306a36Sopenharmony_ci	},	/* r: 7, 30, 5		d: 8^7, 8^7 */
50562306a36Sopenharmony_ci	{
50662306a36Sopenharmony_ci		6, ACTION_GET, EXPECT_DELTA_INC, EXPECT_ROOT_SAME,
50762306a36Sopenharmony_ci		EXPECT_STATS(5, ROOT(7, 1, 3), ROOT(5, 1, 2), ROOT(30, 1, 1),
50862306a36Sopenharmony_ci				DELTA(8, 2), DELTA(6, 1)),
50962306a36Sopenharmony_ci	},	/* r: 7, 30, 5		d: 8^7, 8^7, 6^5 */
51062306a36Sopenharmony_ci	{
51162306a36Sopenharmony_ci		8, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
51262306a36Sopenharmony_ci		EXPECT_STATS(5, ROOT(7, 1, 4), ROOT(5, 1, 2), ROOT(30, 1, 1),
51362306a36Sopenharmony_ci				DELTA(8, 3), DELTA(6, 1)),
51462306a36Sopenharmony_ci	},	/* r: 7, 30, 5		d: 8^7, 8^7, 8^7, 6^5 */
51562306a36Sopenharmony_ci	{
51662306a36Sopenharmony_ci		8, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
51762306a36Sopenharmony_ci		EXPECT_STATS(5, ROOT(7, 1, 3), ROOT(5, 1, 2), ROOT(30, 1, 1),
51862306a36Sopenharmony_ci				DELTA(8, 2), DELTA(6, 1)),
51962306a36Sopenharmony_ci	},	/* r: 7, 30, 5		d: 8^7, 8^7, 6^5 */
52062306a36Sopenharmony_ci	{
52162306a36Sopenharmony_ci		8, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
52262306a36Sopenharmony_ci		EXPECT_STATS(5, ROOT(7, 1, 2), ROOT(5, 1, 2), ROOT(30, 1, 1),
52362306a36Sopenharmony_ci				DELTA(8, 1), DELTA(6, 1)),
52462306a36Sopenharmony_ci	},	/* r: 7, 30, 5		d: 8^7, 6^5 */
52562306a36Sopenharmony_ci	{
52662306a36Sopenharmony_ci		8, ACTION_PUT, EXPECT_DELTA_DEC, EXPECT_ROOT_SAME,
52762306a36Sopenharmony_ci		EXPECT_STATS(4, ROOT(5, 1, 2), ROOT(7, 1, 1), ROOT(30, 1, 1),
52862306a36Sopenharmony_ci				DELTA(6, 1)),
52962306a36Sopenharmony_ci	},	/* r: 7, 30, 5		d: 6^5 */
53062306a36Sopenharmony_ci	{
53162306a36Sopenharmony_ci		8, ACTION_GET, EXPECT_DELTA_INC, EXPECT_ROOT_SAME,
53262306a36Sopenharmony_ci		EXPECT_STATS(5, ROOT(5, 1, 3), ROOT(7, 1, 1), ROOT(30, 1, 1),
53362306a36Sopenharmony_ci				DELTA(6, 1), DELTA(8, 1)),
53462306a36Sopenharmony_ci	},	/* r: 7, 30, 5		d: 6^5, 8^5 */
53562306a36Sopenharmony_ci	{
53662306a36Sopenharmony_ci		7, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_DEC,
53762306a36Sopenharmony_ci		EXPECT_STATS(4, ROOT(5, 1, 3), ROOT(30, 1, 1),
53862306a36Sopenharmony_ci				DELTA(6, 1), DELTA(8, 1)),
53962306a36Sopenharmony_ci	},	/* r: 30, 5		d: 6^5, 8^5 */
54062306a36Sopenharmony_ci	{
54162306a36Sopenharmony_ci		30, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_DEC,
54262306a36Sopenharmony_ci		EXPECT_STATS(3, ROOT(5, 1, 3),
54362306a36Sopenharmony_ci				DELTA(6, 1), DELTA(8, 1)),
54462306a36Sopenharmony_ci	},	/* r: 5			d: 6^5, 8^5 */
54562306a36Sopenharmony_ci	{
54662306a36Sopenharmony_ci		5, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
54762306a36Sopenharmony_ci		EXPECT_STATS(3, ROOT(5, 0, 2),
54862306a36Sopenharmony_ci				DELTA(6, 1), DELTA(8, 1)),
54962306a36Sopenharmony_ci	},	/* r:			d: 6^5, 8^5 */
55062306a36Sopenharmony_ci	{
55162306a36Sopenharmony_ci		6, ACTION_PUT, EXPECT_DELTA_DEC, EXPECT_ROOT_SAME,
55262306a36Sopenharmony_ci		EXPECT_STATS(2, ROOT(5, 0, 1),
55362306a36Sopenharmony_ci				DELTA(8, 1)),
55462306a36Sopenharmony_ci	},	/* r:			d: 6^5 */
55562306a36Sopenharmony_ci	{
55662306a36Sopenharmony_ci		8, ACTION_PUT, EXPECT_DELTA_DEC, EXPECT_ROOT_DEC,
55762306a36Sopenharmony_ci		EXPECT_STATS(0, ),
55862306a36Sopenharmony_ci	},	/* r:			d: */
55962306a36Sopenharmony_ci};
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cistatic int check_expect(struct world *world,
56262306a36Sopenharmony_ci			const struct action_item *action_item,
56362306a36Sopenharmony_ci			unsigned int orig_delta_count,
56462306a36Sopenharmony_ci			unsigned int orig_root_count)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	unsigned int key_id = action_item->key_id;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	switch (action_item->expect_delta) {
56962306a36Sopenharmony_ci	case EXPECT_DELTA_SAME:
57062306a36Sopenharmony_ci		if (orig_delta_count != world->delta_count) {
57162306a36Sopenharmony_ci			pr_err("Key %u: Delta count changed while expected to remain the same.\n",
57262306a36Sopenharmony_ci			       key_id);
57362306a36Sopenharmony_ci			return -EINVAL;
57462306a36Sopenharmony_ci		}
57562306a36Sopenharmony_ci		break;
57662306a36Sopenharmony_ci	case EXPECT_DELTA_INC:
57762306a36Sopenharmony_ci		if (WARN_ON(action_item->action == ACTION_PUT))
57862306a36Sopenharmony_ci			return -EINVAL;
57962306a36Sopenharmony_ci		if (orig_delta_count + 1 != world->delta_count) {
58062306a36Sopenharmony_ci			pr_err("Key %u: Delta count was not incremented.\n",
58162306a36Sopenharmony_ci			       key_id);
58262306a36Sopenharmony_ci			return -EINVAL;
58362306a36Sopenharmony_ci		}
58462306a36Sopenharmony_ci		break;
58562306a36Sopenharmony_ci	case EXPECT_DELTA_DEC:
58662306a36Sopenharmony_ci		if (WARN_ON(action_item->action == ACTION_GET))
58762306a36Sopenharmony_ci			return -EINVAL;
58862306a36Sopenharmony_ci		if (orig_delta_count - 1 != world->delta_count) {
58962306a36Sopenharmony_ci			pr_err("Key %u: Delta count was not decremented.\n",
59062306a36Sopenharmony_ci			       key_id);
59162306a36Sopenharmony_ci			return -EINVAL;
59262306a36Sopenharmony_ci		}
59362306a36Sopenharmony_ci		break;
59462306a36Sopenharmony_ci	}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	switch (action_item->expect_root) {
59762306a36Sopenharmony_ci	case EXPECT_ROOT_SAME:
59862306a36Sopenharmony_ci		if (orig_root_count != world->root_count) {
59962306a36Sopenharmony_ci			pr_err("Key %u: Root count changed while expected to remain the same.\n",
60062306a36Sopenharmony_ci			       key_id);
60162306a36Sopenharmony_ci			return -EINVAL;
60262306a36Sopenharmony_ci		}
60362306a36Sopenharmony_ci		break;
60462306a36Sopenharmony_ci	case EXPECT_ROOT_INC:
60562306a36Sopenharmony_ci		if (WARN_ON(action_item->action == ACTION_PUT))
60662306a36Sopenharmony_ci			return -EINVAL;
60762306a36Sopenharmony_ci		if (orig_root_count + 1 != world->root_count) {
60862306a36Sopenharmony_ci			pr_err("Key %u: Root count was not incremented.\n",
60962306a36Sopenharmony_ci			       key_id);
61062306a36Sopenharmony_ci			return -EINVAL;
61162306a36Sopenharmony_ci		}
61262306a36Sopenharmony_ci		break;
61362306a36Sopenharmony_ci	case EXPECT_ROOT_DEC:
61462306a36Sopenharmony_ci		if (WARN_ON(action_item->action == ACTION_GET))
61562306a36Sopenharmony_ci			return -EINVAL;
61662306a36Sopenharmony_ci		if (orig_root_count - 1 != world->root_count) {
61762306a36Sopenharmony_ci			pr_err("Key %u: Root count was not decremented.\n",
61862306a36Sopenharmony_ci			       key_id);
61962306a36Sopenharmony_ci			return -EINVAL;
62062306a36Sopenharmony_ci		}
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	return 0;
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_cistatic unsigned int obj_to_key_id(struct objagg_obj *objagg_obj)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	const struct tokey *root_key;
62962306a36Sopenharmony_ci	const struct delta *delta;
63062306a36Sopenharmony_ci	unsigned int key_id;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	root_key = objagg_obj_root_priv(objagg_obj);
63362306a36Sopenharmony_ci	key_id = root_key->id;
63462306a36Sopenharmony_ci	delta = objagg_obj_delta_priv(objagg_obj);
63562306a36Sopenharmony_ci	if (delta)
63662306a36Sopenharmony_ci		key_id += delta->key_id_diff;
63762306a36Sopenharmony_ci	return key_id;
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_cistatic int
64162306a36Sopenharmony_cicheck_expect_stats_nums(const struct objagg_obj_stats_info *stats_info,
64262306a36Sopenharmony_ci			const struct expect_stats_info *expect_stats_info,
64362306a36Sopenharmony_ci			const char **errmsg)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	if (stats_info->is_root != expect_stats_info->is_root) {
64662306a36Sopenharmony_ci		if (errmsg)
64762306a36Sopenharmony_ci			*errmsg = "Incorrect root/delta indication";
64862306a36Sopenharmony_ci		return -EINVAL;
64962306a36Sopenharmony_ci	}
65062306a36Sopenharmony_ci	if (stats_info->stats.user_count !=
65162306a36Sopenharmony_ci	    expect_stats_info->stats.user_count) {
65262306a36Sopenharmony_ci		if (errmsg)
65362306a36Sopenharmony_ci			*errmsg = "Incorrect user count";
65462306a36Sopenharmony_ci		return -EINVAL;
65562306a36Sopenharmony_ci	}
65662306a36Sopenharmony_ci	if (stats_info->stats.delta_user_count !=
65762306a36Sopenharmony_ci	    expect_stats_info->stats.delta_user_count) {
65862306a36Sopenharmony_ci		if (errmsg)
65962306a36Sopenharmony_ci			*errmsg = "Incorrect delta user count";
66062306a36Sopenharmony_ci		return -EINVAL;
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci	return 0;
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_cistatic int
66662306a36Sopenharmony_cicheck_expect_stats_key_id(const struct objagg_obj_stats_info *stats_info,
66762306a36Sopenharmony_ci			  const struct expect_stats_info *expect_stats_info,
66862306a36Sopenharmony_ci			  const char **errmsg)
66962306a36Sopenharmony_ci{
67062306a36Sopenharmony_ci	if (obj_to_key_id(stats_info->objagg_obj) !=
67162306a36Sopenharmony_ci	    expect_stats_info->key_id) {
67262306a36Sopenharmony_ci		if (errmsg)
67362306a36Sopenharmony_ci			*errmsg = "incorrect key id";
67462306a36Sopenharmony_ci		return -EINVAL;
67562306a36Sopenharmony_ci	}
67662306a36Sopenharmony_ci	return 0;
67762306a36Sopenharmony_ci}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_cistatic int check_expect_stats_neigh(const struct objagg_stats *stats,
68062306a36Sopenharmony_ci				    const struct expect_stats *expect_stats,
68162306a36Sopenharmony_ci				    int pos)
68262306a36Sopenharmony_ci{
68362306a36Sopenharmony_ci	int i;
68462306a36Sopenharmony_ci	int err;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	for (i = pos - 1; i >= 0; i--) {
68762306a36Sopenharmony_ci		err = check_expect_stats_nums(&stats->stats_info[i],
68862306a36Sopenharmony_ci					      &expect_stats->info[pos], NULL);
68962306a36Sopenharmony_ci		if (err)
69062306a36Sopenharmony_ci			break;
69162306a36Sopenharmony_ci		err = check_expect_stats_key_id(&stats->stats_info[i],
69262306a36Sopenharmony_ci						&expect_stats->info[pos], NULL);
69362306a36Sopenharmony_ci		if (!err)
69462306a36Sopenharmony_ci			return 0;
69562306a36Sopenharmony_ci	}
69662306a36Sopenharmony_ci	for (i = pos + 1; i < stats->stats_info_count; i++) {
69762306a36Sopenharmony_ci		err = check_expect_stats_nums(&stats->stats_info[i],
69862306a36Sopenharmony_ci					      &expect_stats->info[pos], NULL);
69962306a36Sopenharmony_ci		if (err)
70062306a36Sopenharmony_ci			break;
70162306a36Sopenharmony_ci		err = check_expect_stats_key_id(&stats->stats_info[i],
70262306a36Sopenharmony_ci						&expect_stats->info[pos], NULL);
70362306a36Sopenharmony_ci		if (!err)
70462306a36Sopenharmony_ci			return 0;
70562306a36Sopenharmony_ci	}
70662306a36Sopenharmony_ci	return -EINVAL;
70762306a36Sopenharmony_ci}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_cistatic int __check_expect_stats(const struct objagg_stats *stats,
71062306a36Sopenharmony_ci				const struct expect_stats *expect_stats,
71162306a36Sopenharmony_ci				const char **errmsg)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	int i;
71462306a36Sopenharmony_ci	int err;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	if (stats->stats_info_count != expect_stats->info_count) {
71762306a36Sopenharmony_ci		*errmsg = "Unexpected object count";
71862306a36Sopenharmony_ci		return -EINVAL;
71962306a36Sopenharmony_ci	}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	for (i = 0; i < stats->stats_info_count; i++) {
72262306a36Sopenharmony_ci		err = check_expect_stats_nums(&stats->stats_info[i],
72362306a36Sopenharmony_ci					      &expect_stats->info[i], errmsg);
72462306a36Sopenharmony_ci		if (err)
72562306a36Sopenharmony_ci			return err;
72662306a36Sopenharmony_ci		err = check_expect_stats_key_id(&stats->stats_info[i],
72762306a36Sopenharmony_ci						&expect_stats->info[i], errmsg);
72862306a36Sopenharmony_ci		if (err) {
72962306a36Sopenharmony_ci			/* It is possible that one of the neighbor stats with
73062306a36Sopenharmony_ci			 * same numbers have the correct key id, so check it
73162306a36Sopenharmony_ci			 */
73262306a36Sopenharmony_ci			err = check_expect_stats_neigh(stats, expect_stats, i);
73362306a36Sopenharmony_ci			if (err)
73462306a36Sopenharmony_ci				return err;
73562306a36Sopenharmony_ci		}
73662306a36Sopenharmony_ci	}
73762306a36Sopenharmony_ci	return 0;
73862306a36Sopenharmony_ci}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_cistatic int check_expect_stats(struct objagg *objagg,
74162306a36Sopenharmony_ci			      const struct expect_stats *expect_stats,
74262306a36Sopenharmony_ci			      const char **errmsg)
74362306a36Sopenharmony_ci{
74462306a36Sopenharmony_ci	const struct objagg_stats *stats;
74562306a36Sopenharmony_ci	int err;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	stats = objagg_stats_get(objagg);
74862306a36Sopenharmony_ci	if (IS_ERR(stats)) {
74962306a36Sopenharmony_ci		*errmsg = "objagg_stats_get() failed.";
75062306a36Sopenharmony_ci		return PTR_ERR(stats);
75162306a36Sopenharmony_ci	}
75262306a36Sopenharmony_ci	err = __check_expect_stats(stats, expect_stats, errmsg);
75362306a36Sopenharmony_ci	objagg_stats_put(stats);
75462306a36Sopenharmony_ci	return err;
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_cistatic int test_delta_action_item(struct world *world,
75862306a36Sopenharmony_ci				  struct objagg *objagg,
75962306a36Sopenharmony_ci				  const struct action_item *action_item,
76062306a36Sopenharmony_ci				  bool inverse)
76162306a36Sopenharmony_ci{
76262306a36Sopenharmony_ci	unsigned int orig_delta_count = world->delta_count;
76362306a36Sopenharmony_ci	unsigned int orig_root_count = world->root_count;
76462306a36Sopenharmony_ci	unsigned int key_id = action_item->key_id;
76562306a36Sopenharmony_ci	enum action action = action_item->action;
76662306a36Sopenharmony_ci	struct objagg_obj *objagg_obj;
76762306a36Sopenharmony_ci	const char *errmsg;
76862306a36Sopenharmony_ci	int err;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	if (inverse)
77162306a36Sopenharmony_ci		action = action == ACTION_GET ? ACTION_PUT : ACTION_GET;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	switch (action) {
77462306a36Sopenharmony_ci	case ACTION_GET:
77562306a36Sopenharmony_ci		objagg_obj = world_obj_get(world, objagg, key_id);
77662306a36Sopenharmony_ci		if (IS_ERR(objagg_obj))
77762306a36Sopenharmony_ci			return PTR_ERR(objagg_obj);
77862306a36Sopenharmony_ci		break;
77962306a36Sopenharmony_ci	case ACTION_PUT:
78062306a36Sopenharmony_ci		world_obj_put(world, objagg, key_id);
78162306a36Sopenharmony_ci		break;
78262306a36Sopenharmony_ci	}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	if (inverse)
78562306a36Sopenharmony_ci		return 0;
78662306a36Sopenharmony_ci	err = check_expect(world, action_item,
78762306a36Sopenharmony_ci			   orig_delta_count, orig_root_count);
78862306a36Sopenharmony_ci	if (err)
78962306a36Sopenharmony_ci		goto errout;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	err = check_expect_stats(objagg, &action_item->expect_stats, &errmsg);
79262306a36Sopenharmony_ci	if (err) {
79362306a36Sopenharmony_ci		pr_err("Key %u: Stats: %s\n", action_item->key_id, errmsg);
79462306a36Sopenharmony_ci		goto errout;
79562306a36Sopenharmony_ci	}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	return 0;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_cierrout:
80062306a36Sopenharmony_ci	/* This can only happen when action is not inversed.
80162306a36Sopenharmony_ci	 * So in case of an error, cleanup by doing inverse action.
80262306a36Sopenharmony_ci	 */
80362306a36Sopenharmony_ci	test_delta_action_item(world, objagg, action_item, true);
80462306a36Sopenharmony_ci	return err;
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_cistatic int test_delta(void)
80862306a36Sopenharmony_ci{
80962306a36Sopenharmony_ci	struct world world = {};
81062306a36Sopenharmony_ci	struct objagg *objagg;
81162306a36Sopenharmony_ci	int i;
81262306a36Sopenharmony_ci	int err;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	objagg = objagg_create(&delta_ops, NULL, &world);
81562306a36Sopenharmony_ci	if (IS_ERR(objagg))
81662306a36Sopenharmony_ci		return PTR_ERR(objagg);
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(action_items); i++) {
81962306a36Sopenharmony_ci		err = test_delta_action_item(&world, objagg,
82062306a36Sopenharmony_ci					     &action_items[i], false);
82162306a36Sopenharmony_ci		if (err)
82262306a36Sopenharmony_ci			goto err_do_action_item;
82362306a36Sopenharmony_ci	}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	objagg_destroy(objagg);
82662306a36Sopenharmony_ci	return 0;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_cierr_do_action_item:
82962306a36Sopenharmony_ci	for (i--; i >= 0; i--)
83062306a36Sopenharmony_ci		test_delta_action_item(&world, objagg, &action_items[i], true);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	objagg_destroy(objagg);
83362306a36Sopenharmony_ci	return err;
83462306a36Sopenharmony_ci}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_cistruct hints_case {
83762306a36Sopenharmony_ci	const unsigned int *key_ids;
83862306a36Sopenharmony_ci	size_t key_ids_count;
83962306a36Sopenharmony_ci	struct expect_stats expect_stats;
84062306a36Sopenharmony_ci	struct expect_stats expect_stats_hints;
84162306a36Sopenharmony_ci};
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_cistatic const unsigned int hints_case_key_ids[] = {
84462306a36Sopenharmony_ci	1, 7, 3, 5, 3, 1, 30, 8, 8, 5, 6, 8,
84562306a36Sopenharmony_ci};
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_cistatic const struct hints_case hints_case = {
84862306a36Sopenharmony_ci	.key_ids = hints_case_key_ids,
84962306a36Sopenharmony_ci	.key_ids_count = ARRAY_SIZE(hints_case_key_ids),
85062306a36Sopenharmony_ci	.expect_stats =
85162306a36Sopenharmony_ci		EXPECT_STATS(7, ROOT(1, 2, 7), ROOT(7, 1, 4), ROOT(30, 1, 1),
85262306a36Sopenharmony_ci				DELTA(8, 3), DELTA(3, 2),
85362306a36Sopenharmony_ci				DELTA(5, 2), DELTA(6, 1)),
85462306a36Sopenharmony_ci	.expect_stats_hints =
85562306a36Sopenharmony_ci		EXPECT_STATS(7, ROOT(3, 2, 9), ROOT(1, 2, 2), ROOT(30, 1, 1),
85662306a36Sopenharmony_ci				DELTA(8, 3), DELTA(5, 2),
85762306a36Sopenharmony_ci				DELTA(6, 1), DELTA(7, 1)),
85862306a36Sopenharmony_ci};
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_cistatic void __pr_debug_stats(const struct objagg_stats *stats)
86162306a36Sopenharmony_ci{
86262306a36Sopenharmony_ci	int i;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	for (i = 0; i < stats->stats_info_count; i++)
86562306a36Sopenharmony_ci		pr_debug("Stat index %d key %u: u %d, d %d, %s\n", i,
86662306a36Sopenharmony_ci			 obj_to_key_id(stats->stats_info[i].objagg_obj),
86762306a36Sopenharmony_ci			 stats->stats_info[i].stats.user_count,
86862306a36Sopenharmony_ci			 stats->stats_info[i].stats.delta_user_count,
86962306a36Sopenharmony_ci			 stats->stats_info[i].is_root ? "root" : "noroot");
87062306a36Sopenharmony_ci}
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_cistatic void pr_debug_stats(struct objagg *objagg)
87362306a36Sopenharmony_ci{
87462306a36Sopenharmony_ci	const struct objagg_stats *stats;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	stats = objagg_stats_get(objagg);
87762306a36Sopenharmony_ci	if (IS_ERR(stats))
87862306a36Sopenharmony_ci		return;
87962306a36Sopenharmony_ci	__pr_debug_stats(stats);
88062306a36Sopenharmony_ci	objagg_stats_put(stats);
88162306a36Sopenharmony_ci}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_cistatic void pr_debug_hints_stats(struct objagg_hints *objagg_hints)
88462306a36Sopenharmony_ci{
88562306a36Sopenharmony_ci	const struct objagg_stats *stats;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	stats = objagg_hints_stats_get(objagg_hints);
88862306a36Sopenharmony_ci	if (IS_ERR(stats))
88962306a36Sopenharmony_ci		return;
89062306a36Sopenharmony_ci	__pr_debug_stats(stats);
89162306a36Sopenharmony_ci	objagg_stats_put(stats);
89262306a36Sopenharmony_ci}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_cistatic int check_expect_hints_stats(struct objagg_hints *objagg_hints,
89562306a36Sopenharmony_ci				    const struct expect_stats *expect_stats,
89662306a36Sopenharmony_ci				    const char **errmsg)
89762306a36Sopenharmony_ci{
89862306a36Sopenharmony_ci	const struct objagg_stats *stats;
89962306a36Sopenharmony_ci	int err;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	stats = objagg_hints_stats_get(objagg_hints);
90262306a36Sopenharmony_ci	if (IS_ERR(stats))
90362306a36Sopenharmony_ci		return PTR_ERR(stats);
90462306a36Sopenharmony_ci	err = __check_expect_stats(stats, expect_stats, errmsg);
90562306a36Sopenharmony_ci	objagg_stats_put(stats);
90662306a36Sopenharmony_ci	return err;
90762306a36Sopenharmony_ci}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_cistatic int test_hints_case(const struct hints_case *hints_case)
91062306a36Sopenharmony_ci{
91162306a36Sopenharmony_ci	struct objagg_obj *objagg_obj;
91262306a36Sopenharmony_ci	struct objagg_hints *hints;
91362306a36Sopenharmony_ci	struct world world2 = {};
91462306a36Sopenharmony_ci	struct world world = {};
91562306a36Sopenharmony_ci	struct objagg *objagg2;
91662306a36Sopenharmony_ci	struct objagg *objagg;
91762306a36Sopenharmony_ci	const char *errmsg;
91862306a36Sopenharmony_ci	int i;
91962306a36Sopenharmony_ci	int err;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	objagg = objagg_create(&delta_ops, NULL, &world);
92262306a36Sopenharmony_ci	if (IS_ERR(objagg))
92362306a36Sopenharmony_ci		return PTR_ERR(objagg);
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	for (i = 0; i < hints_case->key_ids_count; i++) {
92662306a36Sopenharmony_ci		objagg_obj = world_obj_get(&world, objagg,
92762306a36Sopenharmony_ci					   hints_case->key_ids[i]);
92862306a36Sopenharmony_ci		if (IS_ERR(objagg_obj)) {
92962306a36Sopenharmony_ci			err = PTR_ERR(objagg_obj);
93062306a36Sopenharmony_ci			goto err_world_obj_get;
93162306a36Sopenharmony_ci		}
93262306a36Sopenharmony_ci	}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	pr_debug_stats(objagg);
93562306a36Sopenharmony_ci	err = check_expect_stats(objagg, &hints_case->expect_stats, &errmsg);
93662306a36Sopenharmony_ci	if (err) {
93762306a36Sopenharmony_ci		pr_err("Stats: %s\n", errmsg);
93862306a36Sopenharmony_ci		goto err_check_expect_stats;
93962306a36Sopenharmony_ci	}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	hints = objagg_hints_get(objagg, OBJAGG_OPT_ALGO_SIMPLE_GREEDY);
94262306a36Sopenharmony_ci	if (IS_ERR(hints)) {
94362306a36Sopenharmony_ci		err = PTR_ERR(hints);
94462306a36Sopenharmony_ci		goto err_hints_get;
94562306a36Sopenharmony_ci	}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	pr_debug_hints_stats(hints);
94862306a36Sopenharmony_ci	err = check_expect_hints_stats(hints, &hints_case->expect_stats_hints,
94962306a36Sopenharmony_ci				       &errmsg);
95062306a36Sopenharmony_ci	if (err) {
95162306a36Sopenharmony_ci		pr_err("Hints stats: %s\n", errmsg);
95262306a36Sopenharmony_ci		goto err_check_expect_hints_stats;
95362306a36Sopenharmony_ci	}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	objagg2 = objagg_create(&delta_ops, hints, &world2);
95662306a36Sopenharmony_ci	if (IS_ERR(objagg2))
95762306a36Sopenharmony_ci		return PTR_ERR(objagg2);
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	for (i = 0; i < hints_case->key_ids_count; i++) {
96062306a36Sopenharmony_ci		objagg_obj = world_obj_get(&world2, objagg2,
96162306a36Sopenharmony_ci					   hints_case->key_ids[i]);
96262306a36Sopenharmony_ci		if (IS_ERR(objagg_obj)) {
96362306a36Sopenharmony_ci			err = PTR_ERR(objagg_obj);
96462306a36Sopenharmony_ci			goto err_world2_obj_get;
96562306a36Sopenharmony_ci		}
96662306a36Sopenharmony_ci	}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	pr_debug_stats(objagg2);
96962306a36Sopenharmony_ci	err = check_expect_stats(objagg2, &hints_case->expect_stats_hints,
97062306a36Sopenharmony_ci				 &errmsg);
97162306a36Sopenharmony_ci	if (err) {
97262306a36Sopenharmony_ci		pr_err("Stats2: %s\n", errmsg);
97362306a36Sopenharmony_ci		goto err_check_expect_stats2;
97462306a36Sopenharmony_ci	}
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	err = 0;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_cierr_check_expect_stats2:
97962306a36Sopenharmony_cierr_world2_obj_get:
98062306a36Sopenharmony_ci	for (i--; i >= 0; i--)
98162306a36Sopenharmony_ci		world_obj_put(&world2, objagg, hints_case->key_ids[i]);
98262306a36Sopenharmony_ci	i = hints_case->key_ids_count;
98362306a36Sopenharmony_ci	objagg_destroy(objagg2);
98462306a36Sopenharmony_cierr_check_expect_hints_stats:
98562306a36Sopenharmony_ci	objagg_hints_put(hints);
98662306a36Sopenharmony_cierr_hints_get:
98762306a36Sopenharmony_cierr_check_expect_stats:
98862306a36Sopenharmony_cierr_world_obj_get:
98962306a36Sopenharmony_ci	for (i--; i >= 0; i--)
99062306a36Sopenharmony_ci		world_obj_put(&world, objagg, hints_case->key_ids[i]);
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	objagg_destroy(objagg);
99362306a36Sopenharmony_ci	return err;
99462306a36Sopenharmony_ci}
99562306a36Sopenharmony_cistatic int test_hints(void)
99662306a36Sopenharmony_ci{
99762306a36Sopenharmony_ci	return test_hints_case(&hints_case);
99862306a36Sopenharmony_ci}
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_cistatic int __init test_objagg_init(void)
100162306a36Sopenharmony_ci{
100262306a36Sopenharmony_ci	int err;
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	err = test_nodelta();
100562306a36Sopenharmony_ci	if (err)
100662306a36Sopenharmony_ci		return err;
100762306a36Sopenharmony_ci	err = test_delta();
100862306a36Sopenharmony_ci	if (err)
100962306a36Sopenharmony_ci		return err;
101062306a36Sopenharmony_ci	return test_hints();
101162306a36Sopenharmony_ci}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_cistatic void __exit test_objagg_exit(void)
101462306a36Sopenharmony_ci{
101562306a36Sopenharmony_ci}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_cimodule_init(test_objagg_init);
101862306a36Sopenharmony_cimodule_exit(test_objagg_exit);
101962306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
102062306a36Sopenharmony_ciMODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>");
102162306a36Sopenharmony_ciMODULE_DESCRIPTION("Test module for objagg");
1022