mirror of
https://github.com/netdata/libbpf.git
synced 2026-03-16 14:29:06 +08:00
Compare commits
141 Commits
v0.4.0
...
v0.4.0_net
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aea40f7179 | ||
|
|
8bdc267e7b | ||
|
|
d0c398be4f | ||
|
|
7d9cc837ef | ||
|
|
a3c0cc19d4 | ||
|
|
7c6d34a2c9 | ||
|
|
a69c52bb11 | ||
|
|
6d67d53143 | ||
|
|
91259bc676 | ||
|
|
a3f8c5a306 | ||
|
|
d23679b415 | ||
|
|
9923f25600 | ||
|
|
40160ed4d4 | ||
|
|
7b22fc4cdb | ||
|
|
152882e17a | ||
|
|
0e7520949e | ||
|
|
c3f7daaab5 | ||
|
|
1a1e7a0612 | ||
|
|
827963ffb3 | ||
|
|
4ab24e7d62 | ||
|
|
b2a63c974d | ||
|
|
88649fe655 | ||
|
|
1778e0b1bd | ||
|
|
64f027efda | ||
|
|
6bf8babb33 | ||
|
|
70ad3e8314 | ||
|
|
dbdd8f3b34 | ||
|
|
52e96052a2 | ||
|
|
41db5534d8 | ||
|
|
54a7bc87d5 | ||
|
|
9979463ccf | ||
|
|
b91ca01922 | ||
|
|
8ded7c6db0 | ||
|
|
7df4ea0f0d | ||
|
|
02efadd0b0 | ||
|
|
02333ba360 | ||
|
|
2805c2a4ca | ||
|
|
6921017d25 | ||
|
|
e65d128903 | ||
|
|
512b472d97 | ||
|
|
73788dd22f | ||
|
|
a180eb551e | ||
|
|
9d2b7e471b | ||
|
|
3a0fc666ef | ||
|
|
7c25b1d569 | ||
|
|
d41e821ccf | ||
|
|
2fe57e40ac | ||
|
|
f81dbd3475 | ||
|
|
035fd6aca0 | ||
|
|
e44c8486c6 | ||
|
|
14f5433b2e | ||
|
|
d7a2de020b | ||
|
|
bb92e7ab4d | ||
|
|
3f22535d56 | ||
|
|
f8ab8bde8e | ||
|
|
506a544834 | ||
|
|
ec2c78c034 | ||
|
|
030ff87857 | ||
|
|
0db006d28e | ||
|
|
6e6f18ac5d | ||
|
|
deca7932c3 | ||
|
|
ebcae72279 | ||
|
|
64362b8896 | ||
|
|
df01b246df | ||
|
|
6eb5e25905 | ||
|
|
a603965dad | ||
|
|
f61c3b318b | ||
|
|
8235032464 | ||
|
|
dc2c53b7f6 | ||
|
|
fb3809e940 | ||
|
|
74d3571880 | ||
|
|
be570b29c1 | ||
|
|
9aa71e1040 | ||
|
|
b3ffd258fc | ||
|
|
4447ac82d4 | ||
|
|
8fa229c455 | ||
|
|
8a670b7422 | ||
|
|
21f90f61b0 | ||
|
|
c8b1d14b03 | ||
|
|
c0b2ceba1d | ||
|
|
bd25fc7df1 | ||
|
|
4920031c88 | ||
|
|
8fa50e86c1 | ||
|
|
330a158982 | ||
|
|
a524ae0bbf | ||
|
|
97e2a9c9a1 | ||
|
|
bef77595ca | ||
|
|
6f7839f477 | ||
|
|
90aba5e582 | ||
|
|
4dc3aeb072 | ||
|
|
4ce0551ee5 | ||
|
|
f8411901c4 | ||
|
|
9ff2b76693 | ||
|
|
df023f5cfc | ||
|
|
ae62c159ec | ||
|
|
8bf016110e | ||
|
|
d3e4039a0a | ||
|
|
dd34504b43 | ||
|
|
bec2ae0c6e | ||
|
|
1d6106cf45 | ||
|
|
95e51c1dbe | ||
|
|
db132757c9 | ||
|
|
41cddf18f4 | ||
|
|
f883bbf3f4 | ||
|
|
db8982bcaa | ||
|
|
d1571ab5ce | ||
|
|
03b0787342 | ||
|
|
a1bd8104a9 | ||
|
|
ccead28901 | ||
|
|
0b59d75ecd | ||
|
|
a5ee05d505 | ||
|
|
42ebbbce7d | ||
|
|
26497b9a88 | ||
|
|
5d5af3f07e | ||
|
|
899c45baa2 | ||
|
|
95008d47dd | ||
|
|
13acc0af00 | ||
|
|
1b9138e452 | ||
|
|
2da7f66d3f | ||
|
|
9d5ac4931d | ||
|
|
5bfbb36440 | ||
|
|
343f63e245 | ||
|
|
0dccb885a3 | ||
|
|
8e3a63ea48 | ||
|
|
7c7ba067fc | ||
|
|
12eb2666d9 | ||
|
|
234dea015b | ||
|
|
c3c2e52201 | ||
|
|
b79c698300 | ||
|
|
546199a723 | ||
|
|
b44566c71b | ||
|
|
594960b3db | ||
|
|
694a70c522 | ||
|
|
c96f2f1b29 | ||
|
|
ac2095783a | ||
|
|
fecf2cf6dd | ||
|
|
c1f36fb3e3 | ||
|
|
6eac86910c | ||
|
|
64a654f398 | ||
|
|
34eb4fb3f1 | ||
|
|
007709011e |
16
.github/actions/debian/action.yml
vendored
Normal file
16
.github/actions/debian/action.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
name: 'debian'
|
||||
description: 'Build'
|
||||
inputs:
|
||||
target:
|
||||
description: 'Run target'
|
||||
required: true
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- run: |
|
||||
source /tmp/ci_setup
|
||||
bash -x $CI_ROOT/managers/debian.sh SETUP
|
||||
bash -x $CI_ROOT/managers/debian.sh ${{ inputs.target }}
|
||||
bash -x $CI_ROOT/managers/debian.sh CLEANUP
|
||||
shell: bash
|
||||
|
||||
23
.github/actions/setup/action.yml
vendored
Normal file
23
.github/actions/setup/action.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: 'setup'
|
||||
description: 'setup env, create /tmp/ci_setup'
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- id: variables
|
||||
run: |
|
||||
export REPO_ROOT=$GITHUB_WORKSPACE
|
||||
export CI_ROOT=$REPO_ROOT/travis-ci
|
||||
# this is somewhat ugly, but that is the easiest way to share this code with
|
||||
# arch specific docker
|
||||
echo 'echo ::group::Env setup' > /tmp/ci_setup
|
||||
echo export DEBIAN_FRONTEND=noninteractive >> /tmp/ci_setup
|
||||
echo sudo apt-get update >> /tmp/ci_setup
|
||||
echo sudo apt-get install -y aptitude qemu-kvm zstd binutils-dev elfutils libcap-dev libelf-dev libdw-dev >> /tmp/ci_setup
|
||||
echo export PROJECT_NAME='libbpf' >> /tmp/ci_setup
|
||||
echo export AUTHOR_EMAIL="$(git log -1 --pretty=\"%aE\")" >> /tmp/ci_setup
|
||||
echo export REPO_ROOT=$GITHUB_WORKSPACE >> /tmp/ci_setup
|
||||
echo export CI_ROOT=$REPO_ROOT/travis-ci >> /tmp/ci_setup
|
||||
echo export VMTEST_ROOT=$CI_ROOT/vmtest >> /tmp/ci_setup
|
||||
echo 'echo ::endgroup::' >> /tmp/ci_setup
|
||||
shell: bash
|
||||
|
||||
35
.github/actions/vmtest/action.yml
vendored
Normal file
35
.github/actions/vmtest/action.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
name: 'vmtest'
|
||||
description: 'Build + run vmtest'
|
||||
inputs:
|
||||
kernel:
|
||||
description: 'kernel version or LATEST'
|
||||
required: true
|
||||
default: 'LATEST'
|
||||
kernel-rev:
|
||||
description: 'CHECKPOINT or rev/tag/branch'
|
||||
required: true
|
||||
default: 'CHECKPOINT'
|
||||
kernel-origin:
|
||||
description: 'kernel repo'
|
||||
required: true
|
||||
default: 'https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git'
|
||||
pahole:
|
||||
description: 'pahole rev/tag/branch'
|
||||
required: true
|
||||
default: 'master'
|
||||
pahole-origin:
|
||||
description: 'pahole repo'
|
||||
required: true
|
||||
default: 'https://git.kernel.org/pub/scm/devel/pahole/pahole.git'
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- run: |
|
||||
source /tmp/ci_setup
|
||||
export KERNEL=${{ inputs.kernel }}
|
||||
export KERNEL_BRANCH=${{ inputs.kernel-rev }}
|
||||
export KERNEL_ORIGIN=${{ inputs.kernel-origin }}
|
||||
export PAHOLE_BRANCH=${{ inputs.pahole }}
|
||||
export PAHOLE_ORIGIN=${{ inputs.pahole-origin }}
|
||||
$CI_ROOT/vmtest/run_vmtest.sh
|
||||
shell: bash
|
||||
30
.github/workflows/coverity.yml
vendored
Normal file
30
.github/workflows/coverity.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: libbpf-ci-coverity
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 18 * * *'
|
||||
|
||||
|
||||
jobs:
|
||||
coverity:
|
||||
runs-on: ubuntu-latest
|
||||
name: Coverity
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/actions/setup
|
||||
- name: Run coverity
|
||||
run: |
|
||||
echo ::group::Setup CI env
|
||||
source /tmp/ci_setup
|
||||
export COVERITY_SCAN_NOTIFICATION_EMAIL="${AUTHOR_EMAIL}"
|
||||
export COVERITY_SCAN_BRANCH_PATTERN=${GITHUB_REF##refs/*/}
|
||||
export TRAVIS_BRANCH=${COVERITY_SCAN_BRANCH_PATTERN}
|
||||
echo ::endgroup::
|
||||
scripts/coverity.sh
|
||||
env:
|
||||
COVERITY_SCAN_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
|
||||
COVERITY_SCAN_PROJECT_NAME: libbpf
|
||||
COVERITY_SCAN_BUILD_COMMAND_PREPEND: 'cd src/'
|
||||
COVERITY_SCAN_BUILD_COMMAND: 'make'
|
||||
- name: SCM log
|
||||
run: cat /home/runner/work/libbpf/libbpf/src/cov-int/scm_log.txt
|
||||
36
.github/workflows/ondemand.yml
vendored
Normal file
36
.github/workflows/ondemand.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: ondemand
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
kernel-origin:
|
||||
description: 'git repo for linux kernel'
|
||||
default: 'https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git'
|
||||
required: true
|
||||
kernel-rev:
|
||||
description: 'rev/tag/branch for linux kernel'
|
||||
default: "master"
|
||||
required: true
|
||||
pahole-origin:
|
||||
description: 'git repo for pahole'
|
||||
default: 'https://git.kernel.org/pub/scm/devel/pahole/pahole.git'
|
||||
required: true
|
||||
pahole-rev:
|
||||
description: 'ref/tag/branch for pahole'
|
||||
default: "master"
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
vmtest:
|
||||
runs-on: ubuntu-latest
|
||||
name: vmtest with customized pahole/Kernel
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/actions/setup
|
||||
- uses: ./.github/actions/vmtest
|
||||
with:
|
||||
kernel: 'LATEST'
|
||||
kernel-rev: ${{ github.event.inputs.kernel-rev }}
|
||||
kernel-origin: ${{ github.event.inputs.kernel-origin }}
|
||||
pahole: ${{ github.event.inputs.pahole-rev }}
|
||||
pahole-origin: ${{ github.event.inputs.pahole-origin }}
|
||||
40
.github/workflows/pahole.yml
vendored
Normal file
40
.github/workflows/pahole.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
name: pahole-staging
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 18 * * *'
|
||||
|
||||
|
||||
jobs:
|
||||
vmtest:
|
||||
runs-on: ubuntu-latest
|
||||
name: Kernel LATEST + staging pahole
|
||||
env:
|
||||
STAGING: tmp.master
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/actions/setup
|
||||
- name: Get current pahole sha
|
||||
id: current_sha
|
||||
run:
|
||||
git ls-remote https://git.kernel.org/pub/scm/devel/pahole/pahole.git $STAGING | awk '{print "::set-output name=sha::" $1}'
|
||||
- name: Get latest result for this sha
|
||||
id: latest
|
||||
uses: pat-s/always-upload-cache@v2
|
||||
with:
|
||||
path: last_tested_pahole
|
||||
key: ${{ steps.current_sha.outputs.sha }}
|
||||
- name: Return cached test result
|
||||
run: exit `cat last_tested_pahole || echo 1` # if file is empty that mean previous run timed out of canceled, returning failure
|
||||
if: steps.latest.outputs.cache-hit == 'true'
|
||||
- uses: ./.github/actions/vmtest
|
||||
with:
|
||||
kernel: LATEST
|
||||
pahole: $STAGING
|
||||
if: steps.latest.outputs.cache-hit != 'true'
|
||||
- name: Save success
|
||||
run: echo 0 > last_tested_pahole
|
||||
if: steps.latest.outputs.cache-hit != 'true'
|
||||
- name: Save failure
|
||||
run: echo 1 > last_tested_pahole
|
||||
if: ${{ failure() && steps.latest.outputs.cache-hit != 'true' }}
|
||||
100
.github/workflows/test.yml
vendored
Normal file
100
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
name: libbpf-ci
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
schedule:
|
||||
- cron: '0 18 * * *'
|
||||
|
||||
concurrency:
|
||||
group: ci-test-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
vmtest:
|
||||
runs-on: ubuntu-latest
|
||||
name: Kernel ${{ matrix.kernel }} + selftests
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- kernel: 'LATEST'
|
||||
- kernel: '5.5.0'
|
||||
- kernel: '4.9.0'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
name: Checkout
|
||||
- uses: ./.github/actions/setup
|
||||
name: Setup
|
||||
- uses: ./.github/actions/vmtest
|
||||
name: vmtest
|
||||
with:
|
||||
kernel: ${{ matrix.kernel }}
|
||||
|
||||
debian:
|
||||
runs-on: ubuntu-latest
|
||||
name: Debian Build (${{ matrix.name }})
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- name: default
|
||||
target: RUN
|
||||
- name: ASan+UBSan
|
||||
target: RUN_ASAN
|
||||
- name: clang
|
||||
target: RUN_CLANG
|
||||
- name: clang ASan+UBSan
|
||||
target: RUN_CLANG_ASAN
|
||||
- name: gcc-10
|
||||
target: RUN_GCC10
|
||||
- name: gcc-10 ASan+UBSan
|
||||
target: RUN_GCC10_ASAN
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
name: Checkout
|
||||
- uses: ./.github/actions/setup
|
||||
name: Setup
|
||||
- uses: ./.github/actions/debian
|
||||
name: Build
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
|
||||
ubuntu:
|
||||
runs-on: ubuntu-latest
|
||||
name: Ubuntu Focal Build (${{ matrix.arch }})
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- arch: aarch64
|
||||
- arch: ppc64le
|
||||
- arch: s390x
|
||||
- arch: x86
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
name: Checkout
|
||||
- uses: ./.github/actions/setup
|
||||
name: Pre-Setup
|
||||
- run: source /tmp/ci_setup && sudo -E $CI_ROOT/managers/ubuntu.sh
|
||||
if: matrix.arch == 'x86'
|
||||
name: Setup
|
||||
- uses: uraimo/run-on-arch-action@v2.0.5
|
||||
name: Build in docker
|
||||
if: matrix.arch != 'x86'
|
||||
with:
|
||||
distro:
|
||||
ubuntu20.04
|
||||
arch:
|
||||
${{ matrix.arch }}
|
||||
setup:
|
||||
cp /tmp/ci_setup $GITHUB_WORKSPACE
|
||||
dockerRunArgs: |
|
||||
--volume "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}"
|
||||
shell: /bin/bash
|
||||
install: |
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
export TZ="America/Los_Angeles"
|
||||
apt-get update -y
|
||||
apt-get install -y tzdata build-essential sudo
|
||||
run: source ${GITHUB_WORKSPACE}/ci_setup && $CI_ROOT/managers/ubuntu.sh
|
||||
17
.readthedocs.yaml
Normal file
17
.readthedocs.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
# .readthedocs.yaml
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
builder: html
|
||||
configuration: docs/conf.py
|
||||
|
||||
# Optionally set the version of Python and requirements required to build your docs
|
||||
python:
|
||||
version: 3.7
|
||||
install:
|
||||
- requirements: docs/sphinx/requirements.txt
|
||||
@@ -1 +1 @@
|
||||
d0c0fe10ce6d87734b65c18dc8f4bcae3f4dbea4
|
||||
3776f3517ed94d40ff0e3851d7ce2ce17b63099f
|
||||
|
||||
@@ -1 +1 @@
|
||||
f18ba26da88a89db9b50cb4ff47fadb159f2810b
|
||||
d20b41115ad53293201cc07ee429a38740cb056b
|
||||
|
||||
29
README.md
29
README.md
@@ -1,3 +1,15 @@
|
||||
This is a mirror of [bpf-next Linux source
|
||||
tree](https://kernel.googlesource.com/pub/scm/linux/kernel/git/bpf/bpf-next)'s
|
||||
`tools/lib/bpf` directory plus its supporting header files.
|
||||
|
||||
All the gory details of syncing can be found in `scripts/sync-kernel.sh`
|
||||
script.
|
||||
|
||||
Some header files in this repo (`include/linux/*.h`) are reduced versions of
|
||||
their counterpart files at
|
||||
[bpf-next](https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/)'s
|
||||
`tools/include/linux/*.h` to make compilation successful.
|
||||
|
||||
BPF/libbpf usage and questions
|
||||
==============================
|
||||
|
||||
@@ -20,7 +32,7 @@ should be opened only for dealing with issues pertaining to specific way this
|
||||
libbpf mirror repo is set up and organized.
|
||||
|
||||
Build
|
||||
[](https://travis-ci.com/github/libbpf/libbpf)
|
||||
[](https://github.com/libbpf/libbpf/actions/workflows/test.yml)
|
||||
[](https://lgtm.com/projects/g/libbpf/libbpf/alerts/)
|
||||
[](https://scan.coverity.com/projects/libbpf)
|
||||
=====
|
||||
@@ -98,6 +110,7 @@ Some major Linux distributions come with kernel BTF already built in:
|
||||
- RHEL 8.2+
|
||||
- OpenSUSE Tumbleweed (in the next release, as of 2020-06-04)
|
||||
- Arch Linux (from kernel 5.7.1.arch1-1)
|
||||
- Manjaro (from kernel 5.4 if compiled after 2021-06-18)
|
||||
- Ubuntu 20.10
|
||||
- Debian 11 (amd64/arm64)
|
||||
|
||||
@@ -133,20 +146,6 @@ use it:
|
||||
contain lots of real-world tools converted from BCC to BPF CO-RE. Consider
|
||||
converting some more to both contribute to the BPF community and gain some
|
||||
more experience with it.
|
||||
|
||||
Details
|
||||
=======
|
||||
This is a mirror of [bpf-next Linux source
|
||||
tree](https://kernel.googlesource.com/pub/scm/linux/kernel/git/bpf/bpf-next)'s
|
||||
`tools/lib/bpf` directory plus its supporting header files.
|
||||
|
||||
All the gory details of syncing can be found in `scripts/sync-kernel.sh`
|
||||
script.
|
||||
|
||||
Some header files in this repo (`include/linux/*.h`) are reduced versions of
|
||||
their counterpart files at
|
||||
[bpf-next](https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/)'s
|
||||
`tools/include/linux/*.h` to make compilation successful.
|
||||
|
||||
License
|
||||
=======
|
||||
|
||||
2
docs/.gitignore
vendored
Normal file
2
docs/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
sphinx/build
|
||||
sphinx/doxygen/build
|
||||
51
docs/api.rst
Normal file
51
docs/api.rst
Normal file
@@ -0,0 +1,51 @@
|
||||
.. SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
|
||||
.. _api:
|
||||
|
||||
.. toctree:: Table of Contents
|
||||
|
||||
|
||||
LIBBPF API
|
||||
==================
|
||||
|
||||
libbpf.h
|
||||
--------
|
||||
.. doxygenfile:: libbpf.h
|
||||
:project: libbpf
|
||||
:sections: func define public-type
|
||||
|
||||
bpf.h
|
||||
-----
|
||||
.. doxygenfile:: bpf.h
|
||||
:project: libbpf
|
||||
:sections: func define public-type
|
||||
|
||||
btf.h
|
||||
-----
|
||||
.. doxygenfile:: btf.h
|
||||
:project: libbpf
|
||||
:sections: func define public-type
|
||||
|
||||
xsk.h
|
||||
-----
|
||||
.. doxygenfile:: xsk.h
|
||||
:project: libbpf
|
||||
:sections: func define public-type
|
||||
|
||||
bpf_tracing.h
|
||||
-------------
|
||||
.. doxygenfile:: bpf_tracing.h
|
||||
:project: libbpf
|
||||
:sections: func define public-type
|
||||
|
||||
bpf_core_read.h
|
||||
---------------
|
||||
.. doxygenfile:: bpf_core_read.h
|
||||
:project: libbpf
|
||||
:sections: func define public-type
|
||||
|
||||
bpf_endian.h
|
||||
------------
|
||||
.. doxygenfile:: bpf_endian.h
|
||||
:project: libbpf
|
||||
:sections: func define public-type
|
||||
40
docs/conf.py
Normal file
40
docs/conf.py
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file only contains a selection of the most common options. For a full
|
||||
# list see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
project = "libbpf"
|
||||
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.doctest',
|
||||
'sphinx.ext.mathjax',
|
||||
'sphinx.ext.viewcode',
|
||||
'sphinx.ext.imgmath',
|
||||
'sphinx.ext.todo',
|
||||
'breathe',
|
||||
]
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = []
|
||||
|
||||
read_the_docs_build = os.environ.get('READTHEDOCS', None) == 'True'
|
||||
|
||||
if read_the_docs_build:
|
||||
subprocess.call('cd sphinx ; make clean', shell=True)
|
||||
subprocess.call('cd sphinx/doxygen ; doxygen', shell=True)
|
||||
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
|
||||
breathe_projects = { "libbpf": "./sphinx/doxygen/build/xml/" }
|
||||
breathe_default_project = "libbpf"
|
||||
breathe_show_define_initializer = True
|
||||
breathe_show_enumvalue_initializer = True
|
||||
22
docs/index.rst
Normal file
22
docs/index.rst
Normal file
@@ -0,0 +1,22 @@
|
||||
.. SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
|
||||
libbpf
|
||||
======
|
||||
|
||||
For API documentation see the `versioned API documentation site <https://libbpf.readthedocs.io/en/latest/api.html>`_.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
libbpf_naming_convention
|
||||
libbpf_build
|
||||
|
||||
This is documentation for libbpf, a userspace library for loading and
|
||||
interacting with bpf programs.
|
||||
|
||||
All general BPF questions, including kernel functionality, libbpf APIs and
|
||||
their application, should be sent to bpf@vger.kernel.org mailing list.
|
||||
You can `subscribe <http://vger.kernel.org/vger-lists.html#bpf>`_ to the
|
||||
mailing list search its `archive <https://lore.kernel.org/bpf/>`_.
|
||||
Please search the archive before asking new questions. It very well might
|
||||
be that this was already addressed or answered before.
|
||||
37
docs/libbpf_build.rst
Normal file
37
docs/libbpf_build.rst
Normal file
@@ -0,0 +1,37 @@
|
||||
.. SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
|
||||
Building libbpf
|
||||
===============
|
||||
|
||||
libelf and zlib are internal dependencies of libbpf and thus are required to link
|
||||
against and must be installed on the system for applications to work.
|
||||
pkg-config is used by default to find libelf, and the program called
|
||||
can be overridden with PKG_CONFIG.
|
||||
|
||||
If using pkg-config at build time is not desired, it can be disabled by
|
||||
setting NO_PKG_CONFIG=1 when calling make.
|
||||
|
||||
To build both static libbpf.a and shared libbpf.so:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ cd src
|
||||
$ make
|
||||
|
||||
To build only static libbpf.a library in directory build/ and install them
|
||||
together with libbpf headers in a staging directory root/:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ cd src
|
||||
$ mkdir build root
|
||||
$ BUILD_STATIC_ONLY=y OBJDIR=build DESTDIR=root make install
|
||||
|
||||
To build both static libbpf.a and shared libbpf.so against a custom libelf
|
||||
dependency installed in /build/root/ and install them together with libbpf
|
||||
headers in a build directory /build/root/:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ cd src
|
||||
$ PKG_CONFIG_PATH=/build/root/lib64/pkgconfig DESTDIR=/build/root make
|
||||
@@ -1,7 +1,7 @@
|
||||
.. SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
|
||||
libbpf API naming convention
|
||||
============================
|
||||
API naming convention
|
||||
=====================
|
||||
|
||||
libbpf API provides access to a few logically separated groups of
|
||||
functions and types. Every group has its own naming convention
|
||||
@@ -10,14 +10,14 @@ new function or type is added to keep libbpf API clean and consistent.
|
||||
|
||||
All types and functions provided by libbpf API should have one of the
|
||||
following prefixes: ``bpf_``, ``btf_``, ``libbpf_``, ``xsk_``,
|
||||
``perf_buffer_``.
|
||||
``btf_dump_``, ``ring_buffer_``, ``perf_buffer_``.
|
||||
|
||||
System call wrappers
|
||||
--------------------
|
||||
|
||||
System call wrappers are simple wrappers for commands supported by
|
||||
sys_bpf system call. These wrappers should go to ``bpf.h`` header file
|
||||
and map one-on-one to corresponding commands.
|
||||
and map one to one to corresponding commands.
|
||||
|
||||
For example ``bpf_map_lookup_elem`` wraps ``BPF_MAP_LOOKUP_ELEM``
|
||||
command of sys_bpf, ``bpf_prog_attach`` wraps ``BPF_PROG_ATTACH``, etc.
|
||||
@@ -49,10 +49,6 @@ object, ``bpf_object``, double underscore and ``open`` that defines the
|
||||
purpose of the function to open ELF file and create ``bpf_object`` from
|
||||
it.
|
||||
|
||||
Another example: ``bpf_program__load`` is named for corresponding
|
||||
object, ``bpf_program``, that is separated from other part of the name
|
||||
by double underscore.
|
||||
|
||||
All objects and corresponding functions other than BTF related should go
|
||||
to ``libbpf.h``. BTF types and functions should go to ``btf.h``.
|
||||
|
||||
@@ -72,12 +68,8 @@ of both low-level ring access functions and high-level configuration
|
||||
functions. These can be mixed and matched. Note that these functions
|
||||
are not reentrant for performance reasons.
|
||||
|
||||
Please take a look at Documentation/networking/af_xdp.rst in the Linux
|
||||
kernel source tree on how to use XDP sockets and for some common
|
||||
mistakes in case you do not get any traffic up to user space.
|
||||
|
||||
libbpf ABI
|
||||
==========
|
||||
ABI
|
||||
---
|
||||
|
||||
libbpf can be both linked statically or used as DSO. To avoid possible
|
||||
conflicts with other libraries an application is linked with, all
|
||||
@@ -116,7 +108,8 @@ This bump in ABI version is at most once per kernel development cycle.
|
||||
|
||||
For example, if current state of ``libbpf.map`` is:
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: none
|
||||
|
||||
LIBBPF_0.0.1 {
|
||||
global:
|
||||
bpf_func_a;
|
||||
@@ -128,7 +121,8 @@ For example, if current state of ``libbpf.map`` is:
|
||||
, and a new symbol ``bpf_func_c`` is being introduced, then
|
||||
``libbpf.map`` should be changed like this:
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: none
|
||||
|
||||
LIBBPF_0.0.1 {
|
||||
global:
|
||||
bpf_func_a;
|
||||
@@ -148,7 +142,7 @@ Format of version script and ways to handle ABI changes, including
|
||||
incompatible ones, described in details in [1].
|
||||
|
||||
Stand-alone build
|
||||
=================
|
||||
-------------------
|
||||
|
||||
Under https://github.com/libbpf/libbpf there is a (semi-)automated
|
||||
mirror of the mainline's version of libbpf for a stand-alone build.
|
||||
@@ -157,12 +151,12 @@ However, all changes to libbpf's code base must be upstreamed through
|
||||
the mainline kernel tree.
|
||||
|
||||
License
|
||||
=======
|
||||
-------------------
|
||||
|
||||
libbpf is dual-licensed under LGPL 2.1 and BSD 2-Clause.
|
||||
|
||||
Links
|
||||
=====
|
||||
-------------------
|
||||
|
||||
[1] https://www.akkadia.org/drepper/dsohowto.pdf
|
||||
(Chapter 3. Maintaining APIs and ABIs).
|
||||
9
docs/sphinx/Makefile
Normal file
9
docs/sphinx/Makefile
Normal file
@@ -0,0 +1,9 @@
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = ../src
|
||||
BUILDDIR = build
|
||||
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)"
|
||||
|
||||
%:
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)"
|
||||
277
docs/sphinx/doxygen/Doxyfile
Normal file
277
docs/sphinx/doxygen/Doxyfile
Normal file
@@ -0,0 +1,277 @@
|
||||
DOXYFILE_ENCODING = UTF-8
|
||||
PROJECT_NAME = "libbpf"
|
||||
PROJECT_NUMBER =
|
||||
PROJECT_BRIEF =
|
||||
PROJECT_LOGO =
|
||||
OUTPUT_DIRECTORY = ./build
|
||||
CREATE_SUBDIRS = NO
|
||||
ALLOW_UNICODE_NAMES = NO
|
||||
OUTPUT_LANGUAGE = English
|
||||
OUTPUT_TEXT_DIRECTION = None
|
||||
BRIEF_MEMBER_DESC = YES
|
||||
REPEAT_BRIEF = YES
|
||||
ALWAYS_DETAILED_SEC = NO
|
||||
INLINE_INHERITED_MEMB = NO
|
||||
FULL_PATH_NAMES = YES
|
||||
STRIP_FROM_PATH =
|
||||
STRIP_FROM_INC_PATH =
|
||||
SHORT_NAMES = NO
|
||||
JAVADOC_AUTOBRIEF = NO
|
||||
JAVADOC_BANNER = NO
|
||||
QT_AUTOBRIEF = NO
|
||||
MULTILINE_CPP_IS_BRIEF = NO
|
||||
PYTHON_DOCSTRING = NO
|
||||
INHERIT_DOCS = YES
|
||||
SEPARATE_MEMBER_PAGES = NO
|
||||
TAB_SIZE = 4
|
||||
ALIASES =
|
||||
OPTIMIZE_OUTPUT_FOR_C = YES
|
||||
OPTIMIZE_OUTPUT_JAVA = NO
|
||||
OPTIMIZE_FOR_FORTRAN = NO
|
||||
OPTIMIZE_OUTPUT_VHDL = NO
|
||||
OPTIMIZE_OUTPUT_SLICE = NO
|
||||
EXTENSION_MAPPING =
|
||||
MARKDOWN_SUPPORT = YES
|
||||
TOC_INCLUDE_HEADINGS = 5
|
||||
AUTOLINK_SUPPORT = YES
|
||||
BUILTIN_STL_SUPPORT = NO
|
||||
CPP_CLI_SUPPORT = NO
|
||||
SIP_SUPPORT = NO
|
||||
IDL_PROPERTY_SUPPORT = YES
|
||||
DISTRIBUTE_GROUP_DOC = NO
|
||||
GROUP_NESTED_COMPOUNDS = NO
|
||||
SUBGROUPING = YES
|
||||
INLINE_GROUPED_CLASSES = NO
|
||||
INLINE_SIMPLE_STRUCTS = NO
|
||||
TYPEDEF_HIDES_STRUCT = NO
|
||||
LOOKUP_CACHE_SIZE = 0
|
||||
NUM_PROC_THREADS = 1
|
||||
EXTRACT_ALL = NO
|
||||
EXTRACT_PRIVATE = NO
|
||||
EXTRACT_PRIV_VIRTUAL = NO
|
||||
EXTRACT_PACKAGE = NO
|
||||
EXTRACT_STATIC = NO
|
||||
EXTRACT_LOCAL_CLASSES = YES
|
||||
EXTRACT_LOCAL_METHODS = NO
|
||||
EXTRACT_ANON_NSPACES = NO
|
||||
RESOLVE_UNNAMED_PARAMS = YES
|
||||
HIDE_UNDOC_MEMBERS = NO
|
||||
HIDE_UNDOC_CLASSES = NO
|
||||
HIDE_FRIEND_COMPOUNDS = NO
|
||||
HIDE_IN_BODY_DOCS = NO
|
||||
INTERNAL_DOCS = NO
|
||||
CASE_SENSE_NAMES = YES
|
||||
HIDE_SCOPE_NAMES = NO
|
||||
HIDE_COMPOUND_REFERENCE= NO
|
||||
SHOW_INCLUDE_FILES = YES
|
||||
SHOW_GROUPED_MEMB_INC = NO
|
||||
FORCE_LOCAL_INCLUDES = NO
|
||||
INLINE_INFO = YES
|
||||
SORT_MEMBER_DOCS = YES
|
||||
SORT_BRIEF_DOCS = NO
|
||||
SORT_MEMBERS_CTORS_1ST = NO
|
||||
SORT_GROUP_NAMES = NO
|
||||
SORT_BY_SCOPE_NAME = NO
|
||||
STRICT_PROTO_MATCHING = NO
|
||||
GENERATE_TODOLIST = YES
|
||||
GENERATE_TESTLIST = YES
|
||||
GENERATE_BUGLIST = YES
|
||||
GENERATE_DEPRECATEDLIST= YES
|
||||
ENABLED_SECTIONS =
|
||||
MAX_INITIALIZER_LINES = 30
|
||||
SHOW_USED_FILES = YES
|
||||
SHOW_FILES = YES
|
||||
SHOW_NAMESPACES = YES
|
||||
FILE_VERSION_FILTER =
|
||||
LAYOUT_FILE =
|
||||
CITE_BIB_FILES =
|
||||
QUIET = NO
|
||||
WARNINGS = YES
|
||||
WARN_IF_UNDOCUMENTED = YES
|
||||
WARN_IF_DOC_ERROR = YES
|
||||
WARN_NO_PARAMDOC = NO
|
||||
WARN_AS_ERROR = NO
|
||||
WARN_FORMAT = "$file:$line: $text"
|
||||
WARN_LOGFILE =
|
||||
INPUT = ../../../src
|
||||
INPUT_ENCODING = UTF-8
|
||||
FILE_PATTERNS = *.c \
|
||||
*.h
|
||||
RECURSIVE = NO
|
||||
EXCLUDE =
|
||||
EXCLUDE_SYMLINKS = NO
|
||||
EXCLUDE_PATTERNS =
|
||||
EXCLUDE_SYMBOLS = ___*
|
||||
EXAMPLE_PATH =
|
||||
EXAMPLE_PATTERNS = *
|
||||
EXAMPLE_RECURSIVE = NO
|
||||
IMAGE_PATH =
|
||||
INPUT_FILTER =
|
||||
FILTER_PATTERNS =
|
||||
FILTER_SOURCE_FILES = NO
|
||||
FILTER_SOURCE_PATTERNS =
|
||||
USE_MDFILE_AS_MAINPAGE = YES
|
||||
SOURCE_BROWSER = NO
|
||||
INLINE_SOURCES = NO
|
||||
STRIP_CODE_COMMENTS = YES
|
||||
REFERENCED_BY_RELATION = NO
|
||||
REFERENCES_RELATION = NO
|
||||
REFERENCES_LINK_SOURCE = YES
|
||||
SOURCE_TOOLTIPS = YES
|
||||
USE_HTAGS = NO
|
||||
VERBATIM_HEADERS = YES
|
||||
ALPHABETICAL_INDEX = YES
|
||||
IGNORE_PREFIX =
|
||||
GENERATE_HTML = NO
|
||||
HTML_OUTPUT = html
|
||||
HTML_FILE_EXTENSION = .html
|
||||
HTML_HEADER =
|
||||
HTML_FOOTER =
|
||||
HTML_STYLESHEET =
|
||||
HTML_EXTRA_STYLESHEET =
|
||||
HTML_EXTRA_FILES =
|
||||
HTML_COLORSTYLE_HUE = 220
|
||||
HTML_COLORSTYLE_SAT = 100
|
||||
HTML_COLORSTYLE_GAMMA = 80
|
||||
HTML_TIMESTAMP = NO
|
||||
HTML_DYNAMIC_MENUS = YES
|
||||
HTML_DYNAMIC_SECTIONS = NO
|
||||
HTML_INDEX_NUM_ENTRIES = 100
|
||||
GENERATE_DOCSET = NO
|
||||
DOCSET_FEEDNAME = "Doxygen generated docs"
|
||||
DOCSET_BUNDLE_ID = org.doxygen.Project
|
||||
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
|
||||
DOCSET_PUBLISHER_NAME = Publisher
|
||||
GENERATE_HTMLHELP = NO
|
||||
CHM_FILE =
|
||||
HHC_LOCATION =
|
||||
GENERATE_CHI = NO
|
||||
CHM_INDEX_ENCODING =
|
||||
BINARY_TOC = NO
|
||||
TOC_EXPAND = NO
|
||||
GENERATE_QHP = NO
|
||||
QCH_FILE =
|
||||
QHP_NAMESPACE = org.doxygen.Project
|
||||
QHP_VIRTUAL_FOLDER = doc
|
||||
QHP_CUST_FILTER_NAME =
|
||||
QHP_CUST_FILTER_ATTRS =
|
||||
QHP_SECT_FILTER_ATTRS =
|
||||
QHG_LOCATION =
|
||||
GENERATE_ECLIPSEHELP = NO
|
||||
ECLIPSE_DOC_ID = org.doxygen.Project
|
||||
DISABLE_INDEX = NO
|
||||
GENERATE_TREEVIEW = NO
|
||||
ENUM_VALUES_PER_LINE = 4
|
||||
TREEVIEW_WIDTH = 250
|
||||
EXT_LINKS_IN_WINDOW = NO
|
||||
HTML_FORMULA_FORMAT = png
|
||||
FORMULA_FONTSIZE = 10
|
||||
FORMULA_TRANSPARENT = YES
|
||||
FORMULA_MACROFILE =
|
||||
USE_MATHJAX = NO
|
||||
MATHJAX_FORMAT = HTML-CSS
|
||||
MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2
|
||||
MATHJAX_EXTENSIONS =
|
||||
MATHJAX_CODEFILE =
|
||||
SEARCHENGINE = YES
|
||||
SERVER_BASED_SEARCH = NO
|
||||
EXTERNAL_SEARCH = NO
|
||||
SEARCHENGINE_URL =
|
||||
SEARCHDATA_FILE = searchdata.xml
|
||||
EXTERNAL_SEARCH_ID =
|
||||
EXTRA_SEARCH_MAPPINGS =
|
||||
GENERATE_LATEX = NO
|
||||
LATEX_OUTPUT = latex
|
||||
LATEX_CMD_NAME =
|
||||
MAKEINDEX_CMD_NAME = makeindex
|
||||
LATEX_MAKEINDEX_CMD = makeindex
|
||||
COMPACT_LATEX = NO
|
||||
PAPER_TYPE = a4
|
||||
EXTRA_PACKAGES =
|
||||
LATEX_HEADER =
|
||||
LATEX_FOOTER =
|
||||
LATEX_EXTRA_STYLESHEET =
|
||||
LATEX_EXTRA_FILES =
|
||||
PDF_HYPERLINKS = YES
|
||||
USE_PDFLATEX = YES
|
||||
LATEX_BATCHMODE = NO
|
||||
LATEX_HIDE_INDICES = NO
|
||||
LATEX_SOURCE_CODE = NO
|
||||
LATEX_BIB_STYLE = plain
|
||||
LATEX_TIMESTAMP = NO
|
||||
LATEX_EMOJI_DIRECTORY =
|
||||
GENERATE_RTF = NO
|
||||
RTF_OUTPUT = rtf
|
||||
COMPACT_RTF = NO
|
||||
RTF_HYPERLINKS = NO
|
||||
RTF_STYLESHEET_FILE =
|
||||
RTF_EXTENSIONS_FILE =
|
||||
RTF_SOURCE_CODE = NO
|
||||
GENERATE_MAN = NO
|
||||
MAN_OUTPUT = man
|
||||
MAN_EXTENSION = .3
|
||||
MAN_SUBDIR =
|
||||
MAN_LINKS = NO
|
||||
GENERATE_XML = YES
|
||||
XML_OUTPUT = xml
|
||||
XML_PROGRAMLISTING = YES
|
||||
XML_NS_MEMB_FILE_SCOPE = NO
|
||||
GENERATE_DOCBOOK = NO
|
||||
DOCBOOK_OUTPUT = docbook
|
||||
DOCBOOK_PROGRAMLISTING = NO
|
||||
GENERATE_AUTOGEN_DEF = NO
|
||||
GENERATE_PERLMOD = NO
|
||||
PERLMOD_LATEX = NO
|
||||
PERLMOD_PRETTY = YES
|
||||
PERLMOD_MAKEVAR_PREFIX =
|
||||
ENABLE_PREPROCESSING = YES
|
||||
MACRO_EXPANSION = NO
|
||||
EXPAND_ONLY_PREDEF = YES
|
||||
SEARCH_INCLUDES = YES
|
||||
INCLUDE_PATH =
|
||||
INCLUDE_FILE_PATTERNS =
|
||||
PREDEFINED =
|
||||
EXPAND_AS_DEFINED =
|
||||
SKIP_FUNCTION_MACROS = NO
|
||||
TAGFILES =
|
||||
GENERATE_TAGFILE =
|
||||
ALLEXTERNALS = NO
|
||||
EXTERNAL_GROUPS = YES
|
||||
EXTERNAL_PAGES = YES
|
||||
CLASS_DIAGRAMS = YES
|
||||
DIA_PATH =
|
||||
HIDE_UNDOC_RELATIONS = YES
|
||||
HAVE_DOT = NO
|
||||
DOT_NUM_THREADS = 0
|
||||
DOT_FONTNAME = Helvetica
|
||||
DOT_FONTSIZE = 10
|
||||
DOT_FONTPATH =
|
||||
CLASS_GRAPH = YES
|
||||
COLLABORATION_GRAPH = YES
|
||||
GROUP_GRAPHS = YES
|
||||
UML_LOOK = NO
|
||||
UML_LIMIT_NUM_FIELDS = 10
|
||||
DOT_UML_DETAILS = NO
|
||||
DOT_WRAP_THRESHOLD = 17
|
||||
TEMPLATE_RELATIONS = NO
|
||||
INCLUDE_GRAPH = YES
|
||||
INCLUDED_BY_GRAPH = YES
|
||||
CALL_GRAPH = NO
|
||||
CALLER_GRAPH = NO
|
||||
GRAPHICAL_HIERARCHY = YES
|
||||
DIRECTORY_GRAPH = YES
|
||||
DOT_IMAGE_FORMAT = png
|
||||
INTERACTIVE_SVG = NO
|
||||
DOT_PATH =
|
||||
DOTFILE_DIRS =
|
||||
MSCFILE_DIRS =
|
||||
DIAFILE_DIRS =
|
||||
PLANTUML_JAR_PATH =
|
||||
PLANTUML_CFG_FILE =
|
||||
PLANTUML_INCLUDE_PATH =
|
||||
DOT_GRAPH_MAX_NODES = 50
|
||||
MAX_DOT_GRAPH_DEPTH = 0
|
||||
DOT_TRANSPARENT = NO
|
||||
DOT_MULTI_TARGETS = NO
|
||||
GENERATE_LEGEND = YES
|
||||
DOT_CLEANUP = YES
|
||||
1
docs/sphinx/requirements.txt
Normal file
1
docs/sphinx/requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
breathe
|
||||
@@ -324,9 +324,6 @@ union bpf_iter_link_info {
|
||||
* **BPF_PROG_TYPE_SK_LOOKUP**
|
||||
* *data_in* and *data_out* must be NULL.
|
||||
*
|
||||
* **BPF_PROG_TYPE_XDP**
|
||||
* *ctx_in* and *ctx_out* must be NULL.
|
||||
*
|
||||
* **BPF_PROG_TYPE_RAW_TRACEPOINT**,
|
||||
* **BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE**
|
||||
*
|
||||
@@ -527,6 +524,15 @@ union bpf_iter_link_info {
|
||||
* Look up an element with the given *key* in the map referred to
|
||||
* by the file descriptor *fd*, and if found, delete the element.
|
||||
*
|
||||
* For **BPF_MAP_TYPE_QUEUE** and **BPF_MAP_TYPE_STACK** map
|
||||
* types, the *flags* argument needs to be set to 0, but for other
|
||||
* map types, it may be specified as:
|
||||
*
|
||||
* **BPF_F_LOCK**
|
||||
* Look up and delete the value of a spin-locked map
|
||||
* without returning the lock. This must be specified if
|
||||
* the elements contain a spinlock.
|
||||
*
|
||||
* The **BPF_MAP_TYPE_QUEUE** and **BPF_MAP_TYPE_STACK** map types
|
||||
* implement this command as a "pop" operation, deleting the top
|
||||
* element rather than one corresponding to *key*.
|
||||
@@ -536,6 +542,10 @@ union bpf_iter_link_info {
|
||||
* This command is only valid for the following map types:
|
||||
* * **BPF_MAP_TYPE_QUEUE**
|
||||
* * **BPF_MAP_TYPE_STACK**
|
||||
* * **BPF_MAP_TYPE_HASH**
|
||||
* * **BPF_MAP_TYPE_PERCPU_HASH**
|
||||
* * **BPF_MAP_TYPE_LRU_HASH**
|
||||
* * **BPF_MAP_TYPE_LRU_PERCPU_HASH**
|
||||
*
|
||||
* Return
|
||||
* Returns zero on success. On error, -1 is returned and *errno*
|
||||
@@ -837,6 +847,7 @@ enum bpf_cmd {
|
||||
BPF_PROG_ATTACH,
|
||||
BPF_PROG_DETACH,
|
||||
BPF_PROG_TEST_RUN,
|
||||
BPF_PROG_RUN = BPF_PROG_TEST_RUN,
|
||||
BPF_PROG_GET_NEXT_ID,
|
||||
BPF_MAP_GET_NEXT_ID,
|
||||
BPF_PROG_GET_FD_BY_ID,
|
||||
@@ -937,6 +948,7 @@ enum bpf_prog_type {
|
||||
BPF_PROG_TYPE_EXT,
|
||||
BPF_PROG_TYPE_LSM,
|
||||
BPF_PROG_TYPE_SK_LOOKUP,
|
||||
BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */
|
||||
};
|
||||
|
||||
enum bpf_attach_type {
|
||||
@@ -979,6 +991,9 @@ enum bpf_attach_type {
|
||||
BPF_SK_LOOKUP,
|
||||
BPF_XDP,
|
||||
BPF_SK_SKB_VERDICT,
|
||||
BPF_SK_REUSEPORT_SELECT,
|
||||
BPF_SK_REUSEPORT_SELECT_OR_MIGRATE,
|
||||
BPF_PERF_EVENT,
|
||||
__MAX_BPF_ATTACH_TYPE
|
||||
};
|
||||
|
||||
@@ -992,6 +1007,7 @@ enum bpf_link_type {
|
||||
BPF_LINK_TYPE_ITER = 4,
|
||||
BPF_LINK_TYPE_NETNS = 5,
|
||||
BPF_LINK_TYPE_XDP = 6,
|
||||
BPF_LINK_TYPE_PERF_EVENT = 7,
|
||||
|
||||
MAX_BPF_LINK_TYPE,
|
||||
};
|
||||
@@ -1097,8 +1113,8 @@ enum bpf_link_type {
|
||||
/* When BPF ldimm64's insn[0].src_reg != 0 then this can have
|
||||
* the following extensions:
|
||||
*
|
||||
* insn[0].src_reg: BPF_PSEUDO_MAP_FD
|
||||
* insn[0].imm: map fd
|
||||
* insn[0].src_reg: BPF_PSEUDO_MAP_[FD|IDX]
|
||||
* insn[0].imm: map fd or fd_idx
|
||||
* insn[1].imm: 0
|
||||
* insn[0].off: 0
|
||||
* insn[1].off: 0
|
||||
@@ -1106,15 +1122,19 @@ enum bpf_link_type {
|
||||
* verifier type: CONST_PTR_TO_MAP
|
||||
*/
|
||||
#define BPF_PSEUDO_MAP_FD 1
|
||||
/* insn[0].src_reg: BPF_PSEUDO_MAP_VALUE
|
||||
* insn[0].imm: map fd
|
||||
#define BPF_PSEUDO_MAP_IDX 5
|
||||
|
||||
/* insn[0].src_reg: BPF_PSEUDO_MAP_[IDX_]VALUE
|
||||
* insn[0].imm: map fd or fd_idx
|
||||
* insn[1].imm: offset into value
|
||||
* insn[0].off: 0
|
||||
* insn[1].off: 0
|
||||
* ldimm64 rewrite: address of map[0]+offset
|
||||
* verifier type: PTR_TO_MAP_VALUE
|
||||
*/
|
||||
#define BPF_PSEUDO_MAP_VALUE 2
|
||||
#define BPF_PSEUDO_MAP_VALUE 2
|
||||
#define BPF_PSEUDO_MAP_IDX_VALUE 6
|
||||
|
||||
/* insn[0].src_reg: BPF_PSEUDO_BTF_ID
|
||||
* insn[0].imm: kernel btd id of VAR
|
||||
* insn[1].imm: 0
|
||||
@@ -1314,6 +1334,8 @@ union bpf_attr {
|
||||
/* or valid module BTF object fd or 0 to attach to vmlinux */
|
||||
__u32 attach_btf_obj_fd;
|
||||
};
|
||||
__u32 :32; /* pad */
|
||||
__aligned_u64 fd_array; /* array of FDs */
|
||||
};
|
||||
|
||||
struct { /* anonymous struct used by BPF_OBJ_* commands */
|
||||
@@ -1426,6 +1448,13 @@ union bpf_attr {
|
||||
__aligned_u64 iter_info; /* extra bpf_iter_link_info */
|
||||
__u32 iter_info_len; /* iter_info length */
|
||||
};
|
||||
struct {
|
||||
/* black box user-provided value passed through
|
||||
* to BPF program at the execution time and
|
||||
* accessible through bpf_get_attach_cookie() BPF helper
|
||||
*/
|
||||
__u64 bpf_cookie;
|
||||
} perf_event;
|
||||
};
|
||||
} link_create;
|
||||
|
||||
@@ -2534,8 +2563,12 @@ union bpf_attr {
|
||||
* The lower two bits of *flags* are used as the return code if
|
||||
* the map lookup fails. This is so that the return value can be
|
||||
* one of the XDP program return codes up to **XDP_TX**, as chosen
|
||||
* by the caller. Any higher bits in the *flags* argument must be
|
||||
* unset.
|
||||
* by the caller. The higher bits of *flags* can be set to
|
||||
* BPF_F_BROADCAST or BPF_F_EXCLUDE_INGRESS as defined below.
|
||||
*
|
||||
* With BPF_F_BROADCAST the packet will be broadcasted to all the
|
||||
* interfaces in the map, with BPF_F_EXCLUDE_INGRESS the ingress
|
||||
* interface will be excluded when do broadcasting.
|
||||
*
|
||||
* See also **bpf_redirect**\ (), which only supports redirecting
|
||||
* to an ifindex, but doesn't require a map to do so.
|
||||
@@ -3222,7 +3255,7 @@ union bpf_attr {
|
||||
* long bpf_sk_select_reuseport(struct sk_reuseport_md *reuse, struct bpf_map *map, void *key, u64 flags)
|
||||
* Description
|
||||
* Select a **SO_REUSEPORT** socket from a
|
||||
* **BPF_MAP_TYPE_REUSEPORT_ARRAY** *map*.
|
||||
* **BPF_MAP_TYPE_REUSEPORT_SOCKARRAY** *map*.
|
||||
* It checks the selected socket is matching the incoming
|
||||
* request in the socket buffer.
|
||||
* Return
|
||||
@@ -4735,6 +4768,109 @@ union bpf_attr {
|
||||
* be zero-terminated except when **str_size** is 0.
|
||||
*
|
||||
* Or **-EBUSY** if the per-CPU memory copy buffer is busy.
|
||||
*
|
||||
* long bpf_sys_bpf(u32 cmd, void *attr, u32 attr_size)
|
||||
* Description
|
||||
* Execute bpf syscall with given arguments.
|
||||
* Return
|
||||
* A syscall result.
|
||||
*
|
||||
* long bpf_btf_find_by_name_kind(char *name, int name_sz, u32 kind, int flags)
|
||||
* Description
|
||||
* Find BTF type with given name and kind in vmlinux BTF or in module's BTFs.
|
||||
* Return
|
||||
* Returns btf_id and btf_obj_fd in lower and upper 32 bits.
|
||||
*
|
||||
* long bpf_sys_close(u32 fd)
|
||||
* Description
|
||||
* Execute close syscall for given FD.
|
||||
* Return
|
||||
* A syscall result.
|
||||
*
|
||||
* long bpf_timer_init(struct bpf_timer *timer, struct bpf_map *map, u64 flags)
|
||||
* Description
|
||||
* Initialize the timer.
|
||||
* First 4 bits of *flags* specify clockid.
|
||||
* Only CLOCK_MONOTONIC, CLOCK_REALTIME, CLOCK_BOOTTIME are allowed.
|
||||
* All other bits of *flags* are reserved.
|
||||
* The verifier will reject the program if *timer* is not from
|
||||
* the same *map*.
|
||||
* Return
|
||||
* 0 on success.
|
||||
* **-EBUSY** if *timer* is already initialized.
|
||||
* **-EINVAL** if invalid *flags* are passed.
|
||||
* **-EPERM** if *timer* is in a map that doesn't have any user references.
|
||||
* The user space should either hold a file descriptor to a map with timers
|
||||
* or pin such map in bpffs. When map is unpinned or file descriptor is
|
||||
* closed all timers in the map will be cancelled and freed.
|
||||
*
|
||||
* long bpf_timer_set_callback(struct bpf_timer *timer, void *callback_fn)
|
||||
* Description
|
||||
* Configure the timer to call *callback_fn* static function.
|
||||
* Return
|
||||
* 0 on success.
|
||||
* **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier.
|
||||
* **-EPERM** if *timer* is in a map that doesn't have any user references.
|
||||
* The user space should either hold a file descriptor to a map with timers
|
||||
* or pin such map in bpffs. When map is unpinned or file descriptor is
|
||||
* closed all timers in the map will be cancelled and freed.
|
||||
*
|
||||
* long bpf_timer_start(struct bpf_timer *timer, u64 nsecs, u64 flags)
|
||||
* Description
|
||||
* Set timer expiration N nanoseconds from the current time. The
|
||||
* configured callback will be invoked in soft irq context on some cpu
|
||||
* and will not repeat unless another bpf_timer_start() is made.
|
||||
* In such case the next invocation can migrate to a different cpu.
|
||||
* Since struct bpf_timer is a field inside map element the map
|
||||
* owns the timer. The bpf_timer_set_callback() will increment refcnt
|
||||
* of BPF program to make sure that callback_fn code stays valid.
|
||||
* When user space reference to a map reaches zero all timers
|
||||
* in a map are cancelled and corresponding program's refcnts are
|
||||
* decremented. This is done to make sure that Ctrl-C of a user
|
||||
* process doesn't leave any timers running. If map is pinned in
|
||||
* bpffs the callback_fn can re-arm itself indefinitely.
|
||||
* bpf_map_update/delete_elem() helpers and user space sys_bpf commands
|
||||
* cancel and free the timer in the given map element.
|
||||
* The map can contain timers that invoke callback_fn-s from different
|
||||
* programs. The same callback_fn can serve different timers from
|
||||
* different maps if key/value layout matches across maps.
|
||||
* Every bpf_timer_set_callback() can have different callback_fn.
|
||||
*
|
||||
* Return
|
||||
* 0 on success.
|
||||
* **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier
|
||||
* or invalid *flags* are passed.
|
||||
*
|
||||
* long bpf_timer_cancel(struct bpf_timer *timer)
|
||||
* Description
|
||||
* Cancel the timer and wait for callback_fn to finish if it was running.
|
||||
* Return
|
||||
* 0 if the timer was not active.
|
||||
* 1 if the timer was active.
|
||||
* **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier.
|
||||
* **-EDEADLK** if callback_fn tried to call bpf_timer_cancel() on its
|
||||
* own timer which would have led to a deadlock otherwise.
|
||||
*
|
||||
* u64 bpf_get_func_ip(void *ctx)
|
||||
* Description
|
||||
* Get address of the traced function (for tracing and kprobe programs).
|
||||
* Return
|
||||
* Address of the traced function.
|
||||
*
|
||||
* u64 bpf_get_attach_cookie(void *ctx)
|
||||
* Description
|
||||
* Get bpf_cookie value provided (optionally) during the program
|
||||
* attachment. It might be different for each individual
|
||||
* attachment, even if BPF program itself is the same.
|
||||
* Expects BPF program context *ctx* as a first argument.
|
||||
*
|
||||
* Supported for the following program types:
|
||||
* - kprobe/uprobe;
|
||||
* - tracepoint;
|
||||
* - perf_event.
|
||||
* Return
|
||||
* Value specified by user at BPF link creation/attachment time
|
||||
* or 0, if it was not specified.
|
||||
*/
|
||||
#define __BPF_FUNC_MAPPER(FN) \
|
||||
FN(unspec), \
|
||||
@@ -4903,6 +5039,15 @@ union bpf_attr {
|
||||
FN(check_mtu), \
|
||||
FN(for_each_map_elem), \
|
||||
FN(snprintf), \
|
||||
FN(sys_bpf), \
|
||||
FN(btf_find_by_name_kind), \
|
||||
FN(sys_close), \
|
||||
FN(timer_init), \
|
||||
FN(timer_set_callback), \
|
||||
FN(timer_start), \
|
||||
FN(timer_cancel), \
|
||||
FN(get_func_ip), \
|
||||
FN(get_attach_cookie), \
|
||||
/* */
|
||||
|
||||
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
||||
@@ -5080,6 +5225,12 @@ enum {
|
||||
BPF_F_BPRM_SECUREEXEC = (1ULL << 0),
|
||||
};
|
||||
|
||||
/* Flags for bpf_redirect_map helper */
|
||||
enum {
|
||||
BPF_F_BROADCAST = (1ULL << 3),
|
||||
BPF_F_EXCLUDE_INGRESS = (1ULL << 4),
|
||||
};
|
||||
|
||||
#define __bpf_md_ptr(type, name) \
|
||||
union { \
|
||||
type name; \
|
||||
@@ -5364,6 +5515,20 @@ struct sk_reuseport_md {
|
||||
__u32 ip_protocol; /* IP protocol. e.g. IPPROTO_TCP, IPPROTO_UDP */
|
||||
__u32 bind_inany; /* Is sock bound to an INANY address? */
|
||||
__u32 hash; /* A hash of the packet 4 tuples */
|
||||
/* When reuse->migrating_sk is NULL, it is selecting a sk for the
|
||||
* new incoming connection request (e.g. selecting a listen sk for
|
||||
* the received SYN in the TCP case). reuse->sk is one of the sk
|
||||
* in the reuseport group. The bpf prog can use reuse->sk to learn
|
||||
* the local listening ip/port without looking into the skb.
|
||||
*
|
||||
* When reuse->migrating_sk is not NULL, reuse->sk is closed and
|
||||
* reuse->migrating_sk is the socket that needs to be migrated
|
||||
* to another listening socket. migrating_sk could be a fullsock
|
||||
* sk that is fully established or a reqsk that is in-the-middle
|
||||
* of 3-way handshake.
|
||||
*/
|
||||
__bpf_md_ptr(struct bpf_sock *, sk);
|
||||
__bpf_md_ptr(struct bpf_sock *, migrating_sk);
|
||||
};
|
||||
|
||||
#define BPF_TAG_SIZE 8
|
||||
@@ -6009,6 +6174,11 @@ struct bpf_spin_lock {
|
||||
__u32 val;
|
||||
};
|
||||
|
||||
struct bpf_timer {
|
||||
__u64 :64;
|
||||
__u64 :64;
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
struct bpf_sysctl {
|
||||
__u32 write; /* Sysctl is being read (= 0) or written (= 1).
|
||||
* Allows 1,2,4-byte read, but no write.
|
||||
|
||||
@@ -653,6 +653,7 @@ enum {
|
||||
IFLA_BOND_AD_ACTOR_SYSTEM,
|
||||
IFLA_BOND_TLB_DYNAMIC_LB,
|
||||
IFLA_BOND_PEER_NOTIF_DELAY,
|
||||
IFLA_BOND_AD_LACP_ACTIVE,
|
||||
__IFLA_BOND_MAX,
|
||||
};
|
||||
|
||||
|
||||
612
include/uapi/linux/pkt_cls.h
Normal file
612
include/uapi/linux/pkt_cls.h
Normal file
@@ -0,0 +1,612 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
#ifndef __LINUX_PKT_CLS_H
|
||||
#define __LINUX_PKT_CLS_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/pkt_sched.h>
|
||||
|
||||
#define TC_COOKIE_MAX_SIZE 16
|
||||
|
||||
/* Action attributes */
|
||||
enum {
|
||||
TCA_ACT_UNSPEC,
|
||||
TCA_ACT_KIND,
|
||||
TCA_ACT_OPTIONS,
|
||||
TCA_ACT_INDEX,
|
||||
TCA_ACT_STATS,
|
||||
TCA_ACT_PAD,
|
||||
TCA_ACT_COOKIE,
|
||||
__TCA_ACT_MAX
|
||||
};
|
||||
|
||||
#define TCA_ACT_MAX __TCA_ACT_MAX
|
||||
#define TCA_OLD_COMPAT (TCA_ACT_MAX+1)
|
||||
#define TCA_ACT_MAX_PRIO 32
|
||||
#define TCA_ACT_BIND 1
|
||||
#define TCA_ACT_NOBIND 0
|
||||
#define TCA_ACT_UNBIND 1
|
||||
#define TCA_ACT_NOUNBIND 0
|
||||
#define TCA_ACT_REPLACE 1
|
||||
#define TCA_ACT_NOREPLACE 0
|
||||
|
||||
#define TC_ACT_UNSPEC (-1)
|
||||
#define TC_ACT_OK 0
|
||||
#define TC_ACT_RECLASSIFY 1
|
||||
#define TC_ACT_SHOT 2
|
||||
#define TC_ACT_PIPE 3
|
||||
#define TC_ACT_STOLEN 4
|
||||
#define TC_ACT_QUEUED 5
|
||||
#define TC_ACT_REPEAT 6
|
||||
#define TC_ACT_REDIRECT 7
|
||||
#define TC_ACT_TRAP 8 /* For hw path, this means "trap to cpu"
|
||||
* and don't further process the frame
|
||||
* in hardware. For sw path, this is
|
||||
* equivalent of TC_ACT_STOLEN - drop
|
||||
* the skb and act like everything
|
||||
* is alright.
|
||||
*/
|
||||
#define TC_ACT_VALUE_MAX TC_ACT_TRAP
|
||||
|
||||
/* There is a special kind of actions called "extended actions",
|
||||
* which need a value parameter. These have a local opcode located in
|
||||
* the highest nibble, starting from 1. The rest of the bits
|
||||
* are used to carry the value. These two parts together make
|
||||
* a combined opcode.
|
||||
*/
|
||||
#define __TC_ACT_EXT_SHIFT 28
|
||||
#define __TC_ACT_EXT(local) ((local) << __TC_ACT_EXT_SHIFT)
|
||||
#define TC_ACT_EXT_VAL_MASK ((1 << __TC_ACT_EXT_SHIFT) - 1)
|
||||
#define TC_ACT_EXT_OPCODE(combined) ((combined) & (~TC_ACT_EXT_VAL_MASK))
|
||||
#define TC_ACT_EXT_CMP(combined, opcode) (TC_ACT_EXT_OPCODE(combined) == opcode)
|
||||
|
||||
#define TC_ACT_JUMP __TC_ACT_EXT(1)
|
||||
#define TC_ACT_GOTO_CHAIN __TC_ACT_EXT(2)
|
||||
#define TC_ACT_EXT_OPCODE_MAX TC_ACT_GOTO_CHAIN
|
||||
|
||||
/* Action type identifiers*/
|
||||
enum {
|
||||
TCA_ID_UNSPEC=0,
|
||||
TCA_ID_POLICE=1,
|
||||
/* other actions go here */
|
||||
__TCA_ID_MAX=255
|
||||
};
|
||||
|
||||
#define TCA_ID_MAX __TCA_ID_MAX
|
||||
|
||||
struct tc_police {
|
||||
__u32 index;
|
||||
int action;
|
||||
#define TC_POLICE_UNSPEC TC_ACT_UNSPEC
|
||||
#define TC_POLICE_OK TC_ACT_OK
|
||||
#define TC_POLICE_RECLASSIFY TC_ACT_RECLASSIFY
|
||||
#define TC_POLICE_SHOT TC_ACT_SHOT
|
||||
#define TC_POLICE_PIPE TC_ACT_PIPE
|
||||
|
||||
__u32 limit;
|
||||
__u32 burst;
|
||||
__u32 mtu;
|
||||
struct tc_ratespec rate;
|
||||
struct tc_ratespec peakrate;
|
||||
int refcnt;
|
||||
int bindcnt;
|
||||
__u32 capab;
|
||||
};
|
||||
|
||||
struct tcf_t {
|
||||
__u64 install;
|
||||
__u64 lastuse;
|
||||
__u64 expires;
|
||||
__u64 firstuse;
|
||||
};
|
||||
|
||||
struct tc_cnt {
|
||||
int refcnt;
|
||||
int bindcnt;
|
||||
};
|
||||
|
||||
#define tc_gen \
|
||||
__u32 index; \
|
||||
__u32 capab; \
|
||||
int action; \
|
||||
int refcnt; \
|
||||
int bindcnt
|
||||
|
||||
enum {
|
||||
TCA_POLICE_UNSPEC,
|
||||
TCA_POLICE_TBF,
|
||||
TCA_POLICE_RATE,
|
||||
TCA_POLICE_PEAKRATE,
|
||||
TCA_POLICE_AVRATE,
|
||||
TCA_POLICE_RESULT,
|
||||
TCA_POLICE_TM,
|
||||
TCA_POLICE_PAD,
|
||||
__TCA_POLICE_MAX
|
||||
#define TCA_POLICE_RESULT TCA_POLICE_RESULT
|
||||
};
|
||||
|
||||
#define TCA_POLICE_MAX (__TCA_POLICE_MAX - 1)
|
||||
|
||||
/* tca flags definitions */
|
||||
#define TCA_CLS_FLAGS_SKIP_HW (1 << 0) /* don't offload filter to HW */
|
||||
#define TCA_CLS_FLAGS_SKIP_SW (1 << 1) /* don't use filter in SW */
|
||||
#define TCA_CLS_FLAGS_IN_HW (1 << 2) /* filter is offloaded to HW */
|
||||
#define TCA_CLS_FLAGS_NOT_IN_HW (1 << 3) /* filter isn't offloaded to HW */
|
||||
#define TCA_CLS_FLAGS_VERBOSE (1 << 4) /* verbose logging */
|
||||
|
||||
/* U32 filters */
|
||||
|
||||
#define TC_U32_HTID(h) ((h)&0xFFF00000)
|
||||
#define TC_U32_USERHTID(h) (TC_U32_HTID(h)>>20)
|
||||
#define TC_U32_HASH(h) (((h)>>12)&0xFF)
|
||||
#define TC_U32_NODE(h) ((h)&0xFFF)
|
||||
#define TC_U32_KEY(h) ((h)&0xFFFFF)
|
||||
#define TC_U32_UNSPEC 0
|
||||
#define TC_U32_ROOT (0xFFF00000)
|
||||
|
||||
enum {
|
||||
TCA_U32_UNSPEC,
|
||||
TCA_U32_CLASSID,
|
||||
TCA_U32_HASH,
|
||||
TCA_U32_LINK,
|
||||
TCA_U32_DIVISOR,
|
||||
TCA_U32_SEL,
|
||||
TCA_U32_POLICE,
|
||||
TCA_U32_ACT,
|
||||
TCA_U32_INDEV,
|
||||
TCA_U32_PCNT,
|
||||
TCA_U32_MARK,
|
||||
TCA_U32_FLAGS,
|
||||
TCA_U32_PAD,
|
||||
__TCA_U32_MAX
|
||||
};
|
||||
|
||||
#define TCA_U32_MAX (__TCA_U32_MAX - 1)
|
||||
|
||||
struct tc_u32_key {
|
||||
__be32 mask;
|
||||
__be32 val;
|
||||
int off;
|
||||
int offmask;
|
||||
};
|
||||
|
||||
struct tc_u32_sel {
|
||||
unsigned char flags;
|
||||
unsigned char offshift;
|
||||
unsigned char nkeys;
|
||||
|
||||
__be16 offmask;
|
||||
__u16 off;
|
||||
short offoff;
|
||||
|
||||
short hoff;
|
||||
__be32 hmask;
|
||||
struct tc_u32_key keys[0];
|
||||
};
|
||||
|
||||
struct tc_u32_mark {
|
||||
__u32 val;
|
||||
__u32 mask;
|
||||
__u32 success;
|
||||
};
|
||||
|
||||
struct tc_u32_pcnt {
|
||||
__u64 rcnt;
|
||||
__u64 rhit;
|
||||
__u64 kcnts[0];
|
||||
};
|
||||
|
||||
/* Flags */
|
||||
|
||||
#define TC_U32_TERMINAL 1
|
||||
#define TC_U32_OFFSET 2
|
||||
#define TC_U32_VAROFFSET 4
|
||||
#define TC_U32_EAT 8
|
||||
|
||||
#define TC_U32_MAXDEPTH 8
|
||||
|
||||
|
||||
/* RSVP filter */
|
||||
|
||||
enum {
|
||||
TCA_RSVP_UNSPEC,
|
||||
TCA_RSVP_CLASSID,
|
||||
TCA_RSVP_DST,
|
||||
TCA_RSVP_SRC,
|
||||
TCA_RSVP_PINFO,
|
||||
TCA_RSVP_POLICE,
|
||||
TCA_RSVP_ACT,
|
||||
__TCA_RSVP_MAX
|
||||
};
|
||||
|
||||
#define TCA_RSVP_MAX (__TCA_RSVP_MAX - 1 )
|
||||
|
||||
struct tc_rsvp_gpi {
|
||||
__u32 key;
|
||||
__u32 mask;
|
||||
int offset;
|
||||
};
|
||||
|
||||
struct tc_rsvp_pinfo {
|
||||
struct tc_rsvp_gpi dpi;
|
||||
struct tc_rsvp_gpi spi;
|
||||
__u8 protocol;
|
||||
__u8 tunnelid;
|
||||
__u8 tunnelhdr;
|
||||
__u8 pad;
|
||||
};
|
||||
|
||||
/* ROUTE filter */
|
||||
|
||||
enum {
|
||||
TCA_ROUTE4_UNSPEC,
|
||||
TCA_ROUTE4_CLASSID,
|
||||
TCA_ROUTE4_TO,
|
||||
TCA_ROUTE4_FROM,
|
||||
TCA_ROUTE4_IIF,
|
||||
TCA_ROUTE4_POLICE,
|
||||
TCA_ROUTE4_ACT,
|
||||
__TCA_ROUTE4_MAX
|
||||
};
|
||||
|
||||
#define TCA_ROUTE4_MAX (__TCA_ROUTE4_MAX - 1)
|
||||
|
||||
|
||||
/* FW filter */
|
||||
|
||||
enum {
|
||||
TCA_FW_UNSPEC,
|
||||
TCA_FW_CLASSID,
|
||||
TCA_FW_POLICE,
|
||||
TCA_FW_INDEV,
|
||||
TCA_FW_ACT, /* used by CONFIG_NET_CLS_ACT */
|
||||
TCA_FW_MASK,
|
||||
__TCA_FW_MAX
|
||||
};
|
||||
|
||||
#define TCA_FW_MAX (__TCA_FW_MAX - 1)
|
||||
|
||||
/* TC index filter */
|
||||
|
||||
enum {
|
||||
TCA_TCINDEX_UNSPEC,
|
||||
TCA_TCINDEX_HASH,
|
||||
TCA_TCINDEX_MASK,
|
||||
TCA_TCINDEX_SHIFT,
|
||||
TCA_TCINDEX_FALL_THROUGH,
|
||||
TCA_TCINDEX_CLASSID,
|
||||
TCA_TCINDEX_POLICE,
|
||||
TCA_TCINDEX_ACT,
|
||||
__TCA_TCINDEX_MAX
|
||||
};
|
||||
|
||||
#define TCA_TCINDEX_MAX (__TCA_TCINDEX_MAX - 1)
|
||||
|
||||
/* Flow filter */
|
||||
|
||||
enum {
|
||||
FLOW_KEY_SRC,
|
||||
FLOW_KEY_DST,
|
||||
FLOW_KEY_PROTO,
|
||||
FLOW_KEY_PROTO_SRC,
|
||||
FLOW_KEY_PROTO_DST,
|
||||
FLOW_KEY_IIF,
|
||||
FLOW_KEY_PRIORITY,
|
||||
FLOW_KEY_MARK,
|
||||
FLOW_KEY_NFCT,
|
||||
FLOW_KEY_NFCT_SRC,
|
||||
FLOW_KEY_NFCT_DST,
|
||||
FLOW_KEY_NFCT_PROTO_SRC,
|
||||
FLOW_KEY_NFCT_PROTO_DST,
|
||||
FLOW_KEY_RTCLASSID,
|
||||
FLOW_KEY_SKUID,
|
||||
FLOW_KEY_SKGID,
|
||||
FLOW_KEY_VLAN_TAG,
|
||||
FLOW_KEY_RXHASH,
|
||||
__FLOW_KEY_MAX,
|
||||
};
|
||||
|
||||
#define FLOW_KEY_MAX (__FLOW_KEY_MAX - 1)
|
||||
|
||||
enum {
|
||||
FLOW_MODE_MAP,
|
||||
FLOW_MODE_HASH,
|
||||
};
|
||||
|
||||
enum {
|
||||
TCA_FLOW_UNSPEC,
|
||||
TCA_FLOW_KEYS,
|
||||
TCA_FLOW_MODE,
|
||||
TCA_FLOW_BASECLASS,
|
||||
TCA_FLOW_RSHIFT,
|
||||
TCA_FLOW_ADDEND,
|
||||
TCA_FLOW_MASK,
|
||||
TCA_FLOW_XOR,
|
||||
TCA_FLOW_DIVISOR,
|
||||
TCA_FLOW_ACT,
|
||||
TCA_FLOW_POLICE,
|
||||
TCA_FLOW_EMATCHES,
|
||||
TCA_FLOW_PERTURB,
|
||||
__TCA_FLOW_MAX
|
||||
};
|
||||
|
||||
#define TCA_FLOW_MAX (__TCA_FLOW_MAX - 1)
|
||||
|
||||
/* Basic filter */
|
||||
|
||||
enum {
|
||||
TCA_BASIC_UNSPEC,
|
||||
TCA_BASIC_CLASSID,
|
||||
TCA_BASIC_EMATCHES,
|
||||
TCA_BASIC_ACT,
|
||||
TCA_BASIC_POLICE,
|
||||
__TCA_BASIC_MAX
|
||||
};
|
||||
|
||||
#define TCA_BASIC_MAX (__TCA_BASIC_MAX - 1)
|
||||
|
||||
|
||||
/* Cgroup classifier */
|
||||
|
||||
enum {
|
||||
TCA_CGROUP_UNSPEC,
|
||||
TCA_CGROUP_ACT,
|
||||
TCA_CGROUP_POLICE,
|
||||
TCA_CGROUP_EMATCHES,
|
||||
__TCA_CGROUP_MAX,
|
||||
};
|
||||
|
||||
#define TCA_CGROUP_MAX (__TCA_CGROUP_MAX - 1)
|
||||
|
||||
/* BPF classifier */
|
||||
|
||||
#define TCA_BPF_FLAG_ACT_DIRECT (1 << 0)
|
||||
|
||||
enum {
|
||||
TCA_BPF_UNSPEC,
|
||||
TCA_BPF_ACT,
|
||||
TCA_BPF_POLICE,
|
||||
TCA_BPF_CLASSID,
|
||||
TCA_BPF_OPS_LEN,
|
||||
TCA_BPF_OPS,
|
||||
TCA_BPF_FD,
|
||||
TCA_BPF_NAME,
|
||||
TCA_BPF_FLAGS,
|
||||
TCA_BPF_FLAGS_GEN,
|
||||
TCA_BPF_TAG,
|
||||
TCA_BPF_ID,
|
||||
__TCA_BPF_MAX,
|
||||
};
|
||||
|
||||
#define TCA_BPF_MAX (__TCA_BPF_MAX - 1)
|
||||
|
||||
/* Flower classifier */
|
||||
|
||||
enum {
|
||||
TCA_FLOWER_UNSPEC,
|
||||
TCA_FLOWER_CLASSID,
|
||||
TCA_FLOWER_INDEV,
|
||||
TCA_FLOWER_ACT,
|
||||
TCA_FLOWER_KEY_ETH_DST, /* ETH_ALEN */
|
||||
TCA_FLOWER_KEY_ETH_DST_MASK, /* ETH_ALEN */
|
||||
TCA_FLOWER_KEY_ETH_SRC, /* ETH_ALEN */
|
||||
TCA_FLOWER_KEY_ETH_SRC_MASK, /* ETH_ALEN */
|
||||
TCA_FLOWER_KEY_ETH_TYPE, /* be16 */
|
||||
TCA_FLOWER_KEY_IP_PROTO, /* u8 */
|
||||
TCA_FLOWER_KEY_IPV4_SRC, /* be32 */
|
||||
TCA_FLOWER_KEY_IPV4_SRC_MASK, /* be32 */
|
||||
TCA_FLOWER_KEY_IPV4_DST, /* be32 */
|
||||
TCA_FLOWER_KEY_IPV4_DST_MASK, /* be32 */
|
||||
TCA_FLOWER_KEY_IPV6_SRC, /* struct in6_addr */
|
||||
TCA_FLOWER_KEY_IPV6_SRC_MASK, /* struct in6_addr */
|
||||
TCA_FLOWER_KEY_IPV6_DST, /* struct in6_addr */
|
||||
TCA_FLOWER_KEY_IPV6_DST_MASK, /* struct in6_addr */
|
||||
TCA_FLOWER_KEY_TCP_SRC, /* be16 */
|
||||
TCA_FLOWER_KEY_TCP_DST, /* be16 */
|
||||
TCA_FLOWER_KEY_UDP_SRC, /* be16 */
|
||||
TCA_FLOWER_KEY_UDP_DST, /* be16 */
|
||||
|
||||
TCA_FLOWER_FLAGS,
|
||||
TCA_FLOWER_KEY_VLAN_ID, /* be16 */
|
||||
TCA_FLOWER_KEY_VLAN_PRIO, /* u8 */
|
||||
TCA_FLOWER_KEY_VLAN_ETH_TYPE, /* be16 */
|
||||
|
||||
TCA_FLOWER_KEY_ENC_KEY_ID, /* be32 */
|
||||
TCA_FLOWER_KEY_ENC_IPV4_SRC, /* be32 */
|
||||
TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK,/* be32 */
|
||||
TCA_FLOWER_KEY_ENC_IPV4_DST, /* be32 */
|
||||
TCA_FLOWER_KEY_ENC_IPV4_DST_MASK,/* be32 */
|
||||
TCA_FLOWER_KEY_ENC_IPV6_SRC, /* struct in6_addr */
|
||||
TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK,/* struct in6_addr */
|
||||
TCA_FLOWER_KEY_ENC_IPV6_DST, /* struct in6_addr */
|
||||
TCA_FLOWER_KEY_ENC_IPV6_DST_MASK,/* struct in6_addr */
|
||||
|
||||
TCA_FLOWER_KEY_TCP_SRC_MASK, /* be16 */
|
||||
TCA_FLOWER_KEY_TCP_DST_MASK, /* be16 */
|
||||
TCA_FLOWER_KEY_UDP_SRC_MASK, /* be16 */
|
||||
TCA_FLOWER_KEY_UDP_DST_MASK, /* be16 */
|
||||
TCA_FLOWER_KEY_SCTP_SRC_MASK, /* be16 */
|
||||
TCA_FLOWER_KEY_SCTP_DST_MASK, /* be16 */
|
||||
|
||||
TCA_FLOWER_KEY_SCTP_SRC, /* be16 */
|
||||
TCA_FLOWER_KEY_SCTP_DST, /* be16 */
|
||||
|
||||
TCA_FLOWER_KEY_ENC_UDP_SRC_PORT, /* be16 */
|
||||
TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK, /* be16 */
|
||||
TCA_FLOWER_KEY_ENC_UDP_DST_PORT, /* be16 */
|
||||
TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK, /* be16 */
|
||||
|
||||
TCA_FLOWER_KEY_FLAGS, /* be32 */
|
||||
TCA_FLOWER_KEY_FLAGS_MASK, /* be32 */
|
||||
|
||||
TCA_FLOWER_KEY_ICMPV4_CODE, /* u8 */
|
||||
TCA_FLOWER_KEY_ICMPV4_CODE_MASK,/* u8 */
|
||||
TCA_FLOWER_KEY_ICMPV4_TYPE, /* u8 */
|
||||
TCA_FLOWER_KEY_ICMPV4_TYPE_MASK,/* u8 */
|
||||
TCA_FLOWER_KEY_ICMPV6_CODE, /* u8 */
|
||||
TCA_FLOWER_KEY_ICMPV6_CODE_MASK,/* u8 */
|
||||
TCA_FLOWER_KEY_ICMPV6_TYPE, /* u8 */
|
||||
TCA_FLOWER_KEY_ICMPV6_TYPE_MASK,/* u8 */
|
||||
|
||||
TCA_FLOWER_KEY_ARP_SIP, /* be32 */
|
||||
TCA_FLOWER_KEY_ARP_SIP_MASK, /* be32 */
|
||||
TCA_FLOWER_KEY_ARP_TIP, /* be32 */
|
||||
TCA_FLOWER_KEY_ARP_TIP_MASK, /* be32 */
|
||||
TCA_FLOWER_KEY_ARP_OP, /* u8 */
|
||||
TCA_FLOWER_KEY_ARP_OP_MASK, /* u8 */
|
||||
TCA_FLOWER_KEY_ARP_SHA, /* ETH_ALEN */
|
||||
TCA_FLOWER_KEY_ARP_SHA_MASK, /* ETH_ALEN */
|
||||
TCA_FLOWER_KEY_ARP_THA, /* ETH_ALEN */
|
||||
TCA_FLOWER_KEY_ARP_THA_MASK, /* ETH_ALEN */
|
||||
|
||||
TCA_FLOWER_KEY_MPLS_TTL, /* u8 - 8 bits */
|
||||
TCA_FLOWER_KEY_MPLS_BOS, /* u8 - 1 bit */
|
||||
TCA_FLOWER_KEY_MPLS_TC, /* u8 - 3 bits */
|
||||
TCA_FLOWER_KEY_MPLS_LABEL, /* be32 - 20 bits */
|
||||
|
||||
TCA_FLOWER_KEY_TCP_FLAGS, /* be16 */
|
||||
TCA_FLOWER_KEY_TCP_FLAGS_MASK, /* be16 */
|
||||
|
||||
TCA_FLOWER_KEY_IP_TOS, /* u8 */
|
||||
TCA_FLOWER_KEY_IP_TOS_MASK, /* u8 */
|
||||
TCA_FLOWER_KEY_IP_TTL, /* u8 */
|
||||
TCA_FLOWER_KEY_IP_TTL_MASK, /* u8 */
|
||||
|
||||
TCA_FLOWER_KEY_CVLAN_ID, /* be16 */
|
||||
TCA_FLOWER_KEY_CVLAN_PRIO, /* u8 */
|
||||
TCA_FLOWER_KEY_CVLAN_ETH_TYPE, /* be16 */
|
||||
|
||||
TCA_FLOWER_KEY_ENC_IP_TOS, /* u8 */
|
||||
TCA_FLOWER_KEY_ENC_IP_TOS_MASK, /* u8 */
|
||||
TCA_FLOWER_KEY_ENC_IP_TTL, /* u8 */
|
||||
TCA_FLOWER_KEY_ENC_IP_TTL_MASK, /* u8 */
|
||||
|
||||
TCA_FLOWER_KEY_ENC_OPTS,
|
||||
TCA_FLOWER_KEY_ENC_OPTS_MASK,
|
||||
|
||||
TCA_FLOWER_IN_HW_COUNT,
|
||||
|
||||
__TCA_FLOWER_MAX,
|
||||
};
|
||||
|
||||
#define TCA_FLOWER_MAX (__TCA_FLOWER_MAX - 1)
|
||||
|
||||
enum {
|
||||
TCA_FLOWER_KEY_ENC_OPTS_UNSPEC,
|
||||
TCA_FLOWER_KEY_ENC_OPTS_GENEVE, /* Nested
|
||||
* TCA_FLOWER_KEY_ENC_OPT_GENEVE_
|
||||
* attributes
|
||||
*/
|
||||
__TCA_FLOWER_KEY_ENC_OPTS_MAX,
|
||||
};
|
||||
|
||||
#define TCA_FLOWER_KEY_ENC_OPTS_MAX (__TCA_FLOWER_KEY_ENC_OPTS_MAX - 1)
|
||||
|
||||
enum {
|
||||
TCA_FLOWER_KEY_ENC_OPT_GENEVE_UNSPEC,
|
||||
TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS, /* u16 */
|
||||
TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE, /* u8 */
|
||||
TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA, /* 4 to 128 bytes */
|
||||
|
||||
__TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX,
|
||||
};
|
||||
|
||||
#define TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX \
|
||||
(__TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX - 1)
|
||||
|
||||
enum {
|
||||
TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT = (1 << 0),
|
||||
TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST = (1 << 1),
|
||||
};
|
||||
|
||||
/* Match-all classifier */
|
||||
|
||||
enum {
|
||||
TCA_MATCHALL_UNSPEC,
|
||||
TCA_MATCHALL_CLASSID,
|
||||
TCA_MATCHALL_ACT,
|
||||
TCA_MATCHALL_FLAGS,
|
||||
__TCA_MATCHALL_MAX,
|
||||
};
|
||||
|
||||
#define TCA_MATCHALL_MAX (__TCA_MATCHALL_MAX - 1)
|
||||
|
||||
/* Extended Matches */
|
||||
|
||||
struct tcf_ematch_tree_hdr {
|
||||
__u16 nmatches;
|
||||
__u16 progid;
|
||||
};
|
||||
|
||||
enum {
|
||||
TCA_EMATCH_TREE_UNSPEC,
|
||||
TCA_EMATCH_TREE_HDR,
|
||||
TCA_EMATCH_TREE_LIST,
|
||||
__TCA_EMATCH_TREE_MAX
|
||||
};
|
||||
#define TCA_EMATCH_TREE_MAX (__TCA_EMATCH_TREE_MAX - 1)
|
||||
|
||||
struct tcf_ematch_hdr {
|
||||
__u16 matchid;
|
||||
__u16 kind;
|
||||
__u16 flags;
|
||||
__u16 pad; /* currently unused */
|
||||
};
|
||||
|
||||
/* 0 1
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
* +-----------------------+-+-+---+
|
||||
* | Unused |S|I| R |
|
||||
* +-----------------------+-+-+---+
|
||||
*
|
||||
* R(2) ::= relation to next ematch
|
||||
* where: 0 0 END (last ematch)
|
||||
* 0 1 AND
|
||||
* 1 0 OR
|
||||
* 1 1 Unused (invalid)
|
||||
* I(1) ::= invert result
|
||||
* S(1) ::= simple payload
|
||||
*/
|
||||
#define TCF_EM_REL_END 0
|
||||
#define TCF_EM_REL_AND (1<<0)
|
||||
#define TCF_EM_REL_OR (1<<1)
|
||||
#define TCF_EM_INVERT (1<<2)
|
||||
#define TCF_EM_SIMPLE (1<<3)
|
||||
|
||||
#define TCF_EM_REL_MASK 3
|
||||
#define TCF_EM_REL_VALID(v) (((v) & TCF_EM_REL_MASK) != TCF_EM_REL_MASK)
|
||||
|
||||
enum {
|
||||
TCF_LAYER_LINK,
|
||||
TCF_LAYER_NETWORK,
|
||||
TCF_LAYER_TRANSPORT,
|
||||
__TCF_LAYER_MAX
|
||||
};
|
||||
#define TCF_LAYER_MAX (__TCF_LAYER_MAX - 1)
|
||||
|
||||
/* Ematch type assignments
|
||||
* 1..32767 Reserved for ematches inside kernel tree
|
||||
* 32768..65535 Free to use, not reliable
|
||||
*/
|
||||
#define TCF_EM_CONTAINER 0
|
||||
#define TCF_EM_CMP 1
|
||||
#define TCF_EM_NBYTE 2
|
||||
#define TCF_EM_U32 3
|
||||
#define TCF_EM_META 4
|
||||
#define TCF_EM_TEXT 5
|
||||
#define TCF_EM_VLAN 6
|
||||
#define TCF_EM_CANID 7
|
||||
#define TCF_EM_IPSET 8
|
||||
#define TCF_EM_IPT 9
|
||||
#define TCF_EM_MAX 9
|
||||
|
||||
enum {
|
||||
TCF_EM_PROG_TC
|
||||
};
|
||||
|
||||
enum {
|
||||
TCF_EM_OPND_EQ,
|
||||
TCF_EM_OPND_GT,
|
||||
TCF_EM_OPND_LT
|
||||
};
|
||||
|
||||
#endif
|
||||
1164
include/uapi/linux/pkt_sched.h
Normal file
1164
include/uapi/linux/pkt_sched.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -45,11 +45,14 @@ PATH_MAP=( \
|
||||
[tools/include/uapi/linux/if_link.h]=include/uapi/linux/if_link.h \
|
||||
[tools/include/uapi/linux/if_xdp.h]=include/uapi/linux/if_xdp.h \
|
||||
[tools/include/uapi/linux/netlink.h]=include/uapi/linux/netlink.h \
|
||||
[tools/include/uapi/linux/pkt_cls.h]=include/uapi/linux/pkt_cls.h \
|
||||
[tools/include/uapi/linux/pkt_sched.h]=include/uapi/linux/pkt_sched.h \
|
||||
[Documentation/bpf/libbpf]=docs \
|
||||
)
|
||||
|
||||
LIBBPF_PATHS="${!PATH_MAP[@]} :^tools/lib/bpf/Makefile :^tools/lib/bpf/Build :^tools/lib/bpf/.gitignore :^tools/include/tools/libc_compat.h"
|
||||
LIBBPF_VIEW_PATHS="${PATH_MAP[@]}"
|
||||
LIBBPF_VIEW_EXCLUDE_REGEX='^src/(Makefile|Build|test_libbpf\.c|bpf_helper_defs\.h|\.gitignore)$'
|
||||
LIBBPF_VIEW_EXCLUDE_REGEX='^src/(Makefile|Build|test_libbpf\.c|bpf_helper_defs\.h|\.gitignore)$|^docs/(\.gitignore|api\.rst|conf\.py)$|^docs/sphinx/.*'
|
||||
LINUX_VIEW_EXCLUDE_REGEX='^include/tools/libc_compat.h$'
|
||||
|
||||
LIBBPF_TREE_FILTER="mkdir -p __libbpf/include/uapi/linux __libbpf/include/tools && "$'\\\n'
|
||||
|
||||
@@ -36,7 +36,8 @@ SHARED_OBJDIR := $(OBJDIR)/sharedobjs
|
||||
STATIC_OBJDIR := $(OBJDIR)/staticobjs
|
||||
OBJS := bpf.o btf.o libbpf.o libbpf_errno.o netlink.o \
|
||||
nlattr.o str_error.o libbpf_probes.o bpf_prog_linfo.o xsk.o \
|
||||
btf_dump.o hashmap.o ringbuf.o strset.o linker.o
|
||||
btf_dump.o hashmap.o ringbuf.o strset.o linker.o gen_loader.o \
|
||||
relo_core.o
|
||||
SHARED_OBJS := $(addprefix $(SHARED_OBJDIR)/,$(OBJS))
|
||||
STATIC_OBJS := $(addprefix $(STATIC_OBJDIR)/,$(OBJS))
|
||||
|
||||
@@ -48,9 +49,9 @@ ifndef BUILD_STATIC_ONLY
|
||||
VERSION_SCRIPT := libbpf.map
|
||||
endif
|
||||
|
||||
HEADERS := bpf.h libbpf.h btf.h xsk.h \
|
||||
HEADERS := bpf.h libbpf.h btf.h libbpf_common.h libbpf_legacy.h xsk.h \
|
||||
bpf_helpers.h bpf_helper_defs.h bpf_tracing.h \
|
||||
bpf_endian.h bpf_core_read.h libbpf_common.h
|
||||
bpf_endian.h bpf_core_read.h skel_internal.h
|
||||
UAPI_HEADERS := $(addprefix $(TOPDIR)/include/uapi/linux/,\
|
||||
bpf.h bpf_common.h btf.h)
|
||||
|
||||
@@ -60,7 +61,7 @@ INSTALL = install
|
||||
|
||||
DESTDIR ?=
|
||||
|
||||
ifeq ($(shell uname -m),x86_64)
|
||||
ifeq ($(filter-out %64 %64be %64eb %64le %64el s390x, $(shell uname -m)),)
|
||||
LIBSUBDIR := lib64
|
||||
else
|
||||
LIBSUBDIR := lib
|
||||
|
||||
231
src/bpf.c
231
src/bpf.c
@@ -80,6 +80,7 @@ static inline int sys_bpf_prog_load(union bpf_attr *attr, unsigned int size)
|
||||
int bpf_create_map_xattr(const struct bpf_create_map_attr *create_attr)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int fd;
|
||||
|
||||
memset(&attr, '\0', sizeof(attr));
|
||||
|
||||
@@ -102,7 +103,8 @@ int bpf_create_map_xattr(const struct bpf_create_map_attr *create_attr)
|
||||
else
|
||||
attr.inner_map_fd = create_attr->inner_map_fd;
|
||||
|
||||
return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
|
||||
fd = sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(fd);
|
||||
}
|
||||
|
||||
int bpf_create_map_node(enum bpf_map_type map_type, const char *name,
|
||||
@@ -160,6 +162,7 @@ int bpf_create_map_in_map_node(enum bpf_map_type map_type, const char *name,
|
||||
__u32 map_flags, int node)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int fd;
|
||||
|
||||
memset(&attr, '\0', sizeof(attr));
|
||||
|
||||
@@ -178,7 +181,8 @@ int bpf_create_map_in_map_node(enum bpf_map_type map_type, const char *name,
|
||||
attr.numa_node = node;
|
||||
}
|
||||
|
||||
return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
|
||||
fd = sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(fd);
|
||||
}
|
||||
|
||||
int bpf_create_map_in_map(enum bpf_map_type map_type, const char *name,
|
||||
@@ -222,10 +226,10 @@ int libbpf__bpf_prog_load(const struct bpf_prog_load_params *load_attr)
|
||||
int fd;
|
||||
|
||||
if (!load_attr->log_buf != !load_attr->log_buf_sz)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
if (load_attr->log_level > (4 | 2 | 1) || (load_attr->log_level && !load_attr->log_buf))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.prog_type = load_attr->prog_type;
|
||||
@@ -281,8 +285,10 @@ int libbpf__bpf_prog_load(const struct bpf_prog_load_params *load_attr)
|
||||
load_attr->func_info_cnt,
|
||||
load_attr->func_info_rec_size,
|
||||
attr.func_info_rec_size);
|
||||
if (!finfo)
|
||||
if (!finfo) {
|
||||
errno = E2BIG;
|
||||
goto done;
|
||||
}
|
||||
|
||||
attr.func_info = ptr_to_u64(finfo);
|
||||
attr.func_info_rec_size = load_attr->func_info_rec_size;
|
||||
@@ -293,8 +299,10 @@ int libbpf__bpf_prog_load(const struct bpf_prog_load_params *load_attr)
|
||||
load_attr->line_info_cnt,
|
||||
load_attr->line_info_rec_size,
|
||||
attr.line_info_rec_size);
|
||||
if (!linfo)
|
||||
if (!linfo) {
|
||||
errno = E2BIG;
|
||||
goto done;
|
||||
}
|
||||
|
||||
attr.line_info = ptr_to_u64(linfo);
|
||||
attr.line_info_rec_size = load_attr->line_info_rec_size;
|
||||
@@ -318,9 +326,10 @@ int libbpf__bpf_prog_load(const struct bpf_prog_load_params *load_attr)
|
||||
|
||||
fd = sys_bpf_prog_load(&attr, sizeof(attr));
|
||||
done:
|
||||
/* free() doesn't affect errno, so we don't need to restore it */
|
||||
free(finfo);
|
||||
free(linfo);
|
||||
return fd;
|
||||
return libbpf_err_errno(fd);
|
||||
}
|
||||
|
||||
int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
|
||||
@@ -329,7 +338,7 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
|
||||
struct bpf_prog_load_params p = {};
|
||||
|
||||
if (!load_attr || !log_buf != !log_buf_sz)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
p.prog_type = load_attr->prog_type;
|
||||
p.expected_attach_type = load_attr->expected_attach_type;
|
||||
@@ -391,6 +400,7 @@ int bpf_verify_program(enum bpf_prog_type type, const struct bpf_insn *insns,
|
||||
int log_level)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int fd;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.prog_type = type;
|
||||
@@ -404,13 +414,15 @@ int bpf_verify_program(enum bpf_prog_type type, const struct bpf_insn *insns,
|
||||
attr.kern_version = kern_version;
|
||||
attr.prog_flags = prog_flags;
|
||||
|
||||
return sys_bpf_prog_load(&attr, sizeof(attr));
|
||||
fd = sys_bpf_prog_load(&attr, sizeof(attr));
|
||||
return libbpf_err_errno(fd);
|
||||
}
|
||||
|
||||
int bpf_map_update_elem(int fd, const void *key, const void *value,
|
||||
__u64 flags)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int ret;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.map_fd = fd;
|
||||
@@ -418,22 +430,54 @@ int bpf_map_update_elem(int fd, const void *key, const void *value,
|
||||
attr.value = ptr_to_u64(value);
|
||||
attr.flags = flags;
|
||||
|
||||
return sys_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
|
||||
ret = sys_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(ret);
|
||||
}
|
||||
|
||||
int bpf_map_lookup_elem(int fd, const void *key, void *value)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int ret;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.map_fd = fd;
|
||||
attr.key = ptr_to_u64(key);
|
||||
attr.value = ptr_to_u64(value);
|
||||
|
||||
return sys_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
|
||||
ret = sys_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(ret);
|
||||
}
|
||||
|
||||
int bpf_map_lookup_elem_flags(int fd, const void *key, void *value, __u64 flags)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int ret;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.map_fd = fd;
|
||||
attr.key = ptr_to_u64(key);
|
||||
attr.value = ptr_to_u64(value);
|
||||
attr.flags = flags;
|
||||
|
||||
ret = sys_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(ret);
|
||||
}
|
||||
|
||||
int bpf_map_lookup_and_delete_elem(int fd, const void *key, void *value)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int ret;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.map_fd = fd;
|
||||
attr.key = ptr_to_u64(key);
|
||||
attr.value = ptr_to_u64(value);
|
||||
|
||||
ret = sys_bpf(BPF_MAP_LOOKUP_AND_DELETE_ELEM, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(ret);
|
||||
}
|
||||
|
||||
int bpf_map_lookup_and_delete_elem_flags(int fd, const void *key, void *value, __u64 flags)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
@@ -443,52 +487,46 @@ int bpf_map_lookup_elem_flags(int fd, const void *key, void *value, __u64 flags)
|
||||
attr.value = ptr_to_u64(value);
|
||||
attr.flags = flags;
|
||||
|
||||
return sys_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_map_lookup_and_delete_elem(int fd, const void *key, void *value)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.map_fd = fd;
|
||||
attr.key = ptr_to_u64(key);
|
||||
attr.value = ptr_to_u64(value);
|
||||
|
||||
return sys_bpf(BPF_MAP_LOOKUP_AND_DELETE_ELEM, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_map_delete_elem(int fd, const void *key)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int ret;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.map_fd = fd;
|
||||
attr.key = ptr_to_u64(key);
|
||||
|
||||
return sys_bpf(BPF_MAP_DELETE_ELEM, &attr, sizeof(attr));
|
||||
ret = sys_bpf(BPF_MAP_DELETE_ELEM, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(ret);
|
||||
}
|
||||
|
||||
int bpf_map_get_next_key(int fd, const void *key, void *next_key)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int ret;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.map_fd = fd;
|
||||
attr.key = ptr_to_u64(key);
|
||||
attr.next_key = ptr_to_u64(next_key);
|
||||
|
||||
return sys_bpf(BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr));
|
||||
ret = sys_bpf(BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(ret);
|
||||
}
|
||||
|
||||
int bpf_map_freeze(int fd)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int ret;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.map_fd = fd;
|
||||
|
||||
return sys_bpf(BPF_MAP_FREEZE, &attr, sizeof(attr));
|
||||
ret = sys_bpf(BPF_MAP_FREEZE, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(ret);
|
||||
}
|
||||
|
||||
static int bpf_map_batch_common(int cmd, int fd, void *in_batch,
|
||||
@@ -500,7 +538,7 @@ static int bpf_map_batch_common(int cmd, int fd, void *in_batch,
|
||||
int ret;
|
||||
|
||||
if (!OPTS_VALID(opts, bpf_map_batch_opts))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.batch.map_fd = fd;
|
||||
@@ -515,7 +553,7 @@ static int bpf_map_batch_common(int cmd, int fd, void *in_batch,
|
||||
ret = sys_bpf(cmd, &attr, sizeof(attr));
|
||||
*count = attr.batch.count;
|
||||
|
||||
return ret;
|
||||
return libbpf_err_errno(ret);
|
||||
}
|
||||
|
||||
int bpf_map_delete_batch(int fd, void *keys, __u32 *count,
|
||||
@@ -552,22 +590,26 @@ int bpf_map_update_batch(int fd, void *keys, void *values, __u32 *count,
|
||||
int bpf_obj_pin(int fd, const char *pathname)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int ret;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.pathname = ptr_to_u64((void *)pathname);
|
||||
attr.bpf_fd = fd;
|
||||
|
||||
return sys_bpf(BPF_OBJ_PIN, &attr, sizeof(attr));
|
||||
ret = sys_bpf(BPF_OBJ_PIN, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(ret);
|
||||
}
|
||||
|
||||
int bpf_obj_get(const char *pathname)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int fd;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.pathname = ptr_to_u64((void *)pathname);
|
||||
|
||||
return sys_bpf(BPF_OBJ_GET, &attr, sizeof(attr));
|
||||
fd = sys_bpf(BPF_OBJ_GET, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(fd);
|
||||
}
|
||||
|
||||
int bpf_prog_attach(int prog_fd, int target_fd, enum bpf_attach_type type,
|
||||
@@ -585,9 +627,10 @@ int bpf_prog_attach_xattr(int prog_fd, int target_fd,
|
||||
const struct bpf_prog_attach_opts *opts)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int ret;
|
||||
|
||||
if (!OPTS_VALID(opts, bpf_prog_attach_opts))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.target_fd = target_fd;
|
||||
@@ -596,30 +639,35 @@ int bpf_prog_attach_xattr(int prog_fd, int target_fd,
|
||||
attr.attach_flags = OPTS_GET(opts, flags, 0);
|
||||
attr.replace_bpf_fd = OPTS_GET(opts, replace_prog_fd, 0);
|
||||
|
||||
return sys_bpf(BPF_PROG_ATTACH, &attr, sizeof(attr));
|
||||
ret = sys_bpf(BPF_PROG_ATTACH, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(ret);
|
||||
}
|
||||
|
||||
int bpf_prog_detach(int target_fd, enum bpf_attach_type type)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int ret;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.target_fd = target_fd;
|
||||
attr.attach_type = type;
|
||||
|
||||
return sys_bpf(BPF_PROG_DETACH, &attr, sizeof(attr));
|
||||
ret = sys_bpf(BPF_PROG_DETACH, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(ret);
|
||||
}
|
||||
|
||||
int bpf_prog_detach2(int prog_fd, int target_fd, enum bpf_attach_type type)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int ret;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.target_fd = target_fd;
|
||||
attr.attach_bpf_fd = prog_fd;
|
||||
attr.attach_type = type;
|
||||
|
||||
return sys_bpf(BPF_PROG_DETACH, &attr, sizeof(attr));
|
||||
ret = sys_bpf(BPF_PROG_DETACH, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(ret);
|
||||
}
|
||||
|
||||
int bpf_link_create(int prog_fd, int target_fd,
|
||||
@@ -628,15 +676,21 @@ int bpf_link_create(int prog_fd, int target_fd,
|
||||
{
|
||||
__u32 target_btf_id, iter_info_len;
|
||||
union bpf_attr attr;
|
||||
int fd;
|
||||
|
||||
if (!OPTS_VALID(opts, bpf_link_create_opts))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
iter_info_len = OPTS_GET(opts, iter_info_len, 0);
|
||||
target_btf_id = OPTS_GET(opts, target_btf_id, 0);
|
||||
|
||||
if (iter_info_len && target_btf_id)
|
||||
return -EINVAL;
|
||||
/* validate we don't have unexpected combinations of non-zero fields */
|
||||
if (iter_info_len || target_btf_id) {
|
||||
if (iter_info_len && target_btf_id)
|
||||
return libbpf_err(-EINVAL);
|
||||
if (!OPTS_ZEROED(opts, target_btf_id))
|
||||
return libbpf_err(-EINVAL);
|
||||
}
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.link_create.prog_fd = prog_fd;
|
||||
@@ -644,34 +698,51 @@ int bpf_link_create(int prog_fd, int target_fd,
|
||||
attr.link_create.attach_type = attach_type;
|
||||
attr.link_create.flags = OPTS_GET(opts, flags, 0);
|
||||
|
||||
if (iter_info_len) {
|
||||
attr.link_create.iter_info =
|
||||
ptr_to_u64(OPTS_GET(opts, iter_info, (void *)0));
|
||||
attr.link_create.iter_info_len = iter_info_len;
|
||||
} else if (target_btf_id) {
|
||||
if (target_btf_id) {
|
||||
attr.link_create.target_btf_id = target_btf_id;
|
||||
goto proceed;
|
||||
}
|
||||
|
||||
return sys_bpf(BPF_LINK_CREATE, &attr, sizeof(attr));
|
||||
switch (attach_type) {
|
||||
case BPF_TRACE_ITER:
|
||||
attr.link_create.iter_info = ptr_to_u64(OPTS_GET(opts, iter_info, (void *)0));
|
||||
attr.link_create.iter_info_len = iter_info_len;
|
||||
break;
|
||||
case BPF_PERF_EVENT:
|
||||
attr.link_create.perf_event.bpf_cookie = OPTS_GET(opts, perf_event.bpf_cookie, 0);
|
||||
if (!OPTS_ZEROED(opts, perf_event))
|
||||
return libbpf_err(-EINVAL);
|
||||
break;
|
||||
default:
|
||||
if (!OPTS_ZEROED(opts, flags))
|
||||
return libbpf_err(-EINVAL);
|
||||
break;
|
||||
}
|
||||
proceed:
|
||||
fd = sys_bpf(BPF_LINK_CREATE, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(fd);
|
||||
}
|
||||
|
||||
int bpf_link_detach(int link_fd)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int ret;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.link_detach.link_fd = link_fd;
|
||||
|
||||
return sys_bpf(BPF_LINK_DETACH, &attr, sizeof(attr));
|
||||
ret = sys_bpf(BPF_LINK_DETACH, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(ret);
|
||||
}
|
||||
|
||||
int bpf_link_update(int link_fd, int new_prog_fd,
|
||||
const struct bpf_link_update_opts *opts)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int ret;
|
||||
|
||||
if (!OPTS_VALID(opts, bpf_link_update_opts))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.link_update.link_fd = link_fd;
|
||||
@@ -679,17 +750,20 @@ int bpf_link_update(int link_fd, int new_prog_fd,
|
||||
attr.link_update.flags = OPTS_GET(opts, flags, 0);
|
||||
attr.link_update.old_prog_fd = OPTS_GET(opts, old_prog_fd, 0);
|
||||
|
||||
return sys_bpf(BPF_LINK_UPDATE, &attr, sizeof(attr));
|
||||
ret = sys_bpf(BPF_LINK_UPDATE, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(ret);
|
||||
}
|
||||
|
||||
int bpf_iter_create(int link_fd)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int fd;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.iter_create.link_fd = link_fd;
|
||||
|
||||
return sys_bpf(BPF_ITER_CREATE, &attr, sizeof(attr));
|
||||
fd = sys_bpf(BPF_ITER_CREATE, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(fd);
|
||||
}
|
||||
|
||||
int bpf_prog_query(int target_fd, enum bpf_attach_type type, __u32 query_flags,
|
||||
@@ -706,10 +780,12 @@ int bpf_prog_query(int target_fd, enum bpf_attach_type type, __u32 query_flags,
|
||||
attr.query.prog_ids = ptr_to_u64(prog_ids);
|
||||
|
||||
ret = sys_bpf(BPF_PROG_QUERY, &attr, sizeof(attr));
|
||||
|
||||
if (attach_flags)
|
||||
*attach_flags = attr.query.attach_flags;
|
||||
*prog_cnt = attr.query.prog_cnt;
|
||||
return ret;
|
||||
|
||||
return libbpf_err_errno(ret);
|
||||
}
|
||||
|
||||
int bpf_prog_test_run(int prog_fd, int repeat, void *data, __u32 size,
|
||||
@@ -727,13 +803,15 @@ int bpf_prog_test_run(int prog_fd, int repeat, void *data, __u32 size,
|
||||
attr.test.repeat = repeat;
|
||||
|
||||
ret = sys_bpf(BPF_PROG_TEST_RUN, &attr, sizeof(attr));
|
||||
|
||||
if (size_out)
|
||||
*size_out = attr.test.data_size_out;
|
||||
if (retval)
|
||||
*retval = attr.test.retval;
|
||||
if (duration)
|
||||
*duration = attr.test.duration;
|
||||
return ret;
|
||||
|
||||
return libbpf_err_errno(ret);
|
||||
}
|
||||
|
||||
int bpf_prog_test_run_xattr(struct bpf_prog_test_run_attr *test_attr)
|
||||
@@ -742,7 +820,7 @@ int bpf_prog_test_run_xattr(struct bpf_prog_test_run_attr *test_attr)
|
||||
int ret;
|
||||
|
||||
if (!test_attr->data_out && test_attr->data_size_out > 0)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.test.prog_fd = test_attr->prog_fd;
|
||||
@@ -757,11 +835,13 @@ int bpf_prog_test_run_xattr(struct bpf_prog_test_run_attr *test_attr)
|
||||
attr.test.repeat = test_attr->repeat;
|
||||
|
||||
ret = sys_bpf(BPF_PROG_TEST_RUN, &attr, sizeof(attr));
|
||||
|
||||
test_attr->data_size_out = attr.test.data_size_out;
|
||||
test_attr->ctx_size_out = attr.test.ctx_size_out;
|
||||
test_attr->retval = attr.test.retval;
|
||||
test_attr->duration = attr.test.duration;
|
||||
return ret;
|
||||
|
||||
return libbpf_err_errno(ret);
|
||||
}
|
||||
|
||||
int bpf_prog_test_run_opts(int prog_fd, struct bpf_test_run_opts *opts)
|
||||
@@ -770,7 +850,7 @@ int bpf_prog_test_run_opts(int prog_fd, struct bpf_test_run_opts *opts)
|
||||
int ret;
|
||||
|
||||
if (!OPTS_VALID(opts, bpf_test_run_opts))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.test.prog_fd = prog_fd;
|
||||
@@ -788,11 +868,13 @@ int bpf_prog_test_run_opts(int prog_fd, struct bpf_test_run_opts *opts)
|
||||
attr.test.data_out = ptr_to_u64(OPTS_GET(opts, data_out, NULL));
|
||||
|
||||
ret = sys_bpf(BPF_PROG_TEST_RUN, &attr, sizeof(attr));
|
||||
|
||||
OPTS_SET(opts, data_size_out, attr.test.data_size_out);
|
||||
OPTS_SET(opts, ctx_size_out, attr.test.ctx_size_out);
|
||||
OPTS_SET(opts, duration, attr.test.duration);
|
||||
OPTS_SET(opts, retval, attr.test.retval);
|
||||
return ret;
|
||||
|
||||
return libbpf_err_errno(ret);
|
||||
}
|
||||
|
||||
static int bpf_obj_get_next_id(__u32 start_id, __u32 *next_id, int cmd)
|
||||
@@ -807,7 +889,7 @@ static int bpf_obj_get_next_id(__u32 start_id, __u32 *next_id, int cmd)
|
||||
if (!err)
|
||||
*next_id = attr.next_id;
|
||||
|
||||
return err;
|
||||
return libbpf_err_errno(err);
|
||||
}
|
||||
|
||||
int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id)
|
||||
@@ -833,41 +915,49 @@ int bpf_link_get_next_id(__u32 start_id, __u32 *next_id)
|
||||
int bpf_prog_get_fd_by_id(__u32 id)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int fd;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.prog_id = id;
|
||||
|
||||
return sys_bpf(BPF_PROG_GET_FD_BY_ID, &attr, sizeof(attr));
|
||||
fd = sys_bpf(BPF_PROG_GET_FD_BY_ID, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(fd);
|
||||
}
|
||||
|
||||
int bpf_map_get_fd_by_id(__u32 id)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int fd;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.map_id = id;
|
||||
|
||||
return sys_bpf(BPF_MAP_GET_FD_BY_ID, &attr, sizeof(attr));
|
||||
fd = sys_bpf(BPF_MAP_GET_FD_BY_ID, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(fd);
|
||||
}
|
||||
|
||||
int bpf_btf_get_fd_by_id(__u32 id)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int fd;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.btf_id = id;
|
||||
|
||||
return sys_bpf(BPF_BTF_GET_FD_BY_ID, &attr, sizeof(attr));
|
||||
fd = sys_bpf(BPF_BTF_GET_FD_BY_ID, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(fd);
|
||||
}
|
||||
|
||||
int bpf_link_get_fd_by_id(__u32 id)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int fd;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.link_id = id;
|
||||
|
||||
return sys_bpf(BPF_LINK_GET_FD_BY_ID, &attr, sizeof(attr));
|
||||
fd = sys_bpf(BPF_LINK_GET_FD_BY_ID, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(fd);
|
||||
}
|
||||
|
||||
int bpf_obj_get_info_by_fd(int bpf_fd, void *info, __u32 *info_len)
|
||||
@@ -881,21 +971,24 @@ int bpf_obj_get_info_by_fd(int bpf_fd, void *info, __u32 *info_len)
|
||||
attr.info.info = ptr_to_u64(info);
|
||||
|
||||
err = sys_bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr));
|
||||
|
||||
if (!err)
|
||||
*info_len = attr.info.info_len;
|
||||
|
||||
return err;
|
||||
return libbpf_err_errno(err);
|
||||
}
|
||||
|
||||
int bpf_raw_tracepoint_open(const char *name, int prog_fd)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int fd;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.raw_tracepoint.name = ptr_to_u64(name);
|
||||
attr.raw_tracepoint.prog_fd = prog_fd;
|
||||
|
||||
return sys_bpf(BPF_RAW_TRACEPOINT_OPEN, &attr, sizeof(attr));
|
||||
fd = sys_bpf(BPF_RAW_TRACEPOINT_OPEN, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(fd);
|
||||
}
|
||||
|
||||
int bpf_load_btf(const void *btf, __u32 btf_size, char *log_buf, __u32 log_buf_size,
|
||||
@@ -915,12 +1008,13 @@ retry:
|
||||
}
|
||||
|
||||
fd = sys_bpf(BPF_BTF_LOAD, &attr, sizeof(attr));
|
||||
if (fd == -1 && !do_log && log_buf && log_buf_size) {
|
||||
|
||||
if (fd < 0 && !do_log && log_buf && log_buf_size) {
|
||||
do_log = true;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
return fd;
|
||||
return libbpf_err_errno(fd);
|
||||
}
|
||||
|
||||
int bpf_task_fd_query(int pid, int fd, __u32 flags, char *buf, __u32 *buf_len,
|
||||
@@ -937,37 +1031,42 @@ int bpf_task_fd_query(int pid, int fd, __u32 flags, char *buf, __u32 *buf_len,
|
||||
attr.task_fd_query.buf_len = *buf_len;
|
||||
|
||||
err = sys_bpf(BPF_TASK_FD_QUERY, &attr, sizeof(attr));
|
||||
|
||||
*buf_len = attr.task_fd_query.buf_len;
|
||||
*prog_id = attr.task_fd_query.prog_id;
|
||||
*fd_type = attr.task_fd_query.fd_type;
|
||||
*probe_offset = attr.task_fd_query.probe_offset;
|
||||
*probe_addr = attr.task_fd_query.probe_addr;
|
||||
|
||||
return err;
|
||||
return libbpf_err_errno(err);
|
||||
}
|
||||
|
||||
int bpf_enable_stats(enum bpf_stats_type type)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int fd;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.enable_stats.type = type;
|
||||
|
||||
return sys_bpf(BPF_ENABLE_STATS, &attr, sizeof(attr));
|
||||
fd = sys_bpf(BPF_ENABLE_STATS, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(fd);
|
||||
}
|
||||
|
||||
int bpf_prog_bind_map(int prog_fd, int map_fd,
|
||||
const struct bpf_prog_bind_opts *opts)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int ret;
|
||||
|
||||
if (!OPTS_VALID(opts, bpf_prog_bind_opts))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.prog_bind_map.prog_fd = prog_fd;
|
||||
attr.prog_bind_map.map_fd = map_fd;
|
||||
attr.prog_bind_map.flags = OPTS_GET(opts, flags, 0);
|
||||
|
||||
return sys_bpf(BPF_PROG_BIND_MAP, &attr, sizeof(attr));
|
||||
ret = sys_bpf(BPF_PROG_BIND_MAP, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(ret);
|
||||
}
|
||||
|
||||
10
src/bpf.h
10
src/bpf.h
@@ -124,6 +124,8 @@ LIBBPF_API int bpf_map_lookup_elem_flags(int fd, const void *key, void *value,
|
||||
__u64 flags);
|
||||
LIBBPF_API int bpf_map_lookup_and_delete_elem(int fd, const void *key,
|
||||
void *value);
|
||||
LIBBPF_API int bpf_map_lookup_and_delete_elem_flags(int fd, const void *key,
|
||||
void *value, __u64 flags);
|
||||
LIBBPF_API int bpf_map_delete_elem(int fd, const void *key);
|
||||
LIBBPF_API int bpf_map_get_next_key(int fd, const void *key, void *next_key);
|
||||
LIBBPF_API int bpf_map_freeze(int fd);
|
||||
@@ -175,8 +177,14 @@ struct bpf_link_create_opts {
|
||||
union bpf_iter_link_info *iter_info;
|
||||
__u32 iter_info_len;
|
||||
__u32 target_btf_id;
|
||||
union {
|
||||
struct {
|
||||
__u64 bpf_cookie;
|
||||
} perf_event;
|
||||
};
|
||||
size_t :0;
|
||||
};
|
||||
#define bpf_link_create_opts__last_field target_btf_id
|
||||
#define bpf_link_create_opts__last_field perf_event
|
||||
|
||||
LIBBPF_API int bpf_link_create(int prog_fd, int target_fd,
|
||||
enum bpf_attach_type attach_type,
|
||||
|
||||
41
src/bpf_gen_internal.h
Normal file
41
src/bpf_gen_internal.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
/* Copyright (c) 2021 Facebook */
|
||||
#ifndef __BPF_GEN_INTERNAL_H
|
||||
#define __BPF_GEN_INTERNAL_H
|
||||
|
||||
struct ksym_relo_desc {
|
||||
const char *name;
|
||||
int kind;
|
||||
int insn_idx;
|
||||
};
|
||||
|
||||
struct bpf_gen {
|
||||
struct gen_loader_opts *opts;
|
||||
void *data_start;
|
||||
void *data_cur;
|
||||
void *insn_start;
|
||||
void *insn_cur;
|
||||
ssize_t cleanup_label;
|
||||
__u32 nr_progs;
|
||||
__u32 nr_maps;
|
||||
int log_level;
|
||||
int error;
|
||||
struct ksym_relo_desc *relos;
|
||||
int relo_cnt;
|
||||
char attach_target[128];
|
||||
int attach_kind;
|
||||
};
|
||||
|
||||
void bpf_gen__init(struct bpf_gen *gen, int log_level);
|
||||
int bpf_gen__finish(struct bpf_gen *gen);
|
||||
void bpf_gen__free(struct bpf_gen *gen);
|
||||
void bpf_gen__load_btf(struct bpf_gen *gen, const void *raw_data, __u32 raw_size);
|
||||
void bpf_gen__map_create(struct bpf_gen *gen, struct bpf_create_map_attr *map_attr, int map_idx);
|
||||
struct bpf_prog_load_params;
|
||||
void bpf_gen__prog_load(struct bpf_gen *gen, struct bpf_prog_load_params *load_attr, int prog_idx);
|
||||
void bpf_gen__map_update_elem(struct bpf_gen *gen, int map_idx, void *value, __u32 value_size);
|
||||
void bpf_gen__map_freeze(struct bpf_gen *gen, int map_idx);
|
||||
void bpf_gen__record_attach_target(struct bpf_gen *gen, const char *name, enum bpf_attach_type type);
|
||||
void bpf_gen__record_extern(struct bpf_gen *gen, const char *name, int kind, int insn_idx);
|
||||
|
||||
#endif
|
||||
@@ -36,6 +36,7 @@ struct btf_ptr;
|
||||
struct inode;
|
||||
struct socket;
|
||||
struct file;
|
||||
struct bpf_timer;
|
||||
|
||||
/*
|
||||
* bpf_map_lookup_elem
|
||||
@@ -1277,8 +1278,12 @@ static long (*bpf_skb_adjust_room)(struct __sk_buff *skb, __s32 len_diff, __u32
|
||||
* The lower two bits of *flags* are used as the return code if
|
||||
* the map lookup fails. This is so that the return value can be
|
||||
* one of the XDP program return codes up to **XDP_TX**, as chosen
|
||||
* by the caller. Any higher bits in the *flags* argument must be
|
||||
* unset.
|
||||
* by the caller. The higher bits of *flags* can be set to
|
||||
* BPF_F_BROADCAST or BPF_F_EXCLUDE_INGRESS as defined below.
|
||||
*
|
||||
* With BPF_F_BROADCAST the packet will be broadcasted to all the
|
||||
* interfaces in the map, with BPF_F_EXCLUDE_INGRESS the ingress
|
||||
* interface will be excluded when do broadcasting.
|
||||
*
|
||||
* See also **bpf_redirect**\ (), which only supports redirecting
|
||||
* to an ifindex, but doesn't require a map to do so.
|
||||
@@ -2090,7 +2095,7 @@ static void *(*bpf_get_local_storage)(void *map, __u64 flags) = (void *) 81;
|
||||
* bpf_sk_select_reuseport
|
||||
*
|
||||
* Select a **SO_REUSEPORT** socket from a
|
||||
* **BPF_MAP_TYPE_REUSEPORT_ARRAY** *map*.
|
||||
* **BPF_MAP_TYPE_REUSEPORT_SOCKARRAY** *map*.
|
||||
* It checks the selected socket is matching the incoming
|
||||
* request in the socket buffer.
|
||||
*
|
||||
@@ -3889,4 +3894,143 @@ static long (*bpf_for_each_map_elem)(void *map, void *callback_fn, void *callbac
|
||||
*/
|
||||
static long (*bpf_snprintf)(char *str, __u32 str_size, const char *fmt, __u64 *data, __u32 data_len) = (void *) 165;
|
||||
|
||||
/*
|
||||
* bpf_sys_bpf
|
||||
*
|
||||
* Execute bpf syscall with given arguments.
|
||||
*
|
||||
* Returns
|
||||
* A syscall result.
|
||||
*/
|
||||
static long (*bpf_sys_bpf)(__u32 cmd, void *attr, __u32 attr_size) = (void *) 166;
|
||||
|
||||
/*
|
||||
* bpf_btf_find_by_name_kind
|
||||
*
|
||||
* Find BTF type with given name and kind in vmlinux BTF or in module's BTFs.
|
||||
*
|
||||
* Returns
|
||||
* Returns btf_id and btf_obj_fd in lower and upper 32 bits.
|
||||
*/
|
||||
static long (*bpf_btf_find_by_name_kind)(char *name, int name_sz, __u32 kind, int flags) = (void *) 167;
|
||||
|
||||
/*
|
||||
* bpf_sys_close
|
||||
*
|
||||
* Execute close syscall for given FD.
|
||||
*
|
||||
* Returns
|
||||
* A syscall result.
|
||||
*/
|
||||
static long (*bpf_sys_close)(__u32 fd) = (void *) 168;
|
||||
|
||||
/*
|
||||
* bpf_timer_init
|
||||
*
|
||||
* Initialize the timer.
|
||||
* First 4 bits of *flags* specify clockid.
|
||||
* Only CLOCK_MONOTONIC, CLOCK_REALTIME, CLOCK_BOOTTIME are allowed.
|
||||
* All other bits of *flags* are reserved.
|
||||
* The verifier will reject the program if *timer* is not from
|
||||
* the same *map*.
|
||||
*
|
||||
* Returns
|
||||
* 0 on success.
|
||||
* **-EBUSY** if *timer* is already initialized.
|
||||
* **-EINVAL** if invalid *flags* are passed.
|
||||
* **-EPERM** if *timer* is in a map that doesn't have any user references.
|
||||
* The user space should either hold a file descriptor to a map with timers
|
||||
* or pin such map in bpffs. When map is unpinned or file descriptor is
|
||||
* closed all timers in the map will be cancelled and freed.
|
||||
*/
|
||||
static long (*bpf_timer_init)(struct bpf_timer *timer, void *map, __u64 flags) = (void *) 169;
|
||||
|
||||
/*
|
||||
* bpf_timer_set_callback
|
||||
*
|
||||
* Configure the timer to call *callback_fn* static function.
|
||||
*
|
||||
* Returns
|
||||
* 0 on success.
|
||||
* **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier.
|
||||
* **-EPERM** if *timer* is in a map that doesn't have any user references.
|
||||
* The user space should either hold a file descriptor to a map with timers
|
||||
* or pin such map in bpffs. When map is unpinned or file descriptor is
|
||||
* closed all timers in the map will be cancelled and freed.
|
||||
*/
|
||||
static long (*bpf_timer_set_callback)(struct bpf_timer *timer, void *callback_fn) = (void *) 170;
|
||||
|
||||
/*
|
||||
* bpf_timer_start
|
||||
*
|
||||
* Set timer expiration N nanoseconds from the current time. The
|
||||
* configured callback will be invoked in soft irq context on some cpu
|
||||
* and will not repeat unless another bpf_timer_start() is made.
|
||||
* In such case the next invocation can migrate to a different cpu.
|
||||
* Since struct bpf_timer is a field inside map element the map
|
||||
* owns the timer. The bpf_timer_set_callback() will increment refcnt
|
||||
* of BPF program to make sure that callback_fn code stays valid.
|
||||
* When user space reference to a map reaches zero all timers
|
||||
* in a map are cancelled and corresponding program's refcnts are
|
||||
* decremented. This is done to make sure that Ctrl-C of a user
|
||||
* process doesn't leave any timers running. If map is pinned in
|
||||
* bpffs the callback_fn can re-arm itself indefinitely.
|
||||
* bpf_map_update/delete_elem() helpers and user space sys_bpf commands
|
||||
* cancel and free the timer in the given map element.
|
||||
* The map can contain timers that invoke callback_fn-s from different
|
||||
* programs. The same callback_fn can serve different timers from
|
||||
* different maps if key/value layout matches across maps.
|
||||
* Every bpf_timer_set_callback() can have different callback_fn.
|
||||
*
|
||||
*
|
||||
* Returns
|
||||
* 0 on success.
|
||||
* **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier
|
||||
* or invalid *flags* are passed.
|
||||
*/
|
||||
static long (*bpf_timer_start)(struct bpf_timer *timer, __u64 nsecs, __u64 flags) = (void *) 171;
|
||||
|
||||
/*
|
||||
* bpf_timer_cancel
|
||||
*
|
||||
* Cancel the timer and wait for callback_fn to finish if it was running.
|
||||
*
|
||||
* Returns
|
||||
* 0 if the timer was not active.
|
||||
* 1 if the timer was active.
|
||||
* **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier.
|
||||
* **-EDEADLK** if callback_fn tried to call bpf_timer_cancel() on its
|
||||
* own timer which would have led to a deadlock otherwise.
|
||||
*/
|
||||
static long (*bpf_timer_cancel)(struct bpf_timer *timer) = (void *) 172;
|
||||
|
||||
/*
|
||||
* bpf_get_func_ip
|
||||
*
|
||||
* Get address of the traced function (for tracing and kprobe programs).
|
||||
*
|
||||
* Returns
|
||||
* Address of the traced function.
|
||||
*/
|
||||
static __u64 (*bpf_get_func_ip)(void *ctx) = (void *) 173;
|
||||
|
||||
/*
|
||||
* bpf_get_attach_cookie
|
||||
*
|
||||
* Get bpf_cookie value provided (optionally) during the program
|
||||
* attachment. It might be different for each individual
|
||||
* attachment, even if BPF program itself is the same.
|
||||
* Expects BPF program context *ctx* as a first argument.
|
||||
*
|
||||
* Supported for the following program types:
|
||||
* - kprobe/uprobe;
|
||||
* - tracepoint;
|
||||
* - perf_event.
|
||||
*
|
||||
* Returns
|
||||
* Value specified by user at BPF link creation/attachment time
|
||||
* or 0, if it was not specified.
|
||||
*/
|
||||
static __u64 (*bpf_get_attach_cookie)(void *ctx) = (void *) 174;
|
||||
|
||||
|
||||
|
||||
@@ -158,4 +158,70 @@ enum libbpf_tristate {
|
||||
#define __kconfig __attribute__((section(".kconfig")))
|
||||
#define __ksym __attribute__((section(".ksyms")))
|
||||
|
||||
#ifndef ___bpf_concat
|
||||
#define ___bpf_concat(a, b) a ## b
|
||||
#endif
|
||||
#ifndef ___bpf_apply
|
||||
#define ___bpf_apply(fn, n) ___bpf_concat(fn, n)
|
||||
#endif
|
||||
#ifndef ___bpf_nth
|
||||
#define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N
|
||||
#endif
|
||||
#ifndef ___bpf_narg
|
||||
#define ___bpf_narg(...) \
|
||||
___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
|
||||
#endif
|
||||
|
||||
#define ___bpf_fill0(arr, p, x) do {} while (0)
|
||||
#define ___bpf_fill1(arr, p, x) arr[p] = x
|
||||
#define ___bpf_fill2(arr, p, x, args...) arr[p] = x; ___bpf_fill1(arr, p + 1, args)
|
||||
#define ___bpf_fill3(arr, p, x, args...) arr[p] = x; ___bpf_fill2(arr, p + 1, args)
|
||||
#define ___bpf_fill4(arr, p, x, args...) arr[p] = x; ___bpf_fill3(arr, p + 1, args)
|
||||
#define ___bpf_fill5(arr, p, x, args...) arr[p] = x; ___bpf_fill4(arr, p + 1, args)
|
||||
#define ___bpf_fill6(arr, p, x, args...) arr[p] = x; ___bpf_fill5(arr, p + 1, args)
|
||||
#define ___bpf_fill7(arr, p, x, args...) arr[p] = x; ___bpf_fill6(arr, p + 1, args)
|
||||
#define ___bpf_fill8(arr, p, x, args...) arr[p] = x; ___bpf_fill7(arr, p + 1, args)
|
||||
#define ___bpf_fill9(arr, p, x, args...) arr[p] = x; ___bpf_fill8(arr, p + 1, args)
|
||||
#define ___bpf_fill10(arr, p, x, args...) arr[p] = x; ___bpf_fill9(arr, p + 1, args)
|
||||
#define ___bpf_fill11(arr, p, x, args...) arr[p] = x; ___bpf_fill10(arr, p + 1, args)
|
||||
#define ___bpf_fill12(arr, p, x, args...) arr[p] = x; ___bpf_fill11(arr, p + 1, args)
|
||||
#define ___bpf_fill(arr, args...) \
|
||||
___bpf_apply(___bpf_fill, ___bpf_narg(args))(arr, 0, args)
|
||||
|
||||
/*
|
||||
* BPF_SEQ_PRINTF to wrap bpf_seq_printf to-be-printed values
|
||||
* in a structure.
|
||||
*/
|
||||
#define BPF_SEQ_PRINTF(seq, fmt, args...) \
|
||||
({ \
|
||||
static const char ___fmt[] = fmt; \
|
||||
unsigned long long ___param[___bpf_narg(args)]; \
|
||||
\
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
___bpf_fill(___param, args); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
\
|
||||
bpf_seq_printf(seq, ___fmt, sizeof(___fmt), \
|
||||
___param, sizeof(___param)); \
|
||||
})
|
||||
|
||||
/*
|
||||
* BPF_SNPRINTF wraps the bpf_snprintf helper with variadic arguments instead of
|
||||
* an array of u64.
|
||||
*/
|
||||
#define BPF_SNPRINTF(out, out_size, fmt, args...) \
|
||||
({ \
|
||||
static const char ___fmt[] = fmt; \
|
||||
unsigned long long ___param[___bpf_narg(args)]; \
|
||||
\
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
___bpf_fill(___param, args); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
\
|
||||
bpf_snprintf(out, out_size, ___fmt, \
|
||||
___param, sizeof(___param)); \
|
||||
})
|
||||
|
||||
#endif
|
||||
|
||||
@@ -106,7 +106,7 @@ struct bpf_prog_linfo *bpf_prog_linfo__new(const struct bpf_prog_info *info)
|
||||
nr_linfo = info->nr_line_info;
|
||||
|
||||
if (!nr_linfo)
|
||||
return NULL;
|
||||
return errno = EINVAL, NULL;
|
||||
|
||||
/*
|
||||
* The min size that bpf_prog_linfo has to access for
|
||||
@@ -114,11 +114,11 @@ struct bpf_prog_linfo *bpf_prog_linfo__new(const struct bpf_prog_info *info)
|
||||
*/
|
||||
if (info->line_info_rec_size <
|
||||
offsetof(struct bpf_line_info, file_name_off))
|
||||
return NULL;
|
||||
return errno = EINVAL, NULL;
|
||||
|
||||
prog_linfo = calloc(1, sizeof(*prog_linfo));
|
||||
if (!prog_linfo)
|
||||
return NULL;
|
||||
return errno = ENOMEM, NULL;
|
||||
|
||||
/* Copy xlated line_info */
|
||||
prog_linfo->nr_linfo = nr_linfo;
|
||||
@@ -174,7 +174,7 @@ struct bpf_prog_linfo *bpf_prog_linfo__new(const struct bpf_prog_info *info)
|
||||
|
||||
err_free:
|
||||
bpf_prog_linfo__free(prog_linfo);
|
||||
return NULL;
|
||||
return errno = EINVAL, NULL;
|
||||
}
|
||||
|
||||
const struct bpf_line_info *
|
||||
@@ -186,11 +186,11 @@ bpf_prog_linfo__lfind_addr_func(const struct bpf_prog_linfo *prog_linfo,
|
||||
const __u64 *jited_linfo;
|
||||
|
||||
if (func_idx >= prog_linfo->nr_jited_func)
|
||||
return NULL;
|
||||
return errno = ENOENT, NULL;
|
||||
|
||||
nr_linfo = prog_linfo->nr_jited_linfo_per_func[func_idx];
|
||||
if (nr_skip >= nr_linfo)
|
||||
return NULL;
|
||||
return errno = ENOENT, NULL;
|
||||
|
||||
start = prog_linfo->jited_linfo_func_idx[func_idx] + nr_skip;
|
||||
jited_rec_size = prog_linfo->jited_rec_size;
|
||||
@@ -198,7 +198,7 @@ bpf_prog_linfo__lfind_addr_func(const struct bpf_prog_linfo *prog_linfo,
|
||||
(start * jited_rec_size);
|
||||
jited_linfo = raw_jited_linfo;
|
||||
if (addr < *jited_linfo)
|
||||
return NULL;
|
||||
return errno = ENOENT, NULL;
|
||||
|
||||
nr_linfo -= nr_skip;
|
||||
rec_size = prog_linfo->rec_size;
|
||||
@@ -225,13 +225,13 @@ bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo,
|
||||
|
||||
nr_linfo = prog_linfo->nr_linfo;
|
||||
if (nr_skip >= nr_linfo)
|
||||
return NULL;
|
||||
return errno = ENOENT, NULL;
|
||||
|
||||
rec_size = prog_linfo->rec_size;
|
||||
raw_linfo = prog_linfo->raw_linfo + (nr_skip * rec_size);
|
||||
linfo = raw_linfo;
|
||||
if (insn_off < linfo->insn_off)
|
||||
return NULL;
|
||||
return errno = ENOENT, NULL;
|
||||
|
||||
nr_linfo -= nr_skip;
|
||||
for (i = 0; i < nr_linfo; i++) {
|
||||
|
||||
@@ -25,26 +25,35 @@
|
||||
#define bpf_target_sparc
|
||||
#define bpf_target_defined
|
||||
#else
|
||||
#undef bpf_target_defined
|
||||
#endif
|
||||
|
||||
/* Fall back to what the compiler says */
|
||||
#ifndef bpf_target_defined
|
||||
#if defined(__x86_64__)
|
||||
#define bpf_target_x86
|
||||
#define bpf_target_defined
|
||||
#elif defined(__s390__)
|
||||
#define bpf_target_s390
|
||||
#define bpf_target_defined
|
||||
#elif defined(__arm__)
|
||||
#define bpf_target_arm
|
||||
#define bpf_target_defined
|
||||
#elif defined(__aarch64__)
|
||||
#define bpf_target_arm64
|
||||
#define bpf_target_defined
|
||||
#elif defined(__mips__)
|
||||
#define bpf_target_mips
|
||||
#define bpf_target_defined
|
||||
#elif defined(__powerpc__)
|
||||
#define bpf_target_powerpc
|
||||
#define bpf_target_defined
|
||||
#elif defined(__sparc__)
|
||||
#define bpf_target_sparc
|
||||
#define bpf_target_defined
|
||||
#endif /* no compiler target */
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef __BPF_TARGET_MISSING
|
||||
#define __BPF_TARGET_MISSING "GCC error \"Must specify a BPF target arch via __TARGET_ARCH_xxx\""
|
||||
#endif
|
||||
|
||||
#if defined(bpf_target_x86)
|
||||
@@ -287,7 +296,7 @@ struct pt_regs;
|
||||
#elif defined(bpf_target_sparc)
|
||||
#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = PT_REGS_RET(ctx); })
|
||||
#define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP
|
||||
#else
|
||||
#elif defined(bpf_target_defined)
|
||||
#define BPF_KPROBE_READ_RET_IP(ip, ctx) \
|
||||
({ bpf_probe_read_kernel(&(ip), sizeof(ip), (void *)PT_REGS_RET(ctx)); })
|
||||
#define BPF_KRETPROBE_READ_RET_IP(ip, ctx) \
|
||||
@@ -295,13 +304,48 @@ struct pt_regs;
|
||||
(void *)(PT_REGS_FP(ctx) + sizeof(ip))); })
|
||||
#endif
|
||||
|
||||
#if !defined(bpf_target_defined)
|
||||
|
||||
#define PT_REGS_PARM1(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM2(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM3(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM4(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM5(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_RET(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_FP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_RC(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_SP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_IP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
|
||||
#define PT_REGS_PARM1_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM2_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM3_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM4_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM5_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_RET_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_FP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_RC_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_SP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_IP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
|
||||
#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define BPF_KRETPROBE_READ_RET_IP(ip, ctx) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
|
||||
#endif /* !defined(bpf_target_defined) */
|
||||
|
||||
#ifndef ___bpf_concat
|
||||
#define ___bpf_concat(a, b) a ## b
|
||||
#endif
|
||||
#ifndef ___bpf_apply
|
||||
#define ___bpf_apply(fn, n) ___bpf_concat(fn, n)
|
||||
#endif
|
||||
#ifndef ___bpf_nth
|
||||
#define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N
|
||||
#endif
|
||||
#ifndef ___bpf_narg
|
||||
#define ___bpf_narg(...) \
|
||||
___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
|
||||
#define ___bpf_empty(...) \
|
||||
___bpf_nth(_, ##__VA_ARGS__, N, N, N, N, N, N, N, N, N, N, 0)
|
||||
#endif
|
||||
|
||||
#define ___bpf_ctx_cast0() ctx
|
||||
#define ___bpf_ctx_cast1(x) ___bpf_ctx_cast0(), (void *)ctx[0]
|
||||
@@ -413,56 +457,4 @@ typeof(name(0)) name(struct pt_regs *ctx) \
|
||||
} \
|
||||
static __always_inline typeof(name(0)) ____##name(struct pt_regs *ctx, ##args)
|
||||
|
||||
#define ___bpf_fill0(arr, p, x) do {} while (0)
|
||||
#define ___bpf_fill1(arr, p, x) arr[p] = x
|
||||
#define ___bpf_fill2(arr, p, x, args...) arr[p] = x; ___bpf_fill1(arr, p + 1, args)
|
||||
#define ___bpf_fill3(arr, p, x, args...) arr[p] = x; ___bpf_fill2(arr, p + 1, args)
|
||||
#define ___bpf_fill4(arr, p, x, args...) arr[p] = x; ___bpf_fill3(arr, p + 1, args)
|
||||
#define ___bpf_fill5(arr, p, x, args...) arr[p] = x; ___bpf_fill4(arr, p + 1, args)
|
||||
#define ___bpf_fill6(arr, p, x, args...) arr[p] = x; ___bpf_fill5(arr, p + 1, args)
|
||||
#define ___bpf_fill7(arr, p, x, args...) arr[p] = x; ___bpf_fill6(arr, p + 1, args)
|
||||
#define ___bpf_fill8(arr, p, x, args...) arr[p] = x; ___bpf_fill7(arr, p + 1, args)
|
||||
#define ___bpf_fill9(arr, p, x, args...) arr[p] = x; ___bpf_fill8(arr, p + 1, args)
|
||||
#define ___bpf_fill10(arr, p, x, args...) arr[p] = x; ___bpf_fill9(arr, p + 1, args)
|
||||
#define ___bpf_fill11(arr, p, x, args...) arr[p] = x; ___bpf_fill10(arr, p + 1, args)
|
||||
#define ___bpf_fill12(arr, p, x, args...) arr[p] = x; ___bpf_fill11(arr, p + 1, args)
|
||||
#define ___bpf_fill(arr, args...) \
|
||||
___bpf_apply(___bpf_fill, ___bpf_narg(args))(arr, 0, args)
|
||||
|
||||
/*
|
||||
* BPF_SEQ_PRINTF to wrap bpf_seq_printf to-be-printed values
|
||||
* in a structure.
|
||||
*/
|
||||
#define BPF_SEQ_PRINTF(seq, fmt, args...) \
|
||||
({ \
|
||||
static const char ___fmt[] = fmt; \
|
||||
unsigned long long ___param[___bpf_narg(args)]; \
|
||||
\
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
___bpf_fill(___param, args); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
\
|
||||
bpf_seq_printf(seq, ___fmt, sizeof(___fmt), \
|
||||
___param, sizeof(___param)); \
|
||||
})
|
||||
|
||||
/*
|
||||
* BPF_SNPRINTF wraps the bpf_snprintf helper with variadic arguments instead of
|
||||
* an array of u64.
|
||||
*/
|
||||
#define BPF_SNPRINTF(out, out_size, fmt, args...) \
|
||||
({ \
|
||||
static const char ___fmt[] = fmt; \
|
||||
unsigned long long ___param[___bpf_narg(args)]; \
|
||||
\
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
___bpf_fill(___param, args); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
\
|
||||
bpf_snprintf(out, out_size, ___fmt, \
|
||||
___param, sizeof(___param)); \
|
||||
})
|
||||
|
||||
#endif
|
||||
|
||||
344
src/btf.c
344
src/btf.c
@@ -443,7 +443,7 @@ struct btf_type *btf_type_by_id(struct btf *btf, __u32 type_id)
|
||||
const struct btf_type *btf__type_by_id(const struct btf *btf, __u32 type_id)
|
||||
{
|
||||
if (type_id >= btf->start_id + btf->nr_types)
|
||||
return NULL;
|
||||
return errno = EINVAL, NULL;
|
||||
return btf_type_by_id((struct btf *)btf, type_id);
|
||||
}
|
||||
|
||||
@@ -510,7 +510,7 @@ size_t btf__pointer_size(const struct btf *btf)
|
||||
int btf__set_pointer_size(struct btf *btf, size_t ptr_sz)
|
||||
{
|
||||
if (ptr_sz != 4 && ptr_sz != 8)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
btf->ptr_sz = ptr_sz;
|
||||
return 0;
|
||||
}
|
||||
@@ -537,7 +537,7 @@ enum btf_endianness btf__endianness(const struct btf *btf)
|
||||
int btf__set_endianness(struct btf *btf, enum btf_endianness endian)
|
||||
{
|
||||
if (endian != BTF_LITTLE_ENDIAN && endian != BTF_BIG_ENDIAN)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
btf->swapped_endian = is_host_big_endian() != (endian == BTF_BIG_ENDIAN);
|
||||
if (!btf->swapped_endian) {
|
||||
@@ -568,8 +568,7 @@ __s64 btf__resolve_size(const struct btf *btf, __u32 type_id)
|
||||
int i;
|
||||
|
||||
t = btf__type_by_id(btf, type_id);
|
||||
for (i = 0; i < MAX_RESOLVE_DEPTH && !btf_type_is_void_or_null(t);
|
||||
i++) {
|
||||
for (i = 0; i < MAX_RESOLVE_DEPTH && !btf_type_is_void_or_null(t); i++) {
|
||||
switch (btf_kind(t)) {
|
||||
case BTF_KIND_INT:
|
||||
case BTF_KIND_STRUCT:
|
||||
@@ -592,12 +591,12 @@ __s64 btf__resolve_size(const struct btf *btf, __u32 type_id)
|
||||
case BTF_KIND_ARRAY:
|
||||
array = btf_array(t);
|
||||
if (nelems && array->nelems > UINT32_MAX / nelems)
|
||||
return -E2BIG;
|
||||
return libbpf_err(-E2BIG);
|
||||
nelems *= array->nelems;
|
||||
type_id = array->type;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
}
|
||||
|
||||
t = btf__type_by_id(btf, type_id);
|
||||
@@ -605,9 +604,9 @@ __s64 btf__resolve_size(const struct btf *btf, __u32 type_id)
|
||||
|
||||
done:
|
||||
if (size < 0)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
if (nelems && size > UINT32_MAX / nelems)
|
||||
return -E2BIG;
|
||||
return libbpf_err(-E2BIG);
|
||||
|
||||
return nelems * size;
|
||||
}
|
||||
@@ -640,7 +639,7 @@ int btf__align_of(const struct btf *btf, __u32 id)
|
||||
for (i = 0; i < vlen; i++, m++) {
|
||||
align = btf__align_of(btf, m->type);
|
||||
if (align <= 0)
|
||||
return align;
|
||||
return libbpf_err(align);
|
||||
max_align = max(max_align, align);
|
||||
}
|
||||
|
||||
@@ -648,7 +647,7 @@ int btf__align_of(const struct btf *btf, __u32 id)
|
||||
}
|
||||
default:
|
||||
pr_warn("unsupported BTF_KIND:%u\n", btf_kind(t));
|
||||
return 0;
|
||||
return errno = EINVAL, 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -667,7 +666,7 @@ int btf__resolve_type(const struct btf *btf, __u32 type_id)
|
||||
}
|
||||
|
||||
if (depth == MAX_RESOLVE_DEPTH || btf_type_is_void_or_null(t))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
return type_id;
|
||||
}
|
||||
@@ -687,7 +686,7 @@ __s32 btf__find_by_name(const struct btf *btf, const char *type_name)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
return libbpf_err(-ENOENT);
|
||||
}
|
||||
|
||||
__s32 btf__find_by_name_kind(const struct btf *btf, const char *type_name,
|
||||
@@ -709,7 +708,7 @@ __s32 btf__find_by_name_kind(const struct btf *btf, const char *type_name,
|
||||
return i;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
return libbpf_err(-ENOENT);
|
||||
}
|
||||
|
||||
static bool btf_is_modifiable(const struct btf *btf)
|
||||
@@ -785,12 +784,12 @@ static struct btf *btf_new_empty(struct btf *base_btf)
|
||||
|
||||
struct btf *btf__new_empty(void)
|
||||
{
|
||||
return btf_new_empty(NULL);
|
||||
return libbpf_ptr(btf_new_empty(NULL));
|
||||
}
|
||||
|
||||
struct btf *btf__new_empty_split(struct btf *base_btf)
|
||||
{
|
||||
return btf_new_empty(base_btf);
|
||||
return libbpf_ptr(btf_new_empty(base_btf));
|
||||
}
|
||||
|
||||
static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf)
|
||||
@@ -805,6 +804,7 @@ static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf)
|
||||
btf->nr_types = 0;
|
||||
btf->start_id = 1;
|
||||
btf->start_str_off = 0;
|
||||
btf->fd = -1;
|
||||
|
||||
if (base_btf) {
|
||||
btf->base_btf = base_btf;
|
||||
@@ -833,8 +833,6 @@ static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf)
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
btf->fd = -1;
|
||||
|
||||
done:
|
||||
if (err) {
|
||||
btf__free(btf);
|
||||
@@ -846,7 +844,7 @@ done:
|
||||
|
||||
struct btf *btf__new(const void *data, __u32 size)
|
||||
{
|
||||
return btf_new(data, size, NULL);
|
||||
return libbpf_ptr(btf_new(data, size, NULL));
|
||||
}
|
||||
|
||||
static struct btf *btf_parse_elf(const char *path, struct btf *base_btf,
|
||||
@@ -937,7 +935,8 @@ static struct btf *btf_parse_elf(const char *path, struct btf *base_btf,
|
||||
goto done;
|
||||
}
|
||||
btf = btf_new(btf_data->d_buf, btf_data->d_size, base_btf);
|
||||
if (IS_ERR(btf))
|
||||
err = libbpf_get_error(btf);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
switch (gelf_getclass(elf)) {
|
||||
@@ -953,9 +952,9 @@ static struct btf *btf_parse_elf(const char *path, struct btf *base_btf,
|
||||
}
|
||||
|
||||
if (btf_ext && btf_ext_data) {
|
||||
*btf_ext = btf_ext__new(btf_ext_data->d_buf,
|
||||
btf_ext_data->d_size);
|
||||
if (IS_ERR(*btf_ext))
|
||||
*btf_ext = btf_ext__new(btf_ext_data->d_buf, btf_ext_data->d_size);
|
||||
err = libbpf_get_error(*btf_ext);
|
||||
if (err)
|
||||
goto done;
|
||||
} else if (btf_ext) {
|
||||
*btf_ext = NULL;
|
||||
@@ -965,30 +964,24 @@ done:
|
||||
elf_end(elf);
|
||||
close(fd);
|
||||
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
/*
|
||||
* btf is always parsed before btf_ext, so no need to clean up
|
||||
* btf_ext, if btf loading failed
|
||||
*/
|
||||
if (IS_ERR(btf))
|
||||
if (!err)
|
||||
return btf;
|
||||
if (btf_ext && IS_ERR(*btf_ext)) {
|
||||
btf__free(btf);
|
||||
err = PTR_ERR(*btf_ext);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
return btf;
|
||||
|
||||
if (btf_ext)
|
||||
btf_ext__free(*btf_ext);
|
||||
btf__free(btf);
|
||||
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
struct btf *btf__parse_elf(const char *path, struct btf_ext **btf_ext)
|
||||
{
|
||||
return btf_parse_elf(path, NULL, btf_ext);
|
||||
return libbpf_ptr(btf_parse_elf(path, NULL, btf_ext));
|
||||
}
|
||||
|
||||
struct btf *btf__parse_elf_split(const char *path, struct btf *base_btf)
|
||||
{
|
||||
return btf_parse_elf(path, base_btf, NULL);
|
||||
return libbpf_ptr(btf_parse_elf(path, base_btf, NULL));
|
||||
}
|
||||
|
||||
static struct btf *btf_parse_raw(const char *path, struct btf *base_btf)
|
||||
@@ -1056,36 +1049,39 @@ err_out:
|
||||
|
||||
struct btf *btf__parse_raw(const char *path)
|
||||
{
|
||||
return btf_parse_raw(path, NULL);
|
||||
return libbpf_ptr(btf_parse_raw(path, NULL));
|
||||
}
|
||||
|
||||
struct btf *btf__parse_raw_split(const char *path, struct btf *base_btf)
|
||||
{
|
||||
return btf_parse_raw(path, base_btf);
|
||||
return libbpf_ptr(btf_parse_raw(path, base_btf));
|
||||
}
|
||||
|
||||
static struct btf *btf_parse(const char *path, struct btf *base_btf, struct btf_ext **btf_ext)
|
||||
{
|
||||
struct btf *btf;
|
||||
int err;
|
||||
|
||||
if (btf_ext)
|
||||
*btf_ext = NULL;
|
||||
|
||||
btf = btf_parse_raw(path, base_btf);
|
||||
if (!IS_ERR(btf) || PTR_ERR(btf) != -EPROTO)
|
||||
err = libbpf_get_error(btf);
|
||||
if (!err)
|
||||
return btf;
|
||||
|
||||
if (err != -EPROTO)
|
||||
return ERR_PTR(err);
|
||||
return btf_parse_elf(path, base_btf, btf_ext);
|
||||
}
|
||||
|
||||
struct btf *btf__parse(const char *path, struct btf_ext **btf_ext)
|
||||
{
|
||||
return btf_parse(path, NULL, btf_ext);
|
||||
return libbpf_ptr(btf_parse(path, NULL, btf_ext));
|
||||
}
|
||||
|
||||
struct btf *btf__parse_split(const char *path, struct btf *base_btf)
|
||||
{
|
||||
return btf_parse(path, base_btf, NULL);
|
||||
return libbpf_ptr(btf_parse(path, base_btf, NULL));
|
||||
}
|
||||
|
||||
static int compare_vsi_off(const void *_a, const void *_b)
|
||||
@@ -1178,12 +1174,12 @@ int btf__finalize_data(struct bpf_object *obj, struct btf *btf)
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
return libbpf_err(err);
|
||||
}
|
||||
|
||||
static void *btf_get_raw_data(const struct btf *btf, __u32 *size, bool swap_endian);
|
||||
|
||||
int btf__load(struct btf *btf)
|
||||
int btf__load_into_kernel(struct btf *btf)
|
||||
{
|
||||
__u32 log_buf_size = 0, raw_size;
|
||||
char *log_buf = NULL;
|
||||
@@ -1191,13 +1187,13 @@ int btf__load(struct btf *btf)
|
||||
int err = 0;
|
||||
|
||||
if (btf->fd >= 0)
|
||||
return -EEXIST;
|
||||
return libbpf_err(-EEXIST);
|
||||
|
||||
retry_load:
|
||||
if (log_buf_size) {
|
||||
log_buf = malloc(log_buf_size);
|
||||
if (!log_buf)
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
*log_buf = 0;
|
||||
}
|
||||
@@ -1229,8 +1225,9 @@ retry_load:
|
||||
|
||||
done:
|
||||
free(log_buf);
|
||||
return err;
|
||||
return libbpf_err(err);
|
||||
}
|
||||
int btf__load(struct btf *) __attribute__((alias("btf__load_into_kernel")));
|
||||
|
||||
int btf__fd(const struct btf *btf)
|
||||
{
|
||||
@@ -1305,7 +1302,7 @@ const void *btf__get_raw_data(const struct btf *btf_ro, __u32 *size)
|
||||
|
||||
data = btf_get_raw_data(btf, &data_sz, btf->swapped_endian);
|
||||
if (!data)
|
||||
return NULL;
|
||||
return errno = -ENOMEM, NULL;
|
||||
|
||||
btf->raw_size = data_sz;
|
||||
if (btf->swapped_endian)
|
||||
@@ -1323,7 +1320,7 @@ const char *btf__str_by_offset(const struct btf *btf, __u32 offset)
|
||||
else if (offset - btf->start_str_off < btf->hdr->str_len)
|
||||
return btf_strs_data(btf) + (offset - btf->start_str_off);
|
||||
else
|
||||
return NULL;
|
||||
return errno = EINVAL, NULL;
|
||||
}
|
||||
|
||||
const char *btf__name_by_offset(const struct btf *btf, __u32 offset)
|
||||
@@ -1385,20 +1382,37 @@ exit_free:
|
||||
return btf;
|
||||
}
|
||||
|
||||
struct btf *btf__load_from_kernel_by_id_split(__u32 id, struct btf *base_btf)
|
||||
{
|
||||
struct btf *btf;
|
||||
int btf_fd;
|
||||
|
||||
btf_fd = bpf_btf_get_fd_by_id(id);
|
||||
if (btf_fd < 0)
|
||||
return libbpf_err_ptr(-errno);
|
||||
|
||||
btf = btf_get_from_fd(btf_fd, base_btf);
|
||||
close(btf_fd);
|
||||
|
||||
return libbpf_ptr(btf);
|
||||
}
|
||||
|
||||
struct btf *btf__load_from_kernel_by_id(__u32 id)
|
||||
{
|
||||
return btf__load_from_kernel_by_id_split(id, NULL);
|
||||
}
|
||||
|
||||
int btf__get_from_id(__u32 id, struct btf **btf)
|
||||
{
|
||||
struct btf *res;
|
||||
int btf_fd;
|
||||
int err;
|
||||
|
||||
*btf = NULL;
|
||||
btf_fd = bpf_btf_get_fd_by_id(id);
|
||||
if (btf_fd < 0)
|
||||
return -errno;
|
||||
res = btf__load_from_kernel_by_id(id);
|
||||
err = libbpf_get_error(res);
|
||||
|
||||
res = btf_get_from_fd(btf_fd, NULL);
|
||||
close(btf_fd);
|
||||
if (IS_ERR(res))
|
||||
return PTR_ERR(res);
|
||||
if (err)
|
||||
return libbpf_err(err);
|
||||
|
||||
*btf = res;
|
||||
return 0;
|
||||
@@ -1415,31 +1429,30 @@ int btf__get_map_kv_tids(const struct btf *btf, const char *map_name,
|
||||
__s64 key_size, value_size;
|
||||
__s32 container_id;
|
||||
|
||||
if (snprintf(container_name, max_name, "____btf_map_%s", map_name) ==
|
||||
max_name) {
|
||||
if (snprintf(container_name, max_name, "____btf_map_%s", map_name) == max_name) {
|
||||
pr_warn("map:%s length of '____btf_map_%s' is too long\n",
|
||||
map_name, map_name);
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
}
|
||||
|
||||
container_id = btf__find_by_name(btf, container_name);
|
||||
if (container_id < 0) {
|
||||
pr_debug("map:%s container_name:%s cannot be found in BTF. Missing BPF_ANNOTATE_KV_PAIR?\n",
|
||||
map_name, container_name);
|
||||
return container_id;
|
||||
return libbpf_err(container_id);
|
||||
}
|
||||
|
||||
container_type = btf__type_by_id(btf, container_id);
|
||||
if (!container_type) {
|
||||
pr_warn("map:%s cannot find BTF type for container_id:%u\n",
|
||||
map_name, container_id);
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
}
|
||||
|
||||
if (!btf_is_struct(container_type) || btf_vlen(container_type) < 2) {
|
||||
pr_warn("map:%s container_name:%s is an invalid container struct\n",
|
||||
map_name, container_name);
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
}
|
||||
|
||||
key = btf_members(container_type);
|
||||
@@ -1448,25 +1461,25 @@ int btf__get_map_kv_tids(const struct btf *btf, const char *map_name,
|
||||
key_size = btf__resolve_size(btf, key->type);
|
||||
if (key_size < 0) {
|
||||
pr_warn("map:%s invalid BTF key_type_size\n", map_name);
|
||||
return key_size;
|
||||
return libbpf_err(key_size);
|
||||
}
|
||||
|
||||
if (expected_key_size != key_size) {
|
||||
pr_warn("map:%s btf_key_type_size:%u != map_def_key_size:%u\n",
|
||||
map_name, (__u32)key_size, expected_key_size);
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
}
|
||||
|
||||
value_size = btf__resolve_size(btf, value->type);
|
||||
if (value_size < 0) {
|
||||
pr_warn("map:%s invalid BTF value_type_size\n", map_name);
|
||||
return value_size;
|
||||
return libbpf_err(value_size);
|
||||
}
|
||||
|
||||
if (expected_value_size != value_size) {
|
||||
pr_warn("map:%s btf_value_type_size:%u != map_def_value_size:%u\n",
|
||||
map_name, (__u32)value_size, expected_value_size);
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
}
|
||||
|
||||
*key_type_id = key->type;
|
||||
@@ -1563,11 +1576,11 @@ int btf__find_str(struct btf *btf, const char *s)
|
||||
|
||||
/* BTF needs to be in a modifiable state to build string lookup index */
|
||||
if (btf_ensure_modifiable(btf))
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
off = strset__find_str(btf->strs_set, s);
|
||||
if (off < 0)
|
||||
return off;
|
||||
return libbpf_err(off);
|
||||
|
||||
return btf->start_str_off + off;
|
||||
}
|
||||
@@ -1588,11 +1601,11 @@ int btf__add_str(struct btf *btf, const char *s)
|
||||
}
|
||||
|
||||
if (btf_ensure_modifiable(btf))
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
off = strset__add_str(btf->strs_set, s);
|
||||
if (off < 0)
|
||||
return off;
|
||||
return libbpf_err(off);
|
||||
|
||||
btf->hdr->str_len = strset__data_size(btf->strs_set);
|
||||
|
||||
@@ -1616,7 +1629,7 @@ static int btf_commit_type(struct btf *btf, int data_sz)
|
||||
|
||||
err = btf_add_type_idx_entry(btf, btf->hdr->type_len);
|
||||
if (err)
|
||||
return err;
|
||||
return libbpf_err(err);
|
||||
|
||||
btf->hdr->type_len += data_sz;
|
||||
btf->hdr->str_off += data_sz;
|
||||
@@ -1653,21 +1666,21 @@ int btf__add_type(struct btf *btf, const struct btf *src_btf, const struct btf_t
|
||||
|
||||
sz = btf_type_size(src_type);
|
||||
if (sz < 0)
|
||||
return sz;
|
||||
return libbpf_err(sz);
|
||||
|
||||
/* deconstruct BTF, if necessary, and invalidate raw_data */
|
||||
if (btf_ensure_modifiable(btf))
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
t = btf_add_type_mem(btf, sz);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
memcpy(t, src_type, sz);
|
||||
|
||||
err = btf_type_visit_str_offs(t, btf_rewrite_str, &p);
|
||||
if (err)
|
||||
return err;
|
||||
return libbpf_err(err);
|
||||
|
||||
return btf_commit_type(btf, sz);
|
||||
}
|
||||
@@ -1688,21 +1701,21 @@ int btf__add_int(struct btf *btf, const char *name, size_t byte_sz, int encoding
|
||||
|
||||
/* non-empty name */
|
||||
if (!name || !name[0])
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
/* byte_sz must be power of 2 */
|
||||
if (!byte_sz || (byte_sz & (byte_sz - 1)) || byte_sz > 16)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
if (encoding & ~(BTF_INT_SIGNED | BTF_INT_CHAR | BTF_INT_BOOL))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
/* deconstruct BTF, if necessary, and invalidate raw_data */
|
||||
if (btf_ensure_modifiable(btf))
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
sz = sizeof(struct btf_type) + sizeof(int);
|
||||
t = btf_add_type_mem(btf, sz);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
/* if something goes wrong later, we might end up with an extra string,
|
||||
* but that shouldn't be a problem, because BTF can't be constructed
|
||||
@@ -1736,20 +1749,20 @@ int btf__add_float(struct btf *btf, const char *name, size_t byte_sz)
|
||||
|
||||
/* non-empty name */
|
||||
if (!name || !name[0])
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
/* byte_sz must be one of the explicitly allowed values */
|
||||
if (byte_sz != 2 && byte_sz != 4 && byte_sz != 8 && byte_sz != 12 &&
|
||||
byte_sz != 16)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
if (btf_ensure_modifiable(btf))
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
sz = sizeof(struct btf_type);
|
||||
t = btf_add_type_mem(btf, sz);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
name_off = btf__add_str(btf, name);
|
||||
if (name_off < 0)
|
||||
@@ -1780,15 +1793,15 @@ static int btf_add_ref_kind(struct btf *btf, int kind, const char *name, int ref
|
||||
int sz, name_off = 0;
|
||||
|
||||
if (validate_type_id(ref_type_id))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
if (btf_ensure_modifiable(btf))
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
sz = sizeof(struct btf_type);
|
||||
t = btf_add_type_mem(btf, sz);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
if (name && name[0]) {
|
||||
name_off = btf__add_str(btf, name);
|
||||
@@ -1831,15 +1844,15 @@ int btf__add_array(struct btf *btf, int index_type_id, int elem_type_id, __u32 n
|
||||
int sz;
|
||||
|
||||
if (validate_type_id(index_type_id) || validate_type_id(elem_type_id))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
if (btf_ensure_modifiable(btf))
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
sz = sizeof(struct btf_type) + sizeof(struct btf_array);
|
||||
t = btf_add_type_mem(btf, sz);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
t->name_off = 0;
|
||||
t->info = btf_type_info(BTF_KIND_ARRAY, 0, 0);
|
||||
@@ -1860,12 +1873,12 @@ static int btf_add_composite(struct btf *btf, int kind, const char *name, __u32
|
||||
int sz, name_off = 0;
|
||||
|
||||
if (btf_ensure_modifiable(btf))
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
sz = sizeof(struct btf_type);
|
||||
t = btf_add_type_mem(btf, sz);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
if (name && name[0]) {
|
||||
name_off = btf__add_str(btf, name);
|
||||
@@ -1943,30 +1956,30 @@ int btf__add_field(struct btf *btf, const char *name, int type_id,
|
||||
|
||||
/* last type should be union/struct */
|
||||
if (btf->nr_types == 0)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
t = btf_last_type(btf);
|
||||
if (!btf_is_composite(t))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
if (validate_type_id(type_id))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
/* best-effort bit field offset/size enforcement */
|
||||
is_bitfield = bit_size || (bit_offset % 8 != 0);
|
||||
if (is_bitfield && (bit_size == 0 || bit_size > 255 || bit_offset > 0xffffff))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
/* only offset 0 is allowed for unions */
|
||||
if (btf_is_union(t) && bit_offset)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
/* decompose and invalidate raw data */
|
||||
if (btf_ensure_modifiable(btf))
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
sz = sizeof(struct btf_member);
|
||||
m = btf_add_type_mem(btf, sz);
|
||||
if (!m)
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
if (name && name[0]) {
|
||||
name_off = btf__add_str(btf, name);
|
||||
@@ -2008,15 +2021,15 @@ int btf__add_enum(struct btf *btf, const char *name, __u32 byte_sz)
|
||||
|
||||
/* byte_sz must be power of 2 */
|
||||
if (!byte_sz || (byte_sz & (byte_sz - 1)) || byte_sz > 8)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
if (btf_ensure_modifiable(btf))
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
sz = sizeof(struct btf_type);
|
||||
t = btf_add_type_mem(btf, sz);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
if (name && name[0]) {
|
||||
name_off = btf__add_str(btf, name);
|
||||
@@ -2048,25 +2061,25 @@ int btf__add_enum_value(struct btf *btf, const char *name, __s64 value)
|
||||
|
||||
/* last type should be BTF_KIND_ENUM */
|
||||
if (btf->nr_types == 0)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
t = btf_last_type(btf);
|
||||
if (!btf_is_enum(t))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
/* non-empty name */
|
||||
if (!name || !name[0])
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
if (value < INT_MIN || value > UINT_MAX)
|
||||
return -E2BIG;
|
||||
return libbpf_err(-E2BIG);
|
||||
|
||||
/* decompose and invalidate raw data */
|
||||
if (btf_ensure_modifiable(btf))
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
sz = sizeof(struct btf_enum);
|
||||
v = btf_add_type_mem(btf, sz);
|
||||
if (!v)
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
name_off = btf__add_str(btf, name);
|
||||
if (name_off < 0)
|
||||
@@ -2096,7 +2109,7 @@ int btf__add_enum_value(struct btf *btf, const char *name, __s64 value)
|
||||
int btf__add_fwd(struct btf *btf, const char *name, enum btf_fwd_kind fwd_kind)
|
||||
{
|
||||
if (!name || !name[0])
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
switch (fwd_kind) {
|
||||
case BTF_FWD_STRUCT:
|
||||
@@ -2117,7 +2130,7 @@ int btf__add_fwd(struct btf *btf, const char *name, enum btf_fwd_kind fwd_kind)
|
||||
*/
|
||||
return btf__add_enum(btf, name, sizeof(int));
|
||||
default:
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2132,7 +2145,7 @@ int btf__add_fwd(struct btf *btf, const char *name, enum btf_fwd_kind fwd_kind)
|
||||
int btf__add_typedef(struct btf *btf, const char *name, int ref_type_id)
|
||||
{
|
||||
if (!name || !name[0])
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
return btf_add_ref_kind(btf, BTF_KIND_TYPEDEF, name, ref_type_id);
|
||||
}
|
||||
@@ -2187,10 +2200,10 @@ int btf__add_func(struct btf *btf, const char *name,
|
||||
int id;
|
||||
|
||||
if (!name || !name[0])
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
if (linkage != BTF_FUNC_STATIC && linkage != BTF_FUNC_GLOBAL &&
|
||||
linkage != BTF_FUNC_EXTERN)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
id = btf_add_ref_kind(btf, BTF_KIND_FUNC, name, proto_type_id);
|
||||
if (id > 0) {
|
||||
@@ -2198,7 +2211,7 @@ int btf__add_func(struct btf *btf, const char *name,
|
||||
|
||||
t->info = btf_type_info(BTF_KIND_FUNC, linkage, 0);
|
||||
}
|
||||
return id;
|
||||
return libbpf_err(id);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2219,15 +2232,15 @@ int btf__add_func_proto(struct btf *btf, int ret_type_id)
|
||||
int sz;
|
||||
|
||||
if (validate_type_id(ret_type_id))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
if (btf_ensure_modifiable(btf))
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
sz = sizeof(struct btf_type);
|
||||
t = btf_add_type_mem(btf, sz);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
/* start out with vlen=0; this will be adjusted when adding enum
|
||||
* values, if necessary
|
||||
@@ -2254,23 +2267,23 @@ int btf__add_func_param(struct btf *btf, const char *name, int type_id)
|
||||
int sz, name_off = 0;
|
||||
|
||||
if (validate_type_id(type_id))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
/* last type should be BTF_KIND_FUNC_PROTO */
|
||||
if (btf->nr_types == 0)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
t = btf_last_type(btf);
|
||||
if (!btf_is_func_proto(t))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
/* decompose and invalidate raw data */
|
||||
if (btf_ensure_modifiable(btf))
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
sz = sizeof(struct btf_param);
|
||||
p = btf_add_type_mem(btf, sz);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
if (name && name[0]) {
|
||||
name_off = btf__add_str(btf, name);
|
||||
@@ -2308,21 +2321,21 @@ int btf__add_var(struct btf *btf, const char *name, int linkage, int type_id)
|
||||
|
||||
/* non-empty name */
|
||||
if (!name || !name[0])
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
if (linkage != BTF_VAR_STATIC && linkage != BTF_VAR_GLOBAL_ALLOCATED &&
|
||||
linkage != BTF_VAR_GLOBAL_EXTERN)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
if (validate_type_id(type_id))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
/* deconstruct BTF, if necessary, and invalidate raw_data */
|
||||
if (btf_ensure_modifiable(btf))
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
sz = sizeof(struct btf_type) + sizeof(struct btf_var);
|
||||
t = btf_add_type_mem(btf, sz);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
name_off = btf__add_str(btf, name);
|
||||
if (name_off < 0)
|
||||
@@ -2357,15 +2370,15 @@ int btf__add_datasec(struct btf *btf, const char *name, __u32 byte_sz)
|
||||
|
||||
/* non-empty name */
|
||||
if (!name || !name[0])
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
if (btf_ensure_modifiable(btf))
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
sz = sizeof(struct btf_type);
|
||||
t = btf_add_type_mem(btf, sz);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
name_off = btf__add_str(btf, name);
|
||||
if (name_off < 0)
|
||||
@@ -2397,22 +2410,22 @@ int btf__add_datasec_var_info(struct btf *btf, int var_type_id, __u32 offset, __
|
||||
|
||||
/* last type should be BTF_KIND_DATASEC */
|
||||
if (btf->nr_types == 0)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
t = btf_last_type(btf);
|
||||
if (!btf_is_datasec(t))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
if (validate_type_id(var_type_id))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
/* decompose and invalidate raw data */
|
||||
if (btf_ensure_modifiable(btf))
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
sz = sizeof(struct btf_var_secinfo);
|
||||
v = btf_add_type_mem(btf, sz);
|
||||
if (!v)
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
v->type = var_type_id;
|
||||
v->offset = offset;
|
||||
@@ -2614,11 +2627,11 @@ struct btf_ext *btf_ext__new(__u8 *data, __u32 size)
|
||||
|
||||
err = btf_ext_parse_hdr(data, size);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
return libbpf_err_ptr(err);
|
||||
|
||||
btf_ext = calloc(1, sizeof(struct btf_ext));
|
||||
if (!btf_ext)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
return libbpf_err_ptr(-ENOMEM);
|
||||
|
||||
btf_ext->data_size = size;
|
||||
btf_ext->data = malloc(size);
|
||||
@@ -2628,9 +2641,11 @@ struct btf_ext *btf_ext__new(__u8 *data, __u32 size)
|
||||
}
|
||||
memcpy(btf_ext->data, data, size);
|
||||
|
||||
if (btf_ext->hdr->hdr_len <
|
||||
offsetofend(struct btf_ext_header, line_info_len))
|
||||
if (btf_ext->hdr->hdr_len < offsetofend(struct btf_ext_header, line_info_len)) {
|
||||
err = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = btf_ext_setup_func_info(btf_ext);
|
||||
if (err)
|
||||
goto done;
|
||||
@@ -2639,8 +2654,11 @@ struct btf_ext *btf_ext__new(__u8 *data, __u32 size)
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
if (btf_ext->hdr->hdr_len < offsetofend(struct btf_ext_header, core_relo_len))
|
||||
if (btf_ext->hdr->hdr_len < offsetofend(struct btf_ext_header, core_relo_len)) {
|
||||
err = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = btf_ext_setup_core_relos(btf_ext);
|
||||
if (err)
|
||||
goto done;
|
||||
@@ -2648,7 +2666,7 @@ struct btf_ext *btf_ext__new(__u8 *data, __u32 size)
|
||||
done:
|
||||
if (err) {
|
||||
btf_ext__free(btf_ext);
|
||||
return ERR_PTR(err);
|
||||
return libbpf_err_ptr(err);
|
||||
}
|
||||
|
||||
return btf_ext;
|
||||
@@ -2687,7 +2705,7 @@ static int btf_ext_reloc_info(const struct btf *btf,
|
||||
existing_len = (*cnt) * record_size;
|
||||
data = realloc(*info, existing_len + records_len);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
memcpy(data + existing_len, sinfo->data, records_len);
|
||||
/* adjust insn_off only, the rest data will be passed
|
||||
@@ -2697,15 +2715,14 @@ static int btf_ext_reloc_info(const struct btf *btf,
|
||||
__u32 *insn_off;
|
||||
|
||||
insn_off = data + existing_len + (i * record_size);
|
||||
*insn_off = *insn_off / sizeof(struct bpf_insn) +
|
||||
insns_cnt;
|
||||
*insn_off = *insn_off / sizeof(struct bpf_insn) + insns_cnt;
|
||||
}
|
||||
*info = data;
|
||||
*cnt += sinfo->num_info;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
return libbpf_err(-ENOENT);
|
||||
}
|
||||
|
||||
int btf_ext__reloc_func_info(const struct btf *btf,
|
||||
@@ -2894,11 +2911,11 @@ int btf__dedup(struct btf *btf, struct btf_ext *btf_ext,
|
||||
|
||||
if (IS_ERR(d)) {
|
||||
pr_debug("btf_dedup_new failed: %ld", PTR_ERR(d));
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
}
|
||||
|
||||
if (btf_ensure_modifiable(btf))
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
|
||||
err = btf_dedup_prep(d);
|
||||
if (err) {
|
||||
@@ -2938,7 +2955,7 @@ int btf__dedup(struct btf *btf, struct btf_ext *btf_ext,
|
||||
|
||||
done:
|
||||
btf_dedup_free(d);
|
||||
return err;
|
||||
return libbpf_err(err);
|
||||
}
|
||||
|
||||
#define BTF_UNPROCESSED_ID ((__u32)-1)
|
||||
@@ -4018,7 +4035,7 @@ static void btf_dedup_merge_hypot_map(struct btf_dedup *d)
|
||||
*/
|
||||
if (d->hypot_adjust_canon)
|
||||
continue;
|
||||
|
||||
|
||||
if (t_kind == BTF_KIND_FWD && c_kind != BTF_KIND_FWD)
|
||||
d->map[t_id] = c_id;
|
||||
|
||||
@@ -4391,7 +4408,7 @@ static int btf_dedup_remap_types(struct btf_dedup *d)
|
||||
* Probe few well-known locations for vmlinux kernel image and try to load BTF
|
||||
* data out of it to use for target BTF.
|
||||
*/
|
||||
struct btf *libbpf_find_kernel_btf(void)
|
||||
struct btf *btf__load_vmlinux_btf(void)
|
||||
{
|
||||
struct {
|
||||
const char *path_fmt;
|
||||
@@ -4411,7 +4428,7 @@ struct btf *libbpf_find_kernel_btf(void)
|
||||
char path[PATH_MAX + 1];
|
||||
struct utsname buf;
|
||||
struct btf *btf;
|
||||
int i;
|
||||
int i, err;
|
||||
|
||||
uname(&buf);
|
||||
|
||||
@@ -4425,17 +4442,26 @@ struct btf *libbpf_find_kernel_btf(void)
|
||||
btf = btf__parse_raw(path);
|
||||
else
|
||||
btf = btf__parse_elf(path, NULL);
|
||||
|
||||
pr_debug("loading kernel BTF '%s': %ld\n",
|
||||
path, IS_ERR(btf) ? PTR_ERR(btf) : 0);
|
||||
if (IS_ERR(btf))
|
||||
err = libbpf_get_error(btf);
|
||||
pr_debug("loading kernel BTF '%s': %d\n", path, err);
|
||||
if (err)
|
||||
continue;
|
||||
|
||||
return btf;
|
||||
}
|
||||
|
||||
pr_warn("failed to find valid kernel BTF\n");
|
||||
return ERR_PTR(-ESRCH);
|
||||
return libbpf_err_ptr(-ESRCH);
|
||||
}
|
||||
|
||||
struct btf *libbpf_find_kernel_btf(void) __attribute__((alias("btf__load_vmlinux_btf")));
|
||||
|
||||
struct btf *btf__load_module_btf(const char *module_name, struct btf *vmlinux_btf)
|
||||
{
|
||||
char path[80];
|
||||
|
||||
snprintf(path, sizeof(path), "/sys/kernel/btf/%s", module_name);
|
||||
return btf__parse_split(path, vmlinux_btf);
|
||||
}
|
||||
|
||||
int btf_type_visit_type_ids(struct btf_type *t, type_id_visit_fn visit, void *ctx)
|
||||
|
||||
31
src/btf.h
31
src/btf.h
@@ -44,8 +44,17 @@ LIBBPF_API struct btf *btf__parse_elf_split(const char *path, struct btf *base_b
|
||||
LIBBPF_API struct btf *btf__parse_raw(const char *path);
|
||||
LIBBPF_API struct btf *btf__parse_raw_split(const char *path, struct btf *base_btf);
|
||||
|
||||
LIBBPF_API struct btf *btf__load_vmlinux_btf(void);
|
||||
LIBBPF_API struct btf *btf__load_module_btf(const char *module_name, struct btf *vmlinux_btf);
|
||||
LIBBPF_API struct btf *libbpf_find_kernel_btf(void);
|
||||
|
||||
LIBBPF_API struct btf *btf__load_from_kernel_by_id(__u32 id);
|
||||
LIBBPF_API struct btf *btf__load_from_kernel_by_id_split(__u32 id, struct btf *base_btf);
|
||||
LIBBPF_API int btf__get_from_id(__u32 id, struct btf **btf);
|
||||
|
||||
LIBBPF_API int btf__finalize_data(struct bpf_object *obj, struct btf *btf);
|
||||
LIBBPF_API int btf__load(struct btf *btf);
|
||||
LIBBPF_API int btf__load_into_kernel(struct btf *btf);
|
||||
LIBBPF_API __s32 btf__find_by_name(const struct btf *btf,
|
||||
const char *type_name);
|
||||
LIBBPF_API __s32 btf__find_by_name_kind(const struct btf *btf,
|
||||
@@ -66,7 +75,6 @@ LIBBPF_API void btf__set_fd(struct btf *btf, int fd);
|
||||
LIBBPF_API const void *btf__get_raw_data(const struct btf *btf, __u32 *size);
|
||||
LIBBPF_API const char *btf__name_by_offset(const struct btf *btf, __u32 offset);
|
||||
LIBBPF_API const char *btf__str_by_offset(const struct btf *btf, __u32 offset);
|
||||
LIBBPF_API int btf__get_from_id(__u32 id, struct btf **btf);
|
||||
LIBBPF_API int btf__get_map_kv_tids(const struct btf *btf, const char *map_name,
|
||||
__u32 expected_key_size,
|
||||
__u32 expected_value_size,
|
||||
@@ -89,8 +97,6 @@ int btf_ext__reloc_line_info(const struct btf *btf,
|
||||
LIBBPF_API __u32 btf_ext__func_info_rec_size(const struct btf_ext *btf_ext);
|
||||
LIBBPF_API __u32 btf_ext__line_info_rec_size(const struct btf_ext *btf_ext);
|
||||
|
||||
LIBBPF_API struct btf *libbpf_find_kernel_btf(void);
|
||||
|
||||
LIBBPF_API int btf__find_str(struct btf *btf, const char *s);
|
||||
LIBBPF_API int btf__add_str(struct btf *btf, const char *s);
|
||||
LIBBPF_API int btf__add_type(struct btf *btf, const struct btf *src_btf,
|
||||
@@ -184,6 +190,25 @@ LIBBPF_API int
|
||||
btf_dump__emit_type_decl(struct btf_dump *d, __u32 id,
|
||||
const struct btf_dump_emit_type_decl_opts *opts);
|
||||
|
||||
|
||||
struct btf_dump_type_data_opts {
|
||||
/* size of this struct, for forward/backward compatibility */
|
||||
size_t sz;
|
||||
const char *indent_str;
|
||||
int indent_level;
|
||||
/* below match "show" flags for bpf_show_snprintf() */
|
||||
bool compact; /* no newlines/indentation */
|
||||
bool skip_names; /* skip member/type names */
|
||||
bool emit_zeroes; /* show 0-valued fields */
|
||||
size_t :0;
|
||||
};
|
||||
#define btf_dump_type_data_opts__last_field emit_zeroes
|
||||
|
||||
LIBBPF_API int
|
||||
btf_dump__dump_type_data(struct btf_dump *d, __u32 id,
|
||||
const void *data, size_t data_sz,
|
||||
const struct btf_dump_type_data_opts *opts);
|
||||
|
||||
/*
|
||||
* A set of helpers for easier BTF types handling
|
||||
*/
|
||||
|
||||
885
src/btf_dump.c
885
src/btf_dump.c
@@ -10,6 +10,8 @@
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <endian.h>
|
||||
#include <errno.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/btf.h>
|
||||
@@ -53,6 +55,26 @@ struct btf_dump_type_aux_state {
|
||||
__u8 referenced: 1;
|
||||
};
|
||||
|
||||
/* indent string length; one indent string is added for each indent level */
|
||||
#define BTF_DATA_INDENT_STR_LEN 32
|
||||
|
||||
/*
|
||||
* Common internal data for BTF type data dump operations.
|
||||
*/
|
||||
struct btf_dump_data {
|
||||
const void *data_end; /* end of valid data to show */
|
||||
bool compact;
|
||||
bool skip_names;
|
||||
bool emit_zeroes;
|
||||
__u8 indent_lvl; /* base indent level */
|
||||
char indent_str[BTF_DATA_INDENT_STR_LEN];
|
||||
/* below are used during iteration */
|
||||
int depth;
|
||||
bool is_array_member;
|
||||
bool is_array_terminated;
|
||||
bool is_array_char;
|
||||
};
|
||||
|
||||
struct btf_dump {
|
||||
const struct btf *btf;
|
||||
const struct btf_ext *btf_ext;
|
||||
@@ -60,6 +82,7 @@ struct btf_dump {
|
||||
struct btf_dump_opts opts;
|
||||
int ptr_sz;
|
||||
bool strip_mods;
|
||||
bool skip_anon_defs;
|
||||
int last_id;
|
||||
|
||||
/* per-type auxiliary state */
|
||||
@@ -89,6 +112,10 @@ struct btf_dump {
|
||||
* name occurrences
|
||||
*/
|
||||
struct hashmap *ident_names;
|
||||
/*
|
||||
* data for typed display; allocated if needed.
|
||||
*/
|
||||
struct btf_dump_data *typed_dump;
|
||||
};
|
||||
|
||||
static size_t str_hash_fn(const void *key, void *ctx)
|
||||
@@ -128,7 +155,7 @@ struct btf_dump *btf_dump__new(const struct btf *btf,
|
||||
|
||||
d = calloc(1, sizeof(struct btf_dump));
|
||||
if (!d)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
return libbpf_err_ptr(-ENOMEM);
|
||||
|
||||
d->btf = btf;
|
||||
d->btf_ext = btf_ext;
|
||||
@@ -156,7 +183,7 @@ struct btf_dump *btf_dump__new(const struct btf *btf,
|
||||
return d;
|
||||
err:
|
||||
btf_dump__free(d);
|
||||
return ERR_PTR(err);
|
||||
return libbpf_err_ptr(err);
|
||||
}
|
||||
|
||||
static int btf_dump_resize(struct btf_dump *d)
|
||||
@@ -236,16 +263,16 @@ int btf_dump__dump_type(struct btf_dump *d, __u32 id)
|
||||
int err, i;
|
||||
|
||||
if (id > btf__get_nr_types(d->btf))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
err = btf_dump_resize(d);
|
||||
if (err)
|
||||
return err;
|
||||
return libbpf_err(err);
|
||||
|
||||
d->emit_queue_cnt = 0;
|
||||
err = btf_dump_order_type(d, id, false);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return libbpf_err(err);
|
||||
|
||||
for (i = 0; i < d->emit_queue_cnt; i++)
|
||||
btf_dump_emit_type(d, d->emit_queue[i], 0 /*top-level*/);
|
||||
@@ -765,11 +792,11 @@ static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id)
|
||||
break;
|
||||
case BTF_KIND_FUNC_PROTO: {
|
||||
const struct btf_param *p = btf_params(t);
|
||||
__u16 vlen = btf_vlen(t);
|
||||
__u16 n = btf_vlen(t);
|
||||
int i;
|
||||
|
||||
btf_dump_emit_type(d, t->type, cont_id);
|
||||
for (i = 0; i < vlen; i++, p++)
|
||||
for (i = 0; i < n; i++, p++)
|
||||
btf_dump_emit_type(d, p->type, cont_id);
|
||||
|
||||
break;
|
||||
@@ -852,8 +879,9 @@ static void btf_dump_emit_bit_padding(const struct btf_dump *d,
|
||||
static void btf_dump_emit_struct_fwd(struct btf_dump *d, __u32 id,
|
||||
const struct btf_type *t)
|
||||
{
|
||||
btf_dump_printf(d, "%s %s",
|
||||
btf_dump_printf(d, "%s%s%s",
|
||||
btf_is_struct(t) ? "struct" : "union",
|
||||
t->name_off ? " " : "",
|
||||
btf_dump_type_name(d, id));
|
||||
}
|
||||
|
||||
@@ -1075,11 +1103,11 @@ int btf_dump__emit_type_decl(struct btf_dump *d, __u32 id,
|
||||
int lvl, err;
|
||||
|
||||
if (!OPTS_VALID(opts, btf_dump_emit_type_decl_opts))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
err = btf_dump_resize(d);
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
return libbpf_err(err);
|
||||
|
||||
fname = OPTS_GET(opts, field_name, "");
|
||||
lvl = OPTS_GET(opts, indent_level, 0);
|
||||
@@ -1259,7 +1287,7 @@ static void btf_dump_emit_type_chain(struct btf_dump *d,
|
||||
case BTF_KIND_UNION:
|
||||
btf_dump_emit_mods(d, decls);
|
||||
/* inline anonymous struct/union */
|
||||
if (t->name_off == 0)
|
||||
if (t->name_off == 0 && !d->skip_anon_defs)
|
||||
btf_dump_emit_struct_def(d, id, t, lvl);
|
||||
else
|
||||
btf_dump_emit_struct_fwd(d, id, t);
|
||||
@@ -1267,7 +1295,7 @@ static void btf_dump_emit_type_chain(struct btf_dump *d,
|
||||
case BTF_KIND_ENUM:
|
||||
btf_dump_emit_mods(d, decls);
|
||||
/* inline anonymous enum */
|
||||
if (t->name_off == 0)
|
||||
if (t->name_off == 0 && !d->skip_anon_defs)
|
||||
btf_dump_emit_enum_def(d, id, t, lvl);
|
||||
else
|
||||
btf_dump_emit_enum_fwd(d, id, t);
|
||||
@@ -1392,6 +1420,39 @@ static void btf_dump_emit_type_chain(struct btf_dump *d,
|
||||
btf_dump_emit_name(d, fname, last_was_ptr);
|
||||
}
|
||||
|
||||
/* show type name as (type_name) */
|
||||
static void btf_dump_emit_type_cast(struct btf_dump *d, __u32 id,
|
||||
bool top_level)
|
||||
{
|
||||
const struct btf_type *t;
|
||||
|
||||
/* for array members, we don't bother emitting type name for each
|
||||
* member to avoid the redundancy of
|
||||
* .name = (char[4])[(char)'f',(char)'o',(char)'o',]
|
||||
*/
|
||||
if (d->typed_dump->is_array_member)
|
||||
return;
|
||||
|
||||
/* avoid type name specification for variable/section; it will be done
|
||||
* for the associated variable value(s).
|
||||
*/
|
||||
t = btf__type_by_id(d->btf, id);
|
||||
if (btf_is_var(t) || btf_is_datasec(t))
|
||||
return;
|
||||
|
||||
if (top_level)
|
||||
btf_dump_printf(d, "(");
|
||||
|
||||
d->skip_anon_defs = true;
|
||||
d->strip_mods = true;
|
||||
btf_dump_emit_type_decl(d, id, "", 0);
|
||||
d->strip_mods = false;
|
||||
d->skip_anon_defs = false;
|
||||
|
||||
if (top_level)
|
||||
btf_dump_printf(d, ")");
|
||||
}
|
||||
|
||||
/* return number of duplicates (occurrences) of a given name */
|
||||
static size_t btf_dump_name_dups(struct btf_dump *d, struct hashmap *name_map,
|
||||
const char *orig_name)
|
||||
@@ -1442,3 +1503,803 @@ static const char *btf_dump_ident_name(struct btf_dump *d, __u32 id)
|
||||
{
|
||||
return btf_dump_resolve_name(d, id, d->ident_names);
|
||||
}
|
||||
|
||||
static int btf_dump_dump_type_data(struct btf_dump *d,
|
||||
const char *fname,
|
||||
const struct btf_type *t,
|
||||
__u32 id,
|
||||
const void *data,
|
||||
__u8 bits_offset,
|
||||
__u8 bit_sz);
|
||||
|
||||
static const char *btf_dump_data_newline(struct btf_dump *d)
|
||||
{
|
||||
return d->typed_dump->compact || d->typed_dump->depth == 0 ? "" : "\n";
|
||||
}
|
||||
|
||||
static const char *btf_dump_data_delim(struct btf_dump *d)
|
||||
{
|
||||
return d->typed_dump->depth == 0 ? "" : ",";
|
||||
}
|
||||
|
||||
static void btf_dump_data_pfx(struct btf_dump *d)
|
||||
{
|
||||
int i, lvl = d->typed_dump->indent_lvl + d->typed_dump->depth;
|
||||
|
||||
if (d->typed_dump->compact)
|
||||
return;
|
||||
|
||||
for (i = 0; i < lvl; i++)
|
||||
btf_dump_printf(d, "%s", d->typed_dump->indent_str);
|
||||
}
|
||||
|
||||
/* A macro is used here as btf_type_value[s]() appends format specifiers
|
||||
* to the format specifier passed in; these do the work of appending
|
||||
* delimiters etc while the caller simply has to specify the type values
|
||||
* in the format specifier + value(s).
|
||||
*/
|
||||
#define btf_dump_type_values(d, fmt, ...) \
|
||||
btf_dump_printf(d, fmt "%s%s", \
|
||||
##__VA_ARGS__, \
|
||||
btf_dump_data_delim(d), \
|
||||
btf_dump_data_newline(d))
|
||||
|
||||
static int btf_dump_unsupported_data(struct btf_dump *d,
|
||||
const struct btf_type *t,
|
||||
__u32 id)
|
||||
{
|
||||
btf_dump_printf(d, "<unsupported kind:%u>", btf_kind(t));
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int btf_dump_get_bitfield_value(struct btf_dump *d,
|
||||
const struct btf_type *t,
|
||||
const void *data,
|
||||
__u8 bits_offset,
|
||||
__u8 bit_sz,
|
||||
__u64 *value)
|
||||
{
|
||||
__u16 left_shift_bits, right_shift_bits;
|
||||
__u8 nr_copy_bits, nr_copy_bytes;
|
||||
const __u8 *bytes = data;
|
||||
int sz = t->size;
|
||||
__u64 num = 0;
|
||||
int i;
|
||||
|
||||
/* Maximum supported bitfield size is 64 bits */
|
||||
if (sz > 8) {
|
||||
pr_warn("unexpected bitfield size %d\n", sz);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Bitfield value retrieval is done in two steps; first relevant bytes are
|
||||
* stored in num, then we left/right shift num to eliminate irrelevant bits.
|
||||
*/
|
||||
nr_copy_bits = bit_sz + bits_offset;
|
||||
nr_copy_bytes = t->size;
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
for (i = nr_copy_bytes - 1; i >= 0; i--)
|
||||
num = num * 256 + bytes[i];
|
||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
for (i = 0; i < nr_copy_bytes; i++)
|
||||
num = num * 256 + bytes[i];
|
||||
#else
|
||||
# error "Unrecognized __BYTE_ORDER__"
|
||||
#endif
|
||||
left_shift_bits = 64 - nr_copy_bits;
|
||||
right_shift_bits = 64 - bit_sz;
|
||||
|
||||
*value = (num << left_shift_bits) >> right_shift_bits;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btf_dump_bitfield_check_zero(struct btf_dump *d,
|
||||
const struct btf_type *t,
|
||||
const void *data,
|
||||
__u8 bits_offset,
|
||||
__u8 bit_sz)
|
||||
{
|
||||
__u64 check_num;
|
||||
int err;
|
||||
|
||||
err = btf_dump_get_bitfield_value(d, t, data, bits_offset, bit_sz, &check_num);
|
||||
if (err)
|
||||
return err;
|
||||
if (check_num == 0)
|
||||
return -ENODATA;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btf_dump_bitfield_data(struct btf_dump *d,
|
||||
const struct btf_type *t,
|
||||
const void *data,
|
||||
__u8 bits_offset,
|
||||
__u8 bit_sz)
|
||||
{
|
||||
__u64 print_num;
|
||||
int err;
|
||||
|
||||
err = btf_dump_get_bitfield_value(d, t, data, bits_offset, bit_sz, &print_num);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
btf_dump_type_values(d, "0x%llx", (unsigned long long)print_num);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ints, floats and ptrs */
|
||||
static int btf_dump_base_type_check_zero(struct btf_dump *d,
|
||||
const struct btf_type *t,
|
||||
__u32 id,
|
||||
const void *data)
|
||||
{
|
||||
static __u8 bytecmp[16] = {};
|
||||
int nr_bytes;
|
||||
|
||||
/* For pointer types, pointer size is not defined on a per-type basis.
|
||||
* On dump creation however, we store the pointer size.
|
||||
*/
|
||||
if (btf_kind(t) == BTF_KIND_PTR)
|
||||
nr_bytes = d->ptr_sz;
|
||||
else
|
||||
nr_bytes = t->size;
|
||||
|
||||
if (nr_bytes < 1 || nr_bytes > 16) {
|
||||
pr_warn("unexpected size %d for id [%u]\n", nr_bytes, id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (memcmp(data, bytecmp, nr_bytes) == 0)
|
||||
return -ENODATA;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool ptr_is_aligned(const void *data, int data_sz)
|
||||
{
|
||||
return ((uintptr_t)data) % data_sz == 0;
|
||||
}
|
||||
|
||||
static int btf_dump_int_data(struct btf_dump *d,
|
||||
const struct btf_type *t,
|
||||
__u32 type_id,
|
||||
const void *data,
|
||||
__u8 bits_offset)
|
||||
{
|
||||
__u8 encoding = btf_int_encoding(t);
|
||||
bool sign = encoding & BTF_INT_SIGNED;
|
||||
int sz = t->size;
|
||||
|
||||
if (sz == 0) {
|
||||
pr_warn("unexpected size %d for id [%u]\n", sz, type_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* handle packed int data - accesses of integers not aligned on
|
||||
* int boundaries can cause problems on some platforms.
|
||||
*/
|
||||
if (!ptr_is_aligned(data, sz))
|
||||
return btf_dump_bitfield_data(d, t, data, 0, 0);
|
||||
|
||||
switch (sz) {
|
||||
case 16: {
|
||||
const __u64 *ints = data;
|
||||
__u64 lsi, msi;
|
||||
|
||||
/* avoid use of __int128 as some 32-bit platforms do not
|
||||
* support it.
|
||||
*/
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
lsi = ints[0];
|
||||
msi = ints[1];
|
||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
lsi = ints[1];
|
||||
msi = ints[0];
|
||||
#else
|
||||
# error "Unrecognized __BYTE_ORDER__"
|
||||
#endif
|
||||
if (msi == 0)
|
||||
btf_dump_type_values(d, "0x%llx", (unsigned long long)lsi);
|
||||
else
|
||||
btf_dump_type_values(d, "0x%llx%016llx", (unsigned long long)msi,
|
||||
(unsigned long long)lsi);
|
||||
break;
|
||||
}
|
||||
case 8:
|
||||
if (sign)
|
||||
btf_dump_type_values(d, "%lld", *(long long *)data);
|
||||
else
|
||||
btf_dump_type_values(d, "%llu", *(unsigned long long *)data);
|
||||
break;
|
||||
case 4:
|
||||
if (sign)
|
||||
btf_dump_type_values(d, "%d", *(__s32 *)data);
|
||||
else
|
||||
btf_dump_type_values(d, "%u", *(__u32 *)data);
|
||||
break;
|
||||
case 2:
|
||||
if (sign)
|
||||
btf_dump_type_values(d, "%d", *(__s16 *)data);
|
||||
else
|
||||
btf_dump_type_values(d, "%u", *(__u16 *)data);
|
||||
break;
|
||||
case 1:
|
||||
if (d->typed_dump->is_array_char) {
|
||||
/* check for null terminator */
|
||||
if (d->typed_dump->is_array_terminated)
|
||||
break;
|
||||
if (*(char *)data == '\0') {
|
||||
d->typed_dump->is_array_terminated = true;
|
||||
break;
|
||||
}
|
||||
if (isprint(*(char *)data)) {
|
||||
btf_dump_type_values(d, "'%c'", *(char *)data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sign)
|
||||
btf_dump_type_values(d, "%d", *(__s8 *)data);
|
||||
else
|
||||
btf_dump_type_values(d, "%u", *(__u8 *)data);
|
||||
break;
|
||||
default:
|
||||
pr_warn("unexpected sz %d for id [%u]\n", sz, type_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
union float_data {
|
||||
long double ld;
|
||||
double d;
|
||||
float f;
|
||||
};
|
||||
|
||||
static int btf_dump_float_data(struct btf_dump *d,
|
||||
const struct btf_type *t,
|
||||
__u32 type_id,
|
||||
const void *data)
|
||||
{
|
||||
const union float_data *flp = data;
|
||||
union float_data fl;
|
||||
int sz = t->size;
|
||||
|
||||
/* handle unaligned data; copy to local union */
|
||||
if (!ptr_is_aligned(data, sz)) {
|
||||
memcpy(&fl, data, sz);
|
||||
flp = &fl;
|
||||
}
|
||||
|
||||
switch (sz) {
|
||||
case 16:
|
||||
btf_dump_type_values(d, "%Lf", flp->ld);
|
||||
break;
|
||||
case 8:
|
||||
btf_dump_type_values(d, "%lf", flp->d);
|
||||
break;
|
||||
case 4:
|
||||
btf_dump_type_values(d, "%f", flp->f);
|
||||
break;
|
||||
default:
|
||||
pr_warn("unexpected size %d for id [%u]\n", sz, type_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btf_dump_var_data(struct btf_dump *d,
|
||||
const struct btf_type *v,
|
||||
__u32 id,
|
||||
const void *data)
|
||||
{
|
||||
enum btf_func_linkage linkage = btf_var(v)->linkage;
|
||||
const struct btf_type *t;
|
||||
const char *l;
|
||||
__u32 type_id;
|
||||
|
||||
switch (linkage) {
|
||||
case BTF_FUNC_STATIC:
|
||||
l = "static ";
|
||||
break;
|
||||
case BTF_FUNC_EXTERN:
|
||||
l = "extern ";
|
||||
break;
|
||||
case BTF_FUNC_GLOBAL:
|
||||
default:
|
||||
l = "";
|
||||
break;
|
||||
}
|
||||
|
||||
/* format of output here is [linkage] [type] [varname] = (type)value,
|
||||
* for example "static int cpu_profile_flip = (int)1"
|
||||
*/
|
||||
btf_dump_printf(d, "%s", l);
|
||||
type_id = v->type;
|
||||
t = btf__type_by_id(d->btf, type_id);
|
||||
btf_dump_emit_type_cast(d, type_id, false);
|
||||
btf_dump_printf(d, " %s = ", btf_name_of(d, v->name_off));
|
||||
return btf_dump_dump_type_data(d, NULL, t, type_id, data, 0, 0);
|
||||
}
|
||||
|
||||
static int btf_dump_array_data(struct btf_dump *d,
|
||||
const struct btf_type *t,
|
||||
__u32 id,
|
||||
const void *data)
|
||||
{
|
||||
const struct btf_array *array = btf_array(t);
|
||||
const struct btf_type *elem_type;
|
||||
__u32 i, elem_size = 0, elem_type_id;
|
||||
bool is_array_member;
|
||||
|
||||
elem_type_id = array->type;
|
||||
elem_type = skip_mods_and_typedefs(d->btf, elem_type_id, NULL);
|
||||
elem_size = btf__resolve_size(d->btf, elem_type_id);
|
||||
if (elem_size <= 0) {
|
||||
pr_warn("unexpected elem size %d for array type [%u]\n", elem_size, id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (btf_is_int(elem_type)) {
|
||||
/*
|
||||
* BTF_INT_CHAR encoding never seems to be set for
|
||||
* char arrays, so if size is 1 and element is
|
||||
* printable as a char, we'll do that.
|
||||
*/
|
||||
if (elem_size == 1)
|
||||
d->typed_dump->is_array_char = true;
|
||||
}
|
||||
|
||||
/* note that we increment depth before calling btf_dump_print() below;
|
||||
* this is intentional. btf_dump_data_newline() will not print a
|
||||
* newline for depth 0 (since this leaves us with trailing newlines
|
||||
* at the end of typed display), so depth is incremented first.
|
||||
* For similar reasons, we decrement depth before showing the closing
|
||||
* parenthesis.
|
||||
*/
|
||||
d->typed_dump->depth++;
|
||||
btf_dump_printf(d, "[%s", btf_dump_data_newline(d));
|
||||
|
||||
/* may be a multidimensional array, so store current "is array member"
|
||||
* status so we can restore it correctly later.
|
||||
*/
|
||||
is_array_member = d->typed_dump->is_array_member;
|
||||
d->typed_dump->is_array_member = true;
|
||||
for (i = 0; i < array->nelems; i++, data += elem_size) {
|
||||
if (d->typed_dump->is_array_terminated)
|
||||
break;
|
||||
btf_dump_dump_type_data(d, NULL, elem_type, elem_type_id, data, 0, 0);
|
||||
}
|
||||
d->typed_dump->is_array_member = is_array_member;
|
||||
d->typed_dump->depth--;
|
||||
btf_dump_data_pfx(d);
|
||||
btf_dump_type_values(d, "]");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btf_dump_struct_data(struct btf_dump *d,
|
||||
const struct btf_type *t,
|
||||
__u32 id,
|
||||
const void *data)
|
||||
{
|
||||
const struct btf_member *m = btf_members(t);
|
||||
__u16 n = btf_vlen(t);
|
||||
int i, err;
|
||||
|
||||
/* note that we increment depth before calling btf_dump_print() below;
|
||||
* this is intentional. btf_dump_data_newline() will not print a
|
||||
* newline for depth 0 (since this leaves us with trailing newlines
|
||||
* at the end of typed display), so depth is incremented first.
|
||||
* For similar reasons, we decrement depth before showing the closing
|
||||
* parenthesis.
|
||||
*/
|
||||
d->typed_dump->depth++;
|
||||
btf_dump_printf(d, "{%s", btf_dump_data_newline(d));
|
||||
|
||||
for (i = 0; i < n; i++, m++) {
|
||||
const struct btf_type *mtype;
|
||||
const char *mname;
|
||||
__u32 moffset;
|
||||
__u8 bit_sz;
|
||||
|
||||
mtype = btf__type_by_id(d->btf, m->type);
|
||||
mname = btf_name_of(d, m->name_off);
|
||||
moffset = btf_member_bit_offset(t, i);
|
||||
|
||||
bit_sz = btf_member_bitfield_size(t, i);
|
||||
err = btf_dump_dump_type_data(d, mname, mtype, m->type, data + moffset / 8,
|
||||
moffset % 8, bit_sz);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
d->typed_dump->depth--;
|
||||
btf_dump_data_pfx(d);
|
||||
btf_dump_type_values(d, "}");
|
||||
return err;
|
||||
}
|
||||
|
||||
union ptr_data {
|
||||
unsigned int p;
|
||||
unsigned long long lp;
|
||||
};
|
||||
|
||||
static int btf_dump_ptr_data(struct btf_dump *d,
|
||||
const struct btf_type *t,
|
||||
__u32 id,
|
||||
const void *data)
|
||||
{
|
||||
if (ptr_is_aligned(data, d->ptr_sz) && d->ptr_sz == sizeof(void *)) {
|
||||
btf_dump_type_values(d, "%p", *(void **)data);
|
||||
} else {
|
||||
union ptr_data pt;
|
||||
|
||||
memcpy(&pt, data, d->ptr_sz);
|
||||
if (d->ptr_sz == 4)
|
||||
btf_dump_type_values(d, "0x%x", pt.p);
|
||||
else
|
||||
btf_dump_type_values(d, "0x%llx", pt.lp);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btf_dump_get_enum_value(struct btf_dump *d,
|
||||
const struct btf_type *t,
|
||||
const void *data,
|
||||
__u32 id,
|
||||
__s64 *value)
|
||||
{
|
||||
int sz = t->size;
|
||||
|
||||
/* handle unaligned enum value */
|
||||
if (!ptr_is_aligned(data, sz)) {
|
||||
__u64 val;
|
||||
int err;
|
||||
|
||||
err = btf_dump_get_bitfield_value(d, t, data, 0, 0, &val);
|
||||
if (err)
|
||||
return err;
|
||||
*value = (__s64)val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (t->size) {
|
||||
case 8:
|
||||
*value = *(__s64 *)data;
|
||||
return 0;
|
||||
case 4:
|
||||
*value = *(__s32 *)data;
|
||||
return 0;
|
||||
case 2:
|
||||
*value = *(__s16 *)data;
|
||||
return 0;
|
||||
case 1:
|
||||
*value = *(__s8 *)data;
|
||||
return 0;
|
||||
default:
|
||||
pr_warn("unexpected size %d for enum, id:[%u]\n", t->size, id);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int btf_dump_enum_data(struct btf_dump *d,
|
||||
const struct btf_type *t,
|
||||
__u32 id,
|
||||
const void *data)
|
||||
{
|
||||
const struct btf_enum *e;
|
||||
__s64 value;
|
||||
int i, err;
|
||||
|
||||
err = btf_dump_get_enum_value(d, t, data, id, &value);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
for (i = 0, e = btf_enum(t); i < btf_vlen(t); i++, e++) {
|
||||
if (value != e->val)
|
||||
continue;
|
||||
btf_dump_type_values(d, "%s", btf_name_of(d, e->name_off));
|
||||
return 0;
|
||||
}
|
||||
|
||||
btf_dump_type_values(d, "%d", value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btf_dump_datasec_data(struct btf_dump *d,
|
||||
const struct btf_type *t,
|
||||
__u32 id,
|
||||
const void *data)
|
||||
{
|
||||
const struct btf_var_secinfo *vsi;
|
||||
const struct btf_type *var;
|
||||
__u32 i;
|
||||
int err;
|
||||
|
||||
btf_dump_type_values(d, "SEC(\"%s\") ", btf_name_of(d, t->name_off));
|
||||
|
||||
for (i = 0, vsi = btf_var_secinfos(t); i < btf_vlen(t); i++, vsi++) {
|
||||
var = btf__type_by_id(d->btf, vsi->type);
|
||||
err = btf_dump_dump_type_data(d, NULL, var, vsi->type, data + vsi->offset, 0, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
btf_dump_printf(d, ";");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return size of type, or if base type overflows, return -E2BIG. */
|
||||
static int btf_dump_type_data_check_overflow(struct btf_dump *d,
|
||||
const struct btf_type *t,
|
||||
__u32 id,
|
||||
const void *data,
|
||||
__u8 bits_offset)
|
||||
{
|
||||
__s64 size = btf__resolve_size(d->btf, id);
|
||||
|
||||
if (size < 0 || size >= INT_MAX) {
|
||||
pr_warn("unexpected size [%zu] for id [%u]\n",
|
||||
(size_t)size, id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Only do overflow checking for base types; we do not want to
|
||||
* avoid showing part of a struct, union or array, even if we
|
||||
* do not have enough data to show the full object. By
|
||||
* restricting overflow checking to base types we can ensure
|
||||
* that partial display succeeds, while avoiding overflowing
|
||||
* and using bogus data for display.
|
||||
*/
|
||||
t = skip_mods_and_typedefs(d->btf, id, NULL);
|
||||
if (!t) {
|
||||
pr_warn("unexpected error skipping mods/typedefs for id [%u]\n",
|
||||
id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (btf_kind(t)) {
|
||||
case BTF_KIND_INT:
|
||||
case BTF_KIND_FLOAT:
|
||||
case BTF_KIND_PTR:
|
||||
case BTF_KIND_ENUM:
|
||||
if (data + bits_offset / 8 + size > d->typed_dump->data_end)
|
||||
return -E2BIG;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return (int)size;
|
||||
}
|
||||
|
||||
static int btf_dump_type_data_check_zero(struct btf_dump *d,
|
||||
const struct btf_type *t,
|
||||
__u32 id,
|
||||
const void *data,
|
||||
__u8 bits_offset,
|
||||
__u8 bit_sz)
|
||||
{
|
||||
__s64 value;
|
||||
int i, err;
|
||||
|
||||
/* toplevel exceptions; we show zero values if
|
||||
* - we ask for them (emit_zeros)
|
||||
* - if we are at top-level so we see "struct empty { }"
|
||||
* - or if we are an array member and the array is non-empty and
|
||||
* not a char array; we don't want to be in a situation where we
|
||||
* have an integer array 0, 1, 0, 1 and only show non-zero values.
|
||||
* If the array contains zeroes only, or is a char array starting
|
||||
* with a '\0', the array-level check_zero() will prevent showing it;
|
||||
* we are concerned with determining zero value at the array member
|
||||
* level here.
|
||||
*/
|
||||
if (d->typed_dump->emit_zeroes || d->typed_dump->depth == 0 ||
|
||||
(d->typed_dump->is_array_member &&
|
||||
!d->typed_dump->is_array_char))
|
||||
return 0;
|
||||
|
||||
t = skip_mods_and_typedefs(d->btf, id, NULL);
|
||||
|
||||
switch (btf_kind(t)) {
|
||||
case BTF_KIND_INT:
|
||||
if (bit_sz)
|
||||
return btf_dump_bitfield_check_zero(d, t, data, bits_offset, bit_sz);
|
||||
return btf_dump_base_type_check_zero(d, t, id, data);
|
||||
case BTF_KIND_FLOAT:
|
||||
case BTF_KIND_PTR:
|
||||
return btf_dump_base_type_check_zero(d, t, id, data);
|
||||
case BTF_KIND_ARRAY: {
|
||||
const struct btf_array *array = btf_array(t);
|
||||
const struct btf_type *elem_type;
|
||||
__u32 elem_type_id, elem_size;
|
||||
bool ischar;
|
||||
|
||||
elem_type_id = array->type;
|
||||
elem_size = btf__resolve_size(d->btf, elem_type_id);
|
||||
elem_type = skip_mods_and_typedefs(d->btf, elem_type_id, NULL);
|
||||
|
||||
ischar = btf_is_int(elem_type) && elem_size == 1;
|
||||
|
||||
/* check all elements; if _any_ element is nonzero, all
|
||||
* of array is displayed. We make an exception however
|
||||
* for char arrays where the first element is 0; these
|
||||
* are considered zeroed also, even if later elements are
|
||||
* non-zero because the string is terminated.
|
||||
*/
|
||||
for (i = 0; i < array->nelems; i++) {
|
||||
if (i == 0 && ischar && *(char *)data == 0)
|
||||
return -ENODATA;
|
||||
err = btf_dump_type_data_check_zero(d, elem_type,
|
||||
elem_type_id,
|
||||
data +
|
||||
(i * elem_size),
|
||||
bits_offset, 0);
|
||||
if (err != -ENODATA)
|
||||
return err;
|
||||
}
|
||||
return -ENODATA;
|
||||
}
|
||||
case BTF_KIND_STRUCT:
|
||||
case BTF_KIND_UNION: {
|
||||
const struct btf_member *m = btf_members(t);
|
||||
__u16 n = btf_vlen(t);
|
||||
|
||||
/* if any struct/union member is non-zero, the struct/union
|
||||
* is considered non-zero and dumped.
|
||||
*/
|
||||
for (i = 0; i < n; i++, m++) {
|
||||
const struct btf_type *mtype;
|
||||
__u32 moffset;
|
||||
|
||||
mtype = btf__type_by_id(d->btf, m->type);
|
||||
moffset = btf_member_bit_offset(t, i);
|
||||
|
||||
/* btf_int_bits() does not store member bitfield size;
|
||||
* bitfield size needs to be stored here so int display
|
||||
* of member can retrieve it.
|
||||
*/
|
||||
bit_sz = btf_member_bitfield_size(t, i);
|
||||
err = btf_dump_type_data_check_zero(d, mtype, m->type, data + moffset / 8,
|
||||
moffset % 8, bit_sz);
|
||||
if (err != ENODATA)
|
||||
return err;
|
||||
}
|
||||
return -ENODATA;
|
||||
}
|
||||
case BTF_KIND_ENUM:
|
||||
err = btf_dump_get_enum_value(d, t, data, id, &value);
|
||||
if (err)
|
||||
return err;
|
||||
if (value == 0)
|
||||
return -ENODATA;
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* returns size of data dumped, or error. */
|
||||
static int btf_dump_dump_type_data(struct btf_dump *d,
|
||||
const char *fname,
|
||||
const struct btf_type *t,
|
||||
__u32 id,
|
||||
const void *data,
|
||||
__u8 bits_offset,
|
||||
__u8 bit_sz)
|
||||
{
|
||||
int size, err;
|
||||
|
||||
size = btf_dump_type_data_check_overflow(d, t, id, data, bits_offset);
|
||||
if (size < 0)
|
||||
return size;
|
||||
err = btf_dump_type_data_check_zero(d, t, id, data, bits_offset, bit_sz);
|
||||
if (err) {
|
||||
/* zeroed data is expected and not an error, so simply skip
|
||||
* dumping such data. Record other errors however.
|
||||
*/
|
||||
if (err == -ENODATA)
|
||||
return size;
|
||||
return err;
|
||||
}
|
||||
btf_dump_data_pfx(d);
|
||||
|
||||
if (!d->typed_dump->skip_names) {
|
||||
if (fname && strlen(fname) > 0)
|
||||
btf_dump_printf(d, ".%s = ", fname);
|
||||
btf_dump_emit_type_cast(d, id, true);
|
||||
}
|
||||
|
||||
t = skip_mods_and_typedefs(d->btf, id, NULL);
|
||||
|
||||
switch (btf_kind(t)) {
|
||||
case BTF_KIND_UNKN:
|
||||
case BTF_KIND_FWD:
|
||||
case BTF_KIND_FUNC:
|
||||
case BTF_KIND_FUNC_PROTO:
|
||||
err = btf_dump_unsupported_data(d, t, id);
|
||||
break;
|
||||
case BTF_KIND_INT:
|
||||
if (bit_sz)
|
||||
err = btf_dump_bitfield_data(d, t, data, bits_offset, bit_sz);
|
||||
else
|
||||
err = btf_dump_int_data(d, t, id, data, bits_offset);
|
||||
break;
|
||||
case BTF_KIND_FLOAT:
|
||||
err = btf_dump_float_data(d, t, id, data);
|
||||
break;
|
||||
case BTF_KIND_PTR:
|
||||
err = btf_dump_ptr_data(d, t, id, data);
|
||||
break;
|
||||
case BTF_KIND_ARRAY:
|
||||
err = btf_dump_array_data(d, t, id, data);
|
||||
break;
|
||||
case BTF_KIND_STRUCT:
|
||||
case BTF_KIND_UNION:
|
||||
err = btf_dump_struct_data(d, t, id, data);
|
||||
break;
|
||||
case BTF_KIND_ENUM:
|
||||
/* handle bitfield and int enum values */
|
||||
if (bit_sz) {
|
||||
__u64 print_num;
|
||||
__s64 enum_val;
|
||||
|
||||
err = btf_dump_get_bitfield_value(d, t, data, bits_offset, bit_sz,
|
||||
&print_num);
|
||||
if (err)
|
||||
break;
|
||||
enum_val = (__s64)print_num;
|
||||
err = btf_dump_enum_data(d, t, id, &enum_val);
|
||||
} else
|
||||
err = btf_dump_enum_data(d, t, id, data);
|
||||
break;
|
||||
case BTF_KIND_VAR:
|
||||
err = btf_dump_var_data(d, t, id, data);
|
||||
break;
|
||||
case BTF_KIND_DATASEC:
|
||||
err = btf_dump_datasec_data(d, t, id, data);
|
||||
break;
|
||||
default:
|
||||
pr_warn("unexpected kind [%u] for id [%u]\n",
|
||||
BTF_INFO_KIND(t->info), id);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (err < 0)
|
||||
return err;
|
||||
return size;
|
||||
}
|
||||
|
||||
int btf_dump__dump_type_data(struct btf_dump *d, __u32 id,
|
||||
const void *data, size_t data_sz,
|
||||
const struct btf_dump_type_data_opts *opts)
|
||||
{
|
||||
struct btf_dump_data typed_dump = {};
|
||||
const struct btf_type *t;
|
||||
int ret;
|
||||
|
||||
if (!OPTS_VALID(opts, btf_dump_type_data_opts))
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
t = btf__type_by_id(d->btf, id);
|
||||
if (!t)
|
||||
return libbpf_err(-ENOENT);
|
||||
|
||||
d->typed_dump = &typed_dump;
|
||||
d->typed_dump->data_end = data + data_sz;
|
||||
d->typed_dump->indent_lvl = OPTS_GET(opts, indent_level, 0);
|
||||
|
||||
/* default indent string is a tab */
|
||||
if (!opts->indent_str)
|
||||
d->typed_dump->indent_str[0] = '\t';
|
||||
else
|
||||
strncat(d->typed_dump->indent_str, opts->indent_str,
|
||||
sizeof(d->typed_dump->indent_str) - 1);
|
||||
|
||||
d->typed_dump->compact = OPTS_GET(opts, compact, false);
|
||||
d->typed_dump->skip_names = OPTS_GET(opts, skip_names, false);
|
||||
d->typed_dump->emit_zeroes = OPTS_GET(opts, emit_zeroes, false);
|
||||
|
||||
ret = btf_dump_dump_type_data(d, NULL, t, id, data, 0, 0);
|
||||
|
||||
d->typed_dump = NULL;
|
||||
|
||||
return libbpf_err(ret);
|
||||
}
|
||||
|
||||
729
src/gen_loader.c
Normal file
729
src/gen_loader.c
Normal file
@@ -0,0 +1,729 @@
|
||||
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
/* Copyright (c) 2021 Facebook */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <linux/filter.h>
|
||||
#include "btf.h"
|
||||
#include "bpf.h"
|
||||
#include "libbpf.h"
|
||||
#include "libbpf_internal.h"
|
||||
#include "hashmap.h"
|
||||
#include "bpf_gen_internal.h"
|
||||
#include "skel_internal.h"
|
||||
|
||||
#define MAX_USED_MAPS 64
|
||||
#define MAX_USED_PROGS 32
|
||||
|
||||
/* The following structure describes the stack layout of the loader program.
|
||||
* In addition R6 contains the pointer to context.
|
||||
* R7 contains the result of the last sys_bpf command (typically error or FD).
|
||||
* R9 contains the result of the last sys_close command.
|
||||
*
|
||||
* Naming convention:
|
||||
* ctx - bpf program context
|
||||
* stack - bpf program stack
|
||||
* blob - bpf_attr-s, strings, insns, map data.
|
||||
* All the bytes that loader prog will use for read/write.
|
||||
*/
|
||||
struct loader_stack {
|
||||
__u32 btf_fd;
|
||||
__u32 map_fd[MAX_USED_MAPS];
|
||||
__u32 prog_fd[MAX_USED_PROGS];
|
||||
__u32 inner_map_fd;
|
||||
};
|
||||
|
||||
#define stack_off(field) \
|
||||
(__s16)(-sizeof(struct loader_stack) + offsetof(struct loader_stack, field))
|
||||
|
||||
#define attr_field(attr, field) (attr + offsetof(union bpf_attr, field))
|
||||
|
||||
static int realloc_insn_buf(struct bpf_gen *gen, __u32 size)
|
||||
{
|
||||
size_t off = gen->insn_cur - gen->insn_start;
|
||||
void *insn_start;
|
||||
|
||||
if (gen->error)
|
||||
return gen->error;
|
||||
if (size > INT32_MAX || off + size > INT32_MAX) {
|
||||
gen->error = -ERANGE;
|
||||
return -ERANGE;
|
||||
}
|
||||
insn_start = realloc(gen->insn_start, off + size);
|
||||
if (!insn_start) {
|
||||
gen->error = -ENOMEM;
|
||||
free(gen->insn_start);
|
||||
gen->insn_start = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
gen->insn_start = insn_start;
|
||||
gen->insn_cur = insn_start + off;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int realloc_data_buf(struct bpf_gen *gen, __u32 size)
|
||||
{
|
||||
size_t off = gen->data_cur - gen->data_start;
|
||||
void *data_start;
|
||||
|
||||
if (gen->error)
|
||||
return gen->error;
|
||||
if (size > INT32_MAX || off + size > INT32_MAX) {
|
||||
gen->error = -ERANGE;
|
||||
return -ERANGE;
|
||||
}
|
||||
data_start = realloc(gen->data_start, off + size);
|
||||
if (!data_start) {
|
||||
gen->error = -ENOMEM;
|
||||
free(gen->data_start);
|
||||
gen->data_start = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
gen->data_start = data_start;
|
||||
gen->data_cur = data_start + off;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void emit(struct bpf_gen *gen, struct bpf_insn insn)
|
||||
{
|
||||
if (realloc_insn_buf(gen, sizeof(insn)))
|
||||
return;
|
||||
memcpy(gen->insn_cur, &insn, sizeof(insn));
|
||||
gen->insn_cur += sizeof(insn);
|
||||
}
|
||||
|
||||
static void emit2(struct bpf_gen *gen, struct bpf_insn insn1, struct bpf_insn insn2)
|
||||
{
|
||||
emit(gen, insn1);
|
||||
emit(gen, insn2);
|
||||
}
|
||||
|
||||
void bpf_gen__init(struct bpf_gen *gen, int log_level)
|
||||
{
|
||||
size_t stack_sz = sizeof(struct loader_stack);
|
||||
int i;
|
||||
|
||||
gen->log_level = log_level;
|
||||
/* save ctx pointer into R6 */
|
||||
emit(gen, BPF_MOV64_REG(BPF_REG_6, BPF_REG_1));
|
||||
|
||||
/* bzero stack */
|
||||
emit(gen, BPF_MOV64_REG(BPF_REG_1, BPF_REG_10));
|
||||
emit(gen, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -stack_sz));
|
||||
emit(gen, BPF_MOV64_IMM(BPF_REG_2, stack_sz));
|
||||
emit(gen, BPF_MOV64_IMM(BPF_REG_3, 0));
|
||||
emit(gen, BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel));
|
||||
|
||||
/* jump over cleanup code */
|
||||
emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0,
|
||||
/* size of cleanup code below */
|
||||
(stack_sz / 4) * 3 + 2));
|
||||
|
||||
/* remember the label where all error branches will jump to */
|
||||
gen->cleanup_label = gen->insn_cur - gen->insn_start;
|
||||
/* emit cleanup code: close all temp FDs */
|
||||
for (i = 0; i < stack_sz; i += 4) {
|
||||
emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_10, -stack_sz + i));
|
||||
emit(gen, BPF_JMP_IMM(BPF_JSLE, BPF_REG_1, 0, 1));
|
||||
emit(gen, BPF_EMIT_CALL(BPF_FUNC_sys_close));
|
||||
}
|
||||
/* R7 contains the error code from sys_bpf. Copy it into R0 and exit. */
|
||||
emit(gen, BPF_MOV64_REG(BPF_REG_0, BPF_REG_7));
|
||||
emit(gen, BPF_EXIT_INSN());
|
||||
}
|
||||
|
||||
static int add_data(struct bpf_gen *gen, const void *data, __u32 size)
|
||||
{
|
||||
void *prev;
|
||||
|
||||
if (realloc_data_buf(gen, size))
|
||||
return 0;
|
||||
prev = gen->data_cur;
|
||||
memcpy(gen->data_cur, data, size);
|
||||
gen->data_cur += size;
|
||||
return prev - gen->data_start;
|
||||
}
|
||||
|
||||
static int insn_bytes_to_bpf_size(__u32 sz)
|
||||
{
|
||||
switch (sz) {
|
||||
case 8: return BPF_DW;
|
||||
case 4: return BPF_W;
|
||||
case 2: return BPF_H;
|
||||
case 1: return BPF_B;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* *(u64 *)(blob + off) = (u64)(void *)(blob + data) */
|
||||
static void emit_rel_store(struct bpf_gen *gen, int off, int data)
|
||||
{
|
||||
emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_0, BPF_PSEUDO_MAP_IDX_VALUE,
|
||||
0, 0, 0, data));
|
||||
emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE,
|
||||
0, 0, 0, off));
|
||||
emit(gen, BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, 0));
|
||||
}
|
||||
|
||||
/* *(u64 *)(blob + off) = (u64)(void *)(%sp + stack_off) */
|
||||
static void emit_rel_store_sp(struct bpf_gen *gen, int off, int stack_off)
|
||||
{
|
||||
emit(gen, BPF_MOV64_REG(BPF_REG_0, BPF_REG_10));
|
||||
emit(gen, BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, stack_off));
|
||||
emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE,
|
||||
0, 0, 0, off));
|
||||
emit(gen, BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, 0));
|
||||
}
|
||||
|
||||
static void move_ctx2blob(struct bpf_gen *gen, int off, int size, int ctx_off,
|
||||
bool check_non_zero)
|
||||
{
|
||||
emit(gen, BPF_LDX_MEM(insn_bytes_to_bpf_size(size), BPF_REG_0, BPF_REG_6, ctx_off));
|
||||
if (check_non_zero)
|
||||
/* If value in ctx is zero don't update the blob.
|
||||
* For example: when ctx->map.max_entries == 0, keep default max_entries from bpf.c
|
||||
*/
|
||||
emit(gen, BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 3));
|
||||
emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE,
|
||||
0, 0, 0, off));
|
||||
emit(gen, BPF_STX_MEM(insn_bytes_to_bpf_size(size), BPF_REG_1, BPF_REG_0, 0));
|
||||
}
|
||||
|
||||
static void move_stack2blob(struct bpf_gen *gen, int off, int size, int stack_off)
|
||||
{
|
||||
emit(gen, BPF_LDX_MEM(insn_bytes_to_bpf_size(size), BPF_REG_0, BPF_REG_10, stack_off));
|
||||
emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE,
|
||||
0, 0, 0, off));
|
||||
emit(gen, BPF_STX_MEM(insn_bytes_to_bpf_size(size), BPF_REG_1, BPF_REG_0, 0));
|
||||
}
|
||||
|
||||
static void move_stack2ctx(struct bpf_gen *gen, int ctx_off, int size, int stack_off)
|
||||
{
|
||||
emit(gen, BPF_LDX_MEM(insn_bytes_to_bpf_size(size), BPF_REG_0, BPF_REG_10, stack_off));
|
||||
emit(gen, BPF_STX_MEM(insn_bytes_to_bpf_size(size), BPF_REG_6, BPF_REG_0, ctx_off));
|
||||
}
|
||||
|
||||
static void emit_sys_bpf(struct bpf_gen *gen, int cmd, int attr, int attr_size)
|
||||
{
|
||||
emit(gen, BPF_MOV64_IMM(BPF_REG_1, cmd));
|
||||
emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_2, BPF_PSEUDO_MAP_IDX_VALUE,
|
||||
0, 0, 0, attr));
|
||||
emit(gen, BPF_MOV64_IMM(BPF_REG_3, attr_size));
|
||||
emit(gen, BPF_EMIT_CALL(BPF_FUNC_sys_bpf));
|
||||
/* remember the result in R7 */
|
||||
emit(gen, BPF_MOV64_REG(BPF_REG_7, BPF_REG_0));
|
||||
}
|
||||
|
||||
static bool is_simm16(__s64 value)
|
||||
{
|
||||
return value == (__s64)(__s16)value;
|
||||
}
|
||||
|
||||
static void emit_check_err(struct bpf_gen *gen)
|
||||
{
|
||||
__s64 off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 1;
|
||||
|
||||
/* R7 contains result of last sys_bpf command.
|
||||
* if (R7 < 0) goto cleanup;
|
||||
*/
|
||||
if (is_simm16(off)) {
|
||||
emit(gen, BPF_JMP_IMM(BPF_JSLT, BPF_REG_7, 0, off));
|
||||
} else {
|
||||
gen->error = -ERANGE;
|
||||
emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, -1));
|
||||
}
|
||||
}
|
||||
|
||||
/* reg1 and reg2 should not be R1 - R5. They can be R0, R6 - R10 */
|
||||
static void emit_debug(struct bpf_gen *gen, int reg1, int reg2,
|
||||
const char *fmt, va_list args)
|
||||
{
|
||||
char buf[1024];
|
||||
int addr, len, ret;
|
||||
|
||||
if (!gen->log_level)
|
||||
return;
|
||||
ret = vsnprintf(buf, sizeof(buf), fmt, args);
|
||||
if (ret < 1024 - 7 && reg1 >= 0 && reg2 < 0)
|
||||
/* The special case to accommodate common debug_ret():
|
||||
* to avoid specifying BPF_REG_7 and adding " r=%%d" to
|
||||
* prints explicitly.
|
||||
*/
|
||||
strcat(buf, " r=%d");
|
||||
len = strlen(buf) + 1;
|
||||
addr = add_data(gen, buf, len);
|
||||
|
||||
emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE,
|
||||
0, 0, 0, addr));
|
||||
emit(gen, BPF_MOV64_IMM(BPF_REG_2, len));
|
||||
if (reg1 >= 0)
|
||||
emit(gen, BPF_MOV64_REG(BPF_REG_3, reg1));
|
||||
if (reg2 >= 0)
|
||||
emit(gen, BPF_MOV64_REG(BPF_REG_4, reg2));
|
||||
emit(gen, BPF_EMIT_CALL(BPF_FUNC_trace_printk));
|
||||
}
|
||||
|
||||
static void debug_regs(struct bpf_gen *gen, int reg1, int reg2, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
emit_debug(gen, reg1, reg2, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static void debug_ret(struct bpf_gen *gen, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
emit_debug(gen, BPF_REG_7, -1, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static void __emit_sys_close(struct bpf_gen *gen)
|
||||
{
|
||||
emit(gen, BPF_JMP_IMM(BPF_JSLE, BPF_REG_1, 0,
|
||||
/* 2 is the number of the following insns
|
||||
* * 6 is additional insns in debug_regs
|
||||
*/
|
||||
2 + (gen->log_level ? 6 : 0)));
|
||||
emit(gen, BPF_MOV64_REG(BPF_REG_9, BPF_REG_1));
|
||||
emit(gen, BPF_EMIT_CALL(BPF_FUNC_sys_close));
|
||||
debug_regs(gen, BPF_REG_9, BPF_REG_0, "close(%%d) = %%d");
|
||||
}
|
||||
|
||||
static void emit_sys_close_stack(struct bpf_gen *gen, int stack_off)
|
||||
{
|
||||
emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_10, stack_off));
|
||||
__emit_sys_close(gen);
|
||||
}
|
||||
|
||||
static void emit_sys_close_blob(struct bpf_gen *gen, int blob_off)
|
||||
{
|
||||
emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_0, BPF_PSEUDO_MAP_IDX_VALUE,
|
||||
0, 0, 0, blob_off));
|
||||
emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0));
|
||||
__emit_sys_close(gen);
|
||||
}
|
||||
|
||||
int bpf_gen__finish(struct bpf_gen *gen)
|
||||
{
|
||||
int i;
|
||||
|
||||
emit_sys_close_stack(gen, stack_off(btf_fd));
|
||||
for (i = 0; i < gen->nr_progs; i++)
|
||||
move_stack2ctx(gen,
|
||||
sizeof(struct bpf_loader_ctx) +
|
||||
sizeof(struct bpf_map_desc) * gen->nr_maps +
|
||||
sizeof(struct bpf_prog_desc) * i +
|
||||
offsetof(struct bpf_prog_desc, prog_fd), 4,
|
||||
stack_off(prog_fd[i]));
|
||||
for (i = 0; i < gen->nr_maps; i++)
|
||||
move_stack2ctx(gen,
|
||||
sizeof(struct bpf_loader_ctx) +
|
||||
sizeof(struct bpf_map_desc) * i +
|
||||
offsetof(struct bpf_map_desc, map_fd), 4,
|
||||
stack_off(map_fd[i]));
|
||||
emit(gen, BPF_MOV64_IMM(BPF_REG_0, 0));
|
||||
emit(gen, BPF_EXIT_INSN());
|
||||
pr_debug("gen: finish %d\n", gen->error);
|
||||
if (!gen->error) {
|
||||
struct gen_loader_opts *opts = gen->opts;
|
||||
|
||||
opts->insns = gen->insn_start;
|
||||
opts->insns_sz = gen->insn_cur - gen->insn_start;
|
||||
opts->data = gen->data_start;
|
||||
opts->data_sz = gen->data_cur - gen->data_start;
|
||||
}
|
||||
return gen->error;
|
||||
}
|
||||
|
||||
void bpf_gen__free(struct bpf_gen *gen)
|
||||
{
|
||||
if (!gen)
|
||||
return;
|
||||
free(gen->data_start);
|
||||
free(gen->insn_start);
|
||||
free(gen);
|
||||
}
|
||||
|
||||
void bpf_gen__load_btf(struct bpf_gen *gen, const void *btf_raw_data,
|
||||
__u32 btf_raw_size)
|
||||
{
|
||||
int attr_size = offsetofend(union bpf_attr, btf_log_level);
|
||||
int btf_data, btf_load_attr;
|
||||
union bpf_attr attr;
|
||||
|
||||
memset(&attr, 0, attr_size);
|
||||
pr_debug("gen: load_btf: size %d\n", btf_raw_size);
|
||||
btf_data = add_data(gen, btf_raw_data, btf_raw_size);
|
||||
|
||||
attr.btf_size = btf_raw_size;
|
||||
btf_load_attr = add_data(gen, &attr, attr_size);
|
||||
|
||||
/* populate union bpf_attr with user provided log details */
|
||||
move_ctx2blob(gen, attr_field(btf_load_attr, btf_log_level), 4,
|
||||
offsetof(struct bpf_loader_ctx, log_level), false);
|
||||
move_ctx2blob(gen, attr_field(btf_load_attr, btf_log_size), 4,
|
||||
offsetof(struct bpf_loader_ctx, log_size), false);
|
||||
move_ctx2blob(gen, attr_field(btf_load_attr, btf_log_buf), 8,
|
||||
offsetof(struct bpf_loader_ctx, log_buf), false);
|
||||
/* populate union bpf_attr with a pointer to the BTF data */
|
||||
emit_rel_store(gen, attr_field(btf_load_attr, btf), btf_data);
|
||||
/* emit BTF_LOAD command */
|
||||
emit_sys_bpf(gen, BPF_BTF_LOAD, btf_load_attr, attr_size);
|
||||
debug_ret(gen, "btf_load size %d", btf_raw_size);
|
||||
emit_check_err(gen);
|
||||
/* remember btf_fd in the stack, if successful */
|
||||
emit(gen, BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_7, stack_off(btf_fd)));
|
||||
}
|
||||
|
||||
void bpf_gen__map_create(struct bpf_gen *gen,
|
||||
struct bpf_create_map_attr *map_attr, int map_idx)
|
||||
{
|
||||
int attr_size = offsetofend(union bpf_attr, btf_vmlinux_value_type_id);
|
||||
bool close_inner_map_fd = false;
|
||||
int map_create_attr;
|
||||
union bpf_attr attr;
|
||||
|
||||
memset(&attr, 0, attr_size);
|
||||
attr.map_type = map_attr->map_type;
|
||||
attr.key_size = map_attr->key_size;
|
||||
attr.value_size = map_attr->value_size;
|
||||
attr.map_flags = map_attr->map_flags;
|
||||
memcpy(attr.map_name, map_attr->name,
|
||||
min((unsigned)strlen(map_attr->name), BPF_OBJ_NAME_LEN - 1));
|
||||
attr.numa_node = map_attr->numa_node;
|
||||
attr.map_ifindex = map_attr->map_ifindex;
|
||||
attr.max_entries = map_attr->max_entries;
|
||||
switch (attr.map_type) {
|
||||
case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
|
||||
case BPF_MAP_TYPE_CGROUP_ARRAY:
|
||||
case BPF_MAP_TYPE_STACK_TRACE:
|
||||
case BPF_MAP_TYPE_ARRAY_OF_MAPS:
|
||||
case BPF_MAP_TYPE_HASH_OF_MAPS:
|
||||
case BPF_MAP_TYPE_DEVMAP:
|
||||
case BPF_MAP_TYPE_DEVMAP_HASH:
|
||||
case BPF_MAP_TYPE_CPUMAP:
|
||||
case BPF_MAP_TYPE_XSKMAP:
|
||||
case BPF_MAP_TYPE_SOCKMAP:
|
||||
case BPF_MAP_TYPE_SOCKHASH:
|
||||
case BPF_MAP_TYPE_QUEUE:
|
||||
case BPF_MAP_TYPE_STACK:
|
||||
case BPF_MAP_TYPE_RINGBUF:
|
||||
break;
|
||||
default:
|
||||
attr.btf_key_type_id = map_attr->btf_key_type_id;
|
||||
attr.btf_value_type_id = map_attr->btf_value_type_id;
|
||||
}
|
||||
|
||||
pr_debug("gen: map_create: %s idx %d type %d value_type_id %d\n",
|
||||
attr.map_name, map_idx, map_attr->map_type, attr.btf_value_type_id);
|
||||
|
||||
map_create_attr = add_data(gen, &attr, attr_size);
|
||||
if (attr.btf_value_type_id)
|
||||
/* populate union bpf_attr with btf_fd saved in the stack earlier */
|
||||
move_stack2blob(gen, attr_field(map_create_attr, btf_fd), 4,
|
||||
stack_off(btf_fd));
|
||||
switch (attr.map_type) {
|
||||
case BPF_MAP_TYPE_ARRAY_OF_MAPS:
|
||||
case BPF_MAP_TYPE_HASH_OF_MAPS:
|
||||
move_stack2blob(gen, attr_field(map_create_attr, inner_map_fd), 4,
|
||||
stack_off(inner_map_fd));
|
||||
close_inner_map_fd = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* conditionally update max_entries */
|
||||
if (map_idx >= 0)
|
||||
move_ctx2blob(gen, attr_field(map_create_attr, max_entries), 4,
|
||||
sizeof(struct bpf_loader_ctx) +
|
||||
sizeof(struct bpf_map_desc) * map_idx +
|
||||
offsetof(struct bpf_map_desc, max_entries),
|
||||
true /* check that max_entries != 0 */);
|
||||
/* emit MAP_CREATE command */
|
||||
emit_sys_bpf(gen, BPF_MAP_CREATE, map_create_attr, attr_size);
|
||||
debug_ret(gen, "map_create %s idx %d type %d value_size %d value_btf_id %d",
|
||||
attr.map_name, map_idx, map_attr->map_type, attr.value_size,
|
||||
attr.btf_value_type_id);
|
||||
emit_check_err(gen);
|
||||
/* remember map_fd in the stack, if successful */
|
||||
if (map_idx < 0) {
|
||||
/* This bpf_gen__map_create() function is called with map_idx >= 0
|
||||
* for all maps that libbpf loading logic tracks.
|
||||
* It's called with -1 to create an inner map.
|
||||
*/
|
||||
emit(gen, BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_7,
|
||||
stack_off(inner_map_fd)));
|
||||
} else if (map_idx != gen->nr_maps) {
|
||||
gen->error = -EDOM; /* internal bug */
|
||||
return;
|
||||
} else {
|
||||
emit(gen, BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_7,
|
||||
stack_off(map_fd[map_idx])));
|
||||
gen->nr_maps++;
|
||||
}
|
||||
if (close_inner_map_fd)
|
||||
emit_sys_close_stack(gen, stack_off(inner_map_fd));
|
||||
}
|
||||
|
||||
void bpf_gen__record_attach_target(struct bpf_gen *gen, const char *attach_name,
|
||||
enum bpf_attach_type type)
|
||||
{
|
||||
const char *prefix;
|
||||
int kind, ret;
|
||||
|
||||
btf_get_kernel_prefix_kind(type, &prefix, &kind);
|
||||
gen->attach_kind = kind;
|
||||
ret = snprintf(gen->attach_target, sizeof(gen->attach_target), "%s%s",
|
||||
prefix, attach_name);
|
||||
if (ret == sizeof(gen->attach_target))
|
||||
gen->error = -ENOSPC;
|
||||
}
|
||||
|
||||
static void emit_find_attach_target(struct bpf_gen *gen)
|
||||
{
|
||||
int name, len = strlen(gen->attach_target) + 1;
|
||||
|
||||
pr_debug("gen: find_attach_tgt %s %d\n", gen->attach_target, gen->attach_kind);
|
||||
name = add_data(gen, gen->attach_target, len);
|
||||
|
||||
emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE,
|
||||
0, 0, 0, name));
|
||||
emit(gen, BPF_MOV64_IMM(BPF_REG_2, len));
|
||||
emit(gen, BPF_MOV64_IMM(BPF_REG_3, gen->attach_kind));
|
||||
emit(gen, BPF_MOV64_IMM(BPF_REG_4, 0));
|
||||
emit(gen, BPF_EMIT_CALL(BPF_FUNC_btf_find_by_name_kind));
|
||||
emit(gen, BPF_MOV64_REG(BPF_REG_7, BPF_REG_0));
|
||||
debug_ret(gen, "find_by_name_kind(%s,%d)",
|
||||
gen->attach_target, gen->attach_kind);
|
||||
emit_check_err(gen);
|
||||
/* if successful, btf_id is in lower 32-bit of R7 and
|
||||
* btf_obj_fd is in upper 32-bit
|
||||
*/
|
||||
}
|
||||
|
||||
void bpf_gen__record_extern(struct bpf_gen *gen, const char *name, int kind,
|
||||
int insn_idx)
|
||||
{
|
||||
struct ksym_relo_desc *relo;
|
||||
|
||||
relo = libbpf_reallocarray(gen->relos, gen->relo_cnt + 1, sizeof(*relo));
|
||||
if (!relo) {
|
||||
gen->error = -ENOMEM;
|
||||
return;
|
||||
}
|
||||
gen->relos = relo;
|
||||
relo += gen->relo_cnt;
|
||||
relo->name = name;
|
||||
relo->kind = kind;
|
||||
relo->insn_idx = insn_idx;
|
||||
gen->relo_cnt++;
|
||||
}
|
||||
|
||||
static void emit_relo(struct bpf_gen *gen, struct ksym_relo_desc *relo, int insns)
|
||||
{
|
||||
int name, insn, len = strlen(relo->name) + 1;
|
||||
|
||||
pr_debug("gen: emit_relo: %s at %d\n", relo->name, relo->insn_idx);
|
||||
name = add_data(gen, relo->name, len);
|
||||
|
||||
emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE,
|
||||
0, 0, 0, name));
|
||||
emit(gen, BPF_MOV64_IMM(BPF_REG_2, len));
|
||||
emit(gen, BPF_MOV64_IMM(BPF_REG_3, relo->kind));
|
||||
emit(gen, BPF_MOV64_IMM(BPF_REG_4, 0));
|
||||
emit(gen, BPF_EMIT_CALL(BPF_FUNC_btf_find_by_name_kind));
|
||||
emit(gen, BPF_MOV64_REG(BPF_REG_7, BPF_REG_0));
|
||||
debug_ret(gen, "find_by_name_kind(%s,%d)", relo->name, relo->kind);
|
||||
emit_check_err(gen);
|
||||
/* store btf_id into insn[insn_idx].imm */
|
||||
insn = insns + sizeof(struct bpf_insn) * relo->insn_idx +
|
||||
offsetof(struct bpf_insn, imm);
|
||||
emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_0, BPF_PSEUDO_MAP_IDX_VALUE,
|
||||
0, 0, 0, insn));
|
||||
emit(gen, BPF_STX_MEM(BPF_W, BPF_REG_0, BPF_REG_7, 0));
|
||||
if (relo->kind == BTF_KIND_VAR) {
|
||||
/* store btf_obj_fd into insn[insn_idx + 1].imm */
|
||||
emit(gen, BPF_ALU64_IMM(BPF_RSH, BPF_REG_7, 32));
|
||||
emit(gen, BPF_STX_MEM(BPF_W, BPF_REG_0, BPF_REG_7,
|
||||
sizeof(struct bpf_insn)));
|
||||
}
|
||||
}
|
||||
|
||||
static void emit_relos(struct bpf_gen *gen, int insns)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < gen->relo_cnt; i++)
|
||||
emit_relo(gen, gen->relos + i, insns);
|
||||
}
|
||||
|
||||
static void cleanup_relos(struct bpf_gen *gen, int insns)
|
||||
{
|
||||
int i, insn;
|
||||
|
||||
for (i = 0; i < gen->relo_cnt; i++) {
|
||||
if (gen->relos[i].kind != BTF_KIND_VAR)
|
||||
continue;
|
||||
/* close fd recorded in insn[insn_idx + 1].imm */
|
||||
insn = insns +
|
||||
sizeof(struct bpf_insn) * (gen->relos[i].insn_idx + 1) +
|
||||
offsetof(struct bpf_insn, imm);
|
||||
emit_sys_close_blob(gen, insn);
|
||||
}
|
||||
if (gen->relo_cnt) {
|
||||
free(gen->relos);
|
||||
gen->relo_cnt = 0;
|
||||
gen->relos = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void bpf_gen__prog_load(struct bpf_gen *gen,
|
||||
struct bpf_prog_load_params *load_attr, int prog_idx)
|
||||
{
|
||||
int attr_size = offsetofend(union bpf_attr, fd_array);
|
||||
int prog_load_attr, license, insns, func_info, line_info;
|
||||
union bpf_attr attr;
|
||||
|
||||
memset(&attr, 0, attr_size);
|
||||
pr_debug("gen: prog_load: type %d insns_cnt %zd\n",
|
||||
load_attr->prog_type, load_attr->insn_cnt);
|
||||
/* add license string to blob of bytes */
|
||||
license = add_data(gen, load_attr->license, strlen(load_attr->license) + 1);
|
||||
/* add insns to blob of bytes */
|
||||
insns = add_data(gen, load_attr->insns,
|
||||
load_attr->insn_cnt * sizeof(struct bpf_insn));
|
||||
|
||||
attr.prog_type = load_attr->prog_type;
|
||||
attr.expected_attach_type = load_attr->expected_attach_type;
|
||||
attr.attach_btf_id = load_attr->attach_btf_id;
|
||||
attr.prog_ifindex = load_attr->prog_ifindex;
|
||||
attr.kern_version = 0;
|
||||
attr.insn_cnt = (__u32)load_attr->insn_cnt;
|
||||
attr.prog_flags = load_attr->prog_flags;
|
||||
|
||||
attr.func_info_rec_size = load_attr->func_info_rec_size;
|
||||
attr.func_info_cnt = load_attr->func_info_cnt;
|
||||
func_info = add_data(gen, load_attr->func_info,
|
||||
attr.func_info_cnt * attr.func_info_rec_size);
|
||||
|
||||
attr.line_info_rec_size = load_attr->line_info_rec_size;
|
||||
attr.line_info_cnt = load_attr->line_info_cnt;
|
||||
line_info = add_data(gen, load_attr->line_info,
|
||||
attr.line_info_cnt * attr.line_info_rec_size);
|
||||
|
||||
memcpy(attr.prog_name, load_attr->name,
|
||||
min((unsigned)strlen(load_attr->name), BPF_OBJ_NAME_LEN - 1));
|
||||
prog_load_attr = add_data(gen, &attr, attr_size);
|
||||
|
||||
/* populate union bpf_attr with a pointer to license */
|
||||
emit_rel_store(gen, attr_field(prog_load_attr, license), license);
|
||||
|
||||
/* populate union bpf_attr with a pointer to instructions */
|
||||
emit_rel_store(gen, attr_field(prog_load_attr, insns), insns);
|
||||
|
||||
/* populate union bpf_attr with a pointer to func_info */
|
||||
emit_rel_store(gen, attr_field(prog_load_attr, func_info), func_info);
|
||||
|
||||
/* populate union bpf_attr with a pointer to line_info */
|
||||
emit_rel_store(gen, attr_field(prog_load_attr, line_info), line_info);
|
||||
|
||||
/* populate union bpf_attr fd_array with a pointer to stack where map_fds are saved */
|
||||
emit_rel_store_sp(gen, attr_field(prog_load_attr, fd_array),
|
||||
stack_off(map_fd[0]));
|
||||
|
||||
/* populate union bpf_attr with user provided log details */
|
||||
move_ctx2blob(gen, attr_field(prog_load_attr, log_level), 4,
|
||||
offsetof(struct bpf_loader_ctx, log_level), false);
|
||||
move_ctx2blob(gen, attr_field(prog_load_attr, log_size), 4,
|
||||
offsetof(struct bpf_loader_ctx, log_size), false);
|
||||
move_ctx2blob(gen, attr_field(prog_load_attr, log_buf), 8,
|
||||
offsetof(struct bpf_loader_ctx, log_buf), false);
|
||||
/* populate union bpf_attr with btf_fd saved in the stack earlier */
|
||||
move_stack2blob(gen, attr_field(prog_load_attr, prog_btf_fd), 4,
|
||||
stack_off(btf_fd));
|
||||
if (gen->attach_kind) {
|
||||
emit_find_attach_target(gen);
|
||||
/* populate union bpf_attr with btf_id and btf_obj_fd found by helper */
|
||||
emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_0, BPF_PSEUDO_MAP_IDX_VALUE,
|
||||
0, 0, 0, prog_load_attr));
|
||||
emit(gen, BPF_STX_MEM(BPF_W, BPF_REG_0, BPF_REG_7,
|
||||
offsetof(union bpf_attr, attach_btf_id)));
|
||||
emit(gen, BPF_ALU64_IMM(BPF_RSH, BPF_REG_7, 32));
|
||||
emit(gen, BPF_STX_MEM(BPF_W, BPF_REG_0, BPF_REG_7,
|
||||
offsetof(union bpf_attr, attach_btf_obj_fd)));
|
||||
}
|
||||
emit_relos(gen, insns);
|
||||
/* emit PROG_LOAD command */
|
||||
emit_sys_bpf(gen, BPF_PROG_LOAD, prog_load_attr, attr_size);
|
||||
debug_ret(gen, "prog_load %s insn_cnt %d", attr.prog_name, attr.insn_cnt);
|
||||
/* successful or not, close btf module FDs used in extern ksyms and attach_btf_obj_fd */
|
||||
cleanup_relos(gen, insns);
|
||||
if (gen->attach_kind)
|
||||
emit_sys_close_blob(gen,
|
||||
attr_field(prog_load_attr, attach_btf_obj_fd));
|
||||
emit_check_err(gen);
|
||||
/* remember prog_fd in the stack, if successful */
|
||||
emit(gen, BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_7,
|
||||
stack_off(prog_fd[gen->nr_progs])));
|
||||
gen->nr_progs++;
|
||||
}
|
||||
|
||||
void bpf_gen__map_update_elem(struct bpf_gen *gen, int map_idx, void *pvalue,
|
||||
__u32 value_size)
|
||||
{
|
||||
int attr_size = offsetofend(union bpf_attr, flags);
|
||||
int map_update_attr, value, key;
|
||||
union bpf_attr attr;
|
||||
int zero = 0;
|
||||
|
||||
memset(&attr, 0, attr_size);
|
||||
pr_debug("gen: map_update_elem: idx %d\n", map_idx);
|
||||
|
||||
value = add_data(gen, pvalue, value_size);
|
||||
key = add_data(gen, &zero, sizeof(zero));
|
||||
|
||||
/* if (map_desc[map_idx].initial_value)
|
||||
* copy_from_user(value, initial_value, value_size);
|
||||
*/
|
||||
emit(gen, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_6,
|
||||
sizeof(struct bpf_loader_ctx) +
|
||||
sizeof(struct bpf_map_desc) * map_idx +
|
||||
offsetof(struct bpf_map_desc, initial_value)));
|
||||
emit(gen, BPF_JMP_IMM(BPF_JEQ, BPF_REG_3, 0, 4));
|
||||
emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE,
|
||||
0, 0, 0, value));
|
||||
emit(gen, BPF_MOV64_IMM(BPF_REG_2, value_size));
|
||||
emit(gen, BPF_EMIT_CALL(BPF_FUNC_copy_from_user));
|
||||
|
||||
map_update_attr = add_data(gen, &attr, attr_size);
|
||||
move_stack2blob(gen, attr_field(map_update_attr, map_fd), 4,
|
||||
stack_off(map_fd[map_idx]));
|
||||
emit_rel_store(gen, attr_field(map_update_attr, key), key);
|
||||
emit_rel_store(gen, attr_field(map_update_attr, value), value);
|
||||
/* emit MAP_UPDATE_ELEM command */
|
||||
emit_sys_bpf(gen, BPF_MAP_UPDATE_ELEM, map_update_attr, attr_size);
|
||||
debug_ret(gen, "update_elem idx %d value_size %d", map_idx, value_size);
|
||||
emit_check_err(gen);
|
||||
}
|
||||
|
||||
void bpf_gen__map_freeze(struct bpf_gen *gen, int map_idx)
|
||||
{
|
||||
int attr_size = offsetofend(union bpf_attr, map_fd);
|
||||
int map_freeze_attr;
|
||||
union bpf_attr attr;
|
||||
|
||||
memset(&attr, 0, attr_size);
|
||||
pr_debug("gen: map_freeze: idx %d\n", map_idx);
|
||||
map_freeze_attr = add_data(gen, &attr, attr_size);
|
||||
move_stack2blob(gen, attr_field(map_freeze_attr, map_fd), 4,
|
||||
stack_off(map_fd[map_idx]));
|
||||
/* emit MAP_FREEZE command */
|
||||
emit_sys_bpf(gen, BPF_MAP_FREEZE, map_freeze_attr, attr_size);
|
||||
debug_ret(gen, "map_freeze");
|
||||
emit_check_err(gen);
|
||||
}
|
||||
2686
src/libbpf.c
2686
src/libbpf.c
File diff suppressed because it is too large
Load Diff
90
src/libbpf.h
90
src/libbpf.h
@@ -18,6 +18,7 @@
|
||||
#include <linux/bpf.h>
|
||||
|
||||
#include "libbpf_common.h"
|
||||
#include "libbpf_legacy.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -93,8 +94,15 @@ struct bpf_object_open_opts {
|
||||
* system Kconfig for CONFIG_xxx externs.
|
||||
*/
|
||||
const char *kconfig;
|
||||
/* Path to the custom BTF to be used for BPF CO-RE relocations.
|
||||
* This custom BTF completely replaces the use of vmlinux BTF
|
||||
* for the purpose of CO-RE relocations.
|
||||
* NOTE: any other BPF feature (e.g., fentry/fexit programs,
|
||||
* struct_ops, etc) will need actual kernel BTF at /sys/kernel/btf/vmlinux.
|
||||
*/
|
||||
const char *btf_custom_path;
|
||||
};
|
||||
#define bpf_object_open_opts__last_field kconfig
|
||||
#define bpf_object_open_opts__last_field btf_custom_path
|
||||
|
||||
LIBBPF_API struct bpf_object *bpf_object__open(const char *path);
|
||||
LIBBPF_API struct bpf_object *
|
||||
@@ -236,20 +244,86 @@ LIBBPF_API int bpf_link__destroy(struct bpf_link *link);
|
||||
|
||||
LIBBPF_API struct bpf_link *
|
||||
bpf_program__attach(struct bpf_program *prog);
|
||||
|
||||
struct bpf_perf_event_opts {
|
||||
/* size of this struct, for forward/backward compatiblity */
|
||||
size_t sz;
|
||||
/* custom user-provided value fetchable through bpf_get_attach_cookie() */
|
||||
__u64 bpf_cookie;
|
||||
};
|
||||
#define bpf_perf_event_opts__last_field bpf_cookie
|
||||
|
||||
LIBBPF_API struct bpf_link *
|
||||
bpf_program__attach_perf_event(struct bpf_program *prog, int pfd);
|
||||
|
||||
LIBBPF_API struct bpf_link *
|
||||
bpf_program__attach_perf_event_opts(struct bpf_program *prog, int pfd,
|
||||
const struct bpf_perf_event_opts *opts);
|
||||
|
||||
struct bpf_kprobe_opts {
|
||||
/* size of this struct, for forward/backward compatiblity */
|
||||
size_t sz;
|
||||
/* custom user-provided value fetchable through bpf_get_attach_cookie() */
|
||||
__u64 bpf_cookie;
|
||||
/* function's offset to install kprobe to */
|
||||
unsigned long offset;
|
||||
/* kprobe is return probe */
|
||||
bool retprobe;
|
||||
size_t :0;
|
||||
};
|
||||
#define bpf_kprobe_opts__last_field retprobe
|
||||
|
||||
LIBBPF_API struct bpf_link *
|
||||
bpf_program__attach_kprobe(struct bpf_program *prog, bool retprobe,
|
||||
const char *func_name);
|
||||
LIBBPF_API struct bpf_link *
|
||||
bpf_program__attach_kprobe_opts(struct bpf_program *prog,
|
||||
const char *func_name,
|
||||
const struct bpf_kprobe_opts *opts);
|
||||
|
||||
struct bpf_uprobe_opts {
|
||||
/* size of this struct, for forward/backward compatiblity */
|
||||
size_t sz;
|
||||
/* offset of kernel reference counted USDT semaphore, added in
|
||||
* a6ca88b241d5 ("trace_uprobe: support reference counter in fd-based uprobe")
|
||||
*/
|
||||
size_t ref_ctr_offset;
|
||||
/* custom user-provided value fetchable through bpf_get_attach_cookie() */
|
||||
__u64 bpf_cookie;
|
||||
/* uprobe is return probe, invoked at function return time */
|
||||
bool retprobe;
|
||||
size_t :0;
|
||||
};
|
||||
#define bpf_uprobe_opts__last_field retprobe
|
||||
|
||||
LIBBPF_API struct bpf_link *
|
||||
bpf_program__attach_uprobe(struct bpf_program *prog, bool retprobe,
|
||||
pid_t pid, const char *binary_path,
|
||||
size_t func_offset);
|
||||
LIBBPF_API struct bpf_link *
|
||||
bpf_program__attach_uprobe_opts(struct bpf_program *prog, pid_t pid,
|
||||
const char *binary_path, size_t func_offset,
|
||||
const struct bpf_uprobe_opts *opts);
|
||||
|
||||
struct bpf_tracepoint_opts {
|
||||
/* size of this struct, for forward/backward compatiblity */
|
||||
size_t sz;
|
||||
/* custom user-provided value fetchable through bpf_get_attach_cookie() */
|
||||
__u64 bpf_cookie;
|
||||
};
|
||||
#define bpf_tracepoint_opts__last_field bpf_cookie
|
||||
|
||||
LIBBPF_API struct bpf_link *
|
||||
bpf_program__attach_tracepoint(struct bpf_program *prog,
|
||||
const char *tp_category,
|
||||
const char *tp_name);
|
||||
LIBBPF_API struct bpf_link *
|
||||
bpf_program__attach_tracepoint_opts(struct bpf_program *prog,
|
||||
const char *tp_category,
|
||||
const char *tp_name,
|
||||
const struct bpf_tracepoint_opts *opts);
|
||||
|
||||
LIBBPF_API struct bpf_link *
|
||||
bpf_program__attach_raw_tracepoint(struct bpf_program *prog,
|
||||
const char *tp_name);
|
||||
LIBBPF_API struct bpf_link *
|
||||
@@ -471,10 +545,12 @@ LIBBPF_API int bpf_map__set_priv(struct bpf_map *map, void *priv,
|
||||
LIBBPF_API void *bpf_map__priv(const struct bpf_map *map);
|
||||
LIBBPF_API int bpf_map__set_initial_value(struct bpf_map *map,
|
||||
const void *data, size_t size);
|
||||
LIBBPF_API const void *bpf_map__initial_value(struct bpf_map *map, size_t *psize);
|
||||
LIBBPF_API bool bpf_map__is_offload_neutral(const struct bpf_map *map);
|
||||
LIBBPF_API bool bpf_map__is_internal(const struct bpf_map *map);
|
||||
LIBBPF_API int bpf_map__set_pin_path(struct bpf_map *map, const char *path);
|
||||
LIBBPF_API const char *bpf_map__get_pin_path(const struct bpf_map *map);
|
||||
LIBBPF_API const char *bpf_map__pin_path(const struct bpf_map *map);
|
||||
LIBBPF_API bool bpf_map__is_pinned(const struct bpf_map *map);
|
||||
LIBBPF_API int bpf_map__pin(struct bpf_map *map, const char *path);
|
||||
LIBBPF_API int bpf_map__unpin(struct bpf_map *map, const char *path);
|
||||
@@ -800,6 +876,18 @@ LIBBPF_API int bpf_object__attach_skeleton(struct bpf_object_skeleton *s);
|
||||
LIBBPF_API void bpf_object__detach_skeleton(struct bpf_object_skeleton *s);
|
||||
LIBBPF_API void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s);
|
||||
|
||||
struct gen_loader_opts {
|
||||
size_t sz; /* size of this struct, for forward/backward compatiblity */
|
||||
const char *data;
|
||||
const char *insns;
|
||||
__u32 data_sz;
|
||||
__u32 insns_sz;
|
||||
};
|
||||
|
||||
#define gen_loader_opts__last_field insns_sz
|
||||
LIBBPF_API int bpf_object__gen_loader(struct bpf_object *obj,
|
||||
struct gen_loader_opts *opts);
|
||||
|
||||
enum libbpf_tristate {
|
||||
TRI_NO = 0,
|
||||
TRI_YES = 1,
|
||||
|
||||
@@ -367,3 +367,22 @@ LIBBPF_0.4.0 {
|
||||
bpf_tc_hook_destroy;
|
||||
bpf_tc_query;
|
||||
} LIBBPF_0.3.0;
|
||||
|
||||
LIBBPF_0.5.0 {
|
||||
global:
|
||||
bpf_map__initial_value;
|
||||
bpf_map__pin_path;
|
||||
bpf_map_lookup_and_delete_elem_flags;
|
||||
bpf_program__attach_kprobe_opts;
|
||||
bpf_program__attach_perf_event_opts;
|
||||
bpf_program__attach_tracepoint_opts;
|
||||
bpf_program__attach_uprobe_opts;
|
||||
bpf_object__gen_loader;
|
||||
btf__load_from_kernel_by_id;
|
||||
btf__load_from_kernel_by_id_split;
|
||||
btf__load_into_kernel;
|
||||
btf__load_module_btf;
|
||||
btf__load_vmlinux_btf;
|
||||
btf_dump__dump_type_data;
|
||||
libbpf_set_strict_mode;
|
||||
} LIBBPF_0.4.0;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "libbpf.h"
|
||||
#include "libbpf_internal.h"
|
||||
|
||||
/* make sure libbpf doesn't use kernel-only integer typedefs */
|
||||
#pragma GCC poison u8 u16 u32 u64 s8 s16 s32 s64
|
||||
@@ -39,7 +40,7 @@ static const char *libbpf_strerror_table[NR_ERRNO] = {
|
||||
int libbpf_strerror(int err, char *buf, size_t size)
|
||||
{
|
||||
if (!buf || !size)
|
||||
return -1;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
err = err > 0 ? err : -err;
|
||||
|
||||
@@ -48,7 +49,7 @@ int libbpf_strerror(int err, char *buf, size_t size)
|
||||
|
||||
ret = strerror_r(err, buf, size);
|
||||
buf[size - 1] = '\0';
|
||||
return ret;
|
||||
return libbpf_err_errno(ret);
|
||||
}
|
||||
|
||||
if (err < __LIBBPF_ERRNO__END) {
|
||||
@@ -62,5 +63,5 @@ int libbpf_strerror(int err, char *buf, size_t size)
|
||||
|
||||
snprintf(buf, size, "Unknown libbpf error %d", err);
|
||||
buf[size - 1] = '\0';
|
||||
return -1;
|
||||
return libbpf_err(-ENOENT);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include <linux/err.h>
|
||||
#include "libbpf_legacy.h"
|
||||
#include "relo_core.h"
|
||||
|
||||
/* make sure libbpf doesn't use kernel-only integer typedefs */
|
||||
#pragma GCC poison u8 u16 u32 u64 s8 s16 s32 s64
|
||||
@@ -192,6 +196,17 @@ void *libbpf_add_mem(void **data, size_t *cap_cnt, size_t elem_sz,
|
||||
size_t cur_cnt, size_t max_cnt, size_t add_cnt);
|
||||
int libbpf_ensure_mem(void **data, size_t *cap_cnt, size_t elem_sz, size_t need_cnt);
|
||||
|
||||
static inline bool libbpf_is_mem_zeroed(const char *p, ssize_t len)
|
||||
{
|
||||
while (len > 0) {
|
||||
if (*p)
|
||||
return false;
|
||||
p++;
|
||||
len--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool libbpf_validate_opts(const char *opts,
|
||||
size_t opts_sz, size_t user_sz,
|
||||
const char *type_name)
|
||||
@@ -200,16 +215,9 @@ static inline bool libbpf_validate_opts(const char *opts,
|
||||
pr_warn("%s size (%zu) is too small\n", type_name, user_sz);
|
||||
return false;
|
||||
}
|
||||
if (user_sz > opts_sz) {
|
||||
size_t i;
|
||||
|
||||
for (i = opts_sz; i < user_sz; i++) {
|
||||
if (opts[i]) {
|
||||
pr_warn("%s has non-zero extra bytes\n",
|
||||
type_name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!libbpf_is_mem_zeroed(opts + opts_sz, (ssize_t)user_sz - opts_sz)) {
|
||||
pr_warn("%s has non-zero extra bytes\n", type_name);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -229,6 +237,14 @@ static inline bool libbpf_validate_opts(const char *opts,
|
||||
(opts)->field = value; \
|
||||
} while (0)
|
||||
|
||||
#define OPTS_ZEROED(opts, last_nonzero_field) \
|
||||
({ \
|
||||
ssize_t __off = offsetofend(typeof(*(opts)), last_nonzero_field); \
|
||||
!(opts) || libbpf_is_mem_zeroed((const void *)opts + __off, \
|
||||
(opts)->sz - __off); \
|
||||
})
|
||||
|
||||
|
||||
int parse_cpu_mask_str(const char *s, bool **mask, int *mask_sz);
|
||||
int parse_cpu_mask_file(const char *fcpu, bool **mask, int *mask_sz);
|
||||
int libbpf__load_raw_btf(const char *raw_types, size_t types_len,
|
||||
@@ -269,6 +285,8 @@ int bpf_object__section_size(const struct bpf_object *obj, const char *name,
|
||||
int bpf_object__variable_offset(const struct bpf_object *obj, const char *name,
|
||||
__u32 *off);
|
||||
struct btf *btf_get_from_fd(int btf_fd, struct btf *base_btf);
|
||||
void btf_get_kernel_prefix_kind(enum bpf_attach_type attach_type,
|
||||
const char **prefix, int *kind);
|
||||
|
||||
struct btf_ext_info {
|
||||
/*
|
||||
@@ -361,76 +379,6 @@ struct bpf_line_info_min {
|
||||
__u32 line_col;
|
||||
};
|
||||
|
||||
/* bpf_core_relo_kind encodes which aspect of captured field/type/enum value
|
||||
* has to be adjusted by relocations.
|
||||
*/
|
||||
enum bpf_core_relo_kind {
|
||||
BPF_FIELD_BYTE_OFFSET = 0, /* field byte offset */
|
||||
BPF_FIELD_BYTE_SIZE = 1, /* field size in bytes */
|
||||
BPF_FIELD_EXISTS = 2, /* field existence in target kernel */
|
||||
BPF_FIELD_SIGNED = 3, /* field signedness (0 - unsigned, 1 - signed) */
|
||||
BPF_FIELD_LSHIFT_U64 = 4, /* bitfield-specific left bitshift */
|
||||
BPF_FIELD_RSHIFT_U64 = 5, /* bitfield-specific right bitshift */
|
||||
BPF_TYPE_ID_LOCAL = 6, /* type ID in local BPF object */
|
||||
BPF_TYPE_ID_TARGET = 7, /* type ID in target kernel */
|
||||
BPF_TYPE_EXISTS = 8, /* type existence in target kernel */
|
||||
BPF_TYPE_SIZE = 9, /* type size in bytes */
|
||||
BPF_ENUMVAL_EXISTS = 10, /* enum value existence in target kernel */
|
||||
BPF_ENUMVAL_VALUE = 11, /* enum value integer value */
|
||||
};
|
||||
|
||||
/* The minimum bpf_core_relo checked by the loader
|
||||
*
|
||||
* CO-RE relocation captures the following data:
|
||||
* - insn_off - instruction offset (in bytes) within a BPF program that needs
|
||||
* its insn->imm field to be relocated with actual field info;
|
||||
* - type_id - BTF type ID of the "root" (containing) entity of a relocatable
|
||||
* type or field;
|
||||
* - access_str_off - offset into corresponding .BTF string section. String
|
||||
* interpretation depends on specific relocation kind:
|
||||
* - for field-based relocations, string encodes an accessed field using
|
||||
* a sequence of field and array indices, separated by colon (:). It's
|
||||
* conceptually very close to LLVM's getelementptr ([0]) instruction's
|
||||
* arguments for identifying offset to a field.
|
||||
* - for type-based relocations, strings is expected to be just "0";
|
||||
* - for enum value-based relocations, string contains an index of enum
|
||||
* value within its enum type;
|
||||
*
|
||||
* Example to provide a better feel.
|
||||
*
|
||||
* struct sample {
|
||||
* int a;
|
||||
* struct {
|
||||
* int b[10];
|
||||
* };
|
||||
* };
|
||||
*
|
||||
* struct sample *s = ...;
|
||||
* int x = &s->a; // encoded as "0:0" (a is field #0)
|
||||
* int y = &s->b[5]; // encoded as "0:1:0:5" (anon struct is field #1,
|
||||
* // b is field #0 inside anon struct, accessing elem #5)
|
||||
* int z = &s[10]->b; // encoded as "10:1" (ptr is used as an array)
|
||||
*
|
||||
* type_id for all relocs in this example will capture BTF type id of
|
||||
* `struct sample`.
|
||||
*
|
||||
* Such relocation is emitted when using __builtin_preserve_access_index()
|
||||
* Clang built-in, passing expression that captures field address, e.g.:
|
||||
*
|
||||
* bpf_probe_read(&dst, sizeof(dst),
|
||||
* __builtin_preserve_access_index(&src->a.b.c));
|
||||
*
|
||||
* In this case Clang will emit field relocation recording necessary data to
|
||||
* be able to find offset of embedded `a.b.c` field within `src` struct.
|
||||
*
|
||||
* [0] https://llvm.org/docs/LangRef.html#getelementptr-instruction
|
||||
*/
|
||||
struct bpf_core_relo {
|
||||
__u32 insn_off;
|
||||
__u32 type_id;
|
||||
__u32 access_str_off;
|
||||
enum bpf_core_relo_kind kind;
|
||||
};
|
||||
|
||||
typedef int (*type_id_visit_fn)(__u32 *type_id, void *ctx);
|
||||
typedef int (*str_off_visit_fn)(__u32 *str_off, void *ctx);
|
||||
@@ -439,4 +387,64 @@ int btf_type_visit_str_offs(struct btf_type *t, str_off_visit_fn visit, void *ct
|
||||
int btf_ext_visit_type_ids(struct btf_ext *btf_ext, type_id_visit_fn visit, void *ctx);
|
||||
int btf_ext_visit_str_offs(struct btf_ext *btf_ext, str_off_visit_fn visit, void *ctx);
|
||||
|
||||
extern enum libbpf_strict_mode libbpf_mode;
|
||||
|
||||
/* handle direct returned errors */
|
||||
static inline int libbpf_err(int ret)
|
||||
{
|
||||
if (ret < 0)
|
||||
errno = -ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* handle errno-based (e.g., syscall or libc) errors according to libbpf's
|
||||
* strict mode settings
|
||||
*/
|
||||
static inline int libbpf_err_errno(int ret)
|
||||
{
|
||||
if (libbpf_mode & LIBBPF_STRICT_DIRECT_ERRS)
|
||||
/* errno is already assumed to be set on error */
|
||||
return ret < 0 ? -errno : ret;
|
||||
|
||||
/* legacy: on error return -1 directly and don't touch errno */
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* handle error for pointer-returning APIs, err is assumed to be < 0 always */
|
||||
static inline void *libbpf_err_ptr(int err)
|
||||
{
|
||||
/* set errno on error, this doesn't break anything */
|
||||
errno = -err;
|
||||
|
||||
if (libbpf_mode & LIBBPF_STRICT_CLEAN_PTRS)
|
||||
return NULL;
|
||||
|
||||
/* legacy: encode err as ptr */
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
/* handle pointer-returning APIs' error handling */
|
||||
static inline void *libbpf_ptr(void *ret)
|
||||
{
|
||||
/* set errno on error, this doesn't break anything */
|
||||
if (IS_ERR(ret))
|
||||
errno = -PTR_ERR(ret);
|
||||
|
||||
if (libbpf_mode & LIBBPF_STRICT_CLEAN_PTRS)
|
||||
return IS_ERR(ret) ? NULL : ret;
|
||||
|
||||
/* legacy: pass-through original pointer */
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline bool str_is_empty(const char *s)
|
||||
{
|
||||
return !s || !s[0];
|
||||
}
|
||||
|
||||
static inline bool is_ldimm64_insn(struct bpf_insn *insn)
|
||||
{
|
||||
return insn->code == (BPF_LD | BPF_IMM | BPF_DW);
|
||||
}
|
||||
|
||||
#endif /* __LIBBPF_LIBBPF_INTERNAL_H */
|
||||
|
||||
59
src/libbpf_legacy.h
Normal file
59
src/libbpf_legacy.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
|
||||
/*
|
||||
* Libbpf legacy APIs (either discouraged or deprecated, as mentioned in [0])
|
||||
*
|
||||
* [0] https://docs.google.com/document/d/1UyjTZuPFWiPFyKk1tV5an11_iaRuec6U-ZESZ54nNTY
|
||||
*
|
||||
* Copyright (C) 2021 Facebook
|
||||
*/
|
||||
#ifndef __LIBBPF_LEGACY_BPF_H
|
||||
#define __LIBBPF_LEGACY_BPF_H
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "libbpf_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum libbpf_strict_mode {
|
||||
/* Turn on all supported strict features of libbpf to simulate libbpf
|
||||
* v1.0 behavior.
|
||||
* This will be the default behavior in libbpf v1.0.
|
||||
*/
|
||||
LIBBPF_STRICT_ALL = 0xffffffff,
|
||||
|
||||
/*
|
||||
* Disable any libbpf 1.0 behaviors. This is the default before libbpf
|
||||
* v1.0. It won't be supported anymore in v1.0, please update your
|
||||
* code so that it handles LIBBPF_STRICT_ALL mode before libbpf v1.0.
|
||||
*/
|
||||
LIBBPF_STRICT_NONE = 0x00,
|
||||
/*
|
||||
* Return NULL pointers on error, not ERR_PTR(err).
|
||||
* Additionally, libbpf also always sets errno to corresponding Exx
|
||||
* (positive) error code.
|
||||
*/
|
||||
LIBBPF_STRICT_CLEAN_PTRS = 0x01,
|
||||
/*
|
||||
* Return actual error codes from low-level APIs directly, not just -1.
|
||||
* Additionally, libbpf also always sets errno to corresponding Exx
|
||||
* (positive) error code.
|
||||
*/
|
||||
LIBBPF_STRICT_DIRECT_ERRS = 0x02,
|
||||
|
||||
__LIBBPF_STRICT_LAST,
|
||||
};
|
||||
|
||||
LIBBPF_API int libbpf_set_strict_mode(enum libbpf_strict_mode mode);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* __LIBBPF_LEGACY_BPF_H */
|
||||
@@ -75,6 +75,9 @@ probe_load(enum bpf_prog_type prog_type, const struct bpf_insn *insns,
|
||||
case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
|
||||
xattr.expected_attach_type = BPF_CGROUP_INET4_CONNECT;
|
||||
break;
|
||||
case BPF_PROG_TYPE_CGROUP_SOCKOPT:
|
||||
xattr.expected_attach_type = BPF_CGROUP_GETSOCKOPT;
|
||||
break;
|
||||
case BPF_PROG_TYPE_SK_LOOKUP:
|
||||
xattr.expected_attach_type = BPF_SK_LOOKUP;
|
||||
break;
|
||||
@@ -104,7 +107,6 @@ probe_load(enum bpf_prog_type prog_type, const struct bpf_insn *insns,
|
||||
case BPF_PROG_TYPE_SK_REUSEPORT:
|
||||
case BPF_PROG_TYPE_FLOW_DISSECTOR:
|
||||
case BPF_PROG_TYPE_CGROUP_SYSCTL:
|
||||
case BPF_PROG_TYPE_CGROUP_SOCKOPT:
|
||||
case BPF_PROG_TYPE_TRACING:
|
||||
case BPF_PROG_TYPE_STRUCT_OPS:
|
||||
case BPF_PROG_TYPE_EXT:
|
||||
|
||||
22
src/linker.c
22
src/linker.c
@@ -220,16 +220,16 @@ struct bpf_linker *bpf_linker__new(const char *filename, struct bpf_linker_opts
|
||||
int err;
|
||||
|
||||
if (!OPTS_VALID(opts, bpf_linker_opts))
|
||||
return NULL;
|
||||
return errno = EINVAL, NULL;
|
||||
|
||||
if (elf_version(EV_CURRENT) == EV_NONE) {
|
||||
pr_warn_elf("libelf initialization failed");
|
||||
return NULL;
|
||||
return errno = EINVAL, NULL;
|
||||
}
|
||||
|
||||
linker = calloc(1, sizeof(*linker));
|
||||
if (!linker)
|
||||
return NULL;
|
||||
return errno = ENOMEM, NULL;
|
||||
|
||||
linker->fd = -1;
|
||||
|
||||
@@ -241,7 +241,7 @@ struct bpf_linker *bpf_linker__new(const char *filename, struct bpf_linker_opts
|
||||
|
||||
err_out:
|
||||
bpf_linker__free(linker);
|
||||
return NULL;
|
||||
return errno = -err, NULL;
|
||||
}
|
||||
|
||||
static struct dst_sec *add_dst_sec(struct bpf_linker *linker, const char *sec_name)
|
||||
@@ -444,10 +444,10 @@ int bpf_linker__add_file(struct bpf_linker *linker, const char *filename,
|
||||
int err = 0;
|
||||
|
||||
if (!OPTS_VALID(opts, bpf_linker_file_opts))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
if (!linker->elf)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
err = err ?: linker_load_obj_file(linker, filename, opts, &obj);
|
||||
err = err ?: linker_append_sec_data(linker, &obj);
|
||||
@@ -467,7 +467,7 @@ int bpf_linker__add_file(struct bpf_linker *linker, const char *filename,
|
||||
if (obj.fd >= 0)
|
||||
close(obj.fd);
|
||||
|
||||
return err;
|
||||
return libbpf_err(err);
|
||||
}
|
||||
|
||||
static bool is_dwarf_sec_name(const char *name)
|
||||
@@ -2548,11 +2548,11 @@ int bpf_linker__finalize(struct bpf_linker *linker)
|
||||
int err, i;
|
||||
|
||||
if (!linker->elf)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
err = finalize_btf(linker);
|
||||
if (err)
|
||||
return err;
|
||||
return libbpf_err(err);
|
||||
|
||||
/* Finalize strings */
|
||||
strs_sz = strset__data_size(linker->strtab_strs);
|
||||
@@ -2584,14 +2584,14 @@ int bpf_linker__finalize(struct bpf_linker *linker)
|
||||
if (elf_update(linker->elf, ELF_C_NULL) < 0) {
|
||||
err = -errno;
|
||||
pr_warn_elf("failed to finalize ELF layout");
|
||||
return err;
|
||||
return libbpf_err(err);
|
||||
}
|
||||
|
||||
/* Write out final ELF contents */
|
||||
if (elf_update(linker->elf, ELF_C_WRITE) < 0) {
|
||||
err = -errno;
|
||||
pr_warn_elf("failed to write ELF contents");
|
||||
return err;
|
||||
return libbpf_err(err);
|
||||
}
|
||||
|
||||
elf_end(linker->elf);
|
||||
|
||||
200
src/netlink.c
200
src/netlink.c
@@ -154,7 +154,7 @@ done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int libbpf_netlink_send_recv(struct nlmsghdr *nh,
|
||||
static int libbpf_netlink_send_recv(struct libbpf_nla_req *req,
|
||||
__dump_nlmsg_t parse_msg,
|
||||
libbpf_dump_nlmsg_t parse_attr,
|
||||
void *cookie)
|
||||
@@ -166,15 +166,15 @@ static int libbpf_netlink_send_recv(struct nlmsghdr *nh,
|
||||
if (sock < 0)
|
||||
return sock;
|
||||
|
||||
nh->nlmsg_pid = 0;
|
||||
nh->nlmsg_seq = time(NULL);
|
||||
req->nh.nlmsg_pid = 0;
|
||||
req->nh.nlmsg_seq = time(NULL);
|
||||
|
||||
if (send(sock, nh, nh->nlmsg_len, 0) < 0) {
|
||||
if (send(sock, req, req->nh.nlmsg_len, 0) < 0) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = libbpf_netlink_recv(sock, nl_pid, nh->nlmsg_seq,
|
||||
ret = libbpf_netlink_recv(sock, nl_pid, req->nh.nlmsg_seq,
|
||||
parse_msg, parse_attr, cookie);
|
||||
out:
|
||||
libbpf_netlink_close(sock);
|
||||
@@ -186,11 +186,7 @@ static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd,
|
||||
{
|
||||
struct nlattr *nla;
|
||||
int ret;
|
||||
struct {
|
||||
struct nlmsghdr nh;
|
||||
struct ifinfomsg ifinfo;
|
||||
char attrbuf[64];
|
||||
} req;
|
||||
struct libbpf_nla_req req;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
|
||||
@@ -199,48 +195,51 @@ static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd,
|
||||
req.ifinfo.ifi_family = AF_UNSPEC;
|
||||
req.ifinfo.ifi_index = ifindex;
|
||||
|
||||
nla = nlattr_begin_nested(&req.nh, sizeof(req), IFLA_XDP);
|
||||
nla = nlattr_begin_nested(&req, IFLA_XDP);
|
||||
if (!nla)
|
||||
return -EMSGSIZE;
|
||||
ret = nlattr_add(&req.nh, sizeof(req), IFLA_XDP_FD, &fd, sizeof(fd));
|
||||
ret = nlattr_add(&req, IFLA_XDP_FD, &fd, sizeof(fd));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (flags) {
|
||||
ret = nlattr_add(&req.nh, sizeof(req), IFLA_XDP_FLAGS, &flags,
|
||||
sizeof(flags));
|
||||
ret = nlattr_add(&req, IFLA_XDP_FLAGS, &flags, sizeof(flags));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
if (flags & XDP_FLAGS_REPLACE) {
|
||||
ret = nlattr_add(&req.nh, sizeof(req), IFLA_XDP_EXPECTED_FD,
|
||||
&old_fd, sizeof(old_fd));
|
||||
ret = nlattr_add(&req, IFLA_XDP_EXPECTED_FD, &old_fd,
|
||||
sizeof(old_fd));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
nlattr_end_nested(&req.nh, nla);
|
||||
nlattr_end_nested(&req, nla);
|
||||
|
||||
return libbpf_netlink_send_recv(&req.nh, NULL, NULL, NULL);
|
||||
return libbpf_netlink_send_recv(&req, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,
|
||||
const struct bpf_xdp_set_link_opts *opts)
|
||||
{
|
||||
int old_fd = -1;
|
||||
int old_fd = -1, ret;
|
||||
|
||||
if (!OPTS_VALID(opts, bpf_xdp_set_link_opts))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
if (OPTS_HAS(opts, old_fd)) {
|
||||
old_fd = OPTS_GET(opts, old_fd, -1);
|
||||
flags |= XDP_FLAGS_REPLACE;
|
||||
}
|
||||
|
||||
return __bpf_set_link_xdp_fd_replace(ifindex, fd, old_fd, flags);
|
||||
ret = __bpf_set_link_xdp_fd_replace(ifindex, fd, old_fd, flags);
|
||||
return libbpf_err(ret);
|
||||
}
|
||||
|
||||
int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags)
|
||||
{
|
||||
return __bpf_set_link_xdp_fd_replace(ifindex, fd, 0, flags);
|
||||
int ret;
|
||||
|
||||
ret = __bpf_set_link_xdp_fd_replace(ifindex, fd, 0, flags);
|
||||
return libbpf_err(ret);
|
||||
}
|
||||
|
||||
static int __dump_link_nlmsg(struct nlmsghdr *nlh,
|
||||
@@ -310,29 +309,26 @@ int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
|
||||
struct xdp_id_md xdp_id = {};
|
||||
__u32 mask;
|
||||
int ret;
|
||||
struct {
|
||||
struct nlmsghdr nh;
|
||||
struct ifinfomsg ifm;
|
||||
} req = {
|
||||
.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
|
||||
.nh.nlmsg_type = RTM_GETLINK,
|
||||
.nh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
|
||||
.ifm.ifi_family = AF_PACKET,
|
||||
struct libbpf_nla_req req = {
|
||||
.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
|
||||
.nh.nlmsg_type = RTM_GETLINK,
|
||||
.nh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
|
||||
.ifinfo.ifi_family = AF_PACKET,
|
||||
};
|
||||
|
||||
if (flags & ~XDP_FLAGS_MASK || !info_size)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
/* Check whether the single {HW,DRV,SKB} mode is set */
|
||||
flags &= (XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE);
|
||||
mask = flags - 1;
|
||||
if (flags && flags & mask)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
xdp_id.ifindex = ifindex;
|
||||
xdp_id.flags = flags;
|
||||
|
||||
ret = libbpf_netlink_send_recv(&req.nh, __dump_link_nlmsg,
|
||||
ret = libbpf_netlink_send_recv(&req, __dump_link_nlmsg,
|
||||
get_xdp_info, &xdp_id);
|
||||
if (!ret) {
|
||||
size_t sz = min(info_size, sizeof(xdp_id.info));
|
||||
@@ -341,7 +337,7 @@ int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
|
||||
memset((void *) info + sz, 0, info_size - sz);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return libbpf_err(ret);
|
||||
}
|
||||
|
||||
static __u32 get_xdp_id(struct xdp_link_info *info, __u32 flags)
|
||||
@@ -369,18 +365,17 @@ int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags)
|
||||
if (!ret)
|
||||
*prog_id = get_xdp_id(&info, flags);
|
||||
|
||||
return ret;
|
||||
return libbpf_err(ret);
|
||||
}
|
||||
|
||||
typedef int (*qdisc_config_t)(struct nlmsghdr *nh, struct tcmsg *t,
|
||||
size_t maxsz);
|
||||
typedef int (*qdisc_config_t)(struct libbpf_nla_req *req);
|
||||
|
||||
static int clsact_config(struct nlmsghdr *nh, struct tcmsg *t, size_t maxsz)
|
||||
static int clsact_config(struct libbpf_nla_req *req)
|
||||
{
|
||||
t->tcm_parent = TC_H_CLSACT;
|
||||
t->tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0);
|
||||
req->tc.tcm_parent = TC_H_CLSACT;
|
||||
req->tc.tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0);
|
||||
|
||||
return nlattr_add(nh, maxsz, TCA_KIND, "clsact", sizeof("clsact"));
|
||||
return nlattr_add(req, TCA_KIND, "clsact", sizeof("clsact"));
|
||||
}
|
||||
|
||||
static int attach_point_to_config(struct bpf_tc_hook *hook,
|
||||
@@ -427,11 +422,7 @@ static int tc_qdisc_modify(struct bpf_tc_hook *hook, int cmd, int flags)
|
||||
{
|
||||
qdisc_config_t config;
|
||||
int ret;
|
||||
struct {
|
||||
struct nlmsghdr nh;
|
||||
struct tcmsg tc;
|
||||
char buf[256];
|
||||
} req;
|
||||
struct libbpf_nla_req req;
|
||||
|
||||
ret = attach_point_to_config(hook, &config);
|
||||
if (ret < 0)
|
||||
@@ -444,16 +435,16 @@ static int tc_qdisc_modify(struct bpf_tc_hook *hook, int cmd, int flags)
|
||||
req.tc.tcm_family = AF_UNSPEC;
|
||||
req.tc.tcm_ifindex = OPTS_GET(hook, ifindex, 0);
|
||||
|
||||
ret = config(&req.nh, &req.tc, sizeof(req));
|
||||
ret = config(&req);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return libbpf_netlink_send_recv(&req.nh, NULL, NULL, NULL);
|
||||
return libbpf_netlink_send_recv(&req, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static int tc_qdisc_create_excl(struct bpf_tc_hook *hook)
|
||||
{
|
||||
return tc_qdisc_modify(hook, RTM_NEWQDISC, NLM_F_CREATE);
|
||||
return tc_qdisc_modify(hook, RTM_NEWQDISC, NLM_F_CREATE | NLM_F_EXCL);
|
||||
}
|
||||
|
||||
static int tc_qdisc_delete(struct bpf_tc_hook *hook)
|
||||
@@ -463,11 +454,14 @@ static int tc_qdisc_delete(struct bpf_tc_hook *hook)
|
||||
|
||||
int bpf_tc_hook_create(struct bpf_tc_hook *hook)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!hook || !OPTS_VALID(hook, bpf_tc_hook) ||
|
||||
OPTS_GET(hook, ifindex, 0) <= 0)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
return tc_qdisc_create_excl(hook);
|
||||
ret = tc_qdisc_create_excl(hook);
|
||||
return libbpf_err(ret);
|
||||
}
|
||||
|
||||
static int __bpf_tc_detach(const struct bpf_tc_hook *hook,
|
||||
@@ -478,18 +472,18 @@ int bpf_tc_hook_destroy(struct bpf_tc_hook *hook)
|
||||
{
|
||||
if (!hook || !OPTS_VALID(hook, bpf_tc_hook) ||
|
||||
OPTS_GET(hook, ifindex, 0) <= 0)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
switch (OPTS_GET(hook, attach_point, 0)) {
|
||||
case BPF_TC_INGRESS:
|
||||
case BPF_TC_EGRESS:
|
||||
return __bpf_tc_detach(hook, NULL, true);
|
||||
return libbpf_err(__bpf_tc_detach(hook, NULL, true));
|
||||
case BPF_TC_INGRESS | BPF_TC_EGRESS:
|
||||
return tc_qdisc_delete(hook);
|
||||
return libbpf_err(tc_qdisc_delete(hook));
|
||||
case BPF_TC_CUSTOM:
|
||||
return -EOPNOTSUPP;
|
||||
return libbpf_err(-EOPNOTSUPP);
|
||||
default:
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -530,14 +524,14 @@ static int get_tc_info(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn,
|
||||
struct nlattr *tb[TCA_MAX + 1];
|
||||
|
||||
libbpf_nla_parse(tb, TCA_MAX,
|
||||
(struct nlattr *)((char *)tc + NLMSG_ALIGN(sizeof(*tc))),
|
||||
(struct nlattr *)((void *)tc + NLMSG_ALIGN(sizeof(*tc))),
|
||||
NLMSG_PAYLOAD(nh, sizeof(*tc)), NULL);
|
||||
if (!tb[TCA_KIND])
|
||||
return NL_CONT;
|
||||
return __get_tc_info(cookie, tc, tb, nh->nlmsg_flags & NLM_F_ECHO);
|
||||
}
|
||||
|
||||
static int tc_add_fd_and_name(struct nlmsghdr *nh, size_t maxsz, int fd)
|
||||
static int tc_add_fd_and_name(struct libbpf_nla_req *req, int fd)
|
||||
{
|
||||
struct bpf_prog_info info = {};
|
||||
__u32 info_len = sizeof(info);
|
||||
@@ -548,7 +542,7 @@ static int tc_add_fd_and_name(struct nlmsghdr *nh, size_t maxsz, int fd)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = nlattr_add(nh, maxsz, TCA_BPF_FD, &fd, sizeof(fd));
|
||||
ret = nlattr_add(req, TCA_BPF_FD, &fd, sizeof(fd));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
len = snprintf(name, sizeof(name), "%s:[%u]", info.name, info.id);
|
||||
@@ -556,7 +550,7 @@ static int tc_add_fd_and_name(struct nlmsghdr *nh, size_t maxsz, int fd)
|
||||
return -errno;
|
||||
if (len >= sizeof(name))
|
||||
return -ENAMETOOLONG;
|
||||
return nlattr_add(nh, maxsz, TCA_BPF_NAME, name, len + 1);
|
||||
return nlattr_add(req, TCA_BPF_NAME, name, len + 1);
|
||||
}
|
||||
|
||||
int bpf_tc_attach(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
|
||||
@@ -564,17 +558,13 @@ int bpf_tc_attach(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
|
||||
__u32 protocol, bpf_flags, handle, priority, parent, prog_id, flags;
|
||||
int ret, ifindex, attach_point, prog_fd;
|
||||
struct bpf_cb_ctx info = {};
|
||||
struct libbpf_nla_req req;
|
||||
struct nlattr *nla;
|
||||
struct {
|
||||
struct nlmsghdr nh;
|
||||
struct tcmsg tc;
|
||||
char buf[256];
|
||||
} req;
|
||||
|
||||
if (!hook || !opts ||
|
||||
!OPTS_VALID(hook, bpf_tc_hook) ||
|
||||
!OPTS_VALID(opts, bpf_tc_opts))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
ifindex = OPTS_GET(hook, ifindex, 0);
|
||||
parent = OPTS_GET(hook, parent, 0);
|
||||
@@ -587,11 +577,11 @@ int bpf_tc_attach(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
|
||||
flags = OPTS_GET(opts, flags, 0);
|
||||
|
||||
if (ifindex <= 0 || !prog_fd || prog_id)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
if (priority > UINT16_MAX)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
if (flags & ~BPF_TC_F_REPLACE)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
flags = (flags & BPF_TC_F_REPLACE) ? NLM_F_REPLACE : NLM_F_EXCL;
|
||||
protocol = ETH_P_ALL;
|
||||
@@ -608,32 +598,31 @@ int bpf_tc_attach(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
|
||||
|
||||
ret = tc_get_tcm_parent(attach_point, &parent);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return libbpf_err(ret);
|
||||
req.tc.tcm_parent = parent;
|
||||
|
||||
ret = nlattr_add(&req.nh, sizeof(req), TCA_KIND, "bpf", sizeof("bpf"));
|
||||
ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
nla = nlattr_begin_nested(&req.nh, sizeof(req), TCA_OPTIONS);
|
||||
return libbpf_err(ret);
|
||||
nla = nlattr_begin_nested(&req, TCA_OPTIONS);
|
||||
if (!nla)
|
||||
return -EMSGSIZE;
|
||||
ret = tc_add_fd_and_name(&req.nh, sizeof(req), prog_fd);
|
||||
return libbpf_err(-EMSGSIZE);
|
||||
ret = tc_add_fd_and_name(&req, prog_fd);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return libbpf_err(ret);
|
||||
bpf_flags = TCA_BPF_FLAG_ACT_DIRECT;
|
||||
ret = nlattr_add(&req.nh, sizeof(req), TCA_BPF_FLAGS, &bpf_flags,
|
||||
sizeof(bpf_flags));
|
||||
ret = nlattr_add(&req, TCA_BPF_FLAGS, &bpf_flags, sizeof(bpf_flags));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
nlattr_end_nested(&req.nh, nla);
|
||||
return libbpf_err(ret);
|
||||
nlattr_end_nested(&req, nla);
|
||||
|
||||
info.opts = opts;
|
||||
|
||||
ret = libbpf_netlink_send_recv(&req.nh, get_tc_info, NULL, &info);
|
||||
ret = libbpf_netlink_send_recv(&req, get_tc_info, NULL, &info);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return libbpf_err(ret);
|
||||
if (!info.processed)
|
||||
return -ENOENT;
|
||||
return libbpf_err(-ENOENT);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -643,11 +632,7 @@ static int __bpf_tc_detach(const struct bpf_tc_hook *hook,
|
||||
{
|
||||
__u32 protocol = 0, handle, priority, parent, prog_id, flags;
|
||||
int ret, ifindex, attach_point, prog_fd;
|
||||
struct {
|
||||
struct nlmsghdr nh;
|
||||
struct tcmsg tc;
|
||||
char buf[256];
|
||||
} req;
|
||||
struct libbpf_nla_req req;
|
||||
|
||||
if (!hook ||
|
||||
!OPTS_VALID(hook, bpf_tc_hook) ||
|
||||
@@ -668,8 +653,6 @@ static int __bpf_tc_detach(const struct bpf_tc_hook *hook,
|
||||
return -EINVAL;
|
||||
if (priority > UINT16_MAX)
|
||||
return -EINVAL;
|
||||
if (flags & ~BPF_TC_F_REPLACE)
|
||||
return -EINVAL;
|
||||
if (!flush) {
|
||||
if (!handle || !priority)
|
||||
return -EINVAL;
|
||||
@@ -696,19 +679,24 @@ static int __bpf_tc_detach(const struct bpf_tc_hook *hook,
|
||||
req.tc.tcm_parent = parent;
|
||||
|
||||
if (!flush) {
|
||||
ret = nlattr_add(&req.nh, sizeof(req), TCA_KIND,
|
||||
"bpf", sizeof("bpf"));
|
||||
ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return libbpf_netlink_send_recv(&req.nh, NULL, NULL, NULL);
|
||||
return libbpf_netlink_send_recv(&req, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
int bpf_tc_detach(const struct bpf_tc_hook *hook,
|
||||
const struct bpf_tc_opts *opts)
|
||||
{
|
||||
return !opts ? -EINVAL : __bpf_tc_detach(hook, opts, false);
|
||||
int ret;
|
||||
|
||||
if (!opts)
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
ret = __bpf_tc_detach(hook, opts, false);
|
||||
return libbpf_err(ret);
|
||||
}
|
||||
|
||||
int bpf_tc_query(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
|
||||
@@ -716,16 +704,12 @@ int bpf_tc_query(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
|
||||
__u32 protocol, handle, priority, parent, prog_id, flags;
|
||||
int ret, ifindex, attach_point, prog_fd;
|
||||
struct bpf_cb_ctx info = {};
|
||||
struct {
|
||||
struct nlmsghdr nh;
|
||||
struct tcmsg tc;
|
||||
char buf[256];
|
||||
} req;
|
||||
struct libbpf_nla_req req;
|
||||
|
||||
if (!hook || !opts ||
|
||||
!OPTS_VALID(hook, bpf_tc_hook) ||
|
||||
!OPTS_VALID(opts, bpf_tc_opts))
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
ifindex = OPTS_GET(hook, ifindex, 0);
|
||||
parent = OPTS_GET(hook, parent, 0);
|
||||
@@ -739,9 +723,9 @@ int bpf_tc_query(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
|
||||
|
||||
if (ifindex <= 0 || flags || prog_fd || prog_id ||
|
||||
!handle || !priority)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
if (priority > UINT16_MAX)
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
protocol = ETH_P_ALL;
|
||||
|
||||
@@ -756,19 +740,19 @@ int bpf_tc_query(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
|
||||
|
||||
ret = tc_get_tcm_parent(attach_point, &parent);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return libbpf_err(ret);
|
||||
req.tc.tcm_parent = parent;
|
||||
|
||||
ret = nlattr_add(&req.nh, sizeof(req), TCA_KIND, "bpf", sizeof("bpf"));
|
||||
ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return libbpf_err(ret);
|
||||
|
||||
info.opts = opts;
|
||||
|
||||
ret = libbpf_netlink_send_recv(&req.nh, get_tc_info, NULL, &info);
|
||||
ret = libbpf_netlink_send_recv(&req, get_tc_info, NULL, &info);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return libbpf_err(ret);
|
||||
if (!info.processed)
|
||||
return -ENOENT;
|
||||
return libbpf_err(-ENOENT);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ static struct nlattr *nla_next(const struct nlattr *nla, int *remaining)
|
||||
int totlen = NLA_ALIGN(nla->nla_len);
|
||||
|
||||
*remaining -= totlen;
|
||||
return (struct nlattr *) ((char *) nla + totlen);
|
||||
return (struct nlattr *)((void *)nla + totlen);
|
||||
}
|
||||
|
||||
static int nla_ok(const struct nlattr *nla, int remaining)
|
||||
|
||||
38
src/nlattr.h
38
src/nlattr.h
@@ -13,6 +13,7 @@
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
/* avoid multiple definition of netlink features */
|
||||
#define __LINUX_NETLINK_H
|
||||
@@ -52,6 +53,15 @@ struct libbpf_nla_policy {
|
||||
uint16_t maxlen;
|
||||
};
|
||||
|
||||
struct libbpf_nla_req {
|
||||
struct nlmsghdr nh;
|
||||
union {
|
||||
struct ifinfomsg ifinfo;
|
||||
struct tcmsg tc;
|
||||
};
|
||||
char buf[128];
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup attr
|
||||
* Iterate over a stream of attributes
|
||||
@@ -71,7 +81,7 @@ struct libbpf_nla_policy {
|
||||
*/
|
||||
static inline void *libbpf_nla_data(const struct nlattr *nla)
|
||||
{
|
||||
return (char *) nla + NLA_HDRLEN;
|
||||
return (void *)nla + NLA_HDRLEN;
|
||||
}
|
||||
|
||||
static inline uint8_t libbpf_nla_getattr_u8(const struct nlattr *nla)
|
||||
@@ -108,47 +118,47 @@ int libbpf_nla_dump_errormsg(struct nlmsghdr *nlh);
|
||||
|
||||
static inline struct nlattr *nla_data(struct nlattr *nla)
|
||||
{
|
||||
return (struct nlattr *)((char *)nla + NLA_HDRLEN);
|
||||
return (struct nlattr *)((void *)nla + NLA_HDRLEN);
|
||||
}
|
||||
|
||||
static inline struct nlattr *nh_tail(struct nlmsghdr *nh)
|
||||
static inline struct nlattr *req_tail(struct libbpf_nla_req *req)
|
||||
{
|
||||
return (struct nlattr *)((char *)nh + NLMSG_ALIGN(nh->nlmsg_len));
|
||||
return (struct nlattr *)((void *)req + NLMSG_ALIGN(req->nh.nlmsg_len));
|
||||
}
|
||||
|
||||
static inline int nlattr_add(struct nlmsghdr *nh, size_t maxsz, int type,
|
||||
static inline int nlattr_add(struct libbpf_nla_req *req, int type,
|
||||
const void *data, int len)
|
||||
{
|
||||
struct nlattr *nla;
|
||||
|
||||
if (NLMSG_ALIGN(nh->nlmsg_len) + NLA_ALIGN(NLA_HDRLEN + len) > maxsz)
|
||||
if (NLMSG_ALIGN(req->nh.nlmsg_len) + NLA_ALIGN(NLA_HDRLEN + len) > sizeof(*req))
|
||||
return -EMSGSIZE;
|
||||
if (!!data != !!len)
|
||||
return -EINVAL;
|
||||
|
||||
nla = nh_tail(nh);
|
||||
nla = req_tail(req);
|
||||
nla->nla_type = type;
|
||||
nla->nla_len = NLA_HDRLEN + len;
|
||||
if (data)
|
||||
memcpy(nla_data(nla), data, len);
|
||||
nh->nlmsg_len = NLMSG_ALIGN(nh->nlmsg_len) + NLA_ALIGN(nla->nla_len);
|
||||
req->nh.nlmsg_len = NLMSG_ALIGN(req->nh.nlmsg_len) + NLA_ALIGN(nla->nla_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct nlattr *nlattr_begin_nested(struct nlmsghdr *nh,
|
||||
size_t maxsz, int type)
|
||||
static inline struct nlattr *nlattr_begin_nested(struct libbpf_nla_req *req, int type)
|
||||
{
|
||||
struct nlattr *tail;
|
||||
|
||||
tail = nh_tail(nh);
|
||||
if (nlattr_add(nh, maxsz, type | NLA_F_NESTED, NULL, 0))
|
||||
tail = req_tail(req);
|
||||
if (nlattr_add(req, type | NLA_F_NESTED, NULL, 0))
|
||||
return NULL;
|
||||
return tail;
|
||||
}
|
||||
|
||||
static inline void nlattr_end_nested(struct nlmsghdr *nh, struct nlattr *tail)
|
||||
static inline void nlattr_end_nested(struct libbpf_nla_req *req,
|
||||
struct nlattr *tail)
|
||||
{
|
||||
tail->nla_len = (char *)nh_tail(nh) - (char *)tail;
|
||||
tail->nla_len = (void *)req_tail(req) - (void *)tail;
|
||||
}
|
||||
|
||||
#endif /* __LIBBPF_NLATTR_H */
|
||||
|
||||
1295
src/relo_core.c
Normal file
1295
src/relo_core.c
Normal file
File diff suppressed because it is too large
Load Diff
100
src/relo_core.h
Normal file
100
src/relo_core.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
/* Copyright (c) 2019 Facebook */
|
||||
|
||||
#ifndef __RELO_CORE_H
|
||||
#define __RELO_CORE_H
|
||||
|
||||
/* bpf_core_relo_kind encodes which aspect of captured field/type/enum value
|
||||
* has to be adjusted by relocations.
|
||||
*/
|
||||
enum bpf_core_relo_kind {
|
||||
BPF_FIELD_BYTE_OFFSET = 0, /* field byte offset */
|
||||
BPF_FIELD_BYTE_SIZE = 1, /* field size in bytes */
|
||||
BPF_FIELD_EXISTS = 2, /* field existence in target kernel */
|
||||
BPF_FIELD_SIGNED = 3, /* field signedness (0 - unsigned, 1 - signed) */
|
||||
BPF_FIELD_LSHIFT_U64 = 4, /* bitfield-specific left bitshift */
|
||||
BPF_FIELD_RSHIFT_U64 = 5, /* bitfield-specific right bitshift */
|
||||
BPF_TYPE_ID_LOCAL = 6, /* type ID in local BPF object */
|
||||
BPF_TYPE_ID_TARGET = 7, /* type ID in target kernel */
|
||||
BPF_TYPE_EXISTS = 8, /* type existence in target kernel */
|
||||
BPF_TYPE_SIZE = 9, /* type size in bytes */
|
||||
BPF_ENUMVAL_EXISTS = 10, /* enum value existence in target kernel */
|
||||
BPF_ENUMVAL_VALUE = 11, /* enum value integer value */
|
||||
};
|
||||
|
||||
/* The minimum bpf_core_relo checked by the loader
|
||||
*
|
||||
* CO-RE relocation captures the following data:
|
||||
* - insn_off - instruction offset (in bytes) within a BPF program that needs
|
||||
* its insn->imm field to be relocated with actual field info;
|
||||
* - type_id - BTF type ID of the "root" (containing) entity of a relocatable
|
||||
* type or field;
|
||||
* - access_str_off - offset into corresponding .BTF string section. String
|
||||
* interpretation depends on specific relocation kind:
|
||||
* - for field-based relocations, string encodes an accessed field using
|
||||
* a sequence of field and array indices, separated by colon (:). It's
|
||||
* conceptually very close to LLVM's getelementptr ([0]) instruction's
|
||||
* arguments for identifying offset to a field.
|
||||
* - for type-based relocations, strings is expected to be just "0";
|
||||
* - for enum value-based relocations, string contains an index of enum
|
||||
* value within its enum type;
|
||||
*
|
||||
* Example to provide a better feel.
|
||||
*
|
||||
* struct sample {
|
||||
* int a;
|
||||
* struct {
|
||||
* int b[10];
|
||||
* };
|
||||
* };
|
||||
*
|
||||
* struct sample *s = ...;
|
||||
* int x = &s->a; // encoded as "0:0" (a is field #0)
|
||||
* int y = &s->b[5]; // encoded as "0:1:0:5" (anon struct is field #1,
|
||||
* // b is field #0 inside anon struct, accessing elem #5)
|
||||
* int z = &s[10]->b; // encoded as "10:1" (ptr is used as an array)
|
||||
*
|
||||
* type_id for all relocs in this example will capture BTF type id of
|
||||
* `struct sample`.
|
||||
*
|
||||
* Such relocation is emitted when using __builtin_preserve_access_index()
|
||||
* Clang built-in, passing expression that captures field address, e.g.:
|
||||
*
|
||||
* bpf_probe_read(&dst, sizeof(dst),
|
||||
* __builtin_preserve_access_index(&src->a.b.c));
|
||||
*
|
||||
* In this case Clang will emit field relocation recording necessary data to
|
||||
* be able to find offset of embedded `a.b.c` field within `src` struct.
|
||||
*
|
||||
* [0] https://llvm.org/docs/LangRef.html#getelementptr-instruction
|
||||
*/
|
||||
struct bpf_core_relo {
|
||||
__u32 insn_off;
|
||||
__u32 type_id;
|
||||
__u32 access_str_off;
|
||||
enum bpf_core_relo_kind kind;
|
||||
};
|
||||
|
||||
struct bpf_core_cand {
|
||||
const struct btf *btf;
|
||||
const struct btf_type *t;
|
||||
const char *name;
|
||||
__u32 id;
|
||||
};
|
||||
|
||||
/* dynamically sized list of type IDs and its associated struct btf */
|
||||
struct bpf_core_cand_list {
|
||||
struct bpf_core_cand *cands;
|
||||
int len;
|
||||
};
|
||||
|
||||
int bpf_core_apply_relo_insn(const char *prog_name,
|
||||
struct bpf_insn *insn, int insn_idx,
|
||||
const struct bpf_core_relo *relo, int relo_idx,
|
||||
const struct btf *local_btf,
|
||||
struct bpf_core_cand_list *cands);
|
||||
int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id,
|
||||
const struct btf *targ_btf, __u32 targ_id);
|
||||
|
||||
size_t bpf_core_essential_name_len(const char *name);
|
||||
#endif
|
||||
@@ -69,23 +69,23 @@ int ring_buffer__add(struct ring_buffer *rb, int map_fd,
|
||||
err = -errno;
|
||||
pr_warn("ringbuf: failed to get map info for fd=%d: %d\n",
|
||||
map_fd, err);
|
||||
return err;
|
||||
return libbpf_err(err);
|
||||
}
|
||||
|
||||
if (info.type != BPF_MAP_TYPE_RINGBUF) {
|
||||
pr_warn("ringbuf: map fd=%d is not BPF_MAP_TYPE_RINGBUF\n",
|
||||
map_fd);
|
||||
return -EINVAL;
|
||||
return libbpf_err(-EINVAL);
|
||||
}
|
||||
|
||||
tmp = libbpf_reallocarray(rb->rings, rb->ring_cnt + 1, sizeof(*rb->rings));
|
||||
if (!tmp)
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
rb->rings = tmp;
|
||||
|
||||
tmp = libbpf_reallocarray(rb->events, rb->ring_cnt + 1, sizeof(*rb->events));
|
||||
if (!tmp)
|
||||
return -ENOMEM;
|
||||
return libbpf_err(-ENOMEM);
|
||||
rb->events = tmp;
|
||||
|
||||
r = &rb->rings[rb->ring_cnt];
|
||||
@@ -103,7 +103,7 @@ int ring_buffer__add(struct ring_buffer *rb, int map_fd,
|
||||
err = -errno;
|
||||
pr_warn("ringbuf: failed to mmap consumer page for map fd=%d: %d\n",
|
||||
map_fd, err);
|
||||
return err;
|
||||
return libbpf_err(err);
|
||||
}
|
||||
r->consumer_pos = tmp;
|
||||
|
||||
@@ -118,7 +118,7 @@ int ring_buffer__add(struct ring_buffer *rb, int map_fd,
|
||||
ringbuf_unmap_ring(rb, r);
|
||||
pr_warn("ringbuf: failed to mmap data pages for map fd=%d: %d\n",
|
||||
map_fd, err);
|
||||
return err;
|
||||
return libbpf_err(err);
|
||||
}
|
||||
r->producer_pos = tmp;
|
||||
r->data = tmp + rb->page_size;
|
||||
@@ -133,7 +133,7 @@ int ring_buffer__add(struct ring_buffer *rb, int map_fd,
|
||||
ringbuf_unmap_ring(rb, r);
|
||||
pr_warn("ringbuf: failed to epoll add map fd=%d: %d\n",
|
||||
map_fd, err);
|
||||
return err;
|
||||
return libbpf_err(err);
|
||||
}
|
||||
|
||||
rb->ring_cnt++;
|
||||
@@ -165,11 +165,11 @@ ring_buffer__new(int map_fd, ring_buffer_sample_fn sample_cb, void *ctx,
|
||||
int err;
|
||||
|
||||
if (!OPTS_VALID(opts, ring_buffer_opts))
|
||||
return NULL;
|
||||
return errno = EINVAL, NULL;
|
||||
|
||||
rb = calloc(1, sizeof(*rb));
|
||||
if (!rb)
|
||||
return NULL;
|
||||
return errno = ENOMEM, NULL;
|
||||
|
||||
rb->page_size = getpagesize();
|
||||
|
||||
@@ -188,7 +188,7 @@ ring_buffer__new(int map_fd, ring_buffer_sample_fn sample_cb, void *ctx,
|
||||
|
||||
err_out:
|
||||
ring_buffer__free(rb);
|
||||
return NULL;
|
||||
return errno = -err, NULL;
|
||||
}
|
||||
|
||||
static inline int roundup_len(__u32 len)
|
||||
@@ -260,7 +260,7 @@ int ring_buffer__consume(struct ring_buffer *rb)
|
||||
|
||||
err = ringbuf_process_ring(ring);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return libbpf_err(err);
|
||||
res += err;
|
||||
}
|
||||
if (res > INT_MAX)
|
||||
@@ -279,7 +279,7 @@ int ring_buffer__poll(struct ring_buffer *rb, int timeout_ms)
|
||||
|
||||
cnt = epoll_wait(rb->epoll_fd, rb->events, rb->ring_cnt, timeout_ms);
|
||||
if (cnt < 0)
|
||||
return -errno;
|
||||
return libbpf_err(-errno);
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
__u32 ring_id = rb->events[i].data.fd;
|
||||
@@ -287,7 +287,7 @@ int ring_buffer__poll(struct ring_buffer *rb, int timeout_ms)
|
||||
|
||||
err = ringbuf_process_ring(ring);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return libbpf_err(err);
|
||||
res += err;
|
||||
}
|
||||
if (res > INT_MAX)
|
||||
|
||||
123
src/skel_internal.h
Normal file
123
src/skel_internal.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
/* Copyright (c) 2021 Facebook */
|
||||
#ifndef __SKEL_INTERNAL_H
|
||||
#define __SKEL_INTERNAL_H
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
/* This file is a base header for auto-generated *.lskel.h files.
|
||||
* Its contents will change and may become part of auto-generation in the future.
|
||||
*
|
||||
* The layout of bpf_[map|prog]_desc and bpf_loader_ctx is feature dependent
|
||||
* and will change from one version of libbpf to another and features
|
||||
* requested during loader program generation.
|
||||
*/
|
||||
struct bpf_map_desc {
|
||||
union {
|
||||
/* input for the loader prog */
|
||||
struct {
|
||||
__aligned_u64 initial_value;
|
||||
__u32 max_entries;
|
||||
};
|
||||
/* output of the loader prog */
|
||||
struct {
|
||||
int map_fd;
|
||||
};
|
||||
};
|
||||
};
|
||||
struct bpf_prog_desc {
|
||||
int prog_fd;
|
||||
};
|
||||
|
||||
struct bpf_loader_ctx {
|
||||
size_t sz;
|
||||
__u32 log_level;
|
||||
__u32 log_size;
|
||||
__u64 log_buf;
|
||||
};
|
||||
|
||||
struct bpf_load_and_run_opts {
|
||||
struct bpf_loader_ctx *ctx;
|
||||
const void *data;
|
||||
const void *insns;
|
||||
__u32 data_sz;
|
||||
__u32 insns_sz;
|
||||
const char *errstr;
|
||||
};
|
||||
|
||||
static inline int skel_sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr,
|
||||
unsigned int size)
|
||||
{
|
||||
return syscall(__NR_bpf, cmd, attr, size);
|
||||
}
|
||||
|
||||
static inline int skel_closenz(int fd)
|
||||
{
|
||||
if (fd > 0)
|
||||
return close(fd);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int bpf_load_and_run(struct bpf_load_and_run_opts *opts)
|
||||
{
|
||||
int map_fd = -1, prog_fd = -1, key = 0, err;
|
||||
union bpf_attr attr;
|
||||
|
||||
map_fd = bpf_create_map_name(BPF_MAP_TYPE_ARRAY, "__loader.map", 4,
|
||||
opts->data_sz, 1, 0);
|
||||
if (map_fd < 0) {
|
||||
opts->errstr = "failed to create loader map";
|
||||
err = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = bpf_map_update_elem(map_fd, &key, opts->data, 0);
|
||||
if (err < 0) {
|
||||
opts->errstr = "failed to update loader map";
|
||||
err = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.prog_type = BPF_PROG_TYPE_SYSCALL;
|
||||
attr.insns = (long) opts->insns;
|
||||
attr.insn_cnt = opts->insns_sz / sizeof(struct bpf_insn);
|
||||
attr.license = (long) "Dual BSD/GPL";
|
||||
memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog"));
|
||||
attr.fd_array = (long) &map_fd;
|
||||
attr.log_level = opts->ctx->log_level;
|
||||
attr.log_size = opts->ctx->log_size;
|
||||
attr.log_buf = opts->ctx->log_buf;
|
||||
attr.prog_flags = BPF_F_SLEEPABLE;
|
||||
prog_fd = skel_sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
|
||||
if (prog_fd < 0) {
|
||||
opts->errstr = "failed to load loader prog";
|
||||
err = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.test.prog_fd = prog_fd;
|
||||
attr.test.ctx_in = (long) opts->ctx;
|
||||
attr.test.ctx_size_in = opts->ctx->sz;
|
||||
err = skel_sys_bpf(BPF_PROG_RUN, &attr, sizeof(attr));
|
||||
if (err < 0 || (int)attr.test.retval < 0) {
|
||||
opts->errstr = "failed to execute loader prog";
|
||||
if (err < 0)
|
||||
err = -errno;
|
||||
else
|
||||
err = (int)attr.test.retval;
|
||||
goto out;
|
||||
}
|
||||
err = 0;
|
||||
out:
|
||||
if (map_fd >= 0)
|
||||
close(map_fd);
|
||||
if (prog_fd >= 0)
|
||||
close(prog_fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1094,7 +1094,7 @@ int xsk_socket__create_shared(struct xsk_socket **xsk_ptr,
|
||||
goto out_put_ctx;
|
||||
}
|
||||
if (xsk->fd == umem->fd)
|
||||
umem->rx_ring_setup_done = true;
|
||||
umem->tx_ring_setup_done = true;
|
||||
}
|
||||
|
||||
err = xsk_get_mmap_offsets(xsk->fd, &off);
|
||||
|
||||
@@ -18,7 +18,7 @@ function error() {
|
||||
}
|
||||
|
||||
function docker_exec() {
|
||||
docker exec $ENV_VARS -it $CONT_NAME "$@"
|
||||
docker exec $ENV_VARS $CONT_NAME "$@"
|
||||
}
|
||||
|
||||
set -eu
|
||||
@@ -38,20 +38,24 @@ for phase in "${PHASES[@]}"; do
|
||||
$DOCKER_RUN -v $REPO_ROOT:/build:rw \
|
||||
-w /build --privileged=true --name $CONT_NAME \
|
||||
-dit --net=host debian:$DEBIAN_RELEASE /bin/bash
|
||||
echo -e "::group::Build Env Setup"
|
||||
docker_exec bash -c "echo deb-src http://deb.debian.org/debian $DEBIAN_RELEASE main >>/etc/apt/sources.list"
|
||||
docker_exec apt-get -y update
|
||||
docker_exec apt-get -y install aptitude
|
||||
docker_exec aptitude -y build-dep libelf-dev
|
||||
docker_exec aptitude -y install libelf-dev
|
||||
docker_exec aptitude -y install "${ADDITIONAL_DEPS[@]}"
|
||||
echo -e "::endgroup::"
|
||||
;;
|
||||
RUN|RUN_CLANG|RUN_GCC10|RUN_ASAN|RUN_CLANG_ASAN|RUN_GCC10_ASAN)
|
||||
CC="cc"
|
||||
if [[ "$phase" = *"CLANG"* ]]; then
|
||||
ENV_VARS="-e CC=clang -e CXX=clang++"
|
||||
CC="clang"
|
||||
elif [[ "$phase" = *"GCC10"* ]]; then
|
||||
ENV_VARS="-e CC=gcc-10 -e CXX=g++-10"
|
||||
CC="gcc-10"
|
||||
CFLAGS="${CFLAGS} -Wno-stringop-truncation"
|
||||
else
|
||||
CFLAGS="${CFLAGS} -Wno-stringop-truncation"
|
||||
fi
|
||||
@@ -59,9 +63,9 @@ for phase in "${PHASES[@]}"; do
|
||||
CFLAGS="${CFLAGS} -fsanitize=address,undefined"
|
||||
fi
|
||||
docker_exec mkdir build install
|
||||
docker_exec ${CC:-cc} --version
|
||||
docker_exec ${CC} --version
|
||||
info "build"
|
||||
docker_exec make -j$((4*$(nproc))) CFLAGS="${CFLAGS}" -C ./src -B OBJDIR=../build
|
||||
docker_exec make -j$((4*$(nproc))) CFLAGS="${CFLAGS}" -C ./src -B OBJDIR=../build
|
||||
info "ldd build/libbpf.so:"
|
||||
docker_exec ldd build/libbpf.so
|
||||
if ! docker_exec ldd build/libbpf.so | grep -q libelf; then
|
||||
@@ -70,7 +74,8 @@ for phase in "${PHASES[@]}"; do
|
||||
fi
|
||||
info "install"
|
||||
docker_exec make -j$((4*$(nproc))) -C src OBJDIR=../build DESTDIR=../install install
|
||||
docker_exec rm -rf build install
|
||||
info "link binary"
|
||||
docker_exec bash -c "CFLAGS=\"${CFLAGS}\" ./travis-ci/managers/test_compile.sh"
|
||||
;;
|
||||
CLEANUP)
|
||||
info "Cleanup phase"
|
||||
|
||||
14
travis-ci/managers/test_compile.sh
Executable file
14
travis-ci/managers/test_compile.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
set -euox pipefail
|
||||
|
||||
CFLAGS=${CFLAGS:-}
|
||||
|
||||
cat << EOF > main.c
|
||||
#include <bpf/libbpf.h>
|
||||
int main() {
|
||||
return bpf_object__open(0) < 0;
|
||||
}
|
||||
EOF
|
||||
|
||||
# static linking
|
||||
${CC:-cc} ${CFLAGS} -o main -I./install/usr/include main.c ./build/libbpf.a -lelf -lz
|
||||
@@ -20,4 +20,4 @@ if ! ldd build/libbpf.so | grep -q libelf; then
|
||||
exit 1
|
||||
fi
|
||||
make -j$((4*$(nproc))) -C src OBJDIR=../build DESTDIR=../install install
|
||||
rm -rf build install
|
||||
CFLAGS=${CFLAGS} $(dirname $0)/test_compile.sh
|
||||
|
||||
@@ -4,18 +4,22 @@ set -eu
|
||||
|
||||
source $(cd $(dirname $0) && pwd)/helpers.sh
|
||||
|
||||
travis_fold start build_pahole "Building pahole"
|
||||
|
||||
CWD=$(pwd)
|
||||
REPO_PATH=$1
|
||||
PAHOLE_ORIGIN=https://git.kernel.org/pub/scm/devel/pahole/pahole.git
|
||||
PAHOLE_ORIGIN=${PAHOLE_ORIGIN:-https://git.kernel.org/pub/scm/devel/pahole/pahole.git}
|
||||
PAHOLE_BRANCH=${PAHOLE_BRANCH:-master}
|
||||
|
||||
travis_fold start build_pahole "Building pahole ${PAHOLE_ORIGIN} ${PAHOLE_BRANCH}"
|
||||
|
||||
mkdir -p ${REPO_PATH}
|
||||
cd ${REPO_PATH}
|
||||
git init
|
||||
git remote add origin ${PAHOLE_ORIGIN}
|
||||
git fetch origin
|
||||
git checkout master
|
||||
git checkout ${PAHOLE_BRANCH}
|
||||
|
||||
# temporary work-around to bump pahole to 1.22 before it is officially released
|
||||
sed -i 's/DDWARVES_MINOR_VERSION=21/DDWARVES_MINOR_VERSION=22/' CMakeLists.txt
|
||||
|
||||
mkdir -p build
|
||||
cd build
|
||||
|
||||
@@ -8,9 +8,8 @@ travis_fold start prepare_selftests "Building selftests"
|
||||
|
||||
sudo apt-get -y install python-docutils # for rst2man
|
||||
|
||||
LLVM_VER=13
|
||||
LLVM_VER=14
|
||||
LIBBPF_PATH="${REPO_ROOT}"
|
||||
REPO_PATH="travis-ci/vmtest/bpf-next"
|
||||
|
||||
PREPARE_SELFTESTS_SCRIPT=${VMTEST_ROOT}/prepare_selftests-${KERNEL}.sh
|
||||
if [ -f "${PREPARE_SELFTESTS_SCRIPT}" ]; then
|
||||
@@ -30,7 +29,7 @@ make \
|
||||
VMLINUX_BTF="${VMLINUX_BTF}" \
|
||||
VMLINUX_H=${VMLINUX_H} \
|
||||
-C "${REPO_ROOT}/${REPO_PATH}/tools/testing/selftests/bpf" \
|
||||
-j $((4*$(nproc)))
|
||||
-j $((4*$(nproc))) >/dev/null
|
||||
mkdir ${LIBBPF_PATH}/selftests
|
||||
cp -R "${REPO_ROOT}/${REPO_PATH}/tools/testing/selftests/bpf" \
|
||||
${LIBBPF_PATH}/selftests
|
||||
|
||||
@@ -8,12 +8,22 @@ CWD=$(pwd)
|
||||
LIBBPF_PATH=$(pwd)
|
||||
REPO_PATH=$1
|
||||
|
||||
BPF_NEXT_ORIGIN=https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git
|
||||
LINUX_SHA=$(cat ${LIBBPF_PATH}/CHECKPOINT-COMMIT)
|
||||
SNAPSHOT_URL=https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/snapshot/bpf-next-${LINUX_SHA}.tar.gz
|
||||
KERNEL_ORIGIN=${KERNEL_ORIGIN:-https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git}
|
||||
KERNEL_BRANCH=${KERNEL_BRANCH:-CHECKPOINT}
|
||||
if [[ "${KERNEL_BRANCH}" = 'CHECKPOINT' ]]; then
|
||||
echo "using CHECKPOINT sha1"
|
||||
LINUX_SHA=$(cat ${LIBBPF_PATH}/CHECKPOINT-COMMIT)
|
||||
else
|
||||
echo "using ${KERNEL_BRANCH} sha1"
|
||||
LINUX_SHA=$(git ls-remote ${KERNEL_ORIGIN} ${KERNEL_BRANCH} | awk '{print $1}')
|
||||
fi
|
||||
SNAPSHOT_URL=${KERNEL_ORIGIN}/snapshot/bpf-next-${LINUX_SHA}.tar.gz
|
||||
|
||||
echo REPO_PATH = ${REPO_PATH}
|
||||
|
||||
echo KERNEL_ORIGIN = ${KERNEL_ORIGIN}
|
||||
echo LINUX_SHA = ${LINUX_SHA}
|
||||
echo SNAPSHOT_URL = ${SNAPSHOT_URL}
|
||||
|
||||
if [ ! -d "${REPO_PATH}" ]; then
|
||||
echo
|
||||
@@ -22,14 +32,14 @@ if [ ! -d "${REPO_PATH}" ]; then
|
||||
mkdir -p $(dirname "${REPO_PATH}")
|
||||
cd $(dirname "${REPO_PATH}")
|
||||
# attempt to fetch desired bpf-next repo snapshot
|
||||
if wget ${SNAPSHOT_URL} && tar xf bpf-next-${LINUX_SHA}.tar.gz ; then
|
||||
if wget -nv ${SNAPSHOT_URL} && tar xf bpf-next-${LINUX_SHA}.tar.gz --totals ; then
|
||||
mv bpf-next-${LINUX_SHA} $(basename ${REPO_PATH})
|
||||
else
|
||||
# but fallback to git fetch approach if that fails
|
||||
mkdir -p $(basename ${REPO_PATH})
|
||||
cd $(basename ${REPO_PATH})
|
||||
git init
|
||||
git remote add bpf-next ${BPF_NEXT_ORIGIN}
|
||||
git remote add bpf-next ${KERNEL_ORIGIN}
|
||||
# try shallow clone first
|
||||
git fetch --depth 32 bpf-next
|
||||
# check if desired SHA exists
|
||||
|
||||
@@ -3,6 +3,7 @@ align # verifier output format changed
|
||||
atomics # new atomic operations (v5.12+)
|
||||
atomic_bounds # new atomic operations (v5.12+)
|
||||
bind_perm # changed semantics of return values (v5.12+)
|
||||
bpf_cookie # 5.15+
|
||||
bpf_iter # bpf_iter support is missing
|
||||
bpf_obj_id # bpf_link support missing for GET_OBJ_INFO, GET_FD_BY_ID, etc
|
||||
bpf_tcp_ca # STRUCT_OPS is missing
|
||||
@@ -25,6 +26,7 @@ fexit_test # bpf_prog_test_tracing missing
|
||||
flow_dissector # bpf_link-based flow dissector is in 5.8+
|
||||
flow_dissector_reattach
|
||||
for_each # v5.12+
|
||||
get_func_ip_test # v5.15+
|
||||
get_stack_raw_tp # exercising BPF verifier bug causing infinite loop
|
||||
hash_large_key # v5.11+
|
||||
ima # v5.11+
|
||||
@@ -34,15 +36,19 @@ kfunc_call # v5.13+
|
||||
link_pinning # bpf_link is missing
|
||||
linked_vars # v5.13+
|
||||
load_bytes_relative # new functionality in 5.8
|
||||
lookup_and_delete # v5.14+
|
||||
map_init # per-CPU LRU missing
|
||||
map_ptr # test uses BPF_MAP_TYPE_RINGBUF, added in 5.8
|
||||
metadata # v5.10+
|
||||
migrate_reuseport # v5.14+
|
||||
mmap # 5.5 kernel is too permissive with re-mmaping
|
||||
modify_return # fmod_ret support is missing
|
||||
module_attach # module BTF support missing (v5.11+)
|
||||
netcnt
|
||||
ns_current_pid_tgid # bpf_get_ns_current_pid_tgid() helper is missing
|
||||
pe_preserve_elems # v5.10+
|
||||
perf_branches # bpf_read_branch_records() helper is missing
|
||||
perf_link # v5.15+
|
||||
pkt_access # 32-bit pointer arith in test_pkt_access
|
||||
probe_read_user_str # kernel bug with garbage bytes at the end
|
||||
prog_run_xattr # 32-bit pointer arith in test_pkt_access
|
||||
@@ -70,9 +76,11 @@ sockmap_listen # no listen socket supportin SOCKMAP
|
||||
sockopt_sk
|
||||
stacktrace_build_id # v5.9+
|
||||
stack_var_off # v5.12+
|
||||
syscall # v5.14+
|
||||
task_local_storage # v5.12+
|
||||
tcp_hdr_options # v5.10+, new TCP header options feature in BPF
|
||||
tcpbpf_user # LINK_CREATE is missing
|
||||
tc_redirect # v5.14+
|
||||
test_bpffs # v5.10+, new CONFIG_BPF_PRELOAD=y and CONFIG_BPF_PRELOAD_UMG=y|m
|
||||
test_bprm_opts # v5.11+
|
||||
test_global_funcs # kernel doesn't support BTF linkage=global on FUNCs
|
||||
@@ -81,14 +89,19 @@ test_lsm # no BPF_LSM support
|
||||
test_overhead # no fmod_ret support
|
||||
test_profiler # needs verifier logic improvements from v5.10+
|
||||
test_skb_pkt_end # v5.11+
|
||||
timer # v5.15+
|
||||
timer_mim # v5.15+
|
||||
trace_ext # v5.10+
|
||||
trace_printk # v5.14+
|
||||
trampoline_count # v5.12+ have lower allowed limits
|
||||
udp_limit # no cgroup/sock_release BPF program type (5.9+)
|
||||
varlen # verifier bug fixed in later kernels
|
||||
vmlinux # hrtimer_nanosleep() signature changed incompatibly
|
||||
xdp_adjust_tail # new XDP functionality added in 5.8
|
||||
xdp_attach # IFLA_XDP_EXPECTED_FD support is missing
|
||||
xdp_bonding # v5.15+
|
||||
xdp_bpf2bpf # freplace is missing
|
||||
xdp_context_test_run # v5.15+
|
||||
xdp_cpumap_attach # v5.9+
|
||||
xdp_devmap_attach # new feature in 5.8
|
||||
xdp_link # v5.9+
|
||||
|
||||
@@ -1486,7 +1486,7 @@ CONFIG_SCSI_MOD=y
|
||||
# CONFIG_MACINTOSH_DRIVERS is not set
|
||||
CONFIG_NETDEVICES=y
|
||||
CONFIG_NET_CORE=y
|
||||
# CONFIG_BONDING is not set
|
||||
CONFIG_BONDING=y
|
||||
# CONFIG_DUMMY is not set
|
||||
# CONFIG_WIREGUARD is not set
|
||||
# CONFIG_EQUALIZER is not set
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
btf_dump
|
||||
# btf_dump -- need to disable data dump sub-tests
|
||||
core_retro
|
||||
cpu_mask
|
||||
hashmap
|
||||
|
||||
@@ -2,11 +2,23 @@
|
||||
# $2 - fold identifier, no spaces
|
||||
# $3 - fold section description
|
||||
travis_fold() {
|
||||
local YELLOW='\033[1;33m'
|
||||
local NOCOLOR='\033[0m'
|
||||
echo travis_fold:$1:$2
|
||||
if [ ! -z "${3:-}" ]; then
|
||||
echo -e "${YELLOW}$3${NOCOLOR}"
|
||||
fi
|
||||
echo
|
||||
local YELLOW='\033[1;33m'
|
||||
local NOCOLOR='\033[0m'
|
||||
if [ -z ${GITHUB_WORKFLOW+x} ]; then
|
||||
echo travis_fold:$1:$2
|
||||
if [ ! -z "${3:-}" ]; then
|
||||
echo -e "${YELLOW}$3${NOCOLOR}"
|
||||
fi
|
||||
echo
|
||||
else
|
||||
if [ $1 = "start" ]; then
|
||||
line="::group::$2"
|
||||
if [ ! -z "${3:-}" ]; then
|
||||
line="$line - ${YELLOW}$3${NOCOLOR}"
|
||||
fi
|
||||
else
|
||||
line="::endgroup::"
|
||||
fi
|
||||
echo -e "$line"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -4,17 +4,18 @@ set -eu
|
||||
|
||||
source $(cd $(dirname $0) && pwd)/helpers.sh
|
||||
|
||||
REPO_PATH=$1
|
||||
REPO_PATH=${1:-}
|
||||
|
||||
${VMTEST_ROOT}/checkout_latest_kernel.sh ${REPO_PATH}
|
||||
cd ${REPO_PATH}
|
||||
if [[ ! -z "$REPO_PATH" ]]; then
|
||||
${VMTEST_ROOT}/checkout_latest_kernel.sh ${REPO_PATH}
|
||||
cd ${REPO_PATH}
|
||||
fi
|
||||
|
||||
if [[ "${KERNEL}" = 'LATEST' ]]; then
|
||||
travis_fold start build_kernel "Kernel build"
|
||||
|
||||
cp ${VMTEST_ROOT}/configs/latest.config .config
|
||||
make -j $((4*$(nproc))) olddefconfig all
|
||||
|
||||
make -j $((4*$(nproc))) olddefconfig all >/dev/null
|
||||
travis_fold end build_kernel
|
||||
fi
|
||||
|
||||
|
||||
@@ -92,6 +92,12 @@ SKIPSOURCE=0
|
||||
APPEND=""
|
||||
DIR="$PWD"
|
||||
LIST=0
|
||||
|
||||
# by default will copy all files that aren't listed in git exclusions
|
||||
# but it doesn't work for entire kernel tree very well
|
||||
# so for full kernel tree you may need to SOURCE_FULLCOPY=0
|
||||
SOURCE_FULLCOPY=${SOURCE_FULLCOPY:-1}
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
-k|--kernel)
|
||||
@@ -374,27 +380,38 @@ fi
|
||||
|
||||
travis_fold end vmlinux_setup
|
||||
|
||||
REPO_PATH="${SELFTEST_REPO_PATH:-travis-ci/vmtest/bpf-next}"
|
||||
LIBBPF_PATH="${REPO_ROOT}" \
|
||||
REPO_PATH="travis-ci/vmtest/bpf-next" \
|
||||
VMTEST_ROOT="${VMTEST_ROOT}" \
|
||||
REPO_PATH="${REPO_PATH}" \
|
||||
VMLINUX_BTF=${vmlinux} ${VMTEST_ROOT}/build_selftests.sh
|
||||
|
||||
travis_fold start bpftool_checks "Running bpftool checks..."
|
||||
if [[ "${KERNEL}" = 'LATEST' ]]; then
|
||||
"${REPO_ROOT}/${REPO_PATH}/tools/testing/selftests/bpf/test_bpftool_synctypes.py" && \
|
||||
echo "Consistency checks passed successfully."
|
||||
else
|
||||
echo "Consistency checks skipped."
|
||||
fi
|
||||
travis_fold end bpftool_checks
|
||||
|
||||
travis_fold start vm_init "Starting virtual machine..."
|
||||
|
||||
if (( SKIPSOURCE )); then
|
||||
echo "Not copying source files..." >&2
|
||||
else
|
||||
echo "Copying source files..." >&2
|
||||
|
||||
# Copy the source files in.
|
||||
sudo mkdir -p -m 0755 "$mnt/${PROJECT_NAME}"
|
||||
{
|
||||
if [[ -e .git ]]; then
|
||||
git ls-files -z
|
||||
if [[ "${SOURCE_FULLCOPY}" == "1" ]]; then
|
||||
git ls-files -z | sudo rsync --files-from=- -0cpt . "$mnt/${PROJECT_NAME}"
|
||||
else
|
||||
tr '\n' '\0' < "${PROJECT_NAME}.egg-info/SOURCES.txt"
|
||||
fi
|
||||
} | sudo rsync --files-from=- -0cpt . "$mnt/${PROJECT_NAME}"
|
||||
sudo mkdir -p -m 0755 ${mnt}/${PROJECT_NAME}/{selftests,travis-ci}
|
||||
tree --du -shaC "${REPO_ROOT}/selftests/bpf"
|
||||
sudo rsync -avm "${REPO_ROOT}/selftests/bpf" "$mnt/${PROJECT_NAME}/selftests/"
|
||||
sudo rsync -avm "${REPO_ROOT}/travis-ci/vmtest" "$mnt/${PROJECT_NAME}/travis-ci/"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
setup_script="#!/bin/sh
|
||||
@@ -423,9 +440,10 @@ fi
|
||||
echo "${setup_script}" | sudo tee "$mnt/etc/rcS.d/S50-run-tests" > /dev/null
|
||||
sudo chmod 755 "$mnt/etc/rcS.d/S50-run-tests"
|
||||
|
||||
fold_shutdown="$(travis_fold start shutdown)"
|
||||
poweroff_script="#!/bin/sh
|
||||
|
||||
echo travis_fold:start:shutdown
|
||||
echo ${fold_shutdown}
|
||||
echo -e '\033[1;33mShutdown\033[0m\n'
|
||||
|
||||
poweroff"
|
||||
@@ -436,11 +454,15 @@ sudo umount "$mnt"
|
||||
|
||||
echo "Starting VM with $(nproc) CPUs..."
|
||||
|
||||
if kvm-ok ; then
|
||||
accel="-cpu kvm64 -enable-kvm"
|
||||
else
|
||||
accel="-cpu qemu64 -machine accel=tcg"
|
||||
fi
|
||||
qemu-system-x86_64 -nodefaults -display none -serial mon:stdio \
|
||||
-cpu kvm64 -enable-kvm -smp "$(nproc)" -m 4G \
|
||||
-drive file="$IMG",format=raw,index=1,media=disk,if=virtio,cache=none \
|
||||
-kernel "$vmlinuz" -append "root=/dev/vda rw console=ttyS0,115200$APPEND"
|
||||
|
||||
${accel} -smp "$(nproc)" -m 4G \
|
||||
-drive file="$IMG",format=raw,index=1,media=disk,if=virtio,cache=none \
|
||||
-kernel "$vmlinuz" -append "root=/dev/vda rw console=ttyS0,115200$APPEND"
|
||||
sudo mount -o loop "$IMG" "$mnt"
|
||||
if exitstatus="$(cat "$mnt/exitstatus" 2>/dev/null)"; then
|
||||
printf '\nTests exit status: %s\n' "$exitstatus" >&2
|
||||
|
||||
@@ -46,6 +46,6 @@ cd libbpf/selftests/bpf
|
||||
test_progs
|
||||
|
||||
if [[ "${KERNEL}" == 'latest' ]]; then
|
||||
#test_maps
|
||||
# test_maps
|
||||
test_verifier
|
||||
fi
|
||||
|
||||
@@ -4,7 +4,11 @@ set -eu
|
||||
|
||||
source $(cd $(dirname $0) && pwd)/helpers.sh
|
||||
|
||||
VMTEST_SETUPCMD="PROJECT_NAME=${PROJECT_NAME} ./${PROJECT_NAME}/travis-ci/vmtest/run_selftests.sh"
|
||||
VMTEST_SETUPCMD="GITHUB_WORKFLOW=${GITHUB_WORKFLOW:-} PROJECT_NAME=${PROJECT_NAME} ./${PROJECT_NAME}/travis-ci/vmtest/run_selftests.sh"
|
||||
|
||||
# if CHECKOUT_KERNEL is 1 code will consider that kernel code lives elsewhere
|
||||
# if 0 it will consider that REPO_ROOT is a kernel tree
|
||||
CHECKOUT_KERNEL=${CHECKOUT_KERNEL:-1}
|
||||
|
||||
echo "KERNEL: $KERNEL"
|
||||
echo
|
||||
@@ -19,12 +23,17 @@ sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal mai
|
||||
sudo apt-get update
|
||||
sudo apt-get install --allow-downgrades -y libc6=2.31-0ubuntu9.2
|
||||
sudo aptitude install -y g++ libelf-dev
|
||||
sudo aptitude install -y clang-13 lld-13 llvm-13
|
||||
sudo aptitude install -y clang-14 lld-14 llvm-14
|
||||
|
||||
travis_fold end install_clang
|
||||
|
||||
# Build selftests (and latest kernel, if necessary)
|
||||
KERNEL="${KERNEL}" ${VMTEST_ROOT}/prepare_selftests.sh travis-ci/vmtest/bpf-next
|
||||
|
||||
if [[ "$CHECKOUT_KERNEL" == "1" ]]; then
|
||||
${VMTEST_ROOT}/prepare_selftests.sh travis-ci/vmtest/bpf-next
|
||||
else
|
||||
${VMTEST_ROOT}/prepare_selftests.sh
|
||||
fi
|
||||
|
||||
# Escape whitespace characters.
|
||||
setup_cmd=$(sed 's/\([[:space:]]\)/\\\1/g' <<< "${VMTEST_SETUPCMD}")
|
||||
@@ -32,7 +41,11 @@ setup_cmd=$(sed 's/\([[:space:]]\)/\\\1/g' <<< "${VMTEST_SETUPCMD}")
|
||||
sudo adduser "${USER}" kvm
|
||||
|
||||
if [[ "${KERNEL}" = 'LATEST' ]]; then
|
||||
sudo -E sudo -E -u "${USER}" "${VMTEST_ROOT}/run.sh" -b travis-ci/vmtest/bpf-next -o -d ~ -s "${setup_cmd}" ~/root.img;
|
||||
if [[ "$CHECKOUT_KERNEL" == "1" ]]; then
|
||||
sudo -E sudo -E -u "${USER}" "${VMTEST_ROOT}/run.sh" -b travis-ci/vmtest/bpf-next -o -d ~ -s "${setup_cmd}" ~/root.img
|
||||
else
|
||||
sudo -E sudo -E -u "${USER}" "${VMTEST_ROOT}/run.sh" -b "${REPO_ROOT}" -o -d ~ -s "${setup_cmd}" ~/root.img
|
||||
fi
|
||||
else
|
||||
sudo -E sudo -E -u "${USER}" "${VMTEST_ROOT}/run.sh" -k "${KERNEL}*" -o -d ~ -s "${setup_cmd}" ~/root.img;
|
||||
sudo -E sudo -E -u "${USER}" "${VMTEST_ROOT}/run.sh" -k "${KERNEL}*" -o -d ~ -s "${setup_cmd}" ~/root.img
|
||||
fi
|
||||
|
||||
103476
travis-ci/vmtest/vmlinux.h
103476
travis-ci/vmtest/vmlinux.h
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user