Add DETACH operation to detach a program from a hook. (#352)

* add UNLINK operation

* Some fixes:

1. Added attach_lock to synchronize multiple detach calls on same link object.
2. ebpf_extension_unload() should be called from ebpf_link_detach_program()
3. Changed return type of ebpf_program_get_properties to void.

* Update libs/execution_context/ebpf_program.c

Co-authored-by: Dave Thaler <dthaler@microsoft.com>
This commit is contained in:
saxena-anurag 2021-07-30 17:17:16 -07:00 коммит произвёл GitHub
Родитель 8006f181e0
Коммит 14248601d3
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 316 добавлений и 42 удалений

Просмотреть файл

@ -354,6 +354,17 @@ extern "C"
uint32_t
ebpf_api_link_program(ebpf_handle_t program_handle, ebpf_attach_type_t attach_type, ebpf_handle_t* link_handle);
/**
* @brief Detach the eBPF program from the link.
*
* @param[in] link_handle Handle to the link.
*
* @retval ERROR_SUCCESS The operations succeeded.
* @retval ERROR_INVALID_PARAMETER The link handle is invalid.
*/
uint32_t
ebpf_api_unlink_program(ebpf_handle_t link_handle);
/**
* @brief Close an eBPF handle.
*

Просмотреть файл

@ -556,6 +556,15 @@ ebpf_api_link_program(ebpf_handle_t program_handle, ebpf_attach_type_t attach_ty
return retval;
}
uint32_t
ebpf_api_unlink_program(ebpf_handle_t link_handle)
{
ebpf_operation_unlink_program_request_t request = {
sizeof(request), EBPF_OPERATION_UNLINK_PROGRAM, reinterpret_cast<uint64_t>(link_handle)};
return invoke_ioctl(request);
}
uint32_t
ebpf_api_close_handle(ebpf_handle_t handle)
{

Просмотреть файл

@ -552,9 +552,7 @@ _ebpf_core_protocol_query_program_info(
if (retval != EBPF_SUCCESS)
goto Done;
retval = ebpf_program_get_properties(program, &parameters);
if (retval != EBPF_SUCCESS)
goto Done;
ebpf_program_get_properties(program, &parameters);
required_reply_length = EBPF_OFFSET_OF(struct _ebpf_operation_query_program_info_reply, data) +
parameters.program_name.length + parameters.section_name.length;
@ -665,11 +663,32 @@ _ebpf_core_protocol_link_program(
goto Done;
Done:
if (retval != EBPF_SUCCESS) {
ebpf_link_detach_program(link);
}
ebpf_object_release_reference((ebpf_object_t*)program);
ebpf_object_release_reference((ebpf_object_t*)link);
return retval;
}
static ebpf_result_t
_ebpf_core_protocol_unlink_program(_In_ const ebpf_operation_unlink_program_request_t* request)
{
ebpf_result_t retval;
ebpf_link_t* link = NULL;
retval = ebpf_reference_object_by_handle(request->link_handle, EBPF_OBJECT_LINK, (ebpf_object_t**)&link);
if (retval != EBPF_SUCCESS) {
goto Done;
}
ebpf_link_detach_program(link);
Done:
ebpf_object_release_reference((ebpf_object_t*)link);
return retval;
}
static ebpf_result_t
_ebpf_core_protocol_close_handle(_In_ const ebpf_operation_close_handle_request_t* request)
{
@ -966,6 +985,11 @@ static ebpf_protocol_handler_t _ebpf_protocol_handlers[] = {
sizeof(ebpf_operation_link_program_request_t),
sizeof(ebpf_operation_link_program_reply_t)},
// EBPF_OPERATION_UNLINK_PROGRAM
{(ebpf_result_t(__cdecl*)(const void*))_ebpf_core_protocol_unlink_program,
sizeof(ebpf_operation_unlink_program_request_t),
0},
// EBPF_OPERATION_CLOSE_HANDLE
{(ebpf_result_t(__cdecl*)(const void*))_ebpf_core_protocol_close_handle,
sizeof(ebpf_operation_close_handle_request_t),

Просмотреть файл

@ -16,6 +16,7 @@ typedef struct _ebpf_link
ebpf_program_type_t program_type;
ebpf_extension_data_t client_data;
ebpf_extension_client_t* extension_client_context;
ebpf_lock_t attach_lock;
void* provider_binding_context;
} ebpf_link_t;
@ -34,9 +35,7 @@ static void
_ebpf_link_free(ebpf_object_t* object)
{
ebpf_link_t* link = (ebpf_link_t*)object;
ebpf_extension_unload(link->extension_client_context);
ebpf_link_detach_program(link);
ebpf_free(link->client_data.data);
ebpf_lock_destroy(&link->attach_lock);
ebpf_epoch_free(link);
}
@ -50,6 +49,7 @@ ebpf_link_create(ebpf_link_t** link)
memset(*link, 0, sizeof(ebpf_link_t));
ebpf_object_initialize(&(*link)->object, EBPF_OBJECT_LINK, _ebpf_link_free);
ebpf_lock_create(&(*link)->attach_lock);
return EBPF_SUCCESS;
}
@ -106,42 +106,46 @@ ebpf_link_attach_program(ebpf_link_t* link, ebpf_program_t* program)
{
ebpf_result_t return_value = EBPF_SUCCESS;
ebpf_program_parameters_t program_parameters;
ebpf_lock_state_t state;
state = ebpf_lock_lock(&link->attach_lock);
if (link->program) {
return_value = EBPF_INVALID_ARGUMENT;
goto Done;
}
link->program = program;
ebpf_object_acquire_reference((ebpf_object_t*)program);
return_value = ebpf_program_get_properties(program, &program_parameters);
if (return_value != EBPF_SUCCESS) {
goto Done;
}
ebpf_program_get_properties(program, &program_parameters);
if (memcmp(&program_parameters.program_type, &link->program_type, sizeof(link->program_type)) != 0) {
return_value = EBPF_INVALID_ARGUMENT;
goto Done;
}
link->program = program;
ebpf_program_attach_link(program, link);
Done:
if (return_value != EBPF_SUCCESS) {
if (link->program == program) {
ebpf_object_release_reference((ebpf_object_t*)program);
link->program = NULL;
}
}
ebpf_lock_unlock(&link->attach_lock, state);
return return_value;
}
void
ebpf_link_detach_program(ebpf_link_t* link)
{
if (!link->program)
ebpf_lock_state_t state;
ebpf_program_t* program;
state = ebpf_lock_lock(&link->attach_lock);
if (!link->program) {
ebpf_lock_unlock(&link->attach_lock, state);
return;
}
ebpf_object_release_reference((ebpf_object_t*)link->program);
program = link->program;
link->program = NULL;
ebpf_lock_unlock(&link->attach_lock, state);
ebpf_program_detach_link(program, link);
ebpf_extension_unload(link->extension_client_context);
ebpf_free(link->client_data.data);
}
static ebpf_result_t

Просмотреть файл

@ -47,8 +47,21 @@ typedef struct _ebpf_program
ebpf_trampoline_table_t* trampoline_table;
ebpf_epoch_work_item_t* cleanup_work_item;
ebpf_list_entry_t links;
ebpf_lock_t links_lock;
} ebpf_program_t;
static void
_ebpf_program_detach_links(_Inout_ ebpf_program_t* program)
{
while (!ebpf_list_is_empty(&program->links)) {
ebpf_list_entry_t* entry = program->links.Flink;
ebpf_object_t* object = CONTAINING_RECORD(entry, ebpf_object_t, object_list_entry);
ebpf_link_detach_program((ebpf_link_t*)object);
}
}
static void
_ebpf_program_program_info_provider_changed(
_In_ void* client_binding_context,
@ -110,8 +123,13 @@ static void
_ebpf_program_free(ebpf_object_t* object)
{
ebpf_program_t* program = (ebpf_program_t*)object;
if (!program)
if (!program) {
return;
}
// Detach from all the attach points.
_ebpf_program_detach_links(program);
ebpf_assert(ebpf_list_is_empty(&program->links));
ebpf_epoch_schedule_work_item(program->cleanup_work_item);
}
@ -129,6 +147,8 @@ _ebpf_program_epoch_free(void* context)
ebpf_program_t* program = (ebpf_program_t*)context;
size_t index;
ebpf_lock_destroy(&program->links_lock);
ebpf_extension_unload(program->general_helper_extension_client);
ebpf_extension_unload(program->program_info_client);
@ -275,6 +295,9 @@ ebpf_program_initialize(ebpf_program_t* program, const ebpf_program_parameters_t
goto Done;
}
ebpf_list_initialize(&program->links);
ebpf_lock_create(&program->links_lock);
return_value = EBPF_SUCCESS;
Done:
@ -283,11 +306,10 @@ Done:
return return_value;
}
ebpf_result_t
void
ebpf_program_get_properties(ebpf_program_t* program, ebpf_program_parameters_t* program_parameters)
{
*program_parameters = program->parameters;
return EBPF_SUCCESS;
}
ebpf_result_t
@ -550,3 +572,29 @@ ebpf_program_free_program_info(_In_opt_ _Post_invalid_ ebpf_program_info_t* prog
ebpf_free(program_info);
}
}
void
ebpf_program_attach_link(_Inout_ ebpf_program_t* program, _Inout_ ebpf_link_t* link)
{
// Acquire "attach" reference on the link object.
ebpf_object_acquire_reference((ebpf_object_t*)link);
// Insert the link in the attach list.
ebpf_lock_state_t state;
state = ebpf_lock_lock(&program->links_lock);
ebpf_list_insert_tail(&program->links, &((ebpf_object_t*)link)->object_list_entry);
ebpf_lock_unlock(&program->links_lock, state);
}
void
ebpf_program_detach_link(_Inout_ ebpf_program_t* program, _Inout_ ebpf_link_t* link)
{
// Remove the link from the attach list.
ebpf_lock_state_t state;
state = ebpf_lock_lock(&program->links_lock);
ebpf_list_remove_entry(&((ebpf_object_t*)link)->object_list_entry);
ebpf_lock_unlock(&program->links_lock, state);
// Release the "attach" reference.
ebpf_object_release_reference((ebpf_object_t*)link);
}

Просмотреть файл

@ -64,11 +64,8 @@ extern "C"
*
* @param[in] program Program instance to query.
* @param[in] program_parameters Parameters of the program.
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_NO_MEMORY Unable to allocate resources for this
* program instance.
*/
ebpf_result_t
void
ebpf_program_get_properties(ebpf_program_t* program, ebpf_program_parameters_t* program_parameters);
/**
@ -158,6 +155,24 @@ extern "C"
ebpf_program_get_helper_function_address(
const ebpf_program_t* program, uint32_t helper_function_id, uint64_t* address);
/**
* @brief Attach a link object to an eBPF program.
*
* @param[in] program Program to attach to the link object.
* @param[in] link The link object.
*/
void
ebpf_program_attach_link(_Inout_ ebpf_program_t* program, _Inout_ ebpf_link_t* link);
/**
* @brief Detach a link object from the eBPF program it is attached to.
*
* @param[in] program Program to detach to the link object from.
* @param[in] link The link object.
*/
void
ebpf_program_detach_link(_Inout_ ebpf_program_t* program, _Inout_ ebpf_link_t* link);
#ifdef __cplusplus
}
#endif

Просмотреть файл

@ -24,6 +24,7 @@ typedef enum _ebpf_operation_id
EBPF_OPERATION_UPDATE_PINNING,
EBPF_OPERATION_GET_PINNING,
EBPF_OPERATION_LINK_PROGRAM,
EBPF_OPERATION_UNLINK_PROGRAM,
EBPF_OPERATION_CLOSE_HANDLE,
EBPF_OPERATION_GET_EC_FUNCTION,
EBPF_OPERATION_GET_PROGRAM_INFO,
@ -233,6 +234,12 @@ typedef struct _ebpf_operation_link_program_reply
uint64_t link_handle;
} ebpf_operation_link_program_reply_t;
typedef struct _ebpf_operation_unlink_program_request
{
struct _ebpf_operation_header header;
uint64_t link_handle;
} ebpf_operation_unlink_program_request_t;
typedef struct _ebpf_operation_close_handle_request
{
struct _ebpf_operation_header header;

Просмотреть файл

@ -136,7 +136,7 @@ TEST_CASE("program", "[execution_context]")
REQUIRE(ebpf_program_initialize(program.get(), &program_parameters) == EBPF_SUCCESS);
REQUIRE(ebpf_program_get_properties(program.get(), &returned_program_parameters) == EBPF_SUCCESS);
ebpf_program_get_properties(program.get(), &returned_program_parameters);
REQUIRE(
memcmp(
&program_parameters.program_type,

Просмотреть файл

@ -34,7 +34,7 @@ _ebpf_object_tracking_list_insert(ebpf_object_t* object)
{
ebpf_lock_state_t state;
state = ebpf_lock_lock(&_ebpf_object_tracking_list_lock);
ebpf_list_insert_tail(&_ebpf_object_tracking_list, &object->entry);
ebpf_list_insert_tail(&_ebpf_object_tracking_list, &object->global_list_entry);
ebpf_lock_unlock(&_ebpf_object_tracking_list_lock, state);
}
@ -43,7 +43,7 @@ _ebpf_object_tracking_list_remove(ebpf_object_t* object)
{
ebpf_lock_state_t state;
state = ebpf_lock_lock(&_ebpf_object_tracking_list_lock);
ebpf_list_remove_entry(&object->entry);
ebpf_list_remove_entry(&object->global_list_entry);
ebpf_lock_unlock(&_ebpf_object_tracking_list_lock, state);
}
@ -67,7 +67,8 @@ ebpf_object_initialize(ebpf_object_t* object, ebpf_object_type_t object_type, eb
object->reference_count = 1;
object->type = object_type;
object->free_function = free_function;
ebpf_list_initialize(&object->entry);
ebpf_list_initialize(&object->global_list_entry);
ebpf_list_initialize(&object->object_list_entry);
_ebpf_object_tracking_list_insert(object);
}
@ -133,10 +134,10 @@ ebpf_object_reference_next_object(ebpf_object_t* previous_object, ebpf_object_ty
if (previous_object == NULL)
entry = _ebpf_object_tracking_list.Flink;
else
entry = previous_object->entry.Flink;
entry = previous_object->global_list_entry.Flink;
for (; entry != &_ebpf_object_tracking_list; entry = entry->Flink) {
ebpf_object_t* object = EBPF_FROM_FIELD(ebpf_object_t, entry, entry);
ebpf_object_t* object = EBPF_FROM_FIELD(ebpf_object_t, global_list_entry, entry);
if (object->type == type) {
*next_object = object;
ebpf_object_acquire_reference(object);

Просмотреть файл

@ -28,7 +28,10 @@ extern "C"
volatile int32_t reference_count;
ebpf_object_type_t type;
ebpf_free_object_t free_function;
ebpf_list_entry_t entry;
// Used to insert object in the global tracking list.
ebpf_list_entry_t global_list_entry;
// Used to insert object in an object specific list.
ebpf_list_entry_t object_list_entry;
} ebpf_object_t;
/**

Просмотреть файл

@ -160,7 +160,7 @@ droppacket_test(ebpf_execution_type_t execution_type)
ebpf_handle_t map_handle;
uint32_t count_of_map_handle = 1;
uint32_t result = 0;
const char* error_message = NULL;
const char* error_message = nullptr;
single_instance_hook_t hook(EBPF_PROGRAM_TYPE_XDP, EBPF_ATTACH_TYPE_XDP);
program_info_provider_t xdp_program_info(EBPF_PROGRAM_TYPE_XDP);
@ -230,7 +230,7 @@ divide_by_zero_test(ebpf_execution_type_t execution_type)
ebpf_handle_t map_handle;
uint32_t count_of_map_handle = 1;
uint32_t result = 0;
const char* error_message = NULL;
const char* error_message = nullptr;
single_instance_hook_t hook(EBPF_PROGRAM_TYPE_XDP, EBPF_ATTACH_TYPE_XDP);
program_info_provider_t xdp_program_info(EBPF_PROGRAM_TYPE_XDP);
@ -318,7 +318,7 @@ bindmonitor_test(ebpf_execution_type_t execution_type)
_test_helper_end_to_end test_helper;
ebpf_handle_t program_handle;
const char* error_message = NULL;
const char* error_message = nullptr;
ebpf_handle_t map_handles[2];
uint32_t count_of_map_handles = 2;
uint64_t fake_pid = 12345;
@ -461,7 +461,7 @@ TEST_CASE("map_pinning_test", "[end_to_end]")
_test_helper_end_to_end test_helper;
ebpf_handle_t program_handle;
const char* error_message = NULL;
const char* error_message = nullptr;
ebpf_handle_t map_handles[4] = {0};
uint32_t count_of_map_handles = 2;
uint32_t result;
@ -542,7 +542,7 @@ TEST_CASE("enumerate_and_query_maps", "[end_to_end]")
_test_helper_end_to_end test_helper;
ebpf_handle_t program_handle;
const char* error_message = NULL;
const char* error_message = nullptr;
ebpf_handle_t map_handles[4];
uint32_t count_of_map_handles = 2;
uint32_t result;
@ -607,7 +607,7 @@ TEST_CASE("enumerate_and_query_programs", "[end_to_end]")
ebpf_handle_t program_handle;
ebpf_handle_t map_handles[3];
uint32_t count_of_map_handle = 1;
const char* error_message = NULL;
const char* error_message = nullptr;
uint32_t result;
const char* file_name = nullptr;
const char* section_name = nullptr;
@ -674,3 +674,148 @@ TEST_CASE("pinned_map_enum", "[end_to_end]")
ebpf_test_pinned_map_enum();
}
TEST_CASE("implicit_detach", "[end_to_end]")
{
// This test case does the following:
// 1. Close program handle. An implicit detach should happen and program
// object should be deleted.
// 2. Close link handle. The link object should be deleted.
_test_helper_end_to_end test_helper;
ebpf_handle_t program_handle;
ebpf_handle_t map_handle;
uint32_t count_of_map_handle = 1;
uint32_t result = 0;
const char* error_message = nullptr;
single_instance_hook_t hook(EBPF_PROGRAM_TYPE_XDP, EBPF_ATTACH_TYPE_XDP);
program_info_provider_t xdp_program_info(EBPF_PROGRAM_TYPE_XDP);
result = ebpf_api_load_program(
SAMPLE_PATH "droppacket.o",
"xdp",
EBPF_EXECUTION_INTERPRET,
&program_handle,
&count_of_map_handle,
&map_handle,
&error_message);
if (error_message) {
printf("ebpf_api_load_program failed with %s\n", error_message);
ebpf_free_string(error_message);
error_message = nullptr;
}
REQUIRE(result == EBPF_SUCCESS);
REQUIRE(hook.attach(program_handle) == EBPF_SUCCESS);
// Close program handle. That should detach the program from the hook
// and unload the program.
ebpf_api_close_handle(program_handle);
program_handle = INVALID_HANDLE_VALUE;
REQUIRE(ebpf_api_get_next_program(program_handle, &program_handle) == EBPF_SUCCESS);
REQUIRE(program_handle == INVALID_HANDLE_VALUE);
// Close link handle. This should delete the link object.
// ebpf_object_tracking_terminate() which is called when the test
// exits checks if all the objects in EC have been deleted.
hook.close_handle();
}
TEST_CASE("explicit_detach", "[end_to_end]")
{
// This test case does the following:
// 1. Call detach API and then close the link handle. The link onject
// should be deleted.
// 2. Close program handle. The program object should be deleted.
_test_helper_end_to_end test_helper;
ebpf_handle_t program_handle;
ebpf_handle_t map_handle;
uint32_t count_of_map_handle = 1;
uint32_t result = 0;
const char* error_message = nullptr;
single_instance_hook_t hook(EBPF_PROGRAM_TYPE_XDP, EBPF_ATTACH_TYPE_XDP);
program_info_provider_t xdp_program_info(EBPF_PROGRAM_TYPE_XDP);
result = ebpf_api_load_program(
SAMPLE_PATH "droppacket.o",
"xdp",
EBPF_EXECUTION_INTERPRET,
&program_handle,
&count_of_map_handle,
&map_handle,
&error_message);
if (error_message) {
printf("ebpf_api_load_program failed with %s\n", error_message);
ebpf_free_string(error_message);
error_message = nullptr;
}
REQUIRE(result == EBPF_SUCCESS);
REQUIRE(hook.attach(program_handle) == EBPF_SUCCESS);
// Detach and close link handle.
// ebpf_object_tracking_terminate() which is called when the test
// exits checks if all the objects in EC have been deleted.
hook.detach();
// Close program handle.
ebpf_api_close_handle(program_handle);
program_handle = INVALID_HANDLE_VALUE;
REQUIRE(ebpf_api_get_next_program(program_handle, &program_handle) == EBPF_SUCCESS);
REQUIRE(program_handle == INVALID_HANDLE_VALUE);
}
TEST_CASE("implicit_explicit_detach", "[end_to_end]")
{
// This test case does the following:
// 1. Close the program handle so that an implicit detach happens.
// 2. Explicitly call detach and then close the link handle. Explicit
// detach in this step should be a no-op.
_test_helper_end_to_end test_helper;
ebpf_handle_t program_handle;
ebpf_handle_t map_handle;
uint32_t count_of_map_handle = 1;
uint32_t result = 0;
const char* error_message = nullptr;
single_instance_hook_t hook(EBPF_PROGRAM_TYPE_XDP, EBPF_ATTACH_TYPE_XDP);
program_info_provider_t xdp_program_info(EBPF_PROGRAM_TYPE_XDP);
result = ebpf_api_load_program(
SAMPLE_PATH "droppacket.o",
"xdp",
EBPF_EXECUTION_INTERPRET,
&program_handle,
&count_of_map_handle,
&map_handle,
&error_message);
if (error_message) {
printf("ebpf_api_load_program failed with %s\n", error_message);
ebpf_free_string(error_message);
error_message = nullptr;
}
REQUIRE(result == EBPF_SUCCESS);
REQUIRE(hook.attach(program_handle) == EBPF_SUCCESS);
// Close program handle. That should detach the program from the hook
// and unload the program.
ebpf_api_close_handle(program_handle);
program_handle = INVALID_HANDLE_VALUE;
REQUIRE(ebpf_api_get_next_program(program_handle, &program_handle) == EBPF_SUCCESS);
REQUIRE(program_handle == INVALID_HANDLE_VALUE);
// Detach and close link handle.
// ebpf_object_tracking_terminate() which is called when the test
// exits checks if all the objects in EC have been deleted.
hook.detach();
}

Просмотреть файл

@ -51,6 +51,13 @@ typedef class _single_instance_hook
void
detach()
{
ebpf_api_unlink_program(link_handle);
ebpf_api_close_handle(link_handle);
}
void
close_handle()
{
ebpf_api_close_handle(link_handle);
}