Skip to content

Commit

Permalink
xdp: Refactor devmap allocation code for reuse
Browse files Browse the repository at this point in the history
The subsequent patch to add a new devmap sub-type can re-use much of the
initialisation and allocation code, so refactor it into separate functions.

Signed-off-by: Toke Høiland-Jørgensen <[email protected]>
Acked-by: Yonghong Song <[email protected]>
Acked-by: Jesper Dangaard Brouer <[email protected]>
Signed-off-by: Alexei Starovoitov <[email protected]>
  • Loading branch information
tohojo authored and Alexei Starovoitov committed Jul 29, 2019
1 parent 6dbff13 commit fca16e5
Showing 1 changed file with 83 additions and 53 deletions.
136 changes: 83 additions & 53 deletions kernel/bpf/devmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ struct xdp_bulk_queue {
struct bpf_dtab_netdev {
struct net_device *dev; /* must be first member, due to tracepoint */
struct bpf_dtab *dtab;
unsigned int bit;
struct xdp_bulk_queue __percpu *bulkq;
struct rcu_head rcu;
unsigned int idx; /* keep track of map index for tracepoint */
};

struct bpf_dtab {
Expand All @@ -75,28 +75,21 @@ struct bpf_dtab {
static DEFINE_SPINLOCK(dev_map_lock);
static LIST_HEAD(dev_map_list);

static struct bpf_map *dev_map_alloc(union bpf_attr *attr)
static int dev_map_init_map(struct bpf_dtab *dtab, union bpf_attr *attr)
{
struct bpf_dtab *dtab;
int err, cpu;
u64 cost;

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

/* check sanity of attributes */
if (attr->max_entries == 0 || attr->key_size != 4 ||
attr->value_size != 4 || attr->map_flags & ~DEV_CREATE_FLAG_MASK)
return ERR_PTR(-EINVAL);
return -EINVAL;

/* Lookup returns a pointer straight to dev->ifindex, so make sure the
* verifier prevents writes from the BPF side
*/
attr->map_flags |= BPF_F_RDONLY_PROG;

dtab = kzalloc(sizeof(*dtab), GFP_USER);
if (!dtab)
return ERR_PTR(-ENOMEM);

bpf_map_init_from_attr(&dtab->map, attr);

Expand All @@ -107,9 +100,7 @@ static struct bpf_map *dev_map_alloc(union bpf_attr *attr)
/* if map size is larger than memlock limit, reject it */
err = bpf_map_charge_init(&dtab->map.memory, cost);
if (err)
goto free_dtab;

err = -ENOMEM;
return -EINVAL;

dtab->flush_list = alloc_percpu(struct list_head);
if (!dtab->flush_list)
Expand All @@ -124,19 +115,38 @@ static struct bpf_map *dev_map_alloc(union bpf_attr *attr)
if (!dtab->netdev_map)
goto free_percpu;

spin_lock(&dev_map_lock);
list_add_tail_rcu(&dtab->list, &dev_map_list);
spin_unlock(&dev_map_lock);

return &dtab->map;
return 0;

free_percpu:
free_percpu(dtab->flush_list);
free_charge:
bpf_map_charge_finish(&dtab->map.memory);
free_dtab:
kfree(dtab);
return ERR_PTR(err);
return -ENOMEM;
}

static struct bpf_map *dev_map_alloc(union bpf_attr *attr)
{
struct bpf_dtab *dtab;
int err;

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

dtab = kzalloc(sizeof(*dtab), GFP_USER);
if (!dtab)
return ERR_PTR(-ENOMEM);

err = dev_map_init_map(dtab, attr);
if (err) {
kfree(dtab);
return ERR_PTR(err);
}

spin_lock(&dev_map_lock);
list_add_tail_rcu(&dtab->list, &dev_map_list);
spin_unlock(&dev_map_lock);

return &dtab->map;
}

static void dev_map_free(struct bpf_map *map)
Expand Down Expand Up @@ -235,7 +245,7 @@ static int bq_xmit_all(struct xdp_bulk_queue *bq, u32 flags,
out:
bq->count = 0;

trace_xdp_devmap_xmit(&obj->dtab->map, obj->bit,
trace_xdp_devmap_xmit(&obj->dtab->map, obj->idx,
sent, drops, bq->dev_rx, dev, err);
bq->dev_rx = NULL;
__list_del_clearprev(&bq->flush_node);
Expand Down Expand Up @@ -412,17 +422,52 @@ static int dev_map_delete_elem(struct bpf_map *map, void *key)
return 0;
}

static int dev_map_update_elem(struct bpf_map *map, void *key, void *value,
u64 map_flags)
static struct bpf_dtab_netdev *__dev_map_alloc_node(struct net *net,
struct bpf_dtab *dtab,
u32 ifindex,
unsigned int idx)
{
struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map);
struct net *net = current->nsproxy->net_ns;
gfp_t gfp = GFP_ATOMIC | __GFP_NOWARN;
struct bpf_dtab_netdev *dev;
struct xdp_bulk_queue *bq;
int cpu;

dev = kmalloc_node(sizeof(*dev), gfp, dtab->map.numa_node);
if (!dev)
return ERR_PTR(-ENOMEM);

dev->bulkq = __alloc_percpu_gfp(sizeof(*dev->bulkq),
sizeof(void *), gfp);
if (!dev->bulkq) {
kfree(dev);
return ERR_PTR(-ENOMEM);
}

for_each_possible_cpu(cpu) {
bq = per_cpu_ptr(dev->bulkq, cpu);
bq->obj = dev;
}

dev->dev = dev_get_by_index(net, ifindex);
if (!dev->dev) {
free_percpu(dev->bulkq);
kfree(dev);
return ERR_PTR(-EINVAL);
}

dev->idx = idx;
dev->dtab = dtab;

return dev;
}

static int __dev_map_update_elem(struct net *net, struct bpf_map *map,
void *key, void *value, u64 map_flags)
{
struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map);
struct bpf_dtab_netdev *dev, *old_dev;
u32 ifindex = *(u32 *)value;
struct xdp_bulk_queue *bq;
u32 i = *(u32 *)key;
int cpu;

if (unlikely(map_flags > BPF_EXIST))
return -EINVAL;
Expand All @@ -434,31 +479,9 @@ static int dev_map_update_elem(struct bpf_map *map, void *key, void *value,
if (!ifindex) {
dev = NULL;
} else {
dev = kmalloc_node(sizeof(*dev), gfp, map->numa_node);
if (!dev)
return -ENOMEM;

dev->bulkq = __alloc_percpu_gfp(sizeof(*dev->bulkq),
sizeof(void *), gfp);
if (!dev->bulkq) {
kfree(dev);
return -ENOMEM;
}

for_each_possible_cpu(cpu) {
bq = per_cpu_ptr(dev->bulkq, cpu);
bq->obj = dev;
}

dev->dev = dev_get_by_index(net, ifindex);
if (!dev->dev) {
free_percpu(dev->bulkq);
kfree(dev);
return -EINVAL;
}

dev->bit = i;
dev->dtab = dtab;
dev = __dev_map_alloc_node(net, dtab, ifindex, i);
if (IS_ERR(dev))
return PTR_ERR(dev);
}

/* Use call_rcu() here to ensure rcu critical sections have completed
Expand All @@ -472,6 +495,13 @@ static int dev_map_update_elem(struct bpf_map *map, void *key, void *value,
return 0;
}

static int dev_map_update_elem(struct bpf_map *map, void *key, void *value,
u64 map_flags)
{
return __dev_map_update_elem(current->nsproxy->net_ns,
map, key, value, map_flags);
}

const struct bpf_map_ops dev_map_ops = {
.map_alloc = dev_map_alloc,
.map_free = dev_map_free,
Expand Down

0 comments on commit fca16e5

Please sign in to comment.