| @@ -951,6 +951,38 @@ int scx_cmask_ref_init(struct scx_sched *sch, const struct scx_cmask *src, · 950 unmodified lines |
| 951 | 951 | return 0; |
| 952 | 952 | } |
| 953 | 953 | |
| 954 | +/** |
| 955 | + * scx_cmask_ref_init_kern - Bind a scx_cmask_ref to a kernel-owned cmask |
| 956 | + * @sch: scheduler the cmask belongs to |
| 957 | + * @m: kernel address of the target cmask, storage sized for @nr_cids at @base |
| 958 | + * @base: first cid of the active range |
| 959 | + * @nr_cids: active range length |
| 960 | + * @ref: output ref |
| 961 | + * |
| 962 | + * Like scx_cmask_ref_init() but the geometry is supplied by the caller, not |
| 963 | + * read from @m's header, so a concurrent BPF write to the header can't steer |
| 964 | + * later sizing or offsets. Rewrite the header from the trusted geometry and |
| 965 | + * bind @ref to it. |
| 966 | + */ |
| 967 | +void scx_cmask_ref_init_kern(struct scx_sched *sch, struct scx_cmask *m, |
| 968 | + u32 base, u32 nr_cids, struct scx_cmask_ref *ref) |
| 969 | +{ |
| 970 | + WRITE_ONCE(m->base, base); |
| 971 | + WRITE_ONCE(m->nr_cids, nr_cids); |
| 972 | + WRITE_ONCE(m->alloc_words, SCX_CMASK_NR_WORDS(nr_cids)); |
| 973 | + |
| 974 | + ref->sch = sch; |
| 975 | + ref->src = m; |
| 976 | + ref->base = base; |
| 977 | + ref->nr_cids = nr_cids; |
| 978 | + |
| 979 | + ref->shard_first = scx_cid_to_shard[base]; |
| 980 | + if (likely(nr_cids)) |
| 981 | + ref->shard_end = scx_cid_to_shard[base + nr_cids - 1] + 1; |
| 982 | + else |
| 983 | + ref->shard_end = ref->shard_first; |
| 984 | +} |
| 985 | + |
| 954 | 986 | /** |
| 955 | 987 | * scx_cmask_ref_shard - Read one shard from @ref into @out |
| 956 | 988 | * @ref: validated ref |
| @@ -1032,6 +1064,44 @@ void scx_cmask_ref_copy(const struct scx_cmask_ref *ref, const struct scx_cmask · 75 unmodified lines |
| 1032 | 1064 | src->bits, src->base, src->nr_cids, CMASK_OP2_REF_COPY); |
| 1033 | 1065 | } |
| 1034 | 1066 | |
| 1067 | +/** |
| 1068 | + * scx_cmask_ref_from_cpumask - Populate @ref's arena cmask from a cpumask |
| 1069 | + * @ref: kern-bound ref, see scx_cmask_ref_init_kern() |
| 1070 | + * @cpumask: cpus to translate into cids |
| 1071 | + * |
| 1072 | + * Write @ref's active range one word at a time, setting each cid's bit when |
| 1073 | + * its cpu is in @cpumask. Offsets and length come from @ref's trusted geometry |
| 1074 | + * and stores use WRITE_ONCE since BPF may read concurrently, so the arena |
| 1075 | + * header is never read. |
| 1076 | + */ |
| 1077 | +void scx_cmask_ref_from_cpumask(const struct scx_cmask_ref *ref, |
| 1078 | + const struct cpumask *cpumask) |
| 1079 | +{ |
| 1080 | + struct scx_cmask *m = ref->src; |
| 1081 | + u32 base = ref->base, nr_cids = ref->nr_cids; |
| 1082 | + u32 wi, nr_words; |
| 1083 | + |
| 1084 | + if (!nr_cids) |
| 1085 | + return; |
| 1086 | + |
| 1087 | + nr_words = (base + nr_cids - 1) / 64 - base / 64 + 1; |
| 1088 | + for (wi = 0; wi < nr_words; wi++) { |
| 1089 | + u32 word_first_cid = (base / 64 + wi) * 64; |
| 1090 | + u64 word = 0; |
| 1091 | + u32 bit; |
| 1092 | + |
| 1093 | + for (bit = 0; bit < 64; bit++) { |
| 1094 | + u32 cid = word_first_cid + bit; |
| 1095 | + |
| 1096 | + if (cid < base || cid >= base + nr_cids) |
| 1097 | + continue; |
| 1098 | + if (cpumask_test_cpu(__scx_cid_to_cpu(cid), cpumask)) |
| 1099 | + word |= BIT_U64(bit); |
| 1100 | + } |
| 1101 | + WRITE_ONCE(m->bits[wi], word); |
| 1102 | + } |
| 1103 | +} |
| 1104 | + |
| 1035 | 1105 | int scx_cid_kfunc_init(void) |
| 1036 | 1106 | { |
| 1037 | 1107 | return register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &scx_kfunc_set_init_cids) ?: |