From: Ian Kent <ra...@themaw.net>

Introduce an initial implementation that uses the new libmount
kernelwatch notification method together with the fsinfo() system
call.

What I'm trying to do here is use kernelwatch notifications to create
a set of mount units that have actually changed for the traversal that
follows, which of course means not reading the entire mount table.

So I add the Mount units for mount and umount notifications to a set
that is used for the subsequent state update traversal. That should play
well with the existing mount unit state handling, because umounted mount
units would have been looked at in the traversal previously as well as
ones that have been mounted, but now only the things that have actually
changed will be in the set.

Signed-off-by: Ian Kent <ra...@themaw.net>
---
 src/core/mount.c |  162 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 161 insertions(+), 1 deletion(-)

diff --git a/src/core/mount.c b/src/core/mount.c
index 45b5b0c1ec..9e58458dd3 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -56,6 +56,7 @@ static int mount_dispatch_io(sd_event_source *source, int fd, 
uint32_t revents,
 static void mount_update_unit_state(Mount *m, Set *gone, Set *around);
 static int mount_setup_unit(Manager *m, int mnt_id, const char *what, const 
char *where, const char *options, const char *fstype, bool set_flags);
 static int mount_process_proc_self_mountinfo(Manager *m);
+static int mount_process_libmount_kernelwatch_events(Manager *m);
 
 static Hashmap *mount_unit_by_mnt_id = NULL;
 
@@ -208,6 +209,28 @@ static int mount_hash_replace_mnt_id(Mount *m, int mnt_id) 
{
         return r;
 }
 
+static Mount *mount_hash_get_mount_unit(int mnt_id) {
+        assert(mount_unit_by_mnt_id);
+
+        return hashmap_get(mount_unit_by_mnt_id, INT_TO_PTR(mnt_id));
+}
+
+static int mount_get_and_set_put(Set *s, int mnt_id) {
+        Mount *m;
+
+        assert(s);
+
+        m = mount_hash_get_mount_unit(mnt_id);
+        if (!m)
+                return -ENOENT;
+
+        if (s && set_contains(s, m))
+                return 0;
+        (void) set_put(s, m);
+
+        return 0;
+}
+
 static void mount_init(Unit *u) {
         Mount *m = MOUNT(u);
 
@@ -1962,7 +1985,10 @@ static void mount_enumerate(Manager *m) {
                         goto fail;
                 }
 
-                r = mnt_monitor_enable_kernel(m->mount_monitor, 1);
+                if (mnt_has_fsinfo())
+                        r = mnt_monitor_enable_kernelwatch(m->mount_monitor, 
1);
+                else
+                        r = mnt_monitor_enable_kernel(m->mount_monitor, 1);
                 if (r < 0) {
                         log_error_errno(r, "Failed to enable watching of 
kernel mount events: %m");
                         goto fail;
@@ -2006,6 +2032,132 @@ fail:
         mount_shutdown(m);
 }
 
+static int handle_libmount_kernelwatch_event(Manager *m, void *watch_info, Set 
*changed) {
+        struct libmnt_fs *fs;
+        const char *device, *path, *options, *fstype;
+        Mount *mount;
+        int mnt_id;
+        int op;
+
+        op = mnt_kernelwatch_get_operation(watch_info);
+        if (op == MNT_NOTIFY_MOUNT_EXPIRY ||
+            op == MNT_NOTIFY_MOUNT_READONLY ||
+            op == MNT_NOTIFY_MOUNT_SETATTR)
+                mnt_id = mnt_kernelwatch_get_mount_id(watch_info);
+        else
+                mnt_id = mnt_kernelwatch_get_aux_id(watch_info);
+
+        if (op == MNT_NOTIFY_MOUNT_UNMOUNT ||
+            op == MNT_NOTIFY_MOUNT_EXPIRY ||
+            op == MNT_NOTIFY_MOUNT_MOVE_FROM) {
+                /* Mount unit may already be gone */
+                mount_get_and_set_put(changed, mnt_id);
+                return 0;
+        }
+
+        fs = mnt_new_fs();
+        if (!fs)
+                return log_error_errno(-ENOMEM, "op %d: failed to allocate 
libmount fs struct", op);
+
+        mnt_fs_set_id(fs, mnt_id);
+        mnt_fs_enable_fsinfo(fs, 1);
+
+        device = mnt_fs_get_source(fs);
+        path = mnt_fs_get_target(fs);
+        options = mnt_fs_get_options(fs);
+        fstype = mnt_fs_get_fstype(fs);
+
+        if (!device || !path || !fstype) {
+                mnt_unref_fs(fs);
+                /* the mount with this id has gone away */
+                mount_get_and_set_put(changed, mnt_id);
+                return 0;
+        }
+
+        device_found_node(m, device, DEVICE_FOUND_MOUNT, DEVICE_FOUND_MOUNT);
+        (void) mount_setup_unit(m, mnt_id, device, path, options, fstype, 
true);
+
+        mnt_unref_fs(fs);
+
+        return mount_get_and_set_put(changed, mnt_id);
+}
+
+static int mount_process_libmount_kernelwatch_events(Manager *m) {
+        struct libmnt_monitor *mn = m->mount_monitor;
+        _cleanup_set_free_free_ Set *around = NULL, *gone = NULL;
+        _cleanup_set_free_free_ Set *changed = NULL;
+        const char *what;
+        Iterator i;
+        Mount *mount;
+        void *data;
+        ssize_t sz;
+        int r = 0;
+
+        changed = set_new(NULL);
+        if (!changed)
+                return -ENOMEM;
+
+        do {
+                int type = 0;
+
+                r = mnt_monitor_next_change(m->mount_monitor, NULL, &type);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get next 
kenrelwatch event: %m");
+                if (type != MNT_MONITOR_TYPE_KERNELWATCH)
+                        continue;
+                data = mnt_monitor_event_data(mn, 
MNT_MONITOR_TYPE_KERNELWATCH, &sz);
+                while (data) {
+again:
+                        if (!mnt_kernelwatch_is_valid(data, sz) ||
+                            !mnt_kernelwatch_is_mount(data)) {
+                                r = -EINVAL;
+                                return log_error_errno(r, "Invalid 
kernelwatchevent data: %m");
+                        }
+
+                        r = handle_libmount_kernelwatch_event(m, data, 
changed);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to handle 
kernelwatchevent event: %m");
+
+                        data = mnt_kernelwatch_next_data(data, &sz);
+                }
+
+                /* If there's multiple mounts occurring try and get more
+                 * events.
+                 */
+                if (r == 0) {
+                        struct timespec to = { 0, 100000000 };
+                        struct timespec rm;
+
+                        while (nanosleep(&to, &rm) == -1 && errno == EINTR)
+                                memcpy(&to, &rm, sizeof(struct timespec));
+                        data = mnt_kernelwatch_next_data(data, &sz);
+                        if (data)
+                                goto again;
+                }
+        } while (r == 0);
+
+        manager_dispatch_load_queue(m);
+
+        gone = set_new(NULL);
+        around = set_new(NULL);
+        if (!gone || !around)
+                return -ENOMEM;
+
+        SET_FOREACH(mount, changed, i) {
+                mount_update_unit_state(mount, gone, around);
+        }
+
+        SET_FOREACH(what, gone, i) {
+                if (set_contains(around, what))
+                        continue;
+
+                /* Let the device units know that the device is no longer 
mounted */
+                device_found_node(m, what, 0, DEVICE_FOUND_MOUNT);
+        }
+
+        return 0;
+}
+
 static int drain_libmount(Manager *m) {
         bool rescan = false;
         int r;
@@ -2148,10 +2300,18 @@ static int mount_process_proc_self_mountinfo(Manager 
*m) {
 
 static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t 
revents, void *userdata) {
         Manager *m = userdata;
+        int r;
 
         assert(m);
         assert(revents & EPOLLIN);
 
+        if (mnt_has_fsinfo()) {
+                r = mount_process_libmount_kernelwatch_events(m);
+                if (r == 0)
+                        return r;
+                return log_error_errno(r, "failed to process kernelwatch 
events, falling back to mountinfo load: %m");
+        }
+
         return mount_process_proc_self_mountinfo(m);
 }
 


_______________________________________________
systemd-devel mailing list
systemd-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/systemd-devel

Reply via email to