Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (C) 2015-2016 Mentor Graphics
0004  */
0005 
0006 #include <linux/list.h>
0007 #include <linux/slab.h>
0008 #include <linux/spinlock.h>
0009 #include <linux/string.h>
0010 #include <linux/watchdog.h>
0011 
0012 #include "watchdog_core.h"
0013 #include "watchdog_pretimeout.h"
0014 
0015 /* Default watchdog pretimeout governor */
0016 static struct watchdog_governor *default_gov;
0017 
0018 /* The spinlock protects default_gov, wdd->gov and pretimeout_list */
0019 static DEFINE_SPINLOCK(pretimeout_lock);
0020 
0021 /* List of watchdog devices, which can generate a pretimeout event */
0022 static LIST_HEAD(pretimeout_list);
0023 
0024 struct watchdog_pretimeout {
0025     struct watchdog_device      *wdd;
0026     struct list_head        entry;
0027 };
0028 
0029 /* The mutex protects governor list and serializes external interfaces */
0030 static DEFINE_MUTEX(governor_lock);
0031 
0032 /* List of the registered watchdog pretimeout governors */
0033 static LIST_HEAD(governor_list);
0034 
0035 struct governor_priv {
0036     struct watchdog_governor    *gov;
0037     struct list_head        entry;
0038 };
0039 
0040 static struct governor_priv *find_governor_by_name(const char *gov_name)
0041 {
0042     struct governor_priv *priv;
0043 
0044     list_for_each_entry(priv, &governor_list, entry)
0045         if (sysfs_streq(gov_name, priv->gov->name))
0046             return priv;
0047 
0048     return NULL;
0049 }
0050 
0051 int watchdog_pretimeout_available_governors_get(char *buf)
0052 {
0053     struct governor_priv *priv;
0054     int count = 0;
0055 
0056     mutex_lock(&governor_lock);
0057 
0058     list_for_each_entry(priv, &governor_list, entry)
0059         count += sysfs_emit_at(buf, count, "%s\n", priv->gov->name);
0060 
0061     mutex_unlock(&governor_lock);
0062 
0063     return count;
0064 }
0065 
0066 int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf)
0067 {
0068     int count = 0;
0069 
0070     spin_lock_irq(&pretimeout_lock);
0071     if (wdd->gov)
0072         count = sysfs_emit(buf, "%s\n", wdd->gov->name);
0073     spin_unlock_irq(&pretimeout_lock);
0074 
0075     return count;
0076 }
0077 
0078 int watchdog_pretimeout_governor_set(struct watchdog_device *wdd,
0079                      const char *buf)
0080 {
0081     struct governor_priv *priv;
0082 
0083     mutex_lock(&governor_lock);
0084 
0085     priv = find_governor_by_name(buf);
0086     if (!priv) {
0087         mutex_unlock(&governor_lock);
0088         return -EINVAL;
0089     }
0090 
0091     spin_lock_irq(&pretimeout_lock);
0092     wdd->gov = priv->gov;
0093     spin_unlock_irq(&pretimeout_lock);
0094 
0095     mutex_unlock(&governor_lock);
0096 
0097     return 0;
0098 }
0099 
0100 void watchdog_notify_pretimeout(struct watchdog_device *wdd)
0101 {
0102     unsigned long flags;
0103 
0104     spin_lock_irqsave(&pretimeout_lock, flags);
0105     if (!wdd->gov) {
0106         spin_unlock_irqrestore(&pretimeout_lock, flags);
0107         return;
0108     }
0109 
0110     wdd->gov->pretimeout(wdd);
0111     spin_unlock_irqrestore(&pretimeout_lock, flags);
0112 }
0113 EXPORT_SYMBOL_GPL(watchdog_notify_pretimeout);
0114 
0115 int watchdog_register_governor(struct watchdog_governor *gov)
0116 {
0117     struct watchdog_pretimeout *p;
0118     struct governor_priv *priv;
0119 
0120     priv = kzalloc(sizeof(*priv), GFP_KERNEL);
0121     if (!priv)
0122         return -ENOMEM;
0123 
0124     mutex_lock(&governor_lock);
0125 
0126     if (find_governor_by_name(gov->name)) {
0127         mutex_unlock(&governor_lock);
0128         kfree(priv);
0129         return -EBUSY;
0130     }
0131 
0132     priv->gov = gov;
0133     list_add(&priv->entry, &governor_list);
0134 
0135     if (!strncmp(gov->name, WATCHDOG_PRETIMEOUT_DEFAULT_GOV,
0136              WATCHDOG_GOV_NAME_MAXLEN)) {
0137         spin_lock_irq(&pretimeout_lock);
0138         default_gov = gov;
0139 
0140         list_for_each_entry(p, &pretimeout_list, entry)
0141             if (!p->wdd->gov)
0142                 p->wdd->gov = default_gov;
0143         spin_unlock_irq(&pretimeout_lock);
0144     }
0145 
0146     mutex_unlock(&governor_lock);
0147 
0148     return 0;
0149 }
0150 EXPORT_SYMBOL(watchdog_register_governor);
0151 
0152 void watchdog_unregister_governor(struct watchdog_governor *gov)
0153 {
0154     struct watchdog_pretimeout *p;
0155     struct governor_priv *priv, *t;
0156 
0157     mutex_lock(&governor_lock);
0158 
0159     list_for_each_entry_safe(priv, t, &governor_list, entry) {
0160         if (priv->gov == gov) {
0161             list_del(&priv->entry);
0162             kfree(priv);
0163             break;
0164         }
0165     }
0166 
0167     spin_lock_irq(&pretimeout_lock);
0168     list_for_each_entry(p, &pretimeout_list, entry)
0169         if (p->wdd->gov == gov)
0170             p->wdd->gov = default_gov;
0171     spin_unlock_irq(&pretimeout_lock);
0172 
0173     mutex_unlock(&governor_lock);
0174 }
0175 EXPORT_SYMBOL(watchdog_unregister_governor);
0176 
0177 int watchdog_register_pretimeout(struct watchdog_device *wdd)
0178 {
0179     struct watchdog_pretimeout *p;
0180 
0181     if (!watchdog_have_pretimeout(wdd))
0182         return 0;
0183 
0184     p = kzalloc(sizeof(*p), GFP_KERNEL);
0185     if (!p)
0186         return -ENOMEM;
0187 
0188     spin_lock_irq(&pretimeout_lock);
0189     list_add(&p->entry, &pretimeout_list);
0190     p->wdd = wdd;
0191     wdd->gov = default_gov;
0192     spin_unlock_irq(&pretimeout_lock);
0193 
0194     return 0;
0195 }
0196 
0197 void watchdog_unregister_pretimeout(struct watchdog_device *wdd)
0198 {
0199     struct watchdog_pretimeout *p, *t;
0200 
0201     if (!watchdog_have_pretimeout(wdd))
0202         return;
0203 
0204     spin_lock_irq(&pretimeout_lock);
0205     wdd->gov = NULL;
0206 
0207     list_for_each_entry_safe(p, t, &pretimeout_list, entry) {
0208         if (p->wdd == wdd) {
0209             list_del(&p->entry);
0210             break;
0211         }
0212     }
0213     spin_unlock_irq(&pretimeout_lock);
0214 
0215     kfree(p);
0216 }