MINOR: activity: declare a new structure to collect per-function activity

The new sched_activity structure will be used to collect task-level
activity based on the target function. The principle is to declare a
large enough array to make collisions rare (256 entries), and hash
the function pointer using a reduced XXH to decide where to store the
stats. On first computation an entry is definitely assigned to the
array and it's done atomically. A special entry (0) is used to store
collisions ("others"). The goal is to make it easy and inexpensive for
the scheduler code to use these to store #calls, cpu_time and lat_time
for each task.
diff --git a/include/haproxy/activity-t.h b/include/haproxy/activity-t.h
index 072b9ca..328bde8 100644
--- a/include/haproxy/activity-t.h
+++ b/include/haproxy/activity-t.h
@@ -74,6 +74,19 @@
 	char __end[0] __attribute__((aligned(64))); // align size to 64.
 };
 
+
+/* global profiling stats from the scheduler: each entry corresponds to a
+ * task or tasklet ->process function pointer, with a number of calls and
+ * a total time. Each entry is unique, except entry 0 which is for colliding
+ * hashes (i.e. others). All of these must be accessed atomically.
+ */
+struct sched_activity {
+	const void *func;
+	uint64_t calls;
+	uint64_t cpu_time;
+	uint64_t lat_time;
+};
+
 #endif /* _HAPROXY_ACTIVITY_T_H */
 
 /*
diff --git a/include/haproxy/activity.h b/include/haproxy/activity.h
index 1a73619..42569f2 100644
--- a/include/haproxy/activity.h
+++ b/include/haproxy/activity.h
@@ -22,6 +22,7 @@
 #ifndef _HAPROXY_ACTIVITY_H
 #define _HAPROXY_ACTIVITY_H
 
+#include <import/xxhash.h>
 #include <haproxy/activity-t.h>
 #include <haproxy/api.h>
 #include <haproxy/freq_ctr.h>
@@ -30,6 +31,7 @@
 extern unsigned int profiling;
 extern unsigned long task_profiling_mask;
 extern struct activity activity[MAX_THREADS];
+extern struct sched_activity sched_activity[256];
 
 void report_stolen_time(uint64_t stolen);
 
@@ -88,6 +90,31 @@
 	}
 }
 
+/* Computes the index of function pointer <func> for use with sched_activity[]
+ * or any other similar array passed in <array>, and returns a pointer to the
+ * entry after having atomically assigned it to this function pointer. Note
+ * that in case of collision, the first entry is returned instead ("other").
+ */
+static inline struct sched_activity *sched_activity_entry(struct sched_activity *array, const void *func)
+{
+	uint64_t hash = XXH64_avalanche(XXH64_mergeRound((size_t)func, (size_t)func));
+	struct sched_activity *ret;
+	const void *old = NULL;
+
+	hash ^= (hash >> 32);
+	hash ^= (hash >> 16);
+	hash ^= (hash >> 8);
+	hash &= 0xff;
+	ret = &array[hash];
+
+	if (likely(ret->func == func))
+		return ret;
+
+	if (HA_ATOMIC_CAS(&ret->func, &old, func))
+		return ret;
+
+	return array;
+}
 
 #endif /* _HAPROXY_ACTIVITY_H */
 
diff --git a/src/activity.c b/src/activity.c
index 79aad3e..0d64a36 100644
--- a/src/activity.c
+++ b/src/activity.c
@@ -27,6 +27,8 @@
 /* One struct per thread containing all collected measurements */
 struct activity activity[MAX_THREADS] __attribute__((aligned(64))) = { };
 
+/* One struct per function pointer hash entry (256 values, 0=collision) */
+struct sched_activity sched_activity[256] __attribute__((aligned(64))) = { };
 
 /* Updates the current thread's statistics about stolen CPU time. The unit for
  * <stolen> is half-milliseconds.