From patchwork Mon Oct 29 21:19:33 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shannon Nelson X-Patchwork-Id: 990557 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=pass (p=none dis=none) header.from=oracle.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=oracle.com header.i=@oracle.com header.b="McRNd6Ag"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 42kSDn0f5pz9s7W for ; Tue, 30 Oct 2018 08:20:25 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727524AbeJ3GKu (ORCPT ); Tue, 30 Oct 2018 02:10:50 -0400 Received: from userp2120.oracle.com ([156.151.31.85]:60942 "EHLO userp2120.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726485AbeJ3GKt (ORCPT ); Tue, 30 Oct 2018 02:10:49 -0400 Received: from pps.filterd (userp2120.oracle.com [127.0.0.1]) by userp2120.oracle.com (8.16.0.22/8.16.0.22) with SMTP id w9TLJ5l2056765; Mon, 29 Oct 2018 21:19:44 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id; s=corp-2018-07-02; bh=5QZsKiIvwo/gEZfnFBVdXhkwWpvrZK41ZChl+voIOhU=; b=McRNd6AgnNZodL9mn6L/lViytLz+XM6Togqu7RuiZVmoI3XCMyEJz8m8nswm4OhNIVFI UVlWSf/pUcnr/tkzmKAB/UhzrpgU4jkwJrhbWTG+VaWmulqyPDYj94Di8foB4MHMQiT7 AfGqx9akBYcSYJQEG2XUAW0RCUnvJhybXxjvIVkOCGKVETYEsYhnTJyeHh8F/UyB40QH Fqse19blC3/cnM6RxvuFFhW931kPSeiYTxaZefCA+ANAR+nt+FarN64a57LUH5fR8sga unwAcSo/tGiA0EnISOMiDexiQM4AXL+PV9CtVjdAF5alcFnMvt2K3iC5a4LqOZWyUDzl ig== Received: from userv0022.oracle.com (userv0022.oracle.com [156.151.31.74]) by userp2120.oracle.com with ESMTP id 2ncgnqrbmd-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Mon, 29 Oct 2018 21:19:44 +0000 Received: from aserv0121.oracle.com (aserv0121.oracle.com [141.146.126.235]) by userv0022.oracle.com (8.14.4/8.14.4) with ESMTP id w9TLJhKu005899 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Mon, 29 Oct 2018 21:19:43 GMT Received: from abhmp0012.oracle.com (abhmp0012.oracle.com [141.146.116.18]) by aserv0121.oracle.com (8.14.4/8.13.8) with ESMTP id w9TLJh56005706; Mon, 29 Oct 2018 21:19:43 GMT Received: from slnelson-mint18.us.oracle.com (/10.159.138.154) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Mon, 29 Oct 2018 14:19:43 -0700 From: Shannon Nelson To: ast@kernel.org, daniel@iogearbox.net Cc: netdev@vger.kernel.org, eric.dumazet@gmail.com, john.fastabend@gmail.com, silviu.smarandache@oracle.com, shannon.lee.nelson@gmail.com Subject: [PATCH bpf-next] xdp: sample code for redirecting vlan packets to specific cpus Date: Mon, 29 Oct 2018 14:19:33 -0700 Message-Id: <1540847973-3540-1-git-send-email-shannon.nelson@oracle.com> X-Mailer: git-send-email 2.7.4 X-Proofpoint-Virus-Version: vendor=nai engine=5900 definitions=9061 signatures=668683 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=0 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1807170000 definitions=main-1810290191 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org This is an example of using XDP to redirect the processing of particular vlan packets to specific CPUs. This is in response to comments received on a kernel patch put forth previously to do something similar using RPS. https://www.spinics.net/lists/netdev/msg528210.html [PATCH net-next] net: enable RPS on vlan devices This XDP application watches for inbound vlan-tagged packets and redirects those packets to be processed on a specific CPU as configured in a BPF map. The BPF map can be modified by this user program, which can also load and unload the kernel XDP code. One example use is for supporting VMs where we can't control the OS being used: we'd like to separate the VM CPU processing from the host's CPUs as a way to help mitigate L1TF related issues. When running the VM's traffic on a vlan we can stick the host's Rx processing on one set of CPUs separate from the VM's CPUs. This example currently uses a vlan key and cpu value in the BPF map, so only can do one CPU per vlan. This could easily be modified to use a bitpattern of CPUs rather than a CPU id to allow multiple CPUs per vlan. Signed-off-by: Shannon Nelson Acked-by: John Fastabend --- samples/bpf/Makefile | 3 + samples/bpf/xdp_vlan_redirect_kern.c | 118 +++++++++++ samples/bpf/xdp_vlan_redirect_user.c | 393 +++++++++++++++++++++++++++++++++++ 3 files changed, 514 insertions(+) create mode 100644 samples/bpf/xdp_vlan_redirect_kern.c create mode 100644 samples/bpf/xdp_vlan_redirect_user.c diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 1303af1..a9746ce 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -44,6 +44,7 @@ hostprogs-y += load_sock_ops hostprogs-y += xdp_redirect hostprogs-y += xdp_redirect_map hostprogs-y += xdp_redirect_cpu +hostprogs-y += xdp_vlan_redirect hostprogs-y += xdp_monitor hostprogs-y += xdp_rxq_info hostprogs-y += syscall_tp @@ -99,6 +100,7 @@ per_socket_stats_example-objs := cookie_uid_helper_example.o xdp_redirect-objs := bpf_load.o xdp_redirect_user.o xdp_redirect_map-objs := bpf_load.o xdp_redirect_map_user.o xdp_redirect_cpu-objs := bpf_load.o xdp_redirect_cpu_user.o +xdp_vlan_redirect-objs := bpf_load.o xdp_vlan_redirect_user.o xdp_monitor-objs := bpf_load.o xdp_monitor_user.o xdp_rxq_info-objs := xdp_rxq_info_user.o syscall_tp-objs := bpf_load.o syscall_tp_user.o @@ -154,6 +156,7 @@ always += tcp_basertt_kern.o always += xdp_redirect_kern.o always += xdp_redirect_map_kern.o always += xdp_redirect_cpu_kern.o +always += xdp_vlan_redirect_kern.o always += xdp_monitor_kern.o always += xdp_rxq_info_kern.o always += xdp2skb_meta_kern.o diff --git a/samples/bpf/xdp_vlan_redirect_kern.c b/samples/bpf/xdp_vlan_redirect_kern.c new file mode 100644 index 0000000..3d561a0 --- /dev/null +++ b/samples/bpf/xdp_vlan_redirect_kern.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0 +/* XDP redirect vlans to CPUs - kernel code + * + * Copyright(c) 2018 Oracle Corp. + */ + +#include +#include + +#include +#include "bpf_helpers.h" + +char _license[] SEC("license") = "GPL"; + +#define MAX_CPUS 64 /* WARNING - sync with _user.c */ +#define UNDEF_CPU 0xff000000 /* WARNING - sync with _user.c */ + +/* The vlan index finds cpu(s) for processing a packet */ +struct bpf_map_def SEC("maps") vlan_redirect_map = { + .type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(u32), /* vlan tag */ + .value_size = sizeof(u64), /* cpu bitpattern */ + .max_entries = 4096, +}; + +/* List of cpus that can participate in the vlan redirect */ +struct bpf_map_def SEC("maps") vlan_redirect_cpus_map = { + .type = BPF_MAP_TYPE_CPUMAP, + .key_size = sizeof(u32), /* cpu id */ + .value_size = sizeof(u32), /* queue size */ + .max_entries = MAX_CPUS, +}; + +/* Counters for debug */ +#define VRC_CALLS 0 /* number of calls to this program */ +#define VRC_VLANS 1 /* number of vlan packets seen */ +#define VRC_HITS 2 /* number of redirects attempted */ +#define CPU_COUNT 3 /* number of CPUs found */ +struct bpf_map_def SEC("maps") vlan_redirect_counters_map = { + .type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(u32), /* vlan tag */ + .value_size = sizeof(u64), /* cpu bitpattern */ + .max_entries = 4, +}; + +SEC("xdp_vlan_redirect") +int xdp_vlan_redirect(struct xdp_md *ctx) +{ + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + struct ethhdr *eth = data; + uint64_t h_proto, nh_off; + struct vlan_hdr *vhdr; + uint64_t *cpu_value; + uint32_t num_cpus; + uint32_t minlen; + uint64_t *vp; + uint64_t v, k; + uint64_t vlan; + + /* count packets processed */ + k = VRC_CALLS; + vp = bpf_map_lookup_elem(&vlan_redirect_counters_map, &k); + if (vp) { + v = (*vp) + 1; + bpf_map_update_elem(&vlan_redirect_counters_map, &k, &v, 0); + } + + /* is there enough packet? */ + minlen = sizeof(struct ethhdr) + sizeof(struct vlan_hdr); + if (data + minlen > data_end) + return XDP_PASS; + + /* is there a vlan tag? */ + h_proto = be16_to_cpu(eth->h_proto); + if (h_proto != ETH_P_8021Q && h_proto != ETH_P_8021AD) + return XDP_PASS; + + vhdr = data + sizeof(struct ethhdr); + vlan = be16_to_cpu(vhdr->h_vlan_TCI) & VLAN_VID_MASK; + if (!vlan) + return XDP_PASS; + + /* count vlan packets seen */ + k = VRC_VLANS; + vp = bpf_map_lookup_elem(&vlan_redirect_counters_map, &k); + if (vp) { + v = (*vp) + 1; + bpf_map_update_elem(&vlan_redirect_counters_map, &k, &v, 0); + } + + /* what cpu(s) for this vlanid? */ + cpu_value = bpf_map_lookup_elem(&vlan_redirect_map, &vlan); + if (!cpu_value || *cpu_value == UNDEF_CPU) + return XDP_PASS; + + /* TODO: cpu_value could be a bit-pattern of possible cpus + * from which to choose, and here we would do a hash + * to select the target cpu. + */ + + /* TODO: scale the cpu found to be sure it is less than the + * actual number of CPUs running. + */ + + /* set up the redirect */ + bpf_redirect_map(&vlan_redirect_cpus_map, *cpu_value, 0); + + /* count redirects attempted */ + k = VRC_HITS; + vp = bpf_map_lookup_elem(&vlan_redirect_counters_map, &k); + if (vp) { + v = (*vp) + 1; + bpf_map_update_elem(&vlan_redirect_counters_map, &k, &v, 0); + } + + return XDP_REDIRECT; +} diff --git a/samples/bpf/xdp_vlan_redirect_user.c b/samples/bpf/xdp_vlan_redirect_user.c new file mode 100644 index 0000000..c4079f4 --- /dev/null +++ b/samples/bpf/xdp_vlan_redirect_user.c @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: GPL-2.0 +/* XDP redirect vlans to CPUs - user code + * + * Copyright(c) 2018 Oracle Corp. + * + * + * This _user.c code, along with the accompanying _kern.c, is + * intended as an example of using XDP to redirect processing of + * particular vlan packets to specific CPUs. This is in response + * to comments received on a kernel patch put forth previously + * to do something similar using RPS. + * https://www.spinics.net/lists/netdev/msg528210.html + * [PATCH net-next] net: enable RPS on vlan devices + * + * This XDP application watches for inbound vlan-tagged packets + * and redirects those packets to be processed on a specific CPU + * as configured in a BPF map. The BPF map can be modified by + * this user program, which can also load and unload the kernel + * XDP code. + * + * In supporting VMs where we can't control the OS being used, + * we'd like to separate the VM CPU processing from the host's + * CPUs as a way to help mitigate the impact of the L1TF issue. + * When running the VM's traffic on a vlan we can stick the Rx + * processing on one set of CPUs separate from the VM's CPUs. + * Yes, choosing to use this may cause a bit of throughput pain + * when the packets are actually passed into the VM and have to + * move from one cache to another. + * + * This example currently uses a vlan key and cpu value in the + * BPF map, so only can do one CPU per vlan. This could easily + * be modified to use a bitpattern of CPUs rather than a CPU id + * to allow multiple CPUs per vlan. + * + * Before using, please be sure to mount the bpf pseudo-fs + * mount -t bpf bpf /sys/fs/bpf + * + * Also, be sure that the device is not stripping vlan tags + * so that the XDP program has a chance to inspect them + * ethtool -K eth0 rxvlan off + * + * To load the feature, use a command line something like this: + * xdp_vlan_redirect --dev eth0 --install + * + * Once installed, you can see the pinned files in userspace: + * # ls /sys/fs/bpf + * xdp_vlan_redirect xdp_vlan_redirect_map + * + * These commands add vlan:cpu mappings: + * xdp_vlan_redirect --dev eth0 --vlan 1 --cpu 5 + * xdp_vlan_redirect -d eth0 -v 3 -c 4 + * + * You can use the bpftool to print the contents of the vlan map: + * # bpftool map dump pinned /sys/fs/bpf/xdp_vlan_redirect_vlan_map + * key: 00 00 00 00 value: 00 00 00 ff 00 00 00 00 + * key: 01 00 00 00 value: 05 00 00 00 00 00 00 00 + * key: 02 00 00 00 value: 00 00 00 ff 00 00 00 00 + * key: 03 00 00 00 value: 04 00 00 00 00 00 00 00 + * key: 04 00 00 00 value: 00 00 00 ff 00 00 00 00 + * : + * + * Use negative numbers to remove vlans from the map: + * xdp_vlan_redirect -d eth0 -v -3 + * + * It is possible to do map editing with bpftool, but note that all + * the bytes of both the key and the value must be specified: + * # bpftool map update pinned /sys/fs/bpf/xdp_vlan_redirect_vlan_map \ + * key 3 0 0 0 value 0 7 0 0 0 0 0 0 + * + * Removing the feature is similar to install: + * xdp_vlan_redirect --dev eth0 --remove + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "bpf_load.h" +#include "bpf_util.h" + +#define MAX_CPUS 64 /* WARNING - sync with _kern.c */ +#define UNDEF_CPU 0xff000000 /* WARNING - sync with _kern.c */ + +/* counters in the counter_map */ +#define VRC_CALLS 0 /* number of calls to this program */ +#define VRC_VLANS 1 /* number of vlan packets seen */ +#define VRC_HITS 2 /* number of redirects attempted */ +#define CPU_COUNT 3 /* number of CPUs found */ + +static const struct option long_options[] = { + {"help", no_argument, NULL, 'h' }, + {"dev", required_argument, NULL, 'd' }, + {"cpu", required_argument, NULL, 'c' }, + {"vlan", required_argument, NULL, 'v' }, + {"install", no_argument, NULL, 'i' }, + {"remove", no_argument, NULL, 'r' }, + {0, 0, NULL, 0 } +}; + +static void usage(char *argv[]) +{ + int i; + + printf("%s - CPU targeting for vlan processing\n", argv[0]); + printf("\n"); + printf(" Usage: %s (options-see-below)\n", argv[0]); + printf(" Listing options:\n"); + for (i = 0; long_options[i].name != 0; i++) { + printf(" --%-12s", long_options[i].name); + if (long_options[i].flag != NULL) + printf(" flag (internal value:%d)", + *long_options[i].flag); + else + printf(" short-option: -%c", + long_options[i].val); + printf("\n"); + } + printf("\n"); +} + +int main(int argc, char **argv) +{ + struct rlimit r = {10 * 1024 * 1024, RLIM_INFINITY}; + const char *pinpath = "/sys/fs/bpf/"; + char filename[64] = { 0 }; + char pin_prog_name[sizeof(filename) + sizeof(pinpath)] = { 0 }; + char pin_vlanmap_name[sizeof(filename) + sizeof(pinpath)] = { 0 }; + char pin_countermap_name[sizeof(filename) + sizeof(pinpath)] = { 0 }; + const char *kern_suffix = "_kern.o"; + char ifname[IF_NAMESIZE] = { 0 }; + bool install = false; + bool remove = false; + int cpu = MAX_CPUS; + int longindex = 0; + int ifindex = -1; + int vfd, cfd; + int vlan = 0; + __u64 v64; + __u64 c64; + int ret; + int opt; + + if (setrlimit(RLIMIT_MEMLOCK, &r)) { + perror("setrlimit(RLIMIT_MEMLOCK)"); + return 1; + } + + /* Parse command line args */ + while ((opt = getopt_long(argc, argv, "d:c:v:ri", + long_options, &longindex)) != -1) { + switch (opt) { + case 'd': + strncpy(ifname, optarg, sizeof(ifname)-1); + ifindex = if_nametoindex(ifname); + if (ifindex == 0) { + fprintf(stderr, "ERR: device name '%s' : %s\n", + ifname, strerror(errno)); + goto error; + } + break; + case 'c': + cpu = strtoul(optarg, NULL, 0); + if (cpu >= MAX_CPUS) { + fprintf(stderr, "ERR: invalid cpu id '%d'\n", cpu); + goto error; + } + break; + case 'v': + vlan = atoi(optarg); + if (!vlan || vlan >= 4096) { + fprintf(stderr, "ERR: invalid vlan id '%d'\n", vlan); + goto error; + } + break; + case 'i': + install = true; + break; + case 'r': + remove = true; + break; + case 'h': + default: + goto error; + } + } + + /* Required options */ + if (ifindex == -1) { + fprintf(stderr, "ERR: required option --dev missing\n"); + goto error; + } + + if (install && remove) { + fprintf(stderr, "ERR: pick only one of install or remove\n"); + goto error; + } + + if ((install || remove) && (vlan || cpu != MAX_CPUS)) { + fprintf(stderr, "ERR: pick either (install or remove) or vlan and cpu\n"); + goto error; + } + + if (strlen(argv[0]) > (sizeof(filename) - sizeof(kern_suffix))) { + fprintf(stderr, "filename %s too long\n", argv[0]); + return -1; + } + + snprintf(filename, sizeof(filename), "%s%s", argv[0], kern_suffix); + snprintf(pin_prog_name, sizeof(pin_prog_name), "%s%s", pinpath, argv[0]); + snprintf(pin_vlanmap_name, sizeof(pin_vlanmap_name), "%s%s_vlan_map", pinpath, argv[0]); + snprintf(pin_countermap_name, sizeof(pin_countermap_name), "%s%s_counter_map", pinpath, argv[0]); + + if (install) { + + /* check to see if already installed */ + errno = 0; + access(pin_prog_name, R_OK); + if (errno != ENOENT) { + fprintf(stderr, "ERR: %s is already installed\n", argv[0]); + return -1; + } + + /* load the XDP program and maps with the convenient library */ + if (load_bpf_file(filename)) { + fprintf(stderr, "ERR: load_bpf_file(%s): \n%s", + filename, bpf_log_buf); + return -1; + } + if (!prog_fd[0]) { + fprintf(stderr, "ERR: load_bpf_file(%s): %d %s\n", + filename, errno, strerror(errno)); + return -1; + } + + /* pin the XDP program and maps */ + if (bpf_obj_pin(prog_fd[0], pin_prog_name) < 0) { + fprintf(stderr, "ERR: bpf_obj_pin(%s): %d %s\n", + pin_prog_name, errno, strerror(errno)); + if (errno == 2) + fprintf(stderr, " (is the BPF fs mounted on /sys/fs/bpf?)\n"); + return -1; + } + if (bpf_obj_pin(map_fd[0], pin_vlanmap_name) < 0) { + fprintf(stderr, "ERR: bpf_obj_pin(%s): %d %s\n", + pin_vlanmap_name, errno, strerror(errno)); + return -1; + } + if (bpf_obj_pin(map_fd[2], pin_countermap_name) < 0) { + fprintf(stderr, "ERR: bpf_obj_pin(%s): %d %s\n", + pin_countermap_name, errno, strerror(errno)); + return -1; + } + + /* prep the vlan map with "not used" values */ + c64 = UNDEF_CPU; + for (v64 = 0; v64 < 4096; v64++) { + if (bpf_map_update_elem(map_fd[0], &v64, &c64, 0)) { + fprintf(stderr, "ERR: preping vlan map failed on v=%llu: %d %s\n", + v64, errno, strerror(errno)); + return -1; + } + } + + /* prep the cpumap with queue sizes */ + c64 = 128+64; /* see note in xdp_redirect_cpu_user.c */ + for (v64 = 0; v64 < MAX_CPUS; v64++) { + if (bpf_map_update_elem(map_fd[1], &v64, &c64, 0)) { + if (errno == ENODEV) { + /* Save the last CPU number attempted + * into the counters map + */ + c64 = CPU_COUNT; + ret = bpf_map_update_elem(map_fd[2], &c64, &v64, 0); + break; + } + + fprintf(stderr, "ERR: preping cpu map failed on v=%llu: %d %s\n", + v64, errno, strerror(errno)); + return -1; + } + } + + /* wire the XDP program to the device */ + if (bpf_set_link_xdp_fd(ifindex, prog_fd[0], 0) < 0) { + fprintf(stderr, "ERR: bpf_set_link_xdp_fd(): %d %s\n", + errno, strerror(errno)); + return -1; + } + + return 0; + } + + if (remove) { + + /* unlink the program from the device */ + if (bpf_set_link_xdp_fd(ifindex, -1, 0) < 0) + fprintf(stderr, "ERR: bpf_set_link_xdp_fd(): %d %s\n", + errno, strerror(errno)); + + /* unlink pinned files */ + if (unlink(pin_prog_name)) + fprintf(stderr, "ERR: unlink(%s): %d %s\n", + pin_prog_name, errno, strerror(errno)); + if (unlink(pin_vlanmap_name)) + fprintf(stderr, "ERR: unlink(%s): %d %s\n", + pin_vlanmap_name, errno, strerror(errno)); + if (unlink(pin_countermap_name)) + fprintf(stderr, "ERR: unlink(%s): %d %s\n", + pin_countermap_name, errno, strerror(errno)); + + return 0; + } + + if (vlan == 0) { + fprintf(stderr, "ERR: required option --vlan missing\n"); + goto error; + } + + if (cpu == MAX_CPUS && vlan > 0) { + fprintf(stderr, "ERR: required option --cpu missing\n"); + goto error; + } + + vfd = bpf_obj_get(pin_vlanmap_name); + if (vfd < 0) { + fprintf(stderr, "ERR: can't find pinned map %s: %d %s\n", + pin_vlanmap_name, errno, strerror(errno)); + if (errno == ENOENT) + fprintf(stderr, " (has %s been installed yet?)\n", argv[0]); + return -1; + } + + /* decode the requested action */ + if (vlan > 0) { + /* check cpu against the max value found */ + cfd = bpf_obj_get(pin_countermap_name); + if (cfd < 0) { + fprintf(stderr, "ERR: can't find pinned map %s: %d %s\n", + pin_countermap_name, errno, strerror(errno)); + return -1; + } + c64 = CPU_COUNT; + ret = bpf_map_lookup_elem(cfd, &c64, &v64); + if (cpu >= v64) { + fprintf(stderr, "ERR: cpu %d greater than max %llu\n", cpu, v64); + return -1; + } + + /* Note that the value and key pointers really do need to be + * pointers to 64-bit values, else things get a bit muddled. + */ + v64 = vlan; + c64 = cpu; + ret = bpf_map_update_elem(vfd, &v64, &c64, 0); + if (ret) { + fprintf(stderr, "Adding vlan %d CPU %d failed: %d %s\n", + vlan, cpu, errno, strerror(errno)); + return -1; + } + + } else { + v64 = -vlan; + c64 = UNDEF_CPU; + + /* We can't actually delete from a TYPE_ARRAY map, so we + * simply set it to an undefined value. + */ + ret = bpf_map_update_elem(vfd, &v64, &c64, 0); + if (ret) { + fprintf(stderr, "Delete of vlan %llu failed: %d %s\n", + v64, errno, strerror(errno)); + return -1; + } + } + + return 0; + +error: + usage(argv); + return -1; +}