bpf: Use kvmalloc for map values in syscall
authorStanislav Fomichev <sdf@google.com>
Wed, 18 Aug 2021 23:52:15 +0000 (16:52 -0700)
committerDaniel Borkmann <daniel@iogearbox.net>
Thu, 19 Aug 2021 22:09:38 +0000 (00:09 +0200)
Use kvmalloc/kvfree for temporary value when manipulating a map via
syscall. kmalloc might not be sufficient for percpu maps where the value
is big (and further multiplied by hundreds of CPUs).

Can be reproduced with netcnt test on qemu with "-smp 255".

Signed-off-by: Stanislav Fomichev <sdf@google.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Song Liu <songliubraving@fb.com>
Link: https://lore.kernel.org/bpf/20210818235216.1159202-1-sdf@google.com
kernel/bpf/syscall.c

index 7420e1334ab2b8a5297eabda21bf32e794b1ea0f..075f650d297ad247d428ff0e426b38ec61ff2d14 100644 (file)
@@ -1076,7 +1076,7 @@ static int map_lookup_elem(union bpf_attr *attr)
        value_size = bpf_map_value_size(map);
 
        err = -ENOMEM;
-       value = kmalloc(value_size, GFP_USER | __GFP_NOWARN);
+       value = kvmalloc(value_size, GFP_USER | __GFP_NOWARN);
        if (!value)
                goto free_key;
 
@@ -1091,7 +1091,7 @@ static int map_lookup_elem(union bpf_attr *attr)
        err = 0;
 
 free_value:
-       kfree(value);
+       kvfree(value);
 free_key:
        kfree(key);
 err_put:
@@ -1137,16 +1137,10 @@ static int map_update_elem(union bpf_attr *attr, bpfptr_t uattr)
                goto err_put;
        }
 
-       if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH ||
-           map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH ||
-           map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY ||
-           map->map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE)
-               value_size = round_up(map->value_size, 8) * num_possible_cpus();
-       else
-               value_size = map->value_size;
+       value_size = bpf_map_value_size(map);
 
        err = -ENOMEM;
-       value = kmalloc(value_size, GFP_USER | __GFP_NOWARN);
+       value = kvmalloc(value_size, GFP_USER | __GFP_NOWARN);
        if (!value)
                goto free_key;
 
@@ -1157,7 +1151,7 @@ static int map_update_elem(union bpf_attr *attr, bpfptr_t uattr)
        err = bpf_map_update_value(map, f, key, value, attr->flags);
 
 free_value:
-       kfree(value);
+       kvfree(value);
 free_key:
        kfree(key);
 err_put:
@@ -1367,7 +1361,7 @@ int generic_map_update_batch(struct bpf_map *map,
        if (!key)
                return -ENOMEM;
 
-       value = kmalloc(value_size, GFP_USER | __GFP_NOWARN);
+       value = kvmalloc(value_size, GFP_USER | __GFP_NOWARN);
        if (!value) {
                kfree(key);
                return -ENOMEM;
@@ -1390,7 +1384,7 @@ int generic_map_update_batch(struct bpf_map *map,
        if (copy_to_user(&uattr->batch.count, &cp, sizeof(cp)))
                err = -EFAULT;
 
-       kfree(value);
+       kvfree(value);
        kfree(key);
        return err;
 }
@@ -1429,7 +1423,7 @@ int generic_map_lookup_batch(struct bpf_map *map,
        if (!buf_prevkey)
                return -ENOMEM;
 
-       buf = kmalloc(map->key_size + value_size, GFP_USER | __GFP_NOWARN);
+       buf = kvmalloc(map->key_size + value_size, GFP_USER | __GFP_NOWARN);
        if (!buf) {
                kfree(buf_prevkey);
                return -ENOMEM;
@@ -1492,7 +1486,7 @@ int generic_map_lookup_batch(struct bpf_map *map,
 
 free_buf:
        kfree(buf_prevkey);
-       kfree(buf);
+       kvfree(buf);
        return err;
 }
 
@@ -1547,7 +1541,7 @@ static int map_lookup_and_delete_elem(union bpf_attr *attr)
        value_size = bpf_map_value_size(map);
 
        err = -ENOMEM;
-       value = kmalloc(value_size, GFP_USER | __GFP_NOWARN);
+       value = kvmalloc(value_size, GFP_USER | __GFP_NOWARN);
        if (!value)
                goto free_key;
 
@@ -1579,7 +1573,7 @@ static int map_lookup_and_delete_elem(union bpf_attr *attr)
        err = 0;
 
 free_value:
-       kfree(value);
+       kvfree(value);
 free_key:
        kfree(key);
 err_put: