@@ -3830,7 +3830,9 @@ let generate_complete_userspace_program_from_ir ?(config_declarations = []) ?(ta
38303830#include < getopt.h>
38313831#include < fcntl.h>
38323832#include < net/ if .h>
3833+ #include < sched.h>
38333834#include < setjmp.h>
3835+ #include < stdatomic.h>
38343836#include < linux/ bpf.h>
38353837#include < sys/ resource.h>
38363838#include < pthread.h>
@@ -4109,6 +4111,7 @@ typedef struct PerfAttachment {
41094111 int perf_fd;
41104112 int link_id;
41114113 int prog_fd;
4114+ uint64_t generation;
41124115} PerfAttachment ;
41134116
41144117 struct attachment_entry {
@@ -4120,19 +4123,137 @@ typedef struct PerfAttachment {
41204123 int ifindex; // For XDP programs (0 for kprobe/ tracepoint)
41214124 int perf_fd; // For perf_event programs (- 1 otherwise)
41224125 int detaching; // Non - zero while teardown is in progress
4126+ uint64_t generation; // PerfAttachment stale- handle token
41234127 enum bpf_prog_type type ;
41244128 struct attachment_entry * next;
41254129 };
41264130
4131+ struct perf_attachment_state {
4132+ _Atomic uint64_t generation;
4133+ _Atomic int perf_fd;
4134+ _Atomic unsigned int readers;
4135+ };
4136+
41274137 static struct attachment_entry * attached_programs = NULL ;
4138+ static _Atomic(struct perf_attachment_state * ) perf_attachment_states = NULL ;
4139+ static _Atomic size_t perf_attachment_state_capacity = 0 ;
41284140 static pthread_mutex_t attachment_mutex = PTHREAD_MUTEX_INITIALIZER ;
41294141 static int next_attachment_id = 1 ;
4142+ static uint64_t next_perf_attachment_generation = 1 ;
4143+
4144+ static int ensure_perf_attachment_state_capacity_locked(int perf_fd) {
4145+ if (perf_fd < 0 ) {
4146+ return - 1 ;
4147+ }
4148+
4149+ size_t capacity = atomic_load_explicit(& perf_attachment_state_capacity, memory_order_acquire);
4150+ if ((size_t)perf_fd < capacity) {
4151+ return 0 ;
4152+ }
4153+
4154+ if (capacity > 0 ) {
4155+ fprintf(stderr, " perf fd %d exceeds perf attachment state table capacity %zu\n " ,
4156+ perf_fd, capacity);
4157+ return - 1 ;
4158+ }
4159+
4160+ struct rlimit limit;
4161+ capacity = 1024 ;
4162+ if (getrlimit(RLIMIT_NOFILE , & limit) == 0 &&
4163+ limit.rlim_cur != RLIM_INFINITY &&
4164+ limit.rlim_cur > 0 ) {
4165+ capacity = (size_t)limit.rlim_cur;
4166+ } else {
4167+ long open_max = sysconf(_SC_OPEN_MAX);
4168+ if (open_max > 0 ) {
4169+ capacity = (size_t)open_max;
4170+ }
4171+ }
4172+ if ((size_t)perf_fd > = capacity) {
4173+ capacity = (size_t)perf_fd + 1 ;
4174+ }
4175+
4176+ struct perf_attachment_state * states =
4177+ malloc(capacity * sizeof(struct perf_attachment_state));
4178+ if (! states) {
4179+ fprintf(stderr, " Failed to allocate perf attachment state table\n " );
4180+ return - 1 ;
4181+ }
4182+
4183+ for (size_t i = 0 ; i < capacity; i++ ) {
4184+ atomic_init(& states[i].generation, 0 );
4185+ atomic_init(& states[i].perf_fd, - 1 );
4186+ atomic_init(& states[i].readers, 0 );
4187+ }
4188+
4189+ atomic_store_explicit(& perf_attachment_states, states, memory_order_release);
4190+ atomic_store_explicit(& perf_attachment_state_capacity, capacity, memory_order_release);
4191+ return 0 ;
4192+ }
4193+
4194+ static void invalidate_perf_attachment_state_locked(struct attachment_entry * entry) {
4195+ if (! entry ||
4196+ entry->type != BPF_PROG_TYPE_PERF_EVENT ||
4197+ entry->perf_fd < 0 ||
4198+ entry->generation == 0 ) {
4199+ return;
4200+ }
4201+
4202+ size_t capacity = atomic_load_explicit(& perf_attachment_state_capacity, memory_order_acquire);
4203+ struct perf_attachment_state * states =
4204+ atomic_load_explicit(& perf_attachment_states, memory_order_acquire);
4205+ if ((size_t)entry->perf_fd < capacity && states) {
4206+ struct perf_attachment_state * state = & states[entry->perf_fd];
4207+ atomic_store_explicit(& state->perf_fd, - 1 , memory_order_release);
4208+ atomic_store_explicit(& state->generation, 0 , memory_order_release);
4209+ while (atomic_load_explicit(& state->readers, memory_order_acquire) != 0 ) {
4210+ sched_yield() ;
4211+ }
4212+ }
4213+ entry->generation = 0 ;
4214+ }
4215+
4216+ static struct perf_attachment_state * perf_attachment_begin_read(PerfAttachment attachment) {
4217+ if (attachment.perf_fd < 0 || attachment.link_id < = 0 || attachment.generation == 0 ) {
4218+ return NULL ;
4219+ }
4220+
4221+ size_t capacity = atomic_load_explicit(& perf_attachment_state_capacity, memory_order_acquire);
4222+ struct perf_attachment_state * states =
4223+ atomic_load_explicit(& perf_attachment_states, memory_order_acquire);
4224+ if (! states || (size_t)attachment.perf_fd > = capacity) {
4225+ return NULL ;
4226+ }
4227+
4228+ struct perf_attachment_state * state = & states[attachment.perf_fd];
4229+ uint64_t generation =
4230+ atomic_load_explicit(& state->generation, memory_order_acquire);
4231+ int perf_fd =
4232+ atomic_load_explicit(& state->perf_fd, memory_order_acquire);
4233+ if (generation != attachment.generation || perf_fd != attachment.perf_fd) {
4234+ return NULL ;
4235+ }
4236+
4237+ atomic_fetch_add_explicit(& state->readers, 1 , memory_order_acquire);
4238+ generation = atomic_load_explicit(& state->generation, memory_order_acquire);
4239+ perf_fd = atomic_load_explicit(& state->perf_fd, memory_order_acquire);
4240+ if (generation != attachment.generation || perf_fd != attachment.perf_fd) {
4241+ atomic_fetch_sub_explicit(& state->readers, 1 , memory_order_release);
4242+ return NULL ;
4243+ }
4244+ return state;
4245+ }
4246+
4247+ static void perf_attachment_end_read(struct perf_attachment_state * state) {
4248+ atomic_fetch_sub_explicit(& state->readers, 1 , memory_order_release);
4249+ }
41304250
41314251 // Helper function to add attachment entry.
41324252 // Duplicate check is performed atomically under the same lock as insertion.
41334253 static int add_attachment(int prog_fd, const char * target, uint32_t flags,
41344254 struct bpf_link * link, int ifindex, int perf_fd,
4135- enum bpf_prog_type type ) {
4255+ enum bpf_prog_type type , int * attachment_id_out,
4256+ uint64_t * generation_out) {
41364257 struct attachment_entry * entry = malloc(sizeof(struct attachment_entry));
41374258 if (! entry) {
41384259 fprintf(stderr, " Failed to allocate memory for attachment entry\n " );
@@ -4150,6 +4271,7 @@ typedef struct PerfAttachment {
41504271 entry->type = type ;
41514272
41524273 entry->detaching = 0 ;
4274+ entry->generation = 0 ;
41534275 pthread_mutex_lock(& attachment_mutex);
41544276 /* Reject duplicate insertions atomically.
41554277 * Skip entries that are currently being torn down (detaching != 0 ) so that
@@ -4167,8 +4289,29 @@ typedef struct PerfAttachment {
41674289 existing = existing->next;
41684290 }
41694291 entry->attachment_id = next_attachment_id++ ;
4292+ if (type == BPF_PROG_TYPE_PERF_EVENT && perf_fd > = 0 ) {
4293+ if (ensure_perf_attachment_state_capacity_locked(perf_fd) != 0 ) {
4294+ pthread_mutex_unlock(& attachment_mutex);
4295+ free(entry);
4296+ return - 1 ;
4297+ }
4298+ entry->generation = next_perf_attachment_generation++ ;
4299+ if (next_perf_attachment_generation == 0 ) {
4300+ next_perf_attachment_generation = 1 ;
4301+ }
4302+ struct perf_attachment_state * states =
4303+ atomic_load_explicit(& perf_attachment_states, memory_order_acquire);
4304+ atomic_store_explicit(& states[perf_fd].perf_fd, perf_fd, memory_order_release);
4305+ atomic_store_explicit(& states[perf_fd].generation, entry->generation, memory_order_release);
4306+ }
41704307 entry->next = attached_programs;
41714308 attached_programs = entry;
4309+ if (attachment_id_out) {
4310+ * attachment_id_out = entry->attachment_id;
4311+ }
4312+ if (generation_out) {
4313+ * generation_out = entry->generation;
4314+ }
41724315 pthread_mutex_unlock(& attachment_mutex);
41734316
41744317 return 0 ;
@@ -4232,7 +4375,7 @@ typedef struct PerfAttachment {
42324375 }
42334376
42344377 // Store XDP attachment (no bpf_link for XDP )
4235- if (add_attachment(prog_fd, target, flags, NULL , ifindex, - 1 , BPF_PROG_TYPE_XDP ) != 0 ) {
4378+ if (add_attachment(prog_fd, target, flags, NULL , ifindex, - 1 , BPF_PROG_TYPE_XDP , NULL , NULL ) != 0 ) {
42364379 // If storage fails, detach and return error
42374380 bpf_xdp_detach (ifindex , flags , NULL);
42384381 return -1 ;
@@ -4262,7 +4405,7 @@ typedef struct PerfAttachment {
42624405 printf(" Kprobe attached to function: %s\n " , target);
42634406
42644407 // Store probe attachment for later cleanup
4265- if (add_attachment(prog_fd, target, flags, link, 0 , - 1 , BPF_PROG_TYPE_KPROBE ) != 0 ) {
4408+ if (add_attachment(prog_fd, target, flags, link, 0 , - 1 , BPF_PROG_TYPE_KPROBE , NULL , NULL ) != 0 ) {
42664409 // If storage fails, destroy link and return error
42674410 bpf_link__destroy (link );
42684411 return -1 ;
@@ -4291,7 +4434,7 @@ typedef struct PerfAttachment {
42914434 printf(" Fentry/fexit program attached to function: %s\n " , target);
42924435
42934436 // Store tracing attachment for later cleanup
4294- if (add_attachment(prog_fd, target, flags, link, 0 , - 1 , BPF_PROG_TYPE_TRACING ) != 0 ) {
4437+ if (add_attachment(prog_fd, target, flags, link, 0 , - 1 , BPF_PROG_TYPE_TRACING , NULL , NULL ) != 0 ) {
42954438 // If storage fails, destroy link and return error
42964439 bpf_link__destroy (link );
42974440 return -1 ;
@@ -4335,7 +4478,7 @@ typedef struct PerfAttachment {
43354478 }
43364479
43374480 // Store tracepoint attachment for later cleanup
4338- if (add_attachment(prog_fd, target, flags, link, 0 , - 1 , BPF_PROG_TYPE_TRACEPOINT ) != 0 ) {
4481+ if (add_attachment(prog_fd, target, flags, link, 0 , - 1 , BPF_PROG_TYPE_TRACEPOINT , NULL , NULL ) != 0 ) {
43394482 // If storage fails, destroy link and return error
43404483 bpf_link__destroy (link );
43414484 return -1 ;
@@ -4372,7 +4515,7 @@ typedef struct PerfAttachment {
43724515 }
43734516
43744517 // Store TC attachment for later cleanup (flags no longer needed for direction)
4375- if (add_attachment(prog_fd, target, 0 , link, ifindex, - 1 , BPF_PROG_TYPE_SCHED_CLS ) != 0 ) {
4518+ if (add_attachment(prog_fd, target, 0 , link, ifindex, - 1 , BPF_PROG_TYPE_SCHED_CLS , NULL , NULL ) != 0 ) {
43764519 // If storage fails, destroy link and return error
43774520 bpf_link__destroy (link );
43784521 return -1 ;
@@ -4480,6 +4623,7 @@ void detach_bpf_program_by_fd(int prog_fd) {
44804623 while (entry) {
44814624 if (entry->prog_fd == prog_fd && ! entry->detaching) {
44824625 entry->detaching = 1 ;
4626+ invalidate_perf_attachment_state_locked(entry);
44834627 break;
44844628 }
44854629 entry = entry->next;
@@ -4517,6 +4661,7 @@ void ks_detach_perf_attachment(PerfAttachment attachment) {
45174661 struct attachment_entry *entry = find_attachment_by_id_locked(attachment.link_id);
45184662 if (entry && !entry->detaching) {
45194663 entry->detaching = 1;
4664+ invalidate_perf_attachment_state_locked(entry);
45204665 } else {
45214666 entry = NULL;
45224667 }
@@ -4753,36 +4898,28 @@ PerfAttachment ks_attach_perf_event(int prog_fd, ks_perf_options opts, int flags
47534898 (unsigned long long)opts.perf_config,
47544899 (unsigned long long)opts.period);
47554900
4756- if (add_attachment(prog_fd, perf_target, (uint32_t)flags, link, 0, perf_fd, BPF_PROG_TYPE_PERF_EVENT) != 0) {
4901+ int attachment_id = -1;
4902+ uint64_t generation = 0;
4903+ if (add_attachment(prog_fd, perf_target, (uint32_t)flags, link, 0, perf_fd,
4904+ BPF_PROG_TYPE_PERF_EVENT, &attachment_id, &generation) != 0) {
47574905 ioctl(perf_fd, PERF_EVENT_IOC_DISABLE, 0);
47584906 bpf_link__destroy(link);
47594907 close(perf_fd);
47604908 return attachment;
47614909 }
47624910
4763- pthread_mutex_lock(&attachment_mutex);
4764- struct attachment_entry *entry = attached_programs;
4765- while (entry) {
4766- if (entry->prog_fd == prog_fd &&
4767- entry->perf_fd == perf_fd &&
4768- entry->type == BPF_PROG_TYPE_PERF_EVENT &&
4769- !entry->detaching) {
4770- attachment.perf_fd = perf_fd;
4771- attachment.link_id = entry->attachment_id;
4772- break;
4773- }
4774- entry = entry->next;
4775- }
4776- pthread_mutex_unlock(&attachment_mutex);
4777-
4778- if (attachment.link_id <= 0) {
4911+ if (attachment_id <= 0 || generation == 0) {
47794912 fprintf(stderr, "Failed to record perf_event attachment for program fd %d\n", prog_fd);
47804913 ioctl(perf_fd, PERF_EVENT_IOC_DISABLE, 0);
47814914 bpf_link__destroy(link);
47824915 close(perf_fd);
47834916 return attachment;
47844917 }
47854918
4919+ attachment.perf_fd = perf_fd;
4920+ attachment.link_id = attachment_id;
4921+ attachment.generation = generation;
4922+
47864923 printf("Perf event program attached: id=%d prog_fd=%d perf_fd=%d target=%s\n",
47874924 attachment.link_id, attachment.prog_fd, attachment.perf_fd, perf_target);
47884925 return attachment;
@@ -4815,28 +4952,13 @@ int64_t ks_read_perf_count(int perf_fd) {
48154952
48164953/* Read the counter for a first-class perf attachment value. */
48174954int64_t ks_perf_attachment_read(PerfAttachment attachment) {
4818- pthread_mutex_lock(&attachment_mutex);
4819- int found = 0;
4820- int dup_fd = -1;
4821- struct attachment_entry *cur = find_attachment_by_id_locked(attachment.link_id);
4822- if (cur &&
4823- !cur->detaching &&
4824- cur->perf_fd >= 0 &&
4825- cur->type == BPF_PROG_TYPE_PERF_EVENT) {
4826- found = 1;
4827- dup_fd = dup(cur->perf_fd);
4828- }
4829- pthread_mutex_unlock(&attachment_mutex);
4830- if (!found) {
4831- fprintf(stderr, "ks_perf_attachment_read: no active perf attachment for link id %d\n", attachment.link_id);
4832- return -1;
4833- }
4834- if (dup_fd < 0) {
4835- fprintf(stderr, "ks_perf_attachment_read: dup(perf_fd) failed for link id %d: %s\n", attachment.link_id, strerror(errno));
4955+ struct perf_attachment_state *state = perf_attachment_begin_read(attachment);
4956+ if (!state) {
4957+ fprintf(stderr, "ks_perf_attachment_read: invalid or stale perf attachment\n");
48364958 return -1;
48374959 }
4838- int64_t result = ks_read_perf_count(dup_fd );
4839- close(dup_fd );
4960+ int64_t result = ks_read_perf_count(attachment.perf_fd );
4961+ perf_attachment_end_read(state );
48404962 return result;
48414963}
48424964|}
0 commit comments