cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

watchdog_pretimeout.c (4606B)


      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}