101381811SJohn Baldwin /*- 2fe267a55SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3fe267a55SPedro F. Giffuni * 4179fa75eSJohn Baldwin * Copyright (c) 2009 Hudson River Trading LLC 501381811SJohn Baldwin * Written by: John H. Baldwin <jhb@FreeBSD.org> 601381811SJohn Baldwin * All rights reserved. 701381811SJohn Baldwin * 801381811SJohn Baldwin * Redistribution and use in source and binary forms, with or without 901381811SJohn Baldwin * modification, are permitted provided that the following conditions 1001381811SJohn Baldwin * are met: 1101381811SJohn Baldwin * 1. Redistributions of source code must retain the above copyright 1201381811SJohn Baldwin * notice, this list of conditions and the following disclaimer. 1301381811SJohn Baldwin * 2. Redistributions in binary form must reproduce the above copyright 1401381811SJohn Baldwin * notice, this list of conditions and the following disclaimer in the 1501381811SJohn Baldwin * documentation and/or other materials provided with the distribution. 1601381811SJohn Baldwin * 1701381811SJohn Baldwin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1801381811SJohn Baldwin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1901381811SJohn Baldwin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2001381811SJohn Baldwin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2101381811SJohn Baldwin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2201381811SJohn Baldwin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2301381811SJohn Baldwin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2401381811SJohn Baldwin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2501381811SJohn Baldwin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2601381811SJohn Baldwin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2701381811SJohn Baldwin * SUCH DAMAGE. 2801381811SJohn Baldwin */ 2901381811SJohn Baldwin 3001381811SJohn Baldwin #include <sys/cdefs.h> 3101381811SJohn Baldwin __FBSDID("$FreeBSD$"); 3201381811SJohn Baldwin 3301381811SJohn Baldwin /* 3401381811SJohn Baldwin * This pager manages OBJT_SG objects. These objects are backed by 3501381811SJohn Baldwin * a scatter/gather list of physical address ranges. 3601381811SJohn Baldwin */ 3701381811SJohn Baldwin 3801381811SJohn Baldwin #include <sys/param.h> 3901381811SJohn Baldwin #include <sys/lock.h> 4001381811SJohn Baldwin #include <sys/mutex.h> 4189f6b863SAttilio Rao #include <sys/rwlock.h> 4201381811SJohn Baldwin #include <sys/sglist.h> 4300a3fe96SKonstantin Belousov #include <sys/user.h> 449ed01c32SGleb Smirnoff #include <sys/vmmeter.h> 459ed01c32SGleb Smirnoff 4601381811SJohn Baldwin #include <vm/vm.h> 471c771f92SKonstantin Belousov #include <vm/vm_param.h> 4801381811SJohn Baldwin #include <vm/vm_object.h> 4901381811SJohn Baldwin #include <vm/vm_page.h> 5001381811SJohn Baldwin #include <vm/vm_pager.h> 5143f48b65SKonstantin Belousov #include <vm/vm_phys.h> 5201381811SJohn Baldwin #include <vm/uma.h> 5301381811SJohn Baldwin 5401381811SJohn Baldwin static vm_object_t sg_pager_alloc(void *, vm_ooffset_t, vm_prot_t, 5501381811SJohn Baldwin vm_ooffset_t, struct ucred *); 5601381811SJohn Baldwin static void sg_pager_dealloc(vm_object_t); 57b0cd2017SGleb Smirnoff static int sg_pager_getpages(vm_object_t, vm_page_t *, int, int *, int *); 5801381811SJohn Baldwin static void sg_pager_putpages(vm_object_t, vm_page_t *, int, 59*f74be55eSDimitry Andric int, int *); 6001381811SJohn Baldwin static boolean_t sg_pager_haspage(vm_object_t, vm_pindex_t, int *, 6101381811SJohn Baldwin int *); 6201381811SJohn Baldwin 63d474440aSKonstantin Belousov const struct pagerops sgpagerops = { 6400a3fe96SKonstantin Belousov .pgo_kvme_type = KVME_TYPE_SG, 6501381811SJohn Baldwin .pgo_alloc = sg_pager_alloc, 6601381811SJohn Baldwin .pgo_dealloc = sg_pager_dealloc, 6701381811SJohn Baldwin .pgo_getpages = sg_pager_getpages, 6801381811SJohn Baldwin .pgo_putpages = sg_pager_putpages, 6901381811SJohn Baldwin .pgo_haspage = sg_pager_haspage, 7001381811SJohn Baldwin }; 7101381811SJohn Baldwin 7201381811SJohn Baldwin static vm_object_t 7301381811SJohn Baldwin sg_pager_alloc(void *handle, vm_ooffset_t size, vm_prot_t prot, 7401381811SJohn Baldwin vm_ooffset_t foff, struct ucred *cred) 7501381811SJohn Baldwin { 7601381811SJohn Baldwin struct sglist *sg; 7701381811SJohn Baldwin vm_object_t object; 7801381811SJohn Baldwin vm_pindex_t npages, pindex; 7901381811SJohn Baldwin int i; 8001381811SJohn Baldwin 8101381811SJohn Baldwin /* 8201381811SJohn Baldwin * Offset should be page aligned. 8301381811SJohn Baldwin */ 8401381811SJohn Baldwin if (foff & PAGE_MASK) 8501381811SJohn Baldwin return (NULL); 8601381811SJohn Baldwin 8701381811SJohn Baldwin /* 8801381811SJohn Baldwin * The scatter/gather list must only include page-aligned 8901381811SJohn Baldwin * ranges. 9001381811SJohn Baldwin */ 9101381811SJohn Baldwin npages = 0; 9201381811SJohn Baldwin sg = handle; 9301381811SJohn Baldwin for (i = 0; i < sg->sg_nseg; i++) { 9401381811SJohn Baldwin if ((sg->sg_segs[i].ss_paddr % PAGE_SIZE) != 0 || 9501381811SJohn Baldwin (sg->sg_segs[i].ss_len % PAGE_SIZE) != 0) 9601381811SJohn Baldwin return (NULL); 9701381811SJohn Baldwin npages += sg->sg_segs[i].ss_len / PAGE_SIZE; 9801381811SJohn Baldwin } 9901381811SJohn Baldwin 10001381811SJohn Baldwin /* 10101381811SJohn Baldwin * The scatter/gather list has a fixed size. Refuse requests 10201381811SJohn Baldwin * to map beyond that. 10301381811SJohn Baldwin */ 10401381811SJohn Baldwin size = round_page(size); 10510d9120cSKonstantin Belousov pindex = OFF_TO_IDX(foff) + OFF_TO_IDX(size); 10610d9120cSKonstantin Belousov if (pindex > npages || pindex < OFF_TO_IDX(foff) || 10710d9120cSKonstantin Belousov pindex < OFF_TO_IDX(size)) 10801381811SJohn Baldwin return (NULL); 10901381811SJohn Baldwin 11001381811SJohn Baldwin /* 11101381811SJohn Baldwin * Allocate a new object and associate it with the 11201381811SJohn Baldwin * scatter/gather list. It is ok for our purposes to have 11301381811SJohn Baldwin * multiple VM objects associated with the same scatter/gather 11401381811SJohn Baldwin * list because scatter/gather lists are static. This is also 11501381811SJohn Baldwin * simpler than ensuring a unique object per scatter/gather 11601381811SJohn Baldwin * list. 11701381811SJohn Baldwin */ 11801381811SJohn Baldwin object = vm_object_allocate(OBJT_SG, npages); 11901381811SJohn Baldwin object->handle = sglist_hold(sg); 12001381811SJohn Baldwin TAILQ_INIT(&object->un_pager.sgp.sgp_pglist); 12101381811SJohn Baldwin return (object); 12201381811SJohn Baldwin } 12301381811SJohn Baldwin 12401381811SJohn Baldwin static void 12501381811SJohn Baldwin sg_pager_dealloc(vm_object_t object) 12601381811SJohn Baldwin { 12701381811SJohn Baldwin struct sglist *sg; 12801381811SJohn Baldwin vm_page_t m; 12901381811SJohn Baldwin 13001381811SJohn Baldwin /* 13101381811SJohn Baldwin * Free up our fake pages. 13201381811SJohn Baldwin */ 13301381811SJohn Baldwin while ((m = TAILQ_FIRST(&object->un_pager.sgp.sgp_pglist)) != 0) { 1343cf3b4e6SJeff Roberson if (vm_page_busy_acquire(m, VM_ALLOC_WAITFAIL) == 0) 1353cf3b4e6SJeff Roberson continue; 136c325e866SKonstantin Belousov TAILQ_REMOVE(&object->un_pager.sgp.sgp_pglist, m, plinks.q); 13710cf2560SAlan Cox vm_page_putfake(m); 13801381811SJohn Baldwin } 13901381811SJohn Baldwin 14001381811SJohn Baldwin sg = object->handle; 14101381811SJohn Baldwin sglist_free(sg); 142e735691bSJohn Baldwin object->handle = NULL; 143e735691bSJohn Baldwin object->type = OBJT_DEAD; 14401381811SJohn Baldwin } 14501381811SJohn Baldwin 14601381811SJohn Baldwin static int 147b0cd2017SGleb Smirnoff sg_pager_getpages(vm_object_t object, vm_page_t *m, int count, int *rbehind, 148b0cd2017SGleb Smirnoff int *rahead) 14901381811SJohn Baldwin { 15001381811SJohn Baldwin struct sglist *sg; 15101381811SJohn Baldwin vm_page_t m_paddr, page; 15201381811SJohn Baldwin vm_pindex_t offset; 15301381811SJohn Baldwin vm_paddr_t paddr; 15401381811SJohn Baldwin vm_memattr_t memattr; 15501381811SJohn Baldwin size_t space; 15601381811SJohn Baldwin int i; 15701381811SJohn Baldwin 158b0cd2017SGleb Smirnoff /* Since our haspage reports zero after/before, the count is 1. */ 159b0cd2017SGleb Smirnoff KASSERT(count == 1, ("%s: count %d", __func__, count)); 160d6e13f3bSJeff Roberson /* Handle is stable while paging is in progress. */ 16101381811SJohn Baldwin sg = object->handle; 16201381811SJohn Baldwin memattr = object->memattr; 163b0cd2017SGleb Smirnoff offset = m[0]->pindex; 16401381811SJohn Baldwin 16501381811SJohn Baldwin /* 16601381811SJohn Baldwin * Lookup the physical address of the requested page. An initial 16701381811SJohn Baldwin * value of '1' instead of '0' is used so we can assert that the 16801381811SJohn Baldwin * page is found since '0' can be a valid page-aligned physical 16901381811SJohn Baldwin * address. 17001381811SJohn Baldwin */ 17101381811SJohn Baldwin space = 0; 17201381811SJohn Baldwin paddr = 1; 17301381811SJohn Baldwin for (i = 0; i < sg->sg_nseg; i++) { 17401381811SJohn Baldwin if (space + sg->sg_segs[i].ss_len <= (offset * PAGE_SIZE)) { 17501381811SJohn Baldwin space += sg->sg_segs[i].ss_len; 17601381811SJohn Baldwin continue; 17701381811SJohn Baldwin } 17801381811SJohn Baldwin paddr = sg->sg_segs[i].ss_paddr + offset * PAGE_SIZE - space; 17901381811SJohn Baldwin break; 18001381811SJohn Baldwin } 18101381811SJohn Baldwin KASSERT(paddr != 1, ("invalid SG page index")); 18201381811SJohn Baldwin 18301381811SJohn Baldwin /* If "paddr" is a real page, perform a sanity check on "memattr". */ 18401381811SJohn Baldwin if ((m_paddr = vm_phys_paddr_to_vm_page(paddr)) != NULL && 18501381811SJohn Baldwin pmap_page_get_memattr(m_paddr) != memattr) { 18601381811SJohn Baldwin memattr = pmap_page_get_memattr(m_paddr); 18701381811SJohn Baldwin printf( 18801381811SJohn Baldwin "WARNING: A device driver has set \"memattr\" inconsistently.\n"); 18901381811SJohn Baldwin } 19001381811SJohn Baldwin 19101381811SJohn Baldwin /* Return a fake page for the requested page. */ 192b0cd2017SGleb Smirnoff KASSERT(!(m[0]->flags & PG_FICTITIOUS), 19301381811SJohn Baldwin ("backing page for SG is fake")); 19401381811SJohn Baldwin 19501381811SJohn Baldwin /* Construct a new fake page. */ 19610cf2560SAlan Cox page = vm_page_getfake(paddr, memattr); 19789f6b863SAttilio Rao VM_OBJECT_WLOCK(object); 198c325e866SKonstantin Belousov TAILQ_INSERT_TAIL(&object->un_pager.sgp.sgp_pglist, page, plinks.q); 1993cf3b4e6SJeff Roberson vm_page_replace(page, object, offset, m[0]); 200d6e13f3bSJeff Roberson VM_OBJECT_WUNLOCK(object); 201b0cd2017SGleb Smirnoff m[0] = page; 2020012f373SJeff Roberson vm_page_valid(page); 20301381811SJohn Baldwin 204b0cd2017SGleb Smirnoff if (rbehind) 205b0cd2017SGleb Smirnoff *rbehind = 0; 206b0cd2017SGleb Smirnoff if (rahead) 207b0cd2017SGleb Smirnoff *rahead = 0; 208b0cd2017SGleb Smirnoff 20901381811SJohn Baldwin return (VM_PAGER_OK); 21001381811SJohn Baldwin } 21101381811SJohn Baldwin 21201381811SJohn Baldwin static void 21301381811SJohn Baldwin sg_pager_putpages(vm_object_t object, vm_page_t *m, int count, 214*f74be55eSDimitry Andric int flags, int *rtvals) 21501381811SJohn Baldwin { 21601381811SJohn Baldwin 21701381811SJohn Baldwin panic("sg_pager_putpage called"); 21801381811SJohn Baldwin } 21901381811SJohn Baldwin 22001381811SJohn Baldwin static boolean_t 22101381811SJohn Baldwin sg_pager_haspage(vm_object_t object, vm_pindex_t pindex, int *before, 22201381811SJohn Baldwin int *after) 22301381811SJohn Baldwin { 22401381811SJohn Baldwin 22501381811SJohn Baldwin if (before != NULL) 22601381811SJohn Baldwin *before = 0; 22701381811SJohn Baldwin if (after != NULL) 22801381811SJohn Baldwin *after = 0; 22901381811SJohn Baldwin return (TRUE); 23001381811SJohn Baldwin } 231