-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Wait in libzfs_init() for the /dev/zfs device #3431
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,4 +9,4 @@ ConditionPathExists=!@sysconfdir@/zfs/zpool.cache | |
[Service] | ||
Type=oneshot | ||
RemainAfterExit=yes | ||
ExecStart=@sbindir@/zpool import -d /dev/disk/by-id -aN | ||
ExecStart=/sbin/modprobe zfs && @sbindir@/zpool import -d /dev/disk/by-id -aN | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest adding a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than sneak that in here. Let's tackle that in a separate patch because why that's desirable is going to need a fair bit of explanation and it would be a change in existing behavior. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is fine. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -58,6 +58,31 @@ libzfs_errno(libzfs_handle_t *hdl) | |
return (hdl->libzfs_error); | ||
} | ||
|
||
const char * | ||
libzfs_error_init(int error) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The name There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I oped for this to be consistent with the existing |
||
{ | ||
switch (error) { | ||
case ENXIO: | ||
return (dgettext(TEXT_DOMAIN, "The ZFS modules are not " | ||
"loaded.\nTry running '/sbin/modprobe zfs' as root " | ||
"to load them.\n")); | ||
case ENOENT: | ||
return (dgettext(TEXT_DOMAIN, "The /dev/zfs device is " | ||
"missing and must be created.\nTry running 'udevadm " | ||
"trigger' as root to create it.\n")); | ||
case ENOEXEC: | ||
return (dgettext(TEXT_DOMAIN, "The ZFS modules cannot be " | ||
"auto-loaded.\nTry running '/sbin/modprobe zfs' as " | ||
"root to manually load them.\n")); | ||
case EACCES: | ||
return (dgettext(TEXT_DOMAIN, "Permission denied the " | ||
"ZFS utilities must be run as root.\n")); | ||
default: | ||
return (dgettext(TEXT_DOMAIN, "Failed to initialize the " | ||
"libzfs library.\n")); | ||
} | ||
} | ||
|
||
const char * | ||
libzfs_error_action(libzfs_handle_t *hdl) | ||
{ | ||
|
@@ -628,7 +653,7 @@ int | |
libzfs_run_process(const char *path, char *argv[], int flags) | ||
{ | ||
pid_t pid; | ||
int rc, devnull_fd; | ||
int error, devnull_fd; | ||
|
||
pid = vfork(); | ||
if (pid == 0) { | ||
|
@@ -650,9 +675,9 @@ libzfs_run_process(const char *path, char *argv[], int flags) | |
} else if (pid > 0) { | ||
int status; | ||
|
||
while ((rc = waitpid(pid, &status, 0)) == -1 && | ||
while ((error = waitpid(pid, &status, 0)) == -1 && | ||
errno == EINTR); | ||
if (rc < 0 || !WIFEXITED(status)) | ||
if (error < 0 || !WIFEXITED(status)) | ||
return (-1); | ||
|
||
return (WEXITSTATUS(status)); | ||
|
@@ -661,26 +686,85 @@ libzfs_run_process(const char *path, char *argv[], int flags) | |
return (-1); | ||
} | ||
|
||
int | ||
/* | ||
* Verify the required ZFS_DEV device is available and optionally attempt | ||
* to load the ZFS modules. Under normal circumstances the modules | ||
* should already have been loaded by some external mechanism. | ||
* | ||
* Environment variables: | ||
* - ZFS_MODULE_LOADING="YES|yes|ON|on" - Attempt to load modules. | ||
* - ZFS_MODULE_TIMEOUT="<seconds>" - Seconds to wait for ZFS_DEV | ||
*/ | ||
static int | ||
libzfs_load_module(const char *module) | ||
{ | ||
char *argv[4] = {"/sbin/modprobe", "-q", (char *)module, (char *)0}; | ||
char *load_str, *timeout_str; | ||
long timeout = 10; /* seconds */ | ||
long busy_timeout = 10; /* milliseconds */ | ||
int load = 0, fd; | ||
hrtime_t start; | ||
|
||
/* Optionally request module loading */ | ||
if (!libzfs_module_loaded(module)) { | ||
load_str = getenv("ZFS_MODULE_LOADING"); | ||
if (load_str) { | ||
if (!strncasecmp(load_str, "YES", strlen("YES")) || | ||
!strncasecmp(load_str, "ON", strlen("ON"))) | ||
load = 1; | ||
else | ||
load = 0; | ||
} | ||
|
||
if (libzfs_module_loaded(module)) | ||
return (0); | ||
if (load && libzfs_run_process("/sbin/modprobe", argv, 0)) | ||
return (ENOEXEC); | ||
} | ||
|
||
/* Module loading is synchronous it must be available */ | ||
if (!libzfs_module_loaded(module)) | ||
return (ENXIO); | ||
|
||
/* | ||
* Device creation by udev is asynchronous and waiting may be | ||
* required. Busy wait for 10ms and then fall back to polling every | ||
* 10ms for the allowed timeout (default 10s, max 10m). This is | ||
* done to optimize for the common case where the device is | ||
* immediately available and to avoid penalizing the possible | ||
* case where udev is slow or unable to create the device. | ||
*/ | ||
timeout_str = getenv("ZFS_MODULE_TIMEOUT"); | ||
if (timeout_str) { | ||
timeout = strtol(timeout_str, NULL, 0); | ||
timeout = MAX(MIN(timeout, (10 * 60)), 0); /* 0 <= N <= 600 */ | ||
} | ||
|
||
return (libzfs_run_process("/sbin/modprobe", argv, 0)); | ||
start = gethrtime(); | ||
do { | ||
fd = open(ZFS_DEV, O_RDWR); | ||
if (fd >= 0) { | ||
(void) close(fd); | ||
return (0); | ||
} else if (errno != ENOENT) { | ||
return (errno); | ||
} else if (NSEC2MSEC(gethrtime() - start) < busy_timeout) { | ||
sched_yield(); | ||
} else { | ||
usleep(10 * MILLISEC); | ||
} | ||
} while (NSEC2MSEC(gethrtime() - start) < (timeout * MILLISEC)); | ||
|
||
return (ENOENT); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you think of structuring the loop like this? start = gethrtime();
do {
fd = open(ZFS_DEV, O_RDWR);
if (fd >= 0) {
(void) close(fd);
return (0);
} else if (errno != ENOENT) {
return (errno);
} else if (NSEC2MSEC(gethrtime() - start) < busy_timeout) {
sched_yield();
} else {
usleep(10 * MILLISEC);
}
} while (NSEC2MSEC(gethrtime() - start) < (timeout * MILLISEC));
return (ENOENT); The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can get on board with this. Originally @ryao has structured this as a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A |
||
|
||
libzfs_handle_t * | ||
libzfs_init(void) | ||
{ | ||
libzfs_handle_t *hdl; | ||
int error; | ||
|
||
if (libzfs_load_module("zfs") != 0) { | ||
(void) fprintf(stderr, gettext("Failed to load ZFS module " | ||
"stack.\nLoad the module manually by running " | ||
"'insmod <location>/zfs.ko' as root.\n")); | ||
error = libzfs_load_module(ZFS_DRIVER); | ||
if (error) { | ||
errno = error; | ||
return (NULL); | ||
} | ||
|
||
|
@@ -689,13 +773,6 @@ libzfs_init(void) | |
} | ||
|
||
if ((hdl->libzfs_fd = open(ZFS_DEV, O_RDWR)) < 0) { | ||
(void) fprintf(stderr, gettext("Unable to open %s: %s.\n"), | ||
ZFS_DEV, strerror(errno)); | ||
if (errno == ENOENT) | ||
(void) fprintf(stderr, | ||
gettext("Verify the ZFS module stack is " | ||
"loaded by running '/sbin/modprobe zfs'.\n")); | ||
|
||
free(hdl); | ||
return (NULL); | ||
} | ||
|
@@ -706,8 +783,6 @@ libzfs_init(void) | |
if ((hdl->libzfs_mnttab = fopen(MNTTAB, "r")) == NULL) { | ||
#endif | ||
(void) close(hdl->libzfs_fd); | ||
(void) fprintf(stderr, | ||
gettext("mtab is not present at %s.\n"), MNTTAB); | ||
free(hdl); | ||
return (NULL); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest adding a
-F
here to match the behavior when the module auto-imports the pool itself to fix #3434.