summaryrefslogtreecommitdiffstats
path: root/sys/powerpc
diff options
context:
space:
mode:
authoralc <alc@FreeBSD.org>2003-08-29 20:04:10 +0000
committeralc <alc@FreeBSD.org>2003-08-29 20:04:10 +0000
commit8b0114def15f351fc73725e8fd06c450759dc2fd (patch)
tree8459e08f5260f9190c526b16da0a353795629054 /sys/powerpc
parent2a6fc1b2d46305df7846b5c2e7a07c948aab59d6 (diff)
downloadFreeBSD-src-8b0114def15f351fc73725e8fd06c450759dc2fd.zip
FreeBSD-src-8b0114def15f351fc73725e8fd06c450759dc2fd.tar.gz
Migrate the sf_buf allocator that is used by sendfile(2) and zero-copy
sockets into machine-dependent files. The rationale for this migration is illustrated by the modified amd64 allocator. It uses the amd64's direct map to avoid emphemeral mappings in the kernel's address space. On an SMP, the emphemeral mappings result in an IPI for TLB shootdown for each transmitted page. Yuck. Maintainers of other 64-bit platforms with direct maps should be able to use the amd64 allocator as a reference implementation.
Diffstat (limited to 'sys/powerpc')
-rw-r--r--sys/powerpc/aim/vm_machdep.c101
-rw-r--r--sys/powerpc/powerpc/vm_machdep.c101
2 files changed, 202 insertions, 0 deletions
diff --git a/sys/powerpc/aim/vm_machdep.c b/sys/powerpc/aim/vm_machdep.c
index 3f83f88..cef8327 100644
--- a/sys/powerpc/aim/vm_machdep.c
+++ b/sys/powerpc/aim/vm_machdep.c
@@ -81,6 +81,8 @@
#include <sys/vnode.h>
#include <sys/vmmeter.h>
#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/socketvar.h>
#include <sys/sysctl.h>
#include <sys/unistd.h>
@@ -101,6 +103,20 @@
#include <sys/user.h>
+static void sf_buf_init(void *arg);
+SYSINIT(sock_sf, SI_SUB_MBUF, SI_ORDER_ANY, sf_buf_init, NULL)
+
+/*
+ * Expanded sf_freelist head. Really an SLIST_HEAD() in disguise, with the
+ * sf_freelist head with the sf_lock mutex.
+ */
+static struct {
+ SLIST_HEAD(, sf_buf) sf_head;
+ struct mtx sf_lock;
+} sf_freelist;
+
+static u_int sf_buf_alloc_want;
+
/*
* Finish a fork operation, with process p2 nearly set up.
* Copy and update the pcb, set up the stack so that the child
@@ -220,6 +236,91 @@ cpu_reset()
}
/*
+ * Allocate a pool of sf_bufs (sendfile(2) or "super-fast" if you prefer. :-))
+ */
+static void
+sf_buf_init(void *arg)
+{
+ struct sf_buf *sf_bufs;
+ vm_offset_t sf_base;
+ int i;
+
+ mtx_init(&sf_freelist.sf_lock, "sf_bufs list lock", NULL, MTX_DEF);
+ mtx_lock(&sf_freelist.sf_lock);
+ SLIST_INIT(&sf_freelist.sf_head);
+ sf_base = kmem_alloc_nofault(kernel_map, nsfbufs * PAGE_SIZE);
+ sf_bufs = malloc(nsfbufs * sizeof(struct sf_buf), M_TEMP,
+ M_NOWAIT | M_ZERO);
+ for (i = 0; i < nsfbufs; i++) {
+ sf_bufs[i].kva = sf_base + i * PAGE_SIZE;
+ SLIST_INSERT_HEAD(&sf_freelist.sf_head, &sf_bufs[i], free_list);
+ }
+ sf_buf_alloc_want = 0;
+ mtx_unlock(&sf_freelist.sf_lock);
+}
+
+/*
+ * Get an sf_buf from the freelist. Will block if none are available.
+ */
+struct sf_buf *
+sf_buf_alloc(struct vm_page *m)
+{
+ struct sf_buf *sf;
+ int error;
+
+ mtx_lock(&sf_freelist.sf_lock);
+ while ((sf = SLIST_FIRST(&sf_freelist.sf_head)) == NULL) {
+ sf_buf_alloc_want++;
+ error = msleep(&sf_freelist, &sf_freelist.sf_lock, PVM|PCATCH,
+ "sfbufa", 0);
+ sf_buf_alloc_want--;
+
+ /*
+ * If we got a signal, don't risk going back to sleep.
+ */
+ if (error)
+ break;
+ }
+ if (sf != NULL) {
+ SLIST_REMOVE_HEAD(&sf_freelist.sf_head, free_list);
+ sf->m = m;
+ pmap_qenter(sf->kva, &sf->m, 1);
+ }
+ mtx_unlock(&sf_freelist.sf_lock);
+ return (sf);
+}
+
+/*
+ * Detatch mapped page and release resources back to the system.
+ */
+void
+sf_buf_free(void *addr, void *args)
+{
+ struct sf_buf *sf;
+ struct vm_page *m;
+
+ sf = args;
+ pmap_qremove((vm_offset_t)addr, 1);
+ m = sf->m;
+ vm_page_lock_queues();
+ vm_page_unwire(m, 0);
+ /*
+ * Check for the object going away on us. This can
+ * happen since we don't hold a reference to it.
+ * If so, we're responsible for freeing the page.
+ */
+ if (m->wire_count == 0 && m->object == NULL)
+ vm_page_free(m);
+ vm_page_unlock_queues();
+ sf->m = NULL;
+ mtx_lock(&sf_freelist.sf_lock);
+ SLIST_INSERT_HEAD(&sf_freelist.sf_head, sf, free_list);
+ if (sf_buf_alloc_want > 0)
+ wakeup_one(&sf_freelist);
+ mtx_unlock(&sf_freelist.sf_lock);
+}
+
+/*
* Software interrupt handler for queued VM system processing.
*/
void
diff --git a/sys/powerpc/powerpc/vm_machdep.c b/sys/powerpc/powerpc/vm_machdep.c
index 3f83f88..cef8327 100644
--- a/sys/powerpc/powerpc/vm_machdep.c
+++ b/sys/powerpc/powerpc/vm_machdep.c
@@ -81,6 +81,8 @@
#include <sys/vnode.h>
#include <sys/vmmeter.h>
#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/socketvar.h>
#include <sys/sysctl.h>
#include <sys/unistd.h>
@@ -101,6 +103,20 @@
#include <sys/user.h>
+static void sf_buf_init(void *arg);
+SYSINIT(sock_sf, SI_SUB_MBUF, SI_ORDER_ANY, sf_buf_init, NULL)
+
+/*
+ * Expanded sf_freelist head. Really an SLIST_HEAD() in disguise, with the
+ * sf_freelist head with the sf_lock mutex.
+ */
+static struct {
+ SLIST_HEAD(, sf_buf) sf_head;
+ struct mtx sf_lock;
+} sf_freelist;
+
+static u_int sf_buf_alloc_want;
+
/*
* Finish a fork operation, with process p2 nearly set up.
* Copy and update the pcb, set up the stack so that the child
@@ -220,6 +236,91 @@ cpu_reset()
}
/*
+ * Allocate a pool of sf_bufs (sendfile(2) or "super-fast" if you prefer. :-))
+ */
+static void
+sf_buf_init(void *arg)
+{
+ struct sf_buf *sf_bufs;
+ vm_offset_t sf_base;
+ int i;
+
+ mtx_init(&sf_freelist.sf_lock, "sf_bufs list lock", NULL, MTX_DEF);
+ mtx_lock(&sf_freelist.sf_lock);
+ SLIST_INIT(&sf_freelist.sf_head);
+ sf_base = kmem_alloc_nofault(kernel_map, nsfbufs * PAGE_SIZE);
+ sf_bufs = malloc(nsfbufs * sizeof(struct sf_buf), M_TEMP,
+ M_NOWAIT | M_ZERO);
+ for (i = 0; i < nsfbufs; i++) {
+ sf_bufs[i].kva = sf_base + i * PAGE_SIZE;
+ SLIST_INSERT_HEAD(&sf_freelist.sf_head, &sf_bufs[i], free_list);
+ }
+ sf_buf_alloc_want = 0;
+ mtx_unlock(&sf_freelist.sf_lock);
+}
+
+/*
+ * Get an sf_buf from the freelist. Will block if none are available.
+ */
+struct sf_buf *
+sf_buf_alloc(struct vm_page *m)
+{
+ struct sf_buf *sf;
+ int error;
+
+ mtx_lock(&sf_freelist.sf_lock);
+ while ((sf = SLIST_FIRST(&sf_freelist.sf_head)) == NULL) {
+ sf_buf_alloc_want++;
+ error = msleep(&sf_freelist, &sf_freelist.sf_lock, PVM|PCATCH,
+ "sfbufa", 0);
+ sf_buf_alloc_want--;
+
+ /*
+ * If we got a signal, don't risk going back to sleep.
+ */
+ if (error)
+ break;
+ }
+ if (sf != NULL) {
+ SLIST_REMOVE_HEAD(&sf_freelist.sf_head, free_list);
+ sf->m = m;
+ pmap_qenter(sf->kva, &sf->m, 1);
+ }
+ mtx_unlock(&sf_freelist.sf_lock);
+ return (sf);
+}
+
+/*
+ * Detatch mapped page and release resources back to the system.
+ */
+void
+sf_buf_free(void *addr, void *args)
+{
+ struct sf_buf *sf;
+ struct vm_page *m;
+
+ sf = args;
+ pmap_qremove((vm_offset_t)addr, 1);
+ m = sf->m;
+ vm_page_lock_queues();
+ vm_page_unwire(m, 0);
+ /*
+ * Check for the object going away on us. This can
+ * happen since we don't hold a reference to it.
+ * If so, we're responsible for freeing the page.
+ */
+ if (m->wire_count == 0 && m->object == NULL)
+ vm_page_free(m);
+ vm_page_unlock_queues();
+ sf->m = NULL;
+ mtx_lock(&sf_freelist.sf_lock);
+ SLIST_INSERT_HEAD(&sf_freelist.sf_head, sf, free_list);
+ if (sf_buf_alloc_want > 0)
+ wakeup_one(&sf_freelist);
+ mtx_unlock(&sf_freelist.sf_lock);
+}
+
+/*
* Software interrupt handler for queued VM system processing.
*/
void
OpenPOWER on IntegriCloud