/* * Copyright (C) 2015-2016 Mentor Graphics * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * */ #include #include #include #include #include "watchdog_pretimeout.h" /* Default watchdog pretimeout governor */ static struct watchdog_governor *default_gov; /* The spinlock protects default_gov, wdd->gov and pretimeout_list */ static DEFINE_SPINLOCK(pretimeout_lock); /* List of watchdog devices, which can generate a pretimeout event */ static LIST_HEAD(pretimeout_list); struct watchdog_pretimeout { struct watchdog_device *wdd; struct list_head entry; }; int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf) { int count = 0; spin_lock_irq(&pretimeout_lock); if (wdd->gov) count = sprintf(buf, "%s\n", wdd->gov->name); spin_unlock_irq(&pretimeout_lock); return count; } void watchdog_notify_pretimeout(struct watchdog_device *wdd) { unsigned long flags; spin_lock_irqsave(&pretimeout_lock, flags); if (!wdd->gov) { spin_unlock_irqrestore(&pretimeout_lock, flags); return; } wdd->gov->pretimeout(wdd); spin_unlock_irqrestore(&pretimeout_lock, flags); } EXPORT_SYMBOL_GPL(watchdog_notify_pretimeout); int watchdog_register_governor(struct watchdog_governor *gov) { struct watchdog_pretimeout *p; if (!default_gov) { spin_lock_irq(&pretimeout_lock); default_gov = gov; list_for_each_entry(p, &pretimeout_list, entry) if (!p->wdd->gov) p->wdd->gov = default_gov; spin_unlock_irq(&pretimeout_lock); } return 0; } EXPORT_SYMBOL(watchdog_register_governor); void watchdog_unregister_governor(struct watchdog_governor *gov) { struct watchdog_pretimeout *p; spin_lock_irq(&pretimeout_lock); if (gov == default_gov) default_gov = NULL; list_for_each_entry(p, &pretimeout_list, entry) if (p->wdd->gov == gov) p->wdd->gov = default_gov; spin_unlock_irq(&pretimeout_lock); } EXPORT_SYMBOL(watchdog_unregister_governor); int watchdog_register_pretimeout(struct watchdog_device *wdd) { struct watchdog_pretimeout *p; if (!(wdd->info->options & WDIOF_PRETIMEOUT)) return 0; p = kzalloc(sizeof(*p), GFP_KERNEL); if (!p) return -ENOMEM; spin_lock_irq(&pretimeout_lock); list_add(&p->entry, &pretimeout_list); p->wdd = wdd; wdd->gov = default_gov; spin_unlock_irq(&pretimeout_lock); return 0; } void watchdog_unregister_pretimeout(struct watchdog_device *wdd) { struct watchdog_pretimeout *p, *t; if (!(wdd->info->options & WDIOF_PRETIMEOUT)) return; spin_lock_irq(&pretimeout_lock); wdd->gov = NULL; list_for_each_entry_safe(p, t, &pretimeout_list, entry) { if (p->wdd == wdd) { list_del(&p->entry); break; } } spin_unlock_irq(&pretimeout_lock); kfree(p); }