linux/drivers/watchdog/watchdog_pretimeout.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (C) 2015-2016 Mentor Graphics
   4 */
   5
   6#include <linux/list.h>
   7#include <linux/slab.h>
   8#include <linux/spinlock.h>
   9#include <linux/string.h>
  10#include <linux/watchdog.h>
  11
  12#include "watchdog_core.h"
  13#include "watchdog_pretimeout.h"
  14
  15/* Default watchdog pretimeout governor */
  16static struct watchdog_governor *default_gov;
  17
  18/* The spinlock protects default_gov, wdd->gov and pretimeout_list */
  19static DEFINE_SPINLOCK(pretimeout_lock);
  20
  21/* List of watchdog devices, which can generate a pretimeout event */
  22static LIST_HEAD(pretimeout_list);
  23
  24struct watchdog_pretimeout {
  25        struct watchdog_device          *wdd;
  26        struct list_head                entry;
  27};
  28
  29/* The mutex protects governor list and serializes external interfaces */
  30static DEFINE_MUTEX(governor_lock);
  31
  32/* List of the registered watchdog pretimeout governors */
  33static LIST_HEAD(governor_list);
  34
  35struct governor_priv {
  36        struct watchdog_governor        *gov;
  37        struct list_head                entry;
  38};
  39
  40static struct governor_priv *find_governor_by_name(const char *gov_name)
  41{
  42        struct governor_priv *priv;
  43
  44        list_for_each_entry(priv, &governor_list, entry)
  45                if (sysfs_streq(gov_name, priv->gov->name))
  46                        return priv;
  47
  48        return NULL;
  49}
  50
  51int watchdog_pretimeout_available_governors_get(char *buf)
  52{
  53        struct governor_priv *priv;
  54        int count = 0;
  55
  56        mutex_lock(&governor_lock);
  57
  58        list_for_each_entry(priv, &governor_list, entry)
  59                count += sysfs_emit_at(buf, count, "%s\n", priv->gov->name);
  60
  61        mutex_unlock(&governor_lock);
  62
  63        return count;
  64}
  65
  66int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf)
  67{
  68        int count = 0;
  69
  70        spin_lock_irq(&pretimeout_lock);
  71        if (wdd->gov)
  72                count = sysfs_emit(buf, "%s\n", wdd->gov->name);
  73        spin_unlock_irq(&pretimeout_lock);
  74
  75        return count;
  76}
  77
  78int watchdog_pretimeout_governor_set(struct watchdog_device *wdd,
  79                                     const char *buf)
  80{
  81        struct governor_priv *priv;
  82
  83        mutex_lock(&governor_lock);
  84
  85        priv = find_governor_by_name(buf);
  86        if (!priv) {
  87                mutex_unlock(&governor_lock);
  88                return -EINVAL;
  89        }
  90
  91        spin_lock_irq(&pretimeout_lock);
  92        wdd->gov = priv->gov;
  93        spin_unlock_irq(&pretimeout_lock);
  94
  95        mutex_unlock(&governor_lock);
  96
  97        return 0;
  98}
  99
 100void watchdog_notify_pretimeout(struct watchdog_device *wdd)
 101{
 102        unsigned long flags;
 103
 104        spin_lock_irqsave(&pretimeout_lock, flags);
 105        if (!wdd->gov) {
 106                spin_unlock_irqrestore(&pretimeout_lock, flags);
 107                return;
 108        }
 109
 110        wdd->gov->pretimeout(wdd);
 111        spin_unlock_irqrestore(&pretimeout_lock, flags);
 112}
 113EXPORT_SYMBOL_GPL(watchdog_notify_pretimeout);
 114
 115int watchdog_register_governor(struct watchdog_governor *gov)
 116{
 117        struct watchdog_pretimeout *p;
 118        struct governor_priv *priv;
 119
 120        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 121        if (!priv)
 122                return -ENOMEM;
 123
 124        mutex_lock(&governor_lock);
 125
 126        if (find_governor_by_name(gov->name)) {
 127                mutex_unlock(&governor_lock);
 128                kfree(priv);
 129                return -EBUSY;
 130        }
 131
 132        priv->gov = gov;
 133        list_add(&priv->entry, &governor_list);
 134
 135        if (!strncmp(gov->name, WATCHDOG_PRETIMEOUT_DEFAULT_GOV,
 136                     WATCHDOG_GOV_NAME_MAXLEN)) {
 137                spin_lock_irq(&pretimeout_lock);
 138                default_gov = gov;
 139
 140                list_for_each_entry(p, &pretimeout_list, entry)
 141                        if (!p->wdd->gov)
 142                                p->wdd->gov = default_gov;
 143                spin_unlock_irq(&pretimeout_lock);
 144        }
 145
 146        mutex_unlock(&governor_lock);
 147
 148        return 0;
 149}
 150EXPORT_SYMBOL(watchdog_register_governor);
 151
 152void watchdog_unregister_governor(struct watchdog_governor *gov)
 153{
 154        struct watchdog_pretimeout *p;
 155        struct governor_priv *priv, *t;
 156
 157        mutex_lock(&governor_lock);
 158
 159        list_for_each_entry_safe(priv, t, &governor_list, entry) {
 160                if (priv->gov == gov) {
 161                        list_del(&priv->entry);
 162                        kfree(priv);
 163                        break;
 164                }
 165        }
 166
 167        spin_lock_irq(&pretimeout_lock);
 168        list_for_each_entry(p, &pretimeout_list, entry)
 169                if (p->wdd->gov == gov)
 170                        p->wdd->gov = default_gov;
 171        spin_unlock_irq(&pretimeout_lock);
 172
 173        mutex_unlock(&governor_lock);
 174}
 175EXPORT_SYMBOL(watchdog_unregister_governor);
 176
 177int watchdog_register_pretimeout(struct watchdog_device *wdd)
 178{
 179        struct watchdog_pretimeout *p;
 180
 181        if (!watchdog_have_pretimeout(wdd))
 182                return 0;
 183
 184        p = kzalloc(sizeof(*p), GFP_KERNEL);
 185        if (!p)
 186                return -ENOMEM;
 187
 188        spin_lock_irq(&pretimeout_lock);
 189        list_add(&p->entry, &pretimeout_list);
 190        p->wdd = wdd;
 191        wdd->gov = default_gov;
 192        spin_unlock_irq(&pretimeout_lock);
 193
 194        return 0;
 195}
 196
 197void watchdog_unregister_pretimeout(struct watchdog_device *wdd)
 198{
 199        struct watchdog_pretimeout *p, *t;
 200
 201        if (!watchdog_have_pretimeout(wdd))
 202                return;
 203
 204        spin_lock_irq(&pretimeout_lock);
 205        wdd->gov = NULL;
 206
 207        list_for_each_entry_safe(p, t, &pretimeout_list, entry) {
 208                if (p->wdd == wdd) {
 209                        list_del(&p->entry);
 210                        break;
 211                }
 212        }
 213        spin_unlock_irq(&pretimeout_lock);
 214
 215        kfree(p);
 216}
 217