I have a Linux device driver that implements the firmware update API. The driver updates the firmware on our device by using I2C commands to update an attached EEPROM. I use the firmware update API like so:
request_firmware_nowait(
THIS_MODULE, true, "DEVICE.img", &client->dev,
GFP_KERNEL, pdata,
&DEVICE_firmware_callback);
The issue I have is that if the system is powered down during this firmware update process then the attached EEPROM is left in a corrupt state and the device controller will not respond properly on the next boot, not even enough to start another firmware update to correct the issue.
I think a clean solution would be to prevent the system from powering down or suspending during this process, but I do not know how to implement that. Is there a way to have our device driver prevent the system from shutting down while the firmware update is in process?
You can use a reboot notifier along with a completion for this. Whenever you need to perform the firmware update:
register_reboot_notifier()
.init_completion()
.complete()
.unregister_reboot_notifier()
.Your reboot notifier will detect a reboot (halt, reboot, power off), and will have the possibility to wait for the work to be done through wait_for_completion()
(or one of its variants).
Here's an example module that does exactly this with a dummy kthread that just sleeps for 5 seconds:
// SPDX-License-Identifier: GPL-3.0
#include <linux/init.h> // module_{init,exit}()
#include <linux/module.h> // THIS_MODULE, MODULE_VERSION, ...
#include <linux/kernel.h> // printk(), pr_*()
#include <linux/reboot.h> // register_reboot_notifier()
#include <linux/kthread.h> // kthread_{create,stop,...}()
#include <linux/delay.h> // msleep()
#include <linux/completion.h> // struct completion, complete(), ...
#ifdef pr_fmt
#undef pr_fmt
#endif
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
static DECLARE_COMPLETION(done_wasting_time);
int my_notifier(struct notifier_block *nb, unsigned long action, void *data) {
if (!completion_done(&done_wasting_time)) {
pr_info("Wait! I have some critical job to finish...\n");
wait_for_completion(&done_wasting_time);
pr_info("Done!\n");
}
return NOTIFY_OK;
}
static struct notifier_block notifier = {
.notifier_call = my_notifier,
.next = NULL,
.priority = 0
};
int waste_time(void *data) {
struct completion *cmp = data;
msleep(5000);
complete(cmp);
return 0;
}
static int __init modinit(void)
{
register_reboot_notifier(¬ifier);
kthread_run(waste_time, &done_wasting_time, "waste_time");
return 0;
}
static void __exit modexit(void)
{
unregister_reboot_notifier(¬ifier);
}
module_init(modinit);
module_exit(modexit);
MODULE_VERSION("0.1");
MODULE_DESCRIPTION("Test module: wait for a critical job to finish before"
" rebooting or powering down.");
MODULE_AUTHOR("Marco Bonelli");
MODULE_LICENSE("GPL");
Output on my test machine:
# insmod reboot_notifier_test.ko
# reboot
[ 6.031410] reboot_notifier_test: Wait! I have some critical job to finish...
[ 9.998207] reboot_notifier_test: Done!
[ 10.003917] reboot: Power down