mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-16 17:57:38 -04:00
ERROR: modpost: "cnum64_umin" [drivers/net/ethernet/netronome/nfp/nfp.ko] undefined!
ERROR: modpost: "cnum64_umax" [drivers/net/ethernet/netronome/nfp/nfp.ko] undefined!
Export symbols for these references.
Reported-by: Kaitao Cheng <pilgrimtao@gmail.com>
Fixes: bbc6310855 ("bpf: replace min/max fields with struct cnum{32,64}")
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20260427112205.1346733-1-alan.maguire@oracle.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
248 lines
6.4 KiB
C
248 lines
6.4 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
|
|
|
|
#ifndef T
|
|
#error "Define T (bit width: 32, 64) before including cnum_defs.h"
|
|
#endif
|
|
|
|
#include <linux/cnum.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/limits.h>
|
|
#include <linux/minmax.h>
|
|
#include <linux/compiler_types.h>
|
|
|
|
#define cnum_t __PASTE(cnum, T)
|
|
#define ut __PASTE(u, T)
|
|
#define st __PASTE(s, T)
|
|
#define UT_MAX __PASTE(__PASTE(U, T), _MAX)
|
|
#define ST_MAX __PASTE(__PASTE(S, T), _MAX)
|
|
#define ST_MIN __PASTE(__PASTE(S, T), _MIN)
|
|
#define EMPTY __PASTE(__PASTE(CNUM, T), _EMPTY)
|
|
#define FN(name) __PASTE(__PASTE(cnum, T), __PASTE(_, name))
|
|
|
|
struct cnum_t FN(from_urange)(ut min, ut max)
|
|
{
|
|
return (struct cnum_t){ .base = min, .size = (ut)max - min };
|
|
}
|
|
|
|
struct cnum_t FN(from_srange)(st min, st max)
|
|
{
|
|
ut size = (ut)max - (ut)min;
|
|
ut base = size == UT_MAX ? 0 : (ut)min;
|
|
|
|
return (struct cnum_t){ .base = base, .size = size };
|
|
}
|
|
|
|
/* True if this cnum represents two unsigned ranges. */
|
|
static inline bool FN(urange_overflow)(struct cnum_t cnum)
|
|
{
|
|
/* Same as cnum.base + cnum.size > UT_MAX but avoids overflow */
|
|
return cnum.size > UT_MAX - (ut)cnum.base;
|
|
}
|
|
|
|
/*
|
|
* cnum{T}_umin / cnum{T}_umax query an unsigned range represented by this cnum.
|
|
* If cnum represents a range crossing the UT_MAX/0 boundary, the unbound range
|
|
* [0..UT_MAX] is returned.
|
|
*/
|
|
ut FN(umin)(struct cnum_t cnum)
|
|
{
|
|
return FN(urange_overflow)(cnum) ? 0 : cnum.base;
|
|
}
|
|
EXPORT_SYMBOL_GPL(FN(umin));
|
|
|
|
ut FN(umax)(struct cnum_t cnum)
|
|
{
|
|
return FN(urange_overflow)(cnum) ? UT_MAX : cnum.base + cnum.size;
|
|
}
|
|
EXPORT_SYMBOL_GPL(FN(umax));
|
|
|
|
/* True if this cnum represents two signed ranges. */
|
|
static inline bool FN(srange_overflow)(struct cnum_t cnum)
|
|
{
|
|
return FN(contains)(cnum, (ut)ST_MAX) && FN(contains)(cnum, (ut)ST_MIN);
|
|
}
|
|
|
|
/*
|
|
* cnum{T}_smin / cnum{T}_smax query a signed range represented by this cnum.
|
|
* If cnum represents a range crossing the ST_MAX/ST_MIN boundary, the unbound range
|
|
* [ST_MIN..ST_MAX] is returned.
|
|
*/
|
|
st FN(smin)(struct cnum_t cnum)
|
|
{
|
|
return FN(srange_overflow)(cnum)
|
|
? ST_MIN
|
|
: min((st)cnum.base, (st)(cnum.base + cnum.size));
|
|
}
|
|
|
|
st FN(smax)(struct cnum_t cnum)
|
|
{
|
|
return FN(srange_overflow)(cnum)
|
|
? ST_MAX
|
|
: max((st)cnum.base, (st)(cnum.base + cnum.size));
|
|
}
|
|
|
|
/*
|
|
* Returns a possibly empty intersection of cnums 'a' and 'b'.
|
|
* If 'a' and 'b' intersect in two sub-arcs, the function over-approximates
|
|
* and returns either 'a' or 'b', whichever is smaller.
|
|
*/
|
|
struct cnum_t FN(intersect)(struct cnum_t a, struct cnum_t b)
|
|
{
|
|
struct cnum_t b1;
|
|
ut dbase;
|
|
|
|
if (FN(is_empty)(a) || FN(is_empty)(b))
|
|
return EMPTY;
|
|
|
|
if (a.base > b.base)
|
|
swap(a, b);
|
|
|
|
/*
|
|
* Rotate frame of reference such that a.base is 0.
|
|
* 'b1' is 'b' in this frame of reference.
|
|
*/
|
|
dbase = b.base - a.base;
|
|
b1 = (struct cnum_t){ dbase, b.size };
|
|
if (FN(urange_overflow)(b1)) {
|
|
if (b1.base <= a.size) {
|
|
/*
|
|
* Rotated frame (a.base at origin):
|
|
*
|
|
* 0 UT_MAX
|
|
* |--------------------------------------------|
|
|
* [=== a ==========================] |
|
|
* [= b1 tail =] [========= b1 main ==========>]
|
|
* ^-- b1.base <= a.size
|
|
*
|
|
* 'a' and 'b' intersect in two disjoint arcs,
|
|
* can't represent as single cnum, over-approximate
|
|
* the result.
|
|
*/
|
|
return a.size <= b.size ? a : b;
|
|
} else {
|
|
/*
|
|
* Rotated frame (a.base at origin):
|
|
*
|
|
* 0 UT_MAX
|
|
* |--------------------------------------------|
|
|
* [=== a =============] | |
|
|
* [= b1 tail =] [======= b1 main ====>]
|
|
* ^-- b1.base > a.size
|
|
*
|
|
* Only 'b' tail intersects 'a'.
|
|
*/
|
|
return (struct cnum_t) {
|
|
.base = a.base,
|
|
.size = min(a.size, (ut)(b1.base + b1.size)),
|
|
};
|
|
}
|
|
} else if (a.size >= b1.base) {
|
|
/*
|
|
* Rotated frame (a.base at origin):
|
|
*
|
|
* 0 UT_MAX
|
|
* |--------------------------------------------------|
|
|
* [=== a ==================================] |
|
|
* [== b1 =====================]
|
|
*
|
|
* 0 UT_MAX
|
|
* |--------------------------------------------------|
|
|
* [=== a ==================================] |
|
|
* [== b1 ====]
|
|
* ^-- b1.base <= a.size
|
|
* |<-- a.size - dbase -->|
|
|
*
|
|
* 'a' and 'b' intersect as one cnum.
|
|
*/
|
|
return (struct cnum_t) {
|
|
.base = b.base,
|
|
.size = min((ut)(a.size - dbase), b.size),
|
|
};
|
|
} else {
|
|
return EMPTY;
|
|
}
|
|
}
|
|
|
|
void FN(intersect_with)(struct cnum_t *dst, struct cnum_t src)
|
|
{
|
|
*dst = FN(intersect)(*dst, src);
|
|
}
|
|
|
|
void FN(intersect_with_urange)(struct cnum_t *dst, ut min, ut max)
|
|
{
|
|
FN(intersect_with)(dst, FN(from_urange)(min, max));
|
|
}
|
|
|
|
void FN(intersect_with_srange)(struct cnum_t *dst, st min, st max)
|
|
{
|
|
FN(intersect_with)(dst, FN(from_srange)(min, max));
|
|
}
|
|
|
|
static inline struct cnum_t FN(normalize)(struct cnum_t cnum)
|
|
{
|
|
if (cnum.size == UT_MAX && cnum.base != 0 && cnum.base != (ut)ST_MAX)
|
|
cnum.base = 0;
|
|
return cnum;
|
|
}
|
|
|
|
struct cnum_t FN(add)(struct cnum_t a, struct cnum_t b)
|
|
{
|
|
if (FN(is_empty)(a) || FN(is_empty)(b))
|
|
return EMPTY;
|
|
if (a.size > UT_MAX - b.size)
|
|
return (struct cnum_t){ 0, (ut)UT_MAX };
|
|
else
|
|
return FN(normalize)((struct cnum_t){ a.base + b.base, a.size + b.size });
|
|
}
|
|
|
|
struct cnum_t FN(negate)(struct cnum_t a)
|
|
{
|
|
if (FN(is_empty)(a))
|
|
return EMPTY;
|
|
return FN(normalize)((struct cnum_t){ -((ut)a.base + a.size), a.size });
|
|
}
|
|
|
|
bool FN(is_empty)(struct cnum_t cnum)
|
|
{
|
|
return cnum.base == EMPTY.base && cnum.size == EMPTY.size;
|
|
}
|
|
|
|
bool FN(contains)(struct cnum_t cnum, ut v)
|
|
{
|
|
if (FN(is_empty)(cnum))
|
|
return false;
|
|
if (FN(urange_overflow)(cnum))
|
|
return v >= cnum.base || v <= (ut)cnum.base + cnum.size;
|
|
else
|
|
return v >= cnum.base && v <= (ut)cnum.base + cnum.size;
|
|
}
|
|
|
|
bool FN(is_const)(struct cnum_t cnum)
|
|
{
|
|
return cnum.size == 0;
|
|
}
|
|
|
|
bool FN(is_subset)(struct cnum_t bigger, struct cnum_t smaller)
|
|
{
|
|
if (FN(is_empty(smaller)))
|
|
return true;
|
|
if (FN(is_empty(bigger)))
|
|
return false;
|
|
/* rotate both arcs such that 'bigger' starts at origin, hence does not overflow */
|
|
smaller.base -= bigger.base;
|
|
bigger.base = 0;
|
|
if (FN(urange_overflow)(smaller) && bigger.size < UT_MAX)
|
|
return false;
|
|
return smaller.base + smaller.size <= bigger.size;
|
|
}
|
|
|
|
#undef EMPTY
|
|
#undef cnum_t
|
|
#undef ut
|
|
#undef st
|
|
#undef UT_MAX
|
|
#undef ST_MAX
|
|
#undef ST_MIN
|
|
#undef FN
|