mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 10:00:03 -04:00
rcu: Enable RCU callbacks to benefit from expedited grace periods
Currently, RCU callbacks only track normal grace-period sequence numbers. This means callbacks must wait for normal grace periods to complete even when expedited grace periods have already elapsed. Use the full struct rcu_gp_seq (which tracks both the normal and expedited grace-period sequences) throughout the callback infrastructure. rcu_segcblist_advance() now checks both normal and expedited GP completion via poll_state_synchronize_rcu_full(), and becomes parameterless since it reads the grace-period state internally. rcu_segcblist_accelerate() stores the full state (both sequences) instead of just the normal one. rcu_accelerate_cbs() and rcu_accelerate_cbs_unlocked() use get_state_synchronize_rcu_full() to capture both sequences, and the NOCB advance checks use poll_state_synchronize_rcu_full() instead of comparing only the normal sequence. srcu_segcblist_advance() becomes a standalone implementation because it compares SRCU sequences directly and cannot use poll_state_synchronize_rcu_full(), which reads RCU-specific globals. srcu_segcblist_accelerate() sets the ->exp field to RCU_GET_STATE_NOT_TRACKED so that poll_state_synchronize_rcu_full() compares only ->norm and ignores ->exp. Signed-off-by: Puranjay Mohan <puranjay@kernel.org> Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
This commit is contained in:
committed by
Paul E. McKenney
parent
60b54875bd
commit
aaca360ebb
@@ -12,6 +12,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "rcu.h"
|
||||
#include "rcu_segcblist.h"
|
||||
|
||||
/* Initialize simple callback list. */
|
||||
@@ -494,9 +495,9 @@ static void rcu_segcblist_advance_compact(struct rcu_segcblist *rsclp, int i)
|
||||
|
||||
/*
|
||||
* Advance the callbacks in the specified rcu_segcblist structure based
|
||||
* on the current value passed in for the grace-period counter.
|
||||
* on the current value of the grace-period counter.
|
||||
*/
|
||||
void rcu_segcblist_advance(struct rcu_segcblist *rsclp, struct rcu_gp_seq *gsp)
|
||||
void rcu_segcblist_advance(struct rcu_segcblist *rsclp)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -509,7 +510,7 @@ void rcu_segcblist_advance(struct rcu_segcblist *rsclp, struct rcu_gp_seq *gsp)
|
||||
* are ready to invoke, and put them into the RCU_DONE_TAIL segment.
|
||||
*/
|
||||
for (i = RCU_WAIT_TAIL; i < RCU_NEXT_TAIL; i++) {
|
||||
if (ULONG_CMP_LT(gsp->norm, rsclp->gp_seq[i].norm))
|
||||
if (!poll_state_synchronize_rcu_full(&rsclp->gp_seq[i]))
|
||||
break;
|
||||
WRITE_ONCE(rsclp->tails[RCU_DONE_TAIL], rsclp->tails[i]);
|
||||
rcu_segcblist_move_seglen(rsclp, i, RCU_DONE_TAIL);
|
||||
@@ -595,7 +596,7 @@ bool rcu_segcblist_accelerate(struct rcu_segcblist *rsclp, struct rcu_gp_seq *gs
|
||||
*/
|
||||
for (; i < RCU_NEXT_TAIL; i++) {
|
||||
WRITE_ONCE(rsclp->tails[i], rsclp->tails[RCU_NEXT_TAIL]);
|
||||
rsclp->gp_seq[i].norm = gsp->norm;
|
||||
rsclp->gp_seq[i] = *gsp;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -637,14 +638,29 @@ void rcu_segcblist_merge(struct rcu_segcblist *dst_rsclp,
|
||||
|
||||
void srcu_segcblist_advance(struct rcu_segcblist *rsclp, unsigned long seq)
|
||||
{
|
||||
struct rcu_gp_seq gs = { .norm = seq };
|
||||
int i;
|
||||
|
||||
rcu_segcblist_advance(rsclp, &gs);
|
||||
WARN_ON_ONCE(!rcu_segcblist_is_enabled(rsclp));
|
||||
if (rcu_segcblist_restempty(rsclp, RCU_DONE_TAIL))
|
||||
return;
|
||||
|
||||
for (i = RCU_WAIT_TAIL; i < RCU_NEXT_TAIL; i++) {
|
||||
if (ULONG_CMP_LT(seq, rsclp->gp_seq[i].norm))
|
||||
break;
|
||||
WRITE_ONCE(rsclp->tails[RCU_DONE_TAIL], rsclp->tails[i]);
|
||||
rcu_segcblist_move_seglen(rsclp, i, RCU_DONE_TAIL);
|
||||
}
|
||||
|
||||
/* If no callbacks moved, nothing more need be done. */
|
||||
if (i == RCU_WAIT_TAIL)
|
||||
return;
|
||||
|
||||
rcu_segcblist_advance_compact(rsclp, i);
|
||||
}
|
||||
|
||||
bool srcu_segcblist_accelerate(struct rcu_segcblist *rsclp, unsigned long seq)
|
||||
{
|
||||
struct rcu_gp_seq gs = { .norm = seq };
|
||||
struct rcu_gp_seq gs = { .norm = seq, .exp = RCU_GET_STATE_NOT_TRACKED };
|
||||
|
||||
return rcu_segcblist_accelerate(rsclp, &gs);
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ void rcu_segcblist_insert_done_cbs(struct rcu_segcblist *rsclp,
|
||||
struct rcu_cblist *rclp);
|
||||
void rcu_segcblist_insert_pend_cbs(struct rcu_segcblist *rsclp,
|
||||
struct rcu_cblist *rclp);
|
||||
void rcu_segcblist_advance(struct rcu_segcblist *rsclp, struct rcu_gp_seq *gsp);
|
||||
void rcu_segcblist_advance(struct rcu_segcblist *rsclp);
|
||||
bool rcu_segcblist_accelerate(struct rcu_segcblist *rsclp, struct rcu_gp_seq *gsp);
|
||||
void rcu_segcblist_merge(struct rcu_segcblist *dst_rsclp,
|
||||
struct rcu_segcblist *src_rsclp);
|
||||
|
||||
@@ -1159,7 +1159,7 @@ static bool rcu_accelerate_cbs(struct rcu_node *rnp, struct rcu_data *rdp)
|
||||
* accelerating callback invocation to an earlier grace-period
|
||||
* number.
|
||||
*/
|
||||
gs.norm = rcu_seq_snap(&rcu_state.gp_seq);
|
||||
get_state_synchronize_rcu_full(&gs);
|
||||
if (rcu_segcblist_accelerate(&rdp->cblist, &gs))
|
||||
ret = rcu_start_this_gp(rnp, rdp, gs.norm);
|
||||
|
||||
@@ -1188,7 +1188,7 @@ static void rcu_accelerate_cbs_unlocked(struct rcu_node *rnp,
|
||||
bool needwake;
|
||||
|
||||
rcu_lockdep_assert_cblist_protected(rdp);
|
||||
gs.norm = rcu_seq_snap(&rcu_state.gp_seq);
|
||||
get_state_synchronize_rcu_full(&gs);
|
||||
if (!READ_ONCE(rdp->gpwrap) && ULONG_CMP_GE(rdp->gp_seq_needed, gs.norm)) {
|
||||
/* Old request still live, so mark recent callbacks. */
|
||||
(void)rcu_segcblist_accelerate(&rdp->cblist, &gs);
|
||||
@@ -1213,8 +1213,6 @@ static void rcu_accelerate_cbs_unlocked(struct rcu_node *rnp,
|
||||
*/
|
||||
static bool rcu_advance_cbs(struct rcu_node *rnp, struct rcu_data *rdp)
|
||||
{
|
||||
struct rcu_gp_seq gs;
|
||||
|
||||
rcu_lockdep_assert_cblist_protected(rdp);
|
||||
raw_lockdep_assert_held_rcu_node(rnp);
|
||||
|
||||
@@ -1226,8 +1224,7 @@ static bool rcu_advance_cbs(struct rcu_node *rnp, struct rcu_data *rdp)
|
||||
* Find all callbacks whose ->gp_seq numbers indicate that they
|
||||
* are ready to invoke, and put them into the RCU_DONE_TAIL sublist.
|
||||
*/
|
||||
gs.norm = rnp->gp_seq;
|
||||
rcu_segcblist_advance(&rdp->cblist, &gs);
|
||||
rcu_segcblist_advance(&rdp->cblist);
|
||||
|
||||
/* Classify any remaining callbacks. */
|
||||
return rcu_accelerate_cbs(rnp, rdp);
|
||||
|
||||
@@ -502,7 +502,7 @@ static bool rcu_nocb_try_bypass(struct rcu_data *rdp, struct rcu_head *rhp,
|
||||
}
|
||||
if (j != rdp->nocb_gp_adv_time &&
|
||||
rcu_segcblist_nextgp(&rdp->cblist, &cur_gp_seq) &&
|
||||
rcu_seq_done(&rdp->mynode->gp_seq, cur_gp_seq.norm)) {
|
||||
poll_state_synchronize_rcu_full(&cur_gp_seq)) {
|
||||
rcu_advance_cbs_nowake(rdp->mynode, rdp);
|
||||
rdp->nocb_gp_adv_time = j;
|
||||
}
|
||||
@@ -731,17 +731,17 @@ static noinline_for_stack void nocb_gp_wait(struct rcu_data *my_rdp)
|
||||
if (!rcu_segcblist_restempty(&rdp->cblist,
|
||||
RCU_NEXT_READY_TAIL) ||
|
||||
(rcu_segcblist_nextgp(&rdp->cblist, &cur_gp_seq) &&
|
||||
rcu_seq_done(&rnp->gp_seq, cur_gp_seq.norm))) {
|
||||
poll_state_synchronize_rcu_full(&cur_gp_seq))) {
|
||||
raw_spin_lock_rcu_node(rnp); /* irqs disabled. */
|
||||
needwake_gp = rcu_advance_cbs(rnp, rdp);
|
||||
wasempty = rcu_segcblist_restempty(&rdp->cblist,
|
||||
RCU_NEXT_READY_TAIL);
|
||||
raw_spin_unlock_rcu_node(rnp); /* irqs disabled. */
|
||||
}
|
||||
// Need to wait on some grace period?
|
||||
WARN_ON_ONCE(wasempty &&
|
||||
!rcu_segcblist_restempty(&rdp->cblist,
|
||||
RCU_NEXT_READY_TAIL));
|
||||
// Need to wait on some grace period?
|
||||
if (rcu_segcblist_nextgp(&rdp->cblist, &cur_gp_seq)) {
|
||||
if (!needwait_gp ||
|
||||
ULONG_CMP_LT(cur_gp_seq.norm, wait_gp_seq))
|
||||
@@ -919,7 +919,7 @@ static void nocb_cb_wait(struct rcu_data *rdp)
|
||||
lockdep_assert_irqs_enabled();
|
||||
rcu_nocb_lock_irqsave(rdp, flags);
|
||||
if (rcu_segcblist_nextgp(cblist, &cur_gp_seq) &&
|
||||
rcu_seq_done(&rnp->gp_seq, cur_gp_seq.norm) &&
|
||||
poll_state_synchronize_rcu_full(&cur_gp_seq) &&
|
||||
raw_spin_trylock_rcu_node(rnp)) { /* irqs already disabled. */
|
||||
needwake_gp = rcu_advance_cbs(rdp->mynode, rdp);
|
||||
raw_spin_unlock_rcu_node(rnp); /* irqs remain disabled. */
|
||||
@@ -1548,8 +1548,8 @@ static void show_rcu_nocb_gp_state(struct rcu_data *rdp)
|
||||
static void show_rcu_nocb_state(struct rcu_data *rdp)
|
||||
{
|
||||
char bufd[22];
|
||||
char bufw[45];
|
||||
char bufr[45];
|
||||
char bufw[64];
|
||||
char bufr[64];
|
||||
char bufn[22];
|
||||
char bufb[22];
|
||||
struct rcu_data *nocb_next_rdp;
|
||||
@@ -1569,10 +1569,12 @@ static void show_rcu_nocb_state(struct rcu_data *rdp)
|
||||
nocb_entry_rdp);
|
||||
|
||||
sprintf(bufd, "%ld", rsclp->seglen[RCU_DONE_TAIL]);
|
||||
sprintf(bufw, "%ld(%ld)", rsclp->seglen[RCU_WAIT_TAIL],
|
||||
rsclp->gp_seq[RCU_WAIT_TAIL].norm);
|
||||
sprintf(bufr, "%ld(%ld)", rsclp->seglen[RCU_NEXT_READY_TAIL],
|
||||
rsclp->gp_seq[RCU_NEXT_READY_TAIL].norm);
|
||||
sprintf(bufw, "%ld(%ld/%ld)", rsclp->seglen[RCU_WAIT_TAIL],
|
||||
rsclp->gp_seq[RCU_WAIT_TAIL].norm,
|
||||
rsclp->gp_seq[RCU_WAIT_TAIL].exp);
|
||||
sprintf(bufr, "%ld(%ld/%ld)", rsclp->seglen[RCU_NEXT_READY_TAIL],
|
||||
rsclp->gp_seq[RCU_NEXT_READY_TAIL].norm,
|
||||
rsclp->gp_seq[RCU_NEXT_READY_TAIL].exp);
|
||||
sprintf(bufn, "%ld", rsclp->seglen[RCU_NEXT_TAIL]);
|
||||
sprintf(bufb, "%ld", rcu_cblist_n_cbs(&rdp->nocb_bypass));
|
||||
pr_info(" CB %d^%d->%d %c%c%c%c%c F%ld L%ld C%d %c%s%c%s%c%s%c%s%c%s q%ld %c CPU %d%s\n",
|
||||
|
||||
Reference in New Issue
Block a user