Skip to content

Commit 0de4c40

Browse files
committed
Merge patch series "efivarfs: support freeze/thaw"
Christian Brauner <[email protected]> says: 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. If userspace initiated a freeze/thaw cycle they would need CAP_SYS_ADMIN in the initial user namespace (as that's where efivarfs is mounted) so it can't be triggered by random userspace. IOW, we really really don't care. * patches from https://lore.kernel.org/r/[email protected]: efivarfs: support freeze/thaw libfs: export find_next_child() Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Christian Brauner <[email protected]>
2 parents 6e5af8e + 0e4f948 commit 0de4c40

File tree

4 files changed

+54
-146
lines changed

4 files changed

+54
-146
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
}

fs/internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,3 +343,4 @@ static inline bool path_mounted(const struct path *path)
343343
void file_f_owner_release(struct file *file);
344344
bool file_seek_cur_needs_f_lock(struct file *file);
345345
int statmount_mnt_idmap(struct mnt_idmap *idmap, struct seq_file *seq, bool uid_map);
346+
struct dentry *find_next_child(struct dentry *parent, struct dentry *prev);

fs/libfs.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ const struct file_operations simple_offset_dir_operations = {
583583
.fsync = noop_fsync,
584584
};
585585

586-
static struct dentry *find_next_child(struct dentry *parent, struct dentry *prev)
586+
struct dentry *find_next_child(struct dentry *parent, struct dentry *prev)
587587
{
588588
struct dentry *child = NULL, *d;
589589

@@ -603,6 +603,7 @@ static struct dentry *find_next_child(struct dentry *parent, struct dentry *prev
603603
dput(prev);
604604
return child;
605605
}
606+
EXPORT_SYMBOL(find_next_child);
606607

607608
void simple_recursive_removal(struct dentry *dentry,
608609
void (*callback)(struct dentry *))

0 commit comments

Comments
 (0)