Skip to content

Commit

Permalink
Merge tag 'rcu.release.v6.8' of https://github.com/neeraju/linux
Browse files Browse the repository at this point in the history
Pull RCU updates from Neeraj Upadhyay:

 - Documentation and comment updates

 - RCU torture, locktorture updates that include cleanups; nolibc init
   build support for mips, ppc and rv64; testing of mid stall duration
   scenario and fixing fqs task creation conditions

 - Misc fixes, most notably restricting usage of RCU CPU stall
   notifiers, to confine their usage primarily to debug kernels

 - RCU tasks minor fixes

 - lockdep annotation fix for NMI-safe accesses, callback
   advancing/acceleration cleanup and documentation improvements

* tag 'rcu.release.v6.8' of https://github.com/neeraju/linux:
  rcu: Force quiescent states only for ongoing grace period
  doc: Clarify historical disclaimers in memory-barriers.txt
  doc: Mention address and data dependencies in rcu_dereference.rst
  doc: Clarify RCU Tasks reader/updater checklist
  rculist.h: docs: Fix wrong function summary
  Documentation: RCU: Remove repeated word in comments
  srcu: Use try-lock lockdep annotation for NMI-safe access.
  srcu: Explain why callbacks invocations can't run concurrently
  srcu: No need to advance/accelerate if no callback enqueued
  srcu: Remove superfluous callbacks advancing from srcu_gp_start()
  rcu: Remove unused macros from rcupdate.h
  rcu: Restrict access to RCU CPU stall notifiers
  rcu-tasks: Mark RCU Tasks accesses to current->rcu_tasks_idle_cpu
  rcutorture: Add fqs_holdoff check before fqs_task is created
  rcutorture: Add mid-sized stall to TREE07
  rcutorture: add nolibc init support for mips, ppc and rv64
  locktorture: Increase Hamming distance between call_rcu_chain and rcu_call_chains
  • Loading branch information
Linus Torvalds committed Jan 13, 2024
2 parents 3881433 + 7dfb03d commit 23a80d4
Show file tree
Hide file tree
Showing 20 changed files with 151 additions and 67 deletions.
25 changes: 16 additions & 9 deletions Documentation/RCU/checklist.rst
Original file line number Diff line number Diff line change
Expand Up @@ -241,15 +241,22 @@ over a rather long period of time, but improvements are always welcome!
srcu_struct. The rules for the expedited RCU grace-period-wait
primitives are the same as for their non-expedited counterparts.

If the updater uses call_rcu_tasks() or synchronize_rcu_tasks(),
then the readers must refrain from executing voluntary
context switches, that is, from blocking. If the updater uses
call_rcu_tasks_trace() or synchronize_rcu_tasks_trace(), then
the corresponding readers must use rcu_read_lock_trace() and
rcu_read_unlock_trace(). If an updater uses call_rcu_tasks_rude()
or synchronize_rcu_tasks_rude(), then the corresponding readers
must use anything that disables preemption, for example,
preempt_disable() and preempt_enable().
Similarly, it is necessary to correctly use the RCU Tasks flavors:

a. If the updater uses synchronize_rcu_tasks() or
call_rcu_tasks(), then the readers must refrain from
executing voluntary context switches, that is, from
blocking.

b. If the updater uses call_rcu_tasks_trace()
or synchronize_rcu_tasks_trace(), then the
corresponding readers must use rcu_read_lock_trace()
and rcu_read_unlock_trace().

c. If an updater uses call_rcu_tasks_rude() or
synchronize_rcu_tasks_rude(), then the corresponding
readers must use anything that disables preemption,
for example, preempt_disable() and preempt_enable().

Mixing things up will result in confusion and broken kernels, and
has even resulted in an exploitable security issue. Therefore,
Expand Down
27 changes: 20 additions & 7 deletions Documentation/RCU/rcu_dereference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,26 @@
PROPER CARE AND FEEDING OF RETURN VALUES FROM rcu_dereference()
===============================================================

Most of the time, you can use values from rcu_dereference() or one of
the similar primitives without worries. Dereferencing (prefix "*"),
field selection ("->"), assignment ("="), address-of ("&"), addition and
subtraction of constants, and casts all work quite naturally and safely.

It is nevertheless possible to get into trouble with other operations.
Follow these rules to keep your RCU code working properly:
Proper care and feeding of address and data dependencies is critically
important to correct use of things like RCU. To this end, the pointers
returned from the rcu_dereference() family of primitives carry address and
data dependencies. These dependencies extend from the rcu_dereference()
macro's load of the pointer to the later use of that pointer to compute
either the address of a later memory access (representing an address
dependency) or the value written by a later memory access (representing
a data dependency).

Most of the time, these dependencies are preserved, permitting you to
freely use values from rcu_dereference(). For example, dereferencing
(prefix "*"), field selection ("->"), assignment ("="), address-of
("&"), casts, and addition or subtraction of constants all work quite
naturally and safely. However, because current compilers do not take
either address or data dependencies into account it is still possible
to get into trouble.

Follow these rules to preserve the address and data dependencies emanating
from your calls to rcu_dereference() and friends, thus keeping your RCU
readers working properly:

- You must use one of the rcu_dereference() family of primitives
to load an RCU-protected pointer, otherwise CONFIG_PROVE_RCU
Expand Down
2 changes: 1 addition & 1 deletion Documentation/RCU/torture.rst
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ argument.
Not all changes require that all scenarios be run. For example, a change
to Tree SRCU might run only the SRCU-N and SRCU-P scenarios using the
--configs argument to kvm.sh as follows: "--configs 'SRCU-N SRCU-P'".
Large systems can run multiple copies of of the full set of scenarios,
Large systems can run multiple copies of the full set of scenarios,
for example, a system with 448 hardware threads can run five instances
of the full set concurrently. To make this happen::

Expand Down
6 changes: 6 additions & 0 deletions Documentation/admin-guide/kernel-parameters.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5313,6 +5313,12 @@
Dump ftrace buffer after reporting RCU CPU
stall warning.

rcupdate.rcu_cpu_stall_notifiers= [KNL]
Provide RCU CPU stall notifiers, but see the
warnings in the RCU_CPU_STALL_NOTIFIER Kconfig
option's help text. TL;DR: You almost certainly
do not want rcupdate.rcu_cpu_stall_notifiers.

rcupdate.rcu_cpu_stall_suppress= [KNL]
Suppress RCU CPU stall warning messages.

Expand Down
17 changes: 10 additions & 7 deletions Documentation/memory-barriers.txt
Original file line number Diff line number Diff line change
Expand Up @@ -396,10 +396,11 @@ Memory barriers come in four basic varieties:


(2) Address-dependency barriers (historical).
[!] This section is marked as HISTORICAL: For more up-to-date
information, including how compiler transformations related to pointer
comparisons can sometimes cause problems, see
Documentation/RCU/rcu_dereference.rst.
[!] This section is marked as HISTORICAL: it covers the long-obsolete
smp_read_barrier_depends() macro, the semantics of which are now
implicit in all marked accesses. For more up-to-date information,
including how compiler transformations can sometimes break address
dependencies, see Documentation/RCU/rcu_dereference.rst.

An address-dependency barrier is a weaker form of read barrier. In the
case where two loads are performed such that the second depends on the
Expand Down Expand Up @@ -560,9 +561,11 @@ There are certain things that the Linux kernel memory barriers do not guarantee:

ADDRESS-DEPENDENCY BARRIERS (HISTORICAL)
----------------------------------------
[!] This section is marked as HISTORICAL: For more up-to-date information,
including how compiler transformations related to pointer comparisons can
sometimes cause problems, see Documentation/RCU/rcu_dereference.rst.
[!] This section is marked as HISTORICAL: it covers the long-obsolete
smp_read_barrier_depends() macro, the semantics of which are now implicit
in all marked accesses. For more up-to-date information, including
how compiler transformations can sometimes break address dependencies,
see Documentation/RCU/rcu_dereference.rst.

As of v4.15 of the Linux kernel, an smp_mb() was added to READ_ONCE() for
DEC Alpha, which means that about the only people who need to pay attention
Expand Down
6 changes: 3 additions & 3 deletions include/linux/rcu_notifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,20 @@
#define RCU_STALL_NOTIFY_NORM 1
#define RCU_STALL_NOTIFY_EXP 2

#ifdef CONFIG_RCU_STALL_COMMON
#if defined(CONFIG_RCU_STALL_COMMON) && defined(CONFIG_RCU_CPU_STALL_NOTIFIER)

#include <linux/notifier.h>
#include <linux/types.h>

int rcu_stall_chain_notifier_register(struct notifier_block *n);
int rcu_stall_chain_notifier_unregister(struct notifier_block *n);

#else // #ifdef CONFIG_RCU_STALL_COMMON
#else // #if defined(CONFIG_RCU_STALL_COMMON) && defined(CONFIG_RCU_CPU_STALL_NOTIFIER)

// No RCU CPU stall warnings in Tiny RCU.
static inline int rcu_stall_chain_notifier_register(struct notifier_block *n) { return -EEXIST; }
static inline int rcu_stall_chain_notifier_unregister(struct notifier_block *n) { return -ENOENT; }

#endif // #else // #ifdef CONFIG_RCU_STALL_COMMON
#endif // #else // #if defined(CONFIG_RCU_STALL_COMMON) && defined(CONFIG_RCU_CPU_STALL_NOTIFIER)

#endif /* __LINUX_RCU_NOTIFIER_H */
2 changes: 1 addition & 1 deletion include/linux/rculist.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ static inline void list_splice_tail_init_rcu(struct list_head *list,
})

/**
* list_next_or_null_rcu - get the first element from a list
* list_next_or_null_rcu - get the next element from a list
* @head: the head for the list.
* @ptr: the list head to take the next element from.
* @type: the type of the struct this is embedded in.
Expand Down
9 changes: 6 additions & 3 deletions include/linux/rcupdate.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@

#define ULONG_CMP_GE(a, b) (ULONG_MAX / 2 >= (a) - (b))
#define ULONG_CMP_LT(a, b) (ULONG_MAX / 2 < (a) - (b))
#define ulong2long(a) (*(long *)(&(a)))
#define USHORT_CMP_GE(a, b) (USHRT_MAX / 2 >= (unsigned short)((a) - (b)))
#define USHORT_CMP_LT(a, b) (USHRT_MAX / 2 < (unsigned short)((a) - (b)))

/* Exported common interfaces */
void call_rcu(struct rcu_head *head, rcu_callback_t func);
Expand Down Expand Up @@ -301,6 +298,11 @@ static inline void rcu_lock_acquire(struct lockdep_map *map)
lock_acquire(map, 0, 0, 2, 0, NULL, _THIS_IP_);
}

static inline void rcu_try_lock_acquire(struct lockdep_map *map)
{
lock_acquire(map, 0, 1, 2, 0, NULL, _THIS_IP_);
}

static inline void rcu_lock_release(struct lockdep_map *map)
{
lock_release(map, _THIS_IP_);
Expand All @@ -315,6 +317,7 @@ int rcu_read_lock_any_held(void);
#else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */

# define rcu_lock_acquire(a) do { } while (0)
# define rcu_try_lock_acquire(a) do { } while (0)
# define rcu_lock_release(a) do { } while (0)

static inline int rcu_read_lock_held(void)
Expand Down
2 changes: 1 addition & 1 deletion include/linux/srcu.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ static inline int srcu_read_lock_nmisafe(struct srcu_struct *ssp) __acquires(ssp

srcu_check_nmi_safety(ssp, true);
retval = __srcu_read_lock_nmisafe(ssp);
rcu_lock_acquire(&ssp->dep_map);
rcu_try_lock_acquire(&ssp->dep_map);
return retval;
}

Expand Down
18 changes: 9 additions & 9 deletions kernel/locking/locktorture.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ struct call_rcu_chain {
struct rcu_head crc_rh;
bool crc_stop;
};
struct call_rcu_chain *call_rcu_chain;
struct call_rcu_chain *call_rcu_chain_list;

/* Forward reference. */
static void lock_torture_cleanup(void);
Expand Down Expand Up @@ -1074,12 +1074,12 @@ static int call_rcu_chain_init(void)

if (call_rcu_chains <= 0)
return 0;
call_rcu_chain = kcalloc(call_rcu_chains, sizeof(*call_rcu_chain), GFP_KERNEL);
if (!call_rcu_chain)
call_rcu_chain_list = kcalloc(call_rcu_chains, sizeof(*call_rcu_chain_list), GFP_KERNEL);
if (!call_rcu_chain_list)
return -ENOMEM;
for (i = 0; i < call_rcu_chains; i++) {
call_rcu_chain[i].crc_stop = false;
call_rcu(&call_rcu_chain[i].crc_rh, call_rcu_chain_cb);
call_rcu_chain_list[i].crc_stop = false;
call_rcu(&call_rcu_chain_list[i].crc_rh, call_rcu_chain_cb);
}
return 0;
}
Expand All @@ -1089,13 +1089,13 @@ static void call_rcu_chain_cleanup(void)
{
int i;

if (!call_rcu_chain)
if (!call_rcu_chain_list)
return;
for (i = 0; i < call_rcu_chains; i++)
smp_store_release(&call_rcu_chain[i].crc_stop, true);
smp_store_release(&call_rcu_chain_list[i].crc_stop, true);
rcu_barrier();
kfree(call_rcu_chain);
call_rcu_chain = NULL;
kfree(call_rcu_chain_list);
call_rcu_chain_list = NULL;
}

static void lock_torture_cleanup(void)
Expand Down
25 changes: 25 additions & 0 deletions kernel/rcu/Kconfig.debug
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,31 @@ config RCU_CPU_STALL_CPUTIME
The boot option rcupdate.rcu_cpu_stall_cputime has the same function
as this one, but will override this if it exists.

config RCU_CPU_STALL_NOTIFIER
bool "Provide RCU CPU-stall notifiers"
depends on RCU_STALL_COMMON
depends on DEBUG_KERNEL
depends on RCU_EXPERT
default n
help
WARNING: You almost certainly do not want this!!!

Enable RCU CPU-stall notifiers, which are invoked just before
printing the RCU CPU stall warning. As such, bugs in notifier
callbacks can prevent stall warnings from being printed.
And the whole reason that a stall warning is being printed is
that something is hung up somewhere. Therefore, the notifier
callbacks must be written extremely carefully, preferably
containing only lockless code. After all, it is quite possible
that the whole reason that the RCU CPU stall is happening in
the first place is that someone forgot to release whatever lock
that you are thinking of acquiring. In which case, having your
notifier callback acquire that lock will hang, preventing the
RCU CPU stall warning from appearing.

Say Y here if you want RCU CPU stall notifiers (you don't want them)
Say N if you are unsure.

config RCU_TRACE
bool "Enable tracing for RCU"
depends on DEBUG_KERNEL
Expand Down
8 changes: 5 additions & 3 deletions kernel/rcu/rcu.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,8 @@ static inline bool rcu_stall_is_suppressed_at_boot(void)
return rcu_cpu_stall_suppress_at_boot && !rcu_inkernel_boot_has_ended();
}

extern int rcu_cpu_stall_notifiers;

#ifdef CONFIG_RCU_STALL_COMMON

extern int rcu_cpu_stall_ftrace_dump;
Expand Down Expand Up @@ -659,10 +661,10 @@ static inline bool rcu_cpu_beenfullyonline(int cpu) { return true; }
bool rcu_cpu_beenfullyonline(int cpu);
#endif

#ifdef CONFIG_RCU_STALL_COMMON
#if defined(CONFIG_RCU_STALL_COMMON) && defined(CONFIG_RCU_CPU_STALL_NOTIFIER)
int rcu_stall_notifier_call_chain(unsigned long val, void *v);
#else // #ifdef CONFIG_RCU_STALL_COMMON
#else // #if defined(CONFIG_RCU_STALL_COMMON) && defined(CONFIG_RCU_CPU_STALL_NOTIFIER)
static inline int rcu_stall_notifier_call_chain(unsigned long val, void *v) { return NOTIFY_DONE; }
#endif // #else // #ifdef CONFIG_RCU_STALL_COMMON
#endif // #else // #if defined(CONFIG_RCU_STALL_COMMON) && defined(CONFIG_RCU_CPU_STALL_NOTIFIER)

#endif /* __LINUX_RCU_H */
16 changes: 10 additions & 6 deletions kernel/rcu/rcutorture.c
Original file line number Diff line number Diff line change
Expand Up @@ -2450,10 +2450,12 @@ static int rcu_torture_stall(void *args)
unsigned long stop_at;

VERBOSE_TOROUT_STRING("rcu_torture_stall task started");
ret = rcu_stall_chain_notifier_register(&rcu_torture_stall_block);
if (ret)
pr_info("%s: rcu_stall_chain_notifier_register() returned %d, %sexpected.\n",
__func__, ret, !IS_ENABLED(CONFIG_RCU_STALL_COMMON) ? "un" : "");
if (rcu_cpu_stall_notifiers) {
ret = rcu_stall_chain_notifier_register(&rcu_torture_stall_block);
if (ret)
pr_info("%s: rcu_stall_chain_notifier_register() returned %d, %sexpected.\n",
__func__, ret, !IS_ENABLED(CONFIG_RCU_STALL_COMMON) ? "un" : "");
}
if (stall_cpu_holdoff > 0) {
VERBOSE_TOROUT_STRING("rcu_torture_stall begin holdoff");
schedule_timeout_interruptible(stall_cpu_holdoff * HZ);
Expand Down Expand Up @@ -2497,7 +2499,7 @@ static int rcu_torture_stall(void *args)
cur_ops->readunlock(idx);
}
pr_alert("%s end.\n", __func__);
if (!ret) {
if (rcu_cpu_stall_notifiers && !ret) {
ret = rcu_stall_chain_notifier_unregister(&rcu_torture_stall_block);
if (ret)
pr_info("%s: rcu_stall_chain_notifier_unregister() returned %d.\n", __func__, ret);
Expand Down Expand Up @@ -3872,7 +3874,9 @@ rcu_torture_init(void)
}
if (fqs_duration < 0)
fqs_duration = 0;
if (fqs_duration) {
if (fqs_holdoff < 0)
fqs_holdoff = 0;
if (fqs_duration && fqs_holdoff) {
/* Create the fqs thread */
firsterr = torture_create_kthread(rcu_torture_fqs, NULL,
fqs_task);
Expand Down
24 changes: 11 additions & 13 deletions kernel/rcu/srcutree.c
Original file line number Diff line number Diff line change
Expand Up @@ -772,20 +772,10 @@ EXPORT_SYMBOL_GPL(__srcu_read_unlock_nmisafe);
*/
static void srcu_gp_start(struct srcu_struct *ssp)
{
struct srcu_data *sdp;
int state;

if (smp_load_acquire(&ssp->srcu_sup->srcu_size_state) < SRCU_SIZE_WAIT_BARRIER)
sdp = per_cpu_ptr(ssp->sda, get_boot_cpu_id());
else
sdp = this_cpu_ptr(ssp->sda);
lockdep_assert_held(&ACCESS_PRIVATE(ssp->srcu_sup, lock));
WARN_ON_ONCE(ULONG_CMP_GE(ssp->srcu_sup->srcu_gp_seq, ssp->srcu_sup->srcu_gp_seq_needed));
spin_lock_rcu_node(sdp); /* Interrupts already disabled. */
rcu_segcblist_advance(&sdp->srcu_cblist,
rcu_seq_current(&ssp->srcu_sup->srcu_gp_seq));
WARN_ON_ONCE(!rcu_segcblist_segempty(&sdp->srcu_cblist, RCU_NEXT_TAIL));
spin_unlock_rcu_node(sdp); /* Interrupts remain disabled. */
WRITE_ONCE(ssp->srcu_sup->srcu_gp_start, jiffies);
WRITE_ONCE(ssp->srcu_sup->srcu_n_exp_nodelay, 0);
smp_mb(); /* Order prior store to ->srcu_gp_seq_needed vs. GP start. */
Expand Down Expand Up @@ -1271,9 +1261,11 @@ static unsigned long srcu_gp_start_if_needed(struct srcu_struct *ssp,
* period (gp_num = X + 8). So acceleration fails.
*/
s = rcu_seq_snap(&ssp->srcu_sup->srcu_gp_seq);
rcu_segcblist_advance(&sdp->srcu_cblist,
rcu_seq_current(&ssp->srcu_sup->srcu_gp_seq));
WARN_ON_ONCE(!rcu_segcblist_accelerate(&sdp->srcu_cblist, s) && rhp);
if (rhp) {
rcu_segcblist_advance(&sdp->srcu_cblist,
rcu_seq_current(&ssp->srcu_sup->srcu_gp_seq));
WARN_ON_ONCE(!rcu_segcblist_accelerate(&sdp->srcu_cblist, s));
}
if (ULONG_CMP_LT(sdp->srcu_gp_seq_needed, s)) {
sdp->srcu_gp_seq_needed = s;
needgp = true;
Expand Down Expand Up @@ -1723,6 +1715,11 @@ static void srcu_invoke_callbacks(struct work_struct *work)
WARN_ON_ONCE(!rcu_segcblist_segempty(&sdp->srcu_cblist, RCU_NEXT_TAIL));
rcu_segcblist_advance(&sdp->srcu_cblist,
rcu_seq_current(&ssp->srcu_sup->srcu_gp_seq));
/*
* Although this function is theoretically re-entrant, concurrent
* callbacks invocation is disallowed to avoid executing an SRCU barrier
* too early.
*/
if (sdp->srcu_cblist_invoking ||
!rcu_segcblist_ready_cbs(&sdp->srcu_cblist)) {
spin_unlock_irq_rcu_node(sdp);
Expand Down Expand Up @@ -1753,6 +1750,7 @@ static void srcu_invoke_callbacks(struct work_struct *work)
sdp->srcu_cblist_invoking = false;
more = rcu_segcblist_ready_cbs(&sdp->srcu_cblist);
spin_unlock_irq_rcu_node(sdp);
/* An SRCU barrier or callbacks from previous nesting work pending */
if (more)
srcu_schedule_cbs_sdp(sdp, 0);
}
Expand Down
Loading

0 comments on commit 23a80d4

Please sign in to comment.