From 129337f38e86ac72381ebfaa3fe331d8404c2f87 Mon Sep 17 00:00:00 2001
From: Sascha Wildner <saw@online.de>
Date: Thu, 23 Apr 2020 19:01:49 +0200
Subject: [PATCH] WIP sg_pager

---
 sys/conf/files     |   1 +
 sys/vm/sg_pager.c  | 281 +++++++++++++++++++++++++++++++++++++++++++++
 sys/vm/vm_object.h |  10 ++
 sys/vm/vm_page.h   |  11 ++
 sys/vm/vm_pager.c  |   4 +-
 sys/vm/vm_pager.h  |   1 +
 6 files changed, 307 insertions(+), 1 deletion(-)
 create mode 100644 sys/vm/sg_pager.c

diff --git a/sys/conf/files b/sys/conf/files
index 6966aeac95..7b3faa1c86 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -2052,6 +2052,7 @@ vfs/fuse/fuse_vnops.c		optional fuse
 vm/default_pager.c		standard
 vm/device_pager.c		standard
 vm/phys_pager.c			standard
+vm/sg_pager.c			standard
 vm/swap_pager.c			standard
 vm/vm_contig.c  		standard
 vm/vm_fault.c			standard
diff --git a/sys/vm/sg_pager.c b/sys/vm/sg_pager.c
new file mode 100644
index 0000000000..6e2af50289
--- /dev/null
+++ b/sys/vm/sg_pager.c
@@ -0,0 +1,281 @@
+/*-
+ * Copyright (c) 2009 Hudson River Trading LLC
+ * Written by: John H. Baldwin <jhb@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: head/sys/vm/sg_pager.c 295330 2016-02-05 19:35:53Z markj $
+ */
+
+/*
+ * This pager manages OBJT_SG objects.  These objects are backed by
+ * a scatter/gather list of physical address ranges.
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/sglist.h>
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_object.h>
+#include <vm/vm_page2.h>
+#include <vm/vm_pager.h>
+
+static void sg_pager_dealloc(vm_object_t);
+static int sg_pager_getpage(vm_object_t, vm_page_t *, int);
+static void sg_pager_putpages(vm_object_t, vm_page_t *, int, int, int *);
+
+static boolean_t sg_pager_haspage(vm_object_t, vm_pindex_t);
+
+static MALLOC_DEFINE(M_SG_PAGES, "sg pager pages", "SG pager pages");
+
+#if 0
+static vm_page_t sg_pager_getfake(vm_paddr_t, int);
+#endif
+static void sg_pager_putfake(vm_page_t m);
+
+struct pagerops sgpagerops = {
+	sg_pager_dealloc,
+	sg_pager_getpage,
+	sg_pager_putpages,
+	sg_pager_haspage,
+};
+
+vm_object_t
+sg_pager_alloc(void *handle, off_t length, vm_prot_t prot, off_t offset)
+{
+	struct sglist *sg;
+	vm_object_t object;
+	vm_pindex_t npages, pindex;
+	int i;
+
+	/*
+	 * Offset should be page aligned.
+	 */
+	if (offset & PAGE_MASK)
+		return (NULL);
+
+	/*
+	 * The scatter/gather list must only include page-aligned
+	 * ranges.
+	 */
+	npages = 0;
+	sg = handle;
+	for (i = 0; i < sg->sg_nseg; i++) {
+		if ((sg->sg_segs[i].ss_paddr % PAGE_SIZE) != 0 ||
+		    (sg->sg_segs[i].ss_len % PAGE_SIZE) != 0)
+			return (NULL);
+		npages += sg->sg_segs[i].ss_len / PAGE_SIZE;
+	}
+
+	/*
+	 * The scatter/gather list has a fixed size.  Refuse requests
+	 * to map beyond that.
+	 */
+	length = round_page(length);
+	pindex = OFF_TO_IDX(offset) + OFF_TO_IDX(length);
+        if (pindex > npages || pindex < OFF_TO_IDX(offset) ||
+            pindex < OFF_TO_IDX(length))
+		return (NULL);
+
+	/*
+	 * Allocate a new object and associate it with the
+	 * scatter/gather list.  It is ok for our purposes to have
+	 * multiple VM objects associated with the same scatter/gather
+	 * list because scatter/gather lists are static.  This is also
+	 * simpler than ensuring a unique object per scatter/gather
+	 * list.
+	 */
+	object = vm_object_allocate(OBJT_SG, npages);
+	object->handle = sglist_hold(sg);
+	TAILQ_INIT(&object->un_pager.sgp.sgp_pglist);
+	return (object);
+}
+
+static void
+sg_pager_dealloc(vm_object_t object)
+{
+	struct sglist *sg;
+	vm_page_t m;
+
+	/*
+	 * Free up our fake pages.
+	 */
+	while ((m = TAILQ_FIRST(&object->un_pager.sgp.sgp_pglist)) != NULL) {
+		TAILQ_REMOVE(&object->un_pager.sgp.sgp_pglist, m, plinks.q);
+		sg_pager_putfake(m);
+	}
+
+	sg = object->handle;
+	sglist_free(sg);
+	object->handle = NULL;
+	object->type = OBJT_DEAD;
+}
+
+static int
+sg_pager_getpage(vm_object_t object, vm_page_t *mpp, int seqaccess)
+{
+	struct sglist *sg;
+	vm_page_t /* m_paddr, */page, m;
+	vm_pindex_t offset;
+	vm_paddr_t paddr;
+	vm_memattr_t memattr;
+	size_t space;
+	int i;
+
+	kprintf("%s: object:%p mpp:%p seqaccess:%d\n", __func__,
+	    object, mpp, seqaccess);
+
+	/* Since our haspage reports zero after/before, the count is 1. */
+#ifdef __FreeBSD__
+	KASSERT(count == 1, ("%s: count %d", __func__, count));
+#endif
+	sg = object->handle;
+	memattr = object->memattr;
+	offset = mpp[0]->pindex;
+
+	/*
+	 * Lookup the physical address of the requested page.  An initial
+	 * value of '1' instead of '0' is used so we can assert that the
+	 * page is found since '0' can be a valid page-aligned physical
+	 * address.
+	 */
+	space = 0;
+	paddr = 1;
+	for (i = 0; i < sg->sg_nseg; i++) {
+		if (space + sg->sg_segs[i].ss_len <= (offset * PAGE_SIZE)) {
+			space += sg->sg_segs[i].ss_len;
+			continue;
+		}
+		paddr = sg->sg_segs[i].ss_paddr + offset * PAGE_SIZE - space;
+		break;
+	}
+	KASSERT(paddr != 1, ("invalid SG page index"));
+
+#ifdef __FreeBSD__
+	/* If "paddr" is a real page, perform a sanity check on "memattr". */
+	if ((m_paddr = vm_phys_paddr_to_vm_page(paddr)) != NULL &&
+	    pmap_page_get_memattr(m_paddr) != memattr) {
+		memattr = pmap_page_get_memattr(m_paddr);
+		kprintf(
+	    "WARNING: A device driver has set \"memattr\" inconsistently.\n");
+	}
+#endif
+
+	/* Return a fake page for the requested page. */
+	KASSERT(!(mpp[0]->flags & PG_FICTITIOUS),
+	    ("backing page for SG is fake"));
+
+
+	/* Construct a new fake page. */
+	page = kmalloc(sizeof(*page), M_SG_PAGES, M_WAITOK | M_ZERO);
+	vm_page_initfake(page, paddr, memattr);
+	vm_object_hold(object);
+	TAILQ_INSERT_TAIL(&object->un_pager.sgp.sgp_pglist, page, plinks.q);
+	m = mpp[0];
+	mpp[0] = NULL;
+	kprintf("%s: m:%p mpp[0]:%p page:%p\n", __func__, m, mpp[0], page);
+	vm_page_free(m);
+	mpp[0] = page;
+	page->valid = VM_PAGE_BITS_ALL;
+	vm_page_insert(mpp[0], object, OFF_TO_IDX(offset));
+	vm_object_drop(object);
+
+#ifdef __FreeBSD__
+	if (rbehind)
+		*rbehind = 0;
+	if (rahead)
+		*rahead = 0;
+#endif
+
+	kprintf("%s:%d: returned VM_PAGER_OK\n", __func__, __LINE__);
+	return (VM_PAGER_OK);
+}
+
+static void
+sg_pager_putpages(vm_object_t object, vm_page_t *m, int count,
+    int /* XXX boolean_t */ sync, int *rtvals)
+{
+	panic("%s called", __func__);
+}
+
+static boolean_t
+sg_pager_haspage(vm_object_t object, vm_pindex_t pindex)
+{
+	kprintf("%s: object:%p pindex:%#jx\n", __func__, object, pindex);
+
+#ifdef __FreeBSD_
+	if (before != NULL)
+		*before = 0;
+	if (after != NULL)
+		*after = 0;
+#endif
+	kprintf("%s:%d: returned TRUE\n", __func__, __LINE__);
+	return (TRUE);
+}
+
+#if 0
+/*
+ * XXX: The caller must hold sg_pager_mtx ???
+ */
+static vm_page_t
+sg_pager_getfake(vm_paddr_t paddr, int pat_mode)
+{
+	vm_page_t m;
+
+	kprintf("%s: paddr:%#jx pat_mode:%d\n", __func__, paddr, pat_mode);
+
+	m = kmalloc(sizeof(*m), M_SG_PAGES, M_WAITOK | M_ZERO);
+
+	pmap_page_init(m);
+
+	m->flags = PG_FICTITIOUS;
+	m->valid = VM_PAGE_BITS_ALL;
+	m->dirty = 0;
+	m->queue = PQ_NONE;
+	m->object = NULL;
+
+	m->busy_count = PBUSY_LOCKED;
+	m->wire_count = 1;
+	m->hold_count = 0;
+	m->phys_addr = paddr;
+	m->pat_mode = pat_mode;
+
+	kprintf("%s:%d: returned %p\n", __func__, __LINE__, m);
+	return (m);
+}
+#endif
+
+static void
+sg_pager_putfake(vm_page_t m)
+{
+	kprintf("%s: m:%p\n", __func__, m);
+
+	if (!(m->flags & PG_FICTITIOUS))
+		panic("%s: bad page", __func__);
+	KKASSERT(m->object == NULL);
+	KKASSERT(m->hold_count == 0);
+	kfree(m, M_SG_PAGES);
+}
diff --git a/sys/vm/vm_object.h b/sys/vm/vm_object.h
index b7908f0034..6f5c694187 100644
--- a/sys/vm/vm_object.h
+++ b/sys/vm/vm_object.h
@@ -123,6 +123,7 @@ enum obj_type {
 	OBJT_MGTDEVICE,
 	OBJT_PHYS,  	/* object backed by physical pages */
 	OBJT_DEAD,   	/* dead object */
+	OBJT_SG,	/* backed by S/G list of physical address ranges */
 	OBJT_MARKER	/* marker object */
 };
 typedef u_char objtype_t;
@@ -175,6 +176,15 @@ struct vm_object {
 			struct cdev_pager_ops *ops;
 			struct cdev *dev;
 		} devp;
+
+		/*
+		 * SG pager
+		 *
+		 *	sgp_pglist - list of allocated pages
+		 */
+		struct {
+			TAILQ_HEAD(, vm_page) sgp_pglist;
+		} sgp;
 	} un_pager;
 
 	/*
diff --git a/sys/vm/vm_page.h b/sys/vm/vm_page.h
index 7a95564afd..57662b8f30 100644
--- a/sys/vm/vm_page.h
+++ b/sys/vm/vm_page.h
@@ -179,6 +179,17 @@ RB_PROTOTYPE2(vm_page_rb_tree, vm_page, rb_entry,
 RB_HEAD(vm_page_rb_tree, vm_page);
 
 struct vm_page {
+	union {
+		TAILQ_ENTRY(vm_page) q; /* page queue or free list (Q) */
+		struct {
+			SLIST_ENTRY(vm_page) ss; /* private slists */
+			void *pv;
+		} s;
+		struct {
+			u_long p;
+			u_long v;
+		} memguard;
+	} plinks;
 	TAILQ_ENTRY(vm_page) pageq;	/* vm_page_queues[] list 	*/
 	RB_ENTRY(vm_page) rb_entry;	/* Red-Black tree based at object */
 	struct spinlock	spin;
diff --git a/sys/vm/vm_pager.c b/sys/vm/vm_pager.c
index bf25e280f2..b572447128 100644
--- a/sys/vm/vm_pager.c
+++ b/sys/vm/vm_pager.c
@@ -93,6 +93,7 @@ extern struct pagerops swappagerops;
 extern struct pagerops vnodepagerops;
 extern struct pagerops devicepagerops;
 extern struct pagerops physpagerops;
+extern struct pagerops sgpagerops;
 
 static int dead_pager_getpage (vm_object_t, vm_page_t *, int);
 static void dead_pager_putpages (vm_object_t, vm_page_t *, int, int, int *);
@@ -155,7 +156,8 @@ struct pagerops *pagertab[] = {
 	&devicepagerops,	/* OBJT_DEVICE */
 	&devicepagerops,	/* OBJT_MGTDEVICE */
 	&physpagerops,		/* OBJT_PHYS */
-	&deadpagerops		/* OBJT_DEAD */
+	&deadpagerops,		/* OBJT_DEAD */
+	&sgpagerops		/* OBJT_SG */
 };
 
 int npagers = NELEM(pagertab);
diff --git a/sys/vm/vm_pager.h b/sys/vm/vm_pager.h
index 8990291fc1..dc4f40d07b 100644
--- a/sys/vm/vm_pager.h
+++ b/sys/vm/vm_pager.h
@@ -100,6 +100,7 @@ extern struct pagerops *pagertab[];
 vm_object_t default_pager_alloc(void *, off_t, vm_prot_t, off_t);
 vm_object_t dev_pager_alloc(void *, off_t, vm_prot_t, off_t);
 vm_object_t phys_pager_alloc(void *, off_t, vm_prot_t, off_t);
+vm_object_t sg_pager_alloc(void *, off_t, vm_prot_t, off_t);
 vm_object_t swap_pager_alloc(void *, off_t, vm_prot_t, off_t);
 vm_object_t vnode_pager_alloc (void *, off_t, vm_prot_t, off_t, int, int);
 vm_object_t vnode_pager_reference (struct vnode *);
-- 
2.25.1

