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 <echaudro@redhat.com> Acked-by: Toke Hoiland-Jorgensen <toke@redhat.com> Acked-by: John Fastabend <john.fastabend@gmail.com> Acked-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> Link: https://lore.kernel.org/r/ab285c1efdd5b7a9d361348b1e7d3ef49f6382b3.1642758637.git.lorenzo@kernel.org Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
parent
f45d5b6ce2
commit
3f364222d0
@ -5060,6 +5060,22 @@ union bpf_attr {
|
|||||||
* Get the total size of a given xdp buff (linear and paged area)
|
* Get the total size of a given xdp buff (linear and paged area)
|
||||||
* Return
|
* Return
|
||||||
* The total size of a given xdp buffer.
|
* The total size of a given xdp buffer.
|
||||||
|
*
|
||||||
|
* long bpf_xdp_load_bytes(struct xdp_buff *xdp_md, u32 offset, void *buf, u32 len)
|
||||||
|
* Description
|
||||||
|
* This helper is provided as an easy way to load data from a
|
||||||
|
* xdp buffer. It can be used to load *len* bytes from *offset* from
|
||||||
|
* the frame associated to *xdp_md*, into the buffer pointed by
|
||||||
|
* *buf*.
|
||||||
|
* Return
|
||||||
|
* 0 on success, or a negative error in case of failure.
|
||||||
|
*
|
||||||
|
* long bpf_xdp_store_bytes(struct xdp_buff *xdp_md, u32 offset, void *buf, u32 len)
|
||||||
|
* Description
|
||||||
|
* Store *len* bytes from buffer *buf* into the frame
|
||||||
|
* associated to *xdp_md*, at *offset*.
|
||||||
|
* Return
|
||||||
|
* 0 on success, or a negative error in case of failure.
|
||||||
*/
|
*/
|
||||||
#define __BPF_FUNC_MAPPER(FN) \
|
#define __BPF_FUNC_MAPPER(FN) \
|
||||||
FN(unspec), \
|
FN(unspec), \
|
||||||
@ -5251,6 +5267,8 @@ union bpf_attr {
|
|||||||
FN(get_retval), \
|
FN(get_retval), \
|
||||||
FN(set_retval), \
|
FN(set_retval), \
|
||||||
FN(xdp_get_buff_len), \
|
FN(xdp_get_buff_len), \
|
||||||
|
FN(xdp_load_bytes), \
|
||||||
|
FN(xdp_store_bytes), \
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
||||||
|
@ -3839,6 +3839,138 @@ static const struct bpf_func_proto bpf_xdp_adjust_head_proto = {
|
|||||||
.arg2_type = ARG_ANYTHING,
|
.arg2_type = ARG_ANYTHING,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void bpf_xdp_copy_buf(struct xdp_buff *xdp, unsigned long off,
|
||||||
|
void *buf, unsigned long len, bool flush)
|
||||||
|
{
|
||||||
|
unsigned long ptr_len, ptr_off = 0;
|
||||||
|
skb_frag_t *next_frag, *end_frag;
|
||||||
|
struct skb_shared_info *sinfo;
|
||||||
|
void *src, *dst;
|
||||||
|
u8 *ptr_buf;
|
||||||
|
|
||||||
|
if (likely(xdp->data_end - xdp->data >= off + len)) {
|
||||||
|
src = flush ? buf : xdp->data + off;
|
||||||
|
dst = flush ? xdp->data + off : buf;
|
||||||
|
memcpy(dst, src, len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sinfo = xdp_get_shared_info_from_buff(xdp);
|
||||||
|
end_frag = &sinfo->frags[sinfo->nr_frags];
|
||||||
|
next_frag = &sinfo->frags[0];
|
||||||
|
|
||||||
|
ptr_len = xdp->data_end - xdp->data;
|
||||||
|
ptr_buf = xdp->data;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (off < ptr_off + ptr_len) {
|
||||||
|
unsigned long copy_off = off - ptr_off;
|
||||||
|
unsigned long copy_len = min(len, ptr_len - copy_off);
|
||||||
|
|
||||||
|
src = flush ? buf : ptr_buf + copy_off;
|
||||||
|
dst = flush ? ptr_buf + copy_off : buf;
|
||||||
|
memcpy(dst, src, copy_len);
|
||||||
|
|
||||||
|
off += copy_len;
|
||||||
|
len -= copy_len;
|
||||||
|
buf += copy_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!len || next_frag == end_frag)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ptr_off += ptr_len;
|
||||||
|
ptr_buf = skb_frag_address(next_frag);
|
||||||
|
ptr_len = skb_frag_size(next_frag);
|
||||||
|
next_frag++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *bpf_xdp_pointer(struct xdp_buff *xdp, u32 offset, u32 len)
|
||||||
|
{
|
||||||
|
struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp);
|
||||||
|
u32 size = xdp->data_end - xdp->data;
|
||||||
|
void *addr = xdp->data;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (unlikely(offset > 0xffff || len > 0xffff))
|
||||||
|
return ERR_PTR(-EFAULT);
|
||||||
|
|
||||||
|
if (offset + len > xdp_get_buff_len(xdp))
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
|
if (offset < size) /* linear area */
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
offset -= size;
|
||||||
|
for (i = 0; i < sinfo->nr_frags; i++) { /* paged area */
|
||||||
|
u32 frag_size = skb_frag_size(&sinfo->frags[i]);
|
||||||
|
|
||||||
|
if (offset < frag_size) {
|
||||||
|
addr = skb_frag_address(&sinfo->frags[i]);
|
||||||
|
size = frag_size;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
offset -= frag_size;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
return offset + len < size ? addr + offset : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
BPF_CALL_4(bpf_xdp_load_bytes, struct xdp_buff *, xdp, u32, offset,
|
||||||
|
void *, buf, u32, len)
|
||||||
|
{
|
||||||
|
void *ptr;
|
||||||
|
|
||||||
|
ptr = bpf_xdp_pointer(xdp, offset, len);
|
||||||
|
if (IS_ERR(ptr))
|
||||||
|
return PTR_ERR(ptr);
|
||||||
|
|
||||||
|
if (!ptr)
|
||||||
|
bpf_xdp_copy_buf(xdp, offset, buf, len, false);
|
||||||
|
else
|
||||||
|
memcpy(buf, ptr, len);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct bpf_func_proto bpf_xdp_load_bytes_proto = {
|
||||||
|
.func = bpf_xdp_load_bytes,
|
||||||
|
.gpl_only = false,
|
||||||
|
.ret_type = RET_INTEGER,
|
||||||
|
.arg1_type = ARG_PTR_TO_CTX,
|
||||||
|
.arg2_type = ARG_ANYTHING,
|
||||||
|
.arg3_type = ARG_PTR_TO_UNINIT_MEM,
|
||||||
|
.arg4_type = ARG_CONST_SIZE,
|
||||||
|
};
|
||||||
|
|
||||||
|
BPF_CALL_4(bpf_xdp_store_bytes, struct xdp_buff *, xdp, u32, offset,
|
||||||
|
void *, buf, u32, len)
|
||||||
|
{
|
||||||
|
void *ptr;
|
||||||
|
|
||||||
|
ptr = bpf_xdp_pointer(xdp, offset, len);
|
||||||
|
if (IS_ERR(ptr))
|
||||||
|
return PTR_ERR(ptr);
|
||||||
|
|
||||||
|
if (!ptr)
|
||||||
|
bpf_xdp_copy_buf(xdp, offset, buf, len, true);
|
||||||
|
else
|
||||||
|
memcpy(ptr, buf, len);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct bpf_func_proto bpf_xdp_store_bytes_proto = {
|
||||||
|
.func = bpf_xdp_store_bytes,
|
||||||
|
.gpl_only = false,
|
||||||
|
.ret_type = RET_INTEGER,
|
||||||
|
.arg1_type = ARG_PTR_TO_CTX,
|
||||||
|
.arg2_type = ARG_ANYTHING,
|
||||||
|
.arg3_type = ARG_PTR_TO_UNINIT_MEM,
|
||||||
|
.arg4_type = ARG_CONST_SIZE,
|
||||||
|
};
|
||||||
|
|
||||||
static int bpf_xdp_frags_increase_tail(struct xdp_buff *xdp, int offset)
|
static int bpf_xdp_frags_increase_tail(struct xdp_buff *xdp, int offset)
|
||||||
{
|
{
|
||||||
struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp);
|
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 = {
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static unsigned long bpf_xdp_copy(void *dst_buff, const void *ctx,
|
static unsigned long bpf_xdp_copy(void *dst, const void *ctx,
|
||||||
unsigned long off, unsigned long len)
|
unsigned long off, unsigned long len)
|
||||||
{
|
{
|
||||||
struct xdp_buff *xdp = (struct xdp_buff *)ctx;
|
struct xdp_buff *xdp = (struct xdp_buff *)ctx;
|
||||||
unsigned long ptr_len, ptr_off = 0;
|
|
||||||
skb_frag_t *next_frag, *end_frag;
|
|
||||||
struct skb_shared_info *sinfo;
|
|
||||||
u8 *ptr_buf;
|
|
||||||
|
|
||||||
if (likely(xdp->data_end - xdp->data >= off + len)) {
|
|
||||||
memcpy(dst_buff, xdp->data + off, len);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
sinfo = xdp_get_shared_info_from_buff(xdp);
|
|
||||||
end_frag = &sinfo->frags[sinfo->nr_frags];
|
|
||||||
next_frag = &sinfo->frags[0];
|
|
||||||
|
|
||||||
ptr_len = xdp->data_end - xdp->data;
|
|
||||||
ptr_buf = xdp->data;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (off < ptr_off + ptr_len) {
|
|
||||||
unsigned long copy_off = off - ptr_off;
|
|
||||||
unsigned long copy_len = min(len, ptr_len - copy_off);
|
|
||||||
|
|
||||||
memcpy(dst_buff, ptr_buf + copy_off, copy_len);
|
|
||||||
|
|
||||||
off += copy_len;
|
|
||||||
len -= copy_len;
|
|
||||||
dst_buff += copy_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!len || next_frag == end_frag)
|
|
||||||
break;
|
|
||||||
|
|
||||||
ptr_off += ptr_len;
|
|
||||||
ptr_buf = skb_frag_address(next_frag);
|
|
||||||
ptr_len = skb_frag_size(next_frag);
|
|
||||||
next_frag++;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
bpf_xdp_copy_buf(xdp, off, dst, len, false);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7660,6 +7756,10 @@ xdp_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
|||||||
return &bpf_xdp_adjust_tail_proto;
|
return &bpf_xdp_adjust_tail_proto;
|
||||||
case BPF_FUNC_xdp_get_buff_len:
|
case BPF_FUNC_xdp_get_buff_len:
|
||||||
return &bpf_xdp_get_buff_len_proto;
|
return &bpf_xdp_get_buff_len_proto;
|
||||||
|
case BPF_FUNC_xdp_load_bytes:
|
||||||
|
return &bpf_xdp_load_bytes_proto;
|
||||||
|
case BPF_FUNC_xdp_store_bytes:
|
||||||
|
return &bpf_xdp_store_bytes_proto;
|
||||||
case BPF_FUNC_fib_lookup:
|
case BPF_FUNC_fib_lookup:
|
||||||
return &bpf_xdp_fib_lookup_proto;
|
return &bpf_xdp_fib_lookup_proto;
|
||||||
case BPF_FUNC_check_mtu:
|
case BPF_FUNC_check_mtu:
|
||||||
|
@ -5060,6 +5060,22 @@ union bpf_attr {
|
|||||||
* Get the total size of a given xdp buff (linear and paged area)
|
* Get the total size of a given xdp buff (linear and paged area)
|
||||||
* Return
|
* Return
|
||||||
* The total size of a given xdp buffer.
|
* The total size of a given xdp buffer.
|
||||||
|
*
|
||||||
|
* long bpf_xdp_load_bytes(struct xdp_buff *xdp_md, u32 offset, void *buf, u32 len)
|
||||||
|
* Description
|
||||||
|
* This helper is provided as an easy way to load data from a
|
||||||
|
* xdp buffer. It can be used to load *len* bytes from *offset* from
|
||||||
|
* the frame associated to *xdp_md*, into the buffer pointed by
|
||||||
|
* *buf*.
|
||||||
|
* Return
|
||||||
|
* 0 on success, or a negative error in case of failure.
|
||||||
|
*
|
||||||
|
* long bpf_xdp_store_bytes(struct xdp_buff *xdp_md, u32 offset, void *buf, u32 len)
|
||||||
|
* Description
|
||||||
|
* Store *len* bytes from buffer *buf* into the frame
|
||||||
|
* associated to *xdp_md*, at *offset*.
|
||||||
|
* Return
|
||||||
|
* 0 on success, or a negative error in case of failure.
|
||||||
*/
|
*/
|
||||||
#define __BPF_FUNC_MAPPER(FN) \
|
#define __BPF_FUNC_MAPPER(FN) \
|
||||||
FN(unspec), \
|
FN(unspec), \
|
||||||
@ -5251,6 +5267,8 @@ union bpf_attr {
|
|||||||
FN(get_retval), \
|
FN(get_retval), \
|
||||||
FN(set_retval), \
|
FN(set_retval), \
|
||||||
FN(xdp_get_buff_len), \
|
FN(xdp_get_buff_len), \
|
||||||
|
FN(xdp_load_bytes), \
|
||||||
|
FN(xdp_store_bytes), \
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
||||||
|
Loading…
Reference in New Issue
Block a user