Skip to content

Commit

Permalink
Implement libbpf autoload APIs (#3592)
Browse files Browse the repository at this point in the history
* Implement libbpf autoload APIs

Fixes #3555

Signed-off-by: Dave Thaler <[email protected]>

* Suppress spurious compiler warning

Signed-off-by: Dave Thaler <[email protected]>

* Fix test failure

Signed-off-by: Dave Thaler <[email protected]>

* Update tests

Files with no program sections succeed loading

Signed-off-by: Dave Thaler <[email protected]>

* Address PR comment from Anurag

Signed-off-by: Dave Thaler <[email protected]>

* PR feedback

Signed-off-by: Dave Thaler <[email protected]>

* Fix test

Signed-off-by: Dave Thaler <[email protected]>

* Prevent changing prog type of a native program

Signed-off-by: Dave Thaler <[email protected]>

* PR feedback

Signed-off-by: Dave Thaler <[email protected]>

* Remove unused program_type from native load ioctl

Signed-off-by: Dave Thaler <[email protected]>

* Update default autoload value

Signed-off-by: Dave Thaler <[email protected]>

* PR feedback

Signed-off-by: Dave Thaler <[email protected]>

* Fix test

Signed-off-by: Dave Thaler <[email protected]>

* Add check to unit_test to match api_test

Signed-off-by: Dave Thaler <[email protected]>

* Fix api_test

Signed-off-by: Dave Thaler <[email protected]>

---------

Signed-off-by: Dave Thaler <[email protected]>
  • Loading branch information
dthaler authored Jun 3, 2024
1 parent dfbcda9 commit 7b3f682
Show file tree
Hide file tree
Showing 14 changed files with 289 additions and 119 deletions.
2 changes: 2 additions & 0 deletions ebpfapi/Source.def
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ EXPORTS
bpf_prog_test_run_opts
bpf_program__attach
bpf_program__attach_xdp
bpf_program__autoload
bpf_program__fd
bpf_program__get_expected_attach_type
bpf_program__get_type=bpf_program__type
Expand All @@ -87,6 +88,7 @@ EXPORTS
bpf_program__pin
bpf_program__prev
bpf_program__section_name
bpf_program__set_autoload
bpf_program__set_expected_attach_type
bpf_program__set_type
bpf_program__type
Expand Down
29 changes: 29 additions & 0 deletions include/bpf/libbpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,35 @@ bpf_program__attach(const struct bpf_program* prog);
struct bpf_link*
bpf_program__attach_xdp(struct bpf_program* prog, int ifindex);

/**
* @brief Get whether an eBPF program will be loaded when the object is loaded.
*
* @param[in] prog The program to check.
*
* @returns True if the program will be loaded, false if not.
*
* @sa bpf_object__load
* @sa bpf_object__load_xattr
* @sa bpf_program__set_autoload
*/
bool
bpf_program__autoload(const struct bpf_program* prog);

/**
* @brief Set whether an eBPF program will be loaded when the object is loaded.
*
* @param[in] prog The program to update.
*
* @retval 0 The operation was successful.
* @retval <0 An error occured, and errno was set.
*
* @sa bpf_object__load
* @sa bpf_object__load_xattr
* @sa bpf_program__autoload
*/
int
bpf_program__set_autoload(struct bpf_program* prog, bool autoload);

/**
* @brief Attach an eBPF program to an attach point.
*
Expand Down
4 changes: 1 addition & 3 deletions libs/api/Verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,9 +330,7 @@ load_byte_code(

program->handle = ebpf_handle_invalid;
program->program_type = *(const GUID*)raw_program.info.type.platform_specific_data;

// Only autoload programs with a defined program type.
program->autoload = memcmp(&program->program_type, &empty_program_type, sizeof(empty_program_type)) != 0;
program->autoload = true;

program->section_name = cxplat_duplicate_string(raw_program.section_name.c_str());
program->program_name = cxplat_duplicate_string(raw_program.function_name.c_str());
Expand Down
104 changes: 40 additions & 64 deletions libs/api/ebpf_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,7 @@ static std::mutex _pe_parse_mutex;

static ebpf_result_t
_ebpf_program_load_native(
_In_z_ const char* file_name,
_In_opt_ const ebpf_program_type_t* program_type,
_In_opt_ const ebpf_attach_type_t* attach_type,
ebpf_execution_type_t execution_type,
_Inout_ struct bpf_object* object,
_Out_ fd_t* program_fd) noexcept;
_In_z_ const char* file_name, ebpf_execution_type_t execution_type, _Inout_ struct bpf_object* object) noexcept;

static _Ret_z_ const char*
_ebpf_get_section_string(
Expand Down Expand Up @@ -2097,14 +2092,23 @@ CATCH_NO_MEMORY_EBPF_RESULT
static ebpf_result_t
_initialize_ebpf_programs_native(
size_t count_of_programs,
_In_reads_(count_of_programs) ebpf_handle_t* program_handles,
_Inout_updates_(count_of_programs) ebpf_handle_t* program_handles,
_Inout_ std::vector<ebpf_program_t*>& programs) NO_EXCEPT_TRY
{
EBPF_LOG_ENTRY();
ebpf_assert(program_handles);
ebpf_assert(count_of_programs == 0 || program_handles);
ebpf_result_t result = EBPF_SUCCESS;

for (int i = 0; i < count_of_programs; i++) {
ebpf_program_t* program = programs[i];
if (!program->autoload) {
#pragma warning(suppress : 6001) // The SAL annotation checks that program_handles[i] is ok.
if (program_handles[i] != ebpf_handle_invalid) {
Platform::CloseHandle(program_handles[i]);
program_handles[i] = ebpf_handle_invalid;
}
continue;
}
if (program_handles[i] == ebpf_handle_invalid) {
result = EBPF_INVALID_ARGUMENT;
goto Exit;
Expand All @@ -2116,7 +2120,6 @@ _initialize_ebpf_programs_native(
goto Exit;
}

ebpf_program_t* program = programs[i];
program->fd = _create_file_descriptor_for_handle(program_handles[i]);
if (program->fd == ebpf_fd_invalid) {
result = EBPF_NO_MEMORY;
Expand Down Expand Up @@ -2149,7 +2152,7 @@ _initialize_ebpf_object_native(
ebpf_result_t result = EBPF_SUCCESS;

ebpf_assert(count_of_maps == 0 || map_handles);
ebpf_assert(program_handles);
ebpf_assert(count_of_programs == 0 || program_handles);

object.native_module_fd = native_module_fd;

Expand Down Expand Up @@ -2199,6 +2202,7 @@ _initialize_ebpf_object_from_native_file(
_Outptr_result_maybenull_z_ const char** error_message) NO_EXCEPT_TRY
{
ebpf_program_t* program = nullptr;
ebpf_program_type_t empty_program_type{};

EBPF_LOG_ENTRY();
ebpf_assert(file_name);
Expand All @@ -2223,6 +2227,7 @@ _initialize_ebpf_object_from_native_file(
program->program_type = info->program_type;
program->attach_type = info->expected_attach_type;
program->fd = ebpf_fd_invalid;
program->autoload = true;

program->section_name = cxplat_duplicate_string(info->section_name);
if (program->section_name == nullptr) {
Expand Down Expand Up @@ -3324,18 +3329,8 @@ ebpf_object_load(_Inout_ struct bpf_object* object) NO_EXCEPT_TRY
}

if (Platform::_is_native_program(object->file_name)) {
struct bpf_program* program = bpf_object__next_program(object, nullptr);
if (program == nullptr) {
EBPF_RETURN_RESULT(EBPF_INVALID_ARGUMENT);
}
fd_t program_fd;
return _ebpf_program_load_native(
object->file_name,
&program->program_type,
&program->attach_type,
object->execution_type,
object,
&program_fd);
result = _ebpf_program_load_native(object->file_name, object->execution_type, object);
EBPF_RETURN_RESULT(result);
}

try {
Expand Down Expand Up @@ -3485,9 +3480,6 @@ _load_native_module(
* @brief Create maps and load programs from a loaded native module.
*
* @param[in] module_id Module ID corresponding to the native module.
* @param[in] program_type Optionally, the program type to use when loading
* the eBPF program. If program type is not supplied, it is derived from
* the section prefix in the ELF file.
* @param[in] count_of_maps Count of maps present in the native module.
* @param[out] map_handles Array of size count_of_maps which contains the map handles.
* @param[in] count_of_programs Count of programs present in the native module.
Expand All @@ -3504,7 +3496,6 @@ _load_native_module(
static ebpf_result_t
_load_native_programs(
_In_ const GUID* module_id,
_In_opt_ const ebpf_program_type_t* program_type,
size_t count_of_maps,
_Out_writes_(count_of_maps) ebpf_handle_t* map_handles,
size_t count_of_programs,
Expand All @@ -3516,9 +3507,8 @@ _load_native_programs(
// Map count can be 0 (a program without any maps is a valid use case).
ebpf_assert(count_of_maps == 0 || map_handles);

// Program count *must* be non-zero.
ebpf_assert(count_of_programs);
ebpf_assert(program_handles);
// Program count can be 0 (a map without any programs is a valid use case).
ebpf_assert(count_of_programs == 0 || program_handles);

ebpf_result_t result = EBPF_SUCCESS;
uint32_t error = ERROR_SUCCESS;
Expand All @@ -3532,7 +3522,9 @@ _load_native_programs(
if (map_handles) {
memset(map_handles, 0, map_handles_size);
}
memset(program_handles, 0, program_handles_size);
if (program_handles) {
memset(program_handles, 0, program_handles_size);
}

size_t buffer_size = offsetof(ebpf_operation_load_native_programs_reply_t, data) + handles_size;
reply_buffer.resize(buffer_size);
Expand All @@ -3541,7 +3533,6 @@ _load_native_programs(
request.header.id = ebpf_operation_id_t::EBPF_OPERATION_LOAD_NATIVE_PROGRAMS;
request.header.length = sizeof(ebpf_operation_load_native_programs_request_t);
request.module_id = *module_id;
request.program_type = program_type ? *program_type : GUID_NULL;

error = invoke_ioctl(request, reply_buffer);
if (error != ERROR_SUCCESS) {
Expand All @@ -3563,28 +3554,23 @@ _load_native_programs(
if (count_of_maps) {
memcpy(map_handles, reply->data, map_handles_size);
}
memcpy(program_handles, reply->data + map_handles_size, program_handles_size);
if (count_of_programs) {
memcpy(program_handles, reply->data + map_handles_size, program_handles_size);
}

Done:
EBPF_RETURN_RESULT(result);
}

static ebpf_result_t
_ebpf_program_load_native(
_In_z_ const char* file_name,
_In_opt_ const ebpf_program_type_t* program_type,
_In_opt_ const ebpf_attach_type_t* attach_type,
ebpf_execution_type_t execution_type,
_Inout_ struct bpf_object* object,
_Out_ fd_t* program_fd) NO_EXCEPT_TRY
_In_z_ const char* file_name, ebpf_execution_type_t execution_type, _Inout_ struct bpf_object* object) NO_EXCEPT_TRY
{
EBPF_LOG_ENTRY();
UNREFERENCED_PARAMETER(attach_type);
UNREFERENCED_PARAMETER(execution_type);

ebpf_assert(file_name);
ebpf_assert(object);
ebpf_assert(program_fd);

ebpf_result_t result = EBPF_SUCCESS;
uint32_t error;
Expand Down Expand Up @@ -3679,26 +3665,18 @@ _ebpf_program_load_native(

native_module_handle = ebpf_handle_invalid;

if (count_of_programs == 0) {
result = EBPF_INVALID_OBJECT;
EBPF_LOG_MESSAGE_STRING(
EBPF_TRACELOG_LEVEL_ERROR,
EBPF_TRACELOG_KEYWORD_API,
"_ebpf_program_load_native: O programs found",
file_name);
goto Done;
}

// Allocate buffer for program and map handles.
program_handles = (ebpf_handle_t*)ebpf_allocate(count_of_programs * sizeof(ebpf_handle_t));
if (program_handles == nullptr) {
result = EBPF_NO_MEMORY;
EBPF_LOG_MESSAGE_STRING(
EBPF_TRACELOG_LEVEL_ERROR,
EBPF_TRACELOG_KEYWORD_API,
"_ebpf_program_load_native: program handle buffer allocation failed.",
file_name);
goto Done;
// Allocate buffers for program and map handles.
if (count_of_programs > 0) {
program_handles = (ebpf_handle_t*)ebpf_allocate(count_of_programs * sizeof(ebpf_handle_t));
if (program_handles == nullptr) {
result = EBPF_NO_MEMORY;
EBPF_LOG_MESSAGE_STRING(
EBPF_TRACELOG_LEVEL_ERROR,
EBPF_TRACELOG_KEYWORD_API,
"_ebpf_program_load_native: program handle buffer allocation failed.",
file_name);
goto Done;
}
}

if (count_of_maps > 0) {
Expand All @@ -3714,8 +3692,8 @@ _ebpf_program_load_native(
}
}

result = _load_native_programs(
&provider_module_id, program_type, count_of_maps, map_handles, count_of_programs, program_handles);
result =
_load_native_programs(&provider_module_id, count_of_maps, map_handles, count_of_programs, program_handles);
if (result != EBPF_SUCCESS) {
EBPF_LOG_MESSAGE_STRING(
EBPF_TRACELOG_LEVEL_ERROR,
Expand All @@ -3736,8 +3714,6 @@ _ebpf_program_load_native(
goto Done;
}
native_module_fd = ebpf_fd_invalid;

*program_fd = object->programs[0]->fd;
} catch (const std::bad_alloc&) {
result = EBPF_NO_MEMORY;
goto Done;
Expand Down
23 changes: 22 additions & 1 deletion libs/api/libbpf_program.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,23 @@ bpf_program__section_name(const struct bpf_program* program)
return program->section_name;
}

bool
bpf_program__autoload(const struct bpf_program* program)
{
return program->autoload;
}

int
bpf_program__set_autoload(struct bpf_program* program, bool autoload)
{
if (program->object->loaded) {
return libbpf_err(-EINVAL);
}

program->autoload = autoload;
return 0;
}

size_t
bpf_program__size(const struct bpf_program* program)
{
Expand Down Expand Up @@ -488,9 +505,13 @@ bpf_program__set_type(struct bpf_program* program, enum bpf_prog_type type)
if (program->object->loaded) {
return libbpf_err(-EBUSY);
}
if (program->object->execution_type == EBPF_EXECUTION_NATIVE) {
// Native BPF programs have already passed verification and so cannot
// have their program type changed.
return libbpf_err(-EINVAL);
}
const ebpf_program_type_t* program_type = ebpf_get_ebpf_program_type(type);
program->program_type = (program_type != nullptr) ? *program_type : EBPF_PROGRAM_TYPE_UNSPECIFIED;
program->autoload = true; // TODO(issue #3555): remove this once bpf_program__set_autoload is supported.
return 0;
}

Expand Down
19 changes: 9 additions & 10 deletions libs/execution_context/ebpf_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -715,11 +715,6 @@ _ebpf_core_protocol_load_native_programs(
goto Done;
}

if (count_of_program_handles == 0) {
result = EBPF_INVALID_ARGUMENT;
goto Done;
}

result = ebpf_safe_size_t_multiply(count_of_map_handles, sizeof(ebpf_handle_t), &map_handles_size);
if (result != EBPF_SUCCESS) {
goto Done;
Expand Down Expand Up @@ -754,10 +749,12 @@ _ebpf_core_protocol_load_native_programs(
}
}

program_handles = ebpf_allocate_with_tag(sizeof(ebpf_handle_t) * count_of_program_handles, EBPF_POOL_TAG_CORE);
if (program_handles == NULL) {
result = EBPF_NO_MEMORY;
goto Done;
if (count_of_program_handles) {
program_handles = ebpf_allocate_with_tag(sizeof(ebpf_handle_t) * count_of_program_handles, EBPF_POOL_TAG_CORE);
if (program_handles == NULL) {
result = EBPF_NO_MEMORY;
goto Done;
}
}

result = ebpf_native_load_programs(
Expand All @@ -772,7 +769,9 @@ _ebpf_core_protocol_load_native_programs(
if (map_handles) {
memcpy(reply->data, map_handles, map_handles_size);
}
memcpy(reply->data + map_handles_size, program_handles, program_handles_size);
if (program_handles) {
memcpy(reply->data + map_handles_size, program_handles, program_handles_size);
}

Done:
ebpf_free(map_handles);
Expand Down
Loading

0 comments on commit 7b3f682

Please sign in to comment.