1195840Sjhb/*- 2281887Sjhb * Copyright (c) 2009 Hudson River Trading LLC 3195840Sjhb * Written by: John H. Baldwin <jhb@FreeBSD.org> 4195840Sjhb * All rights reserved. 5195840Sjhb * 6195840Sjhb * Redistribution and use in source and binary forms, with or without 7195840Sjhb * modification, are permitted provided that the following conditions 8195840Sjhb * are met: 9195840Sjhb * 1. Redistributions of source code must retain the above copyright 10195840Sjhb * notice, this list of conditions and the following disclaimer. 11195840Sjhb * 2. Redistributions in binary form must reproduce the above copyright 12195840Sjhb * notice, this list of conditions and the following disclaimer in the 13195840Sjhb * documentation and/or other materials provided with the distribution. 14195840Sjhb * 15195840Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16195840Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17195840Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18195840Sjhb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19195840Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20195840Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21195840Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22195840Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23195840Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24195840Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25195840Sjhb * SUCH DAMAGE. 26195840Sjhb */ 27195840Sjhb 28195840Sjhb#include <sys/cdefs.h> 29195840Sjhb__FBSDID("$FreeBSD: stable/11/sys/vm/sg_pager.c 331017 2018-03-15 19:08:33Z kevans $"); 30195840Sjhb 31195840Sjhb/* 32195840Sjhb * This pager manages OBJT_SG objects. These objects are backed by 33195840Sjhb * a scatter/gather list of physical address ranges. 34195840Sjhb */ 35195840Sjhb 36195840Sjhb#include <sys/param.h> 37195840Sjhb#include <sys/lock.h> 38195840Sjhb#include <sys/mutex.h> 39248084Sattilio#include <sys/rwlock.h> 40195840Sjhb#include <sys/sglist.h> 41331017Skevans#include <sys/vmmeter.h> 42331017Skevans 43195840Sjhb#include <vm/vm.h> 44239065Skib#include <vm/vm_param.h> 45195840Sjhb#include <vm/vm_object.h> 46195840Sjhb#include <vm/vm_page.h> 47195840Sjhb#include <vm/vm_pager.h> 48243132Skib#include <vm/vm_phys.h> 49195840Sjhb#include <vm/uma.h> 50195840Sjhb 51195840Sjhbstatic vm_object_t sg_pager_alloc(void *, vm_ooffset_t, vm_prot_t, 52195840Sjhb vm_ooffset_t, struct ucred *); 53195840Sjhbstatic void sg_pager_dealloc(vm_object_t); 54292373Sglebiusstatic int sg_pager_getpages(vm_object_t, vm_page_t *, int, int *, int *); 55195840Sjhbstatic void sg_pager_putpages(vm_object_t, vm_page_t *, int, 56195840Sjhb boolean_t, int *); 57195840Sjhbstatic boolean_t sg_pager_haspage(vm_object_t, vm_pindex_t, int *, 58195840Sjhb int *); 59195840Sjhb 60195840Sjhbstruct pagerops sgpagerops = { 61195840Sjhb .pgo_alloc = sg_pager_alloc, 62195840Sjhb .pgo_dealloc = sg_pager_dealloc, 63195840Sjhb .pgo_getpages = sg_pager_getpages, 64195840Sjhb .pgo_putpages = sg_pager_putpages, 65195840Sjhb .pgo_haspage = sg_pager_haspage, 66195840Sjhb}; 67195840Sjhb 68195840Sjhbstatic vm_object_t 69195840Sjhbsg_pager_alloc(void *handle, vm_ooffset_t size, vm_prot_t prot, 70195840Sjhb vm_ooffset_t foff, struct ucred *cred) 71195840Sjhb{ 72195840Sjhb struct sglist *sg; 73195840Sjhb vm_object_t object; 74195840Sjhb vm_pindex_t npages, pindex; 75195840Sjhb int i; 76195840Sjhb 77195840Sjhb /* 78195840Sjhb * Offset should be page aligned. 79195840Sjhb */ 80195840Sjhb if (foff & PAGE_MASK) 81195840Sjhb return (NULL); 82195840Sjhb 83195840Sjhb /* 84195840Sjhb * The scatter/gather list must only include page-aligned 85195840Sjhb * ranges. 86195840Sjhb */ 87195840Sjhb npages = 0; 88195840Sjhb sg = handle; 89195840Sjhb for (i = 0; i < sg->sg_nseg; i++) { 90195840Sjhb if ((sg->sg_segs[i].ss_paddr % PAGE_SIZE) != 0 || 91195840Sjhb (sg->sg_segs[i].ss_len % PAGE_SIZE) != 0) 92195840Sjhb return (NULL); 93195840Sjhb npages += sg->sg_segs[i].ss_len / PAGE_SIZE; 94195840Sjhb } 95195840Sjhb 96195840Sjhb /* 97195840Sjhb * The scatter/gather list has a fixed size. Refuse requests 98195840Sjhb * to map beyond that. 99195840Sjhb */ 100195840Sjhb size = round_page(size); 101315563Skib pindex = UOFF_TO_IDX(foff) + UOFF_TO_IDX(size); 102315563Skib if (pindex > npages || pindex < UOFF_TO_IDX(foff) || 103315563Skib pindex < UOFF_TO_IDX(size)) 104195840Sjhb return (NULL); 105195840Sjhb 106195840Sjhb /* 107195840Sjhb * Allocate a new object and associate it with the 108195840Sjhb * scatter/gather list. It is ok for our purposes to have 109195840Sjhb * multiple VM objects associated with the same scatter/gather 110195840Sjhb * list because scatter/gather lists are static. This is also 111195840Sjhb * simpler than ensuring a unique object per scatter/gather 112195840Sjhb * list. 113195840Sjhb */ 114195840Sjhb object = vm_object_allocate(OBJT_SG, npages); 115195840Sjhb object->handle = sglist_hold(sg); 116195840Sjhb TAILQ_INIT(&object->un_pager.sgp.sgp_pglist); 117195840Sjhb return (object); 118195840Sjhb} 119195840Sjhb 120195840Sjhbstatic void 121195840Sjhbsg_pager_dealloc(vm_object_t object) 122195840Sjhb{ 123195840Sjhb struct sglist *sg; 124195840Sjhb vm_page_t m; 125195840Sjhb 126195840Sjhb /* 127195840Sjhb * Free up our fake pages. 128195840Sjhb */ 129195840Sjhb while ((m = TAILQ_FIRST(&object->un_pager.sgp.sgp_pglist)) != 0) { 130254182Skib TAILQ_REMOVE(&object->un_pager.sgp.sgp_pglist, m, plinks.q); 131219476Salc vm_page_putfake(m); 132195840Sjhb } 133195840Sjhb 134195840Sjhb sg = object->handle; 135195840Sjhb sglist_free(sg); 136282660Sjhb object->handle = NULL; 137282660Sjhb object->type = OBJT_DEAD; 138195840Sjhb} 139195840Sjhb 140195840Sjhbstatic int 141292373Sglebiussg_pager_getpages(vm_object_t object, vm_page_t *m, int count, int *rbehind, 142292373Sglebius int *rahead) 143195840Sjhb{ 144195840Sjhb struct sglist *sg; 145195840Sjhb vm_page_t m_paddr, page; 146195840Sjhb vm_pindex_t offset; 147195840Sjhb vm_paddr_t paddr; 148195840Sjhb vm_memattr_t memattr; 149195840Sjhb size_t space; 150195840Sjhb int i; 151195840Sjhb 152292373Sglebius /* Since our haspage reports zero after/before, the count is 1. */ 153292373Sglebius KASSERT(count == 1, ("%s: count %d", __func__, count)); 154248084Sattilio VM_OBJECT_ASSERT_WLOCKED(object); 155195840Sjhb sg = object->handle; 156195840Sjhb memattr = object->memattr; 157248084Sattilio VM_OBJECT_WUNLOCK(object); 158292373Sglebius offset = m[0]->pindex; 159195840Sjhb 160195840Sjhb /* 161195840Sjhb * Lookup the physical address of the requested page. An initial 162195840Sjhb * value of '1' instead of '0' is used so we can assert that the 163195840Sjhb * page is found since '0' can be a valid page-aligned physical 164195840Sjhb * address. 165195840Sjhb */ 166195840Sjhb space = 0; 167195840Sjhb paddr = 1; 168195840Sjhb for (i = 0; i < sg->sg_nseg; i++) { 169195840Sjhb if (space + sg->sg_segs[i].ss_len <= (offset * PAGE_SIZE)) { 170195840Sjhb space += sg->sg_segs[i].ss_len; 171195840Sjhb continue; 172195840Sjhb } 173195840Sjhb paddr = sg->sg_segs[i].ss_paddr + offset * PAGE_SIZE - space; 174195840Sjhb break; 175195840Sjhb } 176195840Sjhb KASSERT(paddr != 1, ("invalid SG page index")); 177195840Sjhb 178195840Sjhb /* If "paddr" is a real page, perform a sanity check on "memattr". */ 179195840Sjhb if ((m_paddr = vm_phys_paddr_to_vm_page(paddr)) != NULL && 180195840Sjhb pmap_page_get_memattr(m_paddr) != memattr) { 181195840Sjhb memattr = pmap_page_get_memattr(m_paddr); 182195840Sjhb printf( 183195840Sjhb "WARNING: A device driver has set \"memattr\" inconsistently.\n"); 184195840Sjhb } 185195840Sjhb 186195840Sjhb /* Return a fake page for the requested page. */ 187292373Sglebius KASSERT(!(m[0]->flags & PG_FICTITIOUS), 188195840Sjhb ("backing page for SG is fake")); 189195840Sjhb 190195840Sjhb /* Construct a new fake page. */ 191219476Salc page = vm_page_getfake(paddr, memattr); 192248084Sattilio VM_OBJECT_WLOCK(object); 193254182Skib TAILQ_INSERT_TAIL(&object->un_pager.sgp.sgp_pglist, page, plinks.q); 194292406Scem vm_page_replace_checked(page, object, offset, m[0]); 195295330Smarkj vm_page_lock(m[0]); 196295330Smarkj vm_page_free(m[0]); 197295330Smarkj vm_page_unlock(m[0]); 198292373Sglebius m[0] = page; 199196637Sjhb page->valid = VM_PAGE_BITS_ALL; 200195840Sjhb 201292373Sglebius if (rbehind) 202292373Sglebius *rbehind = 0; 203292373Sglebius if (rahead) 204292373Sglebius *rahead = 0; 205292373Sglebius 206195840Sjhb return (VM_PAGER_OK); 207195840Sjhb} 208195840Sjhb 209195840Sjhbstatic void 210195840Sjhbsg_pager_putpages(vm_object_t object, vm_page_t *m, int count, 211195840Sjhb boolean_t sync, int *rtvals) 212195840Sjhb{ 213195840Sjhb 214195840Sjhb panic("sg_pager_putpage called"); 215195840Sjhb} 216195840Sjhb 217195840Sjhbstatic boolean_t 218195840Sjhbsg_pager_haspage(vm_object_t object, vm_pindex_t pindex, int *before, 219195840Sjhb int *after) 220195840Sjhb{ 221195840Sjhb 222195840Sjhb if (before != NULL) 223195840Sjhb *before = 0; 224195840Sjhb if (after != NULL) 225195840Sjhb *after = 0; 226195840Sjhb return (TRUE); 227195840Sjhb} 228