mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-04-29 17:53:58 +00:00
srcu: Specify auto-expedite holdoff time
On small systems, in the absence of readers, expedited SRCU grace periods can complete in less than a microsecond. This means that an eight-CPU system can have all CPUs doing synchronize_srcu() in a tight loop and almost always expedite. This might actually be desirable in some situations, but in general it is a good way to needlessly burn CPU cycles. And in those situations where it is desirable, your friend is the function synchronize_srcu_expedited(). For other situations, this commit adds a kernel parameter that specifies a holdoff between completing the last SRCU grace period and auto-expediting the next. If the next grace period starts before the holdoff expires, auto-expediting is disabled. The holdoff is 50 microseconds by default, and can be tuned to the desired number of nanoseconds. A value of zero disables auto-expediting. Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Tested-by: Mike Galbraith <efault@gmx.de>
This commit is contained in:
parent
2da4b2a7fd
commit
22607d66bb
3 changed files with 26 additions and 1 deletions
|
@ -3779,6 +3779,14 @@
|
||||||
spia_pedr=
|
spia_pedr=
|
||||||
spia_peddr=
|
spia_peddr=
|
||||||
|
|
||||||
|
srcutree.exp_holdoff [KNL]
|
||||||
|
Specifies how many nanoseconds must elapse
|
||||||
|
since the end of the last SRCU grace period for
|
||||||
|
a given srcu_struct until the next normal SRCU
|
||||||
|
grace period will be considered for automatic
|
||||||
|
expediting. Set to zero to disable automatic
|
||||||
|
expediting.
|
||||||
|
|
||||||
stacktrace [FTRACE]
|
stacktrace [FTRACE]
|
||||||
Enabled the stack tracer on boot up.
|
Enabled the stack tracer on boot up.
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,7 @@ struct srcu_struct {
|
||||||
unsigned long srcu_gp_seq; /* Grace-period seq #. */
|
unsigned long srcu_gp_seq; /* Grace-period seq #. */
|
||||||
unsigned long srcu_gp_seq_needed; /* Latest gp_seq needed. */
|
unsigned long srcu_gp_seq_needed; /* Latest gp_seq needed. */
|
||||||
unsigned long srcu_gp_seq_needed_exp; /* Furthest future exp GP. */
|
unsigned long srcu_gp_seq_needed_exp; /* Furthest future exp GP. */
|
||||||
|
unsigned long srcu_last_gp_end; /* Last GP end timestamp (ns) */
|
||||||
struct srcu_data __percpu *sda; /* Per-CPU srcu_data array. */
|
struct srcu_data __percpu *sda; /* Per-CPU srcu_data array. */
|
||||||
unsigned long srcu_barrier_seq; /* srcu_barrier seq #. */
|
unsigned long srcu_barrier_seq; /* srcu_barrier seq #. */
|
||||||
struct mutex srcu_barrier_mutex; /* Serialize barrier ops. */
|
struct mutex srcu_barrier_mutex; /* Serialize barrier ops. */
|
||||||
|
|
|
@ -34,10 +34,14 @@
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/smp.h>
|
#include <linux/smp.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/srcu.h>
|
#include <linux/srcu.h>
|
||||||
|
|
||||||
#include "rcu.h"
|
#include "rcu.h"
|
||||||
|
|
||||||
|
ulong exp_holdoff = 50 * 1000; /* Holdoff (ns) for auto-expediting. */
|
||||||
|
module_param(exp_holdoff, ulong, 0444);
|
||||||
|
|
||||||
static void srcu_invoke_callbacks(struct work_struct *work);
|
static void srcu_invoke_callbacks(struct work_struct *work);
|
||||||
static void srcu_reschedule(struct srcu_struct *sp, unsigned long delay);
|
static void srcu_reschedule(struct srcu_struct *sp, unsigned long delay);
|
||||||
|
|
||||||
|
@ -145,6 +149,7 @@ static int init_srcu_struct_fields(struct srcu_struct *sp, bool is_static)
|
||||||
sp->sda = alloc_percpu(struct srcu_data);
|
sp->sda = alloc_percpu(struct srcu_data);
|
||||||
init_srcu_struct_nodes(sp, is_static);
|
init_srcu_struct_nodes(sp, is_static);
|
||||||
sp->srcu_gp_seq_needed_exp = 0;
|
sp->srcu_gp_seq_needed_exp = 0;
|
||||||
|
sp->srcu_last_gp_end = ktime_get_mono_fast_ns();
|
||||||
smp_store_release(&sp->srcu_gp_seq_needed, 0); /* Init done. */
|
smp_store_release(&sp->srcu_gp_seq_needed, 0); /* Init done. */
|
||||||
return sp->sda ? 0 : -ENOMEM;
|
return sp->sda ? 0 : -ENOMEM;
|
||||||
}
|
}
|
||||||
|
@ -498,6 +503,7 @@ static void srcu_gp_end(struct srcu_struct *sp)
|
||||||
idx = rcu_seq_state(sp->srcu_gp_seq);
|
idx = rcu_seq_state(sp->srcu_gp_seq);
|
||||||
WARN_ON_ONCE(idx != SRCU_STATE_SCAN2);
|
WARN_ON_ONCE(idx != SRCU_STATE_SCAN2);
|
||||||
cbdelay = srcu_get_delay(sp);
|
cbdelay = srcu_get_delay(sp);
|
||||||
|
sp->srcu_last_gp_end = ktime_get_mono_fast_ns();
|
||||||
rcu_seq_end(&sp->srcu_gp_seq);
|
rcu_seq_end(&sp->srcu_gp_seq);
|
||||||
gpseq = rcu_seq_current(&sp->srcu_gp_seq);
|
gpseq = rcu_seq_current(&sp->srcu_gp_seq);
|
||||||
if (ULONG_CMP_LT(sp->srcu_gp_seq_needed_exp, gpseq))
|
if (ULONG_CMP_LT(sp->srcu_gp_seq_needed_exp, gpseq))
|
||||||
|
@ -700,9 +706,10 @@ static void srcu_flip(struct srcu_struct *sp)
|
||||||
*/
|
*/
|
||||||
static bool srcu_might_be_idle(struct srcu_struct *sp)
|
static bool srcu_might_be_idle(struct srcu_struct *sp)
|
||||||
{
|
{
|
||||||
|
unsigned long curseq;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
struct srcu_data *sdp;
|
struct srcu_data *sdp;
|
||||||
unsigned long curseq;
|
unsigned long t;
|
||||||
|
|
||||||
/* If the local srcu_data structure has callbacks, not idle. */
|
/* If the local srcu_data structure has callbacks, not idle. */
|
||||||
local_irq_save(flags);
|
local_irq_save(flags);
|
||||||
|
@ -718,6 +725,15 @@ static bool srcu_might_be_idle(struct srcu_struct *sp)
|
||||||
* Exact information would require acquiring locks, which would
|
* Exact information would require acquiring locks, which would
|
||||||
* kill scalability, hence the probabalistic nature of the probe.
|
* kill scalability, hence the probabalistic nature of the probe.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* First, see if enough time has passed since the last GP. */
|
||||||
|
t = ktime_get_mono_fast_ns();
|
||||||
|
if (exp_holdoff == 0 ||
|
||||||
|
time_in_range_open(t, sp->srcu_last_gp_end,
|
||||||
|
sp->srcu_last_gp_end + exp_holdoff))
|
||||||
|
return false; /* Too soon after last GP. */
|
||||||
|
|
||||||
|
/* Next, check for probable idleness. */
|
||||||
curseq = rcu_seq_current(&sp->srcu_gp_seq);
|
curseq = rcu_seq_current(&sp->srcu_gp_seq);
|
||||||
smp_mb(); /* Order ->srcu_gp_seq with ->srcu_gp_seq_needed. */
|
smp_mb(); /* Order ->srcu_gp_seq with ->srcu_gp_seq_needed. */
|
||||||
if (ULONG_CMP_LT(curseq, READ_ONCE(sp->srcu_gp_seq_needed)))
|
if (ULONG_CMP_LT(curseq, READ_ONCE(sp->srcu_gp_seq_needed)))
|
||||||
|
|
Loading…
Add table
Reference in a new issue