@@ -4053,7 +4053,8 @@ struct attachment_entry {
40534053 uint32_t flags;
40544054 struct bpf_link * link; // For kprobe/ tracepoint programs (NULL for XDP )
40554055 int ifindex; // For XDP programs (0 for kprobe/ tracepoint)
4056- int perf_fd; // For perf_event programs (- 1 otherwise)
4056+ int perf_fd; // For perf_event programs (- 1 otherwise)
4057+ int detaching; // Non - zero while teardown is in progress
40574058 enum bpf_prog_type type ;
40584059 struct attachment_entry * next;
40594060};
@@ -4081,11 +4082,14 @@ static int add_attachment(int prog_fd, const char *target, uint32_t flags,
40814082 entry->perf_fd = perf_fd;
40824083 entry->type = type ;
40834084
4085+ entry->detaching = 0 ;
40844086 pthread_mutex_lock(& attachment_mutex);
4085- /* Reject duplicate insertions atomically */
4087+ /* Reject duplicate insertions atomically.
4088+ * Skip entries that are currently being torn down (detaching != 0 ) so that
4089+ * a new attach can succeed while the old detach is still running. */
40864090 struct attachment_entry * existing = attached_programs;
40874091 while (existing) {
4088- if (existing->prog_fd == prog_fd) {
4092+ if (existing->prog_fd == prog_fd && ! existing->detaching ) {
40894093 pthread_mutex_unlock(& attachment_mutex);
40904094 free(entry);
40914095 fprintf(stderr, " Program with fd %d is already attached. Use detach() first.\n " , prog_fd);
@@ -4328,18 +4332,16 @@ static struct bpf_program *find_prog_by_fd(int prog_fd) {
43284332 return;
43294333 }
43304334
4331- /* Atomically extract the entry from the list so concurrent detach / perf_read
4332- * cannot dereference a freed pointer . */
4335+ /* Phase 1 : mark the entry as detaching under the lock so concurrent
4336+ * perf_read skips it and a concurrent add_attachment can proceed . */
43334337 pthread_mutex_lock (&attachment_mutex );
4334- struct attachment_entry * entry = NULL ;
4335- struct attachment_entry ** cur = & attached_programs;
4336- while (* cur) {
4337- if ((* cur)->prog_fd == prog_fd) {
4338- entry = *cur;
4339- *cur = entry->next;
4338+ struct attachment_entry *entry = attached_programs;
4339+ while (entry) {
4340+ if (entry->prog_fd == prog_fd && ! entry->detaching) {
4341+ entry->detaching = 1 ;
43404342 break;
43414343 }
4342- cur = & (* cur) ->next;
4344+ entry = entry ->next;
43434345 }
43444346 pthread_mutex_unlock(& attachment_mutex);
43454347
@@ -4400,6 +4402,17 @@ static struct bpf_program *find_prog_by_fd(int prog_fd) {
44004402 break;
44014403 }
44024404
4405+ /* Phase 2 : teardown is complete; remove entry from tracking list and free. */
4406+ pthread_mutex_lock(& attachment_mutex);
4407+ struct attachment_entry ** cur2 = & attached_programs;
4408+ while (* cur2) {
4409+ if (* cur2 == entry) {
4410+ *cur2 = entry->next;
4411+ break;
4412+ }
4413+ cur2 = &(* cur2)->next;
4414+ }
4415+ pthread_mutex_unlock(&attachment_mutex);
44034416 free(entry);
44044417}|} detach_perf_case
44054418 else "" in
@@ -4685,15 +4698,18 @@ int64_t ks_read_perf_count(int perf_fd) {
46854698/* Read the counter for the perf_event program bound to prog_fd.
46864699 * Looks up the perf_fd from the attachment table and calls ks_read_perf_count. */
46874700int64_t ks_perf_read(int prog_fd) {
4688- /* Read perf_fd under the lock so the entry cannot be freed concurrently */
4701+ /* Dup perf_fd under the lock so a concurrent detach closing the original fd
4702+ * cannot affect the fd we read from. Skip entries marked detaching. */
46894703 pthread_mutex_lock(&attachment_mutex);
46904704 int found = 0;
4691- int perf_fd = -1;
4705+ int dup_fd = -1;
46924706 struct attachment_entry *cur = attached_programs;
46934707 while (cur) {
46944708 if (cur->prog_fd == prog_fd) {
4695- found = 1;
4696- perf_fd = cur->perf_fd;
4709+ if (!cur->detaching && cur->perf_fd >= 0) {
4710+ found = 1;
4711+ dup_fd = dup(cur->perf_fd);
4712+ }
46974713 break;
46984714 }
46994715 cur = cur->next;
@@ -4703,11 +4719,13 @@ int64_t ks_perf_read(int prog_fd) {
47034719 fprintf(stderr, "ks_perf_read: no active attachment for program fd %d\n", prog_fd);
47044720 return -1;
47054721 }
4706- if (perf_fd < 0) {
4722+ if (dup_fd < 0) {
47074723 fprintf(stderr, "ks_perf_read: program fd %d is not a perf_event program\n", prog_fd);
47084724 return -1;
47094725 }
4710- return ks_read_perf_count(perf_fd);
4726+ int64_t result = ks_read_perf_count(dup_fd);
4727+ close(dup_fd);
4728+ return result;
47114729}
47124730
47134731/* Print the current counter value for a named event to stdout.
0 commit comments