summaryrefslogtreecommitdiffstats
path: root/sys/kern
diff options
context:
space:
mode:
authoralc <alc@FreeBSD.org>2012-03-22 04:52:51 +0000
committeralc <alc@FreeBSD.org>2012-03-22 04:52:51 +0000
commite02fd6b8423e63f1fdbfc1f984d7c7291a1bacd1 (patch)
tree6628d621fc3b1f91d03b83dcd89b48528e3132fe /sys/kern
parent1c754fb497b9c43672483c1ed5abe4f9176325cd (diff)
downloadFreeBSD-src-e02fd6b8423e63f1fdbfc1f984d7c7291a1bacd1.zip
FreeBSD-src-e02fd6b8423e63f1fdbfc1f984d7c7291a1bacd1.tar.gz
Handle spurious page faults that may occur in no-fault sections of the
kernel. When access restrictions are added to a page table entry, we flush the corresponding virtual address mapping from the TLB. In contrast, when access restrictions are removed from a page table entry, we do not flush the virtual address mapping from the TLB. This is exactly as recommended in AMD's documentation. In effect, when access restrictions are removed from a page table entry, AMD's MMUs will transparently refresh a stale TLB entry. In short, this saves us from having to perform potentially costly TLB flushes. In contrast, Intel's MMUs are allowed to generate a spurious page fault based upon the stale TLB entry. Usually, such spurious page faults are handled by vm_fault() without incident. However, when we are executing no-fault sections of the kernel, we are not allowed to execute vm_fault(). This change introduces special-case handling for spurious page faults that occur in no-fault sections of the kernel. In collaboration with: kib Tested by: gibbs (an earlier version) I would also like to acknowledge Hiroki Sato's assistance in diagnosing this problem. MFC after: 1 week
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/kern_sysctl.c12
-rw-r--r--sys/kern/subr_uio.c8
2 files changed, 14 insertions, 6 deletions
diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c
index 97d03a3..f294ce3 100644
--- a/sys/kern/kern_sysctl.c
+++ b/sys/kern/kern_sysctl.c
@@ -1294,8 +1294,8 @@ kernel_sysctlbyname(struct thread *td, char *name, void *old, size_t *oldlenp,
static int
sysctl_old_user(struct sysctl_req *req, const void *p, size_t l)
{
- int error = 0;
size_t i, len, origidx;
+ int error;
origidx = req->oldidx;
req->oldidx += l;
@@ -1316,10 +1316,14 @@ sysctl_old_user(struct sysctl_req *req, const void *p, size_t l)
else {
if (i > len - origidx)
i = len - origidx;
- error = copyout(p, (char *)req->oldptr + origidx, i);
+ if (req->lock == REQ_WIRED) {
+ error = copyout_nofault(p, (char *)req->oldptr +
+ origidx, i);
+ } else
+ error = copyout(p, (char *)req->oldptr + origidx, i);
+ if (error != 0)
+ return (error);
}
- if (error)
- return (error);
if (i < l)
return (ENOMEM);
return (0);
diff --git a/sys/kern/subr_uio.c b/sys/kern/subr_uio.c
index 3c7688a..b85e50b 100644
--- a/sys/kern/subr_uio.c
+++ b/sys/kern/subr_uio.c
@@ -187,8 +187,12 @@ uiomove_faultflag(void *cp, int n, struct uio *uio, int nofault)
/* XXX does it make a sense to set TDP_DEADLKTREAT for UIO_SYSSPACE ? */
newflags = TDP_DEADLKTREAT;
- if (uio->uio_segflg == UIO_USERSPACE && nofault)
- newflags |= TDP_NOFAULTING;
+ if (uio->uio_segflg == UIO_USERSPACE && nofault) {
+ /*
+ * Fail if a non-spurious page fault occurs.
+ */
+ newflags |= TDP_NOFAULTING | TDP_RESETSPUR;
+ }
save = curthread_pflags_set(newflags);
while (n > 0 && uio->uio_resid) {
OpenPOWER on IntegriCloud