Add kernel module auto-loading

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 <f.gruenbichler@proxmox.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
TEST_ZIMPORT_SKIP="yes"
Closes #7287
This commit is contained in:
Brian Behlendorf 2018-03-13 10:45:55 -07:00 committed by GitHub
parent 02638a30ef
commit a6cc97566c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 66 additions and 17 deletions

View File

@ -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.])
])
])

View File

@ -6,6 +6,7 @@ AC_DEFUN([ZFS_AC_CONFIG_KERNEL], [
ZFS_AC_SPL ZFS_AC_SPL
ZFS_AC_QAT ZFS_AC_QAT
ZFS_AC_TEST_MODULE ZFS_AC_TEST_MODULE
ZFS_AC_KERNEL_MISC_MINOR
ZFS_AC_KERNEL_OBJTOOL ZFS_AC_KERNEL_OBJTOOL
ZFS_AC_KERNEL_CONFIG ZFS_AC_KERNEL_CONFIG
ZFS_AC_KERNEL_DECLARE_EVENT_CLASS ZFS_AC_KERNEL_DECLARE_EVENT_CLASS

View File

@ -1,3 +1,3 @@
# Always load kernel modules at boot. The default behavior is to load the # The default behavior is to allow udev to load the kernel modules on demand.
# kernel modules in the zfs-import-*.service or when blkid(8) detects a pool. # Uncomment the following line to unconditionally load them at boot.
#zfs #zfs

View File

@ -12,7 +12,6 @@ ConditionPathExists=@sysconfdir@/zfs/zpool.cache
[Service] [Service]
Type=oneshot Type=oneshot
RemainAfterExit=yes RemainAfterExit=yes
ExecStartPre=-/sbin/modprobe zfs
ExecStart=@sbindir@/zpool import -c @sysconfdir@/zfs/zpool.cache -aN 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}')" ExecStartPost=/bin/bash -c "/bin/systemctl set-environment BOOTFS=$(@sbindir@/zpool list -H -o bootfs | /bin/awk '$1 != \"-\" {print; exit}')"

View File

@ -11,7 +11,6 @@ ConditionPathExists=!@sysconfdir@/zfs/zpool.cache
[Service] [Service]
Type=oneshot Type=oneshot
RemainAfterExit=yes RemainAfterExit=yes
ExecStartPre=-/sbin/modprobe zfs
ExecStart=@sbindir@/zpool import -aN -o cachefile=none 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}')" ExecStartPost=/bin/bash -c "/bin/systemctl set-environment BOOTFS=$(@sbindir@/zpool list -H -o bootfs | /bin/awk '$1 != \"-\" {print; exit}')"

View File

@ -1,7 +1,6 @@
[Unit] [Unit]
Description=ZFS Event Daemon (zed) Description=ZFS Event Daemon (zed)
Documentation=man:zed(8) Documentation=man:zed(8)
After=zfs-import.target
[Service] [Service]
ExecStart=@sbindir@/zed -F ExecStart=@sbindir@/zed -F

View File

@ -965,13 +965,14 @@ libzfs_load_module(const char *module)
load = 0; load = 0;
} }
if (load && libzfs_run_process("/sbin/modprobe", argv, 0)) if (load) {
return (ENOEXEC); if (libzfs_run_process("/sbin/modprobe", argv, 0))
} return (ENOEXEC);
/* Module loading is synchronous it must be available */ if (!libzfs_module_loaded(module))
if (!libzfs_module_loaded(module)) return (ENXIO);
return (ENXIO); }
}
/* /*
* Device creation by udev is asynchronous and waiting may be * Device creation by udev is asynchronous and waiting may be

View File

@ -6947,11 +6947,14 @@ static const struct file_operations zfsdev_fops = {
}; };
static struct miscdevice zfs_misc = { static struct miscdevice zfs_misc = {
.minor = MISC_DYNAMIC_MINOR, .minor = ZFS_MINOR,
.name = ZFS_DRIVER, .name = ZFS_DRIVER,
.fops = &zfsdev_fops, .fops = &zfsdev_fops,
}; };
MODULE_ALIAS_MISCDEV(ZFS_MINOR);
MODULE_ALIAS("devname:zfs");
static int static int
zfs_attach(void) zfs_attach(void)
{ {
@ -6962,12 +6965,24 @@ zfs_attach(void)
zfsdev_state_list->zs_minor = -1; zfsdev_state_list->zs_minor = -1;
error = misc_register(&zfs_misc); error = misc_register(&zfs_misc);
if (error != 0) { if (error == -EBUSY) {
printk(KERN_INFO "ZFS: misc_register() failed %d\n", error); /*
return (error); * 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 static void

View File

@ -284,6 +284,15 @@ fi
%endif %endif
exit 0 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 %preun
%if 0%{?_systemd} %if 0%{?_systemd}
%if 0%{?systemd_preun:1} %if 0%{?systemd_preun:1}

View File

@ -7,6 +7,6 @@ ENV{ID_FS_TYPE}=="zfs_member", RUN+="/sbin/modprobe zfs"
KERNEL=="null", SYMLINK+="root" KERNEL=="null", SYMLINK+="root"
SYMLINK=="null", SYMLINK+="root" SYMLINK=="null", SYMLINK+="root"
SUBSYSTEM=="misc", KERNEL=="zfs", MODE="0666" KERNEL=="zfs", MODE="0666", OPTIONS+="static_node=zfs"
LABEL="zfs_end" LABEL="zfs_end"