Received: by 2002:ac0:bc90:0:0:0:0:0 with SMTP id a16csp4651066img; Tue, 26 Mar 2019 13:50:23 -0700 (PDT) X-Google-Smtp-Source: APXvYqyx/4P8X+BlWnwlUk4I1K+oZl8njgvAbeQhKDhKcbOIsLCZ+ucU9DMsQMGkI5fyCpF2tVnZ X-Received: by 2002:a65:4bcc:: with SMTP id p12mr30961020pgr.187.1553633423026; Tue, 26 Mar 2019 13:50:23 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1553633423; cv=none; d=google.com; s=arc-20160816; b=kK0wOsYNgJr6ETwAo2UiOYkoE5eOUPaUVrG2cqkYwQT8XUo+kEjO31Dom8vRVHSOTi +cnx+gh8U6/SIyS7OK7CxbP/q8ullX3AQZGcAmhtAg8lvkckSIdYvx1C4cy9p9wg8yet s87n68/vanAU54+nI5gfyZJc7mi+VWprSNEAWsw4zXhS7fckuFrTYSELOSpHPhY0HDHs X5oGcN3hWEJEWGASaVDqjNxJC1U4toxejIq+jB36auvL8gzvYDQPtkpptGjWe9UI+1yC 9vbT5zlgd9chZzkkAUvqkA6aEydSVr1Ca1uBWVoM7YmQvaHPqmRO9zTd98Ik4fdEtQ0W irDQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:user-agent:references :message-id:in-reply-to:subject:cc:to:from:date; bh=UB3UdArSxYzqk+oLAPLF9WcRW6lniu+QppyUKih1Dug=; b=bl6vNW/dmHsrl8nX3fUdKUYegthkb+BKwvytC3N6XzXwh9B+ltBSMm/wf4ySvcEYxH GADZSE7UGMeKDPNwWklkJkyCLa2sgy4oBlKFRrsQB9ra2ExhSdPbQxxPnwDZFFfrhOJA PEdbPunMBeBWN0mDZLHMjJpSPBrNJ8hIyu5SgUA53lt1vVJ3TEuSml840XQPpb0w/bQd srtbrVN5m4IViM2MyDgu8HoqpBPDKwXewzoEQ0/4SfWxpzmKjWDFnF+CzNCWUpi31gm2 fw6sdZipCMUAt0fQm4w9DsRpsI2CF86K/BODM4/TItRWcCrswoeEkYknXZu+i0+QWxrG v2oQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id k66si8614441pgc.247.2019.03.26.13.50.07; Tue, 26 Mar 2019 13:50:23 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732716AbfCZUt3 (ORCPT + 99 others); Tue, 26 Mar 2019 16:49:29 -0400 Received: from Galois.linutronix.de ([146.0.238.70]:49315 "EHLO Galois.linutronix.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731736AbfCZUt3 (ORCPT ); Tue, 26 Mar 2019 16:49:29 -0400 Received: from p5492e2fc.dip0.t-ipconnect.de ([84.146.226.252] helo=nanos) by Galois.linutronix.de with esmtpsa (TLS1.2:DHE_RSA_AES_256_CBC_SHA256:256) (Exim 4.80) (envelope-from ) id 1h8t0U-00061t-Ei; Tue, 26 Mar 2019 21:49:14 +0100 Date: Tue, 26 Mar 2019 21:49:13 +0100 (CET) From: Thomas Gleixner To: Ricardo Neri cc: Ingo Molnar , Borislav Petkov , Ashok Raj , Andi Kleen , Peter Zijlstra , "Ravi V. Shankar" , x86@kernel.org, linux-kernel@vger.kernel.org, Ricardo Neri , "H. Peter Anvin" , Tony Luck , Clemens Ladisch , Arnd Bergmann , Philippe Ombredanne , Kate Stewart , "Rafael J. Wysocki" , Mimi Zohar , Jan Kiszka , Nick Desaulniers , Masahiro Yamada , Nayna Jain Subject: Re: [RFC PATCH v2 11/14] x86/watchdog/hardlockup: Add an HPET-based hardlockup detector In-Reply-To: <1551283518-18922-12-git-send-email-ricardo.neri-calderon@linux.intel.com> Message-ID: References: <1551283518-18922-1-git-send-email-ricardo.neri-calderon@linux.intel.com> <1551283518-18922-12-git-send-email-ricardo.neri-calderon@linux.intel.com> User-Agent: Alpine 2.21 (DEB 202 2017-01-01) MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII X-Linutronix-Spam-Score: -1.0 X-Linutronix-Spam-Level: - X-Linutronix-Spam-Status: No , -1.0 points, 5.0 required, ALL_TRUSTED=-1,SHORTCIRCUIT=-0.0001 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Wed, 27 Feb 2019, Ricardo Neri wrote: > +/** > + * get_count() - Get the current count of the HPET timer > + * > + * Returns: > + * > + * Value of the main counter of the HPET timer The extra newline is not required IIRC. Returns: Value ...... shoud be sufficient and spares a lot of lines all over the place. > + */ > +static inline unsigned long get_count(void) > +{ > + return hpet_readq(HPET_COUNTER); > +} > + > +/** > + * set_comparator() - Update the comparator in an HPET timer instance > + * @hdata: A data structure with the timer instance to update > + * @cmp: The value to write in the in the comparator registere > + * > + * Returns: > + * > + * None Returns: None is not required and provides no additional value. I appreciate that you try to document your functions extensively, but please apply common sense whether it really provides value. Documenting the obvious is not necessarily an improvement. > + */ > +static inline void set_comparator(struct hpet_hld_data *hdata, > + unsigned long cmp) > +{ > + hpet_writeq(cmp, HPET_Tn_CMP(hdata->num)); > +} Also do we really need wrappers plus N lines documentation for hpet_readq/writeq()? I fail to see the benefit. It's just increasing the line count for no reason and requires the reader to lookup functions which are just cosmetic wrappers. > +/** > + * hardlockup_detector_nmi_handler() - NMI Interrupt handler > + * @val: Attribute associated with the NMI. Not used. > + * @regs: Register values as seen when the NMI was asserted > + * > + * When in NMI context, check if it was caused by the expiration of the When in NMI context? This function _IS_ called in NMI context, right? > + * HPET timer. If yes, create a CPU mask to issue an IPI to the rest > + * of monitored CPUs. Upon receiving their own NMI, the other CPUs will > + * check such mask to determine if they need to also look for lockups. > + * > + * Returns: > + * > + * NMI_DONE if the HPET timer did not cause the interrupt. NMI_HANDLED > + * otherwise. > + */ > +static int hardlockup_detector_nmi_handler(unsigned int val, > + struct pt_regs *regs) > +{ > + struct hpet_hld_data *hdata = hld_data; > + unsigned int cpu = smp_processor_id(); > + > + if (is_hpet_wdt_interrupt(hdata)) { > + /* Get ready to check other CPUs for hardlockups. */ > + cpumask_copy(&hdata->cpu_monitored_mask, > + watchdog_get_allowed_cpumask()); > + cpumask_clear_cpu(smp_processor_id(), > + &hdata->cpu_monitored_mask); Why are you copying the mask in NMI context and not updating it when the watchdog enable/disable functions are called? Also the mask can contain offline CPUs, which is bad because the send IPI function expects offline CPUs to be cleared. Clearing the CPU in the mask is not required because the function you invoke: > + apic->send_IPI_mask_allbutself(&hdata->cpu_monitored_mask, > + NMI_VECTOR); has it's name for a reason. > + > + kick_timer(hdata, !(hdata->flags & HPET_DEV_PERI_CAP)); > + > + inspect_for_hardlockups(regs); > + > + return NMI_HANDLED; > + } > + > + if (cpumask_test_and_clear_cpu(cpu, &hdata->cpu_monitored_mask)) { > + inspect_for_hardlockups(regs); > + return NMI_HANDLED; > + } So if the function above returns false, then why would you try to check that CPU mask and invoke the inspection? You already cleared that bit above. This part is pointless and can be removed. > +void hardlockup_detector_hpet_enable(void) > +{ > + struct cpumask *allowed = watchdog_get_allowed_cpumask(); > + unsigned int cpu = smp_processor_id(); This is called from a function which has already a cpu argument. Why aren't you handing that in? > + > + if (!hld_data) > + return; Why can hld_data be NULL here? > + > + hld_data->handling_cpu = cpumask_first(allowed); > + > + if (cpu == hld_data->handling_cpu) { > + update_handling_cpu(hld_data); > + /* Force timer kick when detector is just enabled */ > + kick_timer(hld_data, true); > + enable_timer(hld_data); > + } That logic is wrong. The per cpu enable function is called via: softlockup_start_all() for_each_cpu(cpu, &watchdog_allowed_mask) smp_call_on_cpu(cpu, softlockup_start_fn, NULL, false); ---> softlockup_start_fn() watchdog_enable() OR lockup_detector_online_cpu() watchdog_enable() The latter is a CPU hotplug operation. The former is invoked when the watchdog is (re)configured in the core code. Both are mutually exclusive and guarantee to never invoke the function concurrently on multiple CPUs. So way you should handle this is: cpumask_set_cpu(cpu, hld_data->cpu_monitored_mask); if (!hld_data->enabled_cpus++) { hld_data->handling_cpu = cpu; kick_timer(); enable_timer(); } The cpu mask starts off empty and each CPU sets itself when the function is invoked on it. data->enabled_cpus keeps track of the enabled cpus so you avoid reconfiguration just because a different cpu comes online. And it's required for disable as well. > +void hardlockup_detector_hpet_disable(void) > +{ > + struct cpumask *allowed = watchdog_get_allowed_cpumask(); > + > + if (!hld_data) > + return; > + > + /* Only disable the timer if there are no more CPUs to monitor. */ > + if (!cpumask_weight(allowed)) > + disable_timer(hld_data); Again this should be: cpumask_clear_cpu(cpu, hld_data->cpu_monitored_mask); hld_data->enabled_cpus--; if (hld_data->handling_cpu != cpu) return; disable_timer(); if (hld_data->enabled_cpus) return; hld_data->handling_cpu = cpumask_first(hld_data->cpu_monitored_mask); enable_timer(); > +} > + > +/** > + * hardlockup_detector_hpet_stop() - Stop the NMI watchdog on all CPUs > + * > + * Returns: > + * > + * None > + */ > +void hardlockup_detector_hpet_stop(void) > +{ > + disable_timer(hld_data); > +} This function is nowhere used. > +int __init hardlockup_detector_hpet_init(void) > +{ > + int ret; > + > + if (!is_hpet_enabled()) > + return -ENODEV; > + > + if (check_tsc_unstable()) What happens if the TSC becomes unstable _AFTER_ you initialized and started the HPET watchdog? Thanks, tglx