This commit adds an srcu_tree.convert_to_big kernel parameter that either refuses to convert at all (0), converts immediately at init_srcu_struct() time (1), or lets rcutorture convert it (2). An addition contention-based dynamic conversion choice will be added, along with documentation. [ paulmck: Apply callback-scanning feedback from Neeraj Upadhyay. ]
Unified · Split
| @@ -5608,6 +5608,19 @@ · 5607 unmodified lines | ||
| 5608 | 5608 | off: Disable mitigation and remove |
| 5609 | 5609 | performance impact to RDRAND and RDSEED |
| 5610 | 5610 | |
| 5611 | + srcutree.convert_to_big [KNL] | |
| 5612 | + Specifies under what conditions an SRCU tree | |
| 5613 | + srcu_struct structure will be converted to big | |
| 5614 | + form, that is, with an rcu_node tree: | |
| 5615 | + | |
| 5616 | + 0: Never. | |
| 5617 | + 1: At init_srcu_struct() time. | |
| 5618 | + 2: When rcutorture decides to. | |
| 5619 | + | |
| 5620 | + Either way, the srcu_node tree will be sized based | |
| 5621 | + on the actual runtime number of CPUs (nr_cpu_ids) | |
| 5622 | + instead of the compile-time CONFIG_NR_CPUS. | |
| 5623 | + | |
| 5611 | 5624 | srcutree.counter_wrap_check [KNL] |
| 5612 | 5625 | Specifies how frequently to check for |
| 5613 | 5626 | grace-period sequence counter wrap for the |
| @@ -39,6 +39,15 @@ module_param(exp_holdoff, ulong, 0444); · 38 unmodified lines | ||
| 39 | 39 | static ulong counter_wrap_check = (ULONG_MAX >> 2); |
| 40 | 40 | module_param(counter_wrap_check, ulong, 0444); |
| 41 | 41 | |
| 42 | +/* | |
| 43 | + * Control conversion to SRCU_SIZE_BIG: | |
| 44 | + * 0: Don't convert at all (default). | |
| 45 | + * 1: Convert at init_srcu_struct() time. | |
| 46 | + * 2: Convert when rcutorture invokes srcu_torture_stats_print(). | |
| 47 | + */ | |
| 48 | +static int convert_to_big; | |
| 49 | +module_param(convert_to_big, int, 0444); | |
| 50 | + | |
| 42 | 51 | /* Early-boot callback-management, so early that no lock is required! */ |
| 43 | 52 | static LIST_HEAD(srcu_boot_list); |
| 44 | 53 | static bool __read_mostly srcu_init_done; |
| @@ -123,7 +132,7 @@ static inline bool srcu_invl_snp_seq(unsigned long s) · 78 unmodified lines | ||
| 123 | 132 | * Allocated and initialize SRCU combining tree. Returns @true if |
| 124 | 133 | * allocation succeeded and @false otherwise. |
| 125 | 134 | */ |
| 126 | −static bool init_srcu_struct_nodes(struct srcu_struct *ssp) | |
| 135 | +static bool init_srcu_struct_nodes(struct srcu_struct *ssp, gfp_t gfp_flags) | |
| 127 | 136 | { |
| 128 | 137 | int cpu; |
| 129 | 138 | int i; |
| @@ -135,7 +144,7 @@ static bool init_srcu_struct_nodes(struct srcu_struct *ssp) · 5 unmodified lines | ||
| 135 | 144 | |
| 136 | 145 | /* Initialize geometry if it has not already been initialized. */ |
| 137 | 146 | rcu_init_geometry(); |
| 138 | − ssp->node = kcalloc(rcu_num_nodes, sizeof(*ssp->node), GFP_ATOMIC); | |
| 147 | + ssp->node = kcalloc(rcu_num_nodes, sizeof(*ssp->node), gfp_flags); | |
| 139 | 148 | if (!ssp->node) |
| 140 | 149 | return false; |
| 141 | 150 | |
| @@ -213,17 +222,19 @@ static int init_srcu_struct_fields(struct srcu_struct *ssp, bool is_static) · 71 unmodified lines | ||
| 213 | 222 | if (!ssp->sda) |
| 214 | 223 | return -ENOMEM; |
| 215 | 224 | init_srcu_struct_data(ssp); |
| 216 | − if (!init_srcu_struct_nodes(ssp)) { | |
| 217 | − if (!is_static) { | |
| 218 | − free_percpu(ssp->sda); | |
| 219 | − ssp->sda = NULL; | |
| 220 | − return -ENOMEM; | |
| 221 | − } | |
| 222 | − } else { | |
| 223 | − ssp->srcu_size_state = SRCU_SIZE_BIG; | |
| 224 | − } | |
| 225 | 225 | ssp->srcu_gp_seq_needed_exp = 0; |
| 226 | 226 | ssp->srcu_last_gp_end = ktime_get_mono_fast_ns(); |
| 227 | + if (READ_ONCE(ssp->srcu_size_state) == SRCU_SIZE_SMALL && convert_to_big == 1) { | |
| 228 | + if (!init_srcu_struct_nodes(ssp, GFP_ATOMIC)) { | |
| 229 | + if (!is_static) { | |
| 230 | + free_percpu(ssp->sda); | |
| 231 | + ssp->sda = NULL; | |
| 232 | + return -ENOMEM; | |
| 233 | + } | |
| 234 | + } else { | |
| 235 | + WRITE_ONCE(ssp->srcu_size_state, SRCU_SIZE_BIG); | |
| 236 | + } | |
| 237 | + } | |
| 227 | 238 | smp_store_release(&ssp->srcu_gp_seq_needed, 0); /* Init done. */ |
| 228 | 239 | return 0; |
| 229 | 240 | } |
| @@ -594,7 +605,8 @@ static void srcu_gp_end(struct srcu_struct *ssp) · 364 unmodified lines | ||
| 594 | 605 | /* A new grace period can start at this point. But only one. */ |
| 595 | 606 | |
| 596 | 607 | /* Initiate callback invocation as needed. */ |
| 597 | − if (smp_load_acquire(&ssp->srcu_size_state) < SRCU_SIZE_WAIT_BARRIER) { | |
| 608 | + ss_state = smp_load_acquire(&ssp->srcu_size_state); | |
| 609 | + if (ss_state < SRCU_SIZE_WAIT_BARRIER) { | |
| 598 | 610 | srcu_schedule_cbs_sdp(per_cpu_ptr(ssp->sda, 0), cbdelay); |
| 599 | 611 | } else { |
| 600 | 612 | idx = rcu_seq_ctr(gpseq) % ARRAY_SIZE(snp->srcu_have_cbs); |
| @@ -603,13 +615,16 @@ static void srcu_gp_end(struct srcu_struct *ssp) · 2 unmodified lines | ||
| 603 | 615 | cbs = false; |
| 604 | 616 | last_lvl = snp >= ssp->level[rcu_num_lvls - 1]; |
| 605 | 617 | if (last_lvl) |
| 606 | − cbs = snp->srcu_have_cbs[idx] == gpseq; | |
| 618 | + cbs = ss_state < SRCU_SIZE_BIG || snp->srcu_have_cbs[idx] == gpseq; | |
| 607 | 619 | snp->srcu_have_cbs[idx] = gpseq; |
| 608 | 620 | rcu_seq_set_state(&snp->srcu_have_cbs[idx], 1); |
| 609 | 621 | sgsne = snp->srcu_gp_seq_needed_exp; |
| 610 | 622 | if (srcu_invl_snp_seq(sgsne) || ULONG_CMP_LT(sgsne, gpseq)) |
| 611 | 623 | WRITE_ONCE(snp->srcu_gp_seq_needed_exp, gpseq); |
| 612 | − mask = snp->srcu_data_have_cbs[idx]; | |
| 624 | + if (ss_state < SRCU_SIZE_BIG) | |
| 625 | + mask = ~0; | |
| 626 | + else | |
| 627 | + mask = snp->srcu_data_have_cbs[idx]; | |
| 613 | 628 | snp->srcu_data_have_cbs[idx] = 0; |
| 614 | 629 | spin_unlock_irq_rcu_node(snp); |
| 615 | 630 | if (cbs) |
| @@ -645,10 +660,9 @@ static void srcu_gp_end(struct srcu_struct *ssp) · 29 unmodified lines | ||
| 645 | 660 | } |
| 646 | 661 | |
| 647 | 662 | /* Transition to big if needed. */ |
| 648 | − ss_state = smp_load_acquire(&ssp->srcu_size_state); | |
| 649 | 663 | if (ss_state != SRCU_SIZE_SMALL && ss_state != SRCU_SIZE_BIG) { |
| 650 | 664 | if (ss_state == SRCU_SIZE_ALLOC) |
| 651 | − init_srcu_struct_nodes(ssp); | |
| 665 | + init_srcu_struct_nodes(ssp, GFP_KERNEL); | |
| 652 | 666 | else |
| 653 | 667 | smp_store_release(&ssp->srcu_size_state, ss_state + 1); |
| 654 | 668 | } |
| @@ -1494,6 +1508,8 @@ void srcu_torture_stats_print(struct srcu_struct *ssp, char *tt, char *tf) · 839 unmodified lines | ||
| 1494 | 1508 | s1 += c1; |
| 1495 | 1509 | } |
| 1496 | 1510 | pr_cont(" T(%ld,%ld)\n", s0, s1); |
| 1511 | + if (READ_ONCE(ssp->srcu_size_state) == SRCU_SIZE_SMALL && convert_to_big == 2) | |
| 1512 | + WRITE_ONCE(ssp->srcu_size_state, SRCU_SIZE_ALLOC); | |
| 1497 | 1513 | } |
| 1498 | 1514 | EXPORT_SYMBOL_GPL(srcu_torture_stats_print); |
| 1499 | 1515 | |