diff mbox series

[net-next/mlxsw,internal,preRFC,1/3] lib: introduce initial implementation of object aggregation manager

Message ID 20180805130133.6812-2-jiri@resnulli.us (mailing list archive)
State RFC
Headers show
Series mlxsw: Introduce ERP sharing by multiple masks - static approach | expand

Commit Message

Jiri Pirko Aug. 5, 2018, 1:01 p.m. UTC
From: Jiri Pirko <jiri@mellanox.com>

This lib tracks objects which could be of two types:
1) root object
2) nested object - with a "delta" which differentiates it from
                   the associated root object
The objects are tracked by a hashtable and reference-counted. User is
responsible of implementing callbacks to create/destroy root entity
related to each root object and callback to create/destroy nested object
delta.

TODO: fill-up test_objagg.c

Signed-off-by: Jiri Pirko <jiri@mellanox.com>
---
 include/linux/objagg.h |  27 ++++++
 lib/Kconfig            |   3 +
 lib/Kconfig.debug      |  10 +++
 lib/Makefile           |   3 +
 lib/objagg.c           | 231 +++++++++++++++++++++++++++++++++++++++++++++++++
 lib/test_objagg.c      |   8 ++
 6 files changed, 282 insertions(+)
 create mode 100644 include/linux/objagg.h
 create mode 100644 lib/objagg.c
 create mode 100644 lib/test_objagg.c
diff mbox series

Patch

diff --git a/include/linux/objagg.h b/include/linux/objagg.h
new file mode 100644
index 000000000000..f81dd1fc1353
--- /dev/null
+++ b/include/linux/objagg.h
@@ -0,0 +1,27 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
+
+#ifndef _OBJAGG_H
+#define _OBJAGG_H
+
+struct objagg_ops {
+	size_t obj_size;
+	void * (*delta_create)(void *priv, void *parent_obj, void *obj);
+	void (*delta_destroy)(void *priv, void *delta_priv);
+	void * (*root_create)(void *priv, void *obj);
+	void (*root_destroy)(void *priv, void *root_priv);
+};
+
+struct objagg;
+struct objagg_obj;
+
+const void *objagg_obj_root_priv(const struct objagg_obj *objagg_obj);
+const void *objagg_obj_delta_priv(const struct objagg_obj *objagg_obj);
+const void *objagg_obj_raw(const struct objagg_obj *objagg_obj);
+
+struct objagg_obj *objagg_obj_get(struct objagg *objagg, void *obj);
+void objagg_obj_put(struct objagg *objagg, struct objagg_obj *objagg_obj);
+struct objagg *objagg_create(const struct objagg_ops *ops, void *priv);
+void objagg_destroy(struct objagg *objagg);
+
+#endif
diff --git a/lib/Kconfig b/lib/Kconfig
index 706836ec314d..f1b7f8042433 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -614,3 +614,6 @@  config GENERIC_LIB_CMPDI2
 
 config GENERIC_LIB_UCMPDI2
 	bool
+
+config OBJAGG
+	tristate "magg" if COMPILE_TEST
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index d3d82eccdfa5..dbd8d0ed2226 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1973,6 +1973,16 @@  config TEST_DEBUG_VIRTUAL
 
 	  If unsure, say N.
 
+config TEST_OBJAGG
+	tristate "Perform selftest on object aggreration manager"
+	default n
+	depends on OBJAGG
+	help
+	  Enable this option to test object aggregation manager on boot
+	  (or module load).
+
+	  If unsure, say N.
+
 endif # RUNTIME_TESTING_MENU
 
 config MEMTEST
diff --git a/lib/Makefile b/lib/Makefile
index 60d0d5f90946..02d66aa0e5cf 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -70,6 +70,7 @@  obj-$(CONFIG_TEST_UUID) += test_uuid.o
 obj-$(CONFIG_TEST_PARMAN) += test_parman.o
 obj-$(CONFIG_TEST_KMOD) += test_kmod.o
 obj-$(CONFIG_TEST_DEBUG_VIRTUAL) += test_debug_virtual.o
+obj-$(CONFIG_TEST_OBJAGG) += test_objagg.o
 
 ifeq ($(CONFIG_DEBUG_KOBJECT),y)
 CFLAGS_kobject.o += -DDEBUG
@@ -258,3 +259,5 @@  obj-$(CONFIG_GENERIC_LIB_LSHRDI3) += lshrdi3.o
 obj-$(CONFIG_GENERIC_LIB_MULDI3) += muldi3.o
 obj-$(CONFIG_GENERIC_LIB_CMPDI2) += cmpdi2.o
 obj-$(CONFIG_GENERIC_LIB_UCMPDI2) += ucmpdi2.o
+
+obj-$(CONFIG_OBJAGG) += objagg.o
diff --git a/lib/objagg.c b/lib/objagg.c
new file mode 100644
index 000000000000..936177e96f47
--- /dev/null
+++ b/lib/objagg.c
@@ -0,0 +1,231 @@ 
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/refcount.h>
+#include <linux/list.h>
+#include <linux/rhashtable.h>
+#include <linux/objagg.h>
+
+struct objagg {
+	const struct objagg_ops *ops;
+	void *priv;
+	struct rhashtable obj_ht;
+	struct rhashtable_params ht_params;
+	struct list_head obj_list;
+};
+
+struct objagg_obj {
+	struct rhash_head ht_node; /* member of objagg->obj_ht */
+	struct list_head list; /* member of objagg->obj_list */
+	struct objagg_obj *parent; /* if the object is nested, this
+				    * holds pointer to parent, otherwise NULL
+				    */
+	union {
+		void *delta_priv; /* user delta private */
+		void *root_priv; /* user root private */
+	};
+	refcount_t refcount; /* counts number of users of this object
+			      * including nested objects.
+			      */
+	unsigned long obj[0];
+};
+
+static bool objagg_obj_is_root(const struct objagg_obj *objagg_obj)
+{
+	/* Nesting is not supported, so we can use ->parent
+	 * to figure out if the object is root.
+	 */
+	return !objagg_obj->parent;
+}
+
+const void *objagg_obj_root_priv(const struct objagg_obj *objagg_obj)
+{
+	if (objagg_obj_is_root(objagg_obj))
+		return objagg_obj->root_priv;
+	WARN_ON(!objagg_obj_is_root(objagg_obj->parent));
+	return objagg_obj->parent->root_priv;
+}
+EXPORT_SYMBOL(objagg_obj_root_priv);
+
+const void *objagg_obj_delta_priv(const struct objagg_obj *objagg_obj)
+{
+	if (objagg_obj_is_root(objagg_obj))
+		return NULL;
+	return objagg_obj->delta_priv;
+}
+EXPORT_SYMBOL(objagg_obj_delta_priv);
+
+const void *objagg_obj_raw(const struct objagg_obj *objagg_obj)
+{
+	return objagg_obj->obj;
+}
+EXPORT_SYMBOL(objagg_obj_raw);
+
+static struct objagg_obj *objagg_obj_lookup(struct objagg *objagg, void *obj)
+{
+	return rhashtable_lookup_fast(&objagg->obj_ht, obj, objagg->ht_params);
+}
+
+static int objagg_obj_parent_assign(struct objagg *objagg,
+				    struct objagg_obj *objagg_obj)
+{
+	struct objagg_obj *objagg_obj_cur;
+	void *delta_priv;
+
+	list_for_each_entry(objagg_obj_cur, &objagg->obj_list, list) {
+		/* Nesting is not supported. In case the object
+		 * is not root, it cannot be assigned as parent.
+		 */
+		if (!objagg_obj_is_root(objagg_obj))
+			continue;
+		delta_priv = objagg->ops->delta_create(objagg->priv,
+						       objagg_obj_cur->obj,
+						       objagg_obj->obj);
+		if (!IS_ERR(delta_priv)) {
+			/* User returned a delta private, that means that
+			 * our object can be aggregated into the cursor.
+			 */
+			objagg_obj->parent = objagg_obj_cur;
+			objagg_obj->delta_priv = delta_priv;
+			refcount_inc(&objagg_obj->parent->refcount);
+			return 0;
+		}
+	}
+	return -ENOENT;
+}
+
+static void objagg_obj_parent_unassign(struct objagg *objagg,
+				       struct objagg_obj *objagg_obj)
+{
+	objagg->ops->delta_destroy(objagg->priv, objagg_obj->delta_priv);
+	objagg_obj_put(objagg, objagg_obj->parent);
+}
+
+static int objagg_obj_root_create(struct objagg *objagg,
+				  struct objagg_obj *objagg_obj)
+{
+	objagg_obj->root_priv = objagg->ops->root_create(objagg->priv,
+							 objagg_obj->obj);
+	if (IS_ERR(objagg_obj))
+		return PTR_ERR(objagg_obj);
+	return 0;
+}
+
+static void objagg_obj_root_destroy(struct objagg *objagg,
+				    struct objagg_obj *objagg_obj)
+{
+	objagg->ops->root_destroy(objagg->priv, objagg_obj->root_priv);
+}
+
+static int objagg_obj_init(struct objagg *objagg,
+			   struct objagg_obj *objagg_obj)
+{
+	int err;
+
+	/* Try to find if the object can be aggregated under an existing one. */
+	err = objagg_obj_parent_assign(objagg, objagg_obj);
+	if (!err)
+		return 0;
+	/* If aggregation is not possible, make the object a root. */
+	return objagg_obj_root_create(objagg, objagg_obj);
+}
+
+static void objagg_obj_fini(struct objagg *objagg,
+			    struct objagg_obj *objagg_obj)
+{
+	if (!objagg_obj_is_root(objagg_obj))
+		objagg_obj_parent_unassign(objagg, objagg_obj);
+	else
+		objagg_obj_root_destroy(objagg, objagg_obj);
+}
+
+struct objagg_obj *objagg_obj_get(struct objagg *objagg, void *obj)
+{
+	struct objagg_obj *objagg_obj;
+	int err;
+
+	/* First, try to find the object exactly as user passed that,
+	 * perhaps it is already in use.
+	 */
+	objagg_obj = objagg_obj_lookup(objagg, obj);
+	if (objagg_obj) {
+		refcount_inc(&objagg_obj->refcount);
+		return objagg_obj;
+	}
+
+	objagg_obj = kzalloc(sizeof(*objagg_obj) + objagg->ops->obj_size,
+			     GFP_KERNEL);
+	if (!objagg_obj)
+		return ERR_PTR(-ENOMEM);
+	refcount_set(&objagg_obj->refcount, 1);
+	memcpy(objagg_obj->obj, obj, objagg->ops->obj_size);
+
+	err = objagg_obj_init(objagg, objagg_obj);
+	if (err)
+		goto err_obj_init;
+
+	err = rhashtable_insert_fast(&objagg->obj_ht, &objagg_obj->ht_node,
+				     objagg->ht_params);
+	if (err)
+		goto err_ht_insert;
+
+	return objagg_obj;
+
+err_ht_insert:
+	objagg_obj_fini(objagg, objagg_obj);
+err_obj_init:
+	kfree(objagg_obj);
+	return ERR_PTR(err);
+}
+
+void objagg_obj_put(struct objagg *objagg, struct objagg_obj *objagg_obj)
+{
+	if (!refcount_dec_and_test(&objagg_obj->refcount))
+		return;
+	rhashtable_remove_fast(&objagg->obj_ht, &objagg_obj->ht_node,
+			       objagg->ht_params);
+	objagg_obj_fini(objagg, objagg_obj);
+	kfree(objagg_obj);
+}
+EXPORT_SYMBOL(objagg_obj_put);
+
+struct objagg *objagg_create(const struct objagg_ops *ops, void *priv)
+{
+	struct objagg *objagg;
+	int err;
+
+	objagg = kzalloc(sizeof(*objagg), GFP_KERNEL);
+	if (!objagg)
+		return ERR_PTR(-ENOMEM);
+	objagg->ops = ops;
+	INIT_LIST_HEAD(&objagg->obj_list);
+
+	objagg->ht_params.key_len = ops->obj_size;
+	objagg->ht_params.key_offset = offsetof(struct objagg_obj, obj);
+	objagg->ht_params.head_offset = offsetof(struct objagg_obj, ht_node);
+
+	err = rhashtable_init(&objagg->obj_ht, &objagg->ht_params);
+	if (err)
+		goto err_rhashtable_init;
+
+	return objagg;
+
+err_rhashtable_init:
+	kfree(objagg);
+	return ERR_PTR(err);
+}
+EXPORT_SYMBOL(objagg_create);
+
+void objagg_destroy(struct objagg *objagg)
+{
+	WARN_ON(!list_empty(&objagg->obj_list));
+	rhashtable_destroy(&objagg->obj_ht);
+	kfree(objagg);
+}
+EXPORT_SYMBOL(objagg_destroy);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>");
+MODULE_DESCRIPTION("Object aggregation manager");
diff --git a/lib/test_objagg.c b/lib/test_objagg.c
new file mode 100644
index 000000000000..5669bebf4a41
--- /dev/null
+++ b/lib/test_objagg.c
@@ -0,0 +1,8 @@ 
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
+
+#include <linux/objagg.h>
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>");
+MODULE_DESCRIPTION("Test module for objagg");