| Summary: | "tail -f file" is a CPU hog when file is on an NFS volume | ||
|---|---|---|---|
| Product: | Base System | Reporter: | Andre Albsmeier <Andre.Albsmeier> |
| Component: | kern | Assignee: | freebsd-bugs (Nobody) <bugs> |
| Status: | Closed FIXED | ||
| Severity: | Affects Only Me | ||
| Priority: | Normal | ||
| Version: | 4.2-STABLE | ||
| Hardware: | Any | ||
| OS: | Any | ||
State Changed From-To: open->closed Fixed with latest kqueue update. |
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