All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Paul E. McKenney" <paulmck@kernel.org>
To: Thomas Gleixner <tglx@linutronix.de>
Cc: Andy Lutomirski <luto@kernel.org>,
	LKML <linux-kernel@vger.kernel.org>, X86 ML <x86@kernel.org>,
	Alexandre Chartre <alexandre.chartre@oracle.com>,
	Frederic Weisbecker <frederic@kernel.org>,
	Paolo Bonzini <pbonzini@redhat.com>,
	Sean Christopherson <sean.j.christopherson@intel.com>,
	Masami Hiramatsu <mhiramat@kernel.org>,
	Petr Mladek <pmladek@suse.com>,
	Steven Rostedt <rostedt@goodmis.org>,
	Joel Fernandes <joel@joelfernandes.org>,
	Boris Ostrovsky <boris.ostrovsky@oracle.com>,
	Juergen Gross <jgross@suse.com>, Brian Gerst <brgerst@gmail.com>,
	Mathieu Desnoyers <mathieu.desnoyers@efficios.com>,
	Josh Poimboeuf <jpoimboe@redhat.com>,
	Will Deacon <will@kernel.org>,
	Tom Lendacky <thomas.lendacky@amd.com>,
	Wei Liu <wei.liu@kernel.org>,
	Michael Kelley <mikelley@microsoft.com>,
	Jason Chen CJ <jason.cj.chen@intel.com>,
	Zhao Yakui <yakui.zhao@intel.com>,
	"Peter Zijlstra (Intel)" <peterz@infradead.org>
Subject: Re: [patch V6 12/37] x86/entry: Provide idtentry_entry/exit_cond_rcu()
Date: Wed, 20 May 2020 16:25:50 -0700	[thread overview]
Message-ID: <20200520232550.GA20812@paulmck-ThinkPad-P72> (raw)
In-Reply-To: <20200520221531.GW2869@paulmck-ThinkPad-P72>

On Wed, May 20, 2020 at 03:15:31PM -0700, Paul E. McKenney wrote:
> On Wed, May 20, 2020 at 09:49:18PM +0200, Thomas Gleixner wrote:
> > "Paul E. McKenney" <paulmck@kernel.org> writes:
> > > On Wed, May 20, 2020 at 09:51:17AM -0700, Andy Lutomirski wrote:
> > >> Paul, the major change here is that if an IRQ hits normal kernel code
> > >> (i.e. code where RCU is watching and we're not in an EQS), the IRQ
> > >> won't call rcu_irq_enter() and rcu_irq_exit().  Instead it will call
> > >> rcu_tickle() on entry and nothing on exit.  Does that cover all the
> > >> bases?
> > >
> > > From an RCU viewpoint, yes, give or take my concerns about someone
> > > putting rcu_tickle() on entry and rcu_irq_exit() on exit.  Perhaps
> > > I can bring some lockdep trickery to bear.
> > 
> > An surplus rcu_irq_exit() should already trigger alarms today.
> 
> Fair point!
> 
> > > But I must defer to Thomas and Peter on the non-RCU/non-NO_HZ_FULL
> > > portions of this.
> > 
> > I don't see a problem. Let me write that into actual testable patch
> > form.
> 
> Here is the RCU part, with my current best guess for the commit log.
> 
> Please note that this is on top of my -rcu stack, so some adjustment
> will likely be needed to pull it underneath Joel's series that removes
> the special-purpose bits at the bottom of the ->dynticks counter.
> 
> But a starting point, anyway.

Same patch, but with updated commit log based on IRC discussion
with Andy.

							Thanx, Paul

------------------------------------------------------------------------

commit 1771ea9fac5748d1424d9214c51b2f79cc1176b6
Author: Paul E. McKenney <paulmck@kernel.org>
Date:   Wed May 20 15:03:07 2020 -0700

    rcu: Abstract out tickle_nohz_for_rcu() from rcu_nmi_enter()
    
    There will likely be exception handlers that can sleep, which rules
    out the usual approach of invoking rcu_nmi_enter() on entry and also
    rcu_nmi_exit() on all exit paths.  However, the alternative approach of
    just not calling anything can prevent RCU from coaxing quiescent states
    from nohz_full CPUs that are looping in the kernel:  RCU must instead
    IPI them explicitly.  It would be better to enable the scheduler tick
    on such CPUs to interact with RCU in a lighter-weight manner, and this
    enabling is one of the things that rcu_nmi_enter() currently does.
    
    What is needed is something that helps RCU coax quiescent states while
    not preventing subsequent sleeps.  This commit therefore splits out the
    nohz_full scheduler-tick enabling from the rest of the rcu_nmi_enter()
    logic into a new function named tickle_nohz_for_rcu().
    
    Suggested-by: Andy Lutomirski <luto@kernel.org>
    Signed-off-by: Paul E. McKenney <paulmck@kernel.org>

diff --git a/include/linux/hardirq.h b/include/linux/hardirq.h
index 621556e..d4be42a 100644
--- a/include/linux/hardirq.h
+++ b/include/linux/hardirq.h
@@ -14,6 +14,10 @@ extern bool synchronize_hardirq(unsigned int irq);
 
 #if defined(CONFIG_TINY_RCU)
 
+static inline void tickle_nohz_for_rcu(void)
+{
+}
+
 static inline void rcu_nmi_enter(void)
 {
 }
@@ -23,6 +27,7 @@ static inline void rcu_nmi_exit(void)
 }
 
 #else
+extern void tickle_nohz_for_rcu(void);
 extern void rcu_nmi_enter(void);
 extern void rcu_nmi_exit(void);
 #endif
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
index 7812574..0a3cad4 100644
--- a/kernel/rcu/tree.c
+++ b/kernel/rcu/tree.c
@@ -806,6 +806,67 @@ void noinstr rcu_user_exit(void)
 #endif /* CONFIG_NO_HZ_FULL */
 
 /**
+ * tickle_nohz_for_rcu - Enable scheduler tick on CPU if RCU needs it.
+ *
+ * The scheduler tick is not normally enabled when CPUs enter the kernel
+ * from nohz_full userspace execution.  After all, nohz_full userspace
+ * execution is an RCU quiescent state and the time executing in the kernel
+ * is quite short.  Except of course when it isn't.  And it is not hard to
+ * cause a large system to spend tens of seconds or even minutes looping
+ * in the kernel, which can cause a number of problems, include RCU CPU
+ * stall warnings.
+ *
+ * Therefore, if a nohz_full CPU fails to report a quiescent state
+ * in a timely manner, the RCU grace-period kthread sets that CPU's
+ * ->rcu_urgent_qs flag with the expectation that the next interrupt or
+ * exception will invoke this function, which will turn on the scheduler
+ * tick, which will enable RCU to detect that CPU's quiescent states,
+ * for example, due to cond_resched() calls in CONFIG_PREEMPT=n kernels.
+ * The tick will be disabled once a quiescent state is reported for
+ * this CPU.
+ *
+ * Of course, in carefully tuned systems, there might never be an
+ * interrupt or exception.  In that case, the RCU grace-period kthread
+ * will eventually cause one to happen.  However, in less carefully
+ * controlled environments, this function allows RCU to get what it
+ * needs without creating otherwise useless interruptions.
+ */
+noinstr void tickle_nohz_for_rcu(void)
+{
+	struct rcu_data *rdp = this_cpu_ptr(&rcu_data);
+
+	if (in_nmi())
+		return; // Enabling tick is unsafe in NMI handlers.
+	RCU_LOCKDEP_WARN(rcu_dynticks_curr_cpu_in_eqs(),
+			 "Illegal tickle_nohz_for_rcu from extended quiescent state");
+	instrumentation_begin();
+	if (!tick_nohz_full_cpu(rdp->cpu) ||
+	    !READ_ONCE(rdp->rcu_urgent_qs) ||
+	    READ_ONCE(rdp->rcu_forced_tick)) {
+		// RCU doesn't need nohz_full help from this CPU, or it is
+		// already getting that help.
+		instrumentation_end();
+		return;
+	}
+
+	// We get here only when not in an extended quiescent state and
+	// from interrupts (as opposed to NMIs).  Therefore, (1) RCU is
+	// already watching and (2) The fact that we are in an interrupt
+	// handler and that the rcu_node lock is an irq-disabled lock
+	// prevents self-deadlock.  So we can safely recheck under the lock.
+	// Note that the nohz_full state currently cannot change.
+	raw_spin_lock_rcu_node(rdp->mynode);
+	if (rdp->rcu_urgent_qs && !rdp->rcu_forced_tick) {
+		// A nohz_full CPU is in the kernel and RCU needs a
+		// quiescent state.  Turn on the tick!
+		WRITE_ONCE(rdp->rcu_forced_tick, true);
+		tick_dep_set_cpu(rdp->cpu, TICK_DEP_BIT_RCU);
+	}
+	raw_spin_unlock_rcu_node(rdp->mynode);
+	instrumentation_end();
+}
+
+/**
  * rcu_nmi_enter - inform RCU of entry to NMI context
  * @irq: Is this call from rcu_irq_enter?
  *
@@ -835,7 +896,9 @@ noinstr void rcu_nmi_enter(void)
 	 * is if the interrupt arrived in kernel mode; in this case we would
 	 * be the outermost interrupt but still increment by 2 which is Ok.
 	 */
-	if (rcu_dynticks_curr_cpu_in_eqs()) {
+	if (!rcu_dynticks_curr_cpu_in_eqs()) {
+		tickle_nohz_for_rcu();
+	} else {
 
 		if (!in_nmi())
 			rcu_dynticks_task_exit();
@@ -851,28 +914,6 @@ noinstr void rcu_nmi_enter(void)
 		}
 
 		incby = 1;
-	} else if (!in_nmi()) {
-		instrumentation_begin();
-		if (tick_nohz_full_cpu(rdp->cpu) &&
-		    READ_ONCE(rdp->rcu_urgent_qs) &&
-		    !READ_ONCE(rdp->rcu_forced_tick)) {
-			// We get here only if we had already exited the
-			// extended quiescent state and this was an
-			// interrupt (not an NMI).  Therefore, (1) RCU is
-			// already watching and (2) The fact that we are in
-			// an interrupt handler and that the rcu_node lock
-			// is an irq-disabled lock prevents self-deadlock.
-			// So we can safely recheck under the lock.
-			raw_spin_lock_rcu_node(rdp->mynode);
-			if (rdp->rcu_urgent_qs && !rdp->rcu_forced_tick) {
-				// A nohz_full CPU is in the kernel and RCU
-				// needs a quiescent state.  Turn on the tick!
-				WRITE_ONCE(rdp->rcu_forced_tick, true);
-				tick_dep_set_cpu(rdp->cpu, TICK_DEP_BIT_RCU);
-			}
-			raw_spin_unlock_rcu_node(rdp->mynode);
-		}
-		instrumentation_end();
 	}
 	instrumentation_begin();
 	trace_rcu_dyntick(incby == 1 ? TPS("End") : TPS("StillNonIdle"),

  reply	other threads:[~2020-05-20 23:25 UTC|newest]

Thread overview: 159+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-05-15 23:45 [patch V6 00/37] x86/entry: Rework leftovers and merge plan Thomas Gleixner
2020-05-15 23:45 ` [patch V6 01/37] tracing/hwlat: Use ktime_get_mono_fast_ns() Thomas Gleixner
2020-05-19 21:26   ` Steven Rostedt
2020-05-19 21:45     ` Thomas Gleixner
2020-05-19 22:18       ` Steven Rostedt
2020-05-20 19:51         ` Thomas Gleixner
2020-05-20 20:14   ` Peter Zijlstra
2020-05-20 22:20     ` Thomas Gleixner
2020-05-15 23:45 ` [patch V6 02/37] tracing/hwlat: Split ftrace_nmi_enter/exit() Thomas Gleixner
2020-05-19 22:23   ` Steven Rostedt
2020-05-15 23:45 ` [patch V6 03/37] nmi, tracing: Provide nmi_enter/exit_notrace() Thomas Gleixner
2020-05-17  5:12   ` Andy Lutomirski
2020-05-19 22:24   ` Steven Rostedt
2020-05-15 23:45 ` [patch V6 04/37] x86: Make hardware latency tracing explicit Thomas Gleixner
2020-05-17  5:36   ` Andy Lutomirski
2020-05-17  8:48     ` Thomas Gleixner
2020-05-18  5:50       ` Andy Lutomirski
2020-05-18  8:03         ` Thomas Gleixner
2020-05-18 20:42           ` Andy Lutomirski
2020-05-18  8:01   ` Peter Zijlstra
2020-05-18  8:05     ` Thomas Gleixner
2020-05-18  8:08       ` Peter Zijlstra
2020-05-20 20:09         ` Thomas Gleixner
2020-05-20 20:14           ` Andy Lutomirski
2020-05-20 22:20             ` Thomas Gleixner
2020-05-15 23:45 ` [patch V6 05/37] genirq: Provide irq_enter/exit_rcu() Thomas Gleixner
2020-05-18 23:06   ` Andy Lutomirski
2020-05-15 23:45 ` [patch V6 06/37] genirq: Provde __irq_enter/exit_raw() Thomas Gleixner
2020-05-18 23:07   ` Andy Lutomirski
2020-05-15 23:45 ` [patch V6 07/37] x86/entry: Provide helpers for execute on irqstack Thomas Gleixner
2020-05-18 23:11   ` Andy Lutomirski
2020-05-18 23:46     ` Andy Lutomirski
2020-05-18 23:53       ` Thomas Gleixner
2020-05-18 23:56         ` Andy Lutomirski
2020-05-20 12:35           ` Thomas Gleixner
2020-05-20 15:09             ` Andy Lutomirski
2020-05-20 15:27               ` Thomas Gleixner
2020-05-20 15:36                 ` Andy Lutomirski
2020-05-18 23:51     ` Thomas Gleixner
2020-05-15 23:45 ` [patch V6 08/37] x86/entry/64: Move do_softirq_own_stack() to C Thomas Gleixner
2020-05-18 23:48   ` Andy Lutomirski
2020-05-15 23:45 ` [patch V6 09/37] x86/entry: Split idtentry_enter/exit() Thomas Gleixner
2020-05-18 23:49   ` Andy Lutomirski
2020-05-19  8:25     ` Thomas Gleixner
2020-05-15 23:45 ` [patch V6 10/37] x86/entry: Switch XEN/PV hypercall entry to IDTENTRY Thomas Gleixner
2020-05-19 17:06   ` Andy Lutomirski
2020-05-19 18:57     ` Thomas Gleixner
2020-05-19 19:44       ` Andy Lutomirski
2020-05-20  8:06         ` Jürgen Groß
2020-05-20 11:31           ` Andrew Cooper
2020-05-20 14:13         ` Thomas Gleixner
2020-05-20 15:16           ` Andy Lutomirski
2020-05-20 17:22             ` Andy Lutomirski
2020-05-20 19:16               ` Thomas Gleixner
2020-05-20 23:21                 ` Andy Lutomirski
2020-05-21 10:45                   ` Thomas Gleixner
2020-05-21  2:23                 ` Boris Ostrovsky
2020-05-21  7:08                   ` Thomas Gleixner
2020-05-15 23:45 ` [patch V6 11/37] x86/entry/64: Simplify idtentry_body Thomas Gleixner
2020-05-19 17:06   ` Andy Lutomirski
2020-05-15 23:45 ` [patch V6 12/37] x86/entry: Provide idtentry_entry/exit_cond_rcu() Thomas Gleixner
2020-05-19 17:08   ` Andy Lutomirski
2020-05-19 19:00     ` Thomas Gleixner
2020-05-19 20:20       ` Thomas Gleixner
2020-05-19 20:24         ` Andy Lutomirski
2020-05-19 21:20           ` Thomas Gleixner
2020-05-20  0:26             ` Andy Lutomirski
2020-05-20  2:23               ` Paul E. McKenney
2020-05-20 15:36                 ` Andy Lutomirski
2020-05-20 16:51                   ` Andy Lutomirski
2020-05-20 18:05                     ` Paul E. McKenney
2020-05-20 19:49                       ` Thomas Gleixner
2020-05-20 22:15                         ` Paul E. McKenney
2020-05-20 23:25                           ` Paul E. McKenney [this message]
2020-05-21  8:31                             ` Thomas Gleixner
2020-05-21 13:39                               ` Paul E. McKenney
2020-05-21 18:41                                 ` Thomas Gleixner
2020-05-21 19:04                                   ` Paul E. McKenney
2020-05-20 18:32                     ` Thomas Gleixner
2020-05-20 19:24                     ` Thomas Gleixner
2020-05-20 19:42                       ` Paul E. McKenney
2020-05-20 17:38                   ` Paul E. McKenney
2020-05-20 17:47                     ` Andy Lutomirski
2020-05-20 18:11                       ` Paul E. McKenney
2020-05-20 14:19               ` Thomas Gleixner
2020-05-27  8:12   ` [tip: x86/entry] " tip-bot2 for Thomas Gleixner
2020-05-15 23:46 ` [patch V6 13/37] x86/entry: Switch page fault exception to IDTENTRY_RAW Thomas Gleixner
2020-05-19 20:12   ` Andy Lutomirski
2020-05-15 23:46 ` [patch V6 14/37] x86/entry: Remove the transition leftovers Thomas Gleixner
2020-05-19 20:13   ` Andy Lutomirski
2020-05-15 23:46 ` [patch V6 15/37] x86/entry: Change exit path of xen_failsafe_callback Thomas Gleixner
2020-05-19 20:14   ` Andy Lutomirski
2020-05-15 23:46 ` [patch V6 16/37] x86/entry/64: Remove error_exit Thomas Gleixner
2020-05-19 20:14   ` Andy Lutomirski
2020-05-15 23:46 ` [patch V6 17/37] x86/entry/32: Remove common_exception Thomas Gleixner
2020-05-19 20:14   ` Andy Lutomirski
2020-05-15 23:46 ` [patch V6 18/37] x86/irq: Use generic irq_regs implementation Thomas Gleixner
2020-05-15 23:46 ` [patch V6 19/37] x86/irq: Convey vector as argument and not in ptregs Thomas Gleixner
2020-05-19 20:19   ` Andy Lutomirski
2020-05-21 13:22     ` Thomas Gleixner
2020-05-22 18:48       ` Boris Ostrovsky
2020-05-22 19:26         ` Josh Poimboeuf
2020-05-22 19:54           ` Thomas Gleixner
2020-05-15 23:46 ` [patch V6 20/37] x86/irq/64: Provide handle_irq() Thomas Gleixner
2020-05-19 20:21   ` Andy Lutomirski
2020-05-15 23:46 ` [patch V6 21/37] x86/entry: Add IRQENTRY_IRQ macro Thomas Gleixner
2020-05-19 20:27   ` Andy Lutomirski
2020-05-15 23:46 ` [patch V6 22/37] x86/entry: Use idtentry for interrupts Thomas Gleixner
2020-05-19 20:28   ` Andy Lutomirski
2020-05-15 23:46 ` [patch V6 23/37] x86/entry: Provide IDTENTRY_SYSVEC Thomas Gleixner
2020-05-20  0:29   ` Andy Lutomirski
2020-05-20 15:07     ` Thomas Gleixner
2020-05-15 23:46 ` [patch V6 24/37] x86/entry: Convert APIC interrupts to IDTENTRY_SYSVEC Thomas Gleixner
2020-05-20  0:27   ` Andy Lutomirski
2020-05-15 23:46 ` [patch V6 25/37] x86/entry: Convert SMP system vectors " Thomas Gleixner
2020-05-20  0:28   ` Andy Lutomirski
2020-05-15 23:46 ` [patch V6 26/37] x86/entry: Convert various system vectors Thomas Gleixner
2020-05-20  0:30   ` Andy Lutomirski
2020-05-15 23:46 ` [patch V6 27/37] x86/entry: Convert KVM vectors to IDTENTRY_SYSVEC Thomas Gleixner
2020-05-20  0:30   ` Andy Lutomirski
2020-05-15 23:46 ` [patch V6 28/37] x86/entry: Convert various hypervisor " Thomas Gleixner
2020-05-20  0:31   ` Andy Lutomirski
2020-05-15 23:46 ` [patch V6 29/37] x86/entry: Convert XEN hypercall vector " Thomas Gleixner
2020-05-20  0:31   ` Andy Lutomirski
2020-05-15 23:46 ` [patch V6 30/37] x86/entry: Convert reschedule interrupt to IDTENTRY_RAW Thomas Gleixner
2020-05-19 23:57   ` Andy Lutomirski
2020-05-20 15:08     ` Thomas Gleixner
2020-05-15 23:46 ` [patch V6 31/37] x86/entry: Remove the apic/BUILD interrupt leftovers Thomas Gleixner
2020-05-20  0:32   ` Andy Lutomirski
2020-05-15 23:46 ` [patch V6 32/37] x86/entry/64: Remove IRQ stack switching ASM Thomas Gleixner
2020-05-20  0:33   ` Andy Lutomirski
2020-05-15 23:46 ` [patch V6 33/37] x86/entry: Make enter_from_user_mode() static Thomas Gleixner
2020-05-20  0:34   ` Andy Lutomirski
2020-05-15 23:46 ` [patch V6 34/37] x86/entry/32: Remove redundant irq disable code Thomas Gleixner
2020-05-20  0:35   ` Andy Lutomirski
2020-05-15 23:46 ` [patch V6 35/37] x86/entry/64: Remove TRACE_IRQS_*_DEBUG Thomas Gleixner
2020-05-20  0:46   ` Andy Lutomirski
2020-05-15 23:46 ` [patch V6 36/37] x86/entry: Move paranoid irq tracing out of ASM code Thomas Gleixner
2020-05-20  0:53   ` Andy Lutomirski
2020-05-20 15:16     ` Thomas Gleixner
2020-05-20 17:13       ` Andy Lutomirski
2020-05-20 18:33         ` Thomas Gleixner
2020-05-15 23:46 ` [patch V6 37/37] x86/entry: Remove the TRACE_IRQS cruft Thomas Gleixner
2020-05-18 23:07   ` Andy Lutomirski
2020-05-16 17:18 ` [patch V6 00/37] x86/entry: Rework leftovers and merge plan Paul E. McKenney
2020-05-19 12:28   ` Joel Fernandes
2020-05-18 16:07 ` Peter Zijlstra
2020-05-18 18:53   ` Thomas Gleixner
2020-05-19  8:29     ` Peter Zijlstra
2020-05-18 20:24   ` Thomas Gleixner
2020-05-19  8:38     ` Peter Zijlstra
2020-05-19  9:02       ` Peter Zijlstra
2020-05-23  2:52         ` Lai Jiangshan
2020-05-23 13:08           ` Peter Zijlstra
2020-06-15 16:17             ` Peter Zijlstra
2020-05-19  9:06       ` Thomas Gleixner
2020-05-19 18:37 ` Steven Rostedt
2020-05-19 19:09   ` Thomas Gleixner
2020-05-19 19:13     ` Steven Rostedt

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200520232550.GA20812@paulmck-ThinkPad-P72 \
    --to=paulmck@kernel.org \
    --cc=alexandre.chartre@oracle.com \
    --cc=boris.ostrovsky@oracle.com \
    --cc=brgerst@gmail.com \
    --cc=frederic@kernel.org \
    --cc=jason.cj.chen@intel.com \
    --cc=jgross@suse.com \
    --cc=joel@joelfernandes.org \
    --cc=jpoimboe@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=luto@kernel.org \
    --cc=mathieu.desnoyers@efficios.com \
    --cc=mhiramat@kernel.org \
    --cc=mikelley@microsoft.com \
    --cc=pbonzini@redhat.com \
    --cc=peterz@infradead.org \
    --cc=pmladek@suse.com \
    --cc=rostedt@goodmis.org \
    --cc=sean.j.christopherson@intel.com \
    --cc=tglx@linutronix.de \
    --cc=thomas.lendacky@amd.com \
    --cc=wei.liu@kernel.org \
    --cc=will@kernel.org \
    --cc=x86@kernel.org \
    --cc=yakui.zhao@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.