summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobias Klauser <tklauser@distanz.ch>2007-09-18 18:30:19 +0200
committerTobias Klauser <tklauser@xenon.tklauser.home>2007-09-18 18:30:19 +0200
commitf4c22bb16fc842f66060d4f7a79338c900f8dfb9 (patch)
tree805672c0837da477b6938654c05dba11396bf03d
parent6b1ce4b2a1eb356541bbc5ed661344b30f7bab85 (diff)
inotail.c: Implement pipe tailing lines from begin
Use a general function for lines and bytes to prevent duplicating code.
-rw-r--r--inotail.c62
1 files changed, 57 insertions, 5 deletions
diff --git a/inotail.c b/inotail.c
index c455fd0..7ed5b31 100644
--- a/inotail.c
+++ b/inotail.c
@@ -239,14 +239,63 @@ static off_t bytes_to_offset(struct file_struct *f, unsigned long n_bytes)
return offset;
}
-/* For now more or less a copy of pipe_lines() from coreutils tail */
-static ssize_t tail_pipe_lines(struct file_struct *f, unsigned long n_lines)
+static int tail_pipe_from_begin(struct file_struct *f, unsigned long n_units, const char mode)
+{
+ int bytes_read = 0;
+ char buf[BUFSIZ];
+
+ n_units--;
+
+ while (n_units > 0) {
+ if ((bytes_read = read(f->fd, buf, BUFSIZ)) <= 0) {
+ if (bytes_read < 0 && (errno == EINTR || errno == EAGAIN))
+ continue;
+ else
+ return bytes_read;
+ }
+
+ if (mode == M_LINES) {
+ int i;
+ ssize_t block_size = BUFSIZ;
+
+ if (bytes_read < BUFSIZ)
+ block_size = bytes_read;
+
+ for (i = 0; i < block_size; i++) {
+ if (buf[i] == '\n') {
+ if (--n_units == 0)
+ break;
+ }
+ }
+
+ if (++i < block_size)
+ write(STDOUT_FILENO, &buf[i], bytes_read - i);
+ } else {
+ if ((unsigned long) bytes_read > n_units) {
+ write(STDOUT_FILENO, &buf[n_units], bytes_read - n_units);
+ bytes_read = n_units;
+ }
+
+ n_units -= bytes_read;
+ }
+ }
+
+ while ((bytes_read = read(f->fd, buf, BUFSIZ)) > 0)
+ write(STDOUT_FILENO, buf, (size_t) bytes_read);
+
+ return 0;
+}
+
+static int tail_pipe_lines(struct file_struct *f, unsigned long n_lines)
{
struct line_buf *first, *last, *tmp;
- ssize_t rc;
+ int rc;
unsigned long total_lines = 0;
const char *p;
+ if (from_begin)
+ return tail_pipe_from_begin(f, n_lines, M_LINES);
+
first = last = emalloc(sizeof(struct line_buf));
first->n_bytes = first->n_lines = 0;
first->next = NULL;
@@ -342,13 +391,16 @@ out:
return rc;
}
-static ssize_t tail_pipe_bytes(struct file_struct *f, unsigned long n_bytes)
+static int tail_pipe_bytes(struct file_struct *f, unsigned long n_bytes)
{
struct char_buf *first, *last, *tmp;
- ssize_t rc;
+ int rc;
unsigned long total_bytes = 0;
unsigned long i = 0; /* Index into buffer */
+ if (from_begin)
+ return tail_pipe_from_begin(f, n_bytes, M_BYTES);
+
first = last = emalloc(sizeof(struct char_buf));
first->n_bytes = 0;
first->next = NULL;
nt, page_mapcount(page)); -> 1384 BUG_ON(mapcount != page_mapcount(page)); The root cause of the problem is a race of two threads in a multithreaded process. Thread B incurs a page fault on a virtual address that has never been accessed (PMD entry is zero) while Thread A is executing an madvise() system call on a virtual address within the same 2 MB (huge page) range. virtual address space .---------------------. | | | | .-|---------------------| | | | | | |<-- B(fault) | | | 2 MB | |/////////////////////|-. huge < |/////////////////////| > A(range) page | |/////////////////////|-' | | | | | | '-|---------------------| | | | | '---------------------' - Thread A is executing an madvise(..., MADV_DONTNEED) system call on the virtual address range "A(range)" shown in the picture. sys_madvise // Acquire the semaphore in shared mode. down_read(&current->mm->mmap_sem) ... madvise_vma switch (behavior) case MADV_DONTNEED: madvise_dontneed zap_page_range unmap_vmas unmap_page_range zap_pud_range zap_pmd_range // // Assume that this huge page has never been accessed. // I.e. content of the PMD entry is zero (not mapped). // if (pmd_trans_huge(*pmd)) { // We don't get here due to the above assumption. } // // Assume that Thread B incurred a page fault and .---------> // sneaks in here as shown below. | // | if (pmd_none_or_clear_bad(pmd)) | { | if (unlikely(pmd_bad(*pmd))) | pmd_clear_bad | { | pmd_ERROR | // Log "bad pmd ..." message here. | pmd_clear | // Clear the page's PMD entry. | // Thread B incremented the map count | // in page_add_new_anon_rmap(), but | // now the page is no longer mapped | // by a PMD entry (-> inconsistency). | } | } | v - Thread B is handling a page fault on virtual address "B(fault)" shown in the picture. ... do_page_fault __do_page_fault // Acquire the semaphore in shared mode. down_read_trylock(&mm->mmap_sem) ... handle_mm_fault if (pmd_none(*pmd) && transparent_hugepage_enabled(vma)) // We get here due to the above assumption (PMD entry is zero). do_huge_pmd_anonymous_page alloc_hugepage_vma // Allocate a new transparent huge page here. ... __do_huge_pmd_anonymous_page ... spin_lock(&mm->page_table_lock) ... page_add_new_anon_rmap // Here we increment the page's map count (starts at -1). atomic_set(&page->_mapcount, 0) set_pmd_at // Here we set the page's PMD entry which will be cleared // when Thread A calls pmd_clear_bad(). ... spin_unlock(&mm->page_table_lock) The mmap_sem does not prevent the race because both threads are acquiring it in shared mode (down_read). Thread B holds the page_table_lock while the page's map count and PMD table entry are updated. However, Thread A does not synchronize on that lock. ====== end quote ======= [akpm@linux-foundation.org: checkpatch fixes] Reported-by: Ulrich Obergfell <uobergfe@redhat.com> Signed-off-by: Andrea Arcangeli <aarcange@redhat.com> Acked-by: Johannes Weiner <hannes@cmpxchg.org> Cc: Mel Gorman <mgorman@suse.de> Cc: Hugh Dickins <hughd@google.com> Cc: Dave Jones <davej@redhat.com> Acked-by: Larry Woodman <lwoodman@redhat.com> Acked-by: Rik van Riel <riel@redhat.com> Cc: <stable@vger.kernel.org> [2.6.38+] Cc: Mark Salter <msalter@redhat.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/pagewalk.c')