the size of BPF_MAP_TYPE_PERCPU_ARRAY doesn't match the number of CPU


Forrest Chen
 

Hi all,

I define a BPF_MAP_TYPE_PERCPU_ARRAY and use it to count packets in the xdp program. When I read the map from userspace program, I find that the entry number doesn't match local CPU numbers. I have 2 CPUs in my VM, but the count result appear in index 0 and 2, my expectation is index 0 and 1. 
So why the counting result always appear in index 0 and 2 (or CPU 0 and 2), even when my VM only have 2 cores. Does it because I run the program in VM?

Thanks,
forrest chen


Yonghong Song
 

On Wed, Jul 31, 2019 at 1:34 AM <forrest0579@...> wrote:

Hi all,

I define a BPF_MAP_TYPE_PERCPU_ARRAY and use it to count packets in the xdp program. When I read the map from userspace program, I find that the entry number doesn't match local CPU numbers. I have 2 CPUs in my VM, but the count result appear in index 0 and 2, my expectation is index 0 and 1.
So why the counting result always appear in index 0 and 2 (or CPU 0 and 2), even when my VM only have 2 cores. Does it because I run the program in VM?
Could you post how to reproduce the issue so people can help?
If you do provide reproducible steps, could you submit an issue to
help tracking?

Thanks,


Thanks,
forrest chen


Forrest Chen
 

map defined:
struct bpf_map_def SEC("maps/protocount") proto_count = {
    .type = BPF_MAP_TYPE_PERCPU_ARRAY,
    .key_size = sizeof(__u32),
    .value_size = sizeof(__u32),
    .max_entries = 1,
};
userspace code:
int32_t * allocArray(size_t ln) { return (int32_t*) malloc(ln * sizeof(int32_t)); }
void sum(int32_t* arr, size_t ln, void* sum) {
    int32_t* s = (int32_t*)sum;
    int i=0;
    for(i=0;i<ln;i++){
        printf("cpu %d: %d\n", i, arr[i]);
        (*s)+=arr[i];
    }
    sum = (void*)s;
}

Hi Yonghong Song,
The code is as above.
When I use 32bit key, the count result is in CPU 0 and 2, when I use 64bit key, the result is in CPU 0 and 1 as expect.

Thanks 
forrest chen


Yonghong Song
 

On Thu, Aug 1, 2019 at 7:02 PM <forrest0579@...> wrote:

map defined:
struct bpf_map_def SEC("maps/protocount") proto_count = {
.type = BPF_MAP_TYPE_PERCPU_ARRAY,
.key_size = sizeof(__u32),
.value_size = sizeof(__u32),
.max_entries = 1,
};

userspace code:
int32_t * allocArray(size_t ln) { return (int32_t*) malloc(ln * sizeof(int32_t)); }
void sum(int32_t* arr, size_t ln, void* sum) {
int32_t* s = (int32_t*)sum;
int i=0;
for(i=0;i<ln;i++){
printf("cpu %d: %d\n", i, arr[i]);
(*s)+=arr[i];
}
sum = (void*)s;
}


Hi Yonghong Song,
The code is as above.
When I use 32bit key, the count result is in CPU 0 and 2, when I use 64bit key, the result is in CPU 0 and 1 as expect.
For array map, the key type must be 32bit int.
```
int array_map_alloc_check(union bpf_attr *attr)
{
bool percpu = attr->map_type == BPF_MAP_TYPE_PERCPU_ARRAY;
int numa_node = bpf_map_attr_numa_node(attr);

/* check sanity of attributes */
if (attr->max_entries == 0 || attr->key_size != 4 ||
attr->value_size == 0 ||
attr->map_flags & ~ARRAY_CREATE_FLAG_MASK ||
!bpf_map_flags_access_ok(attr->map_flags) ||
(percpu && numa_node != NUMA_NO_NODE))
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;
}
```


I guess you mean value size. here.
When you got the values from kernel, the value size is rounded to 8. See
https://github.com/torvalds/linux/blob/master/kernel/bpf/arraymap.c#L81

So if you use 64bit value size, you will get correct value.
If you use 32bit value size, you should iterate through with int64_t
size, but only read the first 4 bytes for each iteration.


Thanks
forrest chen


Forrest Chen
 

On Thu, Aug 1, 2019 at 07:16 PM, Yonghong Song wrote:
I guess you mean value size. here.
When you got the values from kernel, the value size is rounded to 8. See
https://github.com/torvalds/linux/blob/master/kernel/bpf/arraymap.c#L81

So if you use 64bit value size, you will get correct value.
If you use 32bit value size, you should iterate through with int64_t
size, but only read the first 4 bytes for each iteration.
Thank you, Yonghong Song