xref: /dragonfly/sys/dev/drm/linux_shmem.c (revision 78973132)
11964046dSFrançois Tigeot /*-
21964046dSFrançois Tigeot  * Copyright (c) 2011 The FreeBSD Foundation
3a85cb24fSFrançois Tigeot  * Copyright (c) 2014-2020 François Tigeot <ftigeot@wolfpond.org>
41964046dSFrançois Tigeot  * All rights reserved.
51964046dSFrançois Tigeot  *
61964046dSFrançois Tigeot  * Portions of this software were developed by Konstantin Belousov
71964046dSFrançois Tigeot  * under sponsorship from the FreeBSD Foundation.
81964046dSFrançois Tigeot  *
91964046dSFrançois Tigeot  * Redistribution and use in source and binary forms, with or without
101964046dSFrançois Tigeot  * modification, are permitted provided that the following conditions
111964046dSFrançois Tigeot  * are met:
121964046dSFrançois Tigeot  * 1. Redistributions of source code must retain the above copyright
131964046dSFrançois Tigeot  *    notice, this list of conditions and the following disclaimer.
141964046dSFrançois Tigeot  * 2. Redistributions in binary form must reproduce the above copyright
151964046dSFrançois Tigeot  *    notice, this list of conditions and the following disclaimer in the
161964046dSFrançois Tigeot  *    documentation and/or other materials provided with the distribution.
171964046dSFrançois Tigeot  *
181964046dSFrançois Tigeot  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
191964046dSFrançois Tigeot  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
201964046dSFrançois Tigeot  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
211964046dSFrançois Tigeot  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
221964046dSFrançois Tigeot  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
231964046dSFrançois Tigeot  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
241964046dSFrançois Tigeot  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
251964046dSFrançois Tigeot  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
261964046dSFrançois Tigeot  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
271964046dSFrançois Tigeot  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
281964046dSFrançois Tigeot  * SUCH DAMAGE.
291964046dSFrançois Tigeot  *
301964046dSFrançois Tigeot  */
311964046dSFrançois Tigeot 
321964046dSFrançois Tigeot #include <vm/vm.h>
331964046dSFrançois Tigeot #include <vm/vm_page.h>
341964046dSFrançois Tigeot #include <vm/vm_page2.h>
351964046dSFrançois Tigeot #include <vm/vm_pager.h>
36*78973132SSergey Zigachev #include <vm/vm_extern.h>
371964046dSFrançois Tigeot 
381964046dSFrançois Tigeot #include <linux/err.h>
391964046dSFrançois Tigeot #include <linux/shmem_fs.h>
401964046dSFrançois Tigeot 
412854a88cSMatthew Dillon /*
422854a88cSMatthew Dillon  * This code is typically called with a normal VM object to access
432854a88cSMatthew Dillon  * data from a userspace shared memory mapping.  However, handle the
442854a88cSMatthew Dillon  * case where it might be called with OBJT_MGTDEVICE anyway.
452854a88cSMatthew Dillon  */
46f0bba3d1SFrançois Tigeot struct page *
shmem_read_mapping_page(vm_object_t object,vm_pindex_t pindex)471964046dSFrançois Tigeot shmem_read_mapping_page(vm_object_t object, vm_pindex_t pindex)
481964046dSFrançois Tigeot {
491964046dSFrançois Tigeot 	vm_page_t m;
501964046dSFrançois Tigeot 	int rv;
511964046dSFrançois Tigeot 
52afa4e707SFrançois Tigeot 	VM_OBJECT_LOCK(object);
532854a88cSMatthew Dillon 	if (object->type == OBJT_MGTDEVICE) {
542854a88cSMatthew Dillon 		m = NULL;
552854a88cSMatthew Dillon 		rv = vm_pager_get_page(object, pindex, &m, 1);
562854a88cSMatthew Dillon 		if (m == NULL)
572854a88cSMatthew Dillon 			return ERR_PTR(-ENOMEM);
582854a88cSMatthew Dillon 		if (rv != VM_PAGER_OK) {
592854a88cSMatthew Dillon 			vm_page_free(m);
602854a88cSMatthew Dillon 			return ERR_PTR(-ENOMEM);
612854a88cSMatthew Dillon 		}
622854a88cSMatthew Dillon 	} else {
632854a88cSMatthew Dillon 		m = vm_page_grab(object, pindex,
642854a88cSMatthew Dillon 				 VM_ALLOC_NORMAL | VM_ALLOC_RETRY);
651964046dSFrançois Tigeot 		if (m->valid != VM_PAGE_BITS_ALL) {
661964046dSFrançois Tigeot 			if (vm_pager_has_page(object, pindex)) {
672854a88cSMatthew Dillon 				rv = vm_pager_get_page(object, pindex, &m, 1);
681964046dSFrançois Tigeot 				m = vm_page_lookup(object, pindex);
691964046dSFrançois Tigeot 				if (m == NULL)
701964046dSFrançois Tigeot 					return ERR_PTR(-ENOMEM);
711964046dSFrançois Tigeot 				if (rv != VM_PAGER_OK) {
721964046dSFrançois Tigeot 					vm_page_free(m);
731964046dSFrançois Tigeot 					return ERR_PTR(-ENOMEM);
741964046dSFrançois Tigeot 				}
751964046dSFrançois Tigeot 			} else {
761964046dSFrançois Tigeot 				pmap_zero_page(VM_PAGE_TO_PHYS(m));
771964046dSFrançois Tigeot 				m->valid = VM_PAGE_BITS_ALL;
781964046dSFrançois Tigeot 				m->dirty = 0;
791964046dSFrançois Tigeot 			}
801964046dSFrançois Tigeot 		}
812854a88cSMatthew Dillon 	}
822854a88cSMatthew Dillon 	vm_page_wire(m);		/* put_page() undoes this */
831964046dSFrançois Tigeot 	vm_page_wakeup(m);
84afa4e707SFrançois Tigeot 	VM_OBJECT_UNLOCK(object);
85afa4e707SFrançois Tigeot 
86f0bba3d1SFrançois Tigeot 	return (struct page *)m;
871964046dSFrançois Tigeot }
88a85cb24fSFrançois Tigeot 
89a85cb24fSFrançois Tigeot struct page *
shmem_read_mapping_page_gfp(struct vm_object * mapping,pgoff_t index,gfp_t gfp_mask)90a85cb24fSFrançois Tigeot shmem_read_mapping_page_gfp(struct vm_object *mapping,
91a85cb24fSFrançois Tigeot     pgoff_t index, gfp_t gfp_mask)
92a85cb24fSFrançois Tigeot {
93a85cb24fSFrançois Tigeot 	return shmem_read_mapping_page(mapping, index);
94a85cb24fSFrançois Tigeot }
953f2dd94aSFrançois Tigeot 
963f2dd94aSFrançois Tigeot #include <linux/fs.h>
973f2dd94aSFrançois Tigeot 
983f2dd94aSFrançois Tigeot int
pagecache_write_begin(struct vm_object * obj,struct address_space * mapping,loff_t pos,unsigned len,unsigned flags,struct page ** pagep,void ** fsdata)993f2dd94aSFrançois Tigeot pagecache_write_begin(struct vm_object *obj, struct address_space *mapping,
1003f2dd94aSFrançois Tigeot     loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata)
1013f2dd94aSFrançois Tigeot {
1023f2dd94aSFrançois Tigeot 	*pagep = shmem_read_mapping_page(obj, OFF_TO_IDX(pos));
1033f2dd94aSFrançois Tigeot 
1043f2dd94aSFrançois Tigeot 	return 0;
1053f2dd94aSFrançois Tigeot }
1063f2dd94aSFrançois Tigeot 
1073f2dd94aSFrançois Tigeot /* This is really shmem_write_end() for the i915 gem code */
1083f2dd94aSFrançois Tigeot int
pagecache_write_end(struct vm_object * obj,struct address_space * mapping,loff_t pos,unsigned len,unsigned copied,struct page * page,void * fsdata)1093f2dd94aSFrançois Tigeot pagecache_write_end(struct vm_object *obj, struct address_space *mapping,
1103f2dd94aSFrançois Tigeot     loff_t pos, unsigned len, unsigned copied, struct page *page, void *fsdata)
1113f2dd94aSFrançois Tigeot {
1123f2dd94aSFrançois Tigeot 	set_page_dirty(page);
1133f2dd94aSFrançois Tigeot 	put_page(page);
1143f2dd94aSFrançois Tigeot 
1153f2dd94aSFrançois Tigeot 	return copied;
1163f2dd94aSFrançois Tigeot }
1173f2dd94aSFrançois Tigeot 
118*78973132SSergey Zigachev /*
119*78973132SSergey Zigachev  * userptr support
120*78973132SSergey Zigachev  */
121*78973132SSergey Zigachev long
get_user_pages(unsigned long start,unsigned long nr_pages,unsigned int gup_flags,struct page ** pages,struct vm_area_struct ** vmas)122*78973132SSergey Zigachev get_user_pages(unsigned long start, unsigned long nr_pages,
123*78973132SSergey Zigachev 	       unsigned int gup_flags, struct page **pages,
124*78973132SSergey Zigachev 	       struct vm_area_struct **vmas)
125*78973132SSergey Zigachev {
126*78973132SSergey Zigachev 	thread_t td;
127*78973132SSergey Zigachev 	vm_page_t m;
128*78973132SSergey Zigachev 	vm_map_t map;
129*78973132SSergey Zigachev 	long i;
130*78973132SSergey Zigachev 	int error;
131*78973132SSergey Zigachev 	int busied;
132*78973132SSergey Zigachev 	int fault_type = VM_PROT_READ;
133*78973132SSergey Zigachev 
134*78973132SSergey Zigachev 	/* returning related vmas not yet supported */
135*78973132SSergey Zigachev 	td = curthread;
136*78973132SSergey Zigachev 	KKASSERT(vmas == NULL);
137*78973132SSergey Zigachev 	KKASSERT(td->td_proc == NULL);
138*78973132SSergey Zigachev 	map = &td->td_proc->p_vmspace->vm_map;
139*78973132SSergey Zigachev 
140*78973132SSergey Zigachev 	if (gup_flags)
141*78973132SSergey Zigachev 		fault_type |= VM_PROT_WRITE;
142*78973132SSergey Zigachev 
143*78973132SSergey Zigachev 	error = 0;
144*78973132SSergey Zigachev 	for (i = 0; i < nr_pages; ++i) {
145*78973132SSergey Zigachev 		m = vm_fault_page(map, start + i * PAGE_SIZE,
146*78973132SSergey Zigachev 				  fault_type, VM_FAULT_NORMAL,
147*78973132SSergey Zigachev 				  &error, &busied);
148*78973132SSergey Zigachev 		if (error)
149*78973132SSergey Zigachev 			break;
150*78973132SSergey Zigachev 		if (busied) {
151*78973132SSergey Zigachev 			vm_page_wire(m);
152*78973132SSergey Zigachev 		} else {
153*78973132SSergey Zigachev 			vm_page_busy_wait(m, TRUE, "drmgup");
154*78973132SSergey Zigachev 			vm_page_wire(m);
155*78973132SSergey Zigachev 			vm_page_unhold(m);
156*78973132SSergey Zigachev 		}
157*78973132SSergey Zigachev 		vm_page_wakeup(m);
158*78973132SSergey Zigachev 		pages[i] = (void *)m;
159*78973132SSergey Zigachev 	}
160*78973132SSergey Zigachev 	if (error) {
161*78973132SSergey Zigachev 		while (--i >= 0) {
162*78973132SSergey Zigachev 			put_page(pages[i]);
163*78973132SSergey Zigachev 			pages[i] = NULL;
164*78973132SSergey Zigachev 		}
165*78973132SSergey Zigachev 		i = -error;
166*78973132SSergey Zigachev 	}
167*78973132SSergey Zigachev 	return i;
168*78973132SSergey Zigachev }
169*78973132SSergey Zigachev 
170*78973132SSergey Zigachev void
release_pages(struct page ** pages,unsigned long nr_pages)171*78973132SSergey Zigachev release_pages(struct page **pages, unsigned long nr_pages)
172*78973132SSergey Zigachev {
173*78973132SSergey Zigachev 	while (nr_pages > 0) {
174*78973132SSergey Zigachev 		--nr_pages;
175*78973132SSergey Zigachev 		put_page(pages[nr_pages]);
176*78973132SSergey Zigachev 		pages[nr_pages] = NULL;
177*78973132SSergey Zigachev 	}
178*78973132SSergey Zigachev }
179