Thread (6 messages) flat view 6 messages, 3 authors, 2017-01-14

Re: [PATCH v2 1/2] bpf: add a longest prefix match trie map implementation

From: Daniel Mack <daniel@zonque.org>
Date: 2017-01-14 10:38:03

On 01/13/2017 07:01 PM, Alexei Starovoitov wrote:
On Thu, Jan 12, 2017 at 06:29:21PM +0100, Daniel Mack wrote:
quoted
This trie implements a longest prefix match algorithm that can be used
to match IP addresses to a stored set of ranges.

Internally, data is stored in an unbalanced trie of nodes that has a
maximum height of n, where n is the prefixlen the trie was created
with.

Tries may be created with prefix lengths that are multiples of 8, in
the range from 8 to 2048. The key used for lookup and update operations
is a struct bpf_lpm_trie_key, and the value is a uint64_t.

The code carries more information about the internal implementation.

Signed-off-by: Daniel Mack <daniel@zonque.org>
Reviewed-by: David Herrmann <redacted>
---
 include/uapi/linux/bpf.h |   7 +
 kernel/bpf/Makefile      |   2 +-
 kernel/bpf/lpm_trie.c    | 499 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 507 insertions(+), 1 deletion(-)
 create mode 100644 kernel/bpf/lpm_trie.c
...

Thanks for spotting my typos! :)
quoted
+static struct lpm_trie_node *lpm_trie_node_alloc(const struct lpm_trie *trie,
+						 const void *value)
+{
+	struct lpm_trie_node *node;
+	gfp_t gfp = GFP_ATOMIC | __GFP_NOWARN;
+
+	node = kmalloc(sizeof(struct lpm_trie_node) + trie->data_size, gfp);
+	if (!node)
+		return ERR_PTR(-ENOMEM);
+
+	if (value) {
+		node->value = kmemdup(value, trie->map.value_size, gfp);
can you make value to be part of the node? similar to how hash map is done?
that will help avoid 2nd allocation, will speedup insertion and will
help converting this code to user pre-allocated elements.
I suspect the concern was that for many inner nodes that value is null ?
But in your use case the value_size will be == 0 eventually,
so by embedding it when can save memory too, since 'value' pointer will
be replaced with boolean present flag ?
So potentially less memory and less cache misses?
Yes, that's a good idea. Implemented that now.
Overall algorithm is indeed straightforward and simple which is great,
but I would still like to see some performance numbers.
I'm not sure yet how to implement such a test in a meaningful way, tbh.
Given that the lookups have to be done one by one, I expect the syscall
overhead to be quite significant.
Looks like
the best case for single 32-bit element it needs 4 xors and compares
which is fine. For mostly populate trie it's 4xors * 32 depth
which is pretty good too, but cache misses on pointer walks may
kill performance unless we're hitting the same path all the time.
I think it's all acceptable due to simplicity of the implementation
which we may improve later if it turns out to be a bottle neck for
some use cases. We just need a baseline to have realistic expectations.
Yes, the maximum height of the trie is the number of bits in the prefix,
so for n bits, the iteration would at most take n steps to finish. For
each step, an xor and compare for n/8 bytes are needed.

As you say, the implementation could be improved under the hood if
someone spots a bottleneck somewhere.

I'll post a v3 with your comments addressed for further discussion.


Thanks,
Daniel
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help