/* emit BPF instructions equivalent to C code of array_map_lookup_elem() */
static int array_map_gen_lookup(struct bpf_map *map, struct bpf_insn *insn_buf)
{
	struct bpf_array *array = container_of(map, struct bpf_array, map);
	struct bpf_insn *insn = insn_buf;
	u32 elem_size = round_up(map->value_size, 8);
	const int ret = BPF_REG_0;
	const int map_ptr = BPF_REG_1;
	const int index = BPF_REG_2;

	if (map->map_flags & BPF_F_INNER_MAP)
		return -EOPNOTSUPP;

	#ifdef CONFIG_HIVE
	if (map->is_aggregated && map->shadow_data) {
		/* substitute array->value with map->shadow_data */
		/* assume map is the first attribute of struct array,
			so that array == array->map 
			array=ffff8000844efe80, &array->map=ffff8000844efe80 */
		// fbpf_log("array=%016llx, array->map=%016llx\n", array, &array->map);
		*insn++ = BPF_LDX_MEM(BPF_DW, map_ptr, map_ptr, offsetof(struct bpf_map, shadow_data));
	}
	else
		*insn++ = BPF_ALU64_IMM(BPF_ADD, map_ptr, 
			offsetof(struct bpf_array, value));
	#else
	*insn++ = BPF_ALU64_IMM(BPF_ADD, map_ptr, offsetof(struct bpf_array, value));
	#endif
	*insn++ = BPF_LDX_MEM(BPF_W, ret, index, 0);
	if (!map->bypass_spec_v1) {
		*insn++ = BPF_JMP_IMM(BPF_JGE, ret, map->max_entries, 4);
		*insn++ = BPF_ALU32_IMM(BPF_AND, ret, array->index_mask);
	} else {
		*insn++ = BPF_JMP_IMM(BPF_JGE, ret, map->max_entries, 3);
	}

	if (is_power_of_2(elem_size)) {
		*insn++ = BPF_ALU64_IMM(BPF_LSH, ret, ilog2(elem_size));
	} else {
		*insn++ = BPF_ALU64_IMM(BPF_MUL, ret, elem_size);
	}
	*insn++ = BPF_ALU64_REG(BPF_ADD, ret, map_ptr);
	*insn++ = BPF_JMP_IMM(BPF_JA, 0, 0, 1);
	*insn++ = BPF_MOV64_IMM(ret, 0);
	return insn - insn_buf;
}

#ifdef CONFIG_HIVE
static void *__percpu_array_map_lookup_elem(struct bpf_array *array, u64 value_ptr)
{
	struct page *phy_page;
	struct list_head *used_pages_head;
	struct bpf_used_page *entry;
	struct bpf_used_page *next;
	u64 in_page_offset;
	
	in_page_offset = value_ptr - round_down(value_ptr, PAGE_SIZE);
	phy_page = kv_virt_to_page((void *)value_ptr);
	
	used_pages_head = &array->map.used_pages->list_head;
	list_for_each_entry_safe(entry, next, used_pages_head, list_head) {
		if (entry->physic_page == phy_page) {
			return (void *)entry->shadow_page + in_page_offset;
		}
	}

	pr_err("fail to find shadow_data of percpu array %016llx\n", 
			(u64)(&array->map));
	return NULL;
}
#endif

/* Called from eBPF program */
static void *percpu_array_map_lookup_elem(struct bpf_map *map, void *key)
{
	struct bpf_array *array = container_of(map, struct bpf_array, map);
	u32 index = *(u32 *)key;

	if (unlikely(index >= array->map.max_entries))
		return NULL;

	#ifdef CONFIG_HIVE
	if (!map->is_aggregated)
		return this_cpu_ptr(array->pptrs[index & array->index_mask]);
	else {
		u64 value_ptr = (u64)this_cpu_ptr(
							array->pptrs[index & array->index_mask]);
		return __percpu_array_map_lookup_elem(array, value_ptr);
	}
	#else
	return this_cpu_ptr(array->pptrs[index & array->index_mask]);
	#endif
}
