/******************************************************************************
 * Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
 * gala-gopher licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan
 *PSL v2. You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
 *KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 *NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the
 *Mulan PSL v2 for more details. Author: curry Create: 2025-06-20 Description:
 *Compatibility APIs for eBPF probes
 ******************************************************************************/

#ifndef __GOPHER_COMPAT_H__
#define __GOPHER_COMPAT_H__

#if defined(BPF_PROG_KERN) || defined(BPF_PROG_USER)
#include <bpf/bpf_helpers.h>

#if defined(BPF_PROG_KERN)
#include "vmlinux.h"
#endif

#include "__feat_probe.h"

#define MAX_DATA_SIZE 10240

struct {
    __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
    __uint(max_entries, 1);
    __uint(key_size, sizeof(__u32));
    __uint(value_size, MAX_DATA_SIZE);
} heap SEC(".maps");

#endif

#if !defined(BPF_PROG_KERN) && !defined(BPF_PROG_USER)
#include <bpf/libbpf.h>
#include <errno.h>
#include <malloc.h>

#include "__feat_probe.h"

#define PERF_BUFFER_PAGES 64

typedef int (*bpf_buffer_sample_fn)(void *ctx, void *data, u32 size);
typedef void (*bpf_buffer_lost_fn)(void *ctx, int cpu, u64 cnt);

struct bpf_buffer {
    struct bpf_map *map;
    void *inner;
    bpf_buffer_sample_fn fn;
    void *ctx;
    int type;
};

static void __perfbuf_sample_fn(void *ctx, int cpu, void *data, __u32 size) {
    struct bpf_buffer *buffer = (struct bpf_buffer *)ctx;
    bpf_buffer_sample_fn fn;

    fn = buffer->fn;
    if (!fn) {
        return;
    }

    (void)fn(buffer->ctx, data, size);
}

static inline int bpf_buffer__reset(struct bpf_map *map, struct bpf_map *heap) {
    bool use_ringbuf;
    int type;

    use_ringbuf = probe_ringbuf();
    if (use_ringbuf) {
        bpf_map__set_autocreate(heap, false);
        bpf_map__set_type(map, BPF_MAP_TYPE_RINGBUF);
        type = BPF_MAP_TYPE_RINGBUF;
    } else {
        bpf_map__set_autocreate(heap, true);
        bpf_map__set_type(map, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
        bpf_map__set_key_size(map, sizeof(int));
        bpf_map__set_value_size(map, sizeof(int));
        type = BPF_MAP_TYPE_PERF_EVENT_ARRAY;
    }

    return type;
}

#define MAX_RB_MAP_SZ 32
static inline int bpf_buffer__set_max_entries(struct bpf_map *map,
                                              struct bpf_buffer *buffer,
                                              unsigned char map_size_mb) {

    if (buffer == NULL || buffer->type != BPF_MAP_TYPE_RINGBUF) {
        return 0;
    }

    if (map_size_mb == 0 || map_size_mb > MAX_RB_MAP_SZ) {
        return -1;
    }

    u32 max_entries = map_size_mb * 1024 * 1024;
    return bpf_map__set_max_entries(map, max_entries);
}

static inline struct bpf_buffer *bpf_buffer__new(struct bpf_map *map,
                                                 struct bpf_map *heap) {
    struct bpf_buffer *buffer;
    int type;

    type = bpf_buffer__reset(map, heap);
    buffer = (struct bpf_buffer *)calloc(1, sizeof(*buffer));
    if (!buffer) {
        errno = ENOMEM;
        return NULL;
    }

    buffer->map = map;
    buffer->type = type;
    return buffer;
}

static inline struct bpf_buffer *
bpf_buffer__new_shared(struct bpf_map *map, struct bpf_map *heap,
                       struct bpf_buffer **buffer_ptr) {
    struct bpf_buffer *buffer;

    if (*buffer_ptr != NULL) {
        (void)bpf_buffer__reset(map, heap);
        return *buffer_ptr;
    }

    buffer = bpf_buffer__new(map, heap);
    *buffer_ptr = buffer;
    return buffer;
}

static inline int bpf_buffer__open(struct bpf_buffer *buffer,
                                   bpf_buffer_sample_fn sample_cb,
                                   bpf_buffer_lost_fn lost_cb, void *ctx) {
    int fd, type;
    void *inner;

    if (buffer == NULL) {
        return -1;
    }

    fd = bpf_map__fd(buffer->map);
    type = buffer->type;

    switch (type) {
    case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
        buffer->fn = sample_cb;
        buffer->ctx = ctx;
        inner = perf_buffer__new(fd, PERF_BUFFER_PAGES, __perfbuf_sample_fn,
                                 lost_cb, buffer, NULL);
        break;
    case BPF_MAP_TYPE_RINGBUF:
        inner =
            ring_buffer__new(fd, (ring_buffer_sample_fn)sample_cb, ctx, NULL);
        break;
    default:
        return 0;
    }

    long err = libbpf_get_error(inner);
    if (err) {
        return err;
    }

    buffer->inner = inner;
    return 0;
}

static inline int bpf_buffer__poll(struct bpf_buffer *buffer, int timeout_ms) {
    switch (buffer->type) {
    case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
        return perf_buffer__poll((struct perf_buffer *)buffer->inner,
                                 timeout_ms);
    case BPF_MAP_TYPE_RINGBUF:
        return ring_buffer__poll((struct ring_buffer *)buffer->inner,
                                 timeout_ms);
    default:
        return -EINVAL;
    }
}

static inline void bpf_buffer__free(struct bpf_buffer *buffer) {
    if (!buffer) {
        return;
    }

    switch (buffer->type) {
    case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
        perf_buffer__free((struct perf_buffer *)buffer->inner);
        break;
    case BPF_MAP_TYPE_RINGBUF:
        ring_buffer__free((struct ring_buffer *)buffer->inner);
        break;
    }
    free(buffer);
}
#endif

#endif