summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_subr.c
diff options
context:
space:
mode:
authoralc <alc@FreeBSD.org>2004-12-13 06:24:14 +0000
committeralc <alc@FreeBSD.org>2004-12-13 06:24:14 +0000
commit04949181f90cacc54091ec897dab09c046e081be (patch)
tree02062969256c34cbd9f75269498af85d70867d58 /sys/kern/kern_subr.c
parent6d4f366de6ce1675d72964b3033b37e8b95f508e (diff)
downloadFreeBSD-src-04949181f90cacc54091ec897dab09c046e081be.zip
FreeBSD-src-04949181f90cacc54091ec897dab09c046e081be.tar.gz
Correct the handling of two unusual cases by the zero-copy receive path,
specifically, vm_pgmoveco(): 1. If vm_pgmoveco() sleeps on a busy page, it must redo the look up because the page may have been freed. 2. If the receive buffer is copy-on-write due to, for example, a fork, then although the first vm object in the shadow chain may not contain a page there may still be one from a backing object that is mapped. Thus, a pmap_remove() is required for the new page rather than the backing object's page to been seen by the application. Also, add some comments to vm_pgmoveco() and update some assertions. Tested by: ken@
Diffstat (limited to 'sys/kern/kern_subr.c')
-rw-r--r--sys/kern/kern_subr.c42
1 files changed, 26 insertions, 16 deletions
diff --git a/sys/kern/kern_subr.c b/sys/kern/kern_subr.c
index a1c90fd..b5de6ed 100644
--- a/sys/kern/kern_subr.c
+++ b/sys/kern/kern_subr.c
@@ -68,6 +68,12 @@ SYSCTL_INT(_kern, KERN_IOV_MAX, iov_max, CTLFLAG_RD, NULL, UIO_MAXIOV,
/* Declared in uipc_socket.c */
extern int so_zero_copy_receive;
+/*
+ * Identify the physical page mapped at the given kernel virtual
+ * address. Insert this physical page into the given address space at
+ * the given virtual address, replacing the physical page, if any,
+ * that already exists there.
+ */
static int
vm_pgmoveco(vm_map_t mapa, vm_offset_t kaddr, vm_offset_t uaddr)
{
@@ -79,10 +85,17 @@ vm_pgmoveco(vm_map_t mapa, vm_offset_t kaddr, vm_offset_t uaddr)
vm_prot_t prot;
boolean_t wired;
+ KASSERT((uaddr & PAGE_MASK) == 0,
+ ("vm_pgmoveco: uaddr is not page aligned"));
+
/*
- * First lookup the kernel page.
+ * Herein the physical page is validated and dirtied. It is
+ * unwired in sf_buf_mext().
*/
kern_pg = PHYS_TO_VM_PAGE(vtophys(kaddr));
+ kern_pg->valid = VM_PAGE_BITS_ALL;
+ KASSERT(kern_pg->queue == PQ_NONE && kern_pg->wire_count == 1,
+ ("vm_pgmoveco: kern_pg is not correctly wired"));
if ((vm_map_lookup(&map, uaddr,
VM_PROT_WRITE, &entry, &uobject,
@@ -90,28 +103,25 @@ vm_pgmoveco(vm_map_t mapa, vm_offset_t kaddr, vm_offset_t uaddr)
return(EFAULT);
}
VM_OBJECT_LOCK(uobject);
+retry:
if ((user_pg = vm_page_lookup(uobject, upindex)) != NULL) {
- do
- vm_page_lock_queues();
- while (vm_page_sleep_if_busy(user_pg, 1, "vm_pgmoveco"));
+ vm_page_lock_queues();
+ if (vm_page_sleep_if_busy(user_pg, 1, "vm_pgmoveco"))
+ goto retry;
pmap_remove_all(user_pg);
vm_page_free(user_pg);
- } else
+ } else {
+ /*
+ * Even if a physical page does not exist in the
+ * object chain's first object, a physical page from a
+ * backing object may be mapped read only.
+ */
+ if (uobject->backing_object != NULL)
+ pmap_remove(map->pmap, uaddr, uaddr + PAGE_SIZE);
vm_page_lock_queues();
- if (kern_pg->busy || ((kern_pg->queue - kern_pg->pc) == PQ_FREE) ||
- (kern_pg->hold_count != 0)|| (kern_pg->flags & PG_BUSY)) {
- printf("vm_pgmoveco: pindex(%lu), busy(%d), PG_BUSY(%d), "
- "hold(%d) paddr(0x%lx)\n", (u_long)kern_pg->pindex,
- kern_pg->busy, (kern_pg->flags & PG_BUSY) ? 1 : 0,
- kern_pg->hold_count, (u_long)kern_pg->phys_addr);
- if ((kern_pg->queue - kern_pg->pc) == PQ_FREE)
- panic("vm_pgmoveco: renaming free page");
- else
- panic("vm_pgmoveco: renaming busy page");
}
vm_page_insert(kern_pg, uobject, upindex);
vm_page_dirty(kern_pg);
- kern_pg->valid = VM_PAGE_BITS_ALL;
vm_page_unlock_queues();
VM_OBJECT_UNLOCK(uobject);
vm_map_lookup_done(map, entry);
OpenPOWER on IntegriCloud