From a6cc97566c54255c98612c801f2c9d9be2d0e8f8 Mon Sep 17 00:00:00 2001 From: Brian Behlendorf Date: Tue, 13 Mar 2018 10:45:55 -0700 Subject: [PATCH] Add kernel module auto-loading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Historically a dynamic misc minor number was registered for the /dev/zfs device in order to prevent minor number collisions. This was fine but it prevented us from being able to use the kernel module auto-loaded which requires a known reserved value. Resolve this issue by adding a configure test to find an available misc minor number which can then be used in MODULE_ALIAS_MISCDEV at build time. By adding this alias the zfs kmod is added to the list of known static-nodes and the systemd-tmpfiles-setup-dev service will create a /dev/zfs character device at boot time. This in turn allows us to update the 90-zfs.rules file to make it aware this is a static node. The upshot of this is that whenever a process (zpool, zfs, zed) opens the /dev/zfs the kmods will be automatic loaded. This even works for unprivileged users so there is no longer a need to manually load the modules at boot time. As an additional bonus the zed now no longer needs to start after the zfs-import.service since it will trigger the module load. In the unlikely event the minor number we selected conflicts with another out of tree unregistered minor number the code falls back to dynamically allocating it. In this case the modules again must be manually loaded. Note that due to the change in the method of registering the minor number the zimport.sh test case may incorrectly fail when the static node for the installed packages is created instead of the dynamic one. This issue will only transiently impact zimport.sh for this single commit when we transition and are mixing and matching methods. Reviewed-by: Fabian Grünbichler Signed-off-by: Brian Behlendorf TEST_ZIMPORT_SKIP="yes" Closes #7287 --- config/kernel-misc-minor.m4 | 26 +++++++++++++++++++ config/kernel.m4 | 1 + etc/modules-load.d/zfs.conf.in | 4 +-- .../system/zfs-import-cache.service.in | 1 - etc/systemd/system/zfs-import-scan.service.in | 1 - etc/systemd/system/zfs-zed.service.in | 1 - lib/libzfs/libzfs_util.c | 13 +++++----- module/zfs/zfs_ioctl.c | 25 ++++++++++++++---- rpm/generic/zfs.spec.in | 9 +++++++ udev/rules.d/90-zfs.rules.in | 2 +- 10 files changed, 66 insertions(+), 17 deletions(-) create mode 100644 config/kernel-misc-minor.m4 diff --git a/config/kernel-misc-minor.m4 b/config/kernel-misc-minor.m4 new file mode 100644 index 0000000000..3917e28ff6 --- /dev/null +++ b/config/kernel-misc-minor.m4 @@ -0,0 +1,26 @@ +dnl # +dnl # Determine an available miscellaneous minor number which can be used +dnl # for the /dev/zfs device. This is needed because kernel module +dnl # auto-loading depends on registering a reserved non-conflicting minor +dnl # number. Start with a large known available unreserved minor and work +dnl # our way down to lower value if a collision is detected. +dnl # +AC_DEFUN([ZFS_AC_KERNEL_MISC_MINOR], [ + AC_MSG_CHECKING([for available /dev/zfs minor]) + + for i in $(seq 249 -1 200); do + if ! grep -q "^#define\s\+.*_MINOR\s\+.*$i" \ + ${LINUX}/include/linux/miscdevice.h; then + ZFS_MINOR="$i" + AC_MSG_RESULT($ZFS_MINOR) + AC_DEFINE_UNQUOTED([ZFS_MINOR], [$ZFS_MINOR], + [/dev/zfs minor]) + break + fi + done + + AS_IF([ test -z "$ZFS_MINOR"], [ + AC_MSG_ERROR([ + *** No available misc minor numbers available for use.]) + ]) +]) diff --git a/config/kernel.m4 b/config/kernel.m4 index b80e57b1c0..1887b178c0 100644 --- a/config/kernel.m4 +++ b/config/kernel.m4 @@ -6,6 +6,7 @@ AC_DEFUN([ZFS_AC_CONFIG_KERNEL], [ ZFS_AC_SPL ZFS_AC_QAT ZFS_AC_TEST_MODULE + ZFS_AC_KERNEL_MISC_MINOR ZFS_AC_KERNEL_OBJTOOL ZFS_AC_KERNEL_CONFIG ZFS_AC_KERNEL_DECLARE_EVENT_CLASS diff --git a/etc/modules-load.d/zfs.conf.in b/etc/modules-load.d/zfs.conf.in index 8b41baa300..44e1bb3ed9 100644 --- a/etc/modules-load.d/zfs.conf.in +++ b/etc/modules-load.d/zfs.conf.in @@ -1,3 +1,3 @@ -# Always load kernel modules at boot. The default behavior is to load the -# kernel modules in the zfs-import-*.service or when blkid(8) detects a pool. +# The default behavior is to allow udev to load the kernel modules on demand. +# Uncomment the following line to unconditionally load them at boot. #zfs diff --git a/etc/systemd/system/zfs-import-cache.service.in b/etc/systemd/system/zfs-import-cache.service.in index 23c725d713..44c26802b1 100644 --- a/etc/systemd/system/zfs-import-cache.service.in +++ b/etc/systemd/system/zfs-import-cache.service.in @@ -12,7 +12,6 @@ ConditionPathExists=@sysconfdir@/zfs/zpool.cache [Service] Type=oneshot RemainAfterExit=yes -ExecStartPre=-/sbin/modprobe zfs ExecStart=@sbindir@/zpool import -c @sysconfdir@/zfs/zpool.cache -aN ExecStartPost=/bin/bash -c "/bin/systemctl set-environment BOOTFS=$(@sbindir@/zpool list -H -o bootfs | /bin/awk '$1 != \"-\" {print; exit}')" diff --git a/etc/systemd/system/zfs-import-scan.service.in b/etc/systemd/system/zfs-import-scan.service.in index 851d96cbc9..44a50a76b6 100644 --- a/etc/systemd/system/zfs-import-scan.service.in +++ b/etc/systemd/system/zfs-import-scan.service.in @@ -11,7 +11,6 @@ ConditionPathExists=!@sysconfdir@/zfs/zpool.cache [Service] Type=oneshot RemainAfterExit=yes -ExecStartPre=-/sbin/modprobe zfs ExecStart=@sbindir@/zpool import -aN -o cachefile=none ExecStartPost=/bin/bash -c "/bin/systemctl set-environment BOOTFS=$(@sbindir@/zpool list -H -o bootfs | /bin/awk '$1 != \"-\" {print; exit}')" diff --git a/etc/systemd/system/zfs-zed.service.in b/etc/systemd/system/zfs-zed.service.in index 3175150030..f4313625ee 100644 --- a/etc/systemd/system/zfs-zed.service.in +++ b/etc/systemd/system/zfs-zed.service.in @@ -1,7 +1,6 @@ [Unit] Description=ZFS Event Daemon (zed) Documentation=man:zed(8) -After=zfs-import.target [Service] ExecStart=@sbindir@/zed -F diff --git a/lib/libzfs/libzfs_util.c b/lib/libzfs/libzfs_util.c index ee8d09b118..6b9cfc2720 100644 --- a/lib/libzfs/libzfs_util.c +++ b/lib/libzfs/libzfs_util.c @@ -965,13 +965,14 @@ libzfs_load_module(const char *module) load = 0; } - if (load && libzfs_run_process("/sbin/modprobe", argv, 0)) - return (ENOEXEC); - } + if (load) { + if (libzfs_run_process("/sbin/modprobe", argv, 0)) + return (ENOEXEC); - /* Module loading is synchronous it must be available */ - if (!libzfs_module_loaded(module)) - return (ENXIO); + if (!libzfs_module_loaded(module)) + return (ENXIO); + } + } /* * Device creation by udev is asynchronous and waiting may be diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index 994b3af36c..92bdec4b96 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -6947,11 +6947,14 @@ static const struct file_operations zfsdev_fops = { }; static struct miscdevice zfs_misc = { - .minor = MISC_DYNAMIC_MINOR, + .minor = ZFS_MINOR, .name = ZFS_DRIVER, .fops = &zfsdev_fops, }; +MODULE_ALIAS_MISCDEV(ZFS_MINOR); +MODULE_ALIAS("devname:zfs"); + static int zfs_attach(void) { @@ -6962,12 +6965,24 @@ zfs_attach(void) zfsdev_state_list->zs_minor = -1; error = misc_register(&zfs_misc); - if (error != 0) { - printk(KERN_INFO "ZFS: misc_register() failed %d\n", error); - return (error); + if (error == -EBUSY) { + /* + * Fallback to dynamic minor allocation in the event of a + * collision with a reserved minor in linux/miscdevice.h. + * In this case the kernel modules must be manually loaded. + */ + printk(KERN_INFO "ZFS: misc_register() with static minor %d " + "failed %d, retrying with MISC_DYNAMIC_MINOR\n", + ZFS_MINOR, error); + + zfs_misc.minor = MISC_DYNAMIC_MINOR; + error = misc_register(&zfs_misc); } - return (0); + if (error) + printk(KERN_INFO "ZFS: misc_register() failed %d\n", error); + + return (error); } static void diff --git a/rpm/generic/zfs.spec.in b/rpm/generic/zfs.spec.in index 27a2424df8..52dc80a4ef 100644 --- a/rpm/generic/zfs.spec.in +++ b/rpm/generic/zfs.spec.in @@ -284,6 +284,15 @@ fi %endif exit 0 +# On RHEL/CentOS 7 the static nodes aren't refreshed by default after +# installing a package. This is the default behavior for Fedora. +%posttrans +%if 0%{?rhel} == 7 || 0%{?centos} == 7 +systemctl restart kmod-static-nodes +systemctl restart systemd-tmpfiles-setup-dev +udevadm trigger +%endif + %preun %if 0%{?_systemd} %if 0%{?systemd_preun:1} diff --git a/udev/rules.d/90-zfs.rules.in b/udev/rules.d/90-zfs.rules.in index 855c154040..82f8cf1cf5 100644 --- a/udev/rules.d/90-zfs.rules.in +++ b/udev/rules.d/90-zfs.rules.in @@ -7,6 +7,6 @@ ENV{ID_FS_TYPE}=="zfs_member", RUN+="/sbin/modprobe zfs" KERNEL=="null", SYMLINK+="root" SYMLINK=="null", SYMLINK+="root" -SUBSYSTEM=="misc", KERNEL=="zfs", MODE="0666" +KERNEL=="zfs", MODE="0666", OPTIONS+="static_node=zfs" LABEL="zfs_end"