|
@@ -58,6 +58,7 @@ torture_param(int, fqs_duration, 0,
|
|
|
"Duration of fqs bursts (us), 0 to disable");
|
|
|
torture_param(int, fqs_holdoff, 0, "Holdoff time within fqs bursts (us)");
|
|
|
torture_param(int, fqs_stutter, 3, "Wait time between fqs bursts (s)");
|
|
|
+torture_param(bool, gp_cond, false, "Use conditional/async GP wait primitives");
|
|
|
torture_param(bool, gp_exp, false, "Use expedited GP wait primitives");
|
|
|
torture_param(bool, gp_normal, false,
|
|
|
"Use normal (non-expedited) GP wait primitives");
|
|
@@ -144,8 +145,10 @@ static int rcu_torture_writer_state;
|
|
|
#define RTWS_REPLACE 2
|
|
|
#define RTWS_DEF_FREE 3
|
|
|
#define RTWS_EXP_SYNC 4
|
|
|
-#define RTWS_STUTTER 5
|
|
|
-#define RTWS_STOPPING 6
|
|
|
+#define RTWS_COND_GET 5
|
|
|
+#define RTWS_COND_SYNC 6
|
|
|
+#define RTWS_STUTTER 7
|
|
|
+#define RTWS_STOPPING 8
|
|
|
|
|
|
#if defined(MODULE) || defined(CONFIG_RCU_TORTURE_TEST_RUNNABLE)
|
|
|
#define RCUTORTURE_RUNNABLE_INIT 1
|
|
@@ -232,6 +235,8 @@ struct rcu_torture_ops {
|
|
|
void (*deferred_free)(struct rcu_torture *p);
|
|
|
void (*sync)(void);
|
|
|
void (*exp_sync)(void);
|
|
|
+ unsigned long (*get_state)(void);
|
|
|
+ void (*cond_sync)(unsigned long oldstate);
|
|
|
void (*call)(struct rcu_head *head, void (*func)(struct rcu_head *rcu));
|
|
|
void (*cb_barrier)(void);
|
|
|
void (*fqs)(void);
|
|
@@ -283,10 +288,48 @@ static int rcu_torture_completed(void)
|
|
|
return rcu_batches_completed();
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Update callback in the pipe. This should be invoked after a grace period.
|
|
|
+ */
|
|
|
+static bool
|
|
|
+rcu_torture_pipe_update_one(struct rcu_torture *rp)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ i = rp->rtort_pipe_count;
|
|
|
+ if (i > RCU_TORTURE_PIPE_LEN)
|
|
|
+ i = RCU_TORTURE_PIPE_LEN;
|
|
|
+ atomic_inc(&rcu_torture_wcount[i]);
|
|
|
+ if (++rp->rtort_pipe_count >= RCU_TORTURE_PIPE_LEN) {
|
|
|
+ rp->rtort_mbtest = 0;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Update all callbacks in the pipe. Suitable for synchronous grace-period
|
|
|
+ * primitives.
|
|
|
+ */
|
|
|
+static void
|
|
|
+rcu_torture_pipe_update(struct rcu_torture *old_rp)
|
|
|
+{
|
|
|
+ struct rcu_torture *rp;
|
|
|
+ struct rcu_torture *rp1;
|
|
|
+
|
|
|
+ if (old_rp)
|
|
|
+ list_add(&old_rp->rtort_free, &rcu_torture_removed);
|
|
|
+ list_for_each_entry_safe(rp, rp1, &rcu_torture_removed, rtort_free) {
|
|
|
+ if (rcu_torture_pipe_update_one(rp)) {
|
|
|
+ list_del(&rp->rtort_free);
|
|
|
+ rcu_torture_free(rp);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static void
|
|
|
rcu_torture_cb(struct rcu_head *p)
|
|
|
{
|
|
|
- int i;
|
|
|
struct rcu_torture *rp = container_of(p, struct rcu_torture, rtort_rcu);
|
|
|
|
|
|
if (torture_must_stop_irq()) {
|
|
@@ -294,16 +337,10 @@ rcu_torture_cb(struct rcu_head *p)
|
|
|
/* The next initialization will pick up the pieces. */
|
|
|
return;
|
|
|
}
|
|
|
- i = rp->rtort_pipe_count;
|
|
|
- if (i > RCU_TORTURE_PIPE_LEN)
|
|
|
- i = RCU_TORTURE_PIPE_LEN;
|
|
|
- atomic_inc(&rcu_torture_wcount[i]);
|
|
|
- if (++rp->rtort_pipe_count >= RCU_TORTURE_PIPE_LEN) {
|
|
|
- rp->rtort_mbtest = 0;
|
|
|
+ if (rcu_torture_pipe_update_one(rp))
|
|
|
rcu_torture_free(rp);
|
|
|
- } else {
|
|
|
+ else
|
|
|
cur_ops->deferred_free(rp);
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
static int rcu_no_completed(void)
|
|
@@ -331,6 +368,8 @@ static struct rcu_torture_ops rcu_ops = {
|
|
|
.deferred_free = rcu_torture_deferred_free,
|
|
|
.sync = synchronize_rcu,
|
|
|
.exp_sync = synchronize_rcu_expedited,
|
|
|
+ .get_state = get_state_synchronize_rcu,
|
|
|
+ .cond_sync = cond_synchronize_rcu,
|
|
|
.call = call_rcu,
|
|
|
.cb_barrier = rcu_barrier,
|
|
|
.fqs = rcu_force_quiescent_state,
|
|
@@ -705,16 +744,39 @@ rcu_torture_fqs(void *arg)
|
|
|
static int
|
|
|
rcu_torture_writer(void *arg)
|
|
|
{
|
|
|
- bool exp;
|
|
|
+ unsigned long gp_snap;
|
|
|
+ bool gp_cond1 = gp_cond, gp_exp1 = gp_exp, gp_normal1 = gp_normal;
|
|
|
int i;
|
|
|
struct rcu_torture *rp;
|
|
|
- struct rcu_torture *rp1;
|
|
|
struct rcu_torture *old_rp;
|
|
|
static DEFINE_TORTURE_RANDOM(rand);
|
|
|
+ int synctype[] = { RTWS_DEF_FREE, RTWS_EXP_SYNC, RTWS_COND_GET };
|
|
|
+ int nsynctypes = 0;
|
|
|
|
|
|
VERBOSE_TOROUT_STRING("rcu_torture_writer task started");
|
|
|
set_user_nice(current, MAX_NICE);
|
|
|
|
|
|
+ /* Initialize synctype[] array. If none set, take default. */
|
|
|
+ if (!gp_cond1 && !gp_exp1 && !gp_normal1)
|
|
|
+ gp_cond1 = gp_exp1 = gp_normal1 = true;
|
|
|
+ if (gp_cond1 && cur_ops->get_state && cur_ops->cond_sync)
|
|
|
+ synctype[nsynctypes++] = RTWS_COND_GET;
|
|
|
+ else if (gp_cond && (!cur_ops->get_state || !cur_ops->cond_sync))
|
|
|
+ pr_alert("rcu_torture_writer: gp_cond without primitives.\n");
|
|
|
+ if (gp_exp1 && cur_ops->exp_sync)
|
|
|
+ synctype[nsynctypes++] = RTWS_EXP_SYNC;
|
|
|
+ else if (gp_exp && !cur_ops->exp_sync)
|
|
|
+ pr_alert("rcu_torture_writer: gp_exp without primitives.\n");
|
|
|
+ if (gp_normal1 && cur_ops->deferred_free)
|
|
|
+ synctype[nsynctypes++] = RTWS_DEF_FREE;
|
|
|
+ else if (gp_normal && !cur_ops->deferred_free)
|
|
|
+ pr_alert("rcu_torture_writer: gp_normal without primitives.\n");
|
|
|
+ if (WARN_ONCE(nsynctypes == 0,
|
|
|
+ "rcu_torture_writer: No update-side primitives.\n")) {
|
|
|
+ rcu_torture_writer_state = RTWS_STOPPING;
|
|
|
+ torture_kthread_stopping("rcu_torture_writer");
|
|
|
+ }
|
|
|
+
|
|
|
do {
|
|
|
rcu_torture_writer_state = RTWS_FIXED_DELAY;
|
|
|
schedule_timeout_uninterruptible(1);
|
|
@@ -736,32 +798,30 @@ rcu_torture_writer(void *arg)
|
|
|
i = RCU_TORTURE_PIPE_LEN;
|
|
|
atomic_inc(&rcu_torture_wcount[i]);
|
|
|
old_rp->rtort_pipe_count++;
|
|
|
- if (gp_normal == gp_exp)
|
|
|
- exp = !!(torture_random(&rand) & 0x80);
|
|
|
- else
|
|
|
- exp = gp_exp;
|
|
|
- if (!exp) {
|
|
|
+ switch (synctype[torture_random(&rand) % nsynctypes]) {
|
|
|
+ case RTWS_DEF_FREE:
|
|
|
rcu_torture_writer_state = RTWS_DEF_FREE;
|
|
|
cur_ops->deferred_free(old_rp);
|
|
|
- } else {
|
|
|
+ break;
|
|
|
+ case RTWS_EXP_SYNC:
|
|
|
rcu_torture_writer_state = RTWS_EXP_SYNC;
|
|
|
cur_ops->exp_sync();
|
|
|
- list_add(&old_rp->rtort_free,
|
|
|
- &rcu_torture_removed);
|
|
|
- list_for_each_entry_safe(rp, rp1,
|
|
|
- &rcu_torture_removed,
|
|
|
- rtort_free) {
|
|
|
- i = rp->rtort_pipe_count;
|
|
|
- if (i > RCU_TORTURE_PIPE_LEN)
|
|
|
- i = RCU_TORTURE_PIPE_LEN;
|
|
|
- atomic_inc(&rcu_torture_wcount[i]);
|
|
|
- if (++rp->rtort_pipe_count >=
|
|
|
- RCU_TORTURE_PIPE_LEN) {
|
|
|
- rp->rtort_mbtest = 0;
|
|
|
- list_del(&rp->rtort_free);
|
|
|
- rcu_torture_free(rp);
|
|
|
- }
|
|
|
- }
|
|
|
+ rcu_torture_pipe_update(old_rp);
|
|
|
+ break;
|
|
|
+ case RTWS_COND_GET:
|
|
|
+ rcu_torture_writer_state = RTWS_COND_GET;
|
|
|
+ gp_snap = cur_ops->get_state();
|
|
|
+ i = torture_random(&rand) % 16;
|
|
|
+ if (i != 0)
|
|
|
+ schedule_timeout_interruptible(i);
|
|
|
+ udelay(torture_random(&rand) % 1000);
|
|
|
+ rcu_torture_writer_state = RTWS_COND_SYNC;
|
|
|
+ cur_ops->cond_sync(gp_snap);
|
|
|
+ rcu_torture_pipe_update(old_rp);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ WARN_ON_ONCE(1);
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
rcutorture_record_progress(++rcu_torture_current_version);
|