Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 6 Feb 2013 12:11:13 -0500
From:      John Baldwin <jhb@freebsd.org>
To:        src-committers@freebsd.org
Cc:        svn-src-head@freebsd.org, svn-src-all@freebsd.org
Subject:   Re: svn commit: r246417 - in head/sys: fs/nfs kern nfsclient sys
Message-ID:  <201302061211.14184.jhb@freebsd.org>
In-Reply-To: <201302061706.r16H6qMo088889@svn.freebsd.org>
References:  <201302061706.r16H6qMo088889@svn.freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
On Wednesday, February 06, 2013 12:06:52 pm John Baldwin wrote:
> Author: jhb
> Date: Wed Feb  6 17:06:51 2013
> New Revision: 246417
> URL: http://svnweb.freebsd.org/changeset/base/246417
> 
> Log:
>   Rework the handling of stop signals in the NFS client.  The changes in
>   195702, 195703, and 195821 prevented a thread from suspending while holding
>   locks inside of NFS by forcing the thread to fail sleeps with EINTR or
>   ERESTART but defer the thread suspension to the user boundary.  However,
>   this had the effect that stopping a process during an NFS request could
>   abort the request and trigger EINTR errors that were visible to userland
>   processes (previously the thread would have suspended and completed the
>   request once it was resumed).
>   
>   This change instead effectively masks stop signals while in the NFS client.
>   It uses the existing TDF_SBDRY flag to effect this since SIGSTOP cannot
>   be masked directly.  Also, instead of setting PBDRY on individual sleeps,
>   the NFS client now sets the TDF_SBDRY flag around each NFS request and
>   stop signals are masked for all sleeps during that region (the previous
>   change missed sleeps in lockmgr locks).  The end result is that stop
>   signals sent to threads performing an NFS request are completely
>   ignored until after the NFS request has finished processing and the
>   thread prepares to return to userland.  This restores the behavior of
>   stop signals being transparent to userland processes while still
>   preventing threads from suspending while holding NFS locks.
>   
>   Reviewed by:	kib
>   MFC after:	1 month

I have a test case (included below).  You give it a path to a file on an
interruptible NFS mount as the sole argument.  In the broken case you will
see lots of reads fail with EINTR.

#include <sys/types.h>
#include <sys/stat.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static void
usage(void)
{

	fprintf(stderr, "Usage: nfsintr <path>\n");
	exit(1);
}

static volatile sig_atomic_t info;

static void
child_info_handler(int sig)
{

	info = 1;
}

/*
 * Check to see if STOP/CONT signals affect I/O.  One process
 * continually opens a file with O_DIRECT and reads it while another
 * process keeps pausing the first with SIGSTOP.
 */
static void
child(const char *path)
{
	char buf[128 * 1024];
	struct stat sb;
	ssize_t nread;
	off_t off;
	int fd, shorts;

	if (signal(SIGINFO, child_info_handler) == SIG_ERR)
		err(1, "signal(SIGINFO) (child)");
	shorts = 0;
	while (getppid() != 1) {
		fd = open(path, O_RDONLY | O_DIRECT);
		while (fd < 0) {
			if (errno == EINTR)
				warn("open(%s)", path);
			else
				err(1, "open(%s)", path);
		}
		while (fstat(fd, &sb) < 0) {
			if (errno == EINTR)
				warn("fstat");
			else
				err(1, "fstat");
		}
		for (;;) {
			if (info) {
				if (shorts > 0)
					printf("nfsintr: %d short reads\n",
					    shorts);
				shorts = 0;
				info = 0;
			}
			nread = read(fd, buf, sizeof(buf));
			if (nread < 0) {
				if (errno == EINTR)
					warn("read");
				else
					err(1, "read");
				continue;
			}
			if (nread == 0)
				break;
			if (nread == sizeof(buf))
				continue;
			if (nread < (ssize_t)sizeof(buf)) {
				off = lseek(fd, SEEK_CUR, 0);
				if (off < 0)
					err(1, "lseek");
				if (off == sb.st_size)
					break;
				/*
				 * These happen a lot.
				warnx("short read: %zd", nread);
				 */
				shorts++;
				continue;
			}
		}
		close(fd);
	}
}

static void
parent(pid_t pid)
{

	for (;;) {
		if (kill(pid, SIGSTOP) < 0) {
			if (errno == ESRCH)
				return;
			err(1, "kill(SIGSTOP)");
		}
		usleep(500);
		if (kill(pid, SIGCONT) < 0) {
			if (errno == ESRCH)
				return;
			err(1, "kill(SIGCONT)");
		}
		usleep(500);
	}
}

int
main(int ac, char **av)
{
	pid_t pid;

	if (ac != 2)
		usage();
	if (access(av[1], R_OK) < 0)
		err(1, "Unable to access file %s", av[1]);
	if (signal(SIGCHLD, SIG_IGN) == SIG_ERR)
		err(1, "signal(SIGCHLD)");
	pid = fork();
	if (pid < 0)
		err(1, "fork");
	if (pid == 0)
		child(av[1]);
	else
		parent(pid);
	return (0);
}

-- 
John Baldwin



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201302061211.14184.jhb>