From: Dmitry Monakhov <dmonak...@openvz.org>

*Purpose:
It is reasonable to annaunce 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 <dmonak...@openvz.org>

[aryabinin: add error event, rh8 rebase]
Signed-off-by: Andrey Ryabinin <aryabi...@virtuozzo.com>
---
 fs/ext4/ext4.h  |  11 +++++
 fs/ext4/super.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 138 insertions(+), 1 deletion(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 028832d858fc..228492c9518f 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1498,6 +1498,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 */
@@ -3119,6 +3121,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 7fc5ad243953..3cc979825ec8 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -354,6 +354,117 @@ 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);
+}
+
 static void __save_error_info(struct super_block *sb, const char *func,
                            unsigned int line)
 {
@@ -453,6 +564,9 @@ static bool system_going_down(void)
 
 static void ext4_handle_error(struct super_block *sb)
 {
+       if (!xchg(&EXT4_SB(sb)->s_err_event_sent, 1))
+               ext4_send_uevent(sb, EXT4_UA_ERROR);
+
        if (test_opt(sb, WARN_ON_ERROR))
                WARN_ON_ONCE(1);
 
@@ -972,10 +1086,12 @@ 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);
 
        destroy_workqueue(sbi->rsv_conversion_wq);
+       sbi->rsv_conversion_wq = NULL;
 
        if (sbi->s_journal) {
                aborted = is_journal_aborted(sbi->s_journal);
@@ -4523,6 +4639,7 @@ static int ext4_fill_super(struct super_block *sb, void 
*data, int silent)
        ratelimit_state_init(&sbi->s_warning_ratelimit_state, 5 * HZ, 10);
        ratelimit_state_init(&sbi->s_msg_ratelimit_state, 5 * HZ, 10);
 
+       ext4_send_uevent(sb, EXT4_UA_MOUNT);
        kfree(orig_data);
        return 0;
 
@@ -5081,8 +5198,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;
 
@@ -5107,6 +5226,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;
 }
 
@@ -5116,6 +5239,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;
 
@@ -5394,6 +5519,7 @@ static int ext4_remount(struct super_block *sb, int 
*flags, char *data)
 
        *flags = (*flags & ~SB_LAZYTIME) | (sb->s_flags & SB_LAZYTIME);
        ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s", orig_data);
+       ext4_send_uevent(sb, EXT4_UA_REMOUNT);
        kfree(orig_data);
        return 0;
 
-- 
2.26.2

_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

Reply via email to