Call exportfs -v once for NFS shares.
At the moment we call exportfs -v every time we check whether an NFS share is active. This happens every time you run a zfs or zpool command, making them extremely slow when you have a lot of exports. The time taken is approx O(n2) of the number of shares. This commit stores the output from exportfs -v in a temporary file and use this to speed up subsequent accesses. This mechanism is still too slow - if you have tens of thousands of NFS shares it will still be painful running ANY zfs/zpool command. Signed-off-by: Gunnar Beutner <gunnar@beutner.name> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Closes #341
This commit is contained in:
parent
77999e804f
commit
5333eb0b3b
|
@ -36,6 +36,12 @@
|
||||||
static sa_fstype_t *nfs_fstype;
|
static sa_fstype_t *nfs_fstype;
|
||||||
static boolean_t nfs_available;
|
static boolean_t nfs_available;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* nfs_exportfs_temp_fd refers to a temporary copy of the output
|
||||||
|
* from exportfs -v.
|
||||||
|
*/
|
||||||
|
static int nfs_exportfs_temp_fd = -1;
|
||||||
|
|
||||||
typedef int (*nfs_shareopt_callback_t)(const char *opt, const char *value,
|
typedef int (*nfs_shareopt_callback_t)(const char *opt, const char *value,
|
||||||
void *cookie);
|
void *cookie);
|
||||||
|
|
||||||
|
@ -493,111 +499,62 @@ nfs_validate_shareopts(const char *shareopts)
|
||||||
return SA_OK;
|
return SA_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO: Rather than invoking "exportfs -v" once for each share we should only
|
|
||||||
* call it once for all shares.
|
|
||||||
*/
|
|
||||||
static boolean_t
|
static boolean_t
|
||||||
is_share_active(sa_share_impl_t impl_share)
|
is_share_active(sa_share_impl_t impl_share)
|
||||||
{
|
{
|
||||||
pid_t pid;
|
|
||||||
int rc, status;
|
|
||||||
int pipes[2];
|
|
||||||
FILE *exportfs_stdout;
|
|
||||||
char line[512];
|
char line[512];
|
||||||
char *tab, *cur;
|
char *tab, *cur;
|
||||||
boolean_t share_active = B_FALSE;
|
FILE *nfs_exportfs_temp_fp;
|
||||||
|
|
||||||
if (pipe(pipes) < 0)
|
if (nfs_exportfs_temp_fd < 0)
|
||||||
return B_FALSE;
|
return B_FALSE;
|
||||||
|
|
||||||
pid = fork();
|
nfs_exportfs_temp_fp = fdopen(dup(nfs_exportfs_temp_fd), "r");
|
||||||
|
|
||||||
if (pid < 0)
|
if (nfs_exportfs_temp_fp == NULL ||
|
||||||
|
fseek(nfs_exportfs_temp_fp, 0, SEEK_SET) < 0) {
|
||||||
|
fclose(nfs_exportfs_temp_fp);
|
||||||
return B_FALSE;
|
return B_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
if (pid > 0) {
|
while (fgets(line, sizeof(line), nfs_exportfs_temp_fp) != NULL) {
|
||||||
share_active = B_FALSE;
|
/*
|
||||||
|
* exportfs uses separate lines for the share path
|
||||||
|
* and the export options when the share path is longer
|
||||||
|
* than a certain amount of characters; this ignores
|
||||||
|
* the option lines
|
||||||
|
*/
|
||||||
|
if (line[0] == '\t')
|
||||||
|
continue;
|
||||||
|
|
||||||
exportfs_stdout = fdopen(pipes[0], "r");
|
tab = strchr(line, '\t');
|
||||||
close(pipes[1]);
|
|
||||||
|
|
||||||
while (fgets(line, sizeof(line), exportfs_stdout) != NULL) {
|
|
||||||
if (share_active)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
|
if (tab != NULL) {
|
||||||
|
*tab = '\0';
|
||||||
|
cur = tab - 1;
|
||||||
|
} else {
|
||||||
/*
|
/*
|
||||||
* exportfs uses separate lines for the share path
|
* there's no tab character, which means the
|
||||||
* and the export options when the share path is longer
|
* NFS options are on a separate line; we just
|
||||||
* than a certain amount of characters; this ignores
|
* need to remove the new-line character
|
||||||
* the option lines
|
* at the end of the line
|
||||||
*/
|
*/
|
||||||
if (line[0] == '\t')
|
cur = line + strlen(line) - 1;
|
||||||
continue;
|
|
||||||
|
|
||||||
tab = strchr(line, '\t');
|
|
||||||
|
|
||||||
if (tab != NULL) {
|
|
||||||
*tab = '\0';
|
|
||||||
cur = tab - 1;
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* there's no tab character, which means the
|
|
||||||
* NFS options are on a separate line; we just
|
|
||||||
* need to remove the new-line character
|
|
||||||
* at the end of the line
|
|
||||||
*/
|
|
||||||
cur = line + strlen(line) - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* remove trailing spaces and new-line characters */
|
|
||||||
while (cur >= line &&
|
|
||||||
(*cur == ' ' || *cur == '\n'))
|
|
||||||
*cur-- = '\0';
|
|
||||||
|
|
||||||
if (strcmp(line, impl_share->sharepath) == 0) {
|
|
||||||
share_active = B_TRUE;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We can't break here just yet, otherwise
|
|
||||||
* exportfs might die due to write() failing
|
|
||||||
* with EPIPE once we've closed the pipe file
|
|
||||||
* descriptor - we need to read until EOF
|
|
||||||
* occurs on the stdout pipe.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(exportfs_stdout);
|
/* remove trailing spaces and new-line characters */
|
||||||
|
while (cur >= line && (*cur == ' ' || *cur == '\n'))
|
||||||
|
*cur-- = '\0';
|
||||||
|
|
||||||
while ((rc = waitpid(pid, &status, 0)) <= 0 && errno == EINTR)
|
if (strcmp(line, impl_share->sharepath) == 0) {
|
||||||
; /* empty loop body */
|
fclose(nfs_exportfs_temp_fp);
|
||||||
|
return B_TRUE;
|
||||||
if (rc <= 0)
|
}
|
||||||
return B_FALSE;
|
|
||||||
|
|
||||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
|
||||||
return B_FALSE;
|
|
||||||
|
|
||||||
return share_active;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* child */
|
fclose(nfs_exportfs_temp_fp);
|
||||||
|
|
||||||
/* exportfs -v */
|
return B_FALSE;
|
||||||
|
|
||||||
close(pipes[0]);
|
|
||||||
|
|
||||||
if (dup2(pipes[1], STDOUT_FILENO) < 0)
|
|
||||||
exit(1);
|
|
||||||
|
|
||||||
rc = execlp("/usr/sbin/exportfs", "exportfs", "-v", NULL);
|
|
||||||
|
|
||||||
if (rc < 0) {
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
exit(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -654,11 +611,40 @@ static const sa_share_ops_t nfs_shareops = {
|
||||||
.clear_shareopts = nfs_clear_shareopts,
|
.clear_shareopts = nfs_clear_shareopts,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* nfs_check_exportfs() checks that the exportfs command runs
|
||||||
|
* and also maintains a temporary copy of the output from
|
||||||
|
* exportfs -v.
|
||||||
|
* To update this temporary copy simply call this function again.
|
||||||
|
*
|
||||||
|
* TODO : Use /var/lib/nfs/etab instead of our private copy.
|
||||||
|
* But must implement locking to prevent concurrent access.
|
||||||
|
*
|
||||||
|
* TODO : The temporary file descriptor is never closed since
|
||||||
|
* there is no libshare_nfs_fini() function.
|
||||||
|
*/
|
||||||
static int
|
static int
|
||||||
nfs_check_exportfs(void)
|
nfs_check_exportfs(void)
|
||||||
{
|
{
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
int rc, status, null_fd;
|
int rc, status;
|
||||||
|
static char nfs_exportfs_tempfile[] = "/tmp/exportfs.XXXXXX";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Close any existing temporary copies of output from exportfs.
|
||||||
|
* We have already called unlink() so file will be deleted.
|
||||||
|
*/
|
||||||
|
if (nfs_exportfs_temp_fd >= 0)
|
||||||
|
close(nfs_exportfs_temp_fd);
|
||||||
|
|
||||||
|
nfs_exportfs_temp_fd = mkstemp(nfs_exportfs_tempfile);
|
||||||
|
|
||||||
|
if (nfs_exportfs_temp_fd < 0)
|
||||||
|
return SA_SYSTEM_ERR;
|
||||||
|
|
||||||
|
unlink(nfs_exportfs_tempfile);
|
||||||
|
|
||||||
|
fcntl(nfs_exportfs_temp_fd, F_SETFD, FD_CLOEXEC);
|
||||||
|
|
||||||
pid = fork();
|
pid = fork();
|
||||||
|
|
||||||
|
@ -680,14 +666,12 @@ nfs_check_exportfs(void)
|
||||||
|
|
||||||
/* child */
|
/* child */
|
||||||
|
|
||||||
null_fd = open("/dev/null", O_RDONLY);
|
/* exportfs -v */
|
||||||
|
|
||||||
if (null_fd < 0 || dup2(null_fd, 1) < 0 || dup2(null_fd, 2) < 0)
|
if (dup2(nfs_exportfs_temp_fd, STDOUT_FILENO) < 0)
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
||||||
close(null_fd);
|
rc = execlp("/usr/sbin/exportfs", "exportfs", "-v", NULL);
|
||||||
|
|
||||||
rc = execlp("/usr/sbin/exportfs", "exportfs", NULL);
|
|
||||||
|
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
Loading…
Reference in New Issue