Skip to content

Commit 3f36422

Browse files
LorenzoBianconiAlexei Starovoitov
authored and
Alexei Starovoitov
committed
net: xdp: introduce bpf_xdp_pointer utility routine
Similar to skb_header_pointer, introduce bpf_xdp_pointer utility routine to return a pointer to a given position in the xdp_buff if the requested area (offset + len) is contained in a contiguous memory area otherwise it will be copied in a bounce buffer provided by the caller. Similar to the tc counterpart, introduce the two following xdp helpers: - bpf_xdp_load_bytes - bpf_xdp_store_bytes Reviewed-by: Eelco Chaudron <[email protected]> Acked-by: Toke Hoiland-Jorgensen <[email protected]> Acked-by: John Fastabend <[email protected]> Acked-by: Jakub Kicinski <[email protected]> Signed-off-by: Lorenzo Bianconi <[email protected]> Link: https://lore.kernel.org/r/ab285c1efdd5b7a9d361348b1e7d3ef49f6382b3.1642758637.git.lorenzo@kernel.org Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent f45d5b6 commit 3f36422

File tree

3 files changed

+174
-38
lines changed

3 files changed

+174
-38
lines changed

include/uapi/linux/bpf.h

+18
Original file line numberDiff line numberDiff line change
@@ -5060,6 +5060,22 @@ union bpf_attr {
50605060
* Get the total size of a given xdp buff (linear and paged area)
50615061
* Return
50625062
* The total size of a given xdp buffer.
5063+
*
5064+
* long bpf_xdp_load_bytes(struct xdp_buff *xdp_md, u32 offset, void *buf, u32 len)
5065+
* Description
5066+
* This helper is provided as an easy way to load data from a
5067+
* xdp buffer. It can be used to load *len* bytes from *offset* from
5068+
* the frame associated to *xdp_md*, into the buffer pointed by
5069+
* *buf*.
5070+
* Return
5071+
* 0 on success, or a negative error in case of failure.
5072+
*
5073+
* long bpf_xdp_store_bytes(struct xdp_buff *xdp_md, u32 offset, void *buf, u32 len)
5074+
* Description
5075+
* Store *len* bytes from buffer *buf* into the frame
5076+
* associated to *xdp_md*, at *offset*.
5077+
* Return
5078+
* 0 on success, or a negative error in case of failure.
50635079
*/
50645080
#define __BPF_FUNC_MAPPER(FN) \
50655081
FN(unspec), \
@@ -5251,6 +5267,8 @@ union bpf_attr {
52515267
FN(get_retval), \
52525268
FN(set_retval), \
52535269
FN(xdp_get_buff_len), \
5270+
FN(xdp_load_bytes), \
5271+
FN(xdp_store_bytes), \
52545272
/* */
52555273

52565274
/* integer value in 'imm' field of BPF_CALL instruction selects which helper

net/core/filter.c

+138-38
Original file line numberDiff line numberDiff line change
@@ -3839,6 +3839,138 @@ static const struct bpf_func_proto bpf_xdp_adjust_head_proto = {
38393839
.arg2_type = ARG_ANYTHING,
38403840
};
38413841

3842+
static void bpf_xdp_copy_buf(struct xdp_buff *xdp, unsigned long off,
3843+
void *buf, unsigned long len, bool flush)
3844+
{
3845+
unsigned long ptr_len, ptr_off = 0;
3846+
skb_frag_t *next_frag, *end_frag;
3847+
struct skb_shared_info *sinfo;
3848+
void *src, *dst;
3849+
u8 *ptr_buf;
3850+
3851+
if (likely(xdp->data_end - xdp->data >= off + len)) {
3852+
src = flush ? buf : xdp->data + off;
3853+
dst = flush ? xdp->data + off : buf;
3854+
memcpy(dst, src, len);
3855+
return;
3856+
}
3857+
3858+
sinfo = xdp_get_shared_info_from_buff(xdp);
3859+
end_frag = &sinfo->frags[sinfo->nr_frags];
3860+
next_frag = &sinfo->frags[0];
3861+
3862+
ptr_len = xdp->data_end - xdp->data;
3863+
ptr_buf = xdp->data;
3864+
3865+
while (true) {
3866+
if (off < ptr_off + ptr_len) {
3867+
unsigned long copy_off = off - ptr_off;
3868+
unsigned long copy_len = min(len, ptr_len - copy_off);
3869+
3870+
src = flush ? buf : ptr_buf + copy_off;
3871+
dst = flush ? ptr_buf + copy_off : buf;
3872+
memcpy(dst, src, copy_len);
3873+
3874+
off += copy_len;
3875+
len -= copy_len;
3876+
buf += copy_len;
3877+
}
3878+
3879+
if (!len || next_frag == end_frag)
3880+
break;
3881+
3882+
ptr_off += ptr_len;
3883+
ptr_buf = skb_frag_address(next_frag);
3884+
ptr_len = skb_frag_size(next_frag);
3885+
next_frag++;
3886+
}
3887+
}
3888+
3889+
static void *bpf_xdp_pointer(struct xdp_buff *xdp, u32 offset, u32 len)
3890+
{
3891+
struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp);
3892+
u32 size = xdp->data_end - xdp->data;
3893+
void *addr = xdp->data;
3894+
int i;
3895+
3896+
if (unlikely(offset > 0xffff || len > 0xffff))
3897+
return ERR_PTR(-EFAULT);
3898+
3899+
if (offset + len > xdp_get_buff_len(xdp))
3900+
return ERR_PTR(-EINVAL);
3901+
3902+
if (offset < size) /* linear area */
3903+
goto out;
3904+
3905+
offset -= size;
3906+
for (i = 0; i < sinfo->nr_frags; i++) { /* paged area */
3907+
u32 frag_size = skb_frag_size(&sinfo->frags[i]);
3908+
3909+
if (offset < frag_size) {
3910+
addr = skb_frag_address(&sinfo->frags[i]);
3911+
size = frag_size;
3912+
break;
3913+
}
3914+
offset -= frag_size;
3915+
}
3916+
out:
3917+
return offset + len < size ? addr + offset : NULL;
3918+
}
3919+
3920+
BPF_CALL_4(bpf_xdp_load_bytes, struct xdp_buff *, xdp, u32, offset,
3921+
void *, buf, u32, len)
3922+
{
3923+
void *ptr;
3924+
3925+
ptr = bpf_xdp_pointer(xdp, offset, len);
3926+
if (IS_ERR(ptr))
3927+
return PTR_ERR(ptr);
3928+
3929+
if (!ptr)
3930+
bpf_xdp_copy_buf(xdp, offset, buf, len, false);
3931+
else
3932+
memcpy(buf, ptr, len);
3933+
3934+
return 0;
3935+
}
3936+
3937+
static const struct bpf_func_proto bpf_xdp_load_bytes_proto = {
3938+
.func = bpf_xdp_load_bytes,
3939+
.gpl_only = false,
3940+
.ret_type = RET_INTEGER,
3941+
.arg1_type = ARG_PTR_TO_CTX,
3942+
.arg2_type = ARG_ANYTHING,
3943+
.arg3_type = ARG_PTR_TO_UNINIT_MEM,
3944+
.arg4_type = ARG_CONST_SIZE,
3945+
};
3946+
3947+
BPF_CALL_4(bpf_xdp_store_bytes, struct xdp_buff *, xdp, u32, offset,
3948+
void *, buf, u32, len)
3949+
{
3950+
void *ptr;
3951+
3952+
ptr = bpf_xdp_pointer(xdp, offset, len);
3953+
if (IS_ERR(ptr))
3954+
return PTR_ERR(ptr);
3955+
3956+
if (!ptr)
3957+
bpf_xdp_copy_buf(xdp, offset, buf, len, true);
3958+
else
3959+
memcpy(ptr, buf, len);
3960+
3961+
return 0;
3962+
}
3963+
3964+
static const struct bpf_func_proto bpf_xdp_store_bytes_proto = {
3965+
.func = bpf_xdp_store_bytes,
3966+
.gpl_only = false,
3967+
.ret_type = RET_INTEGER,
3968+
.arg1_type = ARG_PTR_TO_CTX,
3969+
.arg2_type = ARG_ANYTHING,
3970+
.arg3_type = ARG_PTR_TO_UNINIT_MEM,
3971+
.arg4_type = ARG_CONST_SIZE,
3972+
};
3973+
38423974
static int bpf_xdp_frags_increase_tail(struct xdp_buff *xdp, int offset)
38433975
{
38443976
struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp);
@@ -4677,48 +4809,12 @@ static const struct bpf_func_proto bpf_sk_ancestor_cgroup_id_proto = {
46774809
};
46784810
#endif
46794811

4680-
static unsigned long bpf_xdp_copy(void *dst_buff, const void *ctx,
4812+
static unsigned long bpf_xdp_copy(void *dst, const void *ctx,
46814813
unsigned long off, unsigned long len)
46824814
{
46834815
struct xdp_buff *xdp = (struct xdp_buff *)ctx;
4684-
unsigned long ptr_len, ptr_off = 0;
4685-
skb_frag_t *next_frag, *end_frag;
4686-
struct skb_shared_info *sinfo;
4687-
u8 *ptr_buf;
4688-
4689-
if (likely(xdp->data_end - xdp->data >= off + len)) {
4690-
memcpy(dst_buff, xdp->data + off, len);
4691-
return 0;
4692-
}
4693-
4694-
sinfo = xdp_get_shared_info_from_buff(xdp);
4695-
end_frag = &sinfo->frags[sinfo->nr_frags];
4696-
next_frag = &sinfo->frags[0];
4697-
4698-
ptr_len = xdp->data_end - xdp->data;
4699-
ptr_buf = xdp->data;
4700-
4701-
while (true) {
4702-
if (off < ptr_off + ptr_len) {
4703-
unsigned long copy_off = off - ptr_off;
4704-
unsigned long copy_len = min(len, ptr_len - copy_off);
4705-
4706-
memcpy(dst_buff, ptr_buf + copy_off, copy_len);
4707-
4708-
off += copy_len;
4709-
len -= copy_len;
4710-
dst_buff += copy_len;
4711-
}
4712-
4713-
if (!len || next_frag == end_frag)
4714-
break;
4715-
4716-
ptr_off += ptr_len;
4717-
ptr_buf = skb_frag_address(next_frag);
4718-
ptr_len = skb_frag_size(next_frag);
4719-
next_frag++;
4720-
}
47214816

4817+
bpf_xdp_copy_buf(xdp, off, dst, len, false);
47224818
return 0;
47234819
}
47244820

@@ -7660,6 +7756,10 @@ xdp_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
76607756
return &bpf_xdp_adjust_tail_proto;
76617757
case BPF_FUNC_xdp_get_buff_len:
76627758
return &bpf_xdp_get_buff_len_proto;
7759+
case BPF_FUNC_xdp_load_bytes:
7760+
return &bpf_xdp_load_bytes_proto;
7761+
case BPF_FUNC_xdp_store_bytes:
7762+
return &bpf_xdp_store_bytes_proto;
76637763
case BPF_FUNC_fib_lookup:
76647764
return &bpf_xdp_fib_lookup_proto;
76657765
case BPF_FUNC_check_mtu:

tools/include/uapi/linux/bpf.h

+18
Original file line numberDiff line numberDiff line change
@@ -5060,6 +5060,22 @@ union bpf_attr {
50605060
* Get the total size of a given xdp buff (linear and paged area)
50615061
* Return
50625062
* The total size of a given xdp buffer.
5063+
*
5064+
* long bpf_xdp_load_bytes(struct xdp_buff *xdp_md, u32 offset, void *buf, u32 len)
5065+
* Description
5066+
* This helper is provided as an easy way to load data from a
5067+
* xdp buffer. It can be used to load *len* bytes from *offset* from
5068+
* the frame associated to *xdp_md*, into the buffer pointed by
5069+
* *buf*.
5070+
* Return
5071+
* 0 on success, or a negative error in case of failure.
5072+
*
5073+
* long bpf_xdp_store_bytes(struct xdp_buff *xdp_md, u32 offset, void *buf, u32 len)
5074+
* Description
5075+
* Store *len* bytes from buffer *buf* into the frame
5076+
* associated to *xdp_md*, at *offset*.
5077+
* Return
5078+
* 0 on success, or a negative error in case of failure.
50635079
*/
50645080
#define __BPF_FUNC_MAPPER(FN) \
50655081
FN(unspec), \
@@ -5251,6 +5267,8 @@ union bpf_attr {
52515267
FN(get_retval), \
52525268
FN(set_retval), \
52535269
FN(xdp_get_buff_len), \
5270+
FN(xdp_load_bytes), \
5271+
FN(xdp_store_bytes), \
52545272
/* */
52555273

52565274
/* integer value in 'imm' field of BPF_CALL instruction selects which helper

0 commit comments

Comments
 (0)