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