Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

net/sched: act_gate: snapshot parameters with RCU on replace

The gate action can be replaced while the hrtimer callback or dump path is
walking the schedule list.

Convert the parameters to an RCU-protected snapshot and swap updates under
tcf_lock, freeing the previous snapshot via call_rcu(). When REPLACE omits
the entry list, preserve the existing schedule so the effective state is
unchanged.

Fixes: a51c328df310 ("net: qos: introduce a gate control flow action")
Cc: stable@vger.kernel.org
Signed-off-by: Paul Moses <p@1g4.org>
Tested-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
Reviewed-by: Victor Nogueira <victor@mojatatu.com>
Link: https://patch.msgid.link/20260223150512.2251594-2-p@1g4.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Paul Moses and committed by
Jakub Kicinski
62413a9c be11a537

+213 -87
+26 -7
include/net/tc_act/tc_gate.h
··· 32 32 s32 tcfg_clockid; 33 33 size_t num_entries; 34 34 struct list_head entries; 35 + struct rcu_head rcu; 35 36 }; 36 37 37 38 #define GATE_ACT_GATE_OPEN BIT(0) ··· 40 39 41 40 struct tcf_gate { 42 41 struct tc_action common; 43 - struct tcf_gate_params param; 42 + struct tcf_gate_params __rcu *param; 44 43 u8 current_gate_status; 45 44 ktime_t current_close_time; 46 45 u32 current_entry_octets; ··· 52 51 53 52 #define to_gate(a) ((struct tcf_gate *)a) 54 53 54 + static inline struct tcf_gate_params *tcf_gate_params_locked(const struct tc_action *a) 55 + { 56 + struct tcf_gate *gact = to_gate(a); 57 + 58 + return rcu_dereference_protected(gact->param, 59 + lockdep_is_held(&gact->tcf_lock)); 60 + } 61 + 55 62 static inline s32 tcf_gate_prio(const struct tc_action *a) 56 63 { 64 + struct tcf_gate_params *p; 57 65 s32 tcfg_prio; 58 66 59 - tcfg_prio = to_gate(a)->param.tcfg_priority; 67 + p = tcf_gate_params_locked(a); 68 + tcfg_prio = p->tcfg_priority; 60 69 61 70 return tcfg_prio; 62 71 } 63 72 64 73 static inline u64 tcf_gate_basetime(const struct tc_action *a) 65 74 { 75 + struct tcf_gate_params *p; 66 76 u64 tcfg_basetime; 67 77 68 - tcfg_basetime = to_gate(a)->param.tcfg_basetime; 78 + p = tcf_gate_params_locked(a); 79 + tcfg_basetime = p->tcfg_basetime; 69 80 70 81 return tcfg_basetime; 71 82 } 72 83 73 84 static inline u64 tcf_gate_cycletime(const struct tc_action *a) 74 85 { 86 + struct tcf_gate_params *p; 75 87 u64 tcfg_cycletime; 76 88 77 - tcfg_cycletime = to_gate(a)->param.tcfg_cycletime; 89 + p = tcf_gate_params_locked(a); 90 + tcfg_cycletime = p->tcfg_cycletime; 78 91 79 92 return tcfg_cycletime; 80 93 } 81 94 82 95 static inline u64 tcf_gate_cycletimeext(const struct tc_action *a) 83 96 { 97 + struct tcf_gate_params *p; 84 98 u64 tcfg_cycletimeext; 85 99 86 - tcfg_cycletimeext = to_gate(a)->param.tcfg_cycletime_ext; 100 + p = tcf_gate_params_locked(a); 101 + tcfg_cycletimeext = p->tcfg_cycletime_ext; 87 102 88 103 return tcfg_cycletimeext; 89 104 } 90 105 91 106 static inline u32 tcf_gate_num_entries(const struct tc_action *a) 92 107 { 108 + struct tcf_gate_params *p; 93 109 u32 num_entries; 94 110 95 - num_entries = to_gate(a)->param.num_entries; 111 + p = tcf_gate_params_locked(a); 112 + num_entries = p->num_entries; 96 113 97 114 return num_entries; 98 115 } ··· 124 105 u32 num_entries; 125 106 int i = 0; 126 107 127 - p = &to_gate(a)->param; 108 + p = tcf_gate_params_locked(a); 128 109 num_entries = p->num_entries; 129 110 130 111 list_for_each_entry(entry, &p->entries, list)
+187 -80
net/sched/act_gate.c
··· 32 32 return KTIME_MAX; 33 33 } 34 34 35 - static void gate_get_start_time(struct tcf_gate *gact, ktime_t *start) 35 + static void tcf_gate_params_free_rcu(struct rcu_head *head); 36 + 37 + static void gate_get_start_time(struct tcf_gate *gact, 38 + const struct tcf_gate_params *param, 39 + ktime_t *start) 36 40 { 37 - struct tcf_gate_params *param = &gact->param; 38 41 ktime_t now, base, cycle; 39 42 u64 n; 40 43 ··· 72 69 { 73 70 struct tcf_gate *gact = container_of(timer, struct tcf_gate, 74 71 hitimer); 75 - struct tcf_gate_params *p = &gact->param; 76 72 struct tcfg_gate_entry *next; 73 + struct tcf_gate_params *p; 77 74 ktime_t close_time, now; 78 75 79 76 spin_lock(&gact->tcf_lock); 80 77 78 + p = rcu_dereference_protected(gact->param, 79 + lockdep_is_held(&gact->tcf_lock)); 81 80 next = gact->next_entry; 82 81 83 82 /* cycle start, clear pending bit, clear total octets */ ··· 230 225 } 231 226 } 232 227 228 + static int tcf_gate_copy_entries(struct tcf_gate_params *dst, 229 + const struct tcf_gate_params *src, 230 + struct netlink_ext_ack *extack) 231 + { 232 + struct tcfg_gate_entry *entry; 233 + int i = 0; 234 + 235 + list_for_each_entry(entry, &src->entries, list) { 236 + struct tcfg_gate_entry *new; 237 + 238 + new = kzalloc(sizeof(*new), GFP_ATOMIC); 239 + if (!new) { 240 + NL_SET_ERR_MSG(extack, "Not enough memory for entry"); 241 + return -ENOMEM; 242 + } 243 + 244 + new->index = entry->index; 245 + new->gate_state = entry->gate_state; 246 + new->interval = entry->interval; 247 + new->ipv = entry->ipv; 248 + new->maxoctets = entry->maxoctets; 249 + list_add_tail(&new->list, &dst->entries); 250 + i++; 251 + } 252 + 253 + dst->num_entries = i; 254 + return 0; 255 + } 256 + 233 257 static int parse_gate_list(struct nlattr *list_attr, 234 258 struct tcf_gate_params *sched, 235 259 struct netlink_ext_ack *extack) ··· 304 270 return err; 305 271 } 306 272 307 - static void gate_setup_timer(struct tcf_gate *gact, u64 basetime, 308 - enum tk_offsets tko, s32 clockid, 309 - bool do_init) 273 + static bool gate_timer_needs_cancel(u64 basetime, u64 old_basetime, 274 + enum tk_offsets tko, 275 + enum tk_offsets old_tko, 276 + s32 clockid, s32 old_clockid) 310 277 { 311 - if (!do_init) { 312 - if (basetime == gact->param.tcfg_basetime && 313 - tko == gact->tk_offset && 314 - clockid == gact->param.tcfg_clockid) 315 - return; 278 + return basetime != old_basetime || 279 + clockid != old_clockid || 280 + tko != old_tko; 281 + } 316 282 317 - spin_unlock_bh(&gact->tcf_lock); 318 - hrtimer_cancel(&gact->hitimer); 319 - spin_lock_bh(&gact->tcf_lock); 283 + static int gate_clock_resolve(s32 clockid, enum tk_offsets *tko, 284 + struct netlink_ext_ack *extack) 285 + { 286 + switch (clockid) { 287 + case CLOCK_REALTIME: 288 + *tko = TK_OFFS_REAL; 289 + return 0; 290 + case CLOCK_MONOTONIC: 291 + *tko = TK_OFFS_MAX; 292 + return 0; 293 + case CLOCK_BOOTTIME: 294 + *tko = TK_OFFS_BOOT; 295 + return 0; 296 + case CLOCK_TAI: 297 + *tko = TK_OFFS_TAI; 298 + return 0; 299 + default: 300 + NL_SET_ERR_MSG(extack, "Invalid 'clockid'"); 301 + return -EINVAL; 320 302 } 321 - gact->param.tcfg_basetime = basetime; 322 - gact->param.tcfg_clockid = clockid; 323 - gact->tk_offset = tko; 324 - hrtimer_setup(&gact->hitimer, gate_timer_func, clockid, HRTIMER_MODE_ABS_SOFT); 303 + } 304 + 305 + static void gate_setup_timer(struct tcf_gate *gact, s32 clockid, 306 + enum tk_offsets tko) 307 + { 308 + WRITE_ONCE(gact->tk_offset, tko); 309 + hrtimer_setup(&gact->hitimer, gate_timer_func, clockid, 310 + HRTIMER_MODE_ABS_SOFT); 325 311 } 326 312 327 313 static int tcf_gate_init(struct net *net, struct nlattr *nla, ··· 350 296 struct netlink_ext_ack *extack) 351 297 { 352 298 struct tc_action_net *tn = net_generic(net, act_gate_ops.net_id); 353 - enum tk_offsets tk_offset = TK_OFFS_TAI; 299 + u64 cycletime = 0, basetime = 0, cycletime_ext = 0; 300 + struct tcf_gate_params *p = NULL, *old_p = NULL; 301 + enum tk_offsets old_tk_offset = TK_OFFS_TAI; 302 + const struct tcf_gate_params *cur_p = NULL; 354 303 bool bind = flags & TCA_ACT_FLAGS_BIND; 355 304 struct nlattr *tb[TCA_GATE_MAX + 1]; 305 + enum tk_offsets tko = TK_OFFS_TAI; 356 306 struct tcf_chain *goto_ch = NULL; 357 - u64 cycletime = 0, basetime = 0; 358 - struct tcf_gate_params *p; 307 + s32 timer_clockid = CLOCK_TAI; 308 + bool use_old_entries = false; 309 + s32 old_clockid = CLOCK_TAI; 310 + bool need_cancel = false; 359 311 s32 clockid = CLOCK_TAI; 360 312 struct tcf_gate *gact; 361 313 struct tc_gate *parm; 314 + u64 old_basetime = 0; 362 315 int ret = 0, err; 363 316 u32 gflags = 0; 364 317 s32 prio = -1; ··· 382 321 if (!tb[TCA_GATE_PARMS]) 383 322 return -EINVAL; 384 323 385 - if (tb[TCA_GATE_CLOCKID]) { 324 + if (tb[TCA_GATE_CLOCKID]) 386 325 clockid = nla_get_s32(tb[TCA_GATE_CLOCKID]); 387 - switch (clockid) { 388 - case CLOCK_REALTIME: 389 - tk_offset = TK_OFFS_REAL; 390 - break; 391 - case CLOCK_MONOTONIC: 392 - tk_offset = TK_OFFS_MAX; 393 - break; 394 - case CLOCK_BOOTTIME: 395 - tk_offset = TK_OFFS_BOOT; 396 - break; 397 - case CLOCK_TAI: 398 - tk_offset = TK_OFFS_TAI; 399 - break; 400 - default: 401 - NL_SET_ERR_MSG(extack, "Invalid 'clockid'"); 402 - return -EINVAL; 403 - } 404 - } 405 326 406 327 parm = nla_data(tb[TCA_GATE_PARMS]); 407 328 index = parm->index; ··· 409 366 return -EEXIST; 410 367 } 411 368 369 + gact = to_gate(*a); 370 + 371 + err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); 372 + if (err < 0) 373 + goto release_idr; 374 + 375 + p = kzalloc(sizeof(*p), GFP_KERNEL); 376 + if (!p) { 377 + err = -ENOMEM; 378 + goto chain_put; 379 + } 380 + INIT_LIST_HEAD(&p->entries); 381 + 382 + use_old_entries = !tb[TCA_GATE_ENTRY_LIST]; 383 + if (!use_old_entries) { 384 + err = parse_gate_list(tb[TCA_GATE_ENTRY_LIST], p, extack); 385 + if (err < 0) 386 + goto err_free; 387 + use_old_entries = !err; 388 + } 389 + 390 + if (ret == ACT_P_CREATED && use_old_entries) { 391 + NL_SET_ERR_MSG(extack, "The entry list is empty"); 392 + err = -EINVAL; 393 + goto err_free; 394 + } 395 + 396 + if (ret != ACT_P_CREATED) { 397 + rcu_read_lock(); 398 + cur_p = rcu_dereference(gact->param); 399 + 400 + old_basetime = cur_p->tcfg_basetime; 401 + old_clockid = cur_p->tcfg_clockid; 402 + old_tk_offset = READ_ONCE(gact->tk_offset); 403 + 404 + basetime = old_basetime; 405 + cycletime_ext = cur_p->tcfg_cycletime_ext; 406 + prio = cur_p->tcfg_priority; 407 + gflags = cur_p->tcfg_flags; 408 + 409 + if (!tb[TCA_GATE_CLOCKID]) 410 + clockid = old_clockid; 411 + 412 + err = 0; 413 + if (use_old_entries) { 414 + err = tcf_gate_copy_entries(p, cur_p, extack); 415 + if (!err && !tb[TCA_GATE_CYCLE_TIME]) 416 + cycletime = cur_p->tcfg_cycletime; 417 + } 418 + rcu_read_unlock(); 419 + if (err) 420 + goto err_free; 421 + } 422 + 412 423 if (tb[TCA_GATE_PRIORITY]) 413 424 prio = nla_get_s32(tb[TCA_GATE_PRIORITY]); 414 425 ··· 472 375 if (tb[TCA_GATE_FLAGS]) 473 376 gflags = nla_get_u32(tb[TCA_GATE_FLAGS]); 474 377 475 - gact = to_gate(*a); 476 - if (ret == ACT_P_CREATED) 477 - INIT_LIST_HEAD(&gact->param.entries); 478 - 479 - err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); 480 - if (err < 0) 481 - goto release_idr; 482 - 483 - spin_lock_bh(&gact->tcf_lock); 484 - p = &gact->param; 485 - 486 378 if (tb[TCA_GATE_CYCLE_TIME]) 487 379 cycletime = nla_get_u64(tb[TCA_GATE_CYCLE_TIME]); 488 380 489 - if (tb[TCA_GATE_ENTRY_LIST]) { 490 - err = parse_gate_list(tb[TCA_GATE_ENTRY_LIST], p, extack); 491 - if (err < 0) 492 - goto chain_put; 493 - } 381 + if (tb[TCA_GATE_CYCLE_TIME_EXT]) 382 + cycletime_ext = nla_get_u64(tb[TCA_GATE_CYCLE_TIME_EXT]); 383 + 384 + err = gate_clock_resolve(clockid, &tko, extack); 385 + if (err) 386 + goto err_free; 387 + timer_clockid = clockid; 388 + 389 + need_cancel = ret != ACT_P_CREATED && 390 + gate_timer_needs_cancel(basetime, old_basetime, 391 + tko, old_tk_offset, 392 + timer_clockid, old_clockid); 393 + 394 + if (need_cancel) 395 + hrtimer_cancel(&gact->hitimer); 396 + 397 + spin_lock_bh(&gact->tcf_lock); 494 398 495 399 if (!cycletime) { 496 400 struct tcfg_gate_entry *entry; ··· 500 402 list_for_each_entry(entry, &p->entries, list) 501 403 cycle = ktime_add_ns(cycle, entry->interval); 502 404 cycletime = cycle; 503 - if (!cycletime) { 504 - err = -EINVAL; 505 - goto chain_put; 506 - } 507 405 } 508 406 p->tcfg_cycletime = cycletime; 407 + p->tcfg_cycletime_ext = cycletime_ext; 509 408 510 - if (tb[TCA_GATE_CYCLE_TIME_EXT]) 511 - p->tcfg_cycletime_ext = 512 - nla_get_u64(tb[TCA_GATE_CYCLE_TIME_EXT]); 513 - 514 - gate_setup_timer(gact, basetime, tk_offset, clockid, 515 - ret == ACT_P_CREATED); 409 + if (need_cancel || ret == ACT_P_CREATED) 410 + gate_setup_timer(gact, timer_clockid, tko); 516 411 p->tcfg_priority = prio; 517 412 p->tcfg_flags = gflags; 518 - gate_get_start_time(gact, &start); 413 + p->tcfg_basetime = basetime; 414 + p->tcfg_clockid = timer_clockid; 415 + gate_get_start_time(gact, p, &start); 416 + 417 + old_p = rcu_replace_pointer(gact->param, p, 418 + lockdep_is_held(&gact->tcf_lock)); 519 419 520 420 gact->current_close_time = start; 521 421 gact->current_gate_status = GATE_ACT_GATE_OPEN | GATE_ACT_PENDING; ··· 530 434 if (goto_ch) 531 435 tcf_chain_put_by_act(goto_ch); 532 436 437 + if (old_p) 438 + call_rcu(&old_p->rcu, tcf_gate_params_free_rcu); 439 + 533 440 return ret; 534 441 442 + err_free: 443 + release_entry_list(&p->entries); 444 + kfree(p); 535 445 chain_put: 536 - spin_unlock_bh(&gact->tcf_lock); 537 - 538 446 if (goto_ch) 539 447 tcf_chain_put_by_act(goto_ch); 540 448 release_idr: ··· 546 446 * without taking tcf_lock. 547 447 */ 548 448 if (ret == ACT_P_CREATED) 549 - gate_setup_timer(gact, gact->param.tcfg_basetime, 550 - gact->tk_offset, gact->param.tcfg_clockid, 551 - true); 449 + gate_setup_timer(gact, timer_clockid, tko); 450 + 552 451 tcf_idr_release(*a, bind); 553 452 return err; 453 + } 454 + 455 + static void tcf_gate_params_free_rcu(struct rcu_head *head) 456 + { 457 + struct tcf_gate_params *p = container_of(head, struct tcf_gate_params, rcu); 458 + 459 + release_entry_list(&p->entries); 460 + kfree(p); 554 461 } 555 462 556 463 static void tcf_gate_cleanup(struct tc_action *a) ··· 565 458 struct tcf_gate *gact = to_gate(a); 566 459 struct tcf_gate_params *p; 567 460 568 - p = &gact->param; 569 461 hrtimer_cancel(&gact->hitimer); 570 - release_entry_list(&p->entries); 462 + p = rcu_dereference_protected(gact->param, 1); 463 + if (p) 464 + call_rcu(&p->rcu, tcf_gate_params_free_rcu); 571 465 } 572 466 573 467 static int dumping_entry(struct sk_buff *skb, ··· 617 509 struct nlattr *entry_list; 618 510 struct tcf_t t; 619 511 620 - spin_lock_bh(&gact->tcf_lock); 621 - opt.action = gact->tcf_action; 622 - 623 - p = &gact->param; 512 + rcu_read_lock(); 513 + opt.action = READ_ONCE(gact->tcf_action); 514 + p = rcu_dereference(gact->param); 624 515 625 516 if (nla_put(skb, TCA_GATE_PARMS, sizeof(opt), &opt)) 626 517 goto nla_put_failure; ··· 659 552 tcf_tm_dump(&t, &gact->tcf_tm); 660 553 if (nla_put_64bit(skb, TCA_GATE_TM, sizeof(t), &t, TCA_GATE_PAD)) 661 554 goto nla_put_failure; 662 - spin_unlock_bh(&gact->tcf_lock); 555 + rcu_read_unlock(); 663 556 664 557 return skb->len; 665 558 666 559 nla_put_failure: 667 - spin_unlock_bh(&gact->tcf_lock); 560 + rcu_read_unlock(); 668 561 nlmsg_trim(skb, b); 669 562 return -1; 670 563 }