From patchwork Mon Aug 19 20:37:00 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Corey Minyard X-Patchwork-Id: 11101887 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 163D5912 for ; Mon, 19 Aug 2019 20:37:17 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id E93A12087E for ; Mon, 19 Aug 2019 20:37:16 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="KMc7Ap1M" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728123AbfHSUhQ (ORCPT ); Mon, 19 Aug 2019 16:37:16 -0400 Received: from mail-ot1-f67.google.com ([209.85.210.67]:39288 "EHLO mail-ot1-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727925AbfHSUhQ (ORCPT ); Mon, 19 Aug 2019 16:37:16 -0400 Received: by mail-ot1-f67.google.com with SMTP id b1so2941860otp.6 for ; Mon, 19 Aug 2019 13:37:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :reply-to; bh=iLqG1t8+/PTpNEIjQR2/+lndK9RpJjp57v9VioZj6nE=; b=KMc7Ap1MDd9HgzzVS3/sw8iobOtU/GyVO0xF26eMQ1zGDc08ufYllS1CRhujLvvUVr IJzUc6YEAgV2KN5DBsca/fvjB8TbABlP2g2Wh/9GdyauYN2nFc6pNdP3+8h1LeZ0e0so xuTzZkx7+8vb6ApnXbuJ8Ft/lfpL5Hk0nnvBqReCvylxL92jE1qNUsvnvKvRYKNE+Wpk cjU0jL8jD2CworanpR7uu+DPC7GIpj9FS2Zc8qNygdME5PovSu0ibXJwelM+iL+3mqPD oPmBUQVuq9GAcZOpdzNUOAoDfmYGJCLNDLPGXy5yJlhh9ebKHRdsmpsLibXNfEo9MVCG gYhA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references:reply-to; bh=iLqG1t8+/PTpNEIjQR2/+lndK9RpJjp57v9VioZj6nE=; b=hqrKJpPq2GIJmVsFc9DFBepOvvsNaJ+40yeQrB4qSLYvJ4dexaWyPnuukLK8de/7S7 EmbakXiHMXSicMSaD6FdNSTs9IHOL2QhrLp5RpD6qXzjUOgarf9ylX8jMKOOpgMRf4Br tKts8eT+eF45DcX9eqMMLWamDw6u8VTfMaeIJ5yCreMnKJWDMuea+NHp0JrkbpWTBMG/ 4Zg5OYg4T1D3AcnXHDzphxJxz5dd94WKRtaVa84kHDIrpLuCBOHOh4fs+d52jVVDWYGh srLwfJ+/WODczfuJRgSWiUBBKQ/L8BW/D52T0HZLs+PlTv7IElx4yGfmLJa+7pFEe8EJ a0xA== X-Gm-Message-State: APjAAAUqY1qCj1s01y9x2tTaHCR3VkLpngx+mZAK8tWEF3+3S18tj3mE lREO79edsdMcUo1GLs8A+Q== X-Google-Smtp-Source: APXvYqy/+cnQpCKiUlrwnvRuoN/Zjt1X8xscJZhyp82eQfxTBHp5Bo8ixf2Rt8tKnLx33ZEljbCdyw== X-Received: by 2002:a9d:5f10:: with SMTP id f16mr20636986oti.320.1566247035138; Mon, 19 Aug 2019 13:37:15 -0700 (PDT) Received: from serve.minyard.net ([47.184.134.43]) by smtp.gmail.com with ESMTPSA id o26sm5444482otl.34.2019.08.19.13.37.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2019 13:37:14 -0700 (PDT) Received: from t430.minyard.net (t430m.minyard.net [192.168.27.3]) by serve.minyard.net (Postfix) with ESMTPA id F29221800D4; Mon, 19 Aug 2019 20:37:13 +0000 (UTC) Received: by t430.minyard.net (Postfix, from userid 1000) id D8887301176; Mon, 19 Aug 2019 15:37:13 -0500 (CDT) From: minyard@acm.org To: linux-watchdog@vger.kernel.org, Guenter Roeck , Wim Van Sebroeck Cc: Corey Minyard Subject: [PATCH 01/12] watchdog: NULL the default governor if it is unregistered Date: Mon, 19 Aug 2019 15:37:00 -0500 Message-Id: <20190819203711.32599-2-minyard@acm.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190819203711.32599-1-minyard@acm.org> References: <20190819203711.32599-1-minyard@acm.org> Reply-To: "[PATCH 00/12]"@minyard.net, Convert@minyard.net, the@minyard.net, IPMI@minyard.net, watchdog@minyard.net, to@minyard.net, the@minyard.net, standard@minyard.net, interface@minyard.net Sender: linux-watchdog-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-watchdog@vger.kernel.org From: Corey Minyard Otherwise it could be used after being freed. Signed-off-by: Corey Minyard --- drivers/watchdog/watchdog_pretimeout.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/watchdog/watchdog_pretimeout.c b/drivers/watchdog/watchdog_pretimeout.c index 01ca84be240f..b45041b0ef39 100644 --- a/drivers/watchdog/watchdog_pretimeout.c +++ b/drivers/watchdog/watchdog_pretimeout.c @@ -162,6 +162,8 @@ void watchdog_unregister_governor(struct watchdog_governor *gov) break; } } + if (gov == default_gov) + default_gov = NULL; spin_lock_irq(&pretimeout_lock); list_for_each_entry(p, &pretimeout_list, entry) From patchwork Mon Aug 19 20:37:01 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Corey Minyard X-Patchwork-Id: 11101891 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 99B97184E for ; Mon, 19 Aug 2019 20:37:17 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 77AF62087E for ; Mon, 19 Aug 2019 20:37:17 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="L8JavJtT" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727971AbfHSUhR (ORCPT ); Mon, 19 Aug 2019 16:37:17 -0400 Received: from mail-oi1-f193.google.com ([209.85.167.193]:45965 "EHLO mail-oi1-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727769AbfHSUhQ (ORCPT ); Mon, 19 Aug 2019 16:37:16 -0400 Received: by mail-oi1-f193.google.com with SMTP id v12so2355506oic.12 for ; Mon, 19 Aug 2019 13:37:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :reply-to; bh=QhoBQQ6l9oUi8mNhEv5RMMIQeW5JDhE0xlxpkZyDFS4=; b=L8JavJtTgse9YOJRhbFCvvX6TxHvPuJRzUBPp3OPVUeEgXvgcntj7jl7PaFeRhzZ6/ 6QnWk3W/yjzVutHi0HSyL7huQhitbO96Cyve2nIA+t0IOu8fS1nXE4d93zJFQnLZAipY /iT6fvl5w4PwRI5Ok7A8VKbUbes3QSx1brlpn0+EGJ3U38+FY3ilNxx2JrIn1q3t5YCS ym9M0/J9Ay+osWIbzlX7RpJJxWMgzkYhcD/t7fqKbPzpzj2OiNjCCfnmRrdtvYiBUpAj ohIxo1+jCIWKsLwzywQtua6WwW34jpQaYZhJvlbEPMidmq+ODZa2/U76E0SCV81VQ8Oh q5Cg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references:reply-to; bh=QhoBQQ6l9oUi8mNhEv5RMMIQeW5JDhE0xlxpkZyDFS4=; b=ERabBBZyQ6GLgQff193E9npr9bGAbGOx6oYIcaz9GRpFGvIVUiGhLjetyw825PSS6t Hy8lIoNI8ybM+Uq6eAuvlpwht0Qx17wxCEuMr0tdgl5697iBLp77opv7Ki6xlU2Yq1+B zCFHNgFrC7NnWqjuno+LBNvK0v3MDSpVhifRcRT8e5yT7NLahE8autnfV+BQ8972PMl3 sHNVu0qOTDVy33y4dIF7Tcyziq9mIIj7YA5/RYf0Qdjk07H8CqhJNdhGMn3lXBV1Ella GJMHtCMQumU3a50G5smmT38pNGMJLAS9LpjjygBWwmh0BTLbHHxX0n2+y7UfUxw5ZfzV eQbw== X-Gm-Message-State: APjAAAUwHQZW3+jEOatLDZbmtkF6VVXRopNohhL8yk+rTRx48W1iKlLP FciF40LKIfp1KZMn9x7bhA== X-Google-Smtp-Source: APXvYqySq8xAc/CbPL0q1q/EHBG+E0EwI1s2eJFmWnC6zwwo+Zujiqiq+h2pm/62uFVowCQRx89AjQ== X-Received: by 2002:aca:5e06:: with SMTP id s6mr15037232oib.171.1566247034896; Mon, 19 Aug 2019 13:37:14 -0700 (PDT) Received: from serve.minyard.net ([47.184.134.43]) by smtp.gmail.com with ESMTPSA id 13sm5886029otv.14.2019.08.19.13.37.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2019 13:37:14 -0700 (PDT) Received: from t430.minyard.net (t430m.minyard.net [192.168.27.3]) by serve.minyard.net (Postfix) with ESMTPA id 145B41800D5; Mon, 19 Aug 2019 20:37:14 +0000 (UTC) Received: by t430.minyard.net (Postfix, from userid 1000) id E4F5D30232A; Mon, 19 Aug 2019 15:37:13 -0500 (CDT) From: minyard@acm.org To: linux-watchdog@vger.kernel.org, Guenter Roeck , Wim Van Sebroeck Cc: Corey Minyard Subject: [PATCH 02/12] watchdog: Add the ability to provide data to read Date: Mon, 19 Aug 2019 15:37:01 -0500 Message-Id: <20190819203711.32599-3-minyard@acm.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190819203711.32599-1-minyard@acm.org> References: <20190819203711.32599-1-minyard@acm.org> Reply-To: "[PATCH 00/12]"@minyard.net, Convert@minyard.net, the@minyard.net, IPMI@minyard.net, watchdog@minyard.net, to@minyard.net, the@minyard.net, standard@minyard.net, interface@minyard.net Sender: linux-watchdog-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-watchdog@vger.kernel.org From: Corey Minyard This is for the read data pretimeout governor. Signed-off-by: Corey Minyard --- drivers/watchdog/watchdog_core.c | 3 + drivers/watchdog/watchdog_dev.c | 113 +++++++++++++++++++++++++++++++ include/linux/watchdog.h | 5 ++ 3 files changed, 121 insertions(+) diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index 21e8085b848b..80149ac229fc 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -216,6 +216,9 @@ static int __watchdog_register_device(struct watchdog_device *wdd) return id; wdd->id = id; + spin_lock_init(&wdd->readlock); + init_waitqueue_head(&wdd->read_q); + ret = watchdog_dev_register(wdd); if (ret) { ida_simple_remove(&watchdog_ida, id); diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index dbd2ad4c9294..8e8304607a8c 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -44,6 +44,8 @@ #include /* For standard types (like size_t) */ #include /* For watchdog specific items */ #include /* For copy_to_user/put_user/... */ +#include /* For poll_table/... */ +#include /* For signal_pending */ #include /* For struct sched_param */ @@ -929,12 +931,120 @@ static int watchdog_release(struct inode *inode, struct file *file) return 0; } +static ssize_t watchdog_read(struct file *file, + char __user *buf, + size_t count, + loff_t *ppos) +{ + struct watchdog_core_data *wd_data = file->private_data; + struct watchdog_device *wdd; + int err = 0; + wait_queue_entry_t wait; + char dummy = 1; + + if (count <= 0) + return 0; + + mutex_lock(&wd_data->lock); + + wdd = wd_data->wdd; + if (!wdd) + goto done; + + /* + * Reading returns if the pretimeout has gone off, and it only does + * it once per pretimeout. + */ + spin_lock_irq(&wdd->readlock); + while (!wdd->data_to_read) { + if (file->f_flags & O_NONBLOCK) { + err = -EAGAIN; + goto out; + } + + init_waitqueue_entry(&wait, current); + add_wait_queue(&wdd->read_q, &wait); + set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irq(&wdd->readlock); + schedule(); + spin_lock_irq(&wdd->readlock); + remove_wait_queue(&wdd->read_q, &wait); + + if (signal_pending(current)) { + err = -ERESTARTSYS; + goto out; + } + } + dummy = wdd->data_to_read; + wdd->data_to_read = 0; + + out: + spin_unlock_irq(&wdd->readlock); + + if (err == 0) { + if (copy_to_user(buf, &dummy, 1)) + err = -EFAULT; + else + err = 1; + } + + done: + mutex_unlock(&wd_data->lock); + + return err; +} + +static __poll_t watchdog_poll(struct file *file, poll_table *wait) +{ + struct watchdog_core_data *wd_data = file->private_data; + struct watchdog_device *wdd; + __poll_t mask = 0; + + mutex_lock(&wd_data->lock); + + wdd = wd_data->wdd; + if (!wdd) + goto done; + + poll_wait(file, &wdd->read_q, wait); + + spin_lock_irq(&wdd->readlock); + if (wdd->data_to_read) + mask |= (EPOLLIN | EPOLLRDNORM); + spin_unlock_irq(&wdd->readlock); + +done: + mutex_unlock(&wd_data->lock); + return mask; +} + +static int watchdog_fasync(int fd, struct file *file, int on) +{ + struct watchdog_core_data *wd_data = file->private_data; + struct watchdog_device *wdd; + int err = -ENODEV; + + mutex_lock(&wd_data->lock); + + wdd = wd_data->wdd; + if (!wdd) + goto done; + + err = fasync_helper(fd, file, on, &wdd->fasync_q); +done: + mutex_unlock(&wd_data->lock); + return err; +} + static const struct file_operations watchdog_fops = { .owner = THIS_MODULE, .write = watchdog_write, .unlocked_ioctl = watchdog_ioctl, .open = watchdog_open, .release = watchdog_release, + .read = watchdog_read, + .poll = watchdog_poll, + .fasync = watchdog_fasync, }; static struct miscdevice watchdog_miscdev = { @@ -970,6 +1080,9 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) if (IS_ERR_OR_NULL(watchdog_kworker)) return -ENODEV; + spin_lock_init(&wdd->readlock); + init_waitqueue_head(&wdd->read_q); + kthread_init_work(&wd_data->work, watchdog_ping_work); hrtimer_init(&wd_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); wd_data->timer.function = watchdog_timer_expired; diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 417d9f37077a..e34501a822f0 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -117,6 +117,11 @@ struct watchdog_device { #define WDOG_HW_RUNNING 3 /* True if HW watchdog running */ #define WDOG_STOP_ON_UNREGISTER 4 /* Should be stopped on unregister */ struct list_head deferred; + + spinlock_t readlock; + bool data_to_read; + struct wait_queue_head read_q; + struct fasync_struct *fasync_q; }; #define WATCHDOG_NOWAYOUT IS_BUILTIN(CONFIG_WATCHDOG_NOWAYOUT) From patchwork Mon Aug 19 20:37:02 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Corey Minyard X-Patchwork-Id: 11101893 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E893A1890 for ; Mon, 19 Aug 2019 20:37:17 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id C88DE2087E for ; Mon, 19 Aug 2019 20:37:17 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="iuTWR0oz" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728193AbfHSUhR (ORCPT ); Mon, 19 Aug 2019 16:37:17 -0400 Received: from mail-oi1-f195.google.com ([209.85.167.195]:40408 "EHLO mail-oi1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728177AbfHSUhR (ORCPT ); Mon, 19 Aug 2019 16:37:17 -0400 Received: by mail-oi1-f195.google.com with SMTP id h21so2364719oie.7 for ; Mon, 19 Aug 2019 13:37:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :reply-to; bh=ahYWgNn4ghDOFq3tmvRr1AkEjevCgPd/PVakoGoQrig=; b=iuTWR0ozAmPLu+zaANXoa0Kfu/rP0lzX+D/LQspkM9w6485DPEZsfqEHOqf36CXHRl 28zJ/m5kf7s6J6smK+jNzWg6jgX19Nz1D1kKc+Z/kNRk9obn28o8OXzl5fkBqTXQIkXv LTo4hztcLFIIiR1XCRBuupRK+omBjwzxT8iV/DAp3yiEKO0R/njHCJWmvDUopGRyae9b 8y2R7w9sTmEzLFaBzMWzpwiN35ODlvftxhN74oCuVeYLsmu+nfWecvv/AMrmoCsCIXxh Ez+yLagxW4x0j++/3yEa0AHcaVibwZmWBy68KRlAROBV3FtmRDRWhxoh9IQwlRuVR+Q1 lEzA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references:reply-to; bh=ahYWgNn4ghDOFq3tmvRr1AkEjevCgPd/PVakoGoQrig=; b=MRYRUI2XXAQtYjYFAtDt1QbKQSqL098vBN6SkCi1+zH1K8EsS/AiwOwRxjvJoJHe1Z sbVQoCMgolQu9KcKIKnVBdkWpsw7mrLs5L6tIPkjrky+T3vKWh6gyGT2KeaDbwRQHwfs m7F5d9HVkUeJn1SHpCtnHE5JQQwNynt/WHR6ii7AaEb8Ant7NVKQIAqpR9ode3lGQXSx U6NlpMYfJ6/wa58+teZXDyjsYVOroK6gCv0YX0eYmkH7SEz4GCuBXuTzw8lKQrZJAFrM hLKqEXZeCVIasb8znLW2j2XhHvBQbAKVSZVqAEOyCPxQk6XQInazQz6aewWVYMIDelbA SQFQ== X-Gm-Message-State: APjAAAVGTkZz0Ne8vanDZvAO0sUVnuWs3RwJrmTX1Uj74PtKnuUrZ0hp gpn3rVhhzIMJXIVwP7gE2A== X-Google-Smtp-Source: APXvYqwaqWVrtX6M0k9UKLOcEjflh2bf83FT0E0Sju2UR4QDm2RCoCYkt7wmS4nIQfDayV56sdgYZg== X-Received: by 2002:aca:2109:: with SMTP id 9mr15087198oiz.59.1566247036216; Mon, 19 Aug 2019 13:37:16 -0700 (PDT) Received: from serve.minyard.net ([47.184.134.43]) by smtp.gmail.com with ESMTPSA id s24sm5971025otd.81.2019.08.19.13.37.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2019 13:37:15 -0700 (PDT) Received: from t430.minyard.net (t430m.minyard.net [192.168.27.3]) by serve.minyard.net (Postfix) with ESMTPA id 25A2A1805A8; Mon, 19 Aug 2019 20:37:14 +0000 (UTC) Received: by t430.minyard.net (Postfix, from userid 1000) id 0B6C2302506; Mon, 19 Aug 2019 15:37:14 -0500 (CDT) From: minyard@acm.org To: linux-watchdog@vger.kernel.org, Guenter Roeck , Wim Van Sebroeck Cc: Corey Minyard Subject: [PATCH 03/12] watchdog: Add a pretimeout governor to provide read data Date: Mon, 19 Aug 2019 15:37:02 -0500 Message-Id: <20190819203711.32599-4-minyard@acm.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190819203711.32599-1-minyard@acm.org> References: <20190819203711.32599-1-minyard@acm.org> Reply-To: "[PATCH 00/12]"@minyard.net, Convert@minyard.net, the@minyard.net, IPMI@minyard.net, watchdog@minyard.net, to@minyard.net, the@minyard.net, standard@minyard.net, interface@minyard.net Sender: linux-watchdog-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-watchdog@vger.kernel.org From: Corey Minyard When a pretimeout occurs, provide a byte of data on the watchdog device. Signed-off-by: Corey Minyard --- drivers/watchdog/Kconfig | 16 +++++++++ drivers/watchdog/Makefile | 1 + drivers/watchdog/pretimeout_read_data.c | 47 +++++++++++++++++++++++++ drivers/watchdog/watchdog_pretimeout.h | 2 ++ 4 files changed, 66 insertions(+) create mode 100644 drivers/watchdog/pretimeout_read_data.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 8188963a405b..3578b7bc863c 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -105,6 +105,14 @@ config WATCHDOG_PRETIMEOUT_GOV_PANIC Panic watchdog pretimeout governor, on watchdog pretimeout event put the kernel into panic. +config WATCHDOG_PRETIMEOUT_GOV_READ_DATA + tristate "Read data watchdog pretimeout governor" + depends on WATCHDOG_CORE + default WATCHDOG_CORE + help + Read data watchdog pretimeout governor, on watchdog pretimeout + event provide a byte of data on the watchdog device. + choice prompt "Default Watchdog Pretimeout Governor" default WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC @@ -129,6 +137,14 @@ config WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC a watchdog pretimeout event happens, consider that a watchdog feeder is dead and reboot is unavoidable. +config WATCHDOG_PRETIMEOUT_DEFAULT_GOV_READ_DATA + bool "read_data" + depends on WATCHDOG_PRETIMEOUT_GOV_READ_DATA + help + Use read data watchdog pretimeout governor by default, if + a watchdog pretimeout event happens, provide a byte of read + data on the watchdog device. + endchoice endif # WATCHDOG_PRETIMEOUT_GOV diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 7caa920e7e60..9cfe4ad32dc4 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -12,6 +12,7 @@ watchdog-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV) += watchdog_pretimeout.o obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_NOOP) += pretimeout_noop.o obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_PANIC) += pretimeout_panic.o +obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_READ_DATA) += pretimeout_read_data.o # Only one watchdog can succeed. We probe the ISA/PCI/USB based # watchdog-cards first, then the architecture specific watchdog diff --git a/drivers/watchdog/pretimeout_read_data.c b/drivers/watchdog/pretimeout_read_data.c new file mode 100644 index 000000000000..197e9d692044 --- /dev/null +++ b/drivers/watchdog/pretimeout_read_data.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2019 MontaVista Software, LLC + */ + +#include +#include +#include +#include + +#include "watchdog_pretimeout.h" + +/** + * pretimeout_read_data - Cause a read to return on the watchdog device. + * @wdd - watchdog_device + */ +static void pretimeout_read_data(struct watchdog_device *wdd) +{ + unsigned long flags; + + spin_lock_irqsave(&wdd->readlock, flags); + wdd->data_to_read = 1; + wake_up_interruptible(&wdd->read_q); + kill_fasync(&wdd->fasync_q, SIGIO, POLL_IN); + spin_unlock_irqrestore(&wdd->readlock, flags); +} + +static struct watchdog_governor watchdog_gov_read_data = { + .name = "read_data", + .pretimeout = pretimeout_read_data, +}; + +static int __init watchdog_gov_read_data_register(void) +{ + return watchdog_register_governor(&watchdog_gov_read_data); +} + +static void __exit watchdog_gov_read_data_unregister(void) +{ + watchdog_unregister_governor(&watchdog_gov_read_data); +} +module_init(watchdog_gov_read_data_register); +module_exit(watchdog_gov_read_data_unregister); + +MODULE_AUTHOR("Corey Minyard "); +MODULE_DESCRIPTION("Read data watchdog pretimeout governor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/watchdog_pretimeout.h b/drivers/watchdog/watchdog_pretimeout.h index a3f1abc68839..819517ed0138 100644 --- a/drivers/watchdog/watchdog_pretimeout.h +++ b/drivers/watchdog/watchdog_pretimeout.h @@ -28,6 +28,8 @@ int watchdog_pretimeout_governor_set(struct watchdog_device *wdd, #define WATCHDOG_PRETIMEOUT_DEFAULT_GOV "noop" #elif IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC) #define WATCHDOG_PRETIMEOUT_DEFAULT_GOV "panic" +#elif IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_READ_DATA) +#define WATCHDOG_PRETIMEOUT_DEFAULT_GOV "read_data" #endif #else From patchwork Mon Aug 19 20:37:03 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Corey Minyard X-Patchwork-Id: 11101889 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 413C214DB for ; Mon, 19 Aug 2019 20:37:17 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 20B4C2087E for ; Mon, 19 Aug 2019 20:37:17 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="oNM5q18g" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727925AbfHSUhQ (ORCPT ); Mon, 19 Aug 2019 16:37:16 -0400 Received: from mail-oi1-f195.google.com ([209.85.167.195]:45967 "EHLO mail-oi1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728018AbfHSUhQ (ORCPT ); Mon, 19 Aug 2019 16:37:16 -0400 Received: by mail-oi1-f195.google.com with SMTP id v12so2355523oic.12 for ; Mon, 19 Aug 2019 13:37:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :reply-to; bh=JX68BlRG1YFE+L+0xwopCCOIywBef5tbpEIb1SAdao8=; b=oNM5q18guIaPGkRgMbjx8gUWbMGsNCcCSxln0CIEQsbCtQfdQjyyamq8BsFFOLvWeY q/IR95jM3SuAR1quTaqPzr6OxQ6tYuR++O57QEXYtdxJfsnc3EcupJlbc7Z3nLxLsUce RLxstz+NJod84WV+kz0jDqSO+jjtSPUkGdWWKLTwcKgd31c8q783wzLIl4wvo5lN4yiW cA2Kro6w0U0Lkc4+IVUc8aPxLhwTzk8uDKnMPzH4aMmK3b8KNFzzDxU9us8W/3ZrHLFs EzIu7YTN4S/Rh4feeue4nBnsXk+qUzmYoN2RyvySU/T/7Fcrv0AvW1qYFAIpJnWi6usz /mbA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references:reply-to; bh=JX68BlRG1YFE+L+0xwopCCOIywBef5tbpEIb1SAdao8=; b=PGanL/LT41pxdnPCHlBUnL0UQpM5U4PD4DJcKZ4L9IukECfDjEn/DKkgziZ5RKd9Ju 0b78LmP4XnkngkEj0Sd5sn27XhP8+9GGfA9SiH9YzZ2kwfm7KEgFumIqV1LHcOuMrysb hhFkeifTwjhtqyVYeIHuJVaVpVHWwCcA3gNSYlzIq4j3EKkm41dHDJFPPIO98lL6Y2XF g1PU6TQWM97KvZIMXUJ1MRVMELvNe7Ea8AJ6kyCcD+ykn1xTY2OELrDaO+A1d+QFfem+ 6EEFjHre0lF+aXnuBkrVJ71MWoZ2aPdgrPAqhKPBrC2g9ypRN4+POnkbpsFE5eUVbTTf qsgA== X-Gm-Message-State: APjAAAWx1VeOtiaaxeHtAwAqTcOiAbElIkdh8ZMDgd1i7gJVXq0bnhLf RoJBQ4wydxS3c5vjVb8mPA== X-Google-Smtp-Source: APXvYqyMs4FfzFOEKYIrPIySec1MbW+7jBfsphBoMB54GtWTXIRP4taK8K5iOhuc+/dG6WR2oAY9mA== X-Received: by 2002:a05:6808:92:: with SMTP id s18mr13962838oic.141.1566247035638; Mon, 19 Aug 2019 13:37:15 -0700 (PDT) Received: from serve.minyard.net ([47.184.134.43]) by smtp.gmail.com with ESMTPSA id k24sm4256706oic.29.2019.08.19.13.37.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2019 13:37:15 -0700 (PDT) Received: from t430.minyard.net (t430m.minyard.net [192.168.27.3]) by serve.minyard.net (Postfix) with ESMTPA id 3EDA61805A9; Mon, 19 Aug 2019 20:37:14 +0000 (UTC) Received: by t430.minyard.net (Postfix, from userid 1000) id 20032301220; Mon, 19 Aug 2019 15:37:14 -0500 (CDT) From: minyard@acm.org To: linux-watchdog@vger.kernel.org, Guenter Roeck , Wim Van Sebroeck Cc: Corey Minyard Subject: [PATCH 04/12] watchdog: Allow pretimeout governor setting to be accessed from modules Date: Mon, 19 Aug 2019 15:37:03 -0500 Message-Id: <20190819203711.32599-5-minyard@acm.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190819203711.32599-1-minyard@acm.org> References: <20190819203711.32599-1-minyard@acm.org> Reply-To: "[PATCH 00/12]"@minyard.net, Convert@minyard.net, the@minyard.net, IPMI@minyard.net, watchdog@minyard.net, to@minyard.net, the@minyard.net, standard@minyard.net, interface@minyard.net Sender: linux-watchdog-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-watchdog@vger.kernel.org From: Corey Minyard This is so watchdog driver (like IPMI) can set it. Signed-off-by: Corey Minyard --- drivers/watchdog/watchdog_pretimeout.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/watchdog/watchdog_pretimeout.c b/drivers/watchdog/watchdog_pretimeout.c index b45041b0ef39..270baf7b3fa0 100644 --- a/drivers/watchdog/watchdog_pretimeout.c +++ b/drivers/watchdog/watchdog_pretimeout.c @@ -95,6 +95,7 @@ int watchdog_pretimeout_governor_set(struct watchdog_device *wdd, return 0; } +EXPORT_SYMBOL_GPL(watchdog_pretimeout_governor_set); void watchdog_notify_pretimeout(struct watchdog_device *wdd) { From patchwork Mon Aug 19 20:37:04 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Corey Minyard X-Patchwork-Id: 11101901 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A74FE1864 for ; Mon, 19 Aug 2019 20:37:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 8751722CF4 for ; Mon, 19 Aug 2019 20:37:19 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="POpI2NEz" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728292AbfHSUhT (ORCPT ); Mon, 19 Aug 2019 16:37:19 -0400 Received: from mail-ot1-f66.google.com ([209.85.210.66]:34235 "EHLO mail-ot1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727769AbfHSUhT (ORCPT ); Mon, 19 Aug 2019 16:37:19 -0400 Received: by mail-ot1-f66.google.com with SMTP id c7so2951136otp.1 for ; Mon, 19 Aug 2019 13:37:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :reply-to; bh=PQ0zaDzdDJ6UtQk6w4zWTK2JD2j0cuDEtJE7gO5QF1Y=; b=POpI2NEzf1RtprsdvzsDV277pRkBkgLc/LRw6WaVUAxhvieEjPKAGKjjFahGJtEh1N EOxqHV23EHvZ+fP5yPh6dTr8nUyvn9XszVEoetL8AEz3bdO5J3Qxm1g3fMbav9WhRTjl sx6qg8F6tx5pQC6fqEgsnBp/tg0x0100d4IUvbrF0MQEGicXyBoEjtzcAM19wviVmlaP m/KjkpUdO4TbaBwH1vlxiz01y/2HtY1/PE4DeRzigfgi1oGXYmERvaR0LCnyP3JCiFsW 9Suv5kPaaUSXA+EAoxlPIGzl1cnughcl0Dt9SQ0o1OHDFmWEQ/tfoYywSkyDg8N+3J75 /fhA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references:reply-to; bh=PQ0zaDzdDJ6UtQk6w4zWTK2JD2j0cuDEtJE7gO5QF1Y=; b=BjYSEqS2sTDRhPOq9pQESaNksixaxgszd47Fa8ZSwiCYMCy7qq21y/tFzUo4FEbJQR 7oEfIxvOWW/SI1DFgQhA6dkWa0hdCh3pM/N1O0IVqrH7md654C/gHvp9Q2ASxcxyvqnu 81vw2ieCrPdqqPFUw/RxanVYYJU6GckLYR2EbLTNayjKWzqzQWSaC0hL4vdDVkQFMUdo lko+KeWa/KhbnHvyOGfpsNrGOmYa8yrsyNBdVR7EQf4BxSb56FEKrMA1Ja+brp9qy+gf /7HuPp/GczfyYHRwrtPDlawhKvkgbz+WaCxNfFO9XxnGgvDU2FIO3MqJYoNzoej7fv22 5vfg== X-Gm-Message-State: APjAAAVXZtAl1KfVitarK14DZEYvwg1LJdELx7TBC1uGZ44JF3enjuN7 A4FpB1trGLouUnNG4O2seQ== X-Google-Smtp-Source: APXvYqwfTfS8nS/MByI7yIDw/ahDVTgpMIt7G9RLA+Xm1R53K31xs47gvC19CoZEe075AHPNb0Njwg== X-Received: by 2002:a9d:5911:: with SMTP id t17mr18700345oth.159.1566247037838; Mon, 19 Aug 2019 13:37:17 -0700 (PDT) Received: from serve.minyard.net (serve.minyard.net. [2001:470:b8f6:1b::1]) by smtp.gmail.com with ESMTPSA id d62sm4620771oia.28.2019.08.19.13.37.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2019 13:37:16 -0700 (PDT) Received: from t430.minyard.net (t430m.minyard.net [192.168.27.3]) by serve.minyard.net (Postfix) with ESMTPA id 446321805AA; Mon, 19 Aug 2019 20:37:14 +0000 (UTC) Received: by t430.minyard.net (Postfix, from userid 1000) id 2C729301176; Mon, 19 Aug 2019 15:37:14 -0500 (CDT) From: minyard@acm.org To: linux-watchdog@vger.kernel.org, Guenter Roeck , Wim Van Sebroeck Cc: Corey Minyard Subject: [PATCH 05/12] watchdog:ipmi: Move the IPMI watchdog to drivers/watchdog Date: Mon, 19 Aug 2019 15:37:04 -0500 Message-Id: <20190819203711.32599-6-minyard@acm.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190819203711.32599-1-minyard@acm.org> References: <20190819203711.32599-1-minyard@acm.org> Reply-To: "[PATCH 00/12]"@minyard.net, Convert@minyard.net, the@minyard.net, IPMI@minyard.net, watchdog@minyard.net, to@minyard.net, the@minyard.net, standard@minyard.net, interface@minyard.net Sender: linux-watchdog-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-watchdog@vger.kernel.org From: Corey Minyard It probably belongs there, and it will need access to the watchdog_pretimeout.h file when converted over to the standard watchdog interface. Signed-off-by: Corey Minyard --- MAINTAINERS | 1 + drivers/char/ipmi/Kconfig | 5 ----- drivers/char/ipmi/Makefile | 1 - drivers/watchdog/Kconfig | 6 ++++++ drivers/watchdog/Makefile | 1 + drivers/{char/ipmi => watchdog}/ipmi_watchdog.c | 0 6 files changed, 8 insertions(+), 6 deletions(-) rename drivers/{char/ipmi => watchdog}/ipmi_watchdog.c (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 6426db5198f0..760bcf92fde1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8456,6 +8456,7 @@ F: Documentation/IPMI.txt F: drivers/char/ipmi/ F: include/linux/ipmi* F: include/uapi/linux/ipmi* +F: drivers/watchdog/ipmi_watchdog.c IPS SCSI RAID DRIVER M: Adaptec OEM Raid Solutions diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index 4bad0614109b..5f310ff0bd89 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -81,11 +81,6 @@ config IPMI_POWERNV help Provides a driver for OPAL firmware-based IPMI interfaces. -config IPMI_WATCHDOG - tristate 'IPMI Watchdog Timer' - help - This enables the IPMI watchdog timer. - config IPMI_POWEROFF tristate 'IPMI Poweroff' help diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile index 0822adc2ec41..a9edcd473615 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -20,7 +20,6 @@ obj-$(CONFIG_IPMI_DMI_DECODE) += ipmi_dmi.o obj-$(CONFIG_IPMI_PLAT_DATA) += ipmi_plat_data.o obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o -obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 3578b7bc863c..de462f995329 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -960,6 +960,12 @@ config STPMIC1_WATCHDOG To compile this driver as a module, choose M here: the module will be called spmic1_wdt. +config IPMI_WATCHDOG + tristate 'IPMI Watchdog Timer' + depends on IPMI_HANDLER + help + This enables the IPMI watchdog timer. + config UNIPHIER_WATCHDOG tristate "UniPhier watchdog support" depends on ARCH_UNIPHIER || COMPILE_TEST diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 9cfe4ad32dc4..5840773bf2b4 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -225,3 +225,4 @@ obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o obj-$(CONFIG_MENZ069_WATCHDOG) += menz69_wdt.o obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o +obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/watchdog/ipmi_watchdog.c similarity index 100% rename from drivers/char/ipmi/ipmi_watchdog.c rename to drivers/watchdog/ipmi_watchdog.c From patchwork Mon Aug 19 20:37:05 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Corey Minyard X-Patchwork-Id: 11101909 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 9ABED14DB for ; Mon, 19 Aug 2019 20:37:24 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 46ECB22CE8 for ; Mon, 19 Aug 2019 20:37:24 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="qOwP9/dV" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727769AbfHSUhY (ORCPT ); Mon, 19 Aug 2019 16:37:24 -0400 Received: from mail-oi1-f196.google.com ([209.85.167.196]:35280 "EHLO mail-oi1-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728229AbfHSUhX (ORCPT ); Mon, 19 Aug 2019 16:37:23 -0400 Received: by mail-oi1-f196.google.com with SMTP id a127so2365498oii.2 for ; Mon, 19 Aug 2019 13:37:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :reply-to; bh=afVVkhiAhS/5yGhJkERMRjqcLkxcohiQZxgssbm+lkc=; b=qOwP9/dVMhrbAK57DvdjbLmPmMOfxk90Yd9HnqnEUi9lMHww64XCagUk3s+4Qtf5EY 9dDFLzQV1CTOy3Hs9iDj3JcPtycWcw5EUVCwNjepZrDQ+MPRz10EMt6PmPCUZkJKDjVy ABgRIpJrRQOKu8g37w/G/2r/2T9fiEtNQsbNUsMZ//lZvNDynxEKSnpIRVWTlmvihOwT DHiO3/ixQEcONgwHjH1+DrpJtxUg21/gEmQApzAQ9eq1dbDoha8d77FSzud/Lw5vqm72 MExkkc/HwY0qwaV07YNtU/aIDjNPIAXIQDOrVIGgcYr0oB7fWLxojT+n3iGHUhgwPoO2 ka3Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references:reply-to; bh=afVVkhiAhS/5yGhJkERMRjqcLkxcohiQZxgssbm+lkc=; b=bE7HGGQ+mJ0cXbK+GI/790qbVBhlLhhoS+qmtY6R2oq0csc9WA6QxX86vGdSCXZJ1M 6GRn70R5bjSa0TUqUGsvpRv/GhSj70QVWG/HpUlCOkQ8sIWb8sTycYPWllqxbMY4XDNb Y+5GBCjgpDUoKEFbI53BENgN7OTnM3XFj5PgiAWBdRuY0f8k7/lIazubPZCE5n+8KslX KsaPp3ZdAjiC69GPTlv+Yg2hS7prqgGNNWkmrtDltqMe7f41bw5dP/orL4/6Xarfah0X I85TnXmXMrJCzu0nip2vOoT9yz0yt2KoczklE4NeuJgn5IK+B/IIeFDPxyo/63Q3DUPq MCpw== X-Gm-Message-State: APjAAAV/IG5VDQp9rvyNue+n0GsSzRScQnNeTkmyj/Us8Wt6+xTMQil0 OpliLC/KVDEjKWAjGXzShg== X-Google-Smtp-Source: APXvYqx2uPT+agNuzKCLmzekWXw8Edov8fOqTBPfRaZOfU2D3jUFCCaouEHW8q68mF1Fq5AMU65Sog== X-Received: by 2002:aca:1803:: with SMTP id h3mr13949775oih.24.1566247039392; Mon, 19 Aug 2019 13:37:19 -0700 (PDT) Received: from serve.minyard.net (serve.minyard.net. [2001:470:b8f6:1b::1]) by smtp.gmail.com with ESMTPSA id j189sm759040oih.30.2019.08.19.13.37.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2019 13:37:16 -0700 (PDT) Received: from t430.minyard.net (t430m.minyard.net [192.168.27.3]) by serve.minyard.net (Postfix) with ESMTPA id 534591805AB; Mon, 19 Aug 2019 20:37:14 +0000 (UTC) Received: by t430.minyard.net (Postfix, from userid 1000) id 3CECE30232A; Mon, 19 Aug 2019 15:37:14 -0500 (CDT) From: minyard@acm.org To: linux-watchdog@vger.kernel.org, Guenter Roeck , Wim Van Sebroeck Cc: Corey Minyard Subject: [PATCH 06/12] watchdog:ipmi: Convert over to the standard watchdog infrastructure Date: Mon, 19 Aug 2019 15:37:05 -0500 Message-Id: <20190819203711.32599-7-minyard@acm.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190819203711.32599-1-minyard@acm.org> References: <20190819203711.32599-1-minyard@acm.org> Reply-To: "[PATCH 00/12]"@minyard.net, Convert@minyard.net, the@minyard.net, IPMI@minyard.net, watchdog@minyard.net, to@minyard.net, the@minyard.net, standard@minyard.net, interface@minyard.net Sender: linux-watchdog-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-watchdog@vger.kernel.org From: Corey Minyard The IPMI watchdog was using its own ioctl and misc registration. Switch over to using the standard watchdog code. This required almost a complete rewrite, but this allowed a lot of improvements. No functional changes. Signed-off-by: Corey Minyard --- drivers/watchdog/ipmi_watchdog.c | 1350 ++++++++++++++---------------- 1 file changed, 621 insertions(+), 729 deletions(-) diff --git a/drivers/watchdog/ipmi_watchdog.c b/drivers/watchdog/ipmi_watchdog.c index 74c6d1f34132..c8c11a61ebb9 100644 --- a/drivers/watchdog/ipmi_watchdog.c +++ b/drivers/watchdog/ipmi_watchdog.c @@ -36,6 +36,9 @@ #include #include #include +#include + +#include "watchdog_pretimeout.h" #ifdef CONFIG_X86 /* @@ -122,20 +125,69 @@ #define IPMI_WDOG_TIMER_NOT_INIT_RESP 0x80 -static DEFINE_MUTEX(ipmi_watchdog_mutex); -static bool nowayout = WATCHDOG_NOWAYOUT; +#define IPMI_MAX_TIMEOUT 6553 /* 16 bit value in 1/10 of a second */ +#define IPMI_MIN_TIMEOUT 0 + +struct ipmi_wdt { + struct watchdog_device wdd; + struct watchdog_info info; + struct ipmi_user *user; + int ifnum; /* IPMI interface number. */ + u8 ipmi_version_major; + u8 ipmi_version_minor; + + struct mutex lock; + + struct completion msg_wait; + atomic_t msg_tofree; + struct ipmi_smi_msg smi_msg; + struct ipmi_recv_msg recv_msg; + + int state; /* One of WDOG_TIMEOUT_xxx, NONE if disabled. */ + + bool nmi_works; + + atomic_t pretimeout_since_last_heartbeat; + bool panic_event_handled; +}; + +#define wdd_to_ipmi_wdt(wdd) container_of(wdd, struct ipmi_wdt, wdd) + +/* Parameters to ipmi_set_timeout(), _ipmi_update_timeout() */ +#define IPMI_SET_TIMEOUT_NO_HB 0 +#define IPMI_SET_TIMEOUT_HB_IF_NECESSARY 1 +#define IPMI_SET_TIMEOUT_FORCE_HB 2 + +static int _ipmi_update_timeout(struct ipmi_wdt *iwd, int do_heartbeat, + bool do_poll); +static int ipmi_set_timeout(struct ipmi_wdt *iwd, int timeout, + int do_heartbeat); +static int ipmi_set_pretimeout(struct ipmi_wdt *iwd, int timeout, + int do_heartbeat); +static void ipmi_register_watchdog(int ipmi_intf, struct device *dev); +static void _ipmi_unregister_watchdog(struct ipmi_wdt *iwd); +static void ipmi_unregister_watchdog(int ipmi_intf); + +/* + * Only used if HAVE_DIE_NMI is set, but pretimeout will not be delivered + * if this is set. + * 0 = not testing + * 1 = test running, no NMI + * 2 = test running, got an NMI + */ +static int testing_nmi; -static struct ipmi_user *watchdog_user; -static int watchdog_ifnum; -/* Default the timeout to 10 seconds. */ -static int timeout = 10; +/* Protects the following values. */ +static DEFINE_MUTEX(ipmi_wdt_data_mutex); +static int ifnum_to_use = -1; +static struct ipmi_wdt *ipmi_wdt; -/* The pre-timeout is disabled by default. */ -static int pretimeout; +static bool nowayout = WATCHDOG_NOWAYOUT; -/* Default timeout to set on panic */ -static int panic_wdt_timeout = 255; +static int timeout = 10; /* Default timeout. */ +static int pretimeout; /* Default pretimeout. */ +static int panic_wdt_timeout = 255; /* Default timeout to set on panic */ /* Default action is to reset the board on a timeout. */ static unsigned char action_val = WDOG_TIMEOUT_RESET; @@ -149,23 +201,6 @@ static char preaction[16] = "pre_none"; static unsigned char preop_val = WDOG_PREOP_NONE; static char preop[16] = "preop_none"; -static DEFINE_SPINLOCK(ipmi_read_lock); -static char data_to_read; -static DECLARE_WAIT_QUEUE_HEAD(read_q); -static struct fasync_struct *fasync_q; -static atomic_t pretimeout_since_last_heartbeat; -static char expect_close; - -static int ifnum_to_use = -1; - -/* Parameters to ipmi_set_timeout */ -#define IPMI_SET_TIMEOUT_NO_HB 0 -#define IPMI_SET_TIMEOUT_HB_IF_NECESSARY 1 -#define IPMI_SET_TIMEOUT_FORCE_HB 2 - -static int ipmi_set_timeout(int do_heartbeat); -static void ipmi_register_watchdog(int ipmi_intf); -static void ipmi_unregister_watchdog(int ipmi_intf); /* * If true, the driver will start running as soon as it is configured @@ -173,21 +208,162 @@ static void ipmi_unregister_watchdog(int ipmi_intf); */ static int start_now; +static int action_op(const char *inval, char *outval) +{ + int rv = 0; + + mutex_lock(&ipmi_wdt_data_mutex); + if (outval) + strcpy(outval, action); + + if (!inval) + goto out_unlock; + + if (strcmp(inval, "reset") == 0) + action_val = WDOG_TIMEOUT_RESET; + else if (strcmp(inval, "none") == 0) + action_val = WDOG_TIMEOUT_NONE; + else if (strcmp(inval, "power_cycle") == 0) + action_val = WDOG_TIMEOUT_POWER_CYCLE; + else if (strcmp(inval, "power_off") == 0) + action_val = WDOG_TIMEOUT_POWER_DOWN; + else + rv = -EINVAL; + if (!rv) { + strcpy(action, inval); + if (ipmi_wdt && ipmi_wdt->state != WDOG_TIMEOUT_NONE) { + ipmi_wdt->state = action_val; + rv = _ipmi_update_timeout(ipmi_wdt, + IPMI_SET_TIMEOUT_HB_IF_NECESSARY, + false); + } + } +out_unlock: + mutex_unlock(&ipmi_wdt_data_mutex); + + return rv; +} + +static int preaction_op(const char *inval, char *outval) +{ + int rv = 0; + + mutex_lock(&ipmi_wdt_data_mutex); + if (outval) + strcpy(outval, preaction); + + if (!inval) + goto out_unlock; + + if (strcmp(inval, "pre_none") == 0) + preaction_val = WDOG_PRETIMEOUT_NONE; + else if (strcmp(inval, "pre_smi") == 0) + preaction_val = WDOG_PRETIMEOUT_SMI; +#ifdef HAVE_DIE_NMI + else if (strcmp(inval, "pre_nmi") == 0) { + if (preop_val == WDOG_PREOP_GIVE_DATA) + rv = -EINVAL; + else if (ipmi_wdt && !ipmi_wdt->nmi_works) + rv = -EINVAL; + else + preaction_val = WDOG_PRETIMEOUT_NMI; + } +#endif + else if (strcmp(inval, "pre_int") == 0) + preaction_val = WDOG_PRETIMEOUT_MSG_INT; + else + rv = -EINVAL; + if (!rv) { + strcpy(preaction, inval); + if (ipmi_wdt && ipmi_wdt->state != WDOG_TIMEOUT_NONE && + ipmi_wdt->wdd.pretimeout) { + rv = _ipmi_update_timeout(ipmi_wdt, + IPMI_SET_TIMEOUT_HB_IF_NECESSARY, + false); + } + } +out_unlock: + mutex_unlock(&ipmi_wdt_data_mutex); + + return rv; +} + +static int preop_op(const char *inval, char *outval) +{ + int rv = 0; + const char *gov = NULL; + unsigned int orig_val; + + mutex_lock(&ipmi_wdt_data_mutex); + if (outval) + strcpy(outval, preop); + + if (!inval) + goto out_unlock; + + orig_val = preop_val; + if (strcmp(inval, "preop_none") == 0) { + preop_val = WDOG_PREOP_NONE; + gov = "noop"; + } else if (strcmp(inval, "preop_panic") == 0) { + preop_val = WDOG_PREOP_PANIC; + gov = "panic"; + } else if (strcmp(inval, "preop_give_data") == 0) { + if (preaction_val == WDOG_PRETIMEOUT_NMI) + rv = -EINVAL; + else { + preop_val = WDOG_PREOP_GIVE_DATA; + gov = "read_data"; + } + } else { + rv = -EINVAL; + } + if (!rv) { + rv = watchdog_pretimeout_governor_set(&ipmi_wdt->wdd, gov); + if (rv) + preaction_val = orig_val; + else + strcpy(preop, inval); + } +out_unlock: + mutex_unlock(&ipmi_wdt_data_mutex); + + return rv; +} + +static struct ipmi_smi_watcher smi_watcher = { + .owner = THIS_MODULE, + .new_smi = ipmi_register_watchdog, + .smi_gone = ipmi_unregister_watchdog +}; + static int set_param_timeout(const char *val, const struct kernel_param *kp) { - char *endp; - int l; + unsigned long l; int rv = 0; if (!val) return -EINVAL; - l = simple_strtoul(val, &endp, 0); - if (endp == val) + rv = kstrtoul(val, 0, &l); + if (rv) + return rv; + + if (l < IPMI_MIN_TIMEOUT || l > IPMI_MAX_TIMEOUT) return -EINVAL; + mutex_lock(&ipmi_wdt_data_mutex); *((int *)kp->arg) = l; - if (watchdog_user) - rv = ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); + if (!ipmi_wdt) + goto out_unlock; + if (kp->arg == &timeout) + rv = ipmi_set_timeout(ipmi_wdt, l, + IPMI_SET_TIMEOUT_HB_IF_NECESSARY); + else if (kp->arg == &pretimeout) + rv = ipmi_set_pretimeout(ipmi_wdt, l, + IPMI_SET_TIMEOUT_HB_IF_NECESSARY); + +out_unlock: + mutex_unlock(&ipmi_wdt_data_mutex); return rv; } @@ -200,15 +376,9 @@ static const struct kernel_param_ops param_ops_timeout = { typedef int (*action_fn)(const char *intval, char *outval); -static int action_op(const char *inval, char *outval); -static int preaction_op(const char *inval, char *outval); -static int preop_op(const char *inval, char *outval); -static void check_parms(void); - static int set_param_str(const char *val, const struct kernel_param *kp) { action_fn fn = (action_fn) kp->arg; - int rv = 0; char valcp[16]; char *s; @@ -217,16 +387,7 @@ static int set_param_str(const char *val, const struct kernel_param *kp) s = strstrip(valcp); - rv = fn(s, NULL); - if (rv) - goto out; - - check_parms(); - if (watchdog_user) - rv = ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); - - out: - return rv; + return fn(s, NULL); } static int get_param_str(char *buffer, const struct kernel_param *kp) @@ -240,17 +401,45 @@ static int get_param_str(char *buffer, const struct kernel_param *kp) return strlen(buffer); } - static int set_param_wdog_ifnum(const char *val, const struct kernel_param *kp) { - int rv = param_set_int(val, kp); + int rv; + unsigned long l; + struct ipmi_wdt *old_iwd = NULL; + + if (!val) + return 0; + + rv = kstrtoul(val, 0, &l); if (rv) return rv; - if ((ifnum_to_use < 0) || (ifnum_to_use == watchdog_ifnum)) + + mutex_lock(&ipmi_wdt_data_mutex); + if (l == ifnum_to_use) { + mutex_unlock(&ipmi_wdt_data_mutex); return 0; + } + + if (ipmi_wdt) { + old_iwd = ipmi_wdt; + ipmi_wdt = NULL; + } + mutex_unlock(&ipmi_wdt_data_mutex); + + if (old_iwd) + _ipmi_unregister_watchdog(old_iwd); + + /* + * Re-register the smi watcher to run through all the IPMI + * interfaces again. + */ + ipmi_smi_watcher_unregister(&smi_watcher); + rv = ipmi_smi_watcher_register(&smi_watcher); + if (rv) { + pr_warn("can't register smi watcher\n"); + return rv; + } - ipmi_unregister_watchdog(watchdog_ifnum); - ipmi_register_watchdog(ifnum_to_use); return 0; } @@ -300,61 +489,39 @@ module_param(nowayout, bool, 0644); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " "(default=CONFIG_WATCHDOG_NOWAYOUT)"); -/* Default state of the timer. */ -static unsigned char ipmi_watchdog_state = WDOG_TIMEOUT_NONE; - -/* Is someone using the watchdog? Only one user is allowed. */ -static unsigned long ipmi_wdog_open; - -/* - * If set to 1, the heartbeat command will set the state to reset and - * start the timer. The timer doesn't normally run when the driver is - * first opened until the heartbeat is set the first time, this - * variable is used to accomplish this. - */ -static int ipmi_start_timer_on_heartbeat; - -/* IPMI version of the BMC. */ -static unsigned char ipmi_version_major; -static unsigned char ipmi_version_minor; - -/* If a pretimeout occurs, this is used to allow only one panic to happen. */ -static atomic_t preop_panic_excl = ATOMIC_INIT(-1); - -#ifdef HAVE_DIE_NMI -static int testing_nmi; -static int nmi_handler_registered; -#endif - -static int __ipmi_heartbeat(void); +static int __ipmi_heartbeat(struct ipmi_wdt *iwd, bool do_poll); /* * We use a mutex to make sure that only one thing can send a set a * message at one time. The mutex is claimed when a message is sent * and freed when both the send and receive messages are free. */ -static atomic_t msg_tofree = ATOMIC_INIT(0); -static DECLARE_COMPLETION(msg_wait); static void msg_free_smi(struct ipmi_smi_msg *msg) { - if (atomic_dec_and_test(&msg_tofree)) - complete(&msg_wait); + struct ipmi_wdt *iwd = container_of(msg, struct ipmi_wdt, smi_msg); + + if (atomic_dec_and_test(&iwd->msg_tofree)) + complete(&iwd->msg_wait); } static void msg_free_recv(struct ipmi_recv_msg *msg) { - if (atomic_dec_and_test(&msg_tofree)) - complete(&msg_wait); + struct ipmi_wdt *iwd = container_of(msg, struct ipmi_wdt, recv_msg); + + if (atomic_dec_and_test(&iwd->msg_tofree)) + complete(&iwd->msg_wait); } -static struct ipmi_smi_msg smi_msg = { - .done = msg_free_smi -}; -static struct ipmi_recv_msg recv_msg = { - .done = msg_free_recv -}; -static int __ipmi_set_timeout(struct ipmi_smi_msg *smi_msg, - struct ipmi_recv_msg *recv_msg, - int *send_heartbeat_now) +static void wait_msg_done(struct ipmi_wdt *iwd, bool do_poll) +{ + if (do_poll) { + while (atomic_read(&iwd->msg_tofree) != 0) + ipmi_poll_interface(iwd->user); + } else { + wait_for_completion(&iwd->msg_wait); + } +} + +static int __ipmi_update_timeout(struct ipmi_wdt *iwd, int *send_heartbeat_now) { struct kernel_ipmi_msg msg; unsigned char data[6]; @@ -362,15 +529,17 @@ static int __ipmi_set_timeout(struct ipmi_smi_msg *smi_msg, struct ipmi_system_interface_addr addr; int hbnow = 0; - data[0] = 0; WDOG_SET_TIMER_USE(data[0], WDOG_TIMER_USE_SMS_OS); - if ((ipmi_version_major > 1) - || ((ipmi_version_major == 1) && (ipmi_version_minor >= 5))) { - /* This is an IPMI 1.5-only feature. */ - data[0] |= WDOG_DONT_STOP_ON_SET; - } else if (ipmi_watchdog_state != WDOG_TIMEOUT_NONE) { + /* If timer is running, keep it running. */ + if (iwd->state != WDOG_TIMEOUT_NONE) { + if ((iwd->ipmi_version_major > 1) + || ((iwd->ipmi_version_major == 1) && + (iwd->ipmi_version_minor >= 5))) + /* This is an IPMI 1.5-only feature. */ + data[0] |= WDOG_DONT_STOP_ON_SET; + else /* * In ipmi 1.0, setting the timer stops the watchdog, we * need to start it back up again. @@ -379,16 +548,17 @@ static int __ipmi_set_timeout(struct ipmi_smi_msg *smi_msg, } data[1] = 0; - WDOG_SET_TIMEOUT_ACT(data[1], ipmi_watchdog_state); - if ((pretimeout > 0) && (ipmi_watchdog_state != WDOG_TIMEOUT_NONE)) { - WDOG_SET_PRETIMEOUT_ACT(data[1], preaction_val); - data[2] = pretimeout; + WDOG_SET_TIMEOUT_ACT(data[1], iwd->state); + if ((iwd->wdd.pretimeout > 0) && + (iwd->state != WDOG_TIMEOUT_NONE)) { + WDOG_SET_PRETIMEOUT_ACT(data[1], preaction_val); + data[2] = iwd->wdd.pretimeout; } else { - WDOG_SET_PRETIMEOUT_ACT(data[1], WDOG_PRETIMEOUT_NONE); - data[2] = 0; /* No pretimeout. */ + WDOG_SET_PRETIMEOUT_ACT(data[1], WDOG_PRETIMEOUT_NONE); + data[2] = 0; /* No pretimeout. */ } data[3] = 0; - WDOG_SET_TIMEOUT(data[4], data[5], timeout); + WDOG_SET_TIMEOUT(data[4], data[5], iwd->wdd.timeout); addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; addr.channel = IPMI_BMC_CHANNEL; @@ -398,13 +568,13 @@ static int __ipmi_set_timeout(struct ipmi_smi_msg *smi_msg, msg.cmd = IPMI_WDOG_SET_TIMER; msg.data = data; msg.data_len = sizeof(data); - rv = ipmi_request_supply_msgs(watchdog_user, + rv = ipmi_request_supply_msgs(iwd->user, (struct ipmi_addr *) &addr, 0, &msg, NULL, - smi_msg, - recv_msg, + &iwd->smi_msg, + &iwd->recv_msg, 1); if (rv) pr_warn("set timeout error: %d\n", rv); @@ -414,132 +584,79 @@ static int __ipmi_set_timeout(struct ipmi_smi_msg *smi_msg, return rv; } -static int _ipmi_set_timeout(int do_heartbeat) +static int _ipmi_update_timeout(struct ipmi_wdt *iwd, int do_heartbeat, + bool do_poll) { int send_heartbeat_now; int rv; - if (!watchdog_user) - return -ENODEV; - - atomic_set(&msg_tofree, 2); + atomic_set(&iwd->msg_tofree, 2); - rv = __ipmi_set_timeout(&smi_msg, - &recv_msg, - &send_heartbeat_now); - if (rv) + rv = __ipmi_update_timeout(iwd, &send_heartbeat_now); + if (rv) { + atomic_set(&iwd->msg_tofree, 0); return rv; + } - wait_for_completion(&msg_wait); + wait_msg_done(iwd, do_poll); if ((do_heartbeat == IPMI_SET_TIMEOUT_FORCE_HB) || ((send_heartbeat_now) && (do_heartbeat == IPMI_SET_TIMEOUT_HB_IF_NECESSARY))) - rv = __ipmi_heartbeat(); + rv = __ipmi_heartbeat(iwd, do_poll); return rv; } -static int ipmi_set_timeout(int do_heartbeat) +static int ipmi_set_timeout(struct ipmi_wdt *iwd, int val, int do_heartbeat) { int rv; - mutex_lock(&ipmi_watchdog_mutex); - rv = _ipmi_set_timeout(do_heartbeat); - mutex_unlock(&ipmi_watchdog_mutex); + mutex_lock(&iwd->lock); + if (val <= iwd->wdd.pretimeout) { + pr_warn("pretimeout set >= timeout, pretimeout disabled\n"); + iwd->wdd.pretimeout = 0; + } + iwd->wdd.timeout = val; + rv = _ipmi_update_timeout(iwd, do_heartbeat, false); + mutex_unlock(&iwd->lock); return rv; } -static atomic_t panic_done_count = ATOMIC_INIT(0); - -static void panic_smi_free(struct ipmi_smi_msg *msg) -{ - atomic_dec(&panic_done_count); -} -static void panic_recv_free(struct ipmi_recv_msg *msg) -{ - atomic_dec(&panic_done_count); -} - -static struct ipmi_smi_msg panic_halt_heartbeat_smi_msg = { - .done = panic_smi_free -}; -static struct ipmi_recv_msg panic_halt_heartbeat_recv_msg = { - .done = panic_recv_free -}; - -static void panic_halt_ipmi_heartbeat(void) +static int ipmi_set_pretimeout(struct ipmi_wdt *iwd, int val, int do_heartbeat) { - struct kernel_ipmi_msg msg; - struct ipmi_system_interface_addr addr; int rv; - /* - * Don't reset the timer if we have the timer turned off, that - * re-enables the watchdog. - */ - if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE) - return; - - addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; - addr.channel = IPMI_BMC_CHANNEL; - addr.lun = 0; + mutex_lock(&iwd->lock); + if (val >= iwd->wdd.timeout) { + pr_warn("pretimeout set >= timeout, pretimeout disabled\n"); + val = 0; + } + iwd->wdd.pretimeout = val; + rv = _ipmi_update_timeout(iwd, do_heartbeat, false); + mutex_unlock(&iwd->lock); - msg.netfn = 0x06; - msg.cmd = IPMI_WDOG_RESET_TIMER; - msg.data = NULL; - msg.data_len = 0; - atomic_add(1, &panic_done_count); - rv = ipmi_request_supply_msgs(watchdog_user, - (struct ipmi_addr *) &addr, - 0, - &msg, - NULL, - &panic_halt_heartbeat_smi_msg, - &panic_halt_heartbeat_recv_msg, - 1); - if (rv) - atomic_sub(1, &panic_done_count); + return rv; } -static struct ipmi_smi_msg panic_halt_smi_msg = { - .done = panic_smi_free -}; -static struct ipmi_recv_msg panic_halt_recv_msg = { - .done = panic_recv_free -}; - /* * Special call, doesn't claim any locks. This is only to be called * at panic or halt time, in run-to-completion mode, when the caller * is the only CPU and the only thing that will be going is these IPMI * calls. */ -static void panic_halt_ipmi_set_timeout(void) +static void panic_halt_ipmi_update_timeout(struct ipmi_wdt *iwd) { - int send_heartbeat_now; int rv; - /* Wait for the messages to be free. */ - while (atomic_read(&panic_done_count) != 0) - ipmi_poll_interface(watchdog_user); - atomic_add(1, &panic_done_count); - rv = __ipmi_set_timeout(&panic_halt_smi_msg, - &panic_halt_recv_msg, - &send_heartbeat_now); - if (rv) { - atomic_sub(1, &panic_done_count); + wait_msg_done(iwd, true); + rv = _ipmi_update_timeout(iwd, IPMI_SET_TIMEOUT_HB_IF_NECESSARY, true); + if (rv) pr_warn("Unable to extend the watchdog timeout\n"); - } else { - if (send_heartbeat_now) - panic_halt_ipmi_heartbeat(); - } - while (atomic_read(&panic_done_count) != 0) - ipmi_poll_interface(watchdog_user); } -static int __ipmi_heartbeat(void) +static int __ipmi_heartbeat(struct ipmi_wdt *iwd, bool do_poll) { struct kernel_ipmi_msg msg; int rv; @@ -551,10 +668,10 @@ static int __ipmi_heartbeat(void) * Don't reset the timer if we have the timer turned off, that * re-enables the watchdog. */ - if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE) + if (iwd->state == WDOG_TIMEOUT_NONE) return 0; - atomic_set(&msg_tofree, 2); + atomic_set(&iwd->msg_tofree, 2); addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; addr.channel = IPMI_BMC_CHANNEL; @@ -564,23 +681,24 @@ static int __ipmi_heartbeat(void) msg.cmd = IPMI_WDOG_RESET_TIMER; msg.data = NULL; msg.data_len = 0; - rv = ipmi_request_supply_msgs(watchdog_user, + rv = ipmi_request_supply_msgs(iwd->user, (struct ipmi_addr *) &addr, 0, &msg, NULL, - &smi_msg, - &recv_msg, + &iwd->smi_msg, + &iwd->recv_msg, 1); if (rv) { + atomic_set(&iwd->msg_tofree, 0); pr_warn("heartbeat send failure: %d\n", rv); return rv; } /* Wait for the heartbeat to be sent. */ - wait_for_completion(&msg_wait); + wait_msg_done(iwd, do_poll); - if (recv_msg.msg.data[0] == IPMI_WDOG_TIMER_NOT_INIT_RESP) { + if (iwd->recv_msg.msg.data[0] == IPMI_WDOG_TIMER_NOT_INIT_RESP) { timeout_retries++; if (timeout_retries > 3) { pr_err("Unable to restore the IPMI watchdog's settings, giving up\n"); @@ -596,7 +714,8 @@ static int __ipmi_heartbeat(void) * in this process, so must say no heartbeat to avoid a * deadlock on this mutex */ - rv = _ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); + rv = _ipmi_update_timeout(iwd, IPMI_SET_TIMEOUT_NO_HB, + do_poll); if (rv) { pr_err("Unable to send the command to set the watchdog's settings, giving up\n"); goto out; @@ -604,7 +723,7 @@ static int __ipmi_heartbeat(void) /* Might need a heartbeat send, go ahead and do it. */ goto restart; - } else if (recv_msg.msg.data[0] != 0) { + } else if (iwd->recv_msg.msg.data[0] != 0) { /* * Got an error in the heartbeat response. It was already * reported in ipmi_wdog_msg_handler, but we should return @@ -617,545 +736,434 @@ static int __ipmi_heartbeat(void) return rv; } -static int _ipmi_heartbeat(void) +static int _ipmi_heartbeat(struct ipmi_wdt *iwd) { int rv; - if (!watchdog_user) - return -ENODEV; - - if (ipmi_start_timer_on_heartbeat) { - ipmi_start_timer_on_heartbeat = 0; - ipmi_watchdog_state = action_val; - rv = _ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB); - } else if (atomic_cmpxchg(&pretimeout_since_last_heartbeat, 1, 0)) { + if (atomic_cmpxchg(&iwd->pretimeout_since_last_heartbeat, 1, 0)) { /* * A pretimeout occurred, make sure we set the timeout. * We don't want to set the action, though, we want to * leave that alone (thus it can't be combined with the * above operation. */ - rv = _ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); + rv = _ipmi_update_timeout(iwd, + IPMI_SET_TIMEOUT_HB_IF_NECESSARY, + false); } else { - rv = __ipmi_heartbeat(); + rv = __ipmi_heartbeat(iwd, false); } return rv; } -static int ipmi_heartbeat(void) +static void ipmi_wdog_msg_handler(struct ipmi_recv_msg *msg, + void *handler_data) { - int rv; - - mutex_lock(&ipmi_watchdog_mutex); - rv = _ipmi_heartbeat(); - mutex_unlock(&ipmi_watchdog_mutex); + if (msg->msg.cmd == IPMI_WDOG_RESET_TIMER && + msg->msg.data[0] == IPMI_WDOG_TIMER_NOT_INIT_RESP) + pr_info("response: The IPMI controller appears to have been reset, will attempt to reinitialize the watchdog timer\n"); + else if (msg->msg.data[0] != 0) + pr_err("response: Error %x on cmd %x\n", + msg->msg.data[0], + msg->msg.cmd); - return rv; + ipmi_free_recv_msg(msg); } -static struct watchdog_info ident = { - .options = 0, /* WDIOF_SETTIMEOUT, */ - .firmware_version = 1, - .identity = "IPMI" -}; - -static int ipmi_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) +static void ipmi_wdog_pretimeout_handler(void *handler_data) { - void __user *argp = (void __user *)arg; - int i; - int val; - - switch (cmd) { - case WDIOC_GETSUPPORT: - i = copy_to_user(argp, &ident, sizeof(ident)); - return i ? -EFAULT : 0; - - case WDIOC_SETTIMEOUT: - i = copy_from_user(&val, argp, sizeof(int)); - if (i) - return -EFAULT; - timeout = val; - return _ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); - - case WDIOC_GETTIMEOUT: - i = copy_to_user(argp, &timeout, sizeof(timeout)); - if (i) - return -EFAULT; - return 0; + struct ipmi_wdt *iwd = handler_data; - case WDIOC_SETPRETIMEOUT: - i = copy_from_user(&val, argp, sizeof(int)); - if (i) - return -EFAULT; - pretimeout = val; - return _ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); - - case WDIOC_GETPRETIMEOUT: - i = copy_to_user(argp, &pretimeout, sizeof(pretimeout)); - if (i) - return -EFAULT; - return 0; + /* + * On some machines, the heartbeat will give an error and not + * work unless we re-enable the timer. So do so. Do this + * before the notify because it may panic. + */ + atomic_set(&iwd->pretimeout_since_last_heartbeat, 1); - case WDIOC_KEEPALIVE: - return _ipmi_heartbeat(); - - case WDIOC_SETOPTIONS: - i = copy_from_user(&val, argp, sizeof(int)); - if (i) - return -EFAULT; - if (val & WDIOS_DISABLECARD) { - ipmi_watchdog_state = WDOG_TIMEOUT_NONE; - _ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); - ipmi_start_timer_on_heartbeat = 0; - } + if (!testing_nmi) + watchdog_notify_pretimeout(&iwd->wdd); +} - if (val & WDIOS_ENABLECARD) { - ipmi_watchdog_state = action_val; - _ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB); - } - return 0; +static void ipmi_wdog_panic_handler(void *user_data) +{ + struct ipmi_wdt *iwd = user_data; - case WDIOC_GETSTATUS: - val = 0; - i = copy_to_user(argp, &val, sizeof(val)); - if (i) - return -EFAULT; - return 0; + /* + * On a panic, if we have a panic timeout, make sure to extend + * the watchdog timer to a reasonable value to complete the + * panic, if the watchdog timer is running. Plus the + * pretimeout is meaningless at panic time. + */ + if (!iwd->panic_event_handled && + iwd->state != WDOG_TIMEOUT_NONE) { + /* Make sure we do this only once. */ + iwd->panic_event_handled = true; - default: - return -ENOIOCTLCMD; + iwd->wdd.timeout = panic_wdt_timeout; + iwd->wdd.pretimeout = 0; + panic_halt_ipmi_update_timeout(iwd); } } -static long ipmi_unlocked_ioctl(struct file *file, - unsigned int cmd, - unsigned long arg) +static int ipmi_wdt_start(struct watchdog_device *wdd) { - int ret; + struct ipmi_wdt *iwd = wdd_to_ipmi_wdt(wdd); + int rv; - mutex_lock(&ipmi_watchdog_mutex); - ret = ipmi_ioctl(file, cmd, arg); - mutex_unlock(&ipmi_watchdog_mutex); + mutex_lock(&iwd->lock); + iwd->state = action_val; + rv = _ipmi_update_timeout(iwd, IPMI_SET_TIMEOUT_FORCE_HB, false); + mutex_unlock(&iwd->lock); - return ret; + return rv; } -static ssize_t ipmi_write(struct file *file, - const char __user *buf, - size_t len, - loff_t *ppos) +static int ipmi_wdt_stop(struct watchdog_device *wdd) { + struct ipmi_wdt *iwd = wdd_to_ipmi_wdt(wdd); int rv; - if (len) { - if (!nowayout) { - size_t i; + mutex_lock(&iwd->lock); + iwd->state = WDOG_TIMEOUT_NONE; + rv = _ipmi_update_timeout(iwd, IPMI_SET_TIMEOUT_NO_HB, false); + mutex_unlock(&iwd->lock); + + return rv; +} - /* In case it was set long ago */ - expect_close = 0; +static int ipmi_wdt_ping(struct watchdog_device *wdd) +{ + struct ipmi_wdt *iwd = wdd_to_ipmi_wdt(wdd); + int rv; - for (i = 0; i != len; i++) { - char c; + mutex_lock(&iwd->lock); + rv = _ipmi_heartbeat(iwd); + mutex_unlock(&iwd->lock); - if (get_user(c, buf + i)) - return -EFAULT; - if (c == 'V') - expect_close = 42; - } - } - rv = ipmi_heartbeat(); - if (rv) - return rv; - } - return len; + return rv; } -static ssize_t ipmi_read(struct file *file, - char __user *buf, - size_t count, - loff_t *ppos) +static int ipmi_wdt_set_timeout(struct watchdog_device *wdd, unsigned int val) { - int rv = 0; - wait_queue_entry_t wait; + struct ipmi_wdt *iwd = wdd_to_ipmi_wdt(wdd); - if (count <= 0) - return 0; + return ipmi_set_timeout(iwd, val, IPMI_SET_TIMEOUT_HB_IF_NECESSARY); +} - /* - * Reading returns if the pretimeout has gone off, and it only does - * it once per pretimeout. - */ - spin_lock_irq(&ipmi_read_lock); - if (!data_to_read) { - if (file->f_flags & O_NONBLOCK) { - rv = -EAGAIN; - goto out; - } - - init_waitqueue_entry(&wait, current); - add_wait_queue(&read_q, &wait); - while (!data_to_read) { - set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irq(&ipmi_read_lock); - schedule(); - spin_lock_irq(&ipmi_read_lock); - } - remove_wait_queue(&read_q, &wait); - - if (signal_pending(current)) { - rv = -ERESTARTSYS; - goto out; - } - } - data_to_read = 0; +static int ipmi_wdt_set_pretimeout(struct watchdog_device *wdd, + unsigned int val) +{ + struct ipmi_wdt *iwd = wdd_to_ipmi_wdt(wdd); + int rv; - out: - spin_unlock_irq(&ipmi_read_lock); - - if (rv == 0) { - if (copy_to_user(buf, &data_to_read, 1)) - rv = -EFAULT; - else - rv = 1; - } + mutex_lock(&iwd->lock); + iwd->wdd.pretimeout = val; + rv = _ipmi_update_timeout(iwd, IPMI_SET_TIMEOUT_NO_HB, false); + mutex_unlock(&iwd->lock); return rv; } -static int ipmi_open(struct inode *ino, struct file *filep) -{ - switch (iminor(ino)) { - case WATCHDOG_MINOR: - if (test_and_set_bit(0, &ipmi_wdog_open)) - return -EBUSY; +static const struct watchdog_info ipmi_wdt_ident = { + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | + WDIOF_PRETIMEOUT | WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "IPMI" +}; +static const struct watchdog_ops ipmi_wdt_ops = { + .owner = THIS_MODULE, + .start = ipmi_wdt_start, + .stop = ipmi_wdt_stop, + .ping = ipmi_wdt_ping, + .set_timeout = ipmi_wdt_set_timeout, + .set_pretimeout = ipmi_wdt_set_pretimeout, +}; - /* - * Don't start the timer now, let it start on the - * first heartbeat. - */ - ipmi_start_timer_on_heartbeat = 1; - return stream_open(ino, filep); +static const struct ipmi_user_hndl ipmi_hndlrs = { + .ipmi_recv_hndl = ipmi_wdog_msg_handler, + .ipmi_watchdog_pretimeout = ipmi_wdog_pretimeout_handler, + .ipmi_panic_handler = ipmi_wdog_panic_handler +}; - default: - return (-ENODEV); - } -} +#ifdef HAVE_DIE_NMI +static DEFINE_MUTEX(ipmi_nmi_test_mutex); +static bool nmi_handler_registered; -static __poll_t ipmi_poll(struct file *file, poll_table *wait) -{ - __poll_t mask = 0; +/* If a pretimeout occurs, this is used to allow only one panic to happen. */ +static atomic_t preop_panic_excl = ATOMIC_INIT(-1); - poll_wait(file, &read_q, wait); +static int +ipmi_nmi(unsigned int val, struct pt_regs *regs) +{ + struct ipmi_wdt *iwd = READ_ONCE(ipmi_wdt); - spin_lock_irq(&ipmi_read_lock); - if (data_to_read) - mask |= (EPOLLIN | EPOLLRDNORM); - spin_unlock_irq(&ipmi_read_lock); + /* + * If we get here, it's an NMI that's not a memory or I/O + * error. We can't truly tell if it's from IPMI or not + * without sending a message, and sending a message is almost + * impossible because of locking. + */ - return mask; -} + if (testing_nmi) { + testing_nmi = 2; + return NMI_HANDLED; + } -static int ipmi_fasync(int fd, struct file *file, int on) -{ - int result; + if (!iwd) + return NMI_DONE; - result = fasync_helper(fd, file, on, &fasync_q); + /* If we are not expecting a timeout, ignore it. */ + if (iwd->state == WDOG_TIMEOUT_NONE) + return NMI_DONE; - return (result); -} + if (preaction_val != WDOG_PRETIMEOUT_NMI) + return NMI_DONE; -static int ipmi_close(struct inode *ino, struct file *filep) -{ - if (iminor(ino) == WATCHDOG_MINOR) { - if (expect_close == 42) { - mutex_lock(&ipmi_watchdog_mutex); - ipmi_watchdog_state = WDOG_TIMEOUT_NONE; - _ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); - mutex_unlock(&ipmi_watchdog_mutex); - } else { - pr_crit("Unexpected close, not stopping watchdog!\n"); - ipmi_heartbeat(); - } - clear_bit(0, &ipmi_wdog_open); + /* + * If no one else handled the NMI, we assume it was the IPMI + * watchdog. + */ + if (preop_val == WDOG_PREOP_PANIC) { + /* + * On some machines, the heartbeat will give an error + * and not work unless we re-enable the timer. So + * make it do so on the next heartbeat. + */ + atomic_set(&iwd->pretimeout_since_last_heartbeat, 1); + if (atomic_inc_and_test(&preop_panic_excl)) + nmi_panic(regs, "pre-timeout"); } - expect_close = 0; - - return 0; + return NMI_HANDLED; } -static const struct file_operations ipmi_wdog_fops = { - .owner = THIS_MODULE, - .read = ipmi_read, - .poll = ipmi_poll, - .write = ipmi_write, - .unlocked_ioctl = ipmi_unlocked_ioctl, - .open = ipmi_open, - .release = ipmi_close, - .fasync = ipmi_fasync, - .llseek = no_llseek, -}; - -static struct miscdevice ipmi_wdog_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &ipmi_wdog_fops -}; - -static void ipmi_wdog_msg_handler(struct ipmi_recv_msg *msg, - void *handler_data) +static void nmi_check(struct ipmi_wdt *iwd) { - if (msg->msg.cmd == IPMI_WDOG_RESET_TIMER && - msg->msg.data[0] == IPMI_WDOG_TIMER_NOT_INIT_RESP) - pr_info("response: The IPMI controller appears to have been reset, will attempt to reinitialize the watchdog timer\n"); - else if (msg->msg.data[0] != 0) - pr_err("response: Error %x on cmd %x\n", - msg->msg.data[0], - msg->msg.cmd); + int old_state = iwd->state; + int old_pretimeout = iwd->wdd.pretimeout; + int old_timeout = iwd->wdd.timeout; + int rv; + unsigned int count; - ipmi_free_recv_msg(msg); -} + if (iwd->nmi_works) + return; -static void ipmi_wdog_pretimeout_handler(void *handler_data) -{ - if (preaction_val != WDOG_PRETIMEOUT_NONE) { - if (preop_val == WDOG_PREOP_PANIC) { - if (atomic_inc_and_test(&preop_panic_excl)) - panic("Watchdog pre-timeout"); - } else if (preop_val == WDOG_PREOP_GIVE_DATA) { - unsigned long flags; - - spin_lock_irqsave(&ipmi_read_lock, flags); - data_to_read = 1; - wake_up_interruptible(&read_q); - kill_fasync(&fasync_q, SIGIO, POLL_IN); - spin_unlock_irqrestore(&ipmi_read_lock, flags); + mutex_lock(&ipmi_nmi_test_mutex); + if (!nmi_handler_registered) { + rv = register_nmi_handler(NMI_UNKNOWN, ipmi_nmi, 0, + "ipmi"); + if (rv) { + iwd->nmi_works = false; + pr_warn("Can't register nmi handler\n"); + goto out_restore; + } else { + nmi_handler_registered = true; } } /* - * On some machines, the heartbeat will give an error and not - * work unless we re-enable the timer. So do so. + * Set the pretimeout to go off in a second and give + * ourselves 99 seconds to stop the timer. */ - atomic_set(&pretimeout_since_last_heartbeat, 1); -} + iwd->state = WDOG_TIMEOUT_RESET; + iwd->wdd.pretimeout = 99; + iwd->wdd.timeout = 100; -static void ipmi_wdog_panic_handler(void *user_data) -{ - static int panic_event_handled; + testing_nmi = 1; - /* - * On a panic, if we have a panic timeout, make sure to extend - * the watchdog timer to a reasonable value to complete the - * panic, if the watchdog timer is running. Plus the - * pretimeout is meaningless at panic time. - */ - if (watchdog_user && !panic_event_handled && - ipmi_watchdog_state != WDOG_TIMEOUT_NONE) { - /* Make sure we do this only once. */ - panic_event_handled = 1; + rv = _ipmi_update_timeout(iwd, IPMI_SET_TIMEOUT_FORCE_HB, false); + if (rv) { + pr_warn("Error starting timer to test NMI: 0x%x. The NMI pretimeout will likely not work\n", + rv); + iwd->nmi_works = false; + goto out_restore; + } - timeout = panic_wdt_timeout; - pretimeout = 0; - panic_halt_ipmi_set_timeout(); + if (iwd->recv_msg.msg.data[0] != 0) { + pr_warn("Error in IPMI NMI pretimeout set: 0x%x. The NMI pretimeout will likely not work\n", + iwd->recv_msg.msg.data[0]); + iwd->nmi_works = false; + goto out_restore; } -} -static const struct ipmi_user_hndl ipmi_hndlrs = { - .ipmi_recv_hndl = ipmi_wdog_msg_handler, - .ipmi_watchdog_pretimeout = ipmi_wdog_pretimeout_handler, - .ipmi_panic_handler = ipmi_wdog_panic_handler -}; + for (count = 0; count < 50; count++) { + if (testing_nmi == 2) + break; + msleep(20); + } + + if (testing_nmi == 2) { + iwd->nmi_works = true; + } else { + iwd->nmi_works = false; + pr_warn("IPMI NMI didn't seem to occur. The NMI pretimeout will likely not work\n"); + } + out_restore: + testing_nmi = 0; + mutex_unlock(&ipmi_nmi_test_mutex); + iwd->wdd.pretimeout = old_pretimeout; + iwd->wdd.timeout = old_timeout; + iwd->state = old_state; + rv = _ipmi_update_timeout(iwd, IPMI_SET_TIMEOUT_FORCE_HB, false); + if (rv) + pr_warn("Unable to stop timer after NMI test: 0x%x.\n", rv); +} +#else +static void nmi_check(struct ipmi_wdt *iwd) +{ + iwd->nmi_works = false; +} +#endif -static void ipmi_register_watchdog(int ipmi_intf) +static void ipmi_register_watchdog(int ipmi_intf, struct device *dev) { + struct ipmi_wdt *iwd = NULL; int rv = -EBUSY; - if (watchdog_user) + mutex_lock(&ipmi_wdt_data_mutex); + if ((ifnum_to_use != -1) && (ifnum_to_use != ipmi_intf)) goto out; - - if ((ifnum_to_use >= 0) && (ifnum_to_use != ipmi_intf)) + /* -1 means allow the first interface. */ + if (ifnum_to_use == -1 && ipmi_wdt) goto out; - watchdog_ifnum = ipmi_intf; - - rv = ipmi_create_user(ipmi_intf, &ipmi_hndlrs, NULL, &watchdog_user); + iwd = kzalloc(sizeof(*iwd), GFP_KERNEL); + if (!iwd) { + rv = -ENOMEM; + goto out; + } + iwd->ifnum = ipmi_intf; + init_completion(&iwd->msg_wait); + iwd->smi_msg.done = msg_free_smi; + iwd->recv_msg.done = msg_free_recv; + iwd->state = WDOG_TIMEOUT_NONE; + mutex_init(&iwd->lock); + + rv = ipmi_create_user(ipmi_intf, &ipmi_hndlrs, iwd, &iwd->user); if (rv < 0) { pr_crit("Unable to register with ipmi\n"); goto out; } - rv = ipmi_get_version(watchdog_user, - &ipmi_version_major, - &ipmi_version_minor); + rv = ipmi_get_version(iwd->user, + &iwd->ipmi_version_major, + &iwd->ipmi_version_minor); if (rv) { pr_warn("Unable to get IPMI version, assuming 1.0\n"); - ipmi_version_major = 1; - ipmi_version_minor = 0; + iwd->ipmi_version_major = 1; + iwd->ipmi_version_minor = 0; } - rv = misc_register(&ipmi_wdog_miscdev); - if (rv < 0) { - ipmi_destroy_user(watchdog_user); - watchdog_user = NULL; - pr_crit("Unable to register misc device\n"); + iwd->wdd.ops = &ipmi_wdt_ops; + iwd->info = ipmi_wdt_ident; + iwd->info.firmware_version = (iwd->ipmi_version_major << 8 | + iwd->ipmi_version_minor); + iwd->wdd.info = &iwd->info; + iwd->wdd.max_timeout = IPMI_MAX_TIMEOUT; + iwd->wdd.min_timeout = IPMI_MIN_TIMEOUT; + watchdog_stop_on_unregister(&iwd->wdd); + watchdog_set_nowayout(&iwd->wdd, nowayout); + watchdog_init_timeout(&iwd->wdd, timeout, NULL); + iwd->wdd.pretimeout = pretimeout; + iwd->wdd.parent = dev; + + rv = watchdog_register_device(&iwd->wdd); + if (rv) { + ipmi_destroy_user(iwd->user); + pr_crit("Unable to register IPMI watchdog device\n"); + goto out; } -#ifdef HAVE_DIE_NMI - if (nmi_handler_registered) { - int old_pretimeout = pretimeout; - int old_timeout = timeout; - int old_preop_val = preop_val; - - /* - * Set the pretimeout to go off in a second and give - * ourselves plenty of time to stop the timer. - */ - ipmi_watchdog_state = WDOG_TIMEOUT_RESET; - preop_val = WDOG_PREOP_NONE; /* Make sure nothing happens */ - pretimeout = 99; - timeout = 100; + nmi_check(iwd); - testing_nmi = 1; - - rv = ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB); - if (rv) { - pr_warn("Error starting timer to test NMI: 0x%x. The NMI pretimeout will likely not work\n", - rv); - rv = 0; - goto out_restore; - } - - msleep(1500); - - if (testing_nmi != 2) { - pr_warn("IPMI NMI didn't seem to occur. The NMI pretimeout will likely not work\n"); - } - out_restore: - testing_nmi = 0; - preop_val = old_preop_val; - pretimeout = old_pretimeout; - timeout = old_timeout; + if (preaction_val == WDOG_PRETIMEOUT_NMI && !iwd->nmi_works) { + pr_warn("NMI pretimeout test failed, disabling pretimeout.\n"); + preaction_val = WDOG_PRETIMEOUT_NONE; } -#endif + + ipmi_wdt = iwd; out: - if ((start_now) && (rv == 0)) { + mutex_unlock(&ipmi_wdt_data_mutex); + if (start_now && rv == 0) { /* Run from startup, so start the timer now. */ start_now = 0; /* Disable this function after first startup. */ - ipmi_watchdog_state = action_val; - ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB); + iwd->state = action_val; + mutex_lock(&iwd->lock); + _ipmi_update_timeout(iwd, IPMI_SET_TIMEOUT_FORCE_HB, false); + mutex_unlock(&iwd->lock); pr_info("Starting now!\n"); - } else { - /* Stop the timer now. */ - ipmi_watchdog_state = WDOG_TIMEOUT_NONE; - ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); + } else if (iwd && rv != 0) { + kfree(iwd); } } -static void ipmi_unregister_watchdog(int ipmi_intf) +static void _ipmi_unregister_watchdog(struct ipmi_wdt *iwd) { int rv; - struct ipmi_user *loc_user = watchdog_user; - - if (!loc_user) - return; - - if (watchdog_ifnum != ipmi_intf) - return; - - /* Make sure no one can call us any more. */ - misc_deregister(&ipmi_wdog_miscdev); - - watchdog_user = NULL; /* - * Wait to make sure the message makes it out. The lower layer has - * pointers to our buffers, we want to make sure they are done before - * we release our memory. + * This function should make sure that the misc device has no + * outstanding calls, thus we should have no messages being + * used after this. */ - while (atomic_read(&msg_tofree)) - msg_free_smi(NULL); + watchdog_unregister_device(&iwd->wdd); - mutex_lock(&ipmi_watchdog_mutex); + /* Make sure no NMIs or interrupts are running. */ + synchronize_rcu(); /* Disconnect from IPMI. */ - rv = ipmi_destroy_user(loc_user); + rv = ipmi_destroy_user(iwd->user); if (rv) pr_warn("error unlinking from IPMI: %d\n", rv); - /* If it comes back, restart it properly. */ - ipmi_start_timer_on_heartbeat = 1; - - mutex_unlock(&ipmi_watchdog_mutex); + kfree(iwd); } -#ifdef HAVE_DIE_NMI -static int -ipmi_nmi(unsigned int val, struct pt_regs *regs) +static void ipmi_unregister_watchdog(int ipmi_intf) { - /* - * If we get here, it's an NMI that's not a memory or I/O - * error. We can't truly tell if it's from IPMI or not - * without sending a message, and sending a message is almost - * impossible because of locking. - */ + struct ipmi_wdt *iwd = NULL; - if (testing_nmi) { - testing_nmi = 2; - return NMI_HANDLED; - } - - /* If we are not expecting a timeout, ignore it. */ - if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE) - return NMI_DONE; - - if (preaction_val != WDOG_PRETIMEOUT_NMI) - return NMI_DONE; - - /* - * If no one else handled the NMI, we assume it was the IPMI - * watchdog. - */ - if (preop_val == WDOG_PREOP_PANIC) { - /* On some machines, the heartbeat will give - an error and not work unless we re-enable - the timer. So do so. */ - atomic_set(&pretimeout_since_last_heartbeat, 1); - if (atomic_inc_and_test(&preop_panic_excl)) - nmi_panic(regs, "pre-timeout"); + mutex_lock(&ipmi_wdt_data_mutex); + if (ipmi_wdt->ifnum == ipmi_intf) { + iwd = ipmi_wdt; + ipmi_wdt = NULL; } + mutex_unlock(&ipmi_wdt_data_mutex); + if (!iwd) + return; - return NMI_HANDLED; + _ipmi_unregister_watchdog(iwd); } -#endif static int wdog_reboot_handler(struct notifier_block *this, unsigned long code, void *unused) { static int reboot_event_handled; + struct ipmi_wdt *iwd = READ_ONCE(ipmi_wdt); - if ((watchdog_user) && (!reboot_event_handled)) { + if (iwd && !reboot_event_handled) { /* Make sure we only do this once. */ reboot_event_handled = 1; if (code == SYS_POWER_OFF || code == SYS_HALT) { /* Disable the WDT if we are shutting down. */ - ipmi_watchdog_state = WDOG_TIMEOUT_NONE; - ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); - } else if (ipmi_watchdog_state != WDOG_TIMEOUT_NONE) { + iwd->state = WDOG_TIMEOUT_NONE; + _ipmi_update_timeout(iwd, IPMI_SET_TIMEOUT_NO_HB, + false); + } else if (iwd->state != WDOG_TIMEOUT_NONE) { /* Set a long timer to let the reboot happen or reset if it hangs, but only if the watchdog timer was already running. */ - if (timeout < 120) - timeout = 120; - pretimeout = 0; - ipmi_watchdog_state = WDOG_TIMEOUT_RESET; - ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); + if (iwd->wdd.timeout < panic_wdt_timeout) + iwd->wdd.timeout = panic_wdt_timeout; + iwd->wdd.pretimeout = 0; + _ipmi_update_timeout(iwd, IPMI_SET_TIMEOUT_NO_HB, + false); } } return NOTIFY_OK; @@ -1167,147 +1175,30 @@ static struct notifier_block wdog_reboot_notifier = { .priority = 0 }; -static void ipmi_new_smi(int if_num, struct device *device) -{ - ipmi_register_watchdog(if_num); -} - -static void ipmi_smi_gone(int if_num) -{ - ipmi_unregister_watchdog(if_num); -} - -static struct ipmi_smi_watcher smi_watcher = { - .owner = THIS_MODULE, - .new_smi = ipmi_new_smi, - .smi_gone = ipmi_smi_gone -}; - -static int action_op(const char *inval, char *outval) -{ - if (outval) - strcpy(outval, action); - - if (!inval) - return 0; - - if (strcmp(inval, "reset") == 0) - action_val = WDOG_TIMEOUT_RESET; - else if (strcmp(inval, "none") == 0) - action_val = WDOG_TIMEOUT_NONE; - else if (strcmp(inval, "power_cycle") == 0) - action_val = WDOG_TIMEOUT_POWER_CYCLE; - else if (strcmp(inval, "power_off") == 0) - action_val = WDOG_TIMEOUT_POWER_DOWN; - else - return -EINVAL; - strcpy(action, inval); - return 0; -} - -static int preaction_op(const char *inval, char *outval) -{ - if (outval) - strcpy(outval, preaction); - - if (!inval) - return 0; - - if (strcmp(inval, "pre_none") == 0) - preaction_val = WDOG_PRETIMEOUT_NONE; - else if (strcmp(inval, "pre_smi") == 0) - preaction_val = WDOG_PRETIMEOUT_SMI; -#ifdef HAVE_DIE_NMI - else if (strcmp(inval, "pre_nmi") == 0) - preaction_val = WDOG_PRETIMEOUT_NMI; -#endif - else if (strcmp(inval, "pre_int") == 0) - preaction_val = WDOG_PRETIMEOUT_MSG_INT; - else - return -EINVAL; - strcpy(preaction, inval); - return 0; -} - -static int preop_op(const char *inval, char *outval) -{ - if (outval) - strcpy(outval, preop); - - if (!inval) - return 0; - - if (strcmp(inval, "preop_none") == 0) - preop_val = WDOG_PREOP_NONE; - else if (strcmp(inval, "preop_panic") == 0) - preop_val = WDOG_PREOP_PANIC; - else if (strcmp(inval, "preop_give_data") == 0) - preop_val = WDOG_PREOP_GIVE_DATA; - else - return -EINVAL; - strcpy(preop, inval); - return 0; -} - -static void check_parms(void) -{ -#ifdef HAVE_DIE_NMI - int do_nmi = 0; - int rv; - - if (preaction_val == WDOG_PRETIMEOUT_NMI) { - do_nmi = 1; - if (preop_val == WDOG_PREOP_GIVE_DATA) { - pr_warn("Pretimeout op is to give data but NMI pretimeout is enabled, setting pretimeout op to none\n"); - preop_op("preop_none", NULL); - do_nmi = 0; - } - } - if (do_nmi && !nmi_handler_registered) { - rv = register_nmi_handler(NMI_UNKNOWN, ipmi_nmi, 0, - "ipmi"); - if (rv) { - pr_warn("Can't register nmi handler\n"); - return; - } else - nmi_handler_registered = 1; - } else if (!do_nmi && nmi_handler_registered) { - unregister_nmi_handler(NMI_UNKNOWN, "ipmi"); - nmi_handler_registered = 0; - } -#endif -} - static int __init ipmi_wdog_init(void) { int rv; if (action_op(action, NULL)) { - action_op("reset", NULL); pr_info("Unknown action '%s', defaulting to reset\n", action); + action_op("reset", NULL); } if (preaction_op(preaction, NULL)) { - preaction_op("pre_none", NULL); pr_info("Unknown preaction '%s', defaulting to none\n", preaction); + preaction_op("pre_none", NULL); } if (preop_op(preop, NULL)) { - preop_op("preop_none", NULL); pr_info("Unknown preop '%s', defaulting to none\n", preop); + preop_op("preop_none", NULL); } - check_parms(); - register_reboot_notifier(&wdog_reboot_notifier); rv = ipmi_smi_watcher_register(&smi_watcher); if (rv) { -#ifdef HAVE_DIE_NMI - if (nmi_handler_registered) - unregister_nmi_handler(NMI_UNKNOWN, "ipmi"); -#endif unregister_reboot_notifier(&wdog_reboot_notifier); pr_warn("can't register smi watcher\n"); return rv; @@ -1321,7 +1212,8 @@ static int __init ipmi_wdog_init(void) static void __exit ipmi_wdog_exit(void) { ipmi_smi_watcher_unregister(&smi_watcher); - ipmi_unregister_watchdog(watchdog_ifnum); + if (ipmi_wdt) + ipmi_unregister_watchdog(ipmi_wdt->ifnum); #ifdef HAVE_DIE_NMI if (nmi_handler_registered) From patchwork Mon Aug 19 20:37:06 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Corey Minyard X-Patchwork-Id: 11101895 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 93361912 for ; Mon, 19 Aug 2019 20:37:18 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 72C4722CE8 for ; Mon, 19 Aug 2019 20:37:18 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="V9qsVg12" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728255AbfHSUhS (ORCPT ); Mon, 19 Aug 2019 16:37:18 -0400 Received: from mail-oi1-f194.google.com ([209.85.167.194]:45969 "EHLO mail-oi1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727769AbfHSUhR (ORCPT ); Mon, 19 Aug 2019 16:37:17 -0400 Received: by mail-oi1-f194.google.com with SMTP id v12so2355572oic.12 for ; Mon, 19 Aug 2019 13:37:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :reply-to; bh=rr7PFA4y4COyKOxB5OU5lFbFTlcHHXX96fkNQiJtv3g=; b=V9qsVg12Fy6vR58Tu6ILDoXkUHgD8jWS6NwTygJncRiEEz1/vbuAOC1TJkl+R5gymU xApCfFiA9HzkAtsN0Cs85IB8rAS01Rcv/9jIiiyAU5GQFp7RlRxmLvzKhpW87KMt1V8j Q3xyMsDzzomRExkhwOokWUbb8jbeqS3PZT35ChmTUJOnCwkrXFk2Ffir/r2q1JLL2ZsG 2jou54GJG5mYYyb08wQk8TF/Na2gx1tszKj3Q+5VnlHTGzJYgS3ntbMMXIogsokbWWbS PMGqYB5Qpt+veT/l1FU34SPDYSzVidP9aQ/HulPCIFD+7ymWiuEKUYTEy3u80gnAds1U YPmQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references:reply-to; bh=rr7PFA4y4COyKOxB5OU5lFbFTlcHHXX96fkNQiJtv3g=; b=BPGuBY+mMPAf/EDwQB7b9ODzHoe9KPNb0r6rmYnwM4JvpfmO3PtSl464JEalH9H9/K 06kqg2FI/ggxVWIc1plLR+fFEfuhtkawQN8G37z91dWUsd7njQELyxriiObuSVktIihx tf/gYSuAjbmcb2KlqjNWI0QNMKMY6JsEOx3AVPDCbSSI/Lu80D63QTY+DZmatPnHw6jP 1vqyMiqilXRyF6lAzWtX/RZW5FFL17pZ9KjIutIoQjB4pWI6RFVDcstNUwLaqGGcnf8c gxbVeTB3S/11Yy89G3QyNkPNU+jz7FZ7Tzalt5HgL+6TnrkKjEqxsLy4+iKUMWZuw1ZZ 459g== X-Gm-Message-State: APjAAAXEM8uSW+migveWx0sHOhWEKHuLq7ZmU610GL0ovVjygiRTDrbK 03mXGNbRsiNb7esBDEhiHg== X-Google-Smtp-Source: APXvYqze4qUnL/mPSHYAlsR3u8GhAPMeHu5jDk4VcTk3hGqiN+n5FeqrfMQ3xu9sNUlsEBKiGC7trQ== X-Received: by 2002:aca:58c3:: with SMTP id m186mr14804229oib.21.1566247036825; Mon, 19 Aug 2019 13:37:16 -0700 (PDT) Received: from serve.minyard.net ([47.184.134.43]) by smtp.gmail.com with ESMTPSA id g3sm6293416oti.41.2019.08.19.13.37.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2019 13:37:16 -0700 (PDT) Received: from t430.minyard.net (t430m.minyard.net [192.168.27.3]) by serve.minyard.net (Postfix) with ESMTPA id 61D111805AC; Mon, 19 Aug 2019 20:37:14 +0000 (UTC) Received: by t430.minyard.net (Postfix, from userid 1000) id 4D645302506; Mon, 19 Aug 2019 15:37:14 -0500 (CDT) From: minyard@acm.org To: linux-watchdog@vger.kernel.org, Guenter Roeck , Wim Van Sebroeck Cc: Corey Minyard Subject: [PATCH 07/12] watchdog:ipmi: Add the ability to fetch the current time left Date: Mon, 19 Aug 2019 15:37:06 -0500 Message-Id: <20190819203711.32599-8-minyard@acm.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190819203711.32599-1-minyard@acm.org> References: <20190819203711.32599-1-minyard@acm.org> Reply-To: "[PATCH 00/12]"@minyard.net, Convert@minyard.net, the@minyard.net, IPMI@minyard.net, watchdog@minyard.net, to@minyard.net, the@minyard.net, standard@minyard.net, interface@minyard.net Sender: linux-watchdog-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-watchdog@vger.kernel.org From: Corey Minyard The new API has the capability, and so do the IPMI interface, so do it. Signed-off-by: Corey Minyard --- drivers/watchdog/ipmi_watchdog.c | 57 ++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/drivers/watchdog/ipmi_watchdog.c b/drivers/watchdog/ipmi_watchdog.c index c8c11a61ebb9..218c01707c0b 100644 --- a/drivers/watchdog/ipmi_watchdog.c +++ b/drivers/watchdog/ipmi_watchdog.c @@ -757,6 +757,62 @@ static int _ipmi_heartbeat(struct ipmi_wdt *iwd) return rv; } +static unsigned int ipmi_wdt_get_timeleft(struct watchdog_device *wdd) +{ + struct ipmi_wdt *iwd = wdd_to_ipmi_wdt(wdd); + struct kernel_ipmi_msg msg; + int rv = 0; + struct ipmi_system_interface_addr addr; + + mutex_lock(&iwd->lock); + if (iwd->state == WDOG_TIMEOUT_NONE) + goto out_unlock; + + addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + addr.channel = IPMI_BMC_CHANNEL; + addr.lun = 0; + + atomic_set(&iwd->msg_tofree, 2); + + msg.netfn = 0x06; + msg.cmd = IPMI_WDOG_GET_TIMER; + msg.data = NULL; + msg.data_len = 0; + rv = ipmi_request_supply_msgs(iwd->user, + (struct ipmi_addr *) &addr, + 0, + &msg, + NULL, + &iwd->smi_msg, + &iwd->recv_msg, + 1); + if (rv) { + pr_warn("get timeout error: %d\n", rv); + goto out_unlock; + } + + wait_msg_done(iwd, false); + + if (iwd->recv_msg.msg.data[0] != 0) { + pr_warn("get timeout IPMI error: %d\n", + iwd->recv_msg.msg.data[0]); + goto out_unlock; + } + + if (iwd->recv_msg.msg.data_len < 9) { + pr_warn("get timeout IPMI response too short: %d\n", + iwd->recv_msg.msg.data_len); + goto out_unlock; + } + + rv = iwd->recv_msg.msg.data[8] << 8 | iwd->recv_msg.msg.data[7]; + rv /= 10; /* IPMI time is in 100s of milliseconds. */ + +out_unlock: + mutex_unlock(&iwd->lock); + return rv; +} + static void ipmi_wdog_msg_handler(struct ipmi_recv_msg *msg, void *handler_data) { @@ -880,6 +936,7 @@ static const struct watchdog_ops ipmi_wdt_ops = { .ping = ipmi_wdt_ping, .set_timeout = ipmi_wdt_set_timeout, .set_pretimeout = ipmi_wdt_set_pretimeout, + .get_timeleft = ipmi_wdt_get_timeleft, }; static const struct ipmi_user_hndl ipmi_hndlrs = { From patchwork Mon Aug 19 20:37:07 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Corey Minyard X-Patchwork-Id: 11101899 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 4DF9D912 for ; Mon, 19 Aug 2019 20:37:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 2CECC22CF5 for ; Mon, 19 Aug 2019 20:37:19 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="DBWh8Ykv" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728177AbfHSUhS (ORCPT ); Mon, 19 Aug 2019 16:37:18 -0400 Received: from mail-ot1-f66.google.com ([209.85.210.66]:44852 "EHLO mail-ot1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728229AbfHSUhS (ORCPT ); Mon, 19 Aug 2019 16:37:18 -0400 Received: by mail-ot1-f66.google.com with SMTP id w4so2928670ote.11 for ; Mon, 19 Aug 2019 13:37:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :reply-to; bh=u29FeR1SKyP0iadm6DJ9FKc4kWwIxuJkgydwi575YWQ=; b=DBWh8YkvU59NDatDgFlNzC7Q+uf8pXXzLtImsY3akNmv/5e71OS+zq8UNgzo2o1fV0 S/WAwIzjwzjoZFzPK7Z2JB8dlYqxOJ9iHaQx+pf3KhlTszCHUAc6IBbOVHR2qSnp1qfW HWCxusHvQadoeGx6tfRi8HOrQQW420t4O+tYh7uIc1rpfNaWeyjdh8aL/WAPXc1AUl1g gcZZrfqCjVYD106NBYl0kUL3Hjb+M675qOlxufcvOkx2QJCV7SCV9qlQwjcxBvAbCZOR jY08Lsb4fxXyvD3DWKSbqJK/jPLhP5JDRA5iOOz3RFR0e/nk5RFpp4F2dZTJftDaMCRl bopA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references:reply-to; bh=u29FeR1SKyP0iadm6DJ9FKc4kWwIxuJkgydwi575YWQ=; b=FdDFAbHPtYbOGE7nBzNvg2aUqB8LaUy8Hz18HPv04VCCdwAUvt0GrWlLHwm6rBQeaL olNpk0XTrEJQc6Mav4jJwGY2N7LyWyPmHN3DWzWG766sygAq5MR++JV1kxl1vd838zJh cRQfTBAb6XXjeA3VvCwV9XeQDmBuy2pMFr3L1LwALMMomE4vhd8gSRXN7HsxRL4pPXrE lsgMwriPe/K0JJFTzGQ6cPXXVc5G76IG5JJ80WH3DTySLwjcdeg2uPrYzO8x/ofqKxJi Ieji7bcIoxtJLdcOy5mO6r7RHTZbXcIA4+AzMHG4ZNN+sPsgW6PaMjQQ693FZErW7Qqg Nuhw== X-Gm-Message-State: APjAAAXu+RlOFFaTL5sqkorOpFuCFmofslao81KMl0uZH8FqkSDOciTk A8EpEsER7pdk+mMXVohxYrk8KbM= X-Google-Smtp-Source: APXvYqyjoloZgKs0ZUk9UYyL861P+5YUAadCySkZ30FyELdfU66dyVTMmrLikHN3HvrtheY0PUzE1g== X-Received: by 2002:a9d:a87:: with SMTP id 7mr19336659otq.256.1566247037099; Mon, 19 Aug 2019 13:37:17 -0700 (PDT) Received: from serve.minyard.net ([47.184.134.43]) by smtp.gmail.com with ESMTPSA id v24sm5746993otj.78.2019.08.19.13.37.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2019 13:37:16 -0700 (PDT) Received: from t430.minyard.net (t430m.minyard.net [192.168.27.3]) by serve.minyard.net (Postfix) with ESMTPA id 7105F1805AD; Mon, 19 Aug 2019 20:37:14 +0000 (UTC) Received: by t430.minyard.net (Postfix, from userid 1000) id 5C2A4301176; Mon, 19 Aug 2019 15:37:14 -0500 (CDT) From: minyard@acm.org To: linux-watchdog@vger.kernel.org, Guenter Roeck , Wim Van Sebroeck Cc: Corey Minyard Subject: [PATCH 08/12] watchdog: Add the ability to set the action of a timeout Date: Mon, 19 Aug 2019 15:37:07 -0500 Message-Id: <20190819203711.32599-9-minyard@acm.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190819203711.32599-1-minyard@acm.org> References: <20190819203711.32599-1-minyard@acm.org> Reply-To: "[PATCH 00/12]"@minyard.net, Convert@minyard.net, the@minyard.net, IPMI@minyard.net, watchdog@minyard.net, to@minyard.net, the@minyard.net, standard@minyard.net, interface@minyard.net Sender: linux-watchdog-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-watchdog@vger.kernel.org From: Corey Minyard Add a way to tell the watchdog what to do on a timeout and on a pretimeout. This is to support the IPMI watchdog's ability to do this. Signed-off-by: Corey Minyard --- Documentation/watchdog/watchdog-api.rst | 40 ++++++++++++ drivers/watchdog/watchdog_dev.c | 82 +++++++++++++++++++++++++ include/linux/watchdog.h | 4 ++ include/uapi/linux/watchdog.h | 14 +++++ 4 files changed, 140 insertions(+) diff --git a/Documentation/watchdog/watchdog-api.rst b/Documentation/watchdog/watchdog-api.rst index c6c1e9fa9f73..927be9e56b5d 100644 --- a/Documentation/watchdog/watchdog-api.rst +++ b/Documentation/watchdog/watchdog-api.rst @@ -112,6 +112,24 @@ current timeout using the GETTIMEOUT ioctl:: ioctl(fd, WDIOC_GETTIMEOUT, &timeout); printf("The timeout was is %d seconds\n", timeout); +Actions +======= + +Some watchdog timers can perform different actions when they time out. +Most will only reset. The values are:: + + WDIOA_RESET - Reset the system + WDIOA_POWER_OFF - Power off the system + WDIOA_POWER_CYCLE - Power off the system then power it back on + +The value can be set:: + + ioctl(fd, WDIOC_SETACTION, &action); + +and queried:: + + ioctl(fd, WDIOC_GETACTION, &action); + Pretimeouts =========== @@ -137,6 +155,28 @@ There is also a get function for getting the pretimeout:: Not all watchdog drivers will support a pretimeout. +Preactions +========== + +Like actions some watchdog timers can perform different actions when +they pretimeout. The values are:: + + WDIOP_NONE - Don't do anything on a pretimeout + WDIOP_NMI - Issue an NMI + WDIOP_SMI - Issue a system management interrupt + WDIOP_INTERRUPT - Issue a normal interrupt + +The value can be set:: + + ioctl(fd, WDIOC_SETPREACTION, &preaction); + +and queried:: + + ioctl(fd, WDIOC_GETPREACTION, &preaction); + +Note that the pretimeout governor that reads data is not compatible with +the NMI preaction. The NMI preaction can only do nothing or panic. + Get the number of seconds before reboot ======================================= diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 8e8304607a8c..0e70f510a491 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -423,6 +423,48 @@ static int watchdog_set_pretimeout(struct watchdog_device *wdd, return err; } +/* + * watchdog_set_action: set the action the watchdog performs. + * @wdd: the watchdog device to set the timeout for + * @action: The action, one of WDIOA_xxx + * + * The caller must hold wd_data->lock. + */ + +static int watchdog_set_action(struct watchdog_device *wdd, + unsigned int action) +{ + int err = 0; + + if (wdd->ops->set_action) + err = wdd->ops->set_action(wdd, action); + else if (action != WDIOA_RESET) + err = -EINVAL; + + return err; +} + +/* + * watchdog_set_preaction: set the action the watchdog pretimeout performs. + * @wdd: the watchdog device to set the timeout for + * @action: The action, one of WDIOP_xxx + * + * The caller must hold wd_data->lock. + */ + +static int watchdog_set_preaction(struct watchdog_device *wdd, + unsigned int action) +{ + int err; + + if (wdd->ops->set_preaction) + err = wdd->ops->set_preaction(wdd, action); + else + err = -EOPNOTSUPP; + + return err; +} + /* * watchdog_get_timeleft: wrapper to get the time left before a reboot * @wdd: the watchdog device to get the remaining time from @@ -516,6 +558,24 @@ static ssize_t pretimeout_show(struct device *dev, } static DEVICE_ATTR_RO(pretimeout); +static ssize_t action_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", wdd->action); +} +static DEVICE_ATTR_RO(action); + +static ssize_t preaction_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", wdd->preaction); +} +static DEVICE_ATTR_RO(preaction); + static ssize_t identity_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -592,6 +652,8 @@ static struct attribute *wdt_attrs[] = { &dev_attr_identity.attr, &dev_attr_timeout.attr, &dev_attr_pretimeout.attr, + &dev_attr_action.attr, + &dev_attr_preaction.attr, &dev_attr_timeleft.attr, &dev_attr_bootstatus.attr, &dev_attr_status.attr, @@ -784,6 +846,26 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, case WDIOC_GETPRETIMEOUT: err = put_user(wdd->pretimeout, p); break; + case WDIOC_SETACTION: + if (get_user(val, p)) { + err = -EFAULT; + break; + } + err = watchdog_set_action(wdd, val); + break; + case WDIOC_GETACTION: + err = put_user(wdd->action, p); + break; + case WDIOC_SETPREACTION: + if (get_user(val, p)) { + err = -EFAULT; + break; + } + err = watchdog_set_preaction(wdd, val); + break; + case WDIOC_GETPREACTION: + err = put_user(wdd->preaction, p); + break; default: err = -ENOTTY; break; diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index e34501a822f0..d4644994106e 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -53,6 +53,8 @@ struct watchdog_ops { unsigned int (*get_timeleft)(struct watchdog_device *); int (*restart)(struct watchdog_device *, unsigned long, void *); long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); + int (*set_action)(struct watchdog_device *wdd, unsigned int val); + int (*set_preaction)(struct watchdog_device *wdd, unsigned int val); }; /** struct watchdog_device - The structure that defines a watchdog device @@ -101,6 +103,8 @@ struct watchdog_device { unsigned int bootstatus; unsigned int timeout; unsigned int pretimeout; + unsigned int action; + unsigned int preaction; unsigned int min_timeout; unsigned int max_timeout; unsigned int min_hw_heartbeat_ms; diff --git a/include/uapi/linux/watchdog.h b/include/uapi/linux/watchdog.h index b15cde5c9054..bf13cf25f9e0 100644 --- a/include/uapi/linux/watchdog.h +++ b/include/uapi/linux/watchdog.h @@ -32,6 +32,10 @@ struct watchdog_info { #define WDIOC_SETPRETIMEOUT _IOWR(WATCHDOG_IOCTL_BASE, 8, int) #define WDIOC_GETPRETIMEOUT _IOR(WATCHDOG_IOCTL_BASE, 9, int) #define WDIOC_GETTIMELEFT _IOR(WATCHDOG_IOCTL_BASE, 10, int) +#define WDIOC_SETACTION _IOWR(WATCHDOG_IOCTL_BASE, 11, int) +#define WDIOC_GETACTION _IOR(WATCHDOG_IOCTL_BASE, 12, int) +#define WDIOC_SETPREACTION _IOWR(WATCHDOG_IOCTL_BASE, 13, int) +#define WDIOC_GETPREACTION _IOR(WATCHDOG_IOCTL_BASE, 14, int) #define WDIOF_UNKNOWN -1 /* Unknown flag error */ #define WDIOS_UNKNOWN -1 /* Unknown status error */ @@ -54,5 +58,15 @@ struct watchdog_info { #define WDIOS_ENABLECARD 0x0002 /* Turn on the watchdog timer */ #define WDIOS_TEMPPANIC 0x0004 /* Kernel panic on temperature trip */ +/* Actions for WDIOC_xxxACTION ioctls. */ +#define WDIOA_RESET 0 /* Reset the system. */ +#define WDIOA_POWER_OFF 1 /* Power off the system. */ +#define WDIOA_POWER_CYCLE 2 /* Power cycle the system. */ + +/* Actions for WDIOC_xxxPREACTION ioctls. */ +#define WDIOP_NONE 0 /* Do nothing. */ +#define WDIOP_NMI 1 /* Issue an NMI. */ +#define WDIOP_SMI 2 /* Issue a system management irq. */ +#define WDIOP_INTERRUPT 3 /* Issue a normal irq. */ #endif /* _UAPI_LINUX_WATCHDOG_H */ From patchwork Mon Aug 19 20:37:08 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Corey Minyard X-Patchwork-Id: 11101903 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BE8CA14DB for ; Mon, 19 Aug 2019 20:37:20 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 893E92087E for ; Mon, 19 Aug 2019 20:37:20 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="DOLEreBA" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728305AbfHSUhU (ORCPT ); Mon, 19 Aug 2019 16:37:20 -0400 Received: from mail-oi1-f195.google.com ([209.85.167.195]:44171 "EHLO mail-oi1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728229AbfHSUhU (ORCPT ); Mon, 19 Aug 2019 16:37:20 -0400 Received: by mail-oi1-f195.google.com with SMTP id k22so2362005oiw.11 for ; Mon, 19 Aug 2019 13:37:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :reply-to; bh=fsycyhYdZgkF4YS9wk2+CF1iHIRRiA9YfeRdUgHPIlg=; b=DOLEreBAIJWUMp6dMGgetaCZnKLvOhLhBoHTakt5yn3TFJtfi5xyUnH5q+mkX37Wcb PRNBhG/ysGI+6LMIwj0AABl11CqS1+s/3TkSCJNlcgdPELueWZm1L9zywQBVWwfGjWRM Vnx9FKnWOT4eQ4+4Su+uQ6WuUMa3TahiFWq9To7UuC7gW5dOcl2PbmuHH5ildEb1oRKu rvw/JarzLbuOZEWrtW7zyYVBrJUS1fsj5usCyFbRPy1JyVg0MvkeYY7epWAmigKhzQNx DK16BQbX2V+eWUIRvIZ74I8X4M2xHr6ArP56ErminEYnE/qZj9xDKEAttngdzw7mKI3d Q+AQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references:reply-to; bh=fsycyhYdZgkF4YS9wk2+CF1iHIRRiA9YfeRdUgHPIlg=; b=nRlObLXCyqnLttyde/nJcURKUQEWK5tcRHFwt6PWCjMY0J2AjRFwtUnQ2HMIfOhJDf XlptiGwwNfZOmXzza6U0vOXz7pXQDZ5MK1TUQNGPgBvapcklkWbMkbosyn9SV/8KsTA3 p/7f3JWtpknmSZ4FUBaJf5uWG8IhL2Lvv/n/M+DrAgE5wzu8hRmzSYkfpJunN1xUe2JF raoBX+Ds3IDb549yHkPETSqzVYmlfJt2P8mlw/SrzhzK30jMdH7Q1e0JcVm23O++8Xxl MjhkOK+ho5UGPeBpq72cboaKdlMCJxM0Zgfx3wo1Pu5d3x7CmbXUACuK9JdiowGJ7pKW iSSA== X-Gm-Message-State: APjAAAWe/cWe3zUWvydHy/2P62Cfn3qynvu7DY90HOiLblM/uexmKd3J QWL3T7NnKpdi5evCFxVHdw== X-Google-Smtp-Source: APXvYqxIaPM1ZctBVkE7Ro+TREsdfryfxj/i6ycz7pQXyKH4dMgOCuTtzqB60i7vAR6HLJ+lPzJGMg== X-Received: by 2002:aca:5246:: with SMTP id g67mr9201133oib.116.1566247038221; Mon, 19 Aug 2019 13:37:18 -0700 (PDT) Received: from serve.minyard.net ([47.184.134.43]) by smtp.gmail.com with ESMTPSA id n109sm5986812ota.36.2019.08.19.13.37.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2019 13:37:16 -0700 (PDT) Received: from t430.minyard.net (t430m.minyard.net [192.168.27.3]) by serve.minyard.net (Postfix) with ESMTPA id 8877B1805AE; Mon, 19 Aug 2019 20:37:14 +0000 (UTC) Received: by t430.minyard.net (Postfix, from userid 1000) id 70C8B30232A; Mon, 19 Aug 2019 15:37:14 -0500 (CDT) From: minyard@acm.org To: linux-watchdog@vger.kernel.org, Guenter Roeck , Wim Van Sebroeck Cc: Corey Minyard Subject: [PATCH 09/12] watchdog:ipmi: Implement action and preaction functions Date: Mon, 19 Aug 2019 15:37:08 -0500 Message-Id: <20190819203711.32599-10-minyard@acm.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190819203711.32599-1-minyard@acm.org> References: <20190819203711.32599-1-minyard@acm.org> Reply-To: "[PATCH 00/12]"@minyard.net, Convert@minyard.net, the@minyard.net, IPMI@minyard.net, watchdog@minyard.net, to@minyard.net, the@minyard.net, standard@minyard.net, interface@minyard.net Sender: linux-watchdog-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-watchdog@vger.kernel.org From: Corey Minyard This required cleanup of how actions and preactions were done. Setting the values through the sysfs interface still works, though the sysfs parameters it will not display changes that come in through the standard watchdog interface. Signed-off-by: Corey Minyard --- drivers/watchdog/ipmi_watchdog.c | 365 +++++++++++++++++++++---------- 1 file changed, 246 insertions(+), 119 deletions(-) diff --git a/drivers/watchdog/ipmi_watchdog.c b/drivers/watchdog/ipmi_watchdog.c index 218c01707c0b..77dbd3851fdc 100644 --- a/drivers/watchdog/ipmi_watchdog.c +++ b/drivers/watchdog/ipmi_watchdog.c @@ -80,12 +80,6 @@ #define WDOG_PRETIMEOUT_NMI 2 #define WDOG_PRETIMEOUT_MSG_INT 3 -/* Operations that can be performed on a pretimout. */ -#define WDOG_PREOP_NONE 0 -#define WDOG_PREOP_PANIC 1 -/* Cause data to be available to read. Doesn't work in NMI mode. */ -#define WDOG_PREOP_GIVE_DATA 2 - /* Actions to perform on a full timeout. */ #define WDOG_SET_TIMEOUT_ACT(byte, use) \ byte = ((byte) & 0xf8) | ((use) & 0x7) @@ -144,6 +138,7 @@ struct ipmi_wdt { struct ipmi_recv_msg recv_msg; int state; /* One of WDOG_TIMEOUT_xxx, NONE if disabled. */ + int prestate; /* One of WDOG_PRETIMEOUT_xxx. */ bool nmi_works; @@ -164,6 +159,8 @@ static int ipmi_set_timeout(struct ipmi_wdt *iwd, int timeout, int do_heartbeat); static int ipmi_set_pretimeout(struct ipmi_wdt *iwd, int timeout, int do_heartbeat); +static int ipmi_set_action(struct ipmi_wdt *iwd, unsigned int action); +static int ipmi_set_preaction(struct ipmi_wdt *iwd, unsigned int action); static void ipmi_register_watchdog(int ipmi_intf, struct device *dev); static void _ipmi_unregister_watchdog(struct ipmi_wdt *iwd); static void ipmi_unregister_watchdog(int ipmi_intf); @@ -189,18 +186,14 @@ static int timeout = 10; /* Default timeout. */ static int pretimeout; /* Default pretimeout. */ static int panic_wdt_timeout = 255; /* Default timeout to set on panic */ -/* Default action is to reset the board on a timeout. */ -static unsigned char action_val = WDOG_TIMEOUT_RESET; - -static char action[16] = "reset"; - -static unsigned char preaction_val = WDOG_PRETIMEOUT_NONE; - -static char preaction[16] = "pre_none"; - -static unsigned char preop_val = WDOG_PREOP_NONE; +#define WDOG_PREOP_NONE 0 +#define WDOG_PREOP_PANIC 1 +#define WDOG_PREOP_GIVE_DATA 2 -static char preop[16] = "preop_none"; +/* Default action is to reset the board on a timeout. */ +static unsigned char def_action_val = WDIOA_RESET; +static unsigned char def_preaction_val = WDIOP_NONE; +static unsigned char def_preop_val = WDOG_PREOP_NONE; /* * If true, the driver will start running as soon as it is configured @@ -211,33 +204,41 @@ static int start_now; static int action_op(const char *inval, char *outval) { int rv = 0; + unsigned int new_val; mutex_lock(&ipmi_wdt_data_mutex); - if (outval) - strcpy(outval, action); + if (outval) { + switch (def_action_val) { + case WDIOA_RESET: + strcpy(outval, "reset"); + break; + case WDIOA_POWER_OFF: + strcpy(outval, "power_off"); + break; + case WDIOA_POWER_CYCLE: + strcpy(outval, "power_cycle"); + break; + default: + strcpy(outval, "?"); + break; + } + } if (!inval) goto out_unlock; if (strcmp(inval, "reset") == 0) - action_val = WDOG_TIMEOUT_RESET; - else if (strcmp(inval, "none") == 0) - action_val = WDOG_TIMEOUT_NONE; + new_val = WDIOA_RESET; else if (strcmp(inval, "power_cycle") == 0) - action_val = WDOG_TIMEOUT_POWER_CYCLE; + new_val = WDIOA_POWER_CYCLE; else if (strcmp(inval, "power_off") == 0) - action_val = WDOG_TIMEOUT_POWER_DOWN; + new_val = WDIOA_POWER_OFF; else rv = -EINVAL; - if (!rv) { - strcpy(action, inval); - if (ipmi_wdt && ipmi_wdt->state != WDOG_TIMEOUT_NONE) { - ipmi_wdt->state = action_val; - rv = _ipmi_update_timeout(ipmi_wdt, - IPMI_SET_TIMEOUT_HB_IF_NECESSARY, - false); - } - } + if (!rv && ipmi_wdt) + rv = ipmi_set_action(ipmi_wdt, new_val); + if (!rv) + def_action_val = new_val; out_unlock: mutex_unlock(&ipmi_wdt_data_mutex); @@ -247,84 +248,115 @@ static int action_op(const char *inval, char *outval) static int preaction_op(const char *inval, char *outval) { int rv = 0; + unsigned int new_val; mutex_lock(&ipmi_wdt_data_mutex); - if (outval) - strcpy(outval, preaction); + if (outval) { + switch (def_preaction_val) { + case WDIOP_NONE: + strcpy(outval, "pre_none"); + break; + case WDIOP_NMI: + strcpy(outval, "pre_nmi"); + break; + case WDIOP_SMI: + strcpy(outval, "pre_smi"); + break; + case WDIOP_INTERRUPT: + strcpy(outval, "pre_int"); + break; + default: + strcpy(outval, "?"); + break; + } + } if (!inval) goto out_unlock; - if (strcmp(inval, "pre_none") == 0) - preaction_val = WDOG_PRETIMEOUT_NONE; - else if (strcmp(inval, "pre_smi") == 0) - preaction_val = WDOG_PRETIMEOUT_SMI; + if (strcmp(inval, "pre_none") == 0) { + new_val = WDIOP_NONE; + } else if (strcmp(inval, "pre_smi") == 0) { + new_val = WDIOP_SMI; #ifdef HAVE_DIE_NMI - else if (strcmp(inval, "pre_nmi") == 0) { - if (preop_val == WDOG_PREOP_GIVE_DATA) - rv = -EINVAL; - else if (ipmi_wdt && !ipmi_wdt->nmi_works) - rv = -EINVAL; - else - preaction_val = WDOG_PRETIMEOUT_NMI; - } + } else if (strcmp(inval, "pre_nmi") == 0) { + new_val = WDIOP_NMI; #endif - else if (strcmp(inval, "pre_int") == 0) - preaction_val = WDOG_PRETIMEOUT_MSG_INT; - else + } else if (strcmp(inval, "pre_int") == 0) { + new_val = WDIOP_INTERRUPT; + } else { rv = -EINVAL; - if (!rv) { - strcpy(preaction, inval); - if (ipmi_wdt && ipmi_wdt->state != WDOG_TIMEOUT_NONE && - ipmi_wdt->wdd.pretimeout) { - rv = _ipmi_update_timeout(ipmi_wdt, - IPMI_SET_TIMEOUT_HB_IF_NECESSARY, - false); - } } + if (!rv && ipmi_wdt) + rv = ipmi_set_preaction(ipmi_wdt, new_val); + if (!rv) + def_preaction_val = new_val; out_unlock: mutex_unlock(&ipmi_wdt_data_mutex); return rv; } +static const char *preop_to_governor(unsigned int preop) +{ + switch (preop) { + case WDOG_PREOP_NONE: + return "noop"; + case WDOG_PREOP_PANIC: + return "panic"; + case WDOG_PREOP_GIVE_DATA: + return "read_data"; + default: + return NULL; + } +} static int preop_op(const char *inval, char *outval) { int rv = 0; const char *gov = NULL; - unsigned int orig_val; + unsigned int new_val; mutex_lock(&ipmi_wdt_data_mutex); - if (outval) - strcpy(outval, preop); + if (outval) { + switch (def_preop_val) { + case WDOG_PREOP_NONE: + strcpy(outval, "preop_none"); + break; + case WDOG_PREOP_PANIC: + strcpy(outval, "preop_panic"); + break; + case WDOG_PREOP_GIVE_DATA: + strcpy(outval, "preop_give_data"); + break; + default: + strcpy(outval, "?"); + break; + } + } if (!inval) goto out_unlock; - orig_val = preop_val; if (strcmp(inval, "preop_none") == 0) { - preop_val = WDOG_PREOP_NONE; - gov = "noop"; + new_val = WDOG_PREOP_NONE; } else if (strcmp(inval, "preop_panic") == 0) { - preop_val = WDOG_PREOP_PANIC; - gov = "panic"; + new_val = WDOG_PREOP_PANIC; } else if (strcmp(inval, "preop_give_data") == 0) { - if (preaction_val == WDOG_PRETIMEOUT_NMI) - rv = -EINVAL; - else { - preop_val = WDOG_PREOP_GIVE_DATA; - gov = "read_data"; - } + new_val = WDOG_PREOP_GIVE_DATA; } else { rv = -EINVAL; } - if (!rv) { - rv = watchdog_pretimeout_governor_set(&ipmi_wdt->wdd, gov); - if (rv) - preaction_val = orig_val; - else - strcpy(preop, inval); + if (!rv && ipmi_wdt) { + gov = preop_to_governor(new_val); + if (!gov) + rv = -EINVAL; + if (!rv) { + rv = watchdog_pretimeout_governor_set(&ipmi_wdt->wdd, + gov); + } } + if (!rv) + def_preop_val = new_val; out_unlock: mutex_unlock(&ipmi_wdt_data_mutex); @@ -551,7 +583,7 @@ static int __ipmi_update_timeout(struct ipmi_wdt *iwd, int *send_heartbeat_now) WDOG_SET_TIMEOUT_ACT(data[1], iwd->state); if ((iwd->wdd.pretimeout > 0) && (iwd->state != WDOG_TIMEOUT_NONE)) { - WDOG_SET_PRETIMEOUT_ACT(data[1], preaction_val); + WDOG_SET_PRETIMEOUT_ACT(data[1], iwd->prestate); data[2] = iwd->wdd.pretimeout; } else { WDOG_SET_PRETIMEOUT_ACT(data[1], WDOG_PRETIMEOUT_NONE); @@ -863,19 +895,36 @@ static void ipmi_wdog_panic_handler(void *user_data) } } -static int ipmi_wdt_start(struct watchdog_device *wdd) +static int _ipmi_wdt_start(struct ipmi_wdt *iwd) { - struct ipmi_wdt *iwd = wdd_to_ipmi_wdt(wdd); - int rv; + int rv = 0; - mutex_lock(&iwd->lock); - iwd->state = action_val; - rv = _ipmi_update_timeout(iwd, IPMI_SET_TIMEOUT_FORCE_HB, false); - mutex_unlock(&iwd->lock); + switch (iwd->wdd.action) { + case WDIOA_RESET: + iwd->state = WDOG_TIMEOUT_RESET; + break; + case WDIOA_POWER_OFF: + iwd->state = WDOG_TIMEOUT_RESET; + break; + case WDIOA_POWER_CYCLE: + iwd->state = WDOG_TIMEOUT_RESET; + break; + default: + rv = -EINVAL; + break; + } + if (!rv) + rv = _ipmi_update_timeout(iwd, IPMI_SET_TIMEOUT_FORCE_HB, + false); return rv; } +static int ipmi_wdt_start(struct watchdog_device *wdd) +{ + return _ipmi_wdt_start(wdd_to_ipmi_wdt(wdd)); +} + static int ipmi_wdt_stop(struct watchdog_device *wdd) { struct ipmi_wdt *iwd = wdd_to_ipmi_wdt(wdd); @@ -903,25 +952,101 @@ static int ipmi_wdt_ping(struct watchdog_device *wdd) static int ipmi_wdt_set_timeout(struct watchdog_device *wdd, unsigned int val) { - struct ipmi_wdt *iwd = wdd_to_ipmi_wdt(wdd); - - return ipmi_set_timeout(iwd, val, IPMI_SET_TIMEOUT_HB_IF_NECESSARY); + return ipmi_set_timeout(wdd_to_ipmi_wdt(wdd), val, + IPMI_SET_TIMEOUT_HB_IF_NECESSARY); } static int ipmi_wdt_set_pretimeout(struct watchdog_device *wdd, unsigned int val) { struct ipmi_wdt *iwd = wdd_to_ipmi_wdt(wdd); - int rv; + + return ipmi_set_pretimeout(iwd, val, IPMI_SET_TIMEOUT_HB_IF_NECESSARY); +} + +static int ipmi_set_action(struct ipmi_wdt *iwd, unsigned int val) +{ + int rv = 0; mutex_lock(&iwd->lock); - iwd->wdd.pretimeout = val; - rv = _ipmi_update_timeout(iwd, IPMI_SET_TIMEOUT_NO_HB, false); + if (val == iwd->wdd.action) + goto out_unlock; + switch (val) { + case WDIOA_RESET: + case WDIOA_POWER_OFF: + case WDIOA_POWER_CYCLE: + iwd->wdd.action = val; + break; + default: + rv = -EINVAL; + } + if (!rv) + rv = _ipmi_wdt_start(iwd); +out_unlock: mutex_unlock(&iwd->lock); return rv; } +static int ipmi_wdt_set_action(struct watchdog_device *wdd, + unsigned int val) +{ + return ipmi_set_action(wdd_to_ipmi_wdt(wdd), val); +} + +static int ipmi_set_preaction(struct ipmi_wdt *iwd, unsigned int val) +{ + int rv = 0; + int new_state = 0; + + mutex_lock(&iwd->lock); + if (val == iwd->wdd.preaction) + goto out_unlock; + switch (val) { + case WDIOP_NONE: + new_state = WDOG_PRETIMEOUT_NONE; + break; + case WDIOP_SMI: + new_state = WDOG_PRETIMEOUT_SMI; + break; + case WDIOP_INTERRUPT: + new_state = WDOG_PRETIMEOUT_MSG_INT; + break; +#ifdef HAVE_DIE_NMI + case WDIOP_NMI: + if (!iwd->nmi_works) + rv = -EINVAL; + else + new_state = WDOG_PRETIMEOUT_NMI; + break; +#endif + default: + rv = -EINVAL; + } + if (!rv) { + int old_preaction = iwd->wdd.preaction; + int old_prestate = iwd->prestate; + + iwd->wdd.preaction = val; + iwd->prestate = new_state; + rv = _ipmi_wdt_start(iwd); + if (rv) { + iwd->wdd.preaction = old_preaction; + iwd->prestate = old_prestate; + } + } +out_unlock: + mutex_unlock(&iwd->lock); + + return rv; +} + +static int ipmi_wdt_set_preaction(struct watchdog_device *wdd, + unsigned int val) +{ + return ipmi_set_preaction(wdd_to_ipmi_wdt(wdd), val); +} + static const struct watchdog_info ipmi_wdt_ident = { .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_PRETIMEOUT | WDIOF_MAGICCLOSE, @@ -936,7 +1061,9 @@ static const struct watchdog_ops ipmi_wdt_ops = { .ping = ipmi_wdt_ping, .set_timeout = ipmi_wdt_set_timeout, .set_pretimeout = ipmi_wdt_set_pretimeout, - .get_timeleft = ipmi_wdt_get_timeleft, + .get_timeleft = ipmi_wdt_get_timeleft, + .set_action = ipmi_wdt_set_action, + .set_preaction = ipmi_wdt_set_preaction, }; static const struct ipmi_user_hndl ipmi_hndlrs = { @@ -976,14 +1103,14 @@ ipmi_nmi(unsigned int val, struct pt_regs *regs) if (iwd->state == WDOG_TIMEOUT_NONE) return NMI_DONE; - if (preaction_val != WDOG_PRETIMEOUT_NMI) + if (iwd->wdd.preaction != WDIOP_NMI) return NMI_DONE; /* * If no one else handled the NMI, we assume it was the IPMI * watchdog. */ - if (preop_val == WDOG_PREOP_PANIC) { + if (iwd->wdd.gov && strcmp(iwd->wdd.gov->name, "panic") == 0) { /* * On some machines, the heartbeat will give an error * and not work unless we re-enable the timer. So @@ -1000,6 +1127,7 @@ ipmi_nmi(unsigned int val, struct pt_regs *regs) static void nmi_check(struct ipmi_wdt *iwd) { int old_state = iwd->state; + int old_prestate = iwd->prestate; int old_pretimeout = iwd->wdd.pretimeout; int old_timeout = iwd->wdd.timeout; int rv; @@ -1026,6 +1154,7 @@ static void nmi_check(struct ipmi_wdt *iwd) * ourselves 99 seconds to stop the timer. */ iwd->state = WDOG_TIMEOUT_RESET; + iwd->prestate = WDOG_PRETIMEOUT_NMI; iwd->wdd.pretimeout = 99; iwd->wdd.timeout = 100; @@ -1063,6 +1192,7 @@ static void nmi_check(struct ipmi_wdt *iwd) mutex_unlock(&ipmi_nmi_test_mutex); iwd->wdd.pretimeout = old_pretimeout; iwd->wdd.timeout = old_timeout; + iwd->prestate = old_prestate; iwd->state = old_state; rv = _ipmi_update_timeout(iwd, IPMI_SET_TIMEOUT_FORCE_HB, false); if (rv) @@ -1079,6 +1209,7 @@ static void ipmi_register_watchdog(int ipmi_intf, struct device *dev) { struct ipmi_wdt *iwd = NULL; int rv = -EBUSY; + const char *gov; mutex_lock(&ipmi_wdt_data_mutex); if ((ifnum_to_use != -1) && (ifnum_to_use != ipmi_intf)) @@ -1126,6 +1257,8 @@ static void ipmi_register_watchdog(int ipmi_intf, struct device *dev) watchdog_init_timeout(&iwd->wdd, timeout, NULL); iwd->wdd.pretimeout = pretimeout; iwd->wdd.parent = dev; + iwd->wdd.action = def_action_val; + iwd->wdd.preaction = def_preaction_val; rv = watchdog_register_device(&iwd->wdd); if (rv) { @@ -1136,24 +1269,34 @@ static void ipmi_register_watchdog(int ipmi_intf, struct device *dev) nmi_check(iwd); - if (preaction_val == WDOG_PRETIMEOUT_NMI && !iwd->nmi_works) { + if (iwd->wdd.preaction == WDIOP_NMI && !iwd->nmi_works) { pr_warn("NMI pretimeout test failed, disabling pretimeout.\n"); - preaction_val = WDOG_PRETIMEOUT_NONE; + iwd->wdd.preaction = WDIOP_NONE; + } + + gov = preop_to_governor(def_preop_val); + if (!gov) { + pr_warn("Invalid preop value: %d\n", def_preop_val); + } else { + rv = watchdog_pretimeout_governor_set(&iwd->wdd, gov); + if (rv) + pr_warn("Setting governor to %s failed: %d\n", + gov, rv); } ipmi_wdt = iwd; + rv = 0; out: mutex_unlock(&ipmi_wdt_data_mutex); if (start_now && rv == 0) { /* Run from startup, so start the timer now. */ start_now = 0; /* Disable this function after first startup. */ - iwd->state = action_val; - mutex_lock(&iwd->lock); - _ipmi_update_timeout(iwd, IPMI_SET_TIMEOUT_FORCE_HB, false); - mutex_unlock(&iwd->lock); - pr_info("Starting now!\n"); - } else if (iwd && rv != 0) { + if (_ipmi_wdt_start(iwd)) + pr_warn("Starting now failed!\n"); + else + pr_info("Starting now!\n"); + } else if (iwd && rv) { kfree(iwd); } } @@ -1236,22 +1379,6 @@ static int __init ipmi_wdog_init(void) { int rv; - if (action_op(action, NULL)) { - pr_info("Unknown action '%s', defaulting to reset\n", action); - action_op("reset", NULL); - } - - if (preaction_op(preaction, NULL)) { - pr_info("Unknown preaction '%s', defaulting to none\n", - preaction); - preaction_op("pre_none", NULL); - } - - if (preop_op(preop, NULL)) { - pr_info("Unknown preop '%s', defaulting to none\n", preop); - preop_op("preop_none", NULL); - } - register_reboot_notifier(&wdog_reboot_notifier); rv = ipmi_smi_watcher_register(&smi_watcher); From patchwork Mon Aug 19 20:37:09 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Corey Minyard X-Patchwork-Id: 11101897 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 1EB72184E for ; Mon, 19 Aug 2019 20:37:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id E8C352087E for ; Mon, 19 Aug 2019 20:37:18 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="dArIVsOL" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728283AbfHSUhS (ORCPT ); Mon, 19 Aug 2019 16:37:18 -0400 Received: from mail-oi1-f194.google.com ([209.85.167.194]:42272 "EHLO mail-oi1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728177AbfHSUhS (ORCPT ); Mon, 19 Aug 2019 16:37:18 -0400 Received: by mail-oi1-f194.google.com with SMTP id o6so2360004oic.9 for ; Mon, 19 Aug 2019 13:37:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :reply-to; bh=bNjK9CgUTygymcC7/Qv0YzgosH+HuLFu57Lex5Wnf74=; b=dArIVsOLVIMeZMqhdqGD551axauQHnPd9UFAL4OtEaN3JKqxBUYCLkeNG6LG0M7X5v J7m2fzdZiFHlXzOLIZdWGrciXc4CI6VjPwXMXw684bYb5iv0pLqjObjPf8gRyDV4w6j2 ZpuAwcq6dmV7mYIgD/AWyiHbjwSZpUZFJ/8aaR/866nfnajSpy6RYXAQttLVX2pq6kzN 7EE/PnHV/4c17QNZLiSRKWJ5K4J3W6lidTLy9NlVfwEmLtvXxjIYEIGY/DDDC8opwiFK CRLgzKgJsF+346EVTvqxi/hlsGdWoe86VBSa4wGW2uAg3VxqjSgpPHZwB1Oi2pUazjSN POiQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references:reply-to; bh=bNjK9CgUTygymcC7/Qv0YzgosH+HuLFu57Lex5Wnf74=; b=A+lNanoZqGVYtZAasj43vYptWrKzgZwXUChTPJ6tNUkvAuIbkNUzIFMfX4BFMDjTfw UCJnSr8RmA3MdzyMne0iRxGIa1hsJKuArH96LPxDVPOwKkp3o07x5wHHessvU9waSH7l TsoM4RcVAYepahJLZx1VSjJ1YkuK3i62pJSrypk8q4Vnyrwmi66i47K3KgNcW7ZeHoDK bli0iYDfAO+2LLPUAd9JFLhpYJ9OeCetqZ8XscSRo5Rq67SxlAZ0uzZXpLrR947f+f93 7wLt/Zzd+eRvr5006+Rv8CvtaWaI1AHKXX49nfsoEF/t3JDO6vToOjBaNz8m76Xy7kqY 0ecw== X-Gm-Message-State: APjAAAV7m1/U7kQejcm+bIrM3WKP4bgZtvWcBxLpxxPnri+gvXHvcgsy Vga0qmLbTzvoxmB+t3AAqQ== X-Google-Smtp-Source: APXvYqztLxM+wsohom0p3+yd+4cTvcdvU0ypJ3sfqP9nle4DUGjBPIpY2QAoGVDsZh617xwfPbPocw== X-Received: by 2002:aca:4846:: with SMTP id v67mr15087302oia.112.1566247037441; Mon, 19 Aug 2019 13:37:17 -0700 (PDT) Received: from serve.minyard.net ([47.184.134.43]) by smtp.gmail.com with ESMTPSA id u5sm4437336oic.45.2019.08.19.13.37.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2019 13:37:16 -0700 (PDT) Received: from t430.minyard.net (t430m.minyard.net [192.168.27.3]) by serve.minyard.net (Postfix) with ESMTPA id A2FC21805B1; Mon, 19 Aug 2019 20:37:14 +0000 (UTC) Received: by t430.minyard.net (Postfix, from userid 1000) id 814AA301220; Mon, 19 Aug 2019 15:37:14 -0500 (CDT) From: minyard@acm.org To: linux-watchdog@vger.kernel.org, Guenter Roeck , Wim Van Sebroeck Cc: Corey Minyard Subject: [PATCH 10/12] watchdog: Add a way to set the governor through the ioctl Date: Mon, 19 Aug 2019 15:37:09 -0500 Message-Id: <20190819203711.32599-11-minyard@acm.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190819203711.32599-1-minyard@acm.org> References: <20190819203711.32599-1-minyard@acm.org> Reply-To: "[PATCH 00/12]"@minyard.net, Convert@minyard.net, the@minyard.net, IPMI@minyard.net, watchdog@minyard.net, to@minyard.net, the@minyard.net, standard@minyard.net, interface@minyard.net Sender: linux-watchdog-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-watchdog@vger.kernel.org From: Corey Minyard The watchdog governor could only be set through the systfs interface before, make that function available through the ioctl, too. Signed-off-by: Corey Minyard --- Documentation/watchdog/watchdog-api.rst | 31 +++++++++++++++++++++++++ drivers/watchdog/watchdog_dev.c | 14 +++++++++++ drivers/watchdog/watchdog_pretimeout.h | 2 +- include/uapi/linux/watchdog.h | 8 +++++++ 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/Documentation/watchdog/watchdog-api.rst b/Documentation/watchdog/watchdog-api.rst index 927be9e56b5d..a2a3057e2b5d 100644 --- a/Documentation/watchdog/watchdog-api.rst +++ b/Documentation/watchdog/watchdog-api.rst @@ -177,6 +177,37 @@ and queried:: Note that the pretimeout governor that reads data is not compatible with the NMI preaction. The NMI preaction can only do nothing or panic. +Pretimeout Governors +==================== + +When a pretimeout occurs and the pretimeout framework is compiled in +to the kernel, the pretimeout framework will generally be called. +(The exception is that NMI pretimeouts do not call the pretimeout +framework because they need special handling.) Several pretimeout +governers can be registered:: + + noop - Don't do anything on a pretimeout + panic - Issue a panic when a pretimeout occurs. This is generally the + default + read_data - Provide one byte of data on the read interface to the + watchdog timer. This way a userland program can handle + the pretimeout. + +If the CONFING_WATCHDOG_SYSFS is enabled, the pretimeout governor can +be set by writing the value to the +/sys/class/watchdog/watchdog/pretimeout_governor sysfs file. + +The pretimeout governor can also be set through the ioctl interface with:: + + char governor[WATCHDOG_GOV_NAME_MAXLEN] = "panic"; + ioctl(fd, WDIOC_SETPREGOV, gov); + +and can be queried with:: + + char governor[WATCHDOG_GOV_NAME_MAXLEN]; + ioctl(fd, WDIOC_GETPREGOV, gov); + printf("The governor is %s\n", gov); + Get the number of seconds before reboot ======================================= diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 0e70f510a491..7e1ad6e303d0 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -762,6 +762,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, int __user *p = argp; unsigned int val; int err; + char gov[WATCHDOG_GOV_NAME_MAXLEN]; mutex_lock(&wd_data->lock); @@ -866,6 +867,19 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, case WDIOC_GETPREACTION: err = put_user(wdd->preaction, p); break; + case WDIOC_SETPREGOV: + err = strncpy_from_user(gov, argp, sizeof(gov)); + if (err >= 0) + err = watchdog_pretimeout_governor_set(wdd, gov); + break; + case WDIOC_GETPREGOV: + err = 0; + val = watchdog_pretimeout_governor_get(wdd, gov); + if (val == 0) + err = -ENOENT; + if (copy_to_user(argp, gov, val + 1)) + err = -EFAULT; + break; default: err = -ENOTTY; break; diff --git a/drivers/watchdog/watchdog_pretimeout.h b/drivers/watchdog/watchdog_pretimeout.h index 819517ed0138..e6e191b787bb 100644 --- a/drivers/watchdog/watchdog_pretimeout.h +++ b/drivers/watchdog/watchdog_pretimeout.h @@ -2,7 +2,7 @@ #ifndef __WATCHDOG_PRETIMEOUT_H #define __WATCHDOG_PRETIMEOUT_H -#define WATCHDOG_GOV_NAME_MAXLEN 20 +#include struct watchdog_device; diff --git a/include/uapi/linux/watchdog.h b/include/uapi/linux/watchdog.h index bf13cf25f9e0..2acbfff6db8c 100644 --- a/include/uapi/linux/watchdog.h +++ b/include/uapi/linux/watchdog.h @@ -36,10 +36,18 @@ struct watchdog_info { #define WDIOC_GETACTION _IOR(WATCHDOG_IOCTL_BASE, 12, int) #define WDIOC_SETPREACTION _IOWR(WATCHDOG_IOCTL_BASE, 13, int) #define WDIOC_GETPREACTION _IOR(WATCHDOG_IOCTL_BASE, 14, int) +#define WDIOC_SETPREGOV _IOWR(WATCHDOG_IOCTL_BASE, 15, char) +#define WDIOC_GETPREGOV _IOR(WATCHDOG_IOCTL_BASE, 16, char) #define WDIOF_UNKNOWN -1 /* Unknown flag error */ #define WDIOS_UNKNOWN -1 /* Unknown status error */ +/* + * Buffer for WDIOC_GETPREGOV must be at least this big. WDIOC_SETPRGOV + * will take at max this many bytes - 1, excess will be ignored. + */ +#define WATCHDOG_GOV_NAME_MAXLEN 20 + #define WDIOF_OVERHEAT 0x0001 /* Reset due to CPU overheat */ #define WDIOF_FANFAULT 0x0002 /* Fan failed */ #define WDIOF_EXTERN1 0x0004 /* External relay 1 */ From patchwork Mon Aug 19 20:37:10 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Corey Minyard X-Patchwork-Id: 11101907 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id EFD9C912 for ; Mon, 19 Aug 2019 20:37:21 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id B1FB722CF6 for ; Mon, 19 Aug 2019 20:37:21 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="A1zpjWek" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728316AbfHSUhV (ORCPT ); Mon, 19 Aug 2019 16:37:21 -0400 Received: from mail-ot1-f68.google.com ([209.85.210.68]:40463 "EHLO mail-ot1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727769AbfHSUhV (ORCPT ); Mon, 19 Aug 2019 16:37:21 -0400 Received: by mail-ot1-f68.google.com with SMTP id c34so2935601otb.7 for ; Mon, 19 Aug 2019 13:37:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :reply-to; bh=ZlcXj4LVdOZ6/0lWZvHtbVhmxLxjhBL626/F+QKo5cw=; b=A1zpjWekoW+5lF9y4IDxk1vcqwNEa4pZslkgpPEcruR8+Us2rihWdgtqOcA2f2wPXR eSswaeQhY6IHxlPZieQo+N/jrlYEsAlCueRZdrCE5nfuWls9mB5JgtE0qrAYparg6ZML IGjs77DVyAwOyx71GbhUr9DGtTEwF5waD/5Uv1E27CRIXmRHcNRHrSYMR9NQLEdst1qt emwHsQ+UooqnbQHIsYjeBGwibJvqUul3YwCiObyqtXC60k+oIskkWa8QA/RVjsWS4Ty9 6kB+wlC9NNEJgZSjAhOZDnH8twClXstipwE5N9k9y6qJZCMZxN+wmQkFxPv+oqq7ceoZ jdIw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references:reply-to; bh=ZlcXj4LVdOZ6/0lWZvHtbVhmxLxjhBL626/F+QKo5cw=; b=uEmQ/fo++XRD5+hVvd1ZgA91F45Z9GNrXoLjcxSw9X52cfqfvklEI5/pjRjrug4BP5 RD3N7MZ0h4hinjNfCHbYc+5vxZqjRpssA2RzzQXvHnF0C+p08WJEu8XXr5X5+ehDzLFd FkvDisAlh83XdCxYtmlOX22LTp7snBZ+W2CGLJOSWhHLsTA5JGshnXA1nc2CXI+WLu9i NrNeQci4xGQ7ymvprSbWZiT9CWdiIMOhDrAbn8eHrUf2VYZAFW60O9dD27UunlV/nFeF LveRT53vipB4GtKD1BWTCxUHXFIv1r2c8ZBkUwcNMZMFlrsv7MDO8lNJq5aRDmJVFOD7 9f6w== X-Gm-Message-State: APjAAAW13M/jvhPP3wWEo/KQY/7QTlWk/uvXrS9emlVA9R2Uig5aoA6q pMFie49r2xRlzTjYCzPWyw== X-Google-Smtp-Source: APXvYqxsQe+mrqy9JYVwFAMBahQhb/61GsAu4uqX1VhJNrXMNDtb9f3CPxVvP9PCr65pyf3ZjQjKkA== X-Received: by 2002:a9d:6657:: with SMTP id q23mr2265052otm.233.1566247039103; Mon, 19 Aug 2019 13:37:19 -0700 (PDT) Received: from serve.minyard.net ([47.184.134.43]) by smtp.gmail.com with ESMTPSA id r125sm4626724oif.3.2019.08.19.13.37.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2019 13:37:16 -0700 (PDT) Received: from t430.minyard.net (t430m.minyard.net [192.168.27.3]) by serve.minyard.net (Postfix) with ESMTPA id A11161805AF; Mon, 19 Aug 2019 20:37:14 +0000 (UTC) Received: by t430.minyard.net (Postfix, from userid 1000) id 91C08301176; Mon, 19 Aug 2019 15:37:14 -0500 (CDT) From: minyard@acm.org To: linux-watchdog@vger.kernel.org, Guenter Roeck , Wim Van Sebroeck Cc: Corey Minyard Subject: [PATCH 11/12] watchdog: Add a sample program that can fully use the watchdog interface Date: Mon, 19 Aug 2019 15:37:10 -0500 Message-Id: <20190819203711.32599-12-minyard@acm.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190819203711.32599-1-minyard@acm.org> References: <20190819203711.32599-1-minyard@acm.org> Reply-To: "[PATCH 00/12]"@minyard.net, Convert@minyard.net, the@minyard.net, IPMI@minyard.net, watchdog@minyard.net, to@minyard.net, the@minyard.net, standard@minyard.net, interface@minyard.net Sender: linux-watchdog-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-watchdog@vger.kernel.org From: Corey Minyard This is useful for testing. Signed-off-by: Corey Minyard --- samples/watchdog/Makefile | 2 +- samples/watchdog/watchdog-set.c | 580 ++++++++++++++++++++++++++++++++ 2 files changed, 581 insertions(+), 1 deletion(-) create mode 100644 samples/watchdog/watchdog-set.c diff --git a/samples/watchdog/Makefile b/samples/watchdog/Makefile index a9430fa60253..3cf63106f3ef 100644 --- a/samples/watchdog/Makefile +++ b/samples/watchdog/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 CC := $(CROSS_COMPILE)gcc -PROGS := watchdog-simple +PROGS := watchdog-simple watchdog-set all: $(PROGS) diff --git a/samples/watchdog/watchdog-set.c b/samples/watchdog/watchdog-set.c new file mode 100644 index 000000000000..ba0457157a16 --- /dev/null +++ b/samples/watchdog/watchdog-set.c @@ -0,0 +1,580 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include + +/* In case we are compiling with older kernel include files. */ +#ifndef WDIOC_SETACTION +#define WDIOC_SETACTION _IOWR(WATCHDOG_IOCTL_BASE, 11, int) +#define WDIOC_GETACTION _IOR(WATCHDOG_IOCTL_BASE, 12, int) +#define WDIOC_SETPREACTION _IOWR(WATCHDOG_IOCTL_BASE, 13, int) +#define WDIOC_GETPREACTION _IOR(WATCHDOG_IOCTL_BASE, 14, int) +#define WDIOC_SETPREGOV _IOWR(WATCHDOG_IOCTL_BASE, 15, char) +#define WDIOC_GETPREGOV _IOR(WATCHDOG_IOCTL_BASE, 16, char) + +/* + * Buffer for WDIOC_GETPREGOV must be at least this big. WDIOC_SETPRGOV + * will take at max this many bytes - 1, excess will be ignored. + */ +#define WATCHDOG_GOV_NAME_MAXLEN 20 + +/* Actions for WDIOC_xxxACTION ioctls. */ +#define WDIOA_RESET 0 /* Reset the system. */ +#define WDIOA_POWER_OFF 1 /* Power off the system. */ +#define WDIOA_POWER_CYCLE 2 /* Power cycle the system. */ + +/* Actions for WDIOC_xxxPREACTION ioctls. */ +#define WDIOP_NONE 0 /* Do nothing. */ +#define WDIOP_NMI 1 /* Issue an NMI. */ +#define WDIOP_SMI 2 /* Issue a system management irq. */ +#define WDIOP_INTERRUPT 3 /* Issue a normal irq. */ +#endif + +struct bitflags { + int flag; + char *name; +}; + +static struct bitflags flags[] = { + { WDIOF_OVERHEAT, "overheat" }, + { WDIOF_FANFAULT, "fanfault" }, + { WDIOF_EXTERN1, "extern1" }, + { WDIOF_EXTERN2, "extern2" }, + { WDIOF_POWERUNDER, "powerunder" }, + { WDIOF_CARDRESET, "cardreset" }, + { WDIOF_POWEROVER, "powerover" }, + { WDIOF_SETTIMEOUT, "settimeout" }, + { WDIOF_MAGICCLOSE, "magicclose" }, + { WDIOF_PRETIMEOUT, "pretimeout" }, + { WDIOF_ALARMONLY, "alarmonly" }, + { WDIOF_KEEPALIVEPING, "keepaliveping" }, + { } +}; + +static struct bitflags options[] = { + { WDIOS_DISABLECARD, "disablecard" }, + { WDIOS_ENABLECARD, "enablecard" }, + { WDIOS_TEMPPANIC, "temppanic" }, + { } +}; + +struct actionvals { + int action; + char *name; +}; + +static struct actionvals actions[] = { + { WDIOA_RESET, "reset" }, + { WDIOA_POWER_OFF, "poweroff" }, + { WDIOA_POWER_CYCLE, "powercycle" }, + { } +}; + +static struct actionvals preactions[] = { + { WDIOP_NONE, "none" }, + { WDIOP_NMI, "nmi" }, + { WDIOP_SMI, "smi" }, + { WDIOP_INTERRUPT, "interrupt" }, + { } +}; + +static void print_bits(int bitmask, struct bitflags *flags) +{ + unsigned int i; + + for (i = 0; flags[i].name; i++) { + if (flags[i].flag & bitmask) { + bitmask &= ~flags[i].flag; + printf(" %s", flags[i].name); + } + } + i = 0; + while (bitmask) { + while (!(bitmask & (1 << i))) + i++; + printf(" bit(%d)", i); + bitmask &= ~(1 << i); + } +} + +static void print_action(int val, struct actionvals *actions) +{ + unsigned int i; + + for (i = 0; actions[i].name; i++) { + if (val == actions[i].action) { + printf("%s\n", actions[i].name); + return; + } + } + printf("%d\n", val); +} + +static int action_to_val(char *action, struct actionvals *actions) +{ + unsigned int i; + int val; + char *end; + + for (i = 0; actions[i].name; i++) { + if (strcmp(action, actions[i].name) == 0) + return actions[i].action; + } + + val = strtoul(action, &end, 0); + if (end == action || *end != '\0') + return -1; + + return val; +} + +static int status(int wdfd, int argc, char *argv[]) +{ + struct watchdog_info info; + int val; + int ret; + char gov[WATCHDOG_GOV_NAME_MAXLEN]; + + printf("info:"); + ret = ioctl(wdfd, WDIOC_GETSUPPORT, &info); + if (ret == -1) { + printf(" error:%s\n", strerror(errno)); + } else { + printf("\n options:"); + print_bits(info.options, flags); + printf("\n fwver: %d (0x%x)", info.firmware_version, + info.firmware_version); + printf("\n identity: %s\n", info.identity); + } + + printf("status:"); + ret = ioctl(wdfd, WDIOC_GETSTATUS, &val); + if (ret == -1) { + printf(" error:%s\n", strerror(errno)); + } else { + print_bits(val, flags); + printf("\n"); + } + + printf("bootstatus:"); + ret = ioctl(wdfd, WDIOC_GETBOOTSTATUS, &val); + if (ret == -1) { + printf(" error:%s\n", strerror(errno)); + } else { + print_bits(val, flags); + printf("\n"); + } + + ret = ioctl(wdfd, WDIOC_GETTEMP, &val); + if (ret != -1) /* Not usually implemented. */ + printf("temp: %d\n", val); + + ret = ioctl(wdfd, WDIOC_GETTIMEOUT, &val); + if (ret == -1) + printf("timeout: error:%s\n", strerror(errno)); + else + printf("timeout: %d\n", val); + + ret = ioctl(wdfd, WDIOC_GETPRETIMEOUT, &val); + if (ret == -1) + printf("pretimeout: error:%s\n", strerror(errno)); + else + printf("pretimeout: %d\n", val); + + ret = ioctl(wdfd, WDIOC_GETACTION, &val); + if (ret == -1) { + if (errno != ENOTTY) /* If not an older kernel. */ + printf("action: error:%s\n", strerror(errno)); + } else { + printf("action: "); + print_action(val, actions); + } + + ret = ioctl(wdfd, WDIOC_GETPREACTION, &val); + if (ret == -1) { + if (errno != ENOTTY) /* If not an older kernel. */ + printf("preaction: error:%s\n", strerror(errno)); + } else { + printf("preaction: "); + print_action(val, preactions); + } + + ret = ioctl(wdfd, WDIOC_GETPREGOV, gov); + if (ret == -1) { + if (errno != ENOTTY) /* If not an older kernel. */ + printf("governor: error:%s\n", strerror(errno)); + } else { + printf("governor: %s\n", gov); + } + + return 0; +} + +static int setoptions(int wdfd, int argc, char *argv[]) +{ + int ret, val = 0; + int i; + unsigned int j; + char *end; + + for (i = 0; i < argc; ) { + for (j = 0; options[j].name; j++) { + if (strcmp(argv[i], options[j].name) == 0) { + val |= options[j].flag; + goto found; + } + } + val |= strtoul(argv[i], &end, 0); + if (end == argv[i] || *end != '\0') { + fprintf(stderr, "invalid option: '%s'\n", argv[i]); + return 1; + } +found: + i++; + } + + ret = ioctl(wdfd, WDIOC_SETOPTIONS, &val); + if (ret == -1) { + fprintf(stderr, "Set options error: %s\n", strerror(errno)); + return 1; + } + + return 0; +} + +static int ping(int wdfd, int argc, char *argv[]) +{ + int ret; + + ret = ioctl(wdfd, WDIOC_KEEPALIVE); + if (ret == -1) { + printf("ping error:%s\n", strerror(errno)); + return 1; + } + + return 0; +} + +static int settimeout(int wdfd, int argc, char *argv[]) +{ + int ret, val; + char *end; + + if (argc < 1) { + fprintf(stderr, "No value for timeout\n"); + return 1; + } + + val = strtoul(argv[0], &end, 0); + if (end == argv[0] || *end != '\0') { + fprintf(stderr, "Invalid number for timeout: '%s'\n", argv[0]); + return 1; + } + + ret = ioctl(wdfd, WDIOC_SETTIMEOUT, &val); + if (ret == -1) { + fprintf(stderr, "Set timeout error: %s\n", strerror(errno)); + return 1; + } + + return 0; +} + +static int gettimeout(int wdfd, int argc, char *argv[]) +{ + int ret, val; + + ret = ioctl(wdfd, WDIOC_GETTIMEOUT, &val); + if (ret == -1) { + fprintf(stderr, "Get timeout error: %s\n", strerror(errno)); + return 1; + } + + printf("%d\n", val); + return 0; +} + +static int setpretimeout(int wdfd, int argc, char *argv[]) +{ + int ret, val; + char *end; + + if (argc < 1) { + fprintf(stderr, "No value for pretimeout\n"); + return 1; + } + + val = strtoul(argv[0], &end, 0); + if (end == argv[0] || *end != '\0') { + fprintf(stderr, "Invalid number for pretimeout: '%s'\n", + argv[0]); + return 1; + } + + ret = ioctl(wdfd, WDIOC_SETPRETIMEOUT, &val); + if (ret == -1) { + fprintf(stderr, "Set pretimeout error: %s\n", strerror(errno)); + return 1; + } + + return 0; +} + +static int getpretimeout(int wdfd, int argc, char *argv[]) +{ + int ret, val; + + ret = ioctl(wdfd, WDIOC_GETPRETIMEOUT, &val); + if (ret == -1) { + fprintf(stderr, "Get pretimeout error: %s\n", strerror(errno)); + return 1; + } + + printf("%d\n", val); + return 0; +} + +static int gettimeleft(int wdfd, int argc, char *argv[]) +{ + int ret, val; + + ret = ioctl(wdfd, WDIOC_GETTIMELEFT, &val); + if (ret == -1) { + fprintf(stderr, "Get time left error: %s\n", strerror(errno)); + return 1; + } + + printf("%d\n", val); + return 0; +} + +static int setaction(int wdfd, int argc, char *argv[]) +{ + int val, ret; + + if (argc < 1) { + fprintf(stderr, "No value for action\n"); + return 1; + } + + val = action_to_val(argv[0], actions); + if (val == -1) { + fprintf(stderr, "Invalid action: '%s'\n", argv[0]); + return 1; + } + + ret = ioctl(wdfd, WDIOC_SETACTION, &val); + if (ret == -1) { + fprintf(stderr, "Set action error: %s\n", strerror(errno)); + return 1; + } + + return 0; +} + +static int getaction(int wdfd, int argc, char *argv[]) +{ + int val, ret; + + ret = ioctl(wdfd, WDIOC_GETACTION, &val); + if (ret == -1) { + fprintf(stderr, "Get action error: %s\n", strerror(errno)); + return 1; + } + + print_action(val, actions); + return 0; +} + +static int setpreaction(int wdfd, int argc, char *argv[]) +{ + int val, ret; + + if (argc < 1) { + fprintf(stderr, "No value for preaction\n"); + return 1; + } + + val = action_to_val(argv[0], preactions); + if (val == -1) { + fprintf(stderr, "Invalid preaction: '%s'\n", argv[0]); + return 1; + } + + ret = ioctl(wdfd, WDIOC_SETPREACTION, &val); + if (ret == -1) { + fprintf(stderr, "Set preaction error: %s\n", strerror(errno)); + return 1; + } + + return 0; +} + +static int getpreaction(int wdfd, int argc, char *argv[]) +{ + int val, ret; + + ret = ioctl(wdfd, WDIOC_GETPREACTION, &val); + if (ret == -1) { + fprintf(stderr, "Get preaction error: %s\n", strerror(errno)); + return 1; + } + + print_action(val, preactions); + return 0; +} + +static int setpregov(int wdfd, int argc, char *argv[]) +{ + int ret; + + if (argc < 1) { + fprintf(stderr, "No value for pretimeout governor\n"); + return 1; + } + + ret = ioctl(wdfd, WDIOC_SETPREGOV, argv[0]); + if (ret == -1) { + fprintf(stderr, "Set preaction governor error: %s\n", + strerror(errno)); + return 1; + } + + return 0; +} + +static int getpregov(int wdfd, int argc, char *argv[]) +{ + int ret; + char gov[WATCHDOG_GOV_NAME_MAXLEN]; + + ret = ioctl(wdfd, WDIOC_GETPREGOV, gov); + if (ret == -1) { + fprintf(stderr, "Get preaction governor error: %s\n", + strerror(errno)); + return 1; + } + + printf("%s\n", gov); + + return 0; +} + +static int waitdata(int wdfd, int argc, char *argv[]) +{ + char dummy; + int ret; + + ret = read(wdfd, &dummy, 1); + if (ret == -1) + perror("read"); + else + printf("Received data\n"); + return 0; +} + +static struct { + char *name; + int (*handler)(int wdfd, int argc, char *argv[]); + char *help; +} handlers[] = { + { "status", status, + "- print out the current watchdog status" }, + { "setoptions", setoptions, + "[