From: Borislav Petkov <bp@amd64.org>
To: Jeroen Van den Keybus <jeroen.vandenkeybus@gmail.com>,
Linus Torvalds <torvalds@linux-foundation.org>,
Thomas Gleixner <tglx@linutronix.de>
Cc: Josh Boyer <jwboyer@gmail.com>,
Clemens Ladisch <clemens@ladisch.de>,
andymatei@gmail.com, "Huang, Shane" <Shane.Huang@amd.com>,
linux-kernel@vger.kernel.org
Subject: Re: Unhandled IRQs on AMD E-450
Date: Mon, 30 Apr 2012 12:21:50 +0200 [thread overview]
Message-ID: <20120430102150.GA9303@aftab.osrc.amd.com> (raw)
In-Reply-To: <CAPRPZsB=axM88+fGkghbh3qxDv=vRQtfrKqT0e59EZPrrS0V+A@mail.gmail.com>
Before you guys get more involved in this, it would be probably prudent
to run this approach by tglx and Linus to check its general sanity :-).
Added.
On Mon, Apr 30, 2012 at 10:29:19AM +0200, Jeroen Van den Keybus wrote:
> > I'd have to go back and look through all the bug reports. Essentially,
> > it wound up being "this used to work fine, now I get stuck in polling
> > mode". When we dropped the patch in those cases, it went back to
> > working fine.
>
> >>> + int unhandled_thresh = 999000;
>
> >>> + if (!irq_poll_and_retry)
> >>> + if (likely(desc->irq_count < 100000))
> >>> + return;
> >>> + else
> >>> + if (likely(desc->irq_count < 10))
> >>> + return;
>
> Given these 2 bugs I wouldn't rule out that this code would have
> malfunctioned, causing the problematic behaviour you describe. Are
> these problematic machines still available ?
>
> I have attached a patch for 3.2.16. It is essential that it is also
> tested by people NOT having any problems. What the patch does:
>
> - Detect stuck interrupts after a number (5) of consecutive unhandled
> IRQs within a specified time frame (100ms).
> - Start polling the handler for this interrupt at a preset (100Hz)
> rate for a preset number of cycles (100).
> - Retry and enable the interrupt line and start the cycle over.
> - Report the first occurrences of this process for a preset number of times (5).
> - Expose the state of stuck_count in /proc/irq/<irq>/spurious. It is
> the number of times an interrupt was deemed stuck.
> - Expose the state of stuck_level_max in /proc/irq/<irq>/spurious. It
> indicates how many consecutive unhandled interrupts were detected
> since the last reset (system startup or reenabling of the interrupt
> line). It is intended for interrupt system diagnostics.
>
> Using '$ cat /proc/irq/*/spurious' one can get a quick overview of the
> operating state of the patch:
>
> - Normal interrupts will have count=0 and level_max=0. Under some
> circumstances, a driver may have already performed work belonging to a
> later occurrence of an interrupt. In this case it may return reporting
> an unhandled event so level_max = 1.
> - The interrupts suffering from the problem allegedly caused by the
> ASM1083 PCI bridge will have a count > 0, but not increasing more
> rapidly than 1 / sec.
> - The interrupts which are genuinely stuck will show a steady (1 /
> sec) increase of count.
>
> This patch does not include modifications to drivers/pci/quirks.c to
> set the SATA interrupt mode of the Asus E450-M1 PRO to MSI. See
> earlier patches for that.
>
> Please test and/or comment.
>
>
> Signed-off-by: Jeroen Van den Keybus <jeroen.vandenkeybus@gmail.com>
> ---
> diff -upr linux-3.2.16.orig/include/linux/irqdesc.h
> linux-3.2.16.new/include/linux/irqdesc.h
> --- linux-3.2.16.orig/include/linux/irqdesc.h 2012-04-23
> 00:31:32.000000000 +0200
> +++ linux-3.2.16.new/include/linux/irqdesc.h 2012-04-29 16:33:48.142332693 +0200
> @@ -14,28 +14,29 @@ struct timer_rand_state;
> struct module;
> /**
> * struct irq_desc - interrupt descriptor
> - * @irq_data: per irq and chip data passed down to chip functions
> - * @timer_rand_state: pointer to timer rand state struct
> - * @kstat_irqs: irq stats per cpu
> - * @handle_irq: highlevel irq-events handler
> - * @preflow_handler: handler called before the flow handler
> (currently used by sparc)
> - * @action: the irq action chain
> - * @status: status information
> - * @core_internal_state__do_not_mess_with_it: core internal status information
> - * @depth: disable-depth, for nested irq_disable() calls
> - * @wake_depth: enable depth, for multiple irq_set_irq_wake() callers
> - * @irq_count: stats field to detect stalled irqs
> - * @last_unhandled: aging timer for unhandled count
> - * @irqs_unhandled: stats field for spurious unhandled interrupts
> - * @lock: locking for SMP
> - * @affinity_hint: hint to user space for preferred irq affinity
> - * @affinity_notify: context for notification of affinity changes
> - * @pending_mask: pending rebalanced interrupts
> - * @threads_oneshot: bitfield to handle shared oneshot threads
> - * @threads_active: number of irqaction threads currently running
> - * @wait_for_threads: wait queue for sync_irq to wait for threaded handlers
> - * @dir: /proc/irq/ procfs entry
> - * @name: flow handler name for /proc/interrupts output
> + * @irq_data: per irq and chip data passed down to chip functions
> + * @timer_rand_state: pointer to timer rand state struct
> + * @kstat_irqs: irq stats per cpu
> + * @handle_irq: highlevel irq-events handler
> + * @preflow_handler: handler called before the flow handler
> (currently used by sparc)
> + * @action: the irq action chain
> + * @status: status information
> + * @istate: core internal status information
> + * @depth: disable-depth, for nested irq_disable() calls
> + * @wake_depth: enable depth, for multiple irq_set_irq_wake() callers
> + * @irqs_stuck_count: stuck interrupt occurrence counter
> + * @irqs_stuck_level: used for stuck interrupt line detection and
> tracking poll cycle count
> + * @irqs_stuck_level_max: indicates the maximum irqs_stuck_level
> since last stuck interrupt occurrence
> + * @irqs_stuck_timeout: deadline for resetting irqs_stuck_level
> + * @lock: locking for SMP
> + * @affinity_hint: hint to user space for preferred irq affinity
> + * @affinity_notify: context for notification of affinity changes
> + * @pending_mask: pending rebalanced interrupts
> + * @threads_oneshot: bitfield to handle shared oneshot threads
> + * @threads_active: number of irqaction threads currently running
> + * @wait_for_threads: wait queue for sync_irq to wait for threaded handlers
> + * @dir: /proc/irq/ procfs entry
> + * @name: flow handler name for /proc/interrupts output
> */
> struct irq_desc {
> struct irq_data irq_data;
> @@ -47,12 +48,13 @@ struct irq_desc {
> #endif
> struct irqaction *action; /* IRQ action list */
> unsigned int status_use_accessors;
> - unsigned int core_internal_state__do_not_mess_with_it;
> + unsigned int istate;
> unsigned int depth; /* nested irq disables */
> unsigned int wake_depth; /* nested wake enables */
> - unsigned int irq_count; /* For detecting broken IRQs */
> - unsigned long last_unhandled; /* Aging timer for unhandled count */
> - unsigned int irqs_unhandled;
> + unsigned int irqs_stuck_count;
> + unsigned int irqs_stuck_level;
> + unsigned int irqs_stuck_level_max;
> + unsigned long irqs_stuck_timeout;
> raw_spinlock_t lock;
> struct cpumask *percpu_enabled;
> #ifdef CONFIG_SMP
> diff -upr linux-3.2.16.orig/kernel/irq/debug.h
> linux-3.2.16.new/kernel/irq/debug.h
> --- linux-3.2.16.orig/kernel/irq/debug.h 2012-04-23 00:31:32.000000000 +0200
> +++ linux-3.2.16.new/kernel/irq/debug.h 2012-04-29 16:35:33.782919592 +0200
> @@ -11,8 +11,9 @@
>
> static inline void print_irq_desc(unsigned int irq, struct irq_desc *desc)
> {
> - printk("irq %d, desc: %p, depth: %d, count: %d, unhandled: %d\n",
> - irq, desc, desc->depth, desc->irq_count, desc->irqs_unhandled);
> + printk("irq %d, desc: %p, depth: %d, stuck_count: %d, stuck_level:
> %d, stuck_level_max: %d, stuck_timeout: %lu\n",
> + irq, desc, desc->depth, desc->irqs_stuck_count,
> desc->irqs_stuck_level, desc->irqs_stuck_level_max,
> + desc->irqs_stuck_timeout);
> printk("->handle_irq(): %p, ", desc->handle_irq);
> print_symbol("%s\n", (unsigned long)desc->handle_irq);
> printk("->irq_data.chip(): %p, ", desc->irq_data.chip);
> diff -upr linux-3.2.16.orig/kernel/irq/internals.h
> linux-3.2.16.new/kernel/irq/internals.h
> --- linux-3.2.16.orig/kernel/irq/internals.h 2012-04-23 00:31:32.000000000 +0200
> +++ linux-3.2.16.new/kernel/irq/internals.h 2012-04-28 22:01:15.124279391 +0200
> @@ -13,9 +13,7 @@
> # define IRQ_BITMAP_BITS NR_IRQS
> #endif
>
> -#define istate core_internal_state__do_not_mess_with_it
> -
> -extern int noirqdebug;
> +extern bool noirqdebug;
>
> /*
> * Bits used by threaded handlers:
> diff -upr linux-3.2.16.orig/kernel/irq/irqdesc.c
> linux-3.2.16.new/kernel/irq/irqdesc.c
> --- linux-3.2.16.orig/kernel/irq/irqdesc.c 2012-04-23 00:31:32.000000000 +0200
> +++ linux-3.2.16.new/kernel/irq/irqdesc.c 2012-04-29 16:00:01.792104426 +0200
> @@ -84,8 +84,10 @@ static void desc_set_defaults(unsigned i
> irqd_set(&desc->irq_data, IRQD_IRQ_DISABLED);
> desc->handle_irq = handle_bad_irq;
> desc->depth = 1;
> - desc->irq_count = 0;
> - desc->irqs_unhandled = 0;
> + desc->irqs_stuck_count = 0;
> + desc->irqs_stuck_level = 0;
> + desc->irqs_stuck_level_max = 0;
> + desc->irqs_stuck_timeout = jiffies;
> desc->name = NULL;
> desc->owner = owner;
> for_each_possible_cpu(cpu)
> diff -upr linux-3.2.16.orig/kernel/irq/manage.c
> linux-3.2.16.new/kernel/irq/manage.c
> --- linux-3.2.16.orig/kernel/irq/manage.c 2012-04-23 00:31:32.000000000 +0200
> +++ linux-3.2.16.new/kernel/irq/manage.c 2012-04-30 09:26:26.395627416 +0200
> @@ -1087,8 +1087,10 @@ __setup_irq(unsigned int irq, struct irq
> *old_ptr = new;
>
> /* Reset broken irq detection when installing new handler */
> - desc->irq_count = 0;
> - desc->irqs_unhandled = 0;
> + desc->irqs_stuck_count = 0;
> + desc->irqs_stuck_level = 0;
> + desc->irqs_stuck_level_max = 0;
> + desc->irqs_stuck_timeout = jiffies;
>
> /*
> * Check whether we disabled the irq via the spurious handler
> diff -upr linux-3.2.16.orig/kernel/irq/proc.c linux-3.2.16.new/kernel/irq/proc.c
> --- linux-3.2.16.orig/kernel/irq/proc.c 2012-04-23 00:31:32.000000000 +0200
> +++ linux-3.2.16.new/kernel/irq/proc.c 2012-04-29 16:34:17.642434577 +0200
> @@ -248,9 +248,9 @@ static int irq_spurious_proc_show(struct
> {
> struct irq_desc *desc = irq_to_desc((long) m->private);
>
> - seq_printf(m, "count %u\n" "unhandled %u\n" "last_unhandled %u ms\n",
> - desc->irq_count, desc->irqs_unhandled,
> - jiffies_to_msecs(desc->last_unhandled));
> + seq_printf(m, "irq=%3d stuck_count=%3u stuck_level_max=%3u\n",
> + desc->irq_data.irq,
> + desc->irqs_stuck_count, desc->irqs_stuck_level_max);
> return 0;
> }
>
> diff -upr linux-3.2.16.orig/kernel/irq/spurious.c
> linux-3.2.16.new/kernel/irq/spurious.c
> --- linux-3.2.16.orig/kernel/irq/spurious.c 2012-04-23 00:31:32.000000000 +0200
> +++ linux-3.2.16.new/kernel/irq/spurious.c 2012-04-29 18:26:59.848127250 +0200
> @@ -18,7 +18,12 @@
>
> static int irqfixup __read_mostly;
>
> -#define POLL_SPURIOUS_IRQ_INTERVAL (HZ/10)
> +#define SPURIOUS_IRQ_TIMEOUT_INTERVAL (HZ/10)
> +#define SPURIOUS_IRQ_TRIGGER 5
> +#define SPURIOUS_IRQ_REPORT_COUNT 5
> +#define SPURIOUS_IRQ_POLL_CYCLES 100
> +#define SPURIOUS_IRQ_POLL_INTERVAL (HZ/100)
> +
> static void poll_spurious_irqs(unsigned long dummy);
> static DEFINE_TIMER(poll_spurious_irq_timer, poll_spurious_irqs, 0, 0);
> static int irq_poll_cpu;
> @@ -141,14 +146,15 @@ out:
> static void poll_spurious_irqs(unsigned long dummy)
> {
> struct irq_desc *desc;
> - int i;
> + int i, poll_again;
>
> if (atomic_inc_return(&irq_poll_active) != 1)
> goto out;
> irq_poll_cpu = smp_processor_id();
>
> + poll_again = 0; /* Will stay false as long as no polling candidate is found */
> for_each_irq_desc(i, desc) {
> - unsigned int state;
> + unsigned int state, irq;
>
> if (!i)
> continue;
> @@ -158,15 +164,38 @@ static void poll_spurious_irqs(unsigned
> barrier();
> if (!(state & IRQS_SPURIOUS_DISABLED))
> continue;
> -
> - local_irq_disable();
> - try_one_irq(i, desc, true);
> - local_irq_enable();
> +
> + /* We end up here with a disabled stuck interrupt.
> + desc->irqs_stuck_level now tracks the number of times
> + the interrupt has been polled */
> +
> + irq = desc->irq_data.irq;
> + if (unlikely(desc->irqs_stuck_level == 1))
> + if (desc->irqs_stuck_count <= SPURIOUS_IRQ_REPORT_COUNT)
> + printk(KERN_EMERG "Polling handlers for IRQ %d.\n", irq);
> + if (desc->irqs_stuck_level < SPURIOUS_IRQ_POLL_CYCLES) {
> + local_irq_disable();
> + try_one_irq(i, desc, true);
> + local_irq_enable();
> + desc->irqs_stuck_level++;
> + poll_again = 1;
> + } else {
> + if (unlikely(desc->irqs_stuck_count <= SPURIOUS_IRQ_REPORT_COUNT)) {
> + printk(KERN_EMERG "Reenabling IRQ %d.\n", irq);
> + if (desc->irqs_stuck_count >= SPURIOUS_IRQ_REPORT_COUNT)
> + printk(KERN_EMERG "No more stuck interrupt reports for IRQ %d.\n", irq);
> + }
> + irq_enable(desc); /* Reenable the interrupt line */
> + desc->depth--;
> + desc->istate &= (~IRQS_SPURIOUS_DISABLED);
> + desc->irqs_stuck_level = 0;
> + }
> }
> + if (poll_again)
> + mod_timer(&poll_spurious_irq_timer,
> + jiffies + SPURIOUS_IRQ_POLL_INTERVAL);
> out:
> atomic_dec(&irq_poll_active);
> - mod_timer(&poll_spurious_irq_timer,
> - jiffies + POLL_SPURIOUS_IRQ_INTERVAL);
> }
>
> static inline int bad_action_ret(irqreturn_t action_ret)
> @@ -176,14 +205,6 @@ static inline int bad_action_ret(irqretu
> return 1;
> }
>
> -/*
> - * If 99,900 of the previous 100,000 interrupts have not been handled
> - * then assume that the IRQ is stuck in some manner. Drop a diagnostic
> - * and try to turn the IRQ off.
> - *
> - * (The other 100-of-100,000 interrupts may have been a correctly
> - * functioning device sharing an IRQ with the failing one)
> - */
> static void
> __report_bad_irq(unsigned int irq, struct irq_desc *desc,
> irqreturn_t action_ret)
> @@ -272,7 +293,6 @@ void note_interrupt(unsigned int irq, st
> if (desc->istate & IRQS_POLL_INPROGRESS)
> return;
>
> - /* we get here again via the threaded handler */
> if (action_ret == IRQ_WAKE_THREAD)
> return;
>
> @@ -281,55 +301,47 @@ void note_interrupt(unsigned int irq, st
> return;
> }
>
> - if (unlikely(action_ret == IRQ_NONE)) {
> - /*
> - * If we are seeing only the odd spurious IRQ caused by
> - * bus asynchronicity then don't eventually trigger an error,
> - * otherwise the counter becomes a doomsday timer for otherwise
> - * working systems
> - */
> - if (time_after(jiffies, desc->last_unhandled + HZ/10))
> - desc->irqs_unhandled = 1;
> - else
> - desc->irqs_unhandled++;
> - desc->last_unhandled = jiffies;
> - }
> -
> - if (unlikely(try_misrouted_irq(irq, desc, action_ret))) {
> - int ok = misrouted_irq(irq);
> - if (action_ret == IRQ_NONE)
> - desc->irqs_unhandled -= ok;
> + /* Adjust action_ret if an optional poll was successful.
> + (See inlined try_misrouted_irq() for conditions (depending
> + on 'irqfixup' and 'irqpoll'), and 'noirqdebug' must not
> + be set, since we wouldn't be here (note_interrupt())
> + at all in that case.) */
> + if (unlikely(try_misrouted_irq(irq, desc, action_ret)))
> + if (misrouted_irq(irq))
> + action_ret = IRQ_HANDLED;
> +
> + if (unlikely(action_ret == IRQ_NONE) && time_before(jiffies,
> desc->irqs_stuck_timeout)) {
> + desc->irqs_stuck_level++;
> + if (desc->irqs_stuck_level > desc->irqs_stuck_level_max)
> + desc->irqs_stuck_level_max = desc->irqs_stuck_level;
> + if (desc->irqs_stuck_level >= SPURIOUS_IRQ_TRIGGER) { /* The
> interrupt is stuck */
> + desc->irqs_stuck_count++; /* TODO: Prevent hypothetical overflow */
> + if (desc->irqs_stuck_count <= SPURIOUS_IRQ_REPORT_COUNT) {
> + __report_bad_irq(irq, desc, action_ret);
> + printk(KERN_EMERG "Disabling IRQ %d.\n", irq);
> + }
> + desc->istate |= IRQS_SPURIOUS_DISABLED;
> + desc->depth++;
> + irq_disable(desc);
> + /* TODO: Do a safe access to the timer. Now we may be extending a deadline
> + for a polling system already running for another interrupt. */
> + mod_timer(&poll_spurious_irq_timer,
> + jiffies + SPURIOUS_IRQ_POLL_INTERVAL); /* Schedule a poll cycle */
> + desc->irqs_stuck_level = 1;
> + desc->irqs_stuck_level_max = 0;
> + }
> }
> -
> - desc->irq_count++;
> - if (likely(desc->irq_count < 100000))
> - return;
> -
> - desc->irq_count = 0;
> - if (unlikely(desc->irqs_unhandled > 99900)) {
> - /*
> - * The interrupt is stuck
> - */
> - __report_bad_irq(irq, desc, action_ret);
> - /*
> - * Now kill the IRQ
> - */
> - printk(KERN_EMERG "Disabling IRQ #%d\n", irq);
> - desc->istate |= IRQS_SPURIOUS_DISABLED;
> - desc->depth++;
> - irq_disable(desc);
> -
> - mod_timer(&poll_spurious_irq_timer,
> - jiffies + POLL_SPURIOUS_IRQ_INTERVAL);
> + else {
> + desc->irqs_stuck_timeout = jiffies + SPURIOUS_IRQ_TIMEOUT_INTERVAL;
> + desc->irqs_stuck_level = 0;
> }
> - desc->irqs_unhandled = 0;
> }
>
> -int noirqdebug __read_mostly;
> +bool noirqdebug __read_mostly;
>
> int noirqdebug_setup(char *str)
> {
> - noirqdebug = 1;
> + noirqdebug = true;
> printk(KERN_INFO "IRQ lockup detection disabled\n");
>
> return 1;
>
--
Regards/Gruss,
Boris.
Advanced Micro Devices GmbH
Einsteinring 24, 85609 Dornach
GM: Alberto Bozzo
Reg: Dornach, Landkreis Muenchen
HRB Nr. 43632 WEEE Registernr: 129 19551
next prev parent reply other threads:[~2012-04-30 10:22 UTC|newest]
Thread overview: 40+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <fa.CZQqvHf3CBfYWzhSDPNOWxTTD9w@ifi.uio.no>
[not found] ` <fa.Vmg5vDod2/oKvwyy9BcalhoT+Lo@ifi.uio.no>
2012-04-25 8:35 ` Unhandled IRQs on AMD E-450 andymatei
2012-04-25 8:48 ` Clemens Ladisch
2012-04-27 8:22 ` Borislav Petkov
2012-04-27 8:29 ` Andrei Matei
2012-04-27 11:46 ` Jeroen Van den Keybus
2012-04-27 13:06 ` Josh Boyer
2012-04-27 13:28 ` Jeroen Van den Keybus
2012-04-27 13:49 ` Josh Boyer
2012-04-30 8:29 ` Jeroen Van den Keybus
2012-04-30 9:57 ` Clemens Ladisch
2012-04-30 10:41 ` Jeroen Van den Keybus
2012-04-30 12:47 ` Clemens Ladisch
2012-05-29 22:20 ` Grant Likely
2012-04-30 10:21 ` Borislav Petkov [this message]
2012-04-30 11:35 ` Jeroen Van den Keybus
2012-05-29 23:36 ` Grant Likely
2012-05-30 0:07 ` Thomas Gleixner
2012-05-30 10:44 ` Borislav Petkov
[not found] <fa.Tzg9rJm1oEMGIL8eap99R7gLU4Q@ifi.uio.no>
[not found] ` <fa.Yw7gRhZrXlfCxofC1BHK22C+oTk@ifi.uio.no>
[not found] ` <fa.l7CBcHbzr+l317AuKP87w9mccUk@ifi.uio.no>
[not found] ` <fa.VXfk4ts2TBVKqgBQtfGn6RQHemg@ifi.uio.no>
2012-04-25 8:48 ` andymatei
2011-11-29 21:44 Jeroen Van den Keybus
2011-11-30 8:30 ` Clemens Ladisch
2011-11-30 15:44 ` Borislav Petkov
2011-12-01 8:01 ` Huang, Shane
2011-12-03 20:36 ` Jeroen Van den Keybus
2011-12-04 12:48 ` Clemens Ladisch
2011-12-04 13:36 ` Jeroen Van den Keybus
2011-12-04 13:54 ` Jeroen Van den Keybus
2011-12-04 14:08 ` Jeroen Van den Keybus
2011-12-04 15:06 ` Jeroen Van den Keybus
2011-12-04 16:59 ` Clemens Ladisch
2011-12-06 0:06 ` Jeroen Van den Keybus
2011-12-08 11:33 ` Jeroen Van den Keybus
2011-12-08 12:45 ` Clemens Ladisch
2011-12-08 21:27 ` Jeroen Van den Keybus
2011-12-09 8:22 ` Clemens Ladisch
2011-12-09 11:17 ` Jeroen Van den Keybus
2011-12-09 12:55 ` Clemens Ladisch
2011-12-10 12:10 ` Jeroen Van den Keybus
2011-12-10 17:58 ` Clemens Ladisch
2011-12-11 15:28 ` Jeroen Van den Keybus
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=20120430102150.GA9303@aftab.osrc.amd.com \
--to=bp@amd64.org \
--cc=Shane.Huang@amd.com \
--cc=andymatei@gmail.com \
--cc=clemens@ladisch.de \
--cc=jeroen.vandenkeybus@gmail.com \
--cc=jwboyer@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=tglx@linutronix.de \
--cc=torvalds@linux-foundation.org \
/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.