101381811SJohn Baldwin /*- 2*179fa75eSJohn Baldwin * Copyright (c) 2009 Hudson River Trading 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> 3989f6b863SAttilio Rao #include <sys/rwlock.h> 4001381811SJohn Baldwin #include <sys/sglist.h> 4101381811SJohn Baldwin #include <vm/vm.h> 421c771f92SKonstantin Belousov #include <vm/vm_param.h> 4301381811SJohn Baldwin #include <vm/vm_object.h> 4401381811SJohn Baldwin #include <vm/vm_page.h> 4501381811SJohn Baldwin #include <vm/vm_pager.h> 4643f48b65SKonstantin Belousov #include <vm/vm_phys.h> 4701381811SJohn Baldwin #include <vm/uma.h> 4801381811SJohn Baldwin 4901381811SJohn Baldwin static vm_object_t sg_pager_alloc(void *, vm_ooffset_t, vm_prot_t, 5001381811SJohn Baldwin vm_ooffset_t, struct ucred *); 5101381811SJohn Baldwin static void sg_pager_dealloc(vm_object_t); 5201381811SJohn Baldwin static int sg_pager_getpages(vm_object_t, vm_page_t *, int, int); 5301381811SJohn Baldwin static void sg_pager_putpages(vm_object_t, vm_page_t *, int, 5401381811SJohn Baldwin boolean_t, int *); 5501381811SJohn Baldwin static boolean_t sg_pager_haspage(vm_object_t, vm_pindex_t, int *, 5601381811SJohn Baldwin int *); 5701381811SJohn Baldwin 5801381811SJohn Baldwin struct pagerops sgpagerops = { 5901381811SJohn Baldwin .pgo_alloc = sg_pager_alloc, 6001381811SJohn Baldwin .pgo_dealloc = sg_pager_dealloc, 6101381811SJohn Baldwin .pgo_getpages = sg_pager_getpages, 6201381811SJohn Baldwin .pgo_putpages = sg_pager_putpages, 6301381811SJohn Baldwin .pgo_haspage = sg_pager_haspage, 6401381811SJohn Baldwin }; 6501381811SJohn Baldwin 6601381811SJohn Baldwin static vm_object_t 6701381811SJohn Baldwin sg_pager_alloc(void *handle, vm_ooffset_t size, vm_prot_t prot, 6801381811SJohn Baldwin vm_ooffset_t foff, struct ucred *cred) 6901381811SJohn Baldwin { 7001381811SJohn Baldwin struct sglist *sg; 7101381811SJohn Baldwin vm_object_t object; 7201381811SJohn Baldwin vm_pindex_t npages, pindex; 7301381811SJohn Baldwin int i; 7401381811SJohn Baldwin 7501381811SJohn Baldwin /* 7601381811SJohn Baldwin * Offset should be page aligned. 7701381811SJohn Baldwin */ 7801381811SJohn Baldwin if (foff & PAGE_MASK) 7901381811SJohn Baldwin return (NULL); 8001381811SJohn Baldwin 8101381811SJohn Baldwin /* 8201381811SJohn Baldwin * The scatter/gather list must only include page-aligned 8301381811SJohn Baldwin * ranges. 8401381811SJohn Baldwin */ 8501381811SJohn Baldwin npages = 0; 8601381811SJohn Baldwin sg = handle; 8701381811SJohn Baldwin for (i = 0; i < sg->sg_nseg; i++) { 8801381811SJohn Baldwin if ((sg->sg_segs[i].ss_paddr % PAGE_SIZE) != 0 || 8901381811SJohn Baldwin (sg->sg_segs[i].ss_len % PAGE_SIZE) != 0) 9001381811SJohn Baldwin return (NULL); 9101381811SJohn Baldwin npages += sg->sg_segs[i].ss_len / PAGE_SIZE; 9201381811SJohn Baldwin } 9301381811SJohn Baldwin 9401381811SJohn Baldwin /* 9501381811SJohn Baldwin * The scatter/gather list has a fixed size. Refuse requests 9601381811SJohn Baldwin * to map beyond that. 9701381811SJohn Baldwin */ 9801381811SJohn Baldwin size = round_page(size); 9901381811SJohn Baldwin pindex = OFF_TO_IDX(foff + size); 10001381811SJohn Baldwin if (pindex > npages) 10101381811SJohn Baldwin return (NULL); 10201381811SJohn Baldwin 10301381811SJohn Baldwin /* 10401381811SJohn Baldwin * Allocate a new object and associate it with the 10501381811SJohn Baldwin * scatter/gather list. It is ok for our purposes to have 10601381811SJohn Baldwin * multiple VM objects associated with the same scatter/gather 10701381811SJohn Baldwin * list because scatter/gather lists are static. This is also 10801381811SJohn Baldwin * simpler than ensuring a unique object per scatter/gather 10901381811SJohn Baldwin * list. 11001381811SJohn Baldwin */ 11101381811SJohn Baldwin object = vm_object_allocate(OBJT_SG, npages); 11201381811SJohn Baldwin object->handle = sglist_hold(sg); 11301381811SJohn Baldwin TAILQ_INIT(&object->un_pager.sgp.sgp_pglist); 11401381811SJohn Baldwin return (object); 11501381811SJohn Baldwin } 11601381811SJohn Baldwin 11701381811SJohn Baldwin static void 11801381811SJohn Baldwin sg_pager_dealloc(vm_object_t object) 11901381811SJohn Baldwin { 12001381811SJohn Baldwin struct sglist *sg; 12101381811SJohn Baldwin vm_page_t m; 12201381811SJohn Baldwin 12301381811SJohn Baldwin /* 12401381811SJohn Baldwin * Free up our fake pages. 12501381811SJohn Baldwin */ 12601381811SJohn Baldwin while ((m = TAILQ_FIRST(&object->un_pager.sgp.sgp_pglist)) != 0) { 127c325e866SKonstantin Belousov TAILQ_REMOVE(&object->un_pager.sgp.sgp_pglist, m, plinks.q); 12810cf2560SAlan Cox vm_page_putfake(m); 12901381811SJohn Baldwin } 13001381811SJohn Baldwin 13101381811SJohn Baldwin sg = object->handle; 13201381811SJohn Baldwin sglist_free(sg); 13301381811SJohn Baldwin } 13401381811SJohn Baldwin 13501381811SJohn Baldwin static int 13601381811SJohn Baldwin sg_pager_getpages(vm_object_t object, vm_page_t *m, int count, int reqpage) 13701381811SJohn Baldwin { 13801381811SJohn Baldwin struct sglist *sg; 13901381811SJohn Baldwin vm_page_t m_paddr, page; 14001381811SJohn Baldwin vm_pindex_t offset; 14101381811SJohn Baldwin vm_paddr_t paddr; 14201381811SJohn Baldwin vm_memattr_t memattr; 14301381811SJohn Baldwin size_t space; 14401381811SJohn Baldwin int i; 14501381811SJohn Baldwin 14689f6b863SAttilio Rao VM_OBJECT_ASSERT_WLOCKED(object); 14701381811SJohn Baldwin sg = object->handle; 14801381811SJohn Baldwin memattr = object->memattr; 14989f6b863SAttilio Rao VM_OBJECT_WUNLOCK(object); 15001381811SJohn Baldwin offset = m[reqpage]->pindex; 15101381811SJohn Baldwin 15201381811SJohn Baldwin /* 15301381811SJohn Baldwin * Lookup the physical address of the requested page. An initial 15401381811SJohn Baldwin * value of '1' instead of '0' is used so we can assert that the 15501381811SJohn Baldwin * page is found since '0' can be a valid page-aligned physical 15601381811SJohn Baldwin * address. 15701381811SJohn Baldwin */ 15801381811SJohn Baldwin space = 0; 15901381811SJohn Baldwin paddr = 1; 16001381811SJohn Baldwin for (i = 0; i < sg->sg_nseg; i++) { 16101381811SJohn Baldwin if (space + sg->sg_segs[i].ss_len <= (offset * PAGE_SIZE)) { 16201381811SJohn Baldwin space += sg->sg_segs[i].ss_len; 16301381811SJohn Baldwin continue; 16401381811SJohn Baldwin } 16501381811SJohn Baldwin paddr = sg->sg_segs[i].ss_paddr + offset * PAGE_SIZE - space; 16601381811SJohn Baldwin break; 16701381811SJohn Baldwin } 16801381811SJohn Baldwin KASSERT(paddr != 1, ("invalid SG page index")); 16901381811SJohn Baldwin 17001381811SJohn Baldwin /* If "paddr" is a real page, perform a sanity check on "memattr". */ 17101381811SJohn Baldwin if ((m_paddr = vm_phys_paddr_to_vm_page(paddr)) != NULL && 17201381811SJohn Baldwin pmap_page_get_memattr(m_paddr) != memattr) { 17301381811SJohn Baldwin memattr = pmap_page_get_memattr(m_paddr); 17401381811SJohn Baldwin printf( 17501381811SJohn Baldwin "WARNING: A device driver has set \"memattr\" inconsistently.\n"); 17601381811SJohn Baldwin } 17701381811SJohn Baldwin 17801381811SJohn Baldwin /* Return a fake page for the requested page. */ 17901381811SJohn Baldwin KASSERT(!(m[reqpage]->flags & PG_FICTITIOUS), 18001381811SJohn Baldwin ("backing page for SG is fake")); 18101381811SJohn Baldwin 18201381811SJohn Baldwin /* Construct a new fake page. */ 18310cf2560SAlan Cox page = vm_page_getfake(paddr, memattr); 18489f6b863SAttilio Rao VM_OBJECT_WLOCK(object); 185c325e866SKonstantin Belousov TAILQ_INSERT_TAIL(&object->un_pager.sgp.sgp_pglist, page, plinks.q); 18601381811SJohn Baldwin 18701381811SJohn Baldwin /* Free the original pages and insert this fake page into the object. */ 1882965a453SKip Macy for (i = 0; i < count; i++) { 189e946b949SAttilio Rao if (i == reqpage && 190e946b949SAttilio Rao vm_page_replace(page, object, offset) != m[i]) 191e946b949SAttilio Rao panic("sg_pager_getpages: invalid place replacement"); 1922965a453SKip Macy vm_page_lock(m[i]); 19301381811SJohn Baldwin vm_page_free(m[i]); 1942965a453SKip Macy vm_page_unlock(m[i]); 1952965a453SKip Macy } 19601381811SJohn Baldwin m[reqpage] = page; 19758e279dbSJohn Baldwin page->valid = VM_PAGE_BITS_ALL; 19801381811SJohn Baldwin 19901381811SJohn Baldwin return (VM_PAGER_OK); 20001381811SJohn Baldwin } 20101381811SJohn Baldwin 20201381811SJohn Baldwin static void 20301381811SJohn Baldwin sg_pager_putpages(vm_object_t object, vm_page_t *m, int count, 20401381811SJohn Baldwin boolean_t sync, int *rtvals) 20501381811SJohn Baldwin { 20601381811SJohn Baldwin 20701381811SJohn Baldwin panic("sg_pager_putpage called"); 20801381811SJohn Baldwin } 20901381811SJohn Baldwin 21001381811SJohn Baldwin static boolean_t 21101381811SJohn Baldwin sg_pager_haspage(vm_object_t object, vm_pindex_t pindex, int *before, 21201381811SJohn Baldwin int *after) 21301381811SJohn Baldwin { 21401381811SJohn Baldwin 21501381811SJohn Baldwin if (before != NULL) 21601381811SJohn Baldwin *before = 0; 21701381811SJohn Baldwin if (after != NULL) 21801381811SJohn Baldwin *after = 0; 21901381811SJohn Baldwin return (TRUE); 22001381811SJohn Baldwin } 221