On Sat, 21 Jun 2025 21:33:26 +0200 [email protected]
wrote:
> +static struct netlink_range_validation dualpi2_alpha_beta_range = {
> + .min = 1,
> + .max = ALPHA_BETA_MAX,
> +};
> +
> +static struct netlink_range_validation dualpi2_wc_range = {
> + .min = 0,
> + .max = MAX_WC,
> +};
> +
> +static struct netlink_range_validation dualpi2_ecn_mask_range = {
> + .min = TCA_DUALPI2_ECN_MASK_L4S_ECT,
> + .max = TCA_DUALPI2_ECN_MASK_MAX,
> +};
ranges which fit in s16 can be expressed directly with
NLA_POLICY_RANGE(), you don't need the out-of-line struct.
> +static const struct nla_policy dualpi2_policy[TCA_DUALPI2_MAX + 1] = {
> + [TCA_DUALPI2_LIMIT] = NLA_POLICY_MIN(NLA_U32, 1),
> + [TCA_DUALPI2_MEMORY_LIMIT] = NLA_POLICY_MIN(NLA_U32, 1),
> + [TCA_DUALPI2_TARGET] = {.type = NLA_U32},
nit: spaces around {} brackets = { .type = NLA_U32 },
> + [TCA_DUALPI2_TUPDATE] = NLA_POLICY_MIN(NLA_U32, 1),
> + [TCA_DUALPI2_ALPHA] =
> + NLA_POLICY_FULL_RANGE(NLA_U32, &dualpi2_alpha_beta_range),
> + [TCA_DUALPI2_BETA] =
> + NLA_POLICY_FULL_RANGE(NLA_U32, &dualpi2_alpha_beta_range),
> + [TCA_DUALPI2_STEP_THRESH] = {.type = NLA_U32},
> + [TCA_DUALPI2_STEP_PACKETS] = {.type = NLA_FLAG},
> + [TCA_DUALPI2_MIN_QLEN_STEP] = {.type = NLA_U32},
> + [TCA_DUALPI2_COUPLING] = NLA_POLICY_MIN(NLA_U8, 1),
> + [TCA_DUALPI2_DROP_OVERLOAD] =
> + NLA_POLICY_MAX(NLA_U8, TCA_DUALPI2_DROP_OVERLOAD_MAX),
> + [TCA_DUALPI2_DROP_EARLY] =
> + NLA_POLICY_MAX(NLA_U8, TCA_DUALPI2_DROP_EARLY_MAX),
> + [TCA_DUALPI2_C_PROTECTION] =
> + NLA_POLICY_FULL_RANGE(NLA_U8, &dualpi2_wc_range),
> + [TCA_DUALPI2_ECN_MASK] =
> + NLA_POLICY_FULL_RANGE(NLA_U8, &dualpi2_ecn_mask_range),
> + [TCA_DUALPI2_SPLIT_GSO] =
> + NLA_POLICY_MAX(NLA_U8, TCA_DUALPI2_SPLIT_GSO_MAX),
> +};
> +
> +static int dualpi2_change(struct Qdisc *sch, struct nlattr *opt,
> + struct netlink_ext_ack *extack)
> +{
> + struct nlattr *tb[TCA_DUALPI2_MAX + 1];
> + struct dualpi2_sched_data *q;
> + int old_backlog;
> + int old_qlen;
> + int err;
> +
> + if (!opt)
should there be an extack message here?
> + return -EINVAL;
> + err = nla_parse_nested(tb, TCA_DUALPI2_MAX, opt, dualpi2_policy,
> + extack);
> + if (err < 0)
> + return err;
> +
> + q = qdisc_priv(sch);
> + sch_tree_lock(sch);
> +
> + if (tb[TCA_DUALPI2_LIMIT]) {
> + u32 limit = nla_get_u32(tb[TCA_DUALPI2_LIMIT]);
> +
> + WRITE_ONCE(sch->limit, limit);
> + WRITE_ONCE(q->memory_limit, get_memory_limit(sch, limit));
> + }
> +
> + if (tb[TCA_DUALPI2_MEMORY_LIMIT])
> + WRITE_ONCE(q->memory_limit,
> + nla_get_u32(tb[TCA_DUALPI2_MEMORY_LIMIT]));
> +
> + if (tb[TCA_DUALPI2_TARGET]) {
> + u64 target = nla_get_u32(tb[TCA_DUALPI2_TARGET]);
> +
> + WRITE_ONCE(q->pi2_target, target * NSEC_PER_USEC);
> + }
> +
> + if (tb[TCA_DUALPI2_TUPDATE]) {
> + u64 tupdate = nla_get_u32(tb[TCA_DUALPI2_TUPDATE]);
> +
> + WRITE_ONCE(q->pi2_tupdate, convert_us_to_nsec(tupdate));
> + }
> +
> + if (tb[TCA_DUALPI2_ALPHA]) {
> + u32 alpha = nla_get_u32(tb[TCA_DUALPI2_ALPHA]);
> +
> + WRITE_ONCE(q->pi2_alpha, dualpi2_scale_alpha_beta(alpha));
> + }
> +
> + if (tb[TCA_DUALPI2_BETA]) {
> + u32 beta = nla_get_u32(tb[TCA_DUALPI2_BETA]);
> +
> + WRITE_ONCE(q->pi2_beta, dualpi2_scale_alpha_beta(beta));
> + }
> +
> + if (tb[TCA_DUALPI2_STEP_THRESH]) {
> + u32 step_th = nla_get_u32(tb[TCA_DUALPI2_STEP_THRESH]);
> + bool step_pkt = nla_get_flag(tb[TCA_DUALPI2_STEP_PACKETS]);
> +
> + WRITE_ONCE(q->step_in_packets, step_pkt);
> + WRITE_ONCE(q->step_thresh,
> + step_pkt ? step_th : convert_us_to_nsec(step_th));
> + }
I don't get the reason for all these WRITE_ONCE()s.
You lock the qdisc to make modifications, right?
And the block under which I'm responding is performing two dependent
writes, one to ->step_in_packets and the other to ->step_thresh
a change which is definitely not atomic..