From 25a1301bc18dcb811f69b29861ad928369b12713 Mon Sep 17 00:00:00 2001
From: Armin Luntzer <armin.luntzer@univie.ac.at>
Date: Mon, 20 Mar 2017 17:12:13 +0100
Subject: [PATCH] add concept of data processing network

---
 include/data_proc_net.h              |  32 +++
 include/data_proc_task.h             |   3 +-
 include/data_proc_tracker.h          |  32 ++-
 init/main.c                          |  18 ++
 lib/Makefile                         |   1 +
 lib/data_proc_net.c                  | 356 +++++++++++++++++++++++++
 lib/data_proc_task.c                 |   7 +-
 lib/data_proc_tracker.c              | 148 +++++++++--
 samples/Kconfig                      |   5 +
 samples/Makefile                     |   1 +
 samples/proc_chain/Makefile          |  27 ++
 samples/proc_chain/proc_chain_demo.c | 374 +++++++++++++++++++++++++++
 12 files changed, 977 insertions(+), 27 deletions(-)
 create mode 100644 include/data_proc_net.h
 create mode 100644 lib/data_proc_net.c
 create mode 100644 samples/proc_chain/Makefile
 create mode 100644 samples/proc_chain/proc_chain_demo.c

diff --git a/include/data_proc_net.h b/include/data_proc_net.h
new file mode 100644
index 0000000..f7a5dab
--- /dev/null
+++ b/include/data_proc_net.h
@@ -0,0 +1,32 @@
+/**
+ * @file include/data_proc_net.h
+ */
+
+#ifndef _DATA_PROC_NET_H_
+#define _DATA_PROC_NET_H_
+
+#include <kernel/types.h>
+#include <list.h>
+#include <data_proc_tracker.h>
+
+
+
+/* these are the reserved processing node identifiers */
+#define PN_OP_NODE_IN   0xFFFFFFFF
+#define PN_OP_NODE_OUT  0x00000000
+
+
+struct proc_net;
+
+int pt_track_execute_next(struct proc_tracker *pt);
+void pn_input_task(struct proc_net *pn, struct proc_task *t);
+int pn_process_next(struct proc_net *pn);
+int pn_process_inputs(struct proc_net *pn);
+
+int pn_create_output_node(struct proc_net *pn,
+			  int (*op)(unsigned long op_code, struct proc_task *));
+int pn_add_node(struct proc_net *pn, struct proc_tracker *pt);
+struct proc_net *pn_create(size_t n_in_tasks_crit, size_t n_out_tasks_crit);
+void pn_destroy(struct proc_net *pn);
+
+#endif /* _DATA_PROC_NET_H_ */
diff --git a/include/data_proc_task.h b/include/data_proc_task.h
index 6145283..a72542e 100644
--- a/include/data_proc_task.h
+++ b/include/data_proc_task.h
@@ -5,7 +5,8 @@
 #ifndef _DATA_PROC_TASK_H_
 #define _DATA_PROC_TASK_H_
 
-
+#include <kernel/types.h>
+#include <list.h>
 
 
 struct proc_task {
diff --git a/include/data_proc_tracker.h b/include/data_proc_tracker.h
index c3a391b..e8ee2e9 100644
--- a/include/data_proc_tracker.h
+++ b/include/data_proc_tracker.h
@@ -5,20 +5,42 @@
 #ifndef _DATA_PROC_TRACKER_H_
 #define _DATA_PROC_TRACKER_H_
 
+#include <kernel/types.h>
+#include <list.h>
+#include <data_proc_task.h>
 
-struct proc_tracker;
+struct proc_tracker {
+	struct list_head tasks;
+	size_t n_tasks;
+	size_t n_tasks_crit;
 
+	unsigned long op_code;
+	
+	int (*op)(unsigned long op_code, struct proc_task *);
+
+
+	struct list_head node;	/* to be used for external tracking */
+};
+
+
+unsigned long pt_track_get_id(struct proc_tracker *pt);
+ 
 int pt_track_get_usage(struct proc_tracker *pt);
 
+int pt_track_level_critical(struct proc_tracker *pt);
+
 int pt_track_put(struct proc_tracker *pt, struct proc_task *t);
 
-struct proc_task pt_track_get(struct proc_tracker *pt);
+int pt_track_put_force(struct proc_tracker *pt, struct proc_task *t);
 
-void pt_track_sort_seq(struct proc_tracker *pt)
+struct proc_task *pt_track_get(struct proc_tracker *pt);
 
-struct proc_tracker *pt_track_create(size_t nmemb);
+void pt_track_sort_seq(struct proc_tracker *pt);
 
-int pt_track_expand(struct proc_tracker *pt, size_t nmemb);
+struct proc_tracker *pt_track_create(int (*op)(unsigned long op_code,
+					       struct proc_task *),
+				     unsigned long op_code,
+				     size_t n_tasks_crit);
 
 void pt_track_destroy(struct proc_tracker *pt);
 
diff --git a/init/main.c b/init/main.c
index 6819227..d5136ab 100644
--- a/init/main.c
+++ b/init/main.c
@@ -17,6 +17,7 @@
 #include <kernel/sysctl.h>
 
 
+
 #define MSG "MAIN: "
 
 void module_image_load_embedded(void);
@@ -34,6 +35,12 @@ static void kernel_init(void)
 }
 
 
+
+
+
+
+
+
 #ifdef CONFIG_TARGET_COMPILER_BOOT_CODE
 
 int main(void)
@@ -41,6 +48,7 @@ int main(void)
 	void *addr;
 	struct elf_module m;
 
+
 	kernel_init();
 
 	/* load the embedded AR image */
@@ -80,8 +88,18 @@ int main(void)
 	modules_list_loaded();
 #endif
 
+
+
+
+
+
+
+
+#if 0
 	/* load all available Xentium kernels from the embedded modules image */
 	module_load_xen_kernels();
+	while(1);
+#endif
 
 	return 0;
 }
diff --git a/lib/Makefile b/lib/Makefile
index 2405f0c..e45e8cf 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -7,3 +7,4 @@ lib-y			+= string.o
 lib-y			+= elf.o
 lib-y			+= data_proc_tracker.o
 lib-y			+= data_proc_task.o
+lib-y			+= data_proc_net.o
diff --git a/lib/data_proc_net.c b/lib/data_proc_net.c
new file mode 100644
index 0000000..543ce06
--- /dev/null
+++ b/lib/data_proc_net.c
@@ -0,0 +1,356 @@
+/**
+ * @file lib/data_proc_net.c
+ *
+ *
+ * This is a data processing network
+ *
+ */
+
+#include <kernel/printk.h>
+#include <kernel/kmem.h>
+#include <kernel/kernel.h>
+
+#include <errno.h>
+
+#include <data_proc_net.h>
+
+
+struct proc_net {
+	struct proc_tracker *in;
+	struct proc_tracker *out;
+
+	struct list_head nodes;
+	
+	size_t n;
+};	
+
+
+static int pn_dummy_op(unsigned long op_code, struct proc_task *pt)
+{
+	return 0;
+}
+
+/**
+ * @brief locate a tracker by op code
+ *
+ * @returns tracker or NULL if not found
+ * 
+ * @note this is not very efficient, especially for large numbers of nodes...
+ */
+
+static struct proc_tracker *pn_find_tracker(struct proc_net *pn,
+					    unsigned long op_code)
+{
+	struct proc_tracker *p_elem;
+
+
+	list_for_each_entry(p_elem, &pn->nodes, node)
+		if (p_elem->op_code == op_code)
+			return p_elem;
+
+
+	return NULL;
+}
+
+
+
+int pn_process_next(struct proc_net *pn)
+{
+	struct proc_task *t = NULL;
+	struct proc_tracker *pt;
+	static struct proc_tracker *pt_out;
+
+	unsigned long op;
+
+	
+	if (!pt_out)
+		pt = list_entry(pn->nodes.next, struct proc_tracker, node);
+
+	/* locate the first tracker that holds at least one task and process it
+	 * ideally, this would be sorted so that the most critical tracker
+	 * was the first item
+	 * XXX implement that sometime...
+	 */
+
+	list_for_each_entry(pt, &pn->nodes, node) {
+
+		t = pt_track_get(pt);
+
+		if (t)
+			break;
+	}
+
+	
+	while (1) {
+		if (!t)
+			break;	/* nothing to do */
+
+		/* current step's op code */
+		op = pt_get_pend_step_op_code(t);
+
+		/* XXX maybe eval return code, e.g. for signalling abort of
+		 * current task node processing */
+		pt->op(op, t);
+
+
+		pt_next_pend_step_done(t);
+
+		/* next steps's op code */
+		op = pt_get_pend_step_op_code(t);
+
+		if (!op) {
+			pt_track_put(pn->out, t);
+		} else if (pt_out->op_code != op) {
+			pt_out = pn_find_tracker(pn, op);
+
+			/* this should not happen */
+			if (!pt_out) {
+				pr_crit("Error, no such op code, "
+					"destroying task\n");
+
+				pt_destroy(t);
+				
+				/* reset */
+				pt_out = list_entry(pn->nodes.next,
+						    struct proc_tracker, node);
+		
+				t = pt_track_get(pt);
+				continue;
+			}
+		}
+
+		/* move to next matching node */	
+		pt_track_put(pt_out, t);
+
+		t = pt_track_get(pt);
+	}
+
+	/* move processing task to end of queue */
+	/* XXX should insert that based on critical level/fill state */
+	if (pt)
+		list_move_tail(&pt->node, &pn->nodes);
+
+
+	return 0;
+}
+
+
+
+
+
+/**
+ * @brief add a task to the input of the network
+ */
+
+void pn_input_task(struct proc_net *pn, struct proc_task *t)
+{
+	if (!pn)
+		return;
+	
+	if (!t)
+		return;
+
+	BUG_ON(pt_track_put_force(pn->in, t));
+}
+
+
+/**
+ * @brief assign input tasks to their first processing node
+ *
+ * @returns 0 or -1 on error (e.g. no processing nodes defined)
+ */
+
+int pn_process_inputs(struct proc_net *pn)
+{
+	unsigned long op;
+
+	struct proc_task *t;
+	static struct proc_tracker *pt;
+
+
+	if (list_empty(&pn->nodes))
+		return -1;
+	
+	if (!pt)
+		pt = list_entry(pn->nodes.next, struct proc_tracker, node);
+	
+	while (1) {
+		t = pt_track_get(pn->in);
+
+		if (!t)
+			break;
+
+		op = pt_get_pend_step_op_code(t);
+
+		if (pt->op_code != op) {
+			pt = pn_find_tracker(pn, op);
+			if (!pt) {
+				pr_crit("Error, no such op code, "
+					"destroying task\n");
+
+				pt_destroy(t);
+				
+				/* reset */
+				pt = list_entry(pn->nodes.next,
+						struct proc_tracker, node);
+				
+				t = pt_track_get(pt);
+				continue;
+			}
+		 }
+
+		BUG_ON(pt_track_put(pt, t));
+	}
+
+
+	return 0;
+}
+
+	
+/**
+ * @brief create an output node of the network
+ *
+ * @returns 0 on success, -ENOMEM on alloc error
+ *
+ * @note this destroys the previous output node on success only, otherwise the
+ *	 original node is left intact
+ */
+
+int pn_create_output_node(struct proc_net *pn,
+			  int (*op)(unsigned long op_code, struct proc_task *))
+
+{
+	struct proc_tracker *pt;
+
+
+	pt = pt_track_create(op, PN_OP_NODE_OUT, 1);
+	
+	if (!pt)
+		return -ENOMEM;
+
+	if (pn->out)
+		pt_track_destroy(pn->out);
+
+	return 0;
+}
+
+
+/**
+ * @brief add a tracker node to a processing network
+ *
+ * @returns 0 on success, -EINVAL on error
+ */
+
+int pn_add_node(struct proc_net *pn, struct proc_tracker *pt)
+
+{
+	if (!pn)
+		return -EINVAL;
+
+	if (!pt)
+		return -EINVAL;
+
+	if (pt->op_code == PN_OP_NODE_IN)
+		return -EINVAL;
+	
+	if (pt->op_code == PN_OP_NODE_OUT)
+		return -EINVAL;
+
+
+	list_add_tail(&pt->node, &pn->nodes);
+
+	pn->n++;
+
+	return 0;
+}
+
+
+/**
+ * @brief create a processing network with an input and output node
+ *
+ * @param n_input_tasks_critical critical level of input task
+ * @param n_output_tasks_critical critical level of output task
+ *
+ * @return processing network or NULL on error
+ *
+ * @note this creates a default output node that does nothing and just
+ *       accumulates tasks
+ */
+
+struct proc_net *pn_create(size_t n_in_tasks_crit, size_t n_out_tasks_crit)
+{
+	struct proc_net *pn;
+
+
+	if (!n_in_tasks_crit)
+		return NULL;
+	
+	if (!n_out_tasks_crit)
+		return NULL;
+
+	pn = (struct proc_net *) kzalloc(sizeof(struct proc_net));
+
+	if (!pn)
+		goto error;
+
+
+	/* the input node just accepts tasks and distributes them to the
+	 * appropriate trackers in the network
+	 */
+	pn->in = pt_track_create(pn_dummy_op, PN_OP_NODE_IN, 1);
+
+	if (!pn->in)
+		goto cleanup;
+
+	/* create a default output node that does nothing */
+	pn->out = pt_track_create(pn_dummy_op, PN_OP_NODE_OUT, 1);
+	if (!pn->out)
+		goto cleanup;
+
+
+	INIT_LIST_HEAD(&pn->nodes);
+
+
+	return pn;
+
+cleanup:
+	pt_track_destroy(pn->in);
+	pt_track_destroy(pn->out);
+
+	kfree(pn);
+
+error:
+	return NULL;
+}
+
+
+/**
+ * @brief destroy a processing network 
+ *
+ * @param pn a struct proc_net
+ *
+ *
+ * @note the data pointers in the processing tasks are untouched,
+ *	 see also pt_destroy() 
+ */
+
+void pn_destroy(struct proc_net *pn)
+{
+	struct proc_tracker *p_elem;
+	struct proc_tracker *p_tmp;
+
+
+	if (!pn)
+		return;
+
+	list_for_each_entry_safe(p_elem, p_tmp, &pn->nodes, node) {
+		list_del(&p_elem->node);
+		pt_track_destroy(p_elem);
+	}
+
+	
+	pt_track_destroy(pn->in);
+	pt_track_destroy(pn->out);
+
+	kfree(pn);
+}
diff --git a/lib/data_proc_task.c b/lib/data_proc_task.c
index 33e3966..6f51742 100644
--- a/lib/data_proc_task.c
+++ b/lib/data_proc_task.c
@@ -40,10 +40,10 @@
 #include <kernel/types.h>
 #include <kernel/kmem.h>
 #include <errno.h>
-#include <list.h>
 
 
 #include <data_proc_task.h>
+
 #define MSG "PT: "
 
 
@@ -204,6 +204,8 @@ unsigned long pt_get_pend_step_op_code(struct proc_task *t)
 {
 	struct proc_step *s;
 
+	if (!t)
+		return 0;
 
 	if (list_empty(&t->todo))
 		return 0;
@@ -467,6 +469,9 @@ void pt_destroy(struct proc_task *t)
 	struct proc_step *p_elem;
 
 
+	if (!t)
+		return;
+
 	list_for_each_entry(p_elem, &t->todo, node)
 		kfree(p_elem->op_info);
 
diff --git a/lib/data_proc_tracker.c b/lib/data_proc_tracker.c
index 5dc2324..708ba05 100644
--- a/lib/data_proc_tracker.c
+++ b/lib/data_proc_tracker.c
@@ -12,16 +12,37 @@
 #include <kernel/log2.h>
 #include <kernel/types.h>
 #include <errno.h>
-#include <list.h>
 
-#include <data_proc_task.h>
+#include <data_proc_tracker.h>
 
-struct proc_tracker {
-	struct list_head tasks;
-	size_t n;
-};
 
 
+/**
+ * @brief returns the op code of the tracker
+ *
+ * @param pt a struct proc_tracker
+ *
+ * @returns the op code
+ */
+
+unsigned long pt_track_get_op_code(struct proc_tracker *pt)
+{
+	return pt->op_code;
+}
+
+
+/**
+ * @brief check if the tracker is above its critical number of tasks 
+ *
+ * @param pt a struct proc_tracker
+ *
+ * @returns 1 if critical, 0 if not
+ */
+
+int pt_track_level_critical(struct proc_tracker *pt)
+{
+	return (pt->n_tasks >= pt->n_tasks_crit);
+}
 
 
 /**
@@ -34,7 +55,7 @@ struct proc_tracker {
 
 int pt_track_get_usage(struct proc_tracker *pt)
 {
-	return pt->n;
+	return pt->n_tasks;
 }
 
 
@@ -46,9 +67,47 @@ int pt_track_get_usage(struct proc_tracker *pt)
  * @param t a pointer to a task
  *
  * @returns 0 on success, -EINVAL on error
+ *
+ * @note if the pending step op code of the task does not match the op code of
+ *	 the tracker, it is rejected
  */
 
 int pt_track_put(struct proc_tracker *pt, struct proc_task *t)
+{
+	unsigned long op;
+
+
+	if (!pt)
+		return -EINVAL;
+	
+	if (!t)
+		return -EINVAL;
+
+	op = pt_get_pend_step_op_code(t);
+
+	if (op != pt->op_code)
+		return -1;
+
+	list_add_tail(&t->node, &pt->tasks);
+
+	pt->n_tasks++;
+
+	return 0;
+}
+
+
+/**
+ * @brief add a new item to the processing tracker, regardless of op code
+ *
+ * @param pt a struct processing_tracker
+ *
+ * @param t a pointer to a task
+ *
+ * @returns 0 on success, -EINVAL on error
+ *
+ */
+
+int pt_track_put_force(struct proc_tracker *pt, struct proc_task *t)
 {
 	if (!pt)
 		return -EINVAL;
@@ -58,7 +117,7 @@ int pt_track_put(struct proc_tracker *pt, struct proc_task *t)
 
 	list_add_tail(&t->node, &pt->tasks);
 
-	pt->n++;
+	pt->n_tasks++;
 
 	return 0;
 }
@@ -85,12 +144,39 @@ struct proc_task *pt_track_get(struct proc_tracker *pt)
 	
 	list_del(&t->node);
 	
-	pt->n--;
+	pt->n_tasks--;
 
 	return t;
 }
 
 
+
+/**
+ * @brief execute next item in processing tracker
+ *
+ * @param pt a struct processing_tracker
+ *
+ * @return -ENOEXEC if no execution (task list empty), otherwise return code of
+ *	   associated op() function
+ */
+
+int pt_track_execute_next(struct proc_tracker *pt)
+{
+	struct proc_task *t;
+
+
+	if (list_empty(&pt->tasks))
+		return -ENOEXEC;
+
+
+	t = list_entry(pt->tasks.next, struct proc_task, node);
+
+	return pt->op(pt->op_code, t);
+}
+
+
+
+
 /**
  * @brief sort the tasks by order of sequence number
  * @param pt a struct processing_tracker
@@ -107,22 +193,39 @@ void pt_track_sort_seq(struct proc_tracker *pt)
 /**
  * @brief create a processing tracker
  *
- * @param nmemb the number of elements to track
+ * @param the function executing the op of this tracker
+ * @param data optional data to pass to the the tracker
+ * @param op_code the identfier of this tracker
+ * @param tasks_crit the number of tasks after which the tracker is
+ *	  considered filled to a critical level, must be at least 1
  *
  * @return processing tracker or NULL on error
  */
 
-struct proc_tracker *pt_track_create(size_t nmemb)
+struct proc_tracker *pt_track_create(int (*op)(unsigned long op_code,
+					       struct proc_task *),
+				     unsigned long op_code, size_t n_tasks_crit)
 {
 	struct proc_tracker *pt;
 
 
+	if (!op)
+		return NULL;
+
+	if (!n_tasks_crit)
+		return NULL;
+
+
 	pt = (struct proc_tracker *) kzalloc(sizeof(struct proc_tracker));
 
 	if (!pt)
 		return NULL;
 
-	pt->n = 0;
+	pt->op = op;
+
+	pt->n_tasks_crit = n_tasks_crit;
+	
+	pt->op_code = op_code;
 
 	INIT_LIST_HEAD(&pt->tasks);
 
@@ -131,19 +234,24 @@ struct proc_tracker *pt_track_create(size_t nmemb)
 
 
 /**
- * @brief destroy a processing tracker
+ * @brief destroy a processing tracker and everything it tracks
  *
  * @param pt a struct processing_tracker
- *
- * @return -1 if tracker is not empty, 0 on success
  */
 
-int pt_track_destroy(struct proc_tracker *pt)
+void pt_track_destroy(struct proc_tracker *pt)
 {
-	if (list_filled(&pt->tasks))
-		return -1;
+	struct proc_task *t;
 
-	kfree(pt);
 
-	return 0;
+	if (!pt)
+		return;
+
+	while (list_filled(&pt->tasks)) {
+		t = pt_track_get(pt);
+
+		pt_destroy(t);
+	}
+
+	kfree(pt);
 }
diff --git a/samples/Kconfig b/samples/Kconfig
index 91214c8..b69434e 100644
--- a/samples/Kconfig
+++ b/samples/Kconfig
@@ -23,4 +23,9 @@ config SAMPLE_CHUNK
 	help
 	  Build a sample demonstrating the use of the chunk memory allocator.
 
+config SAMPLE_PROC_CHAIN
+	bool "Build processing chain sample code"
+	help
+	  Build a sample demonstrating how to create a processing chain. 
+
 endif # SAMPLES
diff --git a/samples/Makefile b/samples/Makefile
index 541459d..c8b523e 100644
--- a/samples/Makefile
+++ b/samples/Makefile
@@ -3,3 +3,4 @@
 obj-$(CONFIG_SAMPLE_SYSCTL)	+= sysctl/
 obj-$(CONFIG_SAMPLE_MM)		+= mm/
 obj-$(CONFIG_SAMPLE_CHUNK)	+= chunk/
+obj-$(CONFIG_SAMPLE_PROC_CHAIN)	+= proc_chain/
diff --git a/samples/proc_chain/Makefile b/samples/proc_chain/Makefile
new file mode 100644
index 0000000..2601c9e
--- /dev/null
+++ b/samples/proc_chain/Makefile
@@ -0,0 +1,27 @@
+# kbuild trick to avoid linker error. Can be omitted if a module is built.
+obj- := dummy.o
+
+hostprogs-$(CONFIG_SAMPLE_PROC_CHAIN) := proc_chain_demo
+
+# I guess I'm too stupid to figure out the proper way to do this
+# (but maybe there is none)
+
+ifdef CROSS_COMPILE
+HOSTCC := $(CROSS_COMPILE)gcc
+HOSTLD := $(CROSS_COMPILE)ld
+endif
+
+
+HOSTCFLAGS_proc_chain_demo.o += -I$(objtree)/include
+proc_chain_demo-objs := proc_chain_demo.o
+
+ifndef CROSS_COMPILE
+EXTRAPFLAG = -m32
+else
+EXTRAPFLAG =
+endif
+
+HOSTCFLAGS_proc_chain_demo.o +=  $(EXTRAFLAG)
+HOSTLOADLIBES_proc_chain_demo += $(EXTRAFLAG) $(objtree)/lib/lib.a
+
+always := $(hostprogs-y)
diff --git a/samples/proc_chain/proc_chain_demo.c b/samples/proc_chain/proc_chain_demo.c
new file mode 100644
index 0000000..e25ddb5
--- /dev/null
+++ b/samples/proc_chain/proc_chain_demo.c
@@ -0,0 +1,374 @@
+/**
+ * This creates a number processing nodes representing Xentium kernel
+ * processing stages. Two special trackers are used for input and output.
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+
+#include <data_proc_task.h>
+#include <data_proc_tracker.h>
+#include <data_proc_net.h>
+
+
+#include <kernel/kernel.h>
+
+
+void *kzalloc(size_t size);
+void kfree(void *ptr);
+int printk(const char *fmt, ...);
+
+int pn_prepare_nodes(struct proc_net *pn);
+void pn_new_input_task(struct proc_net *pn);
+
+int op_add(unsigned long op_code, struct proc_task *pt);
+int op_sub(unsigned long op_code, struct proc_task *pt);
+int op_mul(unsigned long op_code, struct proc_task *pt);
+int op_output(unsigned long op_code, struct proc_task *pt);
+
+
+int printk(const char *fmt, ...)
+{
+	int ret;
+	va_list args;
+
+	va_start(args, fmt);
+	ret = vprintf(fmt, args);
+	va_end(args);
+
+	return ret;
+}
+
+void *kzalloc(size_t size)
+{
+	return calloc(size, 1);
+}
+
+void kfree(void *ptr)
+{
+	free(ptr);
+}
+
+
+
+
+/* special op codes */
+#define OP_INPUT   0xFFFFFFFF
+#define OP_OUTPUT  0x00000000
+/* a random base for op codes for this demo */
+#define OP_BASE   0x10000000
+
+#define TASKS	5
+#define NODES   (STEPS + 2)	/* one per steps/op code + input + output */
+
+#define NODE_IN  STEPS
+#define NODE_OUT (STEPS + 1)
+
+
+
+
+
+
+
+#if 0
+void proc_tasks_prepare(void)
+{
+
+	int i;
+	int j;
+	int go;
+	int op;
+
+	struct proc_task *pt;
+	struct proc_tracker **ptt_nodes;
+
+
+
+
+	/* allocate references to the intermediate nodes */
+	ptt_nodes = (struct proc_tracker **)
+		kzalloc(NODES * sizeof(struct proc_tracker *));
+
+	BUG_ON(!ptt_nodes);
+	
+	/* create the intermediate tracker nodes, their ID is an op code*/
+	for (i = 0; i < STEPS; i++) {
+		ptt_nodes[i] = pt_track_create(OP_BASE + i);
+		BUG_ON(!ptt_nodes[i]);
+
+	}
+
+	/* create the input and output nodes */
+	
+	ptt_nodes[NODE_IN] = pt_track_create(OP_INPUT);
+	BUG_ON(!ptt_nodes[NODE_IN]);
+	ptt_nodes[NODE_OUT] = pt_track_create(OP_OUTPUT);
+	BUG_ON(!ptt_nodes[NODE_OUT]);
+
+
+	/* create a number of individual tasks and define some processing steps,
+	 * then add the to the input stage
+	 */
+
+	for (i = 0; i < TASKS; i++) {
+		/* create a task holding at most STEPS steps */
+		pt = pt_create(NULL, 0, STEPS, 0, i);
+		BUG_ON(!pt);
+	
+		/* activate all steps with different op-codes */
+		for (j = 0; j < STEPS; j++)
+			BUG_ON(pt_add_step(pt, OP_BASE + j, NULL));
+
+		/* add the task to the input tracker */
+		pt_track_put(ptt_nodes[NODE_IN], pt);
+	}
+
+
+	/* now process the tasks, we "schedule" by looping over all the
+	 * trackers until none have pending tasks left
+	 */
+
+	go = 1;
+	while (go) {
+		go = 0;
+		for (i = 0; i < NODES; i++) {
+			while (1) { /* keep processing while the node holds
+				       tasks */
+				pt = pt_track_get(ptt_nodes[i]);
+				if (!pt)
+					break;
+
+				printk("Processing task in node %d\n", i);
+				go++; /* our indicator */
+
+				switch (i) {
+				case NODE_IN:
+					/* the input node, just moves the task
+					 * into the first processing node based
+					 * on the first processing step op code
+					 */
+					op = pt_get_pend_step_op_code(pt);
+					printk("IN: move task to node %d\n",
+					       op - OP_BASE);
+					/* in our demo, we get the correct node
+					 * by calculating it from the OP_BASE
+					 */
+					pt_track_put(ptt_nodes[op - OP_BASE], pt);
+					break;
+
+				case NODE_OUT: /* output node */
+					printk("OUT: destroy task\n");
+					pt_destroy(pt);
+					break;
+
+				default: /* processing node */
+					op = pt_get_pend_step_op_code(pt);
+					printk("PROC: simulating processing "
+					       "operation %d\n", op - OP_BASE);
+					/* this step is now complete */
+					pt_next_pend_step_done(pt);
+				
+					op = pt_get_pend_step_op_code(pt);
+					if (op) {
+						printk("PROC: move task to node"
+						       " %d\n",
+						       op - OP_BASE);
+						pt_track_put(ptt_nodes[op - OP_BASE], pt);
+					} else {
+						printk("PROC: last step, moving"
+						       " to output node");
+						pt_track_put(ptt_nodes[NODE_OUT], pt);
+					}
+
+					break;
+				}
+			}
+
+		}
+		printk("Scheduling cycle complete\n");
+	}
+		
+	printk("Processing complete\n");
+
+
+
+
+#if 0
+	pt_dump_steps_todo(pt);
+	pt_dump_steps_done(pt);
+
+
+	pt_next_pend_step_done(pt);
+	pt_next_pend_step_done(pt);
+	pt_dump_steps_todo(pt);
+	pt_dump_steps_done(pt);
+
+
+	pt_next_pend_step_done(pt);
+	pt_dump_steps_todo(pt);
+	pt_dump_steps_done(pt);
+
+	pt_rewind_steps_done(pt);
+	
+	pt_dump_steps_todo(pt);
+#endif
+
+
+
+	for (i = 0; i < NODES; i++)
+		pt_track_destroy(ptt_nodes[i]);
+	
+	kfree(ptt_nodes);
+
+}
+
+#endif
+
+#define CRIT_LEVEL	10
+#define CRIT_IN		CRIT_LEVEL
+#define CRIT_OUT	CRIT_LEVEL
+
+#define OP_ADD		0x1234
+#define OP_SUB		0x1235
+#define OP_MUL		0x1236
+
+#define STEPS	3
+
+
+
+int op_output(unsigned long op_code, struct proc_task *pt)
+{
+	printk("OUT: op code %d\n", op_code);
+	
+	return 0;
+}
+
+
+int op_add(unsigned long op_code, struct proc_task *pt)
+{
+	printk("ADD: op code %d\n", op_code);
+	
+	return 0;
+}
+
+int op_sub(unsigned long op_code, struct proc_task *pt)
+{
+	printk("SUB: op code %d\n", op_code);
+	
+	return 0;
+}
+
+int op_mul(unsigned long op_code, struct proc_task *pt)
+{
+	printk("MUL: op code %d\n", op_code);
+
+	return 0;
+}
+
+
+int pn_prepare_nodes(struct proc_net *pn)
+{
+	struct proc_tracker *pt;
+
+		
+	/* create and add processing node trackers for the each operation */
+
+	pt = pt_track_create(op_add, OP_ADD, CRIT_LEVEL);
+	BUG_ON(!pt);
+	BUG_ON(pn_add_node(pn, pt));
+
+	pt = pt_track_create(op_sub, OP_SUB, CRIT_LEVEL);
+	BUG_ON(!pt);
+	BUG_ON(pn_add_node(pn, pt));
+
+	pt = pt_track_create(op_mul, OP_MUL, CRIT_LEVEL);
+	BUG_ON(!pt);
+
+	BUG_ON(pn_add_node(pn, pt));
+
+	BUG_ON(pn_create_output_node(pn, op_output));
+
+	return 0;
+}
+
+
+
+void pn_new_input_task(struct proc_net *pn)
+{
+	struct proc_task *t;
+
+	static int seq;
+
+
+	t = pt_create(NULL, 0, STEPS, 0, seq++);
+
+	BUG_ON(!t);
+
+	BUG_ON(pt_add_step(t, OP_ADD, NULL));
+	BUG_ON(pt_add_step(t, OP_SUB, NULL));
+	BUG_ON(pt_add_step(t, OP_MUL, NULL));
+
+
+	pn_input_task(pn, t);
+}
+
+void pn_new_input_task2(struct proc_net *pn)
+{
+	struct proc_task *t;
+
+	static int seq;
+
+
+	t = pt_create(NULL, 0, STEPS, 0, seq++);
+
+	BUG_ON(!t);
+
+	BUG_ON(pt_add_step(t, OP_MUL, NULL));
+	BUG_ON(pt_add_step(t, OP_SUB, NULL));
+	BUG_ON(pt_add_step(t, OP_ADD, NULL));
+
+
+	pn_input_task(pn, t);
+}
+
+
+
+
+
+int main(int argc, char **argv)
+{
+
+	struct proc_net *pn;
+
+
+	pn = pn_create(CRIT_IN, CRIT_OUT);
+
+	BUG_ON(!pn);
+
+	pn_prepare_nodes(pn);
+
+
+	pn_new_input_task(pn);
+	pn_new_input_task2(pn);
+
+
+	pn_process_inputs(pn);	
+	
+	pn_process_next(pn);
+	pn_process_next(pn);
+	pn_process_next(pn);
+	pn_process_next(pn);
+	pn_process_next(pn);
+	pn_process_next(pn);
+
+
+
+	return 0;
+}
+
-- 
GitLab