Skip to content

Commit 81f65e8

Browse files
committed
Gate perf attachment helpers behind perf usage
Signed-off-by: Cong Wang <cwang@multikernel.io>
1 parent 27cbd7e commit 81f65e8

2 files changed

Lines changed: 122 additions & 69 deletions

File tree

src/userspace_codegen.ml

Lines changed: 92 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -4110,30 +4110,18 @@ void cleanup_bpf_maps(void) {
41104110
let load_function = generate_load_function_with_tail_calls base_name all_usage tail_call_analysis all_setup_code kfunc_dependencies (Ir.get_global_variables ir_multi_prog) in
41114111

41124112
(* Global attachment storage (generated when attach/detach/perf attach/perf read are used) *)
4113-
let attachment_storage = if all_usage.uses_attach || all_usage.uses_detach || all_usage.uses_attach_perf || all_usage.uses_perf_read then
4114-
{|// Global attachment storage for tracking active program attachments
4115-
typedef struct PerfAttachment {
4113+
let uses_perf_state = all_usage.uses_attach_perf || all_usage.uses_perf_read in
4114+
let perf_typedef = if uses_perf_state then
4115+
{|typedef struct PerfAttachment {
41164116
int perf_fd;
41174117
int link_id;
41184118
int prog_fd;
41194119
uint64_t generation;
41204120
} PerfAttachment;
4121-
4122-
struct attachment_entry {
4123-
int attachment_id;
4124-
int prog_fd;
4125-
char target[128];
4126-
uint32_t flags;
4127-
struct bpf_link *link; // For kprobe/tracepoint programs (NULL for XDP)
4128-
int ifindex; // For XDP programs (0 for kprobe/tracepoint)
4129-
int perf_fd; // For perf_event programs (-1 otherwise)
4130-
int detaching; // Non-zero while teardown is in progress
4131-
uint64_t generation; // PerfAttachment stale-handle token
4132-
enum bpf_prog_type type;
4133-
struct attachment_entry *next;
4134-
};
4135-
4136-
struct perf_attachment_state {
4121+
|}
4122+
else "" in
4123+
let perf_state_decls = if uses_perf_state then
4124+
{| struct perf_attachment_state {
41374125
_Atomic uint64_t generation;
41384126
_Atomic int perf_fd;
41394127
_Atomic unsigned int readers;
@@ -4151,13 +4139,12 @@ typedef struct PerfAttachment {
41514139
#define KS_PERF_STATE_CHUNK_MASK (KS_PERF_STATE_CHUNK_SIZE - 1u)
41524140
#define KS_PERF_STATE_MAX_CHUNKS 4096u
41534141

4154-
static struct attachment_entry *attached_programs = NULL;
41554142
static _Atomic(struct perf_attachment_state *) perf_state_chunks[KS_PERF_STATE_MAX_CHUNKS];
4156-
static pthread_mutex_t attachment_mutex = PTHREAD_MUTEX_INITIALIZER;
4157-
static int next_attachment_id = 1;
41584143
static uint64_t next_perf_attachment_generation = 1;
4159-
4160-
static struct perf_attachment_state *perf_state_slot_lookup(int perf_fd) {
4144+
|}
4145+
else "" in
4146+
let perf_helpers = if uses_perf_state then
4147+
{| static struct perf_attachment_state *perf_state_slot_lookup(int perf_fd) {
41614148
if (perf_fd < 0) {
41624149
return NULL;
41634150
}
@@ -4254,7 +4241,60 @@ typedef struct PerfAttachment {
42544241
static void perf_attachment_end_read(struct perf_attachment_state *state) {
42554242
atomic_fetch_sub_explicit(&state->readers, 1, memory_order_release);
42564243
}
4244+
|}
4245+
else "" in
4246+
let add_attachment_perf_branch = if uses_perf_state then
4247+
{| if (type == BPF_PROG_TYPE_PERF_EVENT && perf_fd >= 0) {
4248+
struct perf_attachment_state *state = ensure_perf_attachment_state_locked(perf_fd);
4249+
if (!state) {
4250+
pthread_mutex_unlock(&attachment_mutex);
4251+
free(entry);
4252+
return -1;
4253+
}
4254+
entry->generation = next_perf_attachment_generation++;
4255+
if (next_perf_attachment_generation == 0) {
4256+
next_perf_attachment_generation = 1;
4257+
}
4258+
atomic_store_explicit(&state->perf_fd, perf_fd, memory_order_release);
4259+
atomic_store_explicit(&state->generation, entry->generation, memory_order_release);
4260+
}
4261+
|}
4262+
else "" in
4263+
let perf_find_by_id = if uses_perf_state then
4264+
{| static struct attachment_entry *find_attachment_by_id_locked(int attachment_id) {
4265+
struct attachment_entry *entry = attached_programs;
4266+
while (entry) {
4267+
if (entry->attachment_id == attachment_id) {
4268+
return entry;
4269+
}
4270+
entry = entry->next;
4271+
}
4272+
return NULL;
4273+
}
4274+
|}
4275+
else "" in
4276+
let attachment_storage = if all_usage.uses_attach || all_usage.uses_detach || uses_perf_state then
4277+
sprintf {|// Global attachment storage for tracking active program attachments
4278+
%s
4279+
struct attachment_entry {
4280+
int attachment_id;
4281+
int prog_fd;
4282+
char target[128];
4283+
uint32_t flags;
4284+
struct bpf_link *link; // For kprobe/tracepoint programs (NULL for XDP)
4285+
int ifindex; // For XDP programs (0 for kprobe/tracepoint)
4286+
int perf_fd; // For perf_event programs (-1 otherwise)
4287+
int detaching; // Non-zero while teardown is in progress
4288+
uint64_t generation; // PerfAttachment stale-handle token
4289+
enum bpf_prog_type type;
4290+
struct attachment_entry *next;
4291+
};
42574292
4293+
%s static struct attachment_entry *attached_programs = NULL;
4294+
static pthread_mutex_t attachment_mutex = PTHREAD_MUTEX_INITIALIZER;
4295+
static int next_attachment_id = 1;
4296+
4297+
%s
42584298
// Helper function to add attachment entry.
42594299
// Duplicate check is performed atomically under the same lock as insertion.
42604300
static int add_attachment(int prog_fd, const char *target, uint32_t flags,
@@ -4266,7 +4306,7 @@ typedef struct PerfAttachment {
42664306
fprintf(stderr, "Failed to allocate memory for attachment entry\n");
42674307
return -1;
42684308
}
4269-
4309+
42704310
entry->prog_fd = prog_fd;
42714311
entry->attachment_id = 0;
42724312
strncpy(entry->target, target, sizeof(entry->target) - 1);
@@ -4276,7 +4316,7 @@ typedef struct PerfAttachment {
42764316
entry->ifindex = ifindex;
42774317
entry->perf_fd = perf_fd;
42784318
entry->type = type;
4279-
4319+
42804320
entry->detaching = 0;
42814321
entry->generation = 0;
42824322
pthread_mutex_lock(&attachment_mutex);
@@ -4290,27 +4330,13 @@ typedef struct PerfAttachment {
42904330
!existing->detaching) {
42914331
pthread_mutex_unlock(&attachment_mutex);
42924332
free(entry);
4293-
fprintf(stderr, "Program with fd %d is already attached. Use detach() first.\n", prog_fd);
4333+
fprintf(stderr, "Program with fd %%d is already attached. Use detach() first.\n", prog_fd);
42944334
return -1;
42954335
}
42964336
existing = existing->next;
42974337
}
42984338
entry->attachment_id = next_attachment_id++;
4299-
if (type == BPF_PROG_TYPE_PERF_EVENT && perf_fd >= 0) {
4300-
struct perf_attachment_state *state = ensure_perf_attachment_state_locked(perf_fd);
4301-
if (!state) {
4302-
pthread_mutex_unlock(&attachment_mutex);
4303-
free(entry);
4304-
return -1;
4305-
}
4306-
entry->generation = next_perf_attachment_generation++;
4307-
if (next_perf_attachment_generation == 0) {
4308-
next_perf_attachment_generation = 1;
4309-
}
4310-
atomic_store_explicit(&state->perf_fd, perf_fd, memory_order_release);
4311-
atomic_store_explicit(&state->generation, entry->generation, memory_order_release);
4312-
}
4313-
entry->next = attached_programs;
4339+
%s entry->next = attached_programs;
43144340
attached_programs = entry;
43154341
if (attachment_id_out) {
43164342
*attachment_id_out = entry->attachment_id;
@@ -4319,21 +4345,11 @@ typedef struct PerfAttachment {
43194345
*generation_out = entry->generation;
43204346
}
43214347
pthread_mutex_unlock(&attachment_mutex);
4322-
4323-
return 0;
4324-
}
43254348
4326-
static struct attachment_entry *find_attachment_by_id_locked(int attachment_id) {
4327-
struct attachment_entry *entry = attached_programs;
4328-
while (entry) {
4329-
if (entry->attachment_id == attachment_id) {
4330-
return entry;
4331-
}
4332-
entry = entry->next;
4333-
}
4334-
return NULL;
4349+
return 0;
43354350
}
43364351
4352+
%s
43374353
/* Helper: find the bpf_program in the skeleton object for a given fd.
43384354
* Returns NULL if the skeleton is not loaded or no program matches. */
43394355
static struct bpf_program *find_prog_by_fd(int prog_fd) {
@@ -4346,7 +4362,7 @@ typedef struct PerfAttachment {
43464362
}
43474363
return NULL;
43484364
}
4349-
|}
4365+
|} perf_typedef perf_state_decls perf_helpers add_attachment_perf_branch perf_find_by_id
43504366
else "" in
43514367
43524368
let attach_function = if all_usage.uses_attach then
@@ -4556,7 +4572,10 @@ typedef struct PerfAttachment {
45564572
break;
45574573
}|}
45584574
else "" in
4559-
let detach_function = if all_usage.uses_detach || all_usage.uses_attach_perf then
4575+
let invalidate_call_line = if uses_perf_state then
4576+
" invalidate_perf_attachment_state_locked(entry);\n"
4577+
else "" in
4578+
let detach_entry_dispatch = if all_usage.uses_detach || all_usage.uses_attach_perf then
45604579
sprintf {|static void ks_detach_attachment_entry(struct attachment_entry *entry, int identifier_for_logs) {
45614580
if (!entry) {
45624581
return;
@@ -4613,9 +4632,10 @@ typedef struct PerfAttachment {
46134632
fprintf(stderr, "Unsupported program type for detachment: %%d\n", entry->type);
46144633
break;
46154634
}
4616-
}
4617-
4618-
void detach_bpf_program_by_fd(int prog_fd) {
4635+
}|} detach_perf_case
4636+
else "" in
4637+
let std_detach_function = if all_usage.uses_detach then
4638+
sprintf {|void detach_bpf_program_by_fd(int prog_fd) {
46194639
if (prog_fd < 0) {
46204640
fprintf(stderr, "Invalid program file descriptor: %%d\n", prog_fd);
46214641
return;
@@ -4629,8 +4649,7 @@ void detach_bpf_program_by_fd(int prog_fd) {
46294649
while (entry) {
46304650
if (entry->prog_fd == prog_fd && !entry->detaching) {
46314651
entry->detaching = 1;
4632-
invalidate_perf_attachment_state_locked(entry);
4633-
break;
4652+
%s break;
46344653
}
46354654
entry = entry->next;
46364655
}
@@ -4655,11 +4674,12 @@ void detach_bpf_program_by_fd(int prog_fd) {
46554674
pthread_mutex_unlock(&attachment_mutex);
46564675
free(entry);
46574676
}
4658-
}
4659-
4660-
void ks_detach_perf_attachment(PerfAttachment attachment) {
4677+
}|} invalidate_call_line
4678+
else "" in
4679+
let perf_detach_function = if all_usage.uses_attach_perf then
4680+
{|void ks_detach_perf_attachment(PerfAttachment attachment) {
46614681
if (attachment.link_id <= 0) {
4662-
fprintf(stderr, "Invalid perf attachment link id: %%d\n", attachment.link_id);
4682+
fprintf(stderr, "Invalid perf attachment link id: %d\n", attachment.link_id);
46634683
return;
46644684
}
46654685
@@ -4674,7 +4694,7 @@ void ks_detach_perf_attachment(PerfAttachment attachment) {
46744694
pthread_mutex_unlock(&attachment_mutex);
46754695
46764696
if (!entry) {
4677-
fprintf(stderr, "No active perf attachment found for link id %%d\n", attachment.link_id);
4697+
fprintf(stderr, "No active perf attachment found for link id %d\n", attachment.link_id);
46784698
return;
46794699
}
46804700
@@ -4691,8 +4711,13 @@ void ks_detach_perf_attachment(PerfAttachment attachment) {
46914711
}
46924712
pthread_mutex_unlock(&attachment_mutex);
46934713
free(entry);
4694-
}|} detach_perf_case
4714+
}|}
46954715
else "" in
4716+
let detach_function =
4717+
[detach_entry_dispatch; std_detach_function; perf_detach_function]
4718+
|> List.filter (fun s -> s <> "")
4719+
|> String.concat "\n\n"
4720+
in
46964721
46974722
let bpf_obj_decl = "" in (* Skeleton now handles the BPF object *)
46984723

tests/test_perf_event_attach.ml

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -350,8 +350,36 @@ let test_perf_attach_event_function_generated () =
350350
let test_detach_attach_concurrent_window () =
351351
(* During a detach, the entry stays in the list but is marked detaching=1.
352352
* A concurrent attach for the same prog_fd must succeed (not be blocked by
353-
* the still-present but detaching entry). *)
354-
let code = make_perf_code_with ~period:1000000L ~wakeup:1L in
353+
* the still-present but detaching entry).
354+
* We exercise BOTH detach paths here (std detach via detach(prog) and perf
355+
* detach via the perf attach machinery), since each path is only emitted
356+
* when actually used. *)
357+
let prog_handle = make_ir_value (IRVariable "prog") IRI32 test_pos in
358+
let attr_value = make_ir_value (IRVariable "attr") (IRStruct ("perf_options", [])) test_pos in
359+
let flags_value = uint32_value 0L in
360+
let attachment_value =
361+
make_ir_value
362+
(IRVariable "att")
363+
(IRStruct ("PerfAttachment", [("perf_fd", IRI32); ("link_id", IRI32); ("prog_fd", IRI32); ("generation", IRU64)]))
364+
test_pos
365+
in
366+
let attr_decl =
367+
make_ir_instruction
368+
(IRVariableDecl (attr_value, IRStruct ("perf_options", []),
369+
Some (perf_attr_expr_with ~period:1000000L ~wakeup:1L)))
370+
test_pos
371+
in
372+
let attach_call =
373+
make_ir_instruction
374+
(IRCall (DirectCall "attach", [prog_handle; attr_value; flags_value], Some attachment_value))
375+
test_pos
376+
in
377+
let detach_call =
378+
make_ir_instruction
379+
(IRCall (DirectCall "detach", [prog_handle], None))
380+
test_pos
381+
in
382+
let code = make_generated_code [attr_decl; attach_call; detach_call] in
355383
check bool "attachment_entry has detaching field" true
356384
(contains_substr code "int detaching;");
357385
check bool "add_attachment skips detaching entries in duplicate check" true

0 commit comments

Comments
 (0)