sg_pager.c revision 254182
1195840Sjhb/*- 2195840Sjhb * Copyright (c) 2009 Advanced Computing Technologies 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: head/sys/vm/sg_pager.c 254182 2013-08-10 17:36:42Z kib $"); 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> 41195840Sjhb#include <vm/vm.h> 42239065Skib#include <vm/vm_param.h> 43195840Sjhb#include <vm/vm_object.h> 44195840Sjhb#include <vm/vm_page.h> 45195840Sjhb#include <vm/vm_pager.h> 46243132Skib#include <vm/vm_phys.h> 47195840Sjhb#include <vm/uma.h> 48195840Sjhb 49195840Sjhbstatic vm_object_t sg_pager_alloc(void *, vm_ooffset_t, vm_prot_t, 50195840Sjhb vm_ooffset_t, struct ucred *); 51195840Sjhbstatic void sg_pager_dealloc(vm_object_t); 52195840Sjhbstatic int sg_pager_getpages(vm_object_t, vm_page_t *, int, int); 53195840Sjhbstatic void sg_pager_putpages(vm_object_t, vm_page_t *, int, 54195840Sjhb boolean_t, int *); 55195840Sjhbstatic boolean_t sg_pager_haspage(vm_object_t, vm_pindex_t, int *, 56195840Sjhb int *); 57195840Sjhb 58195840Sjhbstruct pagerops sgpagerops = { 59195840Sjhb .pgo_alloc = sg_pager_alloc, 60195840Sjhb .pgo_dealloc = sg_pager_dealloc, 61195840Sjhb .pgo_getpages = sg_pager_getpages, 62195840Sjhb .pgo_putpages = sg_pager_putpages, 63195840Sjhb .pgo_haspage = sg_pager_haspage, 64195840Sjhb}; 65195840Sjhb 66195840Sjhbstatic vm_object_t 67195840Sjhbsg_pager_alloc(void *handle, vm_ooffset_t size, vm_prot_t prot, 68195840Sjhb vm_ooffset_t foff, struct ucred *cred) 69195840Sjhb{ 70195840Sjhb struct sglist *sg; 71195840Sjhb vm_object_t object; 72195840Sjhb vm_pindex_t npages, pindex; 73195840Sjhb int i; 74195840Sjhb 75195840Sjhb /* 76195840Sjhb * Offset should be page aligned. 77195840Sjhb */ 78195840Sjhb if (foff & PAGE_MASK) 79195840Sjhb return (NULL); 80195840Sjhb 81195840Sjhb /* 82195840Sjhb * The scatter/gather list must only include page-aligned 83195840Sjhb * ranges. 84195840Sjhb */ 85195840Sjhb npages = 0; 86195840Sjhb sg = handle; 87195840Sjhb for (i = 0; i < sg->sg_nseg; i++) { 88195840Sjhb if ((sg->sg_segs[i].ss_paddr % PAGE_SIZE) != 0 || 89195840Sjhb (sg->sg_segs[i].ss_len % PAGE_SIZE) != 0) 90195840Sjhb return (NULL); 91195840Sjhb npages += sg->sg_segs[i].ss_len / PAGE_SIZE; 92195840Sjhb } 93195840Sjhb 94195840Sjhb /* 95195840Sjhb * The scatter/gather list has a fixed size. Refuse requests 96195840Sjhb * to map beyond that. 97195840Sjhb */ 98195840Sjhb size = round_page(size); 99195840Sjhb pindex = OFF_TO_IDX(foff + size); 100195840Sjhb if (pindex > npages) 101195840Sjhb return (NULL); 102195840Sjhb 103195840Sjhb /* 104195840Sjhb * Allocate a new object and associate it with the 105195840Sjhb * scatter/gather list. It is ok for our purposes to have 106195840Sjhb * multiple VM objects associated with the same scatter/gather 107195840Sjhb * list because scatter/gather lists are static. This is also 108195840Sjhb * simpler than ensuring a unique object per scatter/gather 109195840Sjhb * list. 110195840Sjhb */ 111195840Sjhb object = vm_object_allocate(OBJT_SG, npages); 112195840Sjhb object->handle = sglist_hold(sg); 113195840Sjhb TAILQ_INIT(&object->un_pager.sgp.sgp_pglist); 114195840Sjhb return (object); 115195840Sjhb} 116195840Sjhb 117195840Sjhbstatic void 118195840Sjhbsg_pager_dealloc(vm_object_t object) 119195840Sjhb{ 120195840Sjhb struct sglist *sg; 121195840Sjhb vm_page_t m; 122195840Sjhb 123195840Sjhb /* 124195840Sjhb * Free up our fake pages. 125195840Sjhb */ 126195840Sjhb while ((m = TAILQ_FIRST(&object->un_pager.sgp.sgp_pglist)) != 0) { 127254182Skib TAILQ_REMOVE(&object->un_pager.sgp.sgp_pglist, m, plinks.q); 128219476Salc vm_page_putfake(m); 129195840Sjhb } 130195840Sjhb 131195840Sjhb sg = object->handle; 132195840Sjhb sglist_free(sg); 133195840Sjhb} 134195840Sjhb 135195840Sjhbstatic int 136195840Sjhbsg_pager_getpages(vm_object_t object, vm_page_t *m, int count, int reqpage) 137195840Sjhb{ 138195840Sjhb struct sglist *sg; 139195840Sjhb vm_page_t m_paddr, page; 140195840Sjhb vm_pindex_t offset; 141195840Sjhb vm_paddr_t paddr; 142195840Sjhb vm_memattr_t memattr; 143195840Sjhb size_t space; 144195840Sjhb int i; 145195840Sjhb 146248084Sattilio VM_OBJECT_ASSERT_WLOCKED(object); 147195840Sjhb sg = object->handle; 148195840Sjhb memattr = object->memattr; 149248084Sattilio VM_OBJECT_WUNLOCK(object); 150195840Sjhb offset = m[reqpage]->pindex; 151195840Sjhb 152195840Sjhb /* 153195840Sjhb * Lookup the physical address of the requested page. An initial 154195840Sjhb * value of '1' instead of '0' is used so we can assert that the 155195840Sjhb * page is found since '0' can be a valid page-aligned physical 156195840Sjhb * address. 157195840Sjhb */ 158195840Sjhb space = 0; 159195840Sjhb paddr = 1; 160195840Sjhb for (i = 0; i < sg->sg_nseg; i++) { 161195840Sjhb if (space + sg->sg_segs[i].ss_len <= (offset * PAGE_SIZE)) { 162195840Sjhb space += sg->sg_segs[i].ss_len; 163195840Sjhb continue; 164195840Sjhb } 165195840Sjhb paddr = sg->sg_segs[i].ss_paddr + offset * PAGE_SIZE - space; 166195840Sjhb break; 167195840Sjhb } 168195840Sjhb KASSERT(paddr != 1, ("invalid SG page index")); 169195840Sjhb 170195840Sjhb /* If "paddr" is a real page, perform a sanity check on "memattr". */ 171195840Sjhb if ((m_paddr = vm_phys_paddr_to_vm_page(paddr)) != NULL && 172195840Sjhb pmap_page_get_memattr(m_paddr) != memattr) { 173195840Sjhb memattr = pmap_page_get_memattr(m_paddr); 174195840Sjhb printf( 175195840Sjhb "WARNING: A device driver has set \"memattr\" inconsistently.\n"); 176195840Sjhb } 177195840Sjhb 178195840Sjhb /* Return a fake page for the requested page. */ 179195840Sjhb KASSERT(!(m[reqpage]->flags & PG_FICTITIOUS), 180195840Sjhb ("backing page for SG is fake")); 181195840Sjhb 182195840Sjhb /* Construct a new fake page. */ 183219476Salc page = vm_page_getfake(paddr, memattr); 184248084Sattilio VM_OBJECT_WLOCK(object); 185254182Skib TAILQ_INSERT_TAIL(&object->un_pager.sgp.sgp_pglist, page, plinks.q); 186195840Sjhb 187195840Sjhb /* Free the original pages and insert this fake page into the object. */ 188207410Skmacy for (i = 0; i < count; i++) { 189254141Sattilio if (i == reqpage && 190254141Sattilio vm_page_replace(page, object, offset) != m[i]) 191254141Sattilio panic("sg_pager_getpages: invalid place replacement"); 192207410Skmacy vm_page_lock(m[i]); 193195840Sjhb vm_page_free(m[i]); 194207410Skmacy vm_page_unlock(m[i]); 195207410Skmacy } 196195840Sjhb m[reqpage] = page; 197196637Sjhb page->valid = VM_PAGE_BITS_ALL; 198195840Sjhb 199195840Sjhb return (VM_PAGER_OK); 200195840Sjhb} 201195840Sjhb 202195840Sjhbstatic void 203195840Sjhbsg_pager_putpages(vm_object_t object, vm_page_t *m, int count, 204195840Sjhb boolean_t sync, int *rtvals) 205195840Sjhb{ 206195840Sjhb 207195840Sjhb panic("sg_pager_putpage called"); 208195840Sjhb} 209195840Sjhb 210195840Sjhbstatic boolean_t 211195840Sjhbsg_pager_haspage(vm_object_t object, vm_pindex_t pindex, int *before, 212195840Sjhb int *after) 213195840Sjhb{ 214195840Sjhb 215195840Sjhb if (before != NULL) 216195840Sjhb *before = 0; 217195840Sjhb if (after != NULL) 218195840Sjhb *after = 0; 219195840Sjhb return (TRUE); 220195840Sjhb} 221