jcs's openbsd hax
openbsd
at jcs 858 lines 18 kB view raw
1/* $OpenBSD: kcov.c,v 1.51 2025/02/02 21:05:12 gnezdo Exp $ */ 2 3/* 4 * Copyright (c) 2018 Anton Lindqvist <anton@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <sys/param.h> 20#include <sys/systm.h> 21#include <sys/proc.h> 22#include <sys/kcov.h> 23#include <sys/malloc.h> 24#include <sys/mutex.h> 25#include <sys/pool.h> 26#include <sys/stdint.h> 27#include <sys/queue.h> 28 29/* kcov_vnode() */ 30#include <sys/conf.h> 31#include <sys/vnode.h> 32#include <sys/specdev.h> 33 34#include <uvm/uvm_extern.h> 35 36#define KCOV_BUF_MEMB_SIZE sizeof(uintptr_t) 37#define KCOV_BUF_MAX_NMEMB (512 << 10) 38 39#define KCOV_CMP_CONST 0x1 40#define KCOV_CMP_SIZE(x) ((x) << 1) 41 42#define KCOV_STATE_NONE 0 43#define KCOV_STATE_READY 1 44#define KCOV_STATE_TRACE 2 45#define KCOV_STATE_DYING 3 46 47#define KCOV_STRIDE_TRACE_PC 1 48#define KCOV_STRIDE_TRACE_CMP 4 49 50/* 51 * Coverage structure. 52 * 53 * Locking: 54 * I immutable after creation 55 * M kcov_mtx 56 * a atomic operations 57 */ 58struct kcov_dev { 59 int kd_state; /* [M] */ 60 int kd_mode; /* [M] */ 61 int kd_unit; /* [I] D_CLONE unique device minor */ 62 int kd_intr; /* [M] currently used in interrupt */ 63 uintptr_t *kd_buf; /* [a] traced coverage */ 64 size_t kd_nmemb; /* [I] */ 65 size_t kd_size; /* [I] */ 66 67 struct kcov_remote *kd_kr; /* [M] */ 68 69 TAILQ_ENTRY(kcov_dev) kd_entry; /* [M] */ 70}; 71 72/* 73 * Remote coverage structure. 74 * 75 * Locking: 76 * I immutable after creation 77 * M kcov_mtx 78 */ 79struct kcov_remote { 80 struct kcov_dev *kr_kd; /* [M] */ 81 void *kr_id; /* [I] */ 82 int kr_subsystem; /* [I] */ 83 int kr_nsections; /* [M] # threads in remote section */ 84 int kr_state; /* [M] */ 85 86 TAILQ_ENTRY(kcov_remote) kr_entry; /* [M] */ 87}; 88 89/* 90 * Per CPU coverage structure used to track coverage when executing in a remote 91 * interrupt context. 92 * 93 * Locking: 94 * I immutable after creation 95 * M kcov_mtx 96 */ 97struct kcov_cpu { 98 struct kcov_dev kc_kd; 99 struct kcov_dev *kc_kd_save; /* [M] previous kcov_dev */ 100 int kc_cpuid; /* [I] cpu number */ 101 102 TAILQ_ENTRY(kcov_cpu) kc_entry; /* [I] */ 103}; 104 105void kcovattach(int); 106 107int kd_init(struct kcov_dev *, unsigned long); 108void kd_free(struct kcov_dev *); 109struct kcov_dev *kd_lookup(int); 110void kd_copy(struct kcov_dev *, struct kcov_dev *); 111 112struct kcov_remote *kcov_remote_register_locked(int, void *); 113int kcov_remote_attach(struct kcov_dev *, struct kio_remote_attach *); 114void kcov_remote_detach(struct kcov_dev *, struct kcov_remote *); 115void kr_free(struct kcov_remote *); 116void kr_barrier(struct kcov_remote *); 117struct kcov_remote *kr_lookup(int, void *); 118 119static struct kcov_dev *kd_curproc(int); 120static struct kcov_cpu *kd_curcpu(void); 121static uint64_t kd_claim(struct kcov_dev *, int, int); 122 123TAILQ_HEAD(, kcov_dev) kd_list = TAILQ_HEAD_INITIALIZER(kd_list); 124TAILQ_HEAD(, kcov_remote) kr_list = TAILQ_HEAD_INITIALIZER(kr_list); 125TAILQ_HEAD(, kcov_cpu) kc_list = TAILQ_HEAD_INITIALIZER(kc_list); 126 127int kcov_cold = 1; 128int kr_cold = 1; 129struct mutex kcov_mtx = MUTEX_INITIALIZER(IPL_MPFLOOR); 130struct pool kr_pool; 131 132static inline int 133inintr(struct cpu_info *ci) 134{ 135 return (ci->ci_idepth > 0); 136} 137 138/* 139 * Compiling the kernel with the `-fsanitize-coverage=trace-pc' option will 140 * cause the following function to be called upon function entry and before 141 * each block of instructions that maps to a single line in the original source 142 * code. 143 * 144 * If kcov is enabled for the current thread, the kernel program counter will 145 * be stored in its corresponding coverage buffer. 146 */ 147void 148__sanitizer_cov_trace_pc(void) 149{ 150 struct kcov_dev *kd; 151 uint64_t idx; 152 153 kd = kd_curproc(KCOV_MODE_TRACE_PC); 154 if (kd == NULL) 155 return; 156 157 if ((idx = kd_claim(kd, KCOV_STRIDE_TRACE_PC, 1))) 158 kd->kd_buf[idx] = (uintptr_t)__builtin_return_address(0); 159} 160 161/* 162 * Compiling the kernel with the `-fsanitize-coverage=trace-cmp' option will 163 * cause the following function to be called upon integer comparisons and switch 164 * statements. 165 * 166 * If kcov is enabled for the current thread, the comparison will be stored in 167 * its corresponding coverage buffer. 168 */ 169void 170trace_cmp(struct kcov_dev *kd, uint64_t type, uint64_t arg1, uint64_t arg2, 171 uintptr_t pc) 172{ 173 uint64_t idx; 174 175 if ((idx = kd_claim(kd, KCOV_STRIDE_TRACE_CMP, 1))) { 176 kd->kd_buf[idx] = type; 177 kd->kd_buf[idx + 1] = arg1; 178 kd->kd_buf[idx + 2] = arg2; 179 kd->kd_buf[idx + 3] = pc; 180 } 181} 182 183#define TRACE_CMP(type, arg1, arg2) do { \ 184 struct kcov_dev *kd; \ 185 if ((kd = kd_curproc(KCOV_MODE_TRACE_CMP)) == NULL) \ 186 return; \ 187 trace_cmp(kd, (type), (arg1), (arg2), \ 188 (uintptr_t)__builtin_return_address(0)); \ 189} while (0) 190 191void 192__sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2) 193{ 194 TRACE_CMP(KCOV_CMP_SIZE(0), arg1, arg2); 195} 196 197void 198__sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2) 199{ 200 TRACE_CMP(KCOV_CMP_SIZE(1), arg1, arg2); 201} 202 203void 204__sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2) 205{ 206 TRACE_CMP(KCOV_CMP_SIZE(2), arg1, arg2); 207} 208 209void 210__sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) 211{ 212 TRACE_CMP(KCOV_CMP_SIZE(3), arg1, arg2); 213} 214 215void 216__sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2) 217{ 218 TRACE_CMP(KCOV_CMP_SIZE(0) | KCOV_CMP_CONST, arg1, arg2); 219} 220 221void 222__sanitizer_cov_trace_const_cmp2(uint16_t arg1, uint16_t arg2) 223{ 224 TRACE_CMP(KCOV_CMP_SIZE(1) | KCOV_CMP_CONST, arg1, arg2); 225} 226 227void 228__sanitizer_cov_trace_const_cmp4(uint32_t arg1, uint32_t arg2) 229{ 230 TRACE_CMP(KCOV_CMP_SIZE(2) | KCOV_CMP_CONST, arg1, arg2); 231} 232 233void 234__sanitizer_cov_trace_const_cmp8(uint64_t arg1, uint64_t arg2) 235{ 236 TRACE_CMP(KCOV_CMP_SIZE(3) | KCOV_CMP_CONST, arg1, arg2); 237} 238 239void 240__sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) 241{ 242 struct kcov_dev *kd; 243 uint64_t i, nbits, ncases, type; 244 uintptr_t pc; 245 246 kd = kd_curproc(KCOV_MODE_TRACE_CMP); 247 if (kd == NULL) 248 return; 249 250 pc = (uintptr_t)__builtin_return_address(0); 251 ncases = cases[0]; 252 nbits = cases[1]; 253 254 switch (nbits) { 255 case 8: 256 type = KCOV_CMP_SIZE(0); 257 break; 258 case 16: 259 type = KCOV_CMP_SIZE(1); 260 break; 261 case 32: 262 type = KCOV_CMP_SIZE(2); 263 break; 264 case 64: 265 type = KCOV_CMP_SIZE(3); 266 break; 267 default: 268 return; 269 } 270 type |= KCOV_CMP_CONST; 271 272 for (i = 0; i < ncases; i++) 273 trace_cmp(kd, type, cases[i + 2], val, pc); 274} 275 276void 277kcovattach(int count) 278{ 279 struct kcov_cpu *kc; 280 int error, i; 281 282 pool_init(&kr_pool, sizeof(struct kcov_remote), 0, IPL_MPFLOOR, PR_WAITOK, 283 "kcovpl", NULL); 284 285 kc = mallocarray(ncpusfound, sizeof(*kc), M_DEVBUF, M_WAITOK | M_ZERO); 286 mtx_enter(&kcov_mtx); 287 for (i = 0; i < ncpusfound; i++) { 288 kc[i].kc_cpuid = i; 289 error = kd_init(&kc[i].kc_kd, KCOV_BUF_MAX_NMEMB); 290 KASSERT(error == 0); 291 TAILQ_INSERT_TAIL(&kc_list, &kc[i], kc_entry); 292 } 293 mtx_leave(&kcov_mtx); 294 295 kr_cold = 0; 296} 297 298int 299kcovopen(dev_t dev, int flag, int mode, struct proc *p) 300{ 301 struct kcov_dev *kd; 302 303 kd = malloc(sizeof(*kd), M_SUBPROC, M_WAITOK | M_ZERO); 304 kd->kd_unit = minor(dev); 305 mtx_enter(&kcov_mtx); 306 KASSERT(kd_lookup(kd->kd_unit) == NULL); 307 TAILQ_INSERT_TAIL(&kd_list, kd, kd_entry); 308 if (kcov_cold) 309 kcov_cold = 0; 310 mtx_leave(&kcov_mtx); 311 return (0); 312} 313 314int 315kcovclose(dev_t dev, int flag, int mode, struct proc *p) 316{ 317 struct kcov_dev *kd; 318 319 mtx_enter(&kcov_mtx); 320 321 kd = kd_lookup(minor(dev)); 322 if (kd == NULL) { 323 mtx_leave(&kcov_mtx); 324 return (ENXIO); 325 } 326 327 TAILQ_REMOVE(&kd_list, kd, kd_entry); 328 if (kd->kd_state == KCOV_STATE_TRACE && kd->kd_kr == NULL) { 329 /* 330 * Another thread is currently using the kcov descriptor, 331 * postpone freeing to kcov_exit(). 332 */ 333 kd->kd_state = KCOV_STATE_DYING; 334 kd->kd_mode = KCOV_MODE_NONE; 335 } else { 336 kd_free(kd); 337 } 338 339 mtx_leave(&kcov_mtx); 340 return (0); 341} 342 343int 344kcovioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 345{ 346 struct kcov_dev *kd; 347 int mode; 348 int error = 0; 349 350 mtx_enter(&kcov_mtx); 351 352 kd = kd_lookup(minor(dev)); 353 if (kd == NULL) { 354 mtx_leave(&kcov_mtx); 355 return (ENXIO); 356 } 357 358 switch (cmd) { 359 case KIOSETBUFSIZE: 360 error = kd_init(kd, *((unsigned long *)data)); 361 break; 362 case KIOENABLE: 363 /* Only one kcov descriptor can be enabled per thread. */ 364 if (p->p_kd != NULL) { 365 error = EBUSY; 366 break; 367 } 368 if (kd->kd_state != KCOV_STATE_READY) { 369 error = ENXIO; 370 break; 371 } 372 mode = *((int *)data); 373 if (mode != KCOV_MODE_TRACE_PC && mode != KCOV_MODE_TRACE_CMP) { 374 error = EINVAL; 375 break; 376 } 377 kd->kd_state = KCOV_STATE_TRACE; 378 kd->kd_mode = mode; 379 /* Remote coverage is mutually exclusive. */ 380 if (kd->kd_kr == NULL) 381 p->p_kd = kd; 382 break; 383 case KIODISABLE: 384 /* Only the enabled thread may disable itself. */ 385 if ((p->p_kd != kd && kd->kd_kr == NULL)) { 386 error = EPERM; 387 break; 388 } 389 if (kd->kd_state != KCOV_STATE_TRACE) { 390 error = ENXIO; 391 break; 392 } 393 kd->kd_state = KCOV_STATE_READY; 394 kd->kd_mode = KCOV_MODE_NONE; 395 if (kd->kd_kr != NULL) 396 kr_barrier(kd->kd_kr); 397 p->p_kd = NULL; 398 break; 399 case KIOREMOTEATTACH: 400 error = kcov_remote_attach(kd, 401 (struct kio_remote_attach *)data); 402 break; 403 default: 404 error = ENOTTY; 405 } 406 mtx_leave(&kcov_mtx); 407 408 return (error); 409} 410 411paddr_t 412kcovmmap(dev_t dev, off_t offset, int prot) 413{ 414 struct kcov_dev *kd; 415 paddr_t pa = -1; 416 vaddr_t va; 417 418 mtx_enter(&kcov_mtx); 419 420 kd = kd_lookup(minor(dev)); 421 if (kd == NULL) 422 goto out; 423 424 if (offset < 0 || offset >= kd->kd_nmemb * KCOV_BUF_MEMB_SIZE) 425 goto out; 426 427 va = (vaddr_t)kd->kd_buf + offset; 428 if (pmap_extract(pmap_kernel(), va, &pa) == FALSE) 429 pa = -1; 430 431out: 432 mtx_leave(&kcov_mtx); 433 return (pa); 434} 435 436void 437kcov_exit(struct proc *p) 438{ 439 struct kcov_dev *kd; 440 441 mtx_enter(&kcov_mtx); 442 443 kd = p->p_kd; 444 if (kd == NULL) { 445 mtx_leave(&kcov_mtx); 446 return; 447 } 448 449 if (kd->kd_state == KCOV_STATE_DYING) { 450 p->p_kd = NULL; 451 kd_free(kd); 452 } else { 453 kd->kd_state = KCOV_STATE_READY; 454 kd->kd_mode = KCOV_MODE_NONE; 455 if (kd->kd_kr != NULL) 456 kr_barrier(kd->kd_kr); 457 p->p_kd = NULL; 458 } 459 460 mtx_leave(&kcov_mtx); 461} 462 463/* 464 * Returns non-zero if the given vnode refers to a kcov device. 465 */ 466int 467kcov_vnode(struct vnode *vp) 468{ 469 return (vp->v_type == VCHR && 470 cdevsw[major(vp->v_rdev)].d_open == kcovopen); 471} 472 473struct kcov_dev * 474kd_lookup(int unit) 475{ 476 struct kcov_dev *kd; 477 478 MUTEX_ASSERT_LOCKED(&kcov_mtx); 479 480 TAILQ_FOREACH(kd, &kd_list, kd_entry) { 481 if (kd->kd_unit == unit) 482 return (kd); 483 } 484 return (NULL); 485} 486 487void 488kd_copy(struct kcov_dev *dst, struct kcov_dev *src) 489{ 490 uint64_t idx, nmemb; 491 int stride; 492 493 MUTEX_ASSERT_LOCKED(&kcov_mtx); 494 KASSERT(dst->kd_mode == src->kd_mode); 495 496 nmemb = src->kd_buf[0]; 497 if (nmemb == 0) 498 return; 499 stride = src->kd_mode == KCOV_MODE_TRACE_CMP ? KCOV_STRIDE_TRACE_CMP : 500 KCOV_STRIDE_TRACE_PC; 501 idx = kd_claim(dst, stride, nmemb); 502 if (idx == 0) 503 return; 504 memcpy(&dst->kd_buf[idx], &src->kd_buf[1], 505 stride * nmemb * KCOV_BUF_MEMB_SIZE); 506} 507 508int 509kd_init(struct kcov_dev *kd, unsigned long nmemb) 510{ 511 void *buf; 512 size_t size; 513 int error; 514 515 KASSERT(kd->kd_buf == NULL); 516 517 if (kd->kd_state != KCOV_STATE_NONE) 518 return (EBUSY); 519 520 if (nmemb == 0 || nmemb > KCOV_BUF_MAX_NMEMB) 521 return (EINVAL); 522 523 size = roundup(nmemb * KCOV_BUF_MEMB_SIZE, PAGE_SIZE); 524 mtx_leave(&kcov_mtx); 525 buf = km_alloc(size, &kv_any, &kp_zero, &kd_waitok); 526 if (buf == NULL) { 527 error = ENOMEM; 528 goto err; 529 } 530 /* km_malloc() can sleep, ensure the race was won. */ 531 if (kd->kd_state != KCOV_STATE_NONE) { 532 error = EBUSY; 533 goto err; 534 } 535 mtx_enter(&kcov_mtx); 536 kd->kd_buf = buf; 537 /* The first element is reserved to hold the number of used elements. */ 538 kd->kd_nmemb = nmemb - 1; 539 kd->kd_size = size; 540 kd->kd_state = KCOV_STATE_READY; 541 return (0); 542 543err: 544 if (buf != NULL) 545 km_free(buf, size, &kv_any, &kp_zero); 546 mtx_enter(&kcov_mtx); 547 return (error); 548} 549 550void 551kd_free(struct kcov_dev *kd) 552{ 553 struct kcov_remote *kr; 554 555 MUTEX_ASSERT_LOCKED(&kcov_mtx); 556 557 kr = kd->kd_kr; 558 if (kr != NULL) 559 kcov_remote_detach(kd, kr); 560 561 if (kd->kd_buf != NULL) { 562 mtx_leave(&kcov_mtx); 563 km_free(kd->kd_buf, kd->kd_size, &kv_any, &kp_zero); 564 mtx_enter(&kcov_mtx); 565 } 566 free(kd, M_SUBPROC, sizeof(*kd)); 567} 568 569static struct kcov_dev * 570kd_curproc(int mode) 571{ 572 struct cpu_info *ci; 573 struct kcov_dev *kd; 574 575 /* 576 * Do not trace before kcovopen() has been called at least once. 577 * At this point, all secondary CPUs have booted and accessing curcpu() 578 * is safe. 579 */ 580 if (__predict_false(kcov_cold)) 581 return (NULL); 582 583 ci = curcpu(); 584 kd = ci->ci_curproc->p_kd; 585 if (__predict_true(kd == NULL) || kd->kd_mode != mode) 586 return (NULL); 587 588 /* 589 * Do not trace if the kernel has panicked. This could happen if curproc 590 * had kcov enabled while panicking. 591 */ 592 if (__predict_false(panicstr || db_active)) 593 return (NULL); 594 595 /* Do not trace in interrupt context unless this is a remote section. */ 596 if (inintr(ci) && kd->kd_intr == 0) 597 return (NULL); 598 599 return (kd); 600 601} 602 603static struct kcov_cpu * 604kd_curcpu(void) 605{ 606 struct kcov_cpu *kc; 607 unsigned int cpuid = cpu_number(); 608 609 TAILQ_FOREACH(kc, &kc_list, kc_entry) { 610 if (kc->kc_cpuid == cpuid) 611 return (kc); 612 } 613 return (NULL); 614} 615 616/* 617 * Claim stride times nmemb number of elements in the coverage buffer. Returns 618 * the index of the first claimed element. If the claim cannot be fulfilled, 619 * zero is returned. 620 */ 621static uint64_t 622kd_claim(struct kcov_dev *kd, int stride, int nmemb) 623{ 624 uint64_t idx, was; 625 626 idx = kd->kd_buf[0]; 627 for (;;) { 628 if (stride * (idx + nmemb) > kd->kd_nmemb) 629 return (0); 630 631 was = atomic_cas_ulong(&kd->kd_buf[0], idx, idx + nmemb); 632 if (was == idx) 633 return (idx * stride + 1); 634 idx = was; 635 } 636} 637 638void 639kcov_remote_enter(int subsystem, void *id) 640{ 641 struct cpu_info *ci; 642 struct kcov_cpu *kc; 643 struct kcov_dev *kd; 644 struct kcov_remote *kr; 645 struct proc *p; 646 647 mtx_enter(&kcov_mtx); 648 kr = kr_lookup(subsystem, id); 649 if (kr == NULL || kr->kr_state != KCOV_STATE_READY) 650 goto out; 651 kd = kr->kr_kd; 652 if (kd == NULL || kd->kd_state != KCOV_STATE_TRACE) 653 goto out; 654 ci = curcpu(); 655 p = ci->ci_curproc; 656 if (inintr(ci)) { 657 /* 658 * XXX we only expect to be called from softclock interrupts at 659 * this point. 660 */ 661 kc = kd_curcpu(); 662 if (kc == NULL || kc->kc_kd.kd_intr == 1) 663 goto out; 664 kc->kc_kd.kd_state = KCOV_STATE_TRACE; 665 kc->kc_kd.kd_mode = kd->kd_mode; 666 kc->kc_kd.kd_intr = 1; 667 kc->kc_kd_save = p->p_kd; 668 kd = &kc->kc_kd; 669 /* Reset coverage buffer. */ 670 kd->kd_buf[0] = 0; 671 } else { 672 KASSERT(p->p_kd == NULL); 673 } 674 kr->kr_nsections++; 675 p->p_kd = kd; 676 677out: 678 mtx_leave(&kcov_mtx); 679} 680 681void 682kcov_remote_leave(int subsystem, void *id) 683{ 684 struct cpu_info *ci; 685 struct kcov_cpu *kc; 686 struct kcov_remote *kr; 687 struct proc *p; 688 689 mtx_enter(&kcov_mtx); 690 ci = curcpu(); 691 p = ci->ci_curproc; 692 if (p->p_kd == NULL) 693 goto out; 694 kr = kr_lookup(subsystem, id); 695 if (kr == NULL) 696 goto out; 697 if (inintr(ci)) { 698 kc = kd_curcpu(); 699 if (kc == NULL || kc->kc_kd.kd_intr == 0) 700 goto out; 701 702 /* 703 * Stop writing to the coverage buffer associated with this CPU 704 * before copying its contents. 705 */ 706 p->p_kd = kc->kc_kd_save; 707 kc->kc_kd_save = NULL; 708 709 kd_copy(kr->kr_kd, &kc->kc_kd); 710 kc->kc_kd.kd_state = KCOV_STATE_READY; 711 kc->kc_kd.kd_mode = KCOV_MODE_NONE; 712 kc->kc_kd.kd_intr = 0; 713 } else { 714 KASSERT(p->p_kd == kr->kr_kd); 715 p->p_kd = NULL; 716 } 717 if (--kr->kr_nsections == 0) 718 wakeup(kr); 719out: 720 mtx_leave(&kcov_mtx); 721} 722 723void 724kcov_remote_register(int subsystem, void *id) 725{ 726 mtx_enter(&kcov_mtx); 727 kcov_remote_register_locked(subsystem, id); 728 mtx_leave(&kcov_mtx); 729} 730 731void 732kcov_remote_unregister(int subsystem, void *id) 733{ 734 struct kcov_remote *kr; 735 736 mtx_enter(&kcov_mtx); 737 kr = kr_lookup(subsystem, id); 738 if (kr != NULL) 739 kr_free(kr); 740 mtx_leave(&kcov_mtx); 741} 742 743struct kcov_remote * 744kcov_remote_register_locked(int subsystem, void *id) 745{ 746 struct kcov_remote *kr, *tmp; 747 748 /* Do not allow registrations before the pool is initialized. */ 749 KASSERT(kr_cold == 0); 750 751 /* 752 * Temporarily release the mutex since the allocation could end up 753 * sleeping. 754 */ 755 mtx_leave(&kcov_mtx); 756 kr = pool_get(&kr_pool, PR_WAITOK | PR_ZERO); 757 kr->kr_subsystem = subsystem; 758 kr->kr_id = id; 759 kr->kr_state = KCOV_STATE_NONE; 760 mtx_enter(&kcov_mtx); 761 762 for (;;) { 763 tmp = kr_lookup(subsystem, id); 764 if (tmp == NULL) 765 break; 766 if (tmp->kr_state != KCOV_STATE_DYING) { 767 pool_put(&kr_pool, kr); 768 return (NULL); 769 } 770 /* 771 * The remote could already be deregistered while another 772 * thread is currently inside a kcov remote section. 773 */ 774 msleep_nsec(tmp, &kcov_mtx, PWAIT, "kcov", INFSLP); 775 } 776 TAILQ_INSERT_TAIL(&kr_list, kr, kr_entry); 777 return (kr); 778} 779 780int 781kcov_remote_attach(struct kcov_dev *kd, struct kio_remote_attach *arg) 782{ 783 struct kcov_remote *kr = NULL; 784 785 MUTEX_ASSERT_LOCKED(&kcov_mtx); 786 787 if (kd->kd_state != KCOV_STATE_READY) 788 return (ENXIO); 789 790 if (arg->subsystem == KCOV_REMOTE_COMMON) { 791 kr = kcov_remote_register_locked(KCOV_REMOTE_COMMON, 792 curproc->p_p); 793 if (kr == NULL) 794 return (EBUSY); 795 } else { 796 return (EINVAL); 797 } 798 799 kr->kr_state = KCOV_STATE_READY; 800 kr->kr_kd = kd; 801 kd->kd_kr = kr; 802 return (0); 803} 804 805void 806kcov_remote_detach(struct kcov_dev *kd, struct kcov_remote *kr) 807{ 808 MUTEX_ASSERT_LOCKED(&kcov_mtx); 809 810 KASSERT(kd == kr->kr_kd); 811 if (kr->kr_subsystem == KCOV_REMOTE_COMMON) { 812 kr_free(kr); 813 } else { 814 kr->kr_state = KCOV_STATE_NONE; 815 kr_barrier(kr); 816 kd->kd_kr = NULL; 817 kr->kr_kd = NULL; 818 } 819} 820 821void 822kr_free(struct kcov_remote *kr) 823{ 824 MUTEX_ASSERT_LOCKED(&kcov_mtx); 825 826 kr->kr_state = KCOV_STATE_DYING; 827 kr_barrier(kr); 828 if (kr->kr_kd != NULL) 829 kr->kr_kd->kd_kr = NULL; 830 kr->kr_kd = NULL; 831 TAILQ_REMOVE(&kr_list, kr, kr_entry); 832 /* Notify thread(s) waiting in kcov_remote_register(). */ 833 wakeup(kr); 834 pool_put(&kr_pool, kr); 835} 836 837void 838kr_barrier(struct kcov_remote *kr) 839{ 840 MUTEX_ASSERT_LOCKED(&kcov_mtx); 841 842 while (kr->kr_nsections > 0) 843 msleep_nsec(kr, &kcov_mtx, PWAIT, "kcovbar", INFSLP); 844} 845 846struct kcov_remote * 847kr_lookup(int subsystem, void *id) 848{ 849 struct kcov_remote *kr; 850 851 MUTEX_ASSERT_LOCKED(&kcov_mtx); 852 853 TAILQ_FOREACH(kr, &kr_list, kr_entry) { 854 if (kr->kr_subsystem == subsystem && kr->kr_id == id) 855 return (kr); 856 } 857 return (NULL); 858}