From patchwork Mon Aug 6 13:58:30 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mauricio Vasquez X-Patchwork-Id: 953892 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=polito.it Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 41kg1M1nDbz9s3x for ; Tue, 7 Aug 2018 00:25:58 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732006AbeHFQfS (ORCPT ); Mon, 6 Aug 2018 12:35:18 -0400 Received: from fm2nodo5.polito.it ([130.192.180.19]:59203 "EHLO fm2nodo5.polito.it" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727451AbeHFQfR (ORCPT ); Mon, 6 Aug 2018 12:35:17 -0400 Received: from polito.it (frontmail2.polito.it [130.192.180.42]) by fm2nodo5.polito.it with ESMTP id w76DwVZS023203-w76DwVZU023203 (version=TLSv1.0 cipher=ECDHE-RSA-AES256-SHA bits=256 verify=CAFAIL); Mon, 6 Aug 2018 15:58:31 +0200 X-ExtScanner: Niversoft's FindAttachments (free) Received: from [130.192.225.196] (account d040768@polito.it HELO [127.0.1.1]) by polito.it (CommuniGate Pro SMTP 6.2.5) with ESMTPSA id 78232792; Mon, 06 Aug 2018 15:58:31 +0200 Subject: [PATCH bpf-next 1/3] bpf: add bpf queue map From: Mauricio Vasquez B To: Alexei Starovoitov , Daniel Borkmann Cc: netdev@vger.kernel.org Date: Mon, 06 Aug 2018 15:58:30 +0200 Message-ID: <153356390770.6981.4228793745105954649.stgit@kernel> In-Reply-To: <153356387977.6981.12236150594041620482.stgit@kernel> References: <153356387977.6981.12236150594041620482.stgit@kernel> User-Agent: StGit/unknown-version MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Bpf queue implements a LIFO/FIFO data containers for ebpf programs. It allows to push an element to the queue by using the update operation and to pop an element from the queue by using the lookup operation. A use case for this is to keep track of a pool of elements, like network ports in a SNAT. Signed-off-by: Mauricio Vasquez B --- include/linux/bpf_types.h | 1 include/uapi/linux/bpf.h | 5 + kernel/bpf/Makefile | 2 kernel/bpf/queuemap.c | 287 +++++++++++++++++++++++++++++++++++++++++++++ kernel/bpf/syscall.c | 61 +++++++--- kernel/bpf/verifier.c | 16 ++- 6 files changed, 353 insertions(+), 19 deletions(-) create mode 100644 kernel/bpf/queuemap.c diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h index c5700c2d5549..6c7a62f3fe43 100644 --- a/include/linux/bpf_types.h +++ b/include/linux/bpf_types.h @@ -58,3 +58,4 @@ BPF_MAP_TYPE(BPF_MAP_TYPE_CPUMAP, cpu_map_ops) BPF_MAP_TYPE(BPF_MAP_TYPE_XSKMAP, xsk_map_ops) #endif #endif +BPF_MAP_TYPE(BPF_MAP_TYPE_QUEUE, queue_map_ops) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 0ebaaf7f3568..2c171c40eb45 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -120,6 +120,7 @@ enum bpf_map_type { BPF_MAP_TYPE_CPUMAP, BPF_MAP_TYPE_XSKMAP, BPF_MAP_TYPE_SOCKHASH, + BPF_MAP_TYPE_QUEUE, }; enum bpf_prog_type { @@ -255,6 +256,10 @@ enum bpf_attach_type { /* Flag for stack_map, store build_id+offset instead of pointer */ #define BPF_F_STACK_BUILD_ID (1U << 5) +/* Flags for queue_map, type of queue */ +#define BPF_F_QUEUE_FIFO (1U << 16) +#define BPF_F_QUEUE_LIFO (2U << 16) + enum bpf_stack_build_id_status { /* user space need an empty entry to identify end of a trace */ BPF_STACK_BUILD_ID_EMPTY = 0, diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile index f27f5496d6fe..30f02ef66635 100644 --- a/kernel/bpf/Makefile +++ b/kernel/bpf/Makefile @@ -2,7 +2,7 @@ obj-y := core.o obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o -obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o +obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o queuemap.o obj-$(CONFIG_BPF_SYSCALL) += disasm.o obj-$(CONFIG_BPF_SYSCALL) += btf.o ifeq ($(CONFIG_NET),y) diff --git a/kernel/bpf/queuemap.c b/kernel/bpf/queuemap.c new file mode 100644 index 000000000000..ab30af43b4cc --- /dev/null +++ b/kernel/bpf/queuemap.c @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * queuemap.c: BPF queue map + * + * Copyright (c) 2018 Politecnico di Torino + */ +#include +#include +#include +#include "percpu_freelist.h" + +#define QUEUE_CREATE_FLAG_MASK \ + (BPF_F_NO_PREALLOC | BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY | \ + BPF_F_QUEUE_FIFO | BPF_F_QUEUE_LIFO) + +enum queue_type { + QUEUE_FIFO = (BPF_F_QUEUE_FIFO >> 16), + QUEUE_LIFO = (BPF_F_QUEUE_LIFO >> 16), +}; + +struct bpf_queue { + struct bpf_map map; + struct list_head head; + struct pcpu_freelist freelist; + void *nodes; + enum queue_type type; + raw_spinlock_t lock; + atomic_t count; + u32 node_size; +}; + +struct queue_node { + struct pcpu_freelist_node fnode; + struct bpf_queue *queue; + struct list_head list; + struct rcu_head rcu; + char element[0] __aligned(8); +}; + +static bool queue_map_is_prealloc(struct bpf_queue *queue) +{ + return !(queue->map.map_flags & BPF_F_NO_PREALLOC); +} + +/* Called from syscall */ +static int queue_map_alloc_check(union bpf_attr *attr) +{ + /* check sanity of attributes */ + if (attr->max_entries == 0 || attr->key_size != 0 || + attr->value_size == 0 || attr->map_flags & ~QUEUE_CREATE_FLAG_MASK) + return -EINVAL; + + if ((attr->map_flags >> 16) != QUEUE_FIFO && + (attr->map_flags >> 16) != QUEUE_LIFO) { + return -EINVAL; + } + + if (attr->value_size > KMALLOC_MAX_SIZE) + /* if value_size is bigger, the user space won't be able to + * access the elements. + */ + return -E2BIG; + + return 0; +} + +static int prealloc_init(struct bpf_queue *queue) +{ + u32 node_size = sizeof(struct queue_node) + + round_up(queue->map.value_size, 8); + u32 num_entries = queue->map.max_entries; + int err; + + queue->nodes = bpf_map_area_alloc(node_size * num_entries, + queue->map.numa_node); + if (!queue->nodes) + return -ENOMEM; + + err = pcpu_freelist_init(&queue->freelist); + if (err) + goto free_nodes; + + pcpu_freelist_populate(&queue->freelist, + queue->nodes + + offsetof(struct queue_node, fnode), + node_size, num_entries); + + return 0; + +free_nodes: + bpf_map_area_free(queue->nodes); + return err; +} + +static void prealloc_destroy(struct bpf_queue *queue) +{ + bpf_map_area_free(queue->nodes); + pcpu_freelist_destroy(&queue->freelist); +} + +static struct bpf_map *queue_map_alloc(union bpf_attr *attr) +{ + struct bpf_queue *queue; + u64 cost = sizeof(*queue); + int ret; + + queue = kzalloc(sizeof(*queue), GFP_USER); + if (!queue) + return ERR_PTR(-ENOMEM); + + bpf_map_init_from_attr(&queue->map, attr); + + queue->node_size = sizeof(struct queue_node) + + round_up(attr->value_size, 8); + cost += (u64) attr->max_entries * queue->node_size; + if (cost >= U32_MAX - PAGE_SIZE) { + ret = -E2BIG; + goto free_queue; + } + + queue->map.pages = round_up(cost, PAGE_SIZE) >> PAGE_SHIFT; + + ret = bpf_map_precharge_memlock(queue->map.pages); + if (ret) + goto free_queue; + + INIT_LIST_HEAD(&queue->head); + + raw_spin_lock_init(&queue->lock); + + queue->type = attr->map_flags >> 16; + + if (queue_map_is_prealloc(queue)) + ret = prealloc_init(queue); + if (ret) + goto free_queue; + + return &queue->map; + +free_queue: + kfree(queue); + return ERR_PTR(ret); +} + +/* Called when map->refcnt goes to zero, either from workqueue or from syscall */ +static void queue_map_free(struct bpf_map *map) +{ + struct bpf_queue *queue = container_of(map, struct bpf_queue, map); + struct queue_node *l; + + /* at this point bpf_prog->aux->refcnt == 0 and this map->refcnt == 0, + * so the programs (can be more than one that used this map) were + * disconnected from events. Wait for outstanding critical sections in + * these programs to complete + */ + synchronize_rcu(); + + /* some of queue_elem_free_rcu() callbacks for elements of this map may + * not have executed. Wait for them. + */ + rcu_barrier(); + if (!queue_map_is_prealloc(queue)) + list_for_each_entry_rcu(l, &queue->head, list) { + list_del_rcu(&l->list); + kfree(l); + } + else + prealloc_destroy(queue); + kfree(queue); +} + +static void queue_elem_free_rcu(struct rcu_head *head) +{ + struct queue_node *l = container_of(head, struct queue_node, rcu); + struct bpf_queue *queue = l->queue; + + /* must increment bpf_prog_active to avoid kprobe+bpf triggering while + * we're calling kfree, otherwise deadlock is possible if kprobes + * are placed somewhere inside of slub + */ + preempt_disable(); + __this_cpu_inc(bpf_prog_active); + if (queue_map_is_prealloc(queue)) + pcpu_freelist_push(&queue->freelist, &l->fnode); + else + kfree(l); + __this_cpu_dec(bpf_prog_active); + preempt_enable(); +} + +/* Called from syscall or from eBPF program */ +static void *queue_map_lookup_elem(struct bpf_map *map, void *key) +{ + struct bpf_queue *queue = container_of(map, struct bpf_queue, map); + unsigned long flags; + struct queue_node *node; + + raw_spin_lock_irqsave(&queue->lock, flags); + + node = list_first_or_null_rcu(&queue->head, struct queue_node, list); + if (!node) { + raw_spin_unlock_irqrestore(&queue->lock, flags); + return NULL; + } + + if (!queue_map_is_prealloc(queue)) + atomic_dec(&queue->count); + + list_del_rcu(&node->list); + call_rcu(&node->rcu, queue_elem_free_rcu); + + raw_spin_unlock_irqrestore(&queue->lock, flags); + + return &node->element; +} + +/* Called from syscall or from eBPF program */ +static int queue_map_update_elem(struct bpf_map *map, void *key, void *value, + u64 map_flags) +{ + struct bpf_queue *queue = container_of(map, struct bpf_queue, map); + unsigned long flags; + struct queue_node *new; + + if (!queue_map_is_prealloc(queue)) { + if (atomic_inc_return(&queue->count) > queue->map.max_entries) { + atomic_dec(&queue->count); + return -E2BIG; + } + + new = kmalloc_node(queue->node_size, GFP_ATOMIC | __GFP_NOWARN, + queue->map.numa_node); + if (!new) { + atomic_dec(&queue->count); + return -ENOMEM; + } + } else { + struct pcpu_freelist_node *l; + + l = pcpu_freelist_pop(&queue->freelist); + if (!l) + return -E2BIG; + new = container_of(l, struct queue_node, fnode); + } + + memcpy(new->element, value, queue->map.value_size); + new->queue = queue; + + raw_spin_lock_irqsave(&queue->lock, flags); + switch (queue->type) { + case QUEUE_FIFO: + list_add_tail_rcu(&new->list, &queue->head); + break; + + case QUEUE_LIFO: + list_add_rcu(&new->list, &queue->head); + break; + } + + raw_spin_unlock_irqrestore(&queue->lock, flags); + + return 0; +} + +/* Called from syscall or from eBPF program */ +static int queue_map_delete_elem(struct bpf_map *map, void *key) +{ + return -EINVAL; +} + +/* Called from syscall */ +static int queue_map_get_next_key(struct bpf_map *map, void *key, + void *next_key) +{ + return -EINVAL; +} + +const struct bpf_map_ops queue_map_ops = { + .map_alloc_check = queue_map_alloc_check, + .map_alloc = queue_map_alloc, + .map_free = queue_map_free, + .map_lookup_elem = queue_map_lookup_elem, + .map_update_elem = queue_map_update_elem, + .map_delete_elem = queue_map_delete_elem, + .map_get_next_key = queue_map_get_next_key, +}; + diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index a31a1ba0f8ea..7e9a11d69eef 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -622,11 +622,19 @@ static int map_lookup_elem(union bpf_attr *attr) err = -EPERM; goto err_put; } + if (map->map_type != BPF_MAP_TYPE_QUEUE) { + key = memdup_user(ukey, map->key_size); + if (IS_ERR(key)) { + err = PTR_ERR(key); + goto err_put; + } + } else { + if (ukey) { + err = -EINVAL; + goto err_put; + } - key = memdup_user(ukey, map->key_size); - if (IS_ERR(key)) { - err = PTR_ERR(key); - goto err_put; + key = NULL; } if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH || @@ -709,10 +717,19 @@ static int map_update_elem(union bpf_attr *attr) goto err_put; } - key = memdup_user(ukey, map->key_size); - if (IS_ERR(key)) { - err = PTR_ERR(key); - goto err_put; + if (map->map_type != BPF_MAP_TYPE_QUEUE) { + key = memdup_user(ukey, map->key_size); + if (IS_ERR(key)) { + err = PTR_ERR(key); + goto err_put; + } + } else { + if (ukey) { + err = -EINVAL; + goto err_put; + } + + key = NULL; } if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH || @@ -803,10 +820,19 @@ static int map_delete_elem(union bpf_attr *attr) goto err_put; } - key = memdup_user(ukey, map->key_size); - if (IS_ERR(key)) { - err = PTR_ERR(key); - goto err_put; + if (map->map_type != BPF_MAP_TYPE_QUEUE) { + key = memdup_user(ukey, map->key_size); + if (IS_ERR(key)) { + err = PTR_ERR(key); + goto err_put; + } + } else { + if (ukey) { + err = -EINVAL; + goto err_put; + } + + key = NULL; } if (bpf_map_is_dev_bound(map)) { @@ -855,9 +881,14 @@ static int map_get_next_key(union bpf_attr *attr) } if (ukey) { - key = memdup_user(ukey, map->key_size); - if (IS_ERR(key)) { - err = PTR_ERR(key); + if (map->map_type != BPF_MAP_TYPE_QUEUE) { + key = memdup_user(ukey, map->key_size); + if (IS_ERR(key)) { + err = PTR_ERR(key); + goto err_put; + } + } else { + err = -EINVAL; goto err_put; } } else { diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 25e47c195874..83099a9a21d9 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1976,8 +1976,12 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, return -EACCES; } - if (arg_type == ARG_PTR_TO_MAP_KEY || - arg_type == ARG_PTR_TO_MAP_VALUE) { + if (arg_type == ARG_PTR_TO_MAP_KEY) { + expected_type = PTR_TO_STACK; + if (!type_is_pkt_pointer(type) && type != PTR_TO_MAP_VALUE && + type != expected_type && type != SCALAR_VALUE) + goto err_type; + } else if (arg_type == ARG_PTR_TO_MAP_VALUE) { expected_type = PTR_TO_STACK; if (!type_is_pkt_pointer(type) && type != PTR_TO_MAP_VALUE && type != expected_type) @@ -2021,6 +2025,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, /* bpf_map_xxx(map_ptr) call: remember that map_ptr */ meta->map_ptr = reg->map_ptr; } else if (arg_type == ARG_PTR_TO_MAP_KEY) { + bool zero_size_allowed = false; /* bpf_map_xxx(..., map_ptr, ..., key) call: * check that [key, key + map->key_size) are within * stack limits and initialized @@ -2034,8 +2039,13 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, verbose(env, "invalid map_ptr to access map->key\n"); return -EACCES; } + + if (meta->map_ptr->map_type == BPF_MAP_TYPE_QUEUE) + zero_size_allowed = true; + err = check_helper_mem_access(env, regno, - meta->map_ptr->key_size, false, + meta->map_ptr->key_size, + zero_size_allowed, NULL); } else if (arg_type == ARG_PTR_TO_MAP_VALUE) { /* bpf_map_xxx(..., map_ptr, ..., value) call: From patchwork Mon Aug 6 13:58:38 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mauricio Vasquez X-Patchwork-Id: 953893 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=polito.it Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 41kg1W4JTHz9s0R for ; Tue, 7 Aug 2018 00:26:07 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732007AbeHFQf1 (ORCPT ); Mon, 6 Aug 2018 12:35:27 -0400 Received: from fm1nodo1.polito.it ([130.192.180.16]:50193 "EHLO antispam.polito.it" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727451AbeHFQf0 (ORCPT ); Mon, 6 Aug 2018 12:35:26 -0400 Received: from polito.it (frontmail2.polito.it [130.192.180.42]) by antispam.polito.it with ESMTP id w76DwdPn003911-w76DwdPp003911 (version=TLSv1.0 cipher=ECDHE-RSA-AES256-SHA bits=256 verify=CAFAIL); Mon, 6 Aug 2018 15:58:39 +0200 X-ExtScanner: Niversoft's FindAttachments (free) Received: from [130.192.225.196] (account d040768@polito.it HELO [127.0.1.1]) by polito.it (CommuniGate Pro SMTP 6.2.5) with ESMTPSA id 78232791; Mon, 06 Aug 2018 15:58:39 +0200 Subject: [PATCH bpf-next 2/3] selftests/bpf: add test cases for BPF_MAP_TYPE_QUEUE From: Mauricio Vasquez B To: Alexei Starovoitov , Daniel Borkmann Cc: netdev@vger.kernel.org Date: Mon, 06 Aug 2018 15:58:38 +0200 Message-ID: <153356391611.6981.14864460244240605372.stgit@kernel> In-Reply-To: <153356387977.6981.12236150594041620482.stgit@kernel> References: <153356387977.6981.12236150594041620482.stgit@kernel> User-Agent: StGit/unknown-version MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Signed-off-by: Mauricio Vasquez B --- tools/include/uapi/linux/bpf.h | 5 ++ tools/testing/selftests/bpf/test_maps.c | 72 +++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 0ebaaf7f3568..2c171c40eb45 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -120,6 +120,7 @@ enum bpf_map_type { BPF_MAP_TYPE_CPUMAP, BPF_MAP_TYPE_XSKMAP, BPF_MAP_TYPE_SOCKHASH, + BPF_MAP_TYPE_QUEUE, }; enum bpf_prog_type { @@ -255,6 +256,10 @@ enum bpf_attach_type { /* Flag for stack_map, store build_id+offset instead of pointer */ #define BPF_F_STACK_BUILD_ID (1U << 5) +/* Flags for queue_map, type of queue */ +#define BPF_F_QUEUE_FIFO (1U << 16) +#define BPF_F_QUEUE_LIFO (2U << 16) + enum bpf_stack_build_id_status { /* user space need an empty entry to identify end of a trace */ BPF_STACK_BUILD_ID_EMPTY = 0, diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c index 6c253343a6f9..34567b017dbb 100644 --- a/tools/testing/selftests/bpf/test_maps.c +++ b/tools/testing/selftests/bpf/test_maps.c @@ -457,6 +457,77 @@ static void test_devmap(int task, void *data) close(fd); } +static void test_queuemap(int task, void *data) +{ + __u32 value; + int fd, i; + + /* test FIFO */ + fd = bpf_create_map(BPF_MAP_TYPE_QUEUE, 0, sizeof(value), 32, + BPF_F_QUEUE_FIFO); + if (fd < 0) { + printf("Failed to create queuemap '%s'!\n", strerror(errno)); + exit(1); + } + + /* Push 32 elements */ + for (i = 0; i < 32; i++) { + value = 1000 - i * 3; + assert(bpf_map_update_elem(fd, NULL, &value, 0) == 0); + } + + /* Check that element cannot be pushed due to max_entries limit */ + value = 1000; + assert(bpf_map_update_elem(fd, NULL, &value, 0) == -1 && + errno == E2BIG); + + /* Pop all elements */ + for (i = 0; i < 32; i++) + assert(bpf_map_lookup_elem(fd, NULL, &value) == 0 && + value == (1000 - i * 3)); + + /* Check that there are not elements left */ + assert(bpf_map_lookup_elem(fd, NULL, &value) == -1 && errno == ENOENT); + + assert(bpf_map_delete_elem(fd, NULL) == -1 && errno == EINVAL); + assert(bpf_map_get_next_key(fd, NULL, NULL) == -1 && errno == EINVAL); + + close(fd); + + /* test LIFO */ + fd = bpf_create_map(BPF_MAP_TYPE_QUEUE, 0, sizeof(value), 32, + BPF_F_QUEUE_LIFO); + if (fd < 0) { + printf("Failed to create queuemap '%s'!\n", strerror(errno)); + exit(1); + } + + /* Push 32 elements */ + for (i = 0; i < 32; i++) { + value = 1000 - i * 3; + assert(bpf_map_update_elem(fd, NULL, &value, 0) == 0); + } + + /* Check that element cannot be pushed due to max_entries limit */ + value = 1000; + assert(bpf_map_update_elem(fd, NULL, &value, 0) == -1 && + errno == E2BIG); + + /* Pop all elements */ + for (i = 31; i >= 0; i--) + assert(bpf_map_lookup_elem(fd, NULL, &value) == 0 && + value == (1000 - i * 3)); + + /* Check that there are not elements left */ + assert(bpf_map_lookup_elem(fd, NULL, &value) == -1 && + errno == ENOENT); + + assert(bpf_map_delete_elem(fd, NULL) == -1 && errno == EINVAL); + assert(bpf_map_get_next_key(fd, NULL, NULL) == -1 && errno == EINVAL); + + close(fd); +} + #include #include #include @@ -1162,6 +1233,7 @@ static void run_all_tests(void) test_arraymap_percpu_many_keys(); test_devmap(0, NULL); + test_queuemap(0, NULL); test_sockmap(0, NULL); test_map_large(); From patchwork Mon Aug 6 13:58:47 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mauricio Vasquez X-Patchwork-Id: 953894 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=polito.it Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 41kg1j1Z4Mz9s0R for ; Tue, 7 Aug 2018 00:26:17 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732079AbeHFQfg (ORCPT ); Mon, 6 Aug 2018 12:35:36 -0400 Received: from fm2nodo1.polito.it ([130.192.180.17]:37847 "EHLO fm2nodo1.polito.it" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727451AbeHFQfg (ORCPT ); Mon, 6 Aug 2018 12:35:36 -0400 Received: from polito.it (frontmail2.polito.it [130.192.180.42]) by fm2nodo1.polito.it with ESMTP id w76DwkXF009983-w76DwkXJ009983 (version=TLSv1.0 cipher=ECDHE-RSA-AES256-SHA bits=256 verify=CAFAIL); Mon, 6 Aug 2018 15:58:48 +0200 X-ExtScanner: Niversoft's FindAttachments (free) Received: from [130.192.225.196] (account d040768@polito.it HELO [127.0.1.1]) by polito.it (CommuniGate Pro SMTP 6.2.5) with ESMTPSA id 78232756; Mon, 06 Aug 2018 15:58:47 +0200 Subject: [PATCH bpf-next 3/3] bpf: add sample for BPF_MAP_TYPE_QUEUE From: Mauricio Vasquez B To: Alexei Starovoitov , Daniel Borkmann Cc: netdev@vger.kernel.org Date: Mon, 06 Aug 2018 15:58:47 +0200 Message-ID: <153356392410.6981.1290059578982921349.stgit@kernel> In-Reply-To: <153356387977.6981.12236150594041620482.stgit@kernel> References: <153356387977.6981.12236150594041620482.stgit@kernel> User-Agent: StGit/unknown-version MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org The example is made by two parts, a eBPF program that consumes elements from a FIFO queue and prints them in the screen and a user space application that inserts new elements into the queue each time this is executed. Signed-off-by: Mauricio Vasquez B --- samples/bpf/.gitignore | 1 + samples/bpf/Makefile | 3 ++ samples/bpf/test_map_in_map_user.c | 9 +----- samples/bpf/test_queuemap.sh | 37 +++++++++++++++++++++++++ samples/bpf/test_queuemap_kern.c | 51 +++++++++++++++++++++++++++++++++++ samples/bpf/test_queuemap_user.c | 53 ++++++++++++++++++++++++++++++++++++ 6 files changed, 147 insertions(+), 7 deletions(-) create mode 100755 samples/bpf/test_queuemap.sh create mode 100644 samples/bpf/test_queuemap_kern.c create mode 100644 samples/bpf/test_queuemap_user.c diff --git a/samples/bpf/.gitignore b/samples/bpf/.gitignore index 8ae4940025f8..d7e518c1b3ed 100644 --- a/samples/bpf/.gitignore +++ b/samples/bpf/.gitignore @@ -26,6 +26,7 @@ test_lru_dist test_map_in_map test_overhead test_probe_write_user +test_queuemap trace_event trace_output tracex1 diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index f88d5683d6ee..624f4f4b81db 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -53,6 +53,7 @@ hostprogs-y += xdpsock hostprogs-y += xdp_fwd hostprogs-y += task_fd_query hostprogs-y += xdp_sample_pkts +hostprogs-y += test_queuemap # Libbpf dependencies LIBBPF = $(TOOLS_PATH)/lib/bpf/libbpf.a @@ -109,6 +110,7 @@ xdpsock-objs := xdpsock_user.o xdp_fwd-objs := xdp_fwd_user.o task_fd_query-objs := bpf_load.o task_fd_query_user.o $(TRACE_HELPERS) xdp_sample_pkts-objs := xdp_sample_pkts_user.o $(TRACE_HELPERS) +test_queuemap-objs := bpf_load.o test_queuemap_user.o # Tell kbuild to always build the programs always := $(hostprogs-y) @@ -166,6 +168,7 @@ always += xdpsock_kern.o always += xdp_fwd_kern.o always += task_fd_query_kern.o always += xdp_sample_pkts_kern.o +always += test_queuemap_kern.o HOSTCFLAGS += -I$(objtree)/usr/include HOSTCFLAGS += -I$(srctree)/tools/lib/ diff --git a/samples/bpf/test_map_in_map_user.c b/samples/bpf/test_map_in_map_user.c index e308858f7bcf..28edac94234e 100644 --- a/samples/bpf/test_map_in_map_user.c +++ b/samples/bpf/test_map_in_map_user.c @@ -1,10 +1,5 @@ -/* - * Copyright (c) 2017 Facebook - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2018 Politecnico di Torino */ #include #include #include diff --git a/samples/bpf/test_queuemap.sh b/samples/bpf/test_queuemap.sh new file mode 100755 index 000000000000..ed08c1fa8c2c --- /dev/null +++ b/samples/bpf/test_queuemap.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +[[ -z $TC ]] && TC='tc' +[[ -z $IP ]] && IP='ip' + +TEST_QUEUE_USER='./test_queuemap' +TEST_QUEUE_BPF='./test_queuemap_kern.o' + +function config { + $IP netns add ns1 + $IP link add ve1 type veth peer name vens1 + $IP link set dev ve1 up + $IP link set dev ve1 mtu 1500 + $IP link set dev vens1 netns ns1 + + $IP -n ns1 link set dev lo up + $IP -n ns1 link set dev vens1 up + $IP -n ns1 addr add 10.1.1.101/24 dev vens1 + + $IP addr add 10.1.1.1/24 dev ve1 + $TC qdisc add dev ve1 clsact + $TC filter add dev ve1 ingress bpf da obj $TEST_QUEUE_BPF sec test_queue +} + +function cleanup { + set +e + [[ -z $DEBUG ]] || set +x + $IP netns delete ns1 >& /dev/null + $IP link del ve1 >& /dev/null + rm -f /sys/fs/bpf/tc/globals/queue + [[ -z $DEBUG ]] || set -x + set -e +} + +cleanup +config diff --git a/samples/bpf/test_queuemap_kern.c b/samples/bpf/test_queuemap_kern.c new file mode 100644 index 000000000000..2b496dafaffd --- /dev/null +++ b/samples/bpf/test_queuemap_kern.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2018 Politecnico di Torino */ +#define KBUILD_MODNAME "foo" +#include +#include +#include +#include +#include +#include "bpf_helpers.h" + +#define PIN_GLOBAL_NS 2 + +struct bpf_elf_map { + __u32 type; + __u32 key_size; + __u32 value_size; + __u32 max_entries; + __u32 flags; + __u32 id; + __u32 pinning; +}; + +/* map #0 */ +struct bpf_elf_map SEC("maps") queue = { + .type = BPF_MAP_TYPE_QUEUE, + .key_size = 0, + .value_size = sizeof(u32), + .flags = BPF_F_QUEUE_FIFO, + .max_entries = 1024, + .pinning = PIN_GLOBAL_NS, +}; + +SEC("test_queue") +int _test_queue(struct __sk_buff *skb) +{ + char msg[] = "element is %u\n"; + char msg_no[] = "there are not elements\n"; + + u32 *val = bpf_map_lookup_elem(&queue, NULL); + + if (!val) { + bpf_trace_printk(msg_no, sizeof(msg_no)); + return TC_ACT_OK; + } + + bpf_trace_printk(msg, sizeof(msg), *val); + return TC_ACT_OK; +} + +char _license[] SEC("license") = "GPL"; +u32 _version SEC("version") = LINUX_VERSION_CODE; diff --git a/samples/bpf/test_queuemap_user.c b/samples/bpf/test_queuemap_user.c new file mode 100644 index 000000000000..68f9a5d54596 --- /dev/null +++ b/samples/bpf/test_queuemap_user.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018 Politecnico di Torino + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bpf_load.h" + +int queue_map; + +static void test_queue_map(void) +{ + int ret; + uint32_t i; + + queue_map = bpf_obj_get("/sys/fs/bpf/tc/globals/queue"); + if (queue_map < 0) { + fprintf(stderr, "error getting map"); + return; + } + + for (i = 0; i < 256; i++) { + uint32_t v = 1000 - i*3; + + ret = bpf_map_update_elem(queue_map, NULL, &v, 0); + if (ret) + fprintf(stderr, "ret is %d\n", ret); + } +} + +int main(int argc, char **argv) +{ + struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; + char filename[256]; + + assert(!setrlimit(RLIMIT_MEMLOCK, &r)); + + snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); + + test_queue_map(); + + return 0; +}