[Devel] [PATCH RH9 11/12] ext4: add generic uevent infrastructure

Kirill Tkhai ktkhai at virtuozzo.com
Thu Oct 7 13:20:53 MSK 2021


From: Dmitry Monakhov <dmonakhov at openvz.org>

*Purpose:
It is reasonable to announce fs related events via uevent infrastructure.
This patch implement only ext4'th part, but IMHO this should be usefull for
any generic filesystem.

Example: Runtime fs-error is pure async event. Currently there is no good
way to handle this situation and inform user-space about this.

*Implementation:
 Add uevent infrastructure similar to dm uevent
 FS_ACTION = {MOUNT|UMOUNT|REMOUNT|ERROR|FREEZE|UNFREEZE}
 FS_UUID
 FS_NAME
 FS_TYPE

Signed-off-by: Dmitry Monakhov <dmonakhov at openvz.org>

[aryabinin: add error event, rh8 rebase]
Signed-off-by: Andrey Ryabinin <aryabinin at virtuozzo.com>
Signed-off-by: Kirill Tkhai <ktkhai at virtuozzo.com>
---
 fs/ext4/ext4.h  |   11 +++++
 fs/ext4/super.c |  129 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 139 insertions(+), 1 deletion(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 5f6fdd5514b2..70b3038fa0d1 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1625,6 +1625,8 @@ struct ext4_sb_info {
 	/* Precomputed FS UUID checksum for seeding other checksums */
 	__u32 s_csum_seed;
 
+	bool s_err_event_sent;
+
 	/* Reclaim extents from extent status tree */
 	struct shrinker s_es_shrinker;
 	struct list_head s_es_list;	/* List of inodes with reclaimable extents */
@@ -3655,6 +3657,15 @@ extern int ext4_check_blockref(const char *, unsigned int,
 struct ext4_ext_path;
 struct ext4_extent;
 
+enum ext4_event_type {
+	EXT4_UA_MOUNT,
+	EXT4_UA_UMOUNT,
+	EXT4_UA_REMOUNT,
+	EXT4_UA_ERROR,
+	EXT4_UA_FREEZE,
+	EXT4_UA_UNFREEZE,
+};
+
 /*
  * Maximum number of logical blocks in a file; ext4_extent's ee_block is
  * __le32.
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 6cf2d3e0ed8f..597768497c42 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -424,6 +424,118 @@ static time64_t __ext4_get_tstamp(__le32 *lo, __u8 *hi)
 #define ext4_get_tstamp(es, tstamp) \
 	__ext4_get_tstamp(&(es)->tstamp, &(es)->tstamp ## _hi)
 
+static int ext4_uuid_valid(const u8 *uuid)
+{
+	int i;
+
+	for (i = 0; i < 16; i++) {
+		if (uuid[i])
+			return 1;
+	}
+	return 0;
+}
+
+struct ext4_uevent {
+	struct super_block *sb;
+	enum ext4_event_type action;
+	struct work_struct work;
+};
+
+/**
+ * ext4_send_uevent - prepare and send uevent
+ *
+ * @sb:		super_block
+ * @action:		action type
+ *
+ */
+static void ext4_send_uevent_work(struct work_struct *w)
+{
+	struct ext4_uevent *e = container_of(w, struct ext4_uevent, work);
+	struct super_block *sb = e->sb;
+	struct kobj_uevent_env *env;
+	const u8 *uuid = EXT4_SB(sb)->s_es->s_uuid;
+	enum kobject_action kaction = KOBJ_CHANGE;
+	int ret;
+
+	env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
+	if (!env){
+		kfree(e);
+		return;
+	}
+	ret = add_uevent_var(env, "FS_TYPE=%s", sb->s_type->name);
+	if (ret)
+		goto out;
+	ret = add_uevent_var(env, "FS_NAME=%s", sb->s_id);
+	if (ret)
+		goto out;
+
+	if (ext4_uuid_valid(uuid)) {
+		ret = add_uevent_var(env, "UUID=%pUB", uuid);
+		if (ret)
+			goto out;
+	}
+
+	switch (e->action) {
+	case EXT4_UA_MOUNT:
+		kaction = KOBJ_ONLINE;
+		ret = add_uevent_var(env, "FS_ACTION=%s", "MOUNT");
+		break;
+	case EXT4_UA_UMOUNT:
+		kaction = KOBJ_OFFLINE;
+		ret = add_uevent_var(env, "FS_ACTION=%s", "UMOUNT");
+		break;
+	case EXT4_UA_REMOUNT:
+		ret = add_uevent_var(env, "FS_ACTION=%s", "REMOUNT");
+		break;
+	case EXT4_UA_ERROR:
+		ret = add_uevent_var(env, "FS_ACTION=%s", "ERROR");
+		break;
+	case EXT4_UA_FREEZE:
+		ret = add_uevent_var(env, "FS_ACTION=%s", "FREEZE");
+		break;
+	case EXT4_UA_UNFREEZE:
+		ret = add_uevent_var(env, "FS_ACTION=%s", "UNFREEZE");
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	if (ret)
+		goto out;
+	ret = kobject_uevent_env(&(EXT4_SB(sb)->s_kobj), kaction, env->envp);
+out:
+	kfree(env);
+	kfree(e);
+}
+
+/**
+ * ext4_send_uevent - prepare and schedule event submission
+ *
+ * @sb:		super_block
+ * @action:		action type
+ *
+ */
+void ext4_send_uevent(struct super_block *sb, enum ext4_event_type action)
+{
+	struct ext4_uevent *e;
+
+	/*
+	 * May happen if called from ext4_put_super() -> __ext4_abort()
+	 * -> ext4_send_uevent()
+	 */
+	if (!EXT4_SB(sb)->rsv_conversion_wq)
+		return;
+
+	e = kzalloc(sizeof(*e), GFP_NOIO);
+	if (!e)
+		return;
+
+	e->sb = sb;
+	e->action = action;
+	INIT_WORK(&e->work, ext4_send_uevent_work);
+	queue_work(EXT4_SB(sb)->rsv_conversion_wq, &e->work);
+}
+
+
 /*
  * The del_gendisk() function uninitializes the disk-specific data
  * structures, including the bdi structure, without telling anyone
@@ -644,6 +756,9 @@ static void ext4_handle_error(struct super_block *sb, bool force_ro, int error,
 	journal_t *journal = EXT4_SB(sb)->s_journal;
 	bool continue_fs = !force_ro && test_opt(sb, ERRORS_CONT);
 
+	if (!xchg(&EXT4_SB(sb)->s_err_event_sent, 1))
+		ext4_send_uevent(sb, EXT4_UA_ERROR);
+
 	EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
 	if (test_opt(sb, WARN_ON_ERROR))
 		WARN_ON_ONCE(1);
@@ -1171,11 +1286,13 @@ static void ext4_put_super(struct super_block *sb)
 	int aborted = 0;
 	int i, err;
 
+	ext4_send_uevent(sb, EXT4_UA_UMOUNT);
 	ext4_unregister_li_request(sb);
 	ext4_quota_off_umount(sb);
 
 	flush_work(&sbi->s_error_work);
 	destroy_workqueue(sbi->rsv_conversion_wq);
+	sbi->rsv_conversion_wq = NULL;
 
 	/*
 	 * Unregister sysfs before destroying jbd2 journal.
@@ -5206,6 +5323,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 	atomic_set(&sbi->s_warning_count, 0);
 	atomic_set(&sbi->s_msg_count, 0);
 
+	ext4_send_uevent(sb, EXT4_UA_MOUNT);
 	kfree(orig_data);
 	return 0;
 
@@ -5853,8 +5971,10 @@ static int ext4_freeze(struct super_block *sb)
 	int error = 0;
 	journal_t *journal;
 
-	if (sb_rdonly(sb))
+	if (sb_rdonly(sb)) {
+		ext4_send_uevent(sb, EXT4_UA_FREEZE);
 		return 0;
+	}
 
 	journal = EXT4_SB(sb)->s_journal;
 
@@ -5879,6 +5999,10 @@ static int ext4_freeze(struct super_block *sb)
 	if (journal)
 		/* we rely on upper layer to stop further updates */
 		jbd2_journal_unlock_updates(journal);
+
+	if (!error)
+		ext4_send_uevent(sb, EXT4_UA_FREEZE);
+
 	return error;
 }
 
@@ -5888,6 +6012,8 @@ static int ext4_freeze(struct super_block *sb)
  */
 static int ext4_unfreeze(struct super_block *sb)
 {
+	ext4_send_uevent(sb, EXT4_UA_UNFREEZE);
+
 	if (sb_rdonly(sb) || ext4_forced_shutdown(EXT4_SB(sb)))
 		return 0;
 
@@ -6222,6 +6348,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
 #endif
 	if (!ext4_has_feature_mmp(sb) || sb_rdonly(sb))
 		ext4_stop_mmpd(sbi);
+	ext4_send_uevent(sb, EXT4_UA_REMOUNT);
 	kfree(orig_data);
 	return err;
 }




More information about the Devel mailing list