1 /* SPDX-License-Identifier: GPL-2.0 */ 2 /* 3 * BPF-side helpers for cids and cmasks. See kernel/sched/ext/cid.h for the 4 * authoritative layout and semantics. The BPF-side helpers use the cmask_* 5 * naming (no scx_ prefix); cmask is the SCX bitmap type so the prefix is 6 * redundant in BPF code. Every helper is inline except the binary operations, 7 * which are weak global functions defined here, so there is no .c counterpart. 8 * 9 * Included by scx/common.bpf.h; don't include directly. 10 * 11 * Copyright (c) 2026 Meta Platforms, Inc. and affiliates. 12 * Copyright (c) 2026 Tejun Heo <tj@kernel.org> 13 */ 14 #ifndef __SCX_CID_BPF_H 15 #define __SCX_CID_BPF_H 16 17 #include "bpf_arena_common.bpf.h" 18 19 /* libbpf's bpf_helpers.h defines it from 1.4 on */ 20 #ifndef __arg_arena 21 #define __arg_arena __attribute((btf_decl_tag("arg:arena"))) 22 #endif 23 24 #ifndef BIT_U64 25 #define BIT_U64(nr) (1ULL << (nr)) 26 #endif 27 #ifndef GENMASK_U64 28 #define GENMASK_U64(h, l) ((~0ULL << (l)) & (~0ULL >> (63 - (h)))) 29 #endif 30 31 /* 32 * Storage cap for bounded loops over bits[]. Sized to cover NR_CPUS=8192 with 33 * one extra word for head-misalignment. Increase if deployment targets larger 34 * NR_CPUS. 35 */ 36 #ifndef CMASK_MAX_WORDS 37 #define CMASK_MAX_WORDS 129 38 #endif 39 40 /* 41 * Mirrors SCX_CMASK_NR_WORDS in kernel/sched/ext/types.h. The u64 cast keeps 42 * the +63 from wrapping when @nr_cids is near U32_MAX, so cmask_reframe() 43 * bounds-checking the result against alloc_words catches the overflow instead 44 * of seeing a small value. 45 */ 46 #define CMASK_NR_WORDS(nr_cids) ((u32)(((u64)(nr_cids) + 63) / 64 + 1)) 47 48 static __always_inline bool __cmask_contains(u32 cid, const struct scx_cmask __arena *m) 49 { 50 return cid >= m->base && cid < m->base + m->nr_cids; 51 } 52 53 static __always_inline u64 __arena *__cmask_word(u32 cid, const struct scx_cmask __arena *m) 54 { 55 return (u64 __arena *)&m->bits[cid / 64 - m->base / 64]; 56 } 57 58 /** 59 * __cmask_init - Initialize @m with explicit storage capacity 60 * @m: cmask to initialize 61 * @base: first cid of the active range 62 * @nr_cids: number of cids in the active range 63 * @alloc_cids: storage capacity in cids, at least @nr_cids 64 * 65 * Use when storage is sized larger than the initial active range. All of 66 * bits[] is zeroed. 67 */ 68 static __always_inline void __cmask_init(struct scx_cmask __arena *m, u32 base, 69 u32 nr_cids, u32 alloc_cids) 70 { 71 u32 alloc_words, i; 72 73 if (unlikely(nr_cids > alloc_cids)) { 74 scx_bpf_error("__cmask_init: nr_cids=%u exceeds alloc_cids=%u", 75 nr_cids, alloc_cids); 76 return; 77 } 78 alloc_words = CMASK_NR_WORDS(alloc_cids); 79 80 m->base = base; 81 m->nr_cids = nr_cids; 82 m->alloc_words = alloc_words; 83 84 bpf_arena_for(i, 0, CMASK_MAX_WORDS) { 85 if (i >= alloc_words) 86 break; 87 m->bits[i] = 0; 88 } 89 } 90 91 /** 92 * cmask_init - Initialize @m on tight storage 93 * @m: cmask to initialize 94 * @base: first cid of the active range 95 * @nr_cids: number of cids in the active range 96 * 97 * All of bits[] is zeroed. 98 */ 99 static __always_inline void cmask_init(struct scx_cmask __arena *m, u32 base, u32 nr_cids) 100 { 101 __cmask_init(m, base, nr_cids, nr_cids); 102 } 103 104 /** 105 * cmask_reframe - Reshape @m's active range without resizing storage 106 * @m: cmask to reframe 107 * @base: new active range base 108 * @nr_cids: new active range length, must fit within @m->alloc_words 109 * 110 * Body bits within the new range become garbage - only the head and tail 111 * words are zeroed to keep the padding invariant. 112 */ 113 static __always_inline void cmask_reframe(struct scx_cmask __arena *m, u32 base, u32 nr_cids) 114 { 115 if (CMASK_NR_WORDS(nr_cids) > m->alloc_words) { 116 scx_bpf_error("cmask_reframe: nr_cids=%u exceeds alloc_words=%u", 117 nr_cids, m->alloc_words); 118 return; 119 } 120 if (nr_cids) { 121 u32 last_word = ((base & 63) + nr_cids - 1) / 64; 122 123 m->bits[0] = 0; 124 m->bits[last_word] = 0; 125 } 126 m->base = base; 127 m->nr_cids = nr_cids; 128 } 129 130 /** 131 * cmask_nr_words - Words of bits[] that @m's active range spans 132 * @m: cmask to measure 133 * 134 * @m->base need not be word aligned: bits[0] covers the whole word @m->base 135 * falls in, so the count is taken from that word, not from @m->base. 136 */ 137 static __always_inline u32 cmask_nr_words(const struct scx_cmask __arena *m) 138 { 139 u32 wbase = m->base / 64; 140 141 return m->nr_cids ? (m->base + m->nr_cids - 1) / 64 - wbase + 1 : 0; 142 } 143 144 /** 145 * cmask_word - Read word @k of @m 146 * @m: cmask to read 147 * @k: word index, counted from the word @m->base falls in 148 * 149 * A word past the active range reads as 0, so a scan that looks one word 150 * ahead (at an SMT sibling that falls in the next word, say) needs no bound 151 * of its own. 152 */ 153 static __always_inline u64 cmask_word(const struct scx_cmask __arena *m, u32 k) 154 { 155 if (k >= cmask_nr_words(m)) 156 return 0; 157 return m->bits[k]; 158 } 159 160 /** 161 * cmask_range_word - Bits of word @k of @m that fall in [@start, @start + @nr) 162 * @m: cmask the word belongs to 163 * @k: word index, counted from the word @m->base falls in 164 * @start: first cid of the range 165 * @nr: number of cids in the range 166 * 167 * Scoping a scan to a domain that is contiguous in cid space, an LLC or a 168 * node, is then one AND per word, rather than a test of the ends of the 169 * range at every bit or a mask kept per domain. 170 */ 171 static __always_inline u64 cmask_range_word(const struct scx_cmask __arena *m, 172 u32 k, u32 start, u32 nr) 173 { 174 u64 wlo = (u64)(m->base / 64 + k) * 64, whi = wlo + 64; 175 u64 lo = start, hi = (u64)start + nr; 176 177 if (lo < wlo) 178 lo = wlo; 179 if (hi > whi) 180 hi = whi; 181 if (lo >= hi) 182 return 0; 183 184 return GENMASK_U64(hi - wlo - 1, lo - wlo); 185 } 186 187 /** 188 * __cmask_test - Test a cid without checking it against the active range 189 * @cid: cid to test 190 * @m: cmask to test 191 * 192 * The caller must already know that @cid lies within 193 * [@m->base, @m->base + @m->nr_cids), or the read runs off @m->bits. 194 * 195 * For hot scans that have already bounded @cid, where the per-call range check 196 * is repeated work on every candidate. 197 */ 198 static __always_inline bool __cmask_test(u32 cid, const struct scx_cmask __arena *m) 199 { 200 return *__cmask_word(cid, m) & BIT_U64(cid & 63); 201 } 202 203 static __always_inline bool cmask_test(u32 cid, const struct scx_cmask __arena *m) 204 { 205 if (!__cmask_contains(cid, m)) 206 return false; 207 return __cmask_test(cid, m); 208 } 209 210 /* 211 * The bit helpers use the fetching bitwise builtins, which order like the 212 * cmpxchg they replace, and not every JIT accepts those on arena pointers. The 213 * loader sets SCX_LIB_FEAT_ARENA_FETCH_BITOPS when a probe with the fetching 214 * form loads, and the helpers run a cmpxchg loop otherwise. The fetched value 215 * is consumed through barrier_var() because clang 19 lowers a builtin whose 216 * result is unused to the non-fetching form, which is relaxed on arm64. 217 * 218 * CMASK_CAS_TRIES is sized so exhausting it means seconds of real spinning 219 * on one word, past any plausible contention. Abort hard. 220 */ 221 #define CMASK_CAS_TRIES (1U << 23) 222 223 static __always_inline u64 __cmask_fetch_or(u64 __arena *w, u64 mask) 224 { 225 u64 old = __atomic_fetch_or(w, mask, __ATOMIC_ACQ_REL); 226 227 barrier_var(old); 228 return old; 229 } 230 231 static __always_inline u64 __cmask_fetch_andnot(u64 __arena *w, u64 mask) 232 { 233 u64 old = __atomic_fetch_and(w, ~mask, __ATOMIC_ACQ_REL); 234 235 barrier_var(old); 236 return old; 237 } 238 239 static __always_inline void cmask_set(u32 cid, struct scx_cmask __arena *m) 240 { 241 u64 __arena *w; 242 u64 bit, old, new; 243 u32 i; 244 245 if (!__cmask_contains(cid, m)) 246 return; 247 w = __cmask_word(cid, m); 248 bit = BIT_U64(cid & 63); 249 if (scx_lib_has(SCX_LIB_FEAT_ARENA_FETCH_BITOPS)) { 250 __cmask_fetch_or(w, bit); 251 return; 252 } 253 bpf_arena_for(i, 0, CMASK_CAS_TRIES) { 254 old = *w; 255 if (old & bit) 256 return; 257 new = old | bit; 258 if (__sync_val_compare_and_swap(w, old, new) == old) 259 return; 260 } 261 scx_bpf_error("cmask_set CAS exhausted at cid %u", cid); 262 } 263 264 static __always_inline void cmask_clear(u32 cid, struct scx_cmask __arena *m) 265 { 266 u64 __arena *w; 267 u64 bit, old, new; 268 u32 i; 269 270 if (!__cmask_contains(cid, m)) 271 return; 272 w = __cmask_word(cid, m); 273 bit = BIT_U64(cid & 63); 274 if (scx_lib_has(SCX_LIB_FEAT_ARENA_FETCH_BITOPS)) { 275 __cmask_fetch_andnot(w, bit); 276 return; 277 } 278 bpf_arena_for(i, 0, CMASK_CAS_TRIES) { 279 old = *w; 280 if (!(old & bit)) 281 return; 282 new = old & ~bit; 283 if (__sync_val_compare_and_swap(w, old, new) == old) 284 return; 285 } 286 scx_bpf_error("cmask_clear CAS exhausted at cid %u", cid); 287 } 288 289 static __always_inline bool cmask_test_and_set(u32 cid, struct scx_cmask __arena *m) 290 { 291 u64 __arena *w; 292 u64 bit, old, new; 293 u32 i; 294 295 if (!__cmask_contains(cid, m)) 296 return false; 297 w = __cmask_word(cid, m); 298 bit = BIT_U64(cid & 63); 299 if (scx_lib_has(SCX_LIB_FEAT_ARENA_FETCH_BITOPS)) 300 return __cmask_fetch_or(w, bit) & bit; 301 bpf_arena_for(i, 0, CMASK_CAS_TRIES) { 302 old = *w; 303 if (old & bit) 304 return true; 305 new = old | bit; 306 if (__sync_val_compare_and_swap(w, old, new) == old) 307 return false; 308 } 309 scx_bpf_error("cmask_test_and_set CAS exhausted at cid %u", cid); 310 return false; 311 } 312 313 static __always_inline bool cmask_test_and_clear(u32 cid, struct scx_cmask __arena *m) 314 { 315 u64 __arena *w; 316 u64 bit, old, new; 317 u32 i; 318 319 if (!__cmask_contains(cid, m)) 320 return false; 321 w = __cmask_word(cid, m); 322 bit = BIT_U64(cid & 63); 323 if (scx_lib_has(SCX_LIB_FEAT_ARENA_FETCH_BITOPS)) 324 return __cmask_fetch_andnot(w, bit) & bit; 325 bpf_arena_for(i, 0, CMASK_CAS_TRIES) { 326 old = *w; 327 if (!(old & bit)) 328 return false; 329 new = old & ~bit; 330 if (__sync_val_compare_and_swap(w, old, new) == old) 331 return true; 332 } 333 scx_bpf_error("cmask_test_and_clear CAS exhausted at cid %u", cid); 334 return false; 335 } 336 337 static __always_inline void __cmask_set(u32 cid, struct scx_cmask __arena *m) 338 { 339 if (!__cmask_contains(cid, m)) 340 return; 341 *__cmask_word(cid, m) |= BIT_U64(cid & 63); 342 } 343 344 static __always_inline void __cmask_clear(u32 cid, struct scx_cmask __arena *m) 345 { 346 if (!__cmask_contains(cid, m)) 347 return; 348 *__cmask_word(cid, m) &= ~BIT_U64(cid & 63); 349 } 350 351 static __always_inline bool __cmask_test_and_set(u32 cid, struct scx_cmask __arena *m) 352 { 353 u64 bit = BIT_U64(cid & 63); 354 u64 __arena *w; 355 u64 prev; 356 357 if (!__cmask_contains(cid, m)) 358 return false; 359 w = __cmask_word(cid, m); 360 prev = *w & bit; 361 *w |= bit; 362 return prev; 363 } 364 365 static __always_inline bool __cmask_test_and_clear(u32 cid, struct scx_cmask __arena *m) 366 { 367 u64 bit = BIT_U64(cid & 63); 368 u64 __arena *w; 369 u64 prev; 370 371 if (!__cmask_contains(cid, m)) 372 return false; 373 w = __cmask_word(cid, m); 374 prev = *w & bit; 375 *w &= ~bit; 376 return prev; 377 } 378 379 /* atomically or @mask into *@w */ 380 static __always_inline void __cmask_word_or(u64 __arena *w, u64 mask) 381 { 382 u64 old, new; 383 u32 i; 384 385 if (scx_lib_has(SCX_LIB_FEAT_ARENA_FETCH_BITOPS)) { 386 __cmask_fetch_or(w, mask); 387 return; 388 } 389 bpf_arena_for(i, 0, CMASK_CAS_TRIES) { 390 old = *w; 391 if ((old & mask) == mask) 392 return; 393 new = old | mask; 394 if (__sync_val_compare_and_swap(w, old, new) == old) 395 return; 396 } 397 scx_bpf_error("__cmask_word_or CAS exhausted"); 398 } 399 400 /* atomically clear @mask bits in *@w */ 401 static __always_inline void __cmask_word_andnot(u64 __arena *w, u64 mask) 402 { 403 u64 old, new; 404 u32 i; 405 406 if (scx_lib_has(SCX_LIB_FEAT_ARENA_FETCH_BITOPS)) { 407 __cmask_fetch_andnot(w, mask); 408 return; 409 } 410 bpf_arena_for(i, 0, CMASK_CAS_TRIES) { 411 old = *w; 412 if (!(old & mask)) 413 return; 414 new = old & ~mask; 415 if (__sync_val_compare_and_swap(w, old, new) == old) 416 return; 417 } 418 scx_bpf_error("__cmask_word_andnot CAS exhausted"); 419 } 420 421 /** 422 * cmask_full_range - Test whether every cid in [@start, @start + @nr) is set 423 * @m: cmask to test 424 * @start: first cid of the range 425 * @nr: number of cids in the range 426 * 427 * The range is clamped to @m's active range first. True when no clamped bit is 428 * clear, including when the clamped range is empty. 429 */ 430 static __always_inline bool cmask_full_range(const struct scx_cmask __arena *m, u32 start, 431 u32 nr) 432 { 433 u64 end = (u64)start + nr; 434 u32 wbase = m->base / 64; 435 u32 first_wi, last_wi, first_bit, last_bit, last, i; 436 437 if (start < m->base) 438 start = m->base; 439 if (end > m->base + m->nr_cids) 440 end = m->base + m->nr_cids; 441 if (start >= end) 442 return true; 443 last = end - 1; 444 445 first_wi = start / 64 - wbase; 446 last_wi = last / 64 - wbase; 447 first_bit = start & 63; 448 last_bit = last & 63; 449 450 bpf_arena_for(i, 0, CMASK_MAX_WORDS) { 451 u32 wi = first_wi + i; 452 u64 mask = ~0ULL; 453 454 if (wi > last_wi) 455 break; 456 if (wi == first_wi) 457 mask &= GENMASK_U64(63, first_bit); 458 if (wi == last_wi) 459 mask &= GENMASK_U64(last_bit, 0); 460 if ((m->bits[wi] & mask) != mask) 461 return false; 462 } 463 return true; 464 } 465 466 /** 467 * cmask_set_range - Set every cid in [@start, @start + @nr) 468 * @m: cmask to modify 469 * @start: first cid of the range 470 * @nr: number of cids in the range 471 * 472 * The range is clamped to @m's active range first. Words are updated atomically 473 * so concurrent updates of other bits sharing a word are never lost. The range 474 * as a whole does not transition atomically. 475 */ 476 static __always_inline void cmask_set_range(struct scx_cmask __arena *m, u32 start, u32 nr) 477 { 478 u64 end = (u64)start + nr; 479 u32 wbase = m->base / 64; 480 u32 first_wi, last_wi, first_bit, last_bit, last, i; 481 482 if (start < m->base) 483 start = m->base; 484 if (end > m->base + m->nr_cids) 485 end = m->base + m->nr_cids; 486 if (start >= end) 487 return; 488 last = end - 1; 489 490 first_wi = start / 64 - wbase; 491 last_wi = last / 64 - wbase; 492 first_bit = start & 63; 493 last_bit = last & 63; 494 495 bpf_arena_for(i, 0, CMASK_MAX_WORDS) { 496 u32 wi = first_wi + i; 497 u64 mask = ~0ULL; 498 499 if (wi > last_wi) 500 break; 501 if (wi == first_wi) 502 mask &= GENMASK_U64(63, first_bit); 503 if (wi == last_wi) 504 mask &= GENMASK_U64(last_bit, 0); 505 __cmask_word_or(&m->bits[wi], mask); 506 } 507 } 508 509 /** 510 * cmask_clear_range - Clear every cid in [@start, @start + @nr) 511 * @m: cmask to modify 512 * @start: first cid of the range 513 * @nr: number of cids in the range 514 * 515 * The range is clamped to @m's active range first. Words are updated atomically 516 * so concurrent updates of other bits sharing a word are never lost. The range 517 * as a whole does not transition atomically. 518 */ 519 static __always_inline void cmask_clear_range(struct scx_cmask __arena *m, u32 start, u32 nr) 520 { 521 u64 end = (u64)start + nr; 522 u32 wbase = m->base / 64; 523 u32 first_wi, last_wi, first_bit, last_bit, last, i; 524 525 if (start < m->base) 526 start = m->base; 527 if (end > m->base + m->nr_cids) 528 end = m->base + m->nr_cids; 529 if (start >= end) 530 return; 531 last = end - 1; 532 533 first_wi = start / 64 - wbase; 534 last_wi = last / 64 - wbase; 535 first_bit = start & 63; 536 last_bit = last & 63; 537 538 bpf_arena_for(i, 0, CMASK_MAX_WORDS) { 539 u32 wi = first_wi + i; 540 u64 mask = ~0ULL; 541 542 if (wi > last_wi) 543 break; 544 if (wi == first_wi) 545 mask &= GENMASK_U64(63, first_bit); 546 if (wi == last_wi) 547 mask &= GENMASK_U64(last_bit, 0); 548 __cmask_word_andnot(&m->bits[wi], mask); 549 } 550 } 551 552 static __always_inline void cmask_zero(struct scx_cmask __arena *m) 553 { 554 u32 nr_words = CMASK_NR_WORDS(m->nr_cids), i; 555 556 bpf_arena_for(i, 0, CMASK_MAX_WORDS) { 557 if (i >= nr_words) 558 break; 559 m->bits[i] = 0; 560 } 561 } 562 563 /** 564 * cmask_fill - Set every cid in @m's active range 565 * @m: cmask to fill 566 * 567 * Counterpart to cmask_zero(). Storage past the active range is left as is, 568 * matching the kernel-side scx_cmask_fill(). 569 */ 570 static __always_inline void cmask_fill(struct scx_cmask __arena *m) 571 { 572 u32 nr_words, head_bits, tail_bits, i; 573 574 if (!m->nr_cids) 575 return; 576 nr_words = (m->base + m->nr_cids - 1) / 64 - m->base / 64 + 1; 577 578 bpf_arena_for(i, 0, CMASK_MAX_WORDS) { 579 if (i >= nr_words) 580 break; 581 m->bits[i] = ~0LLU; 582 } 583 584 /* clear word-0 bits below base */ 585 head_bits = m->base & 63; 586 if (head_bits) 587 m->bits[0] &= ~((1LLU << head_bits) - 1); 588 589 /* clear last-word bits at or past base + nr_cids */ 590 tail_bits = (m->base + m->nr_cids) & 63; 591 if (tail_bits) 592 m->bits[nr_words - 1] &= (1LLU << tail_bits) - 1; 593 } 594 595 /** 596 * cmask_empty - Test whether no cid is set in @m's active range 597 * @m: cmask to test 598 * 599 * Scans the words the active range spans, whose bits outside the range every 600 * mutator keeps zero. 601 */ 602 static __always_inline bool cmask_empty(const struct scx_cmask __arena *m) 603 { 604 u32 nr_words = cmask_nr_words(m), i; 605 606 bpf_arena_for(i, 0, CMASK_MAX_WORDS) { 607 if (i >= nr_words) 608 break; 609 if (m->bits[i]) 610 return false; 611 } 612 return true; 613 } 614 615 /* 616 * BPF_-prefixed to avoid colliding with the kernel's anonymous CMASK_OP_* 617 * enum in ext/cid.c, which is exported via BTF and reachable through 618 * vmlinux.h. 619 */ 620 enum { 621 BPF_CMASK_OP_AND, 622 BPF_CMASK_OP_OR, 623 BPF_CMASK_OP_COPY, 624 BPF_CMASK_OP_ANDNOT, 625 }; 626 627 /* range bits of word @k of @m, which spans @nr words: 0 past the span */ 628 static __always_inline u64 __cmask_word_range(const struct scx_cmask __arena *m, u32 k, 629 u32 nr) 630 { 631 u64 bits = ~0ULL; 632 633 if (k >= nr) 634 return 0; 635 if (k == 0) 636 bits &= GENMASK_U64(63, m->base & 63); 637 if (k == nr - 1) 638 bits &= GENMASK_U64((m->base + m->nr_cids - 1) & 63, 0); 639 return bits; 640 } 641 642 /* 643 * The binary operations combine cmasks of any active ranges. A cmask holds bits 644 * only for its own range and reads as the operation's identity outside it, all 645 * ones for AND and all zeros for OR, so an operand alters only the bits it 646 * covers: ANDing a shard's mask into a global one edits that shard's window and 647 * leaves the rest of the global mask alone. ANDNOT is AND with @src2 inverted 648 * over @src2's range, so where only @src2 has range the result is ~@src2. 649 * 650 * @dst is written on its range intersected with the union of the sources' 651 * ranges and untouched elsewhere, and its padding bits outside its own range 652 * stay zero, the invariant every cmask mutator keeps. @dst may alias either 653 * source, and the two sources may be one mask. Returns whether @dst has a bit 654 * set afterwards, over all of @dst's range. 655 * 656 * The operations are global functions, verified once per program. Inlined, 657 * every data-dependent branch in this loop would multiply the states the 658 * verifier explores per iteration at every call site. 659 */ 660 static __always_inline bool __cmask_op(struct scx_cmask __arena *dst, 661 const struct scx_cmask __arena *src1, 662 const struct scx_cmask __arena *src2, int op) 663 { 664 u32 d_first = dst->base / 64, d_nr = cmask_nr_words(dst); 665 u32 s1_first = src1->base / 64, s1_nr = cmask_nr_words(src1); 666 u32 s2_first = src2->base / 64, s2_nr = cmask_nr_words(src2); 667 u64 any = bpf_arena_loop_zero; 668 u32 i; 669 670 bpf_arena_for(i, 0, d_nr) { 671 u32 k1 = d_first + i - s1_first, k2 = d_first + i - s2_first; 672 u64 m1 = __cmask_word_range(src1, k1, s1_nr); 673 u64 m2 = op == BPF_CMASK_OP_COPY ? m1 : __cmask_word_range(src2, k2, s2_nr); 674 u64 um = __cmask_word_range(dst, i, d_nr) & (m1 | m2); 675 u64 dv = dst->bits[i], v1, v2, rv; 676 677 if (um) { 678 /* a word in range holds zeros outside the range */ 679 v1 = m1 ? src1->bits[k1] : 0; 680 v2 = m2 ? src2->bits[k2] : 0; 681 if (op == BPF_CMASK_OP_AND) 682 rv = (v1 | ~m1) & (v2 | ~m2); 683 else if (op == BPF_CMASK_OP_OR) 684 rv = v1 | v2; 685 else if (op == BPF_CMASK_OP_ANDNOT) 686 rv = (v1 | ~m1) & ~v2; 687 else 688 rv = v1; 689 dv = (dv & ~um) | (rv & um); 690 dst->bits[i] = dv; 691 } 692 any |= dv; 693 } 694 return any != 0; 695 } 696 697 /** 698 * cmask_and - Store @src1 AND @src2 in @dst 699 * @dst: destination, may be @src1 or @src2 700 * @src1: first operand 701 * @src2: second operand 702 * 703 * See __cmask_op() for how the ranges combine. Return whether @dst has a bit 704 * set afterwards. 705 */ 706 __weak bool cmask_and(struct scx_cmask __arena __arg_arena *dst, 707 const struct scx_cmask __arena __arg_arena *src1, 708 const struct scx_cmask __arena __arg_arena *src2) 709 { 710 return __cmask_op(dst, src1, src2, BPF_CMASK_OP_AND); 711 } 712 713 /** 714 * cmask_or - Store @src1 OR @src2 in @dst 715 * @dst: destination, may be @src1 or @src2 716 * @src1: first operand 717 * @src2: second operand 718 * 719 * See __cmask_op() for how the ranges combine. Return whether @dst has a bit 720 * set afterwards. 721 */ 722 __weak bool cmask_or(struct scx_cmask __arena __arg_arena *dst, 723 const struct scx_cmask __arena __arg_arena *src1, 724 const struct scx_cmask __arena __arg_arena *src2) 725 { 726 return __cmask_op(dst, src1, src2, BPF_CMASK_OP_OR); 727 } 728 729 /** 730 * cmask_andnot - Store @src1 AND NOT @src2 in @dst 731 * @dst: destination, may be @src1 or @src2 732 * @src1: operand to remove bits from 733 * @src2: bits to remove, inverted over its own range 734 * 735 * See __cmask_op() for how the ranges combine. Return whether @dst has a bit 736 * set afterwards. 737 */ 738 __weak bool cmask_andnot(struct scx_cmask __arena __arg_arena *dst, 739 const struct scx_cmask __arena __arg_arena *src1, 740 const struct scx_cmask __arena __arg_arena *src2) 741 { 742 return __cmask_op(dst, src1, src2, BPF_CMASK_OP_ANDNOT); 743 } 744 745 /** 746 * cmask_copy - Copy @src into @dst 747 * @dst: destination 748 * @src: source 749 * 750 * The single-source case of __cmask_op(): @dst is written over @src's range and 751 * untouched elsewhere. 752 */ 753 __weak void cmask_copy(struct scx_cmask __arena __arg_arena *dst, 754 const struct scx_cmask __arena __arg_arena *src) 755 { 756 __cmask_op(dst, src, src, BPF_CMASK_OP_COPY); 757 } 758 759 /* 760 * True iff @a and @b have identical bits over their (assumed equal) range. 761 * Callers are expected to pass same-shape cmasks; differing shapes always 762 * compare unequal. 763 */ 764 static __always_inline bool cmask_equal(const struct scx_cmask __arena *a, 765 const struct scx_cmask __arena *b) 766 { 767 u32 nr_words, i; 768 769 if (a->base != b->base || a->nr_cids != b->nr_cids) 770 return false; 771 if (a->nr_cids == 0) 772 return true; 773 nr_words = (a->base + a->nr_cids - 1) / 64 - a->base / 64 + 1; 774 775 bpf_arena_for(i, 0, CMASK_MAX_WORDS) { 776 if (i >= nr_words) 777 break; 778 if (a->bits[i] != b->bits[i]) 779 return false; 780 } 781 return true; 782 } 783 784 /** 785 * cmask_next_set - find the first set bit at or after @cid 786 * @m: cmask to search 787 * @cid: starting cid (clamped to @m->base if below) 788 * 789 * Returns the smallest set cid in [@cid, @m->base + @m->nr_cids), or 790 * @m->base + @m->nr_cids if none (the out-of-range sentinel matches the 791 * termination condition used by cmask_for_each()). 792 */ 793 static __always_inline u32 cmask_next_set(const struct scx_cmask __arena *m, u32 cid) 794 { 795 u32 end = m->base + m->nr_cids; 796 u32 base = m->base / 64; 797 u32 last_wi = (end - 1) / 64 - base; 798 u32 start_wi, start_bit, i; 799 800 if (cid < m->base) 801 cid = m->base; 802 if (cid >= end) 803 return end; 804 805 start_wi = cid / 64 - base; 806 start_bit = cid & 63; 807 808 bpf_arena_for(i, 0, CMASK_MAX_WORDS) { 809 u32 wi = start_wi + i; 810 u64 word; 811 u32 found; 812 813 if (wi > last_wi) 814 break; 815 816 word = m->bits[wi]; 817 if (i == 0) 818 word &= GENMASK_U64(63, start_bit); 819 if (!word) 820 continue; 821 822 found = (base + wi) * 64 + ctzll(word); 823 if (found >= end) 824 return end; 825 return found; 826 } 827 return end; 828 } 829 830 static __always_inline u32 cmask_first_set(const struct scx_cmask __arena *m) 831 { 832 return cmask_next_set(m, m->base); 833 } 834 835 #define cmask_for_each(cid, m) \ 836 for ((cid) = cmask_first_set(m); \ 837 (cid) < (m)->base + (m)->nr_cids && can_loop; \ 838 (cid) = cmask_next_set((m), (cid) + 1)) 839 840 /* 841 * True iff every bit set in @a is also set in @b. Matches the kernel-side 842 * scx_cmask_subset(): ranges don't need to nest, and set bits of @a outside 843 * @b's range fail the test. 844 */ 845 static __always_inline bool cmask_subset(const struct scx_cmask __arena *a, 846 const struct scx_cmask __arena *b) 847 { 848 u32 a_end = a->base + a->nr_cids; 849 u32 b_end = b->base + b->nr_cids; 850 u32 a_wbase = a->base / 64; 851 u32 b_wbase = b->base / 64; 852 u32 lo = a->base > b->base ? a->base : b->base; 853 u32 hi = a_end < b_end ? a_end : b_end; 854 u32 lo_word, hi_word, i; 855 856 /* set bits of @a outside @b's range can't be in @b */ 857 if (a->base < b->base && 858 cmask_next_set(a, a->base) < (b->base < a_end ? b->base : a_end)) 859 return false; 860 if (a_end > b_end && 861 cmask_next_set(a, a->base > b_end ? a->base : b_end) < a_end) 862 return false; 863 864 if (lo >= hi) 865 return true; 866 867 /* 868 * Walk the words the range intersection spans. Plain word tests 869 * suffice: the scans above guarantee @a has no set bit outside @b's 870 * range and padding bits are kept clear by all cmask helpers. 871 */ 872 lo_word = lo / 64; 873 hi_word = (hi - 1) / 64; 874 875 bpf_arena_for(i, 0, CMASK_MAX_WORDS) { 876 u32 w = lo_word + i; 877 878 if (w > hi_word) 879 break; 880 if (a->bits[w - a_wbase] & ~b->bits[w - b_wbase]) 881 return false; 882 } 883 return true; 884 } 885 886 /* 887 * Population count over [base, base + nr_cids). Padding bits in the head/tail 888 * words are guaranteed zero by the mutating helpers, so a flat popcount over 889 * the words the range spans is correct. 890 */ 891 static __always_inline u32 cmask_weight(const struct scx_cmask __arena *m) 892 { 893 u32 nr_words, i; 894 /* callers compare the sum, see bpf_arena_loop_zero */ 895 u32 count = bpf_arena_loop_zero; 896 897 if (!m->nr_cids) 898 return 0; 899 nr_words = (m->base + m->nr_cids - 1) / 64 - m->base / 64 + 1; 900 901 bpf_arena_for(i, 0, CMASK_MAX_WORDS) { 902 if (i >= nr_words) 903 break; 904 count += __builtin_popcountll(m->bits[i]); 905 } 906 return count; 907 } 908 909 /* true if @a and @b share any set bit, over the intersection of their ranges */ 910 static __always_inline bool cmask_intersects(const struct scx_cmask __arena *a, 911 const struct scx_cmask __arena *b) 912 { 913 u32 a_end = a->base + a->nr_cids; 914 u32 b_end = b->base + b->nr_cids; 915 u32 lo = a->base > b->base ? a->base : b->base; 916 u32 hi = a_end < b_end ? a_end : b_end; 917 u32 a_base = a->base / 64; 918 u32 b_base = b->base / 64; 919 u32 lo_word, hi_word, i; 920 u64 head_mask, tail_mask; 921 922 if (lo >= hi) 923 return false; 924 925 lo_word = lo / 64; 926 hi_word = (hi - 1) / 64; 927 head_mask = GENMASK_U64(63, lo & 63); 928 tail_mask = GENMASK_U64((hi - 1) & 63, 0); 929 930 bpf_arena_for(i, 0, CMASK_MAX_WORDS) { 931 u32 w = lo_word + i; 932 u64 mask, av, bv; 933 934 if (w > hi_word) 935 break; 936 937 mask = GENMASK_U64(63, 0); 938 if (w == lo_word) 939 mask &= head_mask; 940 if (w == hi_word) 941 mask &= tail_mask; 942 943 av = a->bits[w - a_base] & mask; 944 bv = b->bits[w - b_base] & mask; 945 if (av & bv) 946 return true; 947 } 948 return false; 949 } 950 951 /* 952 * Find the next cid set in both @a and @b at or after @start, bounded by the 953 * intersection of the two ranges. Return a->base + a->nr_cids if none found. 954 * 955 * Building block for cmask_next_and_set_wrap(). Callers that want a bounded 956 * scan without wrap call this directly. 957 */ 958 static __always_inline u32 cmask_next_and_set(const struct scx_cmask __arena *a, 959 const struct scx_cmask __arena *b, 960 u32 start) 961 { 962 u32 a_end = a->base + a->nr_cids; 963 u32 b_end = b->base + b->nr_cids; 964 u32 a_wbase = a->base / 64; 965 u32 b_wbase = b->base / 64; 966 u32 lo = a->base > b->base ? a->base : b->base; 967 u32 hi = a_end < b_end ? a_end : b_end; 968 u32 last_wi, start_wi, start_bit, i; 969 970 if (lo >= hi) 971 return a_end; 972 if (start < lo) 973 start = lo; 974 if (start >= hi) 975 return a_end; 976 977 last_wi = (hi - 1) / 64; 978 start_wi = start / 64; 979 start_bit = start & 63; 980 981 bpf_arena_for(i, 0, CMASK_MAX_WORDS) { 982 u32 abs_wi = start_wi + i; 983 u64 word; 984 u32 found; 985 986 if (abs_wi > last_wi) 987 break; 988 989 word = a->bits[abs_wi - a_wbase] & b->bits[abs_wi - b_wbase]; 990 if (i == 0) 991 word &= GENMASK_U64(63, start_bit); 992 if (!word) 993 continue; 994 995 found = abs_wi * 64 + ctzll(word); 996 if (found >= hi) 997 return a_end; 998 return found; 999 } 1000 return a_end; 1001 } 1002 1003 /* 1004 * Find the next set cid in @m at or after @start, wrapping to @m->base if no 1005 * set bit is found in [start, m->base + m->nr_cids). Return m->base + 1006 * m->nr_cids if @m is empty. 1007 * 1008 * Callers do round-robin distribution by passing (last_cid + 1) as @start. 1009 */ 1010 static __always_inline u32 cmask_next_set_wrap(const struct scx_cmask __arena *m, 1011 u32 start) 1012 { 1013 u32 end = m->base + m->nr_cids; 1014 u32 found; 1015 1016 found = cmask_next_set(m, start); 1017 if (found < end || start <= m->base) 1018 return found; 1019 1020 found = cmask_next_set(m, m->base); 1021 return found < start ? found : end; 1022 } 1023 1024 /** 1025 * cmask_end - One past the last cid of @m's active range 1026 * @m: cmask of interest 1027 * 1028 * The scan helpers return this value when no set cid is found. 1029 */ 1030 static __always_inline u32 cmask_end(const struct scx_cmask __arena *m) 1031 { 1032 return m->base + m->nr_cids; 1033 } 1034 1035 /* 1036 * Find the next cid set in both @a and @b at or after @start, wrapping to 1037 * @a->base if none found in the forward half. Return a->base + a->nr_cids 1038 * if the intersection is empty. 1039 * 1040 * Callers do round-robin distribution by passing (last_cid + 1) as @start. 1041 */ 1042 static __always_inline u32 cmask_next_and_set_wrap(const struct scx_cmask __arena *a, 1043 const struct scx_cmask __arena *b, 1044 u32 start) 1045 { 1046 u32 a_end = a->base + a->nr_cids; 1047 u32 found; 1048 1049 found = cmask_next_and_set(a, b, start); 1050 if (found < a_end || start <= a->base) 1051 return found; 1052 1053 found = cmask_next_and_set(a, b, a->base); 1054 return found < start ? found : a_end; 1055 } 1056 1057 /* 1058 * The distribute helpers rotate through one slot per cid, a cacheline each so 1059 * picks on one CPU do not bounce the line of another. A missing allocation is a 1060 * setup bug and aborts the scheduler. On a CPU outside the scheduler's cid 1061 * space the helpers fall back to the first matching cid. 1062 * 1063 * TODO: The slots are one allocation without node placement. Move them to a 1064 * per-cid arena allocator once the library has one, for node-local slots 1065 * without the cacheline padding. 1066 */ 1067 struct cmask_rotor_slot { 1068 u32 cid; 1069 } __attribute__((aligned(SCX_CACHELINE_SIZE))); 1070 1071 struct cmask_rotor_slot __arena *cmask_distribute_rotors __weak; 1072 1073 /** 1074 * cmask_distribute_init - Allocate the distribute rotor from @map 1075 * @map: the scheduler's arena map 1076 * 1077 * The shared arena init calls this once. A scheduler with its own arena calls 1078 * it before the first distribute pick. On a kernel without the cid kfuncs 1079 * nothing can pick, so the call allocates nothing and succeeds. Return 0 on 1080 * success, -ENOMEM if the allocation fails. 1081 */ 1082 static __always_inline int cmask_distribute_init(void *map) 1083 { 1084 u64 size; 1085 u32 pages; 1086 1087 /* no cid kfuncs means no picks and nothing to allocate */ 1088 if (!bpf_ksym_exists(scx_bpf_nr_cids)) 1089 return 0; 1090 1091 size = scx_bpf_nr_cids() * sizeof(*cmask_distribute_rotors); 1092 pages = (size + PAGE_SIZE - 1) / PAGE_SIZE; 1093 cmask_distribute_rotors = bpf_arena_alloc_pages(map, NULL, pages, NUMA_NO_NODE, 0); 1094 return cmask_distribute_rotors ? 0 : -ENOMEM; 1095 } 1096 1097 static __always_inline u32 __arena *__cmask_distribute_rotor(void) 1098 { 1099 s32 cid = scx_bpf_this_cid(); 1100 1101 if (unlikely(!cmask_distribute_rotors)) { 1102 scx_bpf_error("cmask distribute rotor not allocated"); 1103 return NULL; 1104 } 1105 if (cid < 0) 1106 return NULL; 1107 return &cmask_distribute_rotors[cid].cid; 1108 } 1109 1110 /** 1111 * cmask_any_distribute - Pick a set cid, spreading successive picks 1112 * @m: cmask to pick from 1113 * 1114 * Counterpart of bpf_cpumask_any_distribute(): a per-cid rotor makes successive 1115 * picks rotate through the set cids instead of repeating the first one. Returns 1116 * cmask_end(@m) if @m is empty. 1117 */ 1118 static __always_inline u32 cmask_any_distribute(const struct scx_cmask __arena *m) 1119 { 1120 u32 __arena *rotor = __cmask_distribute_rotor(); 1121 u32 pick; 1122 1123 if (unlikely(!rotor)) 1124 return cmask_first_set(m); 1125 1126 pick = cmask_next_set_wrap(m, *rotor + 1); 1127 if (pick < cmask_end(m)) 1128 *rotor = pick; 1129 return pick; 1130 } 1131 1132 /** 1133 * cmask_any_and_distribute - Pick a cid set in both masks, spreading picks 1134 * @a: first cmask, the scan is bounded by its range 1135 * @b: second cmask 1136 * 1137 * Counterpart of bpf_cpumask_any_and_distribute(). Shares the rotor with 1138 * cmask_any_distribute() the same way the kernel counterparts share theirs. 1139 * Returns cmask_end(@a) if the intersection is empty. 1140 */ 1141 static __always_inline u32 cmask_any_and_distribute(const struct scx_cmask __arena *a, 1142 const struct scx_cmask __arena *b) 1143 { 1144 u32 __arena *rotor = __cmask_distribute_rotor(); 1145 u32 pick; 1146 1147 if (unlikely(!rotor)) 1148 return cmask_next_and_set(a, b, a->base); 1149 1150 pick = cmask_next_and_set_wrap(a, b, *rotor + 1); 1151 if (pick < cmask_end(a)) 1152 *rotor = pick; 1153 return pick; 1154 } 1155 1156 /** 1157 * cmask_distribute_rotor_pos - Last cid picked by the distribute helpers 1158 * 1159 * For callers that anchor scans of their own on the shared rotor. Returns 0 1160 * when nothing has been picked on this cid yet or the CPU is outside the cid 1161 * space. 1162 */ 1163 static __always_inline u32 cmask_distribute_rotor_pos(void) 1164 { 1165 u32 __arena *rotor = __cmask_distribute_rotor(); 1166 1167 if (unlikely(!rotor)) 1168 return 0; 1169 return *rotor; 1170 } 1171 1172 /* 1173 * Like cmask_next_and_set() but over the intersection of THREE masks. Return 1174 * a->base + a->nr_cids if no cid is set in all three at or after @start. 1175 */ 1176 static __always_inline u32 cmask_next_and2_set(const struct scx_cmask __arena *a, 1177 const struct scx_cmask __arena *b, 1178 const struct scx_cmask __arena *c, 1179 u32 start) 1180 { 1181 u32 a_end = a->base + a->nr_cids; 1182 u32 b_end = b->base + b->nr_cids; 1183 u32 c_end = c->base + c->nr_cids; 1184 u32 a_wbase = a->base / 64; 1185 u32 b_wbase = b->base / 64; 1186 u32 c_wbase = c->base / 64; 1187 u32 lo = a->base > b->base ? a->base : b->base; 1188 u32 hi = a_end < b_end ? a_end : b_end; 1189 u32 last_wi, start_wi, start_bit, i; 1190 1191 lo = lo > c->base ? lo : c->base; 1192 hi = hi < c_end ? hi : c_end; 1193 1194 if (lo >= hi) 1195 return a_end; 1196 if (start < lo) 1197 start = lo; 1198 if (start >= hi) 1199 return a_end; 1200 1201 last_wi = (hi - 1) / 64; 1202 start_wi = start / 64; 1203 start_bit = start & 63; 1204 1205 bpf_arena_for(i, 0, CMASK_MAX_WORDS) { 1206 u32 abs_wi = start_wi + i; 1207 u64 word; 1208 u32 found; 1209 1210 if (abs_wi > last_wi) 1211 break; 1212 1213 word = a->bits[abs_wi - a_wbase] & b->bits[abs_wi - b_wbase] & 1214 c->bits[abs_wi - c_wbase]; 1215 if (i == 0) 1216 word &= GENMASK_U64(63, start_bit); 1217 if (!word) 1218 continue; 1219 1220 found = abs_wi * 64 + ctzll(word); 1221 if (found >= hi) 1222 return a_end; 1223 return found; 1224 } 1225 return a_end; 1226 } 1227 1228 /* 1229 * Round-robin variant of cmask_next_and2_set(): wrap to @a->base if the 1230 * three-way intersection has no cid in the forward half. Return a->base + 1231 * a->nr_cids if empty. 1232 */ 1233 static __always_inline u32 cmask_next_and2_set_wrap(const struct scx_cmask __arena *a, 1234 const struct scx_cmask __arena *b, 1235 const struct scx_cmask __arena *c, 1236 u32 start) 1237 { 1238 u32 a_end = a->base + a->nr_cids; 1239 u32 found; 1240 1241 found = cmask_next_and2_set(a, b, c, start); 1242 if (found < a_end || start <= a->base) 1243 return found; 1244 1245 found = cmask_next_and2_set(a, b, c, a->base); 1246 return found < start ? found : a_end; 1247 } 1248 1249 /** 1250 * cmask_from_cpumask - translate a kernel cpumask to a cid-space cmask 1251 * @m: cmask to fill. Zeroed first; only bits within [@m->base, @m->base + 1252 * @m->nr_cids) are updated - cpus mapping to cids outside that range 1253 * are ignored. 1254 * @cpumask: kernel cpumask to translate 1255 * 1256 * For each cpu in @cpumask, set the cpu's cid in @m. Caller must ensure 1257 * @cpumask stays stable across the call (e.g. RCU read lock for 1258 * task->cpus_ptr). 1259 */ 1260 static __always_inline void cmask_from_cpumask(struct scx_cmask __arena *m, 1261 const struct cpumask *cpumask) 1262 { 1263 u32 nr_cpu_ids = scx_bpf_nr_cpu_ids(); 1264 s32 cpu; 1265 1266 cmask_zero(m); 1267 bpf_arena_for(cpu, 0, nr_cpu_ids) { 1268 s32 cid; 1269 1270 if (!bpf_cpumask_test_cpu(cpu, cpumask)) 1271 continue; 1272 cid = scx_bpf_cpu_to_cid(cpu); 1273 if (cid >= 0) 1274 __cmask_set(cid, m); 1275 } 1276 } 1277 1278 #endif /* __SCX_CID_BPF_H */ 1279