101381811SJohn Baldwin /*- 201381811SJohn Baldwin * Copyright (c) 2009 Advanced Computing Technologies LLC 301381811SJohn Baldwin * Written by: John H. Baldwin <jhb@FreeBSD.org> 401381811SJohn Baldwin * All rights reserved. 501381811SJohn Baldwin * 601381811SJohn Baldwin * Redistribution and use in source and binary forms, with or without 701381811SJohn Baldwin * modification, are permitted provided that the following conditions 801381811SJohn Baldwin * are met: 901381811SJohn Baldwin * 1. Redistributions of source code must retain the above copyright 1001381811SJohn Baldwin * notice, this list of conditions and the following disclaimer. 1101381811SJohn Baldwin * 2. Redistributions in binary form must reproduce the above copyright 1201381811SJohn Baldwin * notice, this list of conditions and the following disclaimer in the 1301381811SJohn Baldwin * documentation and/or other materials provided with the distribution. 1401381811SJohn Baldwin * 1501381811SJohn Baldwin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1601381811SJohn Baldwin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1701381811SJohn Baldwin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1801381811SJohn Baldwin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1901381811SJohn Baldwin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2001381811SJohn Baldwin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2101381811SJohn Baldwin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2201381811SJohn Baldwin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2301381811SJohn Baldwin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2401381811SJohn Baldwin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2501381811SJohn Baldwin * SUCH DAMAGE. 2601381811SJohn Baldwin */ 2701381811SJohn Baldwin 2801381811SJohn Baldwin #include <sys/cdefs.h> 2901381811SJohn Baldwin __FBSDID("$FreeBSD$"); 3001381811SJohn Baldwin 3101381811SJohn Baldwin /* 3201381811SJohn Baldwin * This pager manages OBJT_SG objects. These objects are backed by 3301381811SJohn Baldwin * a scatter/gather list of physical address ranges. 3401381811SJohn Baldwin */ 3501381811SJohn Baldwin 3601381811SJohn Baldwin #include <sys/param.h> 3701381811SJohn Baldwin #include <sys/lock.h> 3801381811SJohn Baldwin #include <sys/mutex.h> 3901381811SJohn Baldwin #include <sys/sglist.h> 4001381811SJohn Baldwin #include <vm/vm.h> 411c771f92SKonstantin Belousov #include <vm/vm_param.h> 4201381811SJohn Baldwin #include <vm/vm_object.h> 4301381811SJohn Baldwin #include <vm/vm_page.h> 4401381811SJohn Baldwin #include <vm/vm_pager.h> 45*43f48b65SKonstantin Belousov #include <vm/vm_phys.h> 4601381811SJohn Baldwin #include <vm/uma.h> 4701381811SJohn Baldwin 4801381811SJohn Baldwin static vm_object_t sg_pager_alloc(void *, vm_ooffset_t, vm_prot_t, 4901381811SJohn Baldwin vm_ooffset_t, struct ucred *); 5001381811SJohn Baldwin static void sg_pager_dealloc(vm_object_t); 5101381811SJohn Baldwin static int sg_pager_getpages(vm_object_t, vm_page_t *, int, int); 5201381811SJohn Baldwin static void sg_pager_putpages(vm_object_t, vm_page_t *, int, 5301381811SJohn Baldwin boolean_t, int *); 5401381811SJohn Baldwin static boolean_t sg_pager_haspage(vm_object_t, vm_pindex_t, int *, 5501381811SJohn Baldwin int *); 5601381811SJohn Baldwin 5701381811SJohn Baldwin struct pagerops sgpagerops = { 5801381811SJohn Baldwin .pgo_alloc = sg_pager_alloc, 5901381811SJohn Baldwin .pgo_dealloc = sg_pager_dealloc, 6001381811SJohn Baldwin .pgo_getpages = sg_pager_getpages, 6101381811SJohn Baldwin .pgo_putpages = sg_pager_putpages, 6201381811SJohn Baldwin .pgo_haspage = sg_pager_haspage, 6301381811SJohn Baldwin }; 6401381811SJohn Baldwin 6501381811SJohn Baldwin static vm_object_t 6601381811SJohn Baldwin sg_pager_alloc(void *handle, vm_ooffset_t size, vm_prot_t prot, 6701381811SJohn Baldwin vm_ooffset_t foff, struct ucred *cred) 6801381811SJohn Baldwin { 6901381811SJohn Baldwin struct sglist *sg; 7001381811SJohn Baldwin vm_object_t object; 7101381811SJohn Baldwin vm_pindex_t npages, pindex; 7201381811SJohn Baldwin int i; 7301381811SJohn Baldwin 7401381811SJohn Baldwin /* 7501381811SJohn Baldwin * Offset should be page aligned. 7601381811SJohn Baldwin */ 7701381811SJohn Baldwin if (foff & PAGE_MASK) 7801381811SJohn Baldwin return (NULL); 7901381811SJohn Baldwin 8001381811SJohn Baldwin /* 8101381811SJohn Baldwin * The scatter/gather list must only include page-aligned 8201381811SJohn Baldwin * ranges. 8301381811SJohn Baldwin */ 8401381811SJohn Baldwin npages = 0; 8501381811SJohn Baldwin sg = handle; 8601381811SJohn Baldwin for (i = 0; i < sg->sg_nseg; i++) { 8701381811SJohn Baldwin if ((sg->sg_segs[i].ss_paddr % PAGE_SIZE) != 0 || 8801381811SJohn Baldwin (sg->sg_segs[i].ss_len % PAGE_SIZE) != 0) 8901381811SJohn Baldwin return (NULL); 9001381811SJohn Baldwin npages += sg->sg_segs[i].ss_len / PAGE_SIZE; 9101381811SJohn Baldwin } 9201381811SJohn Baldwin 9301381811SJohn Baldwin /* 9401381811SJohn Baldwin * The scatter/gather list has a fixed size. Refuse requests 9501381811SJohn Baldwin * to map beyond that. 9601381811SJohn Baldwin */ 9701381811SJohn Baldwin size = round_page(size); 9801381811SJohn Baldwin pindex = OFF_TO_IDX(foff + size); 9901381811SJohn Baldwin if (pindex > npages) 10001381811SJohn Baldwin return (NULL); 10101381811SJohn Baldwin 10201381811SJohn Baldwin /* 10301381811SJohn Baldwin * Allocate a new object and associate it with the 10401381811SJohn Baldwin * scatter/gather list. It is ok for our purposes to have 10501381811SJohn Baldwin * multiple VM objects associated with the same scatter/gather 10601381811SJohn Baldwin * list because scatter/gather lists are static. This is also 10701381811SJohn Baldwin * simpler than ensuring a unique object per scatter/gather 10801381811SJohn Baldwin * list. 10901381811SJohn Baldwin */ 11001381811SJohn Baldwin object = vm_object_allocate(OBJT_SG, npages); 11101381811SJohn Baldwin object->handle = sglist_hold(sg); 11201381811SJohn Baldwin TAILQ_INIT(&object->un_pager.sgp.sgp_pglist); 11301381811SJohn Baldwin return (object); 11401381811SJohn Baldwin } 11501381811SJohn Baldwin 11601381811SJohn Baldwin static void 11701381811SJohn Baldwin sg_pager_dealloc(vm_object_t object) 11801381811SJohn Baldwin { 11901381811SJohn Baldwin struct sglist *sg; 12001381811SJohn Baldwin vm_page_t m; 12101381811SJohn Baldwin 12201381811SJohn Baldwin /* 12301381811SJohn Baldwin * Free up our fake pages. 12401381811SJohn Baldwin */ 12501381811SJohn Baldwin while ((m = TAILQ_FIRST(&object->un_pager.sgp.sgp_pglist)) != 0) { 12601381811SJohn Baldwin TAILQ_REMOVE(&object->un_pager.sgp.sgp_pglist, m, pageq); 12710cf2560SAlan Cox vm_page_putfake(m); 12801381811SJohn Baldwin } 12901381811SJohn Baldwin 13001381811SJohn Baldwin sg = object->handle; 13101381811SJohn Baldwin sglist_free(sg); 13201381811SJohn Baldwin } 13301381811SJohn Baldwin 13401381811SJohn Baldwin static int 13501381811SJohn Baldwin sg_pager_getpages(vm_object_t object, vm_page_t *m, int count, int reqpage) 13601381811SJohn Baldwin { 13701381811SJohn Baldwin struct sglist *sg; 13801381811SJohn Baldwin vm_page_t m_paddr, page; 13901381811SJohn Baldwin vm_pindex_t offset; 14001381811SJohn Baldwin vm_paddr_t paddr; 14101381811SJohn Baldwin vm_memattr_t memattr; 14201381811SJohn Baldwin size_t space; 14301381811SJohn Baldwin int i; 14401381811SJohn Baldwin 14501381811SJohn Baldwin VM_OBJECT_LOCK_ASSERT(object, MA_OWNED); 14601381811SJohn Baldwin sg = object->handle; 14701381811SJohn Baldwin memattr = object->memattr; 14801381811SJohn Baldwin VM_OBJECT_UNLOCK(object); 14901381811SJohn Baldwin offset = m[reqpage]->pindex; 15001381811SJohn Baldwin 15101381811SJohn Baldwin /* 15201381811SJohn Baldwin * Lookup the physical address of the requested page. An initial 15301381811SJohn Baldwin * value of '1' instead of '0' is used so we can assert that the 15401381811SJohn Baldwin * page is found since '0' can be a valid page-aligned physical 15501381811SJohn Baldwin * address. 15601381811SJohn Baldwin */ 15701381811SJohn Baldwin space = 0; 15801381811SJohn Baldwin paddr = 1; 15901381811SJohn Baldwin for (i = 0; i < sg->sg_nseg; i++) { 16001381811SJohn Baldwin if (space + sg->sg_segs[i].ss_len <= (offset * PAGE_SIZE)) { 16101381811SJohn Baldwin space += sg->sg_segs[i].ss_len; 16201381811SJohn Baldwin continue; 16301381811SJohn Baldwin } 16401381811SJohn Baldwin paddr = sg->sg_segs[i].ss_paddr + offset * PAGE_SIZE - space; 16501381811SJohn Baldwin break; 16601381811SJohn Baldwin } 16701381811SJohn Baldwin KASSERT(paddr != 1, ("invalid SG page index")); 16801381811SJohn Baldwin 16901381811SJohn Baldwin /* If "paddr" is a real page, perform a sanity check on "memattr". */ 17001381811SJohn Baldwin if ((m_paddr = vm_phys_paddr_to_vm_page(paddr)) != NULL && 17101381811SJohn Baldwin pmap_page_get_memattr(m_paddr) != memattr) { 17201381811SJohn Baldwin memattr = pmap_page_get_memattr(m_paddr); 17301381811SJohn Baldwin printf( 17401381811SJohn Baldwin "WARNING: A device driver has set \"memattr\" inconsistently.\n"); 17501381811SJohn Baldwin } 17601381811SJohn Baldwin 17701381811SJohn Baldwin /* Return a fake page for the requested page. */ 17801381811SJohn Baldwin KASSERT(!(m[reqpage]->flags & PG_FICTITIOUS), 17901381811SJohn Baldwin ("backing page for SG is fake")); 18001381811SJohn Baldwin 18101381811SJohn Baldwin /* Construct a new fake page. */ 18210cf2560SAlan Cox page = vm_page_getfake(paddr, memattr); 18301381811SJohn Baldwin VM_OBJECT_LOCK(object); 18401381811SJohn Baldwin TAILQ_INSERT_TAIL(&object->un_pager.sgp.sgp_pglist, page, pageq); 18501381811SJohn Baldwin 18601381811SJohn Baldwin /* Free the original pages and insert this fake page into the object. */ 1872965a453SKip Macy for (i = 0; i < count; i++) { 1882965a453SKip Macy vm_page_lock(m[i]); 18901381811SJohn Baldwin vm_page_free(m[i]); 1902965a453SKip Macy vm_page_unlock(m[i]); 1912965a453SKip Macy } 19201381811SJohn Baldwin vm_page_insert(page, object, offset); 19301381811SJohn Baldwin m[reqpage] = page; 19458e279dbSJohn Baldwin page->valid = VM_PAGE_BITS_ALL; 19501381811SJohn Baldwin 19601381811SJohn Baldwin return (VM_PAGER_OK); 19701381811SJohn Baldwin } 19801381811SJohn Baldwin 19901381811SJohn Baldwin static void 20001381811SJohn Baldwin sg_pager_putpages(vm_object_t object, vm_page_t *m, int count, 20101381811SJohn Baldwin boolean_t sync, int *rtvals) 20201381811SJohn Baldwin { 20301381811SJohn Baldwin 20401381811SJohn Baldwin panic("sg_pager_putpage called"); 20501381811SJohn Baldwin } 20601381811SJohn Baldwin 20701381811SJohn Baldwin static boolean_t 20801381811SJohn Baldwin sg_pager_haspage(vm_object_t object, vm_pindex_t pindex, int *before, 20901381811SJohn Baldwin int *after) 21001381811SJohn Baldwin { 21101381811SJohn Baldwin 21201381811SJohn Baldwin if (before != NULL) 21301381811SJohn Baldwin *before = 0; 21401381811SJohn Baldwin if (after != NULL) 21501381811SJohn Baldwin *after = 0; 21601381811SJohn Baldwin return (TRUE); 21701381811SJohn Baldwin } 218