Bug 24223

Summary: "tail -f file" is a CPU hog when file is on an NFS volume
Product: Base System Reporter: Andre Albsmeier <Andre.Albsmeier>
Component: kernAssignee: freebsd-bugs (Nobody) <bugs>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: 4.2-STABLE   
Hardware: Any   
OS: Any   

Description Andre Albsmeier 2001-01-10 14:10:01 UTC
If "tail -f file" is executed for a file which resides on an NFS mounted
volume it consumes all CPU time.

Fix: 

unknown. tail spins around in the code segment below (forward.c).
The switch statement is called repeatedly with action == USE_KQUEUE.


	for (;;) {
		while ((ch = getc(fp)) != EOF)
			if (putchar(ch) == EOF)
				oerr();
		if (ferror(fp)) {
			ierr();
			return;
		}
		(void)fflush(stdout);
		if (! fflag)
			break;
		clearerr(fp);

		switch (action) {
		case ADD_EVENTS: {
			int n = 0;
			struct timespec ts = { 0, 0 };

			if (Fflag && fileno(fp) != STDIN_FILENO) {
				ev[n].ident = fileno(fp);
				ev[n].filter = EVFILT_VNODE;
				ev[n].flags = EV_ADD | EV_ENABLE | EV_CLEAR;
				ev[n].fflags = NOTE_DELETE | NOTE_RENAME;
				n++;
			}
			ev[n].ident = fileno(fp);
			ev[n].filter = EVFILT_READ;
			ev[n].flags = EV_ADD | EV_ENABLE;
			n++;

			if (kevent(kq, ev, n, NULL, 0, &ts) < 0) {
				close(kq);
				kq = -1;
				action = USE_SLEEP;
			} else {
				action = USE_KQUEUE;
			}
			break;
		}

		case USE_KQUEUE:
			if (kevent(kq, NULL, 0, ev, 1, NULL) < 0)
				err(1, "kevent");

			if (ev->filter == EVFILT_VNODE) {
				/* file was rotated, wait until it reappears */
				action = USE_SLEEP;
			} else if (ev->data < 0) {
				/* file shrank, reposition to end */
				if (fseek(fp, 0L, SEEK_END) == -1) {
					ierr();
					return;
				}
			}
			break;

		case USE_SLEEP:
                	(void) usleep(250000);
	                clearerr(fp);

			if (Fflag && fileno(fp) != STDIN_FILENO &&
			    stat(fname, &sb2) != -1) {
				if (sb2.st_ino != sbp->st_ino ||
				    sb2.st_dev != sbp->st_dev ||
				    sb2.st_rdev != sbp->st_rdev ||
				    sb2.st_nlink == 0) {
					fp = freopen(fname, "r", fp);
					if (fp == NULL) {
						ierr();
						break;
					}
					*sbp = sb2;
					if (kq != -1)
						action = ADD_EVENTS;
				}
			}
			break;
		}
	}
}

/*
 * rlines -- display the last offset lines of the file.
 */
static void
rlines(fp, off, sbp)
	FILE *fp;
	long off;
	struct stat *sbp;
{
	register off_t size;
	register char *p;
	char *start;

	if (!(size = sbp->st_size))
		return;

	if (size > SIZE_T_MAX) {
		errno = EFBIG;
		ierr();
		return;
	}

	if ((start = mmap(NULL, (size_t)size,
	    PROT_READ, MAP_SHARED, fileno(fp), (off_t)0)) == MAP_FAILED) {
		ierr();
		return;
	}

	/* Last char is special, ignore whether newline or not. */
	for (p = start + size - 1; --size;)
		if (*--p == '\n' && !--off) {
			++p;
			break;
		}

	/* Set the file pointer to reflect the length displayed. */
	size = sbp->st_size - size;
	WR(p, size);
	if (fseek(fp, (long)sbp->st_size, SEEK_SET) == -1) {
		ierr();
		return;
	}
	if (munmap(start, (size_t)sbp->st_size)) {
		ierr();
		return;
	}
}
How-To-Repeat: 
mount some-server:/filesystem /mnt
cat blah > /mnt/file
tail -f /mnt/file

run top in another window
Comment 1 jlemon freebsd_committer freebsd_triage 2001-03-01 16:18:51 UTC
State Changed
From-To: open->closed

Fixed with latest kqueue update.