extern int
umovestr(struct tcb *, kernel_ulong_t addr, unsigned int len, char *laddr);
+/* Invalidate the cache used by umove* functions. */
+extern void invalidate_umove_cache(void);
+
extern int upeek(struct tcb *tcp, unsigned long, kernel_ulong_t *);
extern int upoke(struct tcb *tcp, unsigned long, kernel_ulong_t);
--- /dev/null
+/*
+ * Check effectiveness of umovestr memory caching.
+ *
+ * Copyright (c) 2019 Dmitry V. Levin <ldv@altlinux.org>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "tests.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/uio.h>
+
+int
+main(void)
+{
+ char *const buf = tail_alloc(DEFAULT_STRLEN);
+ fill_memory_ex(buf, DEFAULT_STRLEN, 'a', 'z' - 'a' + 1);
+
+ struct iovec *const io = tail_alloc(sizeof(*io) * DEFAULT_STRLEN);
+ for (unsigned int i = 0; i < DEFAULT_STRLEN; ++i) {
+ io[i].iov_base = buf + DEFAULT_STRLEN - i;
+ io[i].iov_len = i;
+ }
+
+ tprintf("%s", "");
+
+ int rc = writev(-1, io, DEFAULT_STRLEN);
+ const char *errstr = sprintrc(rc);
+
+ tprintf("writev(-1, [");
+ for (unsigned int i = 0; i < DEFAULT_STRLEN; ++i) {
+ if (i)
+ tprintf(", ");
+ tprintf("{iov_base=\"%.*s\", iov_len=%u}",
+ (int) io[i].iov_len,
+ (char *) io[i].iov_base,
+ (unsigned int) io[i].iov_len);
+ }
+ tprintf("], %u) = %s\n", DEFAULT_STRLEN, errstr);
+
+ tprintf("+++ exited with 0 +++\n");
+ return 0;
+}
--- /dev/null
+#!/bin/sh
+#
+# Check effectiveness of umovestr memory caching.
+#
+# Copyright (c) 2017-2019 Dmitry V. Levin <ldv@altlinux.org>
+# All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+. "${srcdir=.}/init.sh"
+
+check_prog env
+check_prog grep
+run_strace_match_diff -e trace=writev
+
+run_strace -qq -esignal=none -eprocess_vm_readv -o '|grep -c ^process_vm_readv > count' \
+ -- "$STRACE_EXE" -o "$LOG" $args > /dev/null
+
+count="$(cat count)"
+case "$count" in
+ 0) skip_ "$STRACE $args made no process_vm_readv syscall invocations" ;;
+ 2) ;;
+ *) fail_ "$STRACE $args made $count != 2 process_vm_readv syscall invocations" ;;
+esac
#endif /* !HAVE_PROCESS_VM_READV */
static ssize_t
-vm_read_mem(const pid_t pid, void *const laddr,
- const kernel_ulong_t raddr, const size_t len)
+process_read_mem(const pid_t pid, void *const laddr,
+ void *const raddr, const size_t len)
{
- const unsigned long truncated_raddr = raddr;
-
-#if SIZEOF_LONG < SIZEOF_KERNEL_LONG_T
- if (raddr != (kernel_ulong_t) truncated_raddr) {
- errno = EIO;
- return -1;
- }
-#endif
-
const struct iovec local = {
.iov_base = laddr,
.iov_len = len
};
const struct iovec remote = {
- .iov_base = (void *) truncated_raddr,
+ .iov_base = raddr,
.iov_len = len
};
return rc;
}
+static int cached_idx = -1;
+static unsigned long cached_raddr[2];
+
+void
+invalidate_umove_cache(void)
+{
+ cached_idx = -1;
+}
+
+static ssize_t
+vm_read_mem(const pid_t pid, void *const laddr,
+ const kernel_ulong_t raddr, const size_t len)
+{
+ if (!len)
+ return len;
+
+ const unsigned long taddr = raddr;
+
+#if SIZEOF_LONG < SIZEOF_KERNEL_LONG_T
+ if (raddr != (kernel_ulong_t) taddr) {
+ errno = EIO;
+ return -1;
+ }
+#endif
+
+ const size_t page_size = get_pagesize();
+ const size_t page_mask = page_size - 1;
+ const unsigned long raddr_page_start =
+ taddr & ~page_mask;
+ const unsigned long raddr_page_next =
+ (taddr + len + page_mask) & ~page_mask;
+
+ if (!raddr_page_start ||
+ raddr_page_next < raddr_page_start ||
+ raddr_page_next - raddr_page_start != page_size)
+ return process_read_mem(pid, laddr, (void *) taddr, len);
+
+ int idx = -1;
+ if (cached_idx >= 0) {
+ if (raddr_page_start == cached_raddr[cached_idx])
+ idx = cached_idx;
+ else if (raddr_page_start == cached_raddr[!cached_idx])
+ idx = !cached_idx;
+ }
+
+ static char *buf[2];
+
+ if (idx == -1) {
+ idx = !cached_idx;
+
+ if (!buf[idx])
+ buf[idx] = xmalloc(page_size);
+
+ const ssize_t rc =
+ process_read_mem(pid, buf[idx],
+ (void *) raddr_page_start, page_size);
+ if (rc < 0)
+ return rc;
+
+ cached_raddr[idx] = raddr_page_start;
+ if (cached_idx < 0)
+ cached_raddr[!idx] = 0;
+ cached_idx = idx;
+ }
+
+ memcpy(laddr, buf[idx] + (taddr - cached_raddr[idx]), len);
+ return len;
+}
+
static bool
tracee_addr_is_invalid(kernel_ulong_t addr)
{