#ifndef _NFT_SET_PIPAPO_H
#include <linux/log2.h>
#include <net/ipv6.h>
#define NFT_PIPAPO_MAX_FIELDS NFT_REG32_COUNT
#define NFT_PIPAPO_MIN_FIELDS 2
#define NFT_PIPAPO_MAX_BYTES (sizeof(struct in6_addr))
#define NFT_PIPAPO_MAX_BITS (NFT_PIPAPO_MAX_BYTES * BITS_PER_BYTE)
#define NFT_PIPAPO_GROUP_BITS_INIT NFT_PIPAPO_GROUP_BITS_SMALL_SET
#define NFT_PIPAPO_GROUP_BITS_SMALL_SET 8
#define NFT_PIPAPO_GROUP_BITS_LARGE_SET 4
#define NFT_PIPAPO_GROUP_BITS_ARE_8_OR_4 \
BUILD_BUG_ON((NFT_PIPAPO_GROUP_BITS_SMALL_SET != 8) || \
(NFT_PIPAPO_GROUP_BITS_LARGE_SET != 4))
#define NFT_PIPAPO_GROUPS_PER_BYTE(f) (BITS_PER_BYTE / (f)->bb)
#define NFT_PIPAPO_LT_SIZE_THRESHOLD (1 << 21)
#define NFT_PIPAPO_LT_SIZE_HYSTERESIS (1 << 16)
#define NFT_PIPAPO_LT_SIZE_HIGH NFT_PIPAPO_LT_SIZE_THRESHOLD
#define NFT_PIPAPO_LT_SIZE_LOW NFT_PIPAPO_LT_SIZE_THRESHOLD - \
NFT_PIPAPO_LT_SIZE_HYSTERESIS
#define NFT_PIPAPO_GROUPS_PADDED_SIZE(f) \
(round_up((f)->groups / NFT_PIPAPO_GROUPS_PER_BYTE(f), sizeof(u32)))
#define NFT_PIPAPO_BUCKETS(bb) (1 << (bb))
#define NFT_PIPAPO_MAP_NBITS (const_ilog2(NFT_PIPAPO_MAX_BITS * 2))
#if BITS_PER_LONG == 64
#define NFT_PIPAPO_MAP_TOBITS 32
#else
#define NFT_PIPAPO_MAP_TOBITS (BITS_PER_LONG - NFT_PIPAPO_MAP_NBITS)
#endif
#define NFT_PIPAPO_RULE0_MAX ((1UL << (NFT_PIPAPO_MAP_TOBITS - 1)) \
- (1UL << NFT_PIPAPO_MAP_NBITS))
#ifdef NFT_PIPAPO_ALIGN
#define NFT_PIPAPO_ALIGN_HEADROOM \
(NFT_PIPAPO_ALIGN - ARCH_KMALLOC_MINALIGN)
#define NFT_PIPAPO_LT_ALIGN(lt) (PTR_ALIGN((lt), NFT_PIPAPO_ALIGN))
#else
#define NFT_PIPAPO_ALIGN_HEADROOM 0
#define NFT_PIPAPO_LT_ALIGN(lt) (lt)
#endif
#define nft_pipapo_for_each_field(field, index, match) \
for ((field) = (match)->f, (index) = 0; \
(index) < (match)->field_count; \
(index)++, (field)++)
union nft_pipapo_map_bucket {
struct {
#if BITS_PER_LONG == 64
static_assert(NFT_PIPAPO_MAP_TOBITS <= 32);
u32 to;
static_assert(NFT_PIPAPO_MAP_NBITS <= 32);
u32 n;
#else
unsigned long to:NFT_PIPAPO_MAP_TOBITS;
unsigned long n:NFT_PIPAPO_MAP_NBITS;
#endif
};
struct nft_pipapo_elem *e;
};
struct nft_pipapo_field {
unsigned int rules;
unsigned int bsize;
unsigned int rules_alloc;
u8 groups;
u8 bb;
unsigned long *lt;
union nft_pipapo_map_bucket *mt;
};
struct nft_pipapo_scratch {
local_lock_t bh_lock;
u8 map_index;
unsigned long __map[];
};
enum nft_pipapo_clone_state {
NFT_PIPAPO_CLONE_NEW,
NFT_PIPAPO_CLONE_MOD,
NFT_PIPAPO_CLONE_ERR,
};
struct nft_pipapo_match {
u8 field_count;
enum nft_pipapo_clone_state state:8;
unsigned int bsize_max;
struct nft_pipapo_scratch * __percpu *scratch;
struct rcu_head rcu;
struct nft_pipapo_field f[] __counted_by(field_count);
};
struct nft_pipapo {
struct nft_pipapo_match __rcu *match;
struct nft_pipapo_match *clone;
int width;
unsigned long last_gc;
struct list_head gc_head;
};
struct nft_pipapo_elem;
struct nft_pipapo_elem {
struct nft_elem_priv priv;
struct nft_set_ext ext;
};
int pipapo_refill(unsigned long *map, unsigned int len, unsigned int rules,
unsigned long *dst,
const union nft_pipapo_map_bucket *mt, bool match_only);
static inline void pipapo_and_field_buckets_4bit(const struct nft_pipapo_field *f,
unsigned long *dst,
const u8 *data)
{
unsigned long *lt = NFT_PIPAPO_LT_ALIGN(f->lt);
int group;
for (group = 0; group < f->groups; group += BITS_PER_BYTE / 4, data++) {
u8 v;
v = *data >> 4;
__bitmap_and(dst, dst, lt + v * f->bsize,
f->bsize * BITS_PER_LONG);
lt += f->bsize * NFT_PIPAPO_BUCKETS(4);
v = *data & 0x0f;
__bitmap_and(dst, dst, lt + v * f->bsize,
f->bsize * BITS_PER_LONG);
lt += f->bsize * NFT_PIPAPO_BUCKETS(4);
}
}
static inline void pipapo_and_field_buckets_8bit(const struct nft_pipapo_field *f,
unsigned long *dst,
const u8 *data)
{
unsigned long *lt = NFT_PIPAPO_LT_ALIGN(f->lt);
int group;
for (group = 0; group < f->groups; group++, data++) {
__bitmap_and(dst, dst, lt + *data * f->bsize,
f->bsize * BITS_PER_LONG);
lt += f->bsize * NFT_PIPAPO_BUCKETS(8);
}
}
static u64 pipapo_estimate_size(const struct nft_set_desc *desc)
{
unsigned long entry_size;
u64 size;
int i;
for (i = 0, entry_size = 0; i < desc->field_count; i++) {
unsigned long rules;
if (desc->field_len[i] > NFT_PIPAPO_MAX_BYTES)
return 0;
rules = ilog2(desc->field_len[i] * BITS_PER_BYTE) * 2;
entry_size += rules *
NFT_PIPAPO_BUCKETS(NFT_PIPAPO_GROUP_BITS_INIT) /
BITS_PER_BYTE;
entry_size += rules * sizeof(union nft_pipapo_map_bucket);
}
size = desc->size * entry_size;
if (size && div_u64(size, desc->size) != entry_size)
return 0;
size += sizeof(struct nft_pipapo) + sizeof(struct nft_pipapo_match) * 2;
size += sizeof(struct nft_pipapo_field) * desc->field_count;
return size;
}
static inline void pipapo_resmap_init(const struct nft_pipapo_match *m, unsigned long *res_map)
{
const struct nft_pipapo_field *f = m->f;
int i;
for (i = 0; i < f->bsize; i++)
res_map[i] = ULONG_MAX;
for (i = f->bsize; i < m->bsize_max; i++)
res_map[i] = 0ul;
}
#endif