Optimise service provider handler look-up

With the increased use of chained service providers as a
way of extending a core service provider, a simple optimisation
is possible that reduces the average handler look-up time
when handling a call request.  This change adds an opcode
range check such that the handler look-up for a service
provider is only performed if the opcode falls within
the opcode range handled by the service provider.

Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: I8c1e9c699349bd8bd2603c0834fbcd005caa36b3
diff --git a/components/service/common/provider/service_provider.c b/components/service/common/provider/service_provider.c
index 19bd088..e2f21da 100644
--- a/components/service/common/provider/service_provider.c
+++ b/components/service/common/provider/service_provider.c
@@ -12,9 +12,11 @@
 							  uint32_t opcode)
 {
 	const struct service_handler *handler = NULL;
-	size_t index = 0;
 
-	if (sp->num_handlers) {
+	if ((opcode >= sp->opcode_range_lo) && (opcode <= sp->opcode_range_hi)) {
+
+		size_t index = 0;
+
 		while (index < sp->num_handlers) {
 			if (service_handler_get_opcode(&sp->handlers[index]) == opcode) {
 				handler = &sp->handlers[index];
@@ -27,6 +29,28 @@
 	return handler;
 }
 
+static void set_opcode_range(struct service_provider *sp)
+{
+	uint32_t lo = UINT32_MAX;
+	uint32_t hi = 0;
+
+	/* Determine opcode range so that this service may be skipped
+	 * without having to iterate over all handlers.  This reduces
+	 * the time to find a qualifying handler when multiple service
+	 * providers are chained.
+	 */
+	for (size_t index = 0; index < sp->num_handlers; index++) {
+
+		uint32_t opcode = service_handler_get_opcode(&sp->handlers[index]);
+
+		if (opcode < lo) lo = opcode;
+		if (opcode > hi) hi = opcode;
+	}
+
+	sp->opcode_range_lo = lo;
+	sp->opcode_range_hi = hi;
+}
+
 static rpc_status_t receive(struct rpc_interface *rpc_iface, struct call_req *req)
 {
 	rpc_status_t rpc_status;
@@ -63,6 +87,8 @@
 	sp->num_handlers = num_handlers;
 
 	sp->successor = NULL;
+
+	set_opcode_range(sp);
 }
 
 void service_provider_extend(struct service_provider *context,
diff --git a/components/service/common/provider/service_provider.h b/components/service/common/provider/service_provider.h
index a4f15e4..a0e853e 100644
--- a/components/service/common/provider/service_provider.h
+++ b/components/service/common/provider/service_provider.h
@@ -47,6 +47,8 @@
 	struct rpc_interface iface;
 	const struct service_handler *handlers;
 	size_t num_handlers;
+	uint32_t opcode_range_lo;
+	uint32_t opcode_range_hi;
 	struct rpc_interface *successor;
 };