Skip to content

Commit 0e4f948

Browse files
committed
efivarfs: support freeze/thaw
Allow efivarfs to partake to resync variable state during system hibernation and suspend. Add freeze/thaw support. This is a pretty straightforward implementation. We simply add regular freeze/thaw support for both userspace and the kernel. This works without any big issues and congrats afaict efivars is the first pseudofilesystem that adds support for filesystem freezing and thawing. The simplicity comes from the fact that we simply always resync variable state after efivarfs has been frozen. It doesn't matter whether that's because of suspend, userspace initiated freeze or hibernation. Efivars is simple enough that it doesn't matter that we walk all dentries. There are no directories and there aren't insane amounts of entries and both freeze/thaw are already heavy-handed operations. We really really don't need to care. Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Christian Brauner <[email protected]>
1 parent 33445d6 commit 0e4f948

File tree

2 files changed

+51
-145
lines changed

2 files changed

+51
-145
lines changed

fs/efivarfs/internal.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ struct efivarfs_fs_info {
1717
struct efivarfs_mount_opts mount_opts;
1818
struct super_block *sb;
1919
struct notifier_block nb;
20-
struct notifier_block pm_nb;
2120
};
2221

2322
struct efi_variable {

fs/efivarfs/super.c

Lines changed: 51 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <linux/printk.h>
2121

2222
#include "internal.h"
23+
#include "../internal.h"
2324

2425
static int efivarfs_ops_notifier(struct notifier_block *nb, unsigned long event,
2526
void *data)
@@ -119,12 +120,18 @@ static int efivarfs_statfs(struct dentry *dentry, struct kstatfs *buf)
119120

120121
return 0;
121122
}
123+
124+
static int efivarfs_freeze_fs(struct super_block *sb);
125+
static int efivarfs_unfreeze_fs(struct super_block *sb);
126+
122127
static const struct super_operations efivarfs_ops = {
123128
.statfs = efivarfs_statfs,
124129
.drop_inode = generic_delete_inode,
125130
.alloc_inode = efivarfs_alloc_inode,
126131
.free_inode = efivarfs_free_inode,
127132
.show_options = efivarfs_show_options,
133+
.freeze_fs = efivarfs_freeze_fs,
134+
.unfreeze_fs = efivarfs_unfreeze_fs,
128135
};
129136

130137
/*
@@ -367,8 +374,6 @@ static int efivarfs_fill_super(struct super_block *sb, struct fs_context *fc)
367374
if (err)
368375
return err;
369376

370-
register_pm_notifier(&sfi->pm_nb);
371-
372377
return efivar_init(efivarfs_callback, sb, true);
373378
}
374379

@@ -393,48 +398,6 @@ static const struct fs_context_operations efivarfs_context_ops = {
393398
.reconfigure = efivarfs_reconfigure,
394399
};
395400

396-
struct efivarfs_ctx {
397-
struct dir_context ctx;
398-
struct super_block *sb;
399-
struct dentry *dentry;
400-
};
401-
402-
static bool efivarfs_actor(struct dir_context *ctx, const char *name, int len,
403-
loff_t offset, u64 ino, unsigned mode)
404-
{
405-
unsigned long size;
406-
struct efivarfs_ctx *ectx = container_of(ctx, struct efivarfs_ctx, ctx);
407-
struct qstr qstr = { .name = name, .len = len };
408-
struct dentry *dentry = d_hash_and_lookup(ectx->sb->s_root, &qstr);
409-
struct inode *inode;
410-
struct efivar_entry *entry;
411-
int err;
412-
413-
if (IS_ERR_OR_NULL(dentry))
414-
return true;
415-
416-
inode = d_inode(dentry);
417-
entry = efivar_entry(inode);
418-
419-
err = efivar_entry_size(entry, &size);
420-
size += sizeof(__u32); /* attributes */
421-
if (err)
422-
size = 0;
423-
424-
inode_lock_nested(inode, I_MUTEX_CHILD);
425-
i_size_write(inode, size);
426-
inode_unlock(inode);
427-
428-
if (!size) {
429-
ectx->dentry = dentry;
430-
return false;
431-
}
432-
433-
dput(dentry);
434-
435-
return true;
436-
}
437-
438401
static int efivarfs_check_missing(efi_char16_t *name16, efi_guid_t vendor,
439402
unsigned long name_size, void *data)
440403
{
@@ -474,111 +437,59 @@ static int efivarfs_check_missing(efi_char16_t *name16, efi_guid_t vendor,
474437
return err;
475438
}
476439

477-
static void efivarfs_deactivate_super_work(struct work_struct *work)
478-
{
479-
struct super_block *s = container_of(work, struct super_block,
480-
destroy_work);
481-
/*
482-
* note: here s->destroy_work is free for reuse (which
483-
* will happen in deactivate_super)
484-
*/
485-
deactivate_super(s);
486-
}
487-
488440
static struct file_system_type efivarfs_type;
489441

490-
static int efivarfs_pm_notify(struct notifier_block *nb, unsigned long action,
491-
void *ptr)
442+
static int efivarfs_freeze_fs(struct super_block *sb)
492443
{
493-
struct efivarfs_fs_info *sfi = container_of(nb, struct efivarfs_fs_info,
494-
pm_nb);
495-
struct path path;
496-
struct efivarfs_ctx ectx = {
497-
.ctx = {
498-
.actor = efivarfs_actor,
499-
},
500-
.sb = sfi->sb,
501-
};
502-
struct file *file;
503-
struct super_block *s = sfi->sb;
504-
static bool rescan_done = true;
505-
506-
if (action == PM_HIBERNATION_PREPARE) {
507-
rescan_done = false;
508-
return NOTIFY_OK;
509-
} else if (action != PM_POST_HIBERNATION) {
510-
return NOTIFY_DONE;
511-
}
512-
513-
if (rescan_done)
514-
return NOTIFY_DONE;
515-
516-
/* ensure single superblock is alive and pin it */
517-
if (!atomic_inc_not_zero(&s->s_active))
518-
return NOTIFY_DONE;
519-
520-
pr_info("efivarfs: resyncing variable state\n");
521-
522-
path.dentry = sfi->sb->s_root;
523-
524-
/*
525-
* do not add SB_KERNMOUNT which a single superblock could
526-
* expose to userspace and which also causes MNT_INTERNAL, see
527-
* below
528-
*/
529-
path.mnt = vfs_kern_mount(&efivarfs_type, 0,
530-
efivarfs_type.name, NULL);
531-
if (IS_ERR(path.mnt)) {
532-
pr_err("efivarfs: internal mount failed\n");
533-
/*
534-
* We may be the last pinner of the superblock but
535-
* calling efivarfs_kill_sb from within the notifier
536-
* here would deadlock trying to unregister it
537-
*/
538-
INIT_WORK(&s->destroy_work, efivarfs_deactivate_super_work);
539-
schedule_work(&s->destroy_work);
540-
return PTR_ERR(path.mnt);
541-
}
542-
543-
/* path.mnt now has pin on superblock, so this must be above one */
544-
atomic_dec(&s->s_active);
545-
546-
file = kernel_file_open(&path, O_RDONLY | O_DIRECTORY | O_NOATIME,
547-
current_cred());
548-
/*
549-
* safe even if last put because no MNT_INTERNAL means this
550-
* will do delayed deactivate_super and not deadlock
551-
*/
552-
mntput(path.mnt);
553-
if (IS_ERR(file))
554-
return NOTIFY_DONE;
444+
/* Nothing for us to do. */
445+
return 0;
446+
}
555447

556-
rescan_done = true;
448+
static int efivarfs_unfreeze_fs(struct super_block *sb)
449+
{
450+
struct dentry *child = NULL;
557451

558452
/*
559-
* First loop over the directory and verify each entry exists,
560-
* removing it if it doesn't
453+
* Unconditionally resync the variable state on a thaw request.
454+
* Given the size of efivarfs it really doesn't matter to simply
455+
* iterate through all of the entries and resync. Freeze/thaw
456+
* requests are rare enough for that to not matter and the
457+
* number of entries is pretty low too. So we really don't care.
561458
*/
562-
file->f_pos = 2; /* skip . and .. */
563-
do {
564-
ectx.dentry = NULL;
565-
iterate_dir(file, &ectx.ctx);
566-
if (ectx.dentry) {
567-
pr_info("efivarfs: removing variable %pd\n",
568-
ectx.dentry);
569-
simple_recursive_removal(ectx.dentry, NULL);
570-
dput(ectx.dentry);
459+
pr_info("efivarfs: resyncing variable state\n");
460+
for (;;) {
461+
int err;
462+
unsigned long size = 0;
463+
struct inode *inode;
464+
struct efivar_entry *entry;
465+
466+
child = find_next_child(sb->s_root, child);
467+
if (!child)
468+
break;
469+
470+
inode = d_inode(child);
471+
entry = efivar_entry(inode);
472+
473+
err = efivar_entry_size(entry, &size);
474+
if (err)
475+
size = 0;
476+
else
477+
size += sizeof(__u32);
478+
479+
inode_lock(inode);
480+
i_size_write(inode, size);
481+
inode_unlock(inode);
482+
483+
/* The variable doesn't exist anymore, delete it. */
484+
if (!size) {
485+
pr_info("efivarfs: removing variable %pd\n", child);
486+
simple_recursive_removal(child, NULL);
571487
}
572-
} while (ectx.dentry);
573-
fput(file);
574-
575-
/*
576-
* then loop over variables, creating them if there's no matching
577-
* dentry
578-
*/
579-
efivar_init(efivarfs_check_missing, sfi->sb, false);
488+
}
580489

581-
return NOTIFY_OK;
490+
efivar_init(efivarfs_check_missing, sb, false);
491+
pr_info("efivarfs: finished resyncing variable state\n");
492+
return 0;
582493
}
583494

584495
static int efivarfs_init_fs_context(struct fs_context *fc)
@@ -598,9 +509,6 @@ static int efivarfs_init_fs_context(struct fs_context *fc)
598509
fc->s_fs_info = sfi;
599510
fc->ops = &efivarfs_context_ops;
600511

601-
sfi->pm_nb.notifier_call = efivarfs_pm_notify;
602-
sfi->pm_nb.priority = 0;
603-
604512
return 0;
605513
}
606514

@@ -610,7 +518,6 @@ static void efivarfs_kill_sb(struct super_block *sb)
610518

611519
blocking_notifier_chain_unregister(&efivar_ops_nh, &sfi->nb);
612520
kill_litter_super(sb);
613-
unregister_pm_notifier(&sfi->pm_nb);
614521

615522
kfree(sfi);
616523
}

0 commit comments

Comments
 (0)