stackmap.c 7.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
/* Copyright (c) 2016 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.
 */
#include <linux/bpf.h>
#include <linux/jhash.h>
#include <linux/filter.h>
#include <linux/stacktrace.h>
#include <linux/perf_event.h>
12
#include "percpu_freelist.h"
13

14 15 16
#define STACK_CREATE_FLAG_MASK \
	(BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)

17
struct stack_map_bucket {
18
	struct pcpu_freelist_node fnode;
19 20 21 22 23 24 25
	u32 hash;
	u32 nr;
	u64 ip[];
};

struct bpf_stack_map {
	struct bpf_map map;
26 27
	void *elems;
	struct pcpu_freelist freelist;
28
	u32 n_buckets;
29
	struct stack_map_bucket *buckets[];
30 31
};

32 33 34 35 36
static int prealloc_elems_and_freelist(struct bpf_stack_map *smap)
{
	u32 elem_size = sizeof(struct stack_map_bucket) + smap->map.value_size;
	int err;

37 38
	smap->elems = bpf_map_area_alloc(elem_size * smap->map.max_entries,
					 smap->map.numa_node);
39 40 41 42 43 44 45 46 47 48 49 50
	if (!smap->elems)
		return -ENOMEM;

	err = pcpu_freelist_init(&smap->freelist);
	if (err)
		goto free_elems;

	pcpu_freelist_populate(&smap->freelist, smap->elems, elem_size,
			       smap->map.max_entries);
	return 0;

free_elems:
51
	bpf_map_area_free(smap->elems);
52 53 54
	return err;
}

55 56 57 58 59 60 61 62 63 64 65
/* Called from syscall */
static struct bpf_map *stack_map_alloc(union bpf_attr *attr)
{
	u32 value_size = attr->value_size;
	struct bpf_stack_map *smap;
	u64 cost, n_buckets;
	int err;

	if (!capable(CAP_SYS_ADMIN))
		return ERR_PTR(-EPERM);

66
	if (attr->map_flags & ~STACK_CREATE_FLAG_MASK)
67 68
		return ERR_PTR(-EINVAL);

69 70 71
	/* check sanity of attributes */
	if (attr->max_entries == 0 || attr->key_size != 4 ||
	    value_size < 8 || value_size % 8 ||
72
	    value_size / 8 > sysctl_perf_event_max_stack)
73 74 75 76 77 78 79 80 81
		return ERR_PTR(-EINVAL);

	/* hash table size must be power of 2 */
	n_buckets = roundup_pow_of_two(attr->max_entries);

	cost = n_buckets * sizeof(struct stack_map_bucket *) + sizeof(*smap);
	if (cost >= U32_MAX - PAGE_SIZE)
		return ERR_PTR(-E2BIG);

82
	smap = bpf_map_area_alloc(cost, bpf_map_attr_numa_node(attr));
83 84
	if (!smap)
		return ERR_PTR(-ENOMEM);
85 86 87 88 89 90 91 92 93 94

	err = -E2BIG;
	cost += n_buckets * (value_size + sizeof(struct stack_map_bucket));
	if (cost >= U32_MAX - PAGE_SIZE)
		goto free_smap;

	smap->map.map_type = attr->map_type;
	smap->map.key_size = attr->key_size;
	smap->map.value_size = value_size;
	smap->map.max_entries = attr->max_entries;
95
	smap->map.map_flags = attr->map_flags;
96 97
	smap->n_buckets = n_buckets;
	smap->map.pages = round_up(cost, PAGE_SIZE) >> PAGE_SHIFT;
98
	smap->map.numa_node = bpf_map_attr_numa_node(attr);
99

100 101 102 103
	err = bpf_map_precharge_memlock(smap->map.pages);
	if (err)
		goto free_smap;

104
	err = get_callchain_buffers(sysctl_perf_event_max_stack);
105 106 107
	if (err)
		goto free_smap;

108 109 110 111
	err = prealloc_elems_and_freelist(smap);
	if (err)
		goto put_buffers;

112 113
	return &smap->map;

114 115
put_buffers:
	put_callchain_buffers();
116
free_smap:
117
	bpf_map_area_free(smap);
118 119 120
	return ERR_PTR(err);
}

121 122
BPF_CALL_3(bpf_get_stackid, struct pt_regs *, regs, struct bpf_map *, map,
	   u64, flags)
123 124 125 126 127
{
	struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map);
	struct perf_callchain_entry *trace;
	struct stack_map_bucket *bucket, *new_bucket, *old_bucket;
	u32 max_depth = map->value_size / 8;
128 129
	/* stack_map_alloc() checks that max_depth <= sysctl_perf_event_max_stack */
	u32 init_nr = sysctl_perf_event_max_stack - max_depth;
130 131 132 133 134 135 136 137 138 139
	u32 skip = flags & BPF_F_SKIP_FIELD_MASK;
	u32 hash, id, trace_nr, trace_len;
	bool user = flags & BPF_F_USER_STACK;
	bool kernel = !user;
	u64 *ips;

	if (unlikely(flags & ~(BPF_F_SKIP_FIELD_MASK | BPF_F_USER_STACK |
			       BPF_F_FAST_STACK_CMP | BPF_F_REUSE_STACKID)))
		return -EINVAL;

140 141
	trace = get_perf_callchain(regs, init_nr, kernel, user,
				   sysctl_perf_event_max_stack, false, false);
142 143 144 145 146 147

	if (unlikely(!trace))
		/* couldn't fetch the stack trace */
		return -EFAULT;

	/* get_perf_callchain() guarantees that trace->nr >= init_nr
148
	 * and trace-nr <= sysctl_perf_event_max_stack, so trace_nr <= max_depth
149 150 151 152 153 154 155 156 157 158 159 160
	 */
	trace_nr = trace->nr - init_nr;

	if (trace_nr <= skip)
		/* skipping more than usable stack trace */
		return -EFAULT;

	trace_nr -= skip;
	trace_len = trace_nr * sizeof(u64);
	ips = trace->ip + skip + init_nr;
	hash = jhash2((u32 *)ips, trace_len / sizeof(u32), 0);
	id = hash & (smap->n_buckets - 1);
161
	bucket = READ_ONCE(smap->buckets[id]);
162 163 164 165 166 167 168 169 170 171 172 173 174

	if (bucket && bucket->hash == hash) {
		if (flags & BPF_F_FAST_STACK_CMP)
			return id;
		if (bucket->nr == trace_nr &&
		    memcmp(bucket->ip, ips, trace_len) == 0)
			return id;
	}

	/* this call stack is not in the map, try to add it */
	if (bucket && !(flags & BPF_F_REUSE_STACKID))
		return -EEXIST;

175 176
	new_bucket = (struct stack_map_bucket *)
		pcpu_freelist_pop(&smap->freelist);
177 178 179 180 181 182 183 184 185
	if (unlikely(!new_bucket))
		return -ENOMEM;

	memcpy(new_bucket->ip, ips, trace_len);
	new_bucket->hash = hash;
	new_bucket->nr = trace_nr;

	old_bucket = xchg(&smap->buckets[id], new_bucket);
	if (old_bucket)
186
		pcpu_freelist_push(&smap->freelist, &old_bucket->fnode);
187 188 189 190 191 192 193 194 195 196 197 198
	return id;
}

const struct bpf_func_proto bpf_get_stackid_proto = {
	.func		= bpf_get_stackid,
	.gpl_only	= true,
	.ret_type	= RET_INTEGER,
	.arg1_type	= ARG_PTR_TO_CTX,
	.arg2_type	= ARG_CONST_MAP_PTR,
	.arg3_type	= ARG_ANYTHING,
};

199
/* Called from eBPF program */
200
static void *stack_map_lookup_elem(struct bpf_map *map, void *key)
201 202 203 204 205 206
{
	return NULL;
}

/* Called from syscall */
int bpf_stackmap_copy(struct bpf_map *map, void *key, void *value)
207 208
{
	struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map);
209 210
	struct stack_map_bucket *bucket, *old_bucket;
	u32 id = *(u32 *)key, trace_len;
211 212

	if (unlikely(id >= smap->n_buckets))
213 214 215 216 217 218 219 220 221 222 223 224 225 226
		return -ENOENT;

	bucket = xchg(&smap->buckets[id], NULL);
	if (!bucket)
		return -ENOENT;

	trace_len = bucket->nr * sizeof(u64);
	memcpy(value, bucket->ip, trace_len);
	memset(value + trace_len, 0, map->value_size - trace_len);

	old_bucket = xchg(&smap->buckets[id], bucket);
	if (old_bucket)
		pcpu_freelist_push(&smap->freelist, &old_bucket->fnode);
	return 0;
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
}

static int stack_map_get_next_key(struct bpf_map *map, void *key, void *next_key)
{
	return -EINVAL;
}

static int stack_map_update_elem(struct bpf_map *map, void *key, void *value,
				 u64 map_flags)
{
	return -EINVAL;
}

/* Called from syscall or from eBPF program */
static int stack_map_delete_elem(struct bpf_map *map, void *key)
{
	struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map);
	struct stack_map_bucket *old_bucket;
	u32 id = *(u32 *)key;

	if (unlikely(id >= smap->n_buckets))
		return -E2BIG;

	old_bucket = xchg(&smap->buckets[id], NULL);
	if (old_bucket) {
252
		pcpu_freelist_push(&smap->freelist, &old_bucket->fnode);
253 254 255 256 257 258 259 260 261 262 263
		return 0;
	} else {
		return -ENOENT;
	}
}

/* Called when map->refcnt goes to zero, either from workqueue or from syscall */
static void stack_map_free(struct bpf_map *map)
{
	struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map);

264
	/* wait for bpf programs to complete before freeing stack map */
265 266
	synchronize_rcu();

267
	bpf_map_area_free(smap->elems);
268
	pcpu_freelist_destroy(&smap->freelist);
269
	bpf_map_area_free(smap);
270 271 272
	put_callchain_buffers();
}

273
const struct bpf_map_ops stack_map_ops = {
274 275 276 277 278 279 280
	.map_alloc = stack_map_alloc,
	.map_free = stack_map_free,
	.map_get_next_key = stack_map_get_next_key,
	.map_lookup_elem = stack_map_lookup_elem,
	.map_update_elem = stack_map_update_elem,
	.map_delete_elem = stack_map_delete_elem,
};