kernel - pmap (i386) - Reduce kmem use for foreign pmap mapping
[dragonfly.git] / sys / kern / kern_msfbuf.c
1 /*
2  * Copyright (c) 2004, 2005 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Hiten Pandya <hmp@backplane.com> and Matthew Dillon
6  * <dillon@backplane.com>.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  * 3. Neither the name of The DragonFly Project nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific, prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  * The MSF_BUF API was augmented from the SFBUF API:
36  *      Copyright (c) 1998 David Greenman.  All rights reserved.
37  *      src/sys/kern/kern_sfbuf.c,v 1.7 2004/05/13 19:46:18 dillon
38  *
39  * $DragonFly: src/sys/kern/kern_msfbuf.c,v 1.21 2007/06/29 21:54:08 dillon Exp $
40  */
41 /*
42  * MSFBUFs cache linear multi-page ephermal mappings and operate similar
43  * to SFBUFs.  MSFBUFs use XIO's internally to hold the page list and can
44  * be considered to be a KVA wrapper around an XIO.
45  *
46  * Like the SFBUF subsystem, the locking and validation of the page array
47  * is the responsibility of the caller.  Also like the SFBUF subsystem,
48  * MSFBUFs are SMP-friendly, cache the mappings, and will avoid unnecessary
49  * page invalidations when possible.
50  *
51  * MSFBUFs are primarily designed to be used in subsystems that manipulate
52  * XIOs.  The DEV and BUF subsystems are a good example.
53  *
54  * TODO LIST:
55  *      - Overload XIOs representitive of smaller chunks of memory onto the
56  *        same KVA space to efficiently cache smaller mappings (filesystem
57  *        blocks / buffer cache related).
58  */
59
60 #include <sys/param.h>
61 #include <sys/systm.h>
62 #include <sys/globaldata.h>
63 #include <sys/kernel.h>
64 #include <sys/malloc.h>
65 #include <sys/queue.h>
66 #include <sys/proc.h>
67 #include <sys/sfbuf.h>
68 #include <sys/sysctl.h>
69 #include <sys/thread.h>
70 #include <sys/xio.h>
71 #include <sys/msfbuf.h>
72 #include <sys/uio.h>
73 #include <sys/lock.h>
74
75 #include <vm/vm.h>
76 #include <vm/vm_param.h>
77 #include <vm/vm_extern.h>
78 #include <vm/vm_kern.h>
79 #include <vm/vm_page.h>
80 #include <vm/vm_map.h>
81 #include <vm/pmap.h>
82
83 #include <sys/thread2.h>
84 #include <vm/vm_page2.h>
85
86 MALLOC_DEFINE(M_MSFBUF, "MSFBUF", "direct-copy buffers");
87
88 /* lists and queues associated with msf_bufs */
89 LIST_HEAD(msf_buf_list, msf_buf);
90
91 TAILQ_HEAD(, msf_buf) msf_buf_freelist;
92
93 /* indicate shortage of available msf_bufs */
94 static u_int msf_buf_alloc_want;
95
96 /* base of the msf_buf map */
97 static vm_offset_t msf_base;
98 static struct msf_buf *msf_bufs;
99 static int msf_buf_hits;
100 static int msf_buf_misses;
101
102 static int msf_buf_count = 256; /* magic value */
103 SYSCTL_INT(_kern_ipc, OID_AUTO, msf_bufs, CTLFLAG_RD, &msf_buf_count,
104         0, "number of direct-copy buffers available");
105 SYSCTL_INT(_kern_ipc, OID_AUTO, msf_hits, CTLFLAG_RD, &msf_buf_hits,
106         0, "direct-copy buffers cache hits");
107 SYSCTL_INT(_kern_ipc, OID_AUTO, msf_misses, CTLFLAG_RD, &msf_buf_misses,
108         0, "direct-copy buffers cache misses");
109
110 static void
111 msf_buf_init(void *__dummy)
112 {
113         struct msf_buf *msf;
114         int i;
115         
116         msf_buf_alloc_want = 0;
117         TUNABLE_INT_FETCH("kern.ipc.msfbufs", &msf_buf_count);
118
119         TAILQ_INIT(&msf_buf_freelist);
120
121         msf_base = kmem_alloc_nofault(&kernel_map,
122                                       msf_buf_count * XIO_INTERNAL_SIZE,
123                                       PAGE_SIZE);
124
125         msf_bufs = kmalloc(msf_buf_count * sizeof(struct msf_buf), M_MSFBUF,
126                         M_WAITOK|M_ZERO);
127
128         /* Initialize the free list with necessary information. */
129         for (i = 0; i < msf_buf_count; i++) {
130                 msf = &msf_bufs[i];
131                 msf->ms_kva = msf_base + i * XIO_INTERNAL_SIZE;
132                 msf->ms_flags = MSF_ONFREEQ;
133                 msf->ms_type = MSF_TYPE_UNKNOWN;
134                 msf->ms_xio = &msf->ms_internal_xio;
135                 xio_init(&msf->ms_internal_xio);
136                 TAILQ_INSERT_TAIL(&msf_buf_freelist, &msf_bufs[i], free_list);
137         }
138 }
139 SYSINIT(msf_buf, SI_BOOT2_MACHDEP, SI_ORDER_ANY, msf_buf_init, NULL);
140
141 /*
142  * Get an msf_buf from the freelist; if none are available
143  * than it will block.
144  *
145  * If SFB_CATCH was specified in 'flags' than the sleep is
146  * block is interruptable by signals etc; this flag is normally
147  * use for system calls.
148  *
149  */
150 static struct msf_buf *
151 msf_alloc(vm_page_t firstpage, int flags)
152 {
153         struct msf_buf *msf;
154         int pflags;
155         int error;
156
157         crit_enter();
158         if (firstpage && (msf = firstpage->msf_hint) != NULL &&
159                 (msf->ms_flags & MSF_ONFREEQ)
160         ) {
161                 KKASSERT(msf->ms_refcnt == 0);
162                 msf->ms_flags &= ~MSF_ONFREEQ;
163                 msf->ms_refcnt = 1;
164                 TAILQ_REMOVE(&msf_buf_freelist, msf, free_list);
165                 --msf_buf_count;
166                 ++msf_buf_hits;
167         } else {
168                 /*
169                  * Get a buffer off the freelist.  If the freelist is empty, we
170                  * block until something becomes available; this happens quite
171                  * quickly anyway because MSFBUFs are supposed to be temporary
172                  * mappings.
173                  *
174                  * If the SFB_CATCH flag was provided, then we allow the sleep
175                  * to be interruptible.
176                  */
177                 for (;;) {
178                         if ((msf = TAILQ_FIRST(&msf_buf_freelist)) != NULL) {
179                                 KKASSERT(msf->ms_refcnt == 0);
180                                 --msf_buf_count;
181                                 TAILQ_REMOVE(&msf_buf_freelist, msf, free_list);
182                                 msf->ms_flags &= ~MSF_ONFREEQ;
183                                 msf->ms_refcnt = 1;
184                                 if (firstpage)
185                                         firstpage->msf_hint = msf;
186                                 break;
187                         }
188                         pflags = (flags & SFB_CATCH) ? PCATCH : 0;
189                         ++msf_buf_alloc_want;
190                         error = tsleep(&msf_buf_freelist, pflags, "msfbuf", 0);
191                         --msf_buf_alloc_want;
192                         if (error)
193                                         break;
194                 }
195                 ++msf_buf_misses;
196         }
197         crit_exit();
198         return (msf);
199 }
200
201 static
202 void
203 msf_map_msf(struct msf_buf *msf, int flags)
204 {
205 #ifdef SMP
206         if (flags & SFB_CPUPRIVATE) {
207                 pmap_qenter2(msf->ms_kva, msf->ms_xio->xio_pages, 
208                             msf->ms_xio->xio_npages, &msf->ms_cpumask);
209         } else {
210                 pmap_qenter(msf->ms_kva, msf->ms_xio->xio_pages,
211                             msf->ms_xio->xio_npages);
212                 msf->ms_cpumask = (cpumask_t)-1;
213         }
214 #else
215         pmap_qenter2(msf->ms_kva, msf->ms_xio->xio_pages, 
216                         msf->ms_xio->xio_npages, &msf->ms_cpumask);
217 #endif
218 }
219
220 int
221 msf_map_pagelist(struct msf_buf **msfp, vm_page_t *list, int npages, int flags)
222 {
223         struct msf_buf *msf;
224         int i;
225
226         KKASSERT(npages != 0 && npages <= XIO_INTERNAL_PAGES);
227
228         if ((msf = msf_alloc(list[0], flags)) != NULL) {
229                 KKASSERT(msf->ms_xio == &msf->ms_internal_xio);
230                 for (i = 0; i < npages; ++i)
231                         msf->ms_internal_xio.xio_pages[i] = list[i];
232                 msf->ms_internal_xio.xio_offset = 0;
233                 msf->ms_internal_xio.xio_npages = npages;
234                 msf->ms_internal_xio.xio_bytes = npages << PAGE_SHIFT;
235                 msf->ms_type = MSF_TYPE_PGLIST;
236                 msf_map_msf(msf, flags);
237                 *msfp = msf;
238                 return (0);
239         } else {
240                 *msfp = NULL;
241                 return (ENOMEM);
242         }
243 }
244
245 int
246 msf_map_xio(struct msf_buf **msfp, struct xio *xio, int flags)
247 {
248         struct msf_buf *msf;
249
250         KKASSERT(xio != NULL && xio->xio_npages > 0);
251         KKASSERT(xio->xio_npages <= XIO_INTERNAL_PAGES);
252
253         if ((msf = msf_alloc(xio->xio_pages[0], flags)) != NULL) {
254                 msf->ms_type = MSF_TYPE_XIO;
255                 msf->ms_xio = xio;
256                 msf_map_msf(msf, flags);
257                 *msfp = msf;
258                 return(0);
259         } else {
260                 *msfp = NULL;
261                 return(ENOMEM);
262         }
263 }
264
265 int
266 msf_map_ubuf(struct msf_buf **msfp, void *base, size_t nbytes, int flags)
267 {
268         struct msf_buf *msf;
269         vm_paddr_t paddr;
270         int error;
271
272         if (((int)(intptr_t)base & PAGE_MASK) + nbytes > XIO_INTERNAL_SIZE) {
273                 *msfp = NULL;
274                 return (ERANGE);
275         }
276
277         if ((paddr = pmap_extract(&curthread->td_lwp->lwp_vmspace->vm_pmap,
278                                   (vm_offset_t)base)) != 0)
279                 msf = msf_alloc(PHYS_TO_VM_PAGE(paddr), flags);
280         else
281                 msf = msf_alloc(NULL, flags);
282
283         if (msf == NULL) {
284                 error = ENOENT;
285         } else {
286                 error = xio_init_ubuf(&msf->ms_internal_xio, base, nbytes, 0);
287                 if (error == 0) {
288                         KKASSERT(msf->ms_xio == &msf->ms_internal_xio);
289                         msf_map_msf(msf, flags);
290                         msf->ms_type = MSF_TYPE_UBUF;
291                 } else {
292                         msf_buf_free(msf);
293                         msf = NULL;
294                 }
295         }
296         *msfp = msf;
297         return (error);
298 }
299
300 int
301 msf_map_kbuf(struct msf_buf **msfp, void *base, size_t nbytes, int flags)
302 {
303         struct msf_buf *msf;
304         vm_paddr_t paddr;
305         int error;
306
307         if (((int)(intptr_t)base & PAGE_MASK) + nbytes > XIO_INTERNAL_SIZE) {
308                 *msfp = NULL;
309                 return (ERANGE);
310         }
311
312         if ((paddr = pmap_kextract((vm_offset_t)base)) != 0)
313                 msf = msf_alloc(PHYS_TO_VM_PAGE(paddr), flags);
314         else
315                 msf = msf_alloc(NULL, flags);
316
317         if (msf == NULL) {
318                 error = ENOENT;
319         } else {
320                 error = xio_init_kbuf(&msf->ms_internal_xio, base, nbytes);
321                 if (error == 0) {
322                         KKASSERT(msf->ms_xio == &msf->ms_internal_xio);
323                         msf_map_msf(msf, flags);
324                         msf->ms_type = MSF_TYPE_KBUF;
325                 } else {
326                         msf_buf_free(msf);
327                         msf = NULL;
328                 }
329         }
330         *msfp = msf;
331         return (error);
332 }
333
334 /*
335  * Iterate through the specified uio calling the function with a kernel buffer
336  * containing the data until the uio has been exhausted.  If the uio 
337  * represents system space no mapping occurs.  If the uio represents user 
338  * space the data is mapped into system space in chunks.  This function does
339  * not guarentee any particular alignment or minimum chunk size, it will 
340  * depend on the limitations of MSF buffers and the breakdown of the UIO's
341  * elements.
342  */
343 int
344 msf_uio_iterate(struct uio *uio, 
345                 int (*callback)(void *info, char *buf, int bytes), void *info)
346 {
347         struct msf_buf *msf;
348         struct iovec *iov;
349         size_t offset;
350         size_t bytes;
351         size_t pgoff;
352         int error;
353         int i;
354
355         switch (uio->uio_segflg) {
356         case UIO_USERSPACE:
357                 error = 0;
358                 for (i = 0; i < uio->uio_iovcnt && error == 0; ++i) {
359                         iov = &uio->uio_iov[i];
360                         offset = 0;
361                         pgoff = (int)(intptr_t)iov->iov_base & PAGE_MASK;
362                         while (offset < iov->iov_len) {
363                                 bytes = iov->iov_len - offset;
364                                 if (bytes + pgoff > XIO_INTERNAL_SIZE)
365                                         bytes = XIO_INTERNAL_SIZE - pgoff;
366                                 error = msf_map_ubuf(&msf, (char *)iov->iov_base + offset, bytes, 0);
367                                 if (error)
368                                         break;
369                                 error = callback(info, msf_buf_kva(msf), bytes);
370                                 msf_buf_free(msf);
371                                 if (error)
372                                         break;
373                                 pgoff = 0;
374                                 offset += bytes;
375                         }
376                 }
377                 break;
378         case UIO_SYSSPACE:
379                 error = 0;
380                 for (i = 0; i < uio->uio_iovcnt; ++i) {
381                         iov = &uio->uio_iov[i];
382                         if (iov->iov_len == 0)
383                                 continue;
384                         error = callback(info, iov->iov_base, iov->iov_len);
385                         if (error)
386                                 break;
387                 }
388                 break;
389         default:
390                 error = EOPNOTSUPP;
391                 break;
392         }
393         return (error);
394 }
395
396 #if 0
397 /*
398  * Add a reference to a buffer (currently unused)
399  */
400 void
401 msf_buf_ref(struct msf_buf *msf)
402 {
403         if (msf->ms_refcnt == 0)
404                 panic("msf_buf_ref: referencing a free msf_buf");
405         crit_enter();
406         ++msf->ms_refcnt;
407         crit_exit();
408 }
409 #endif
410
411 /*
412  * Lose a reference to an msf_buf. When none left, detach mapped page
413  * and release resources back to the system.  Note that the sfbuf's
414  * removal from the freelist is delayed, so it may in fact already be
415  * on the free list.  This is the optimal (and most likely) scenario.
416  */
417 void
418 msf_buf_free(struct msf_buf *msf)
419 {
420         KKASSERT(msf->ms_refcnt > 0);
421
422         crit_enter();
423         if (--msf->ms_refcnt == 0) {
424                 KKASSERT((msf->ms_flags & MSF_ONFREEQ) == 0);
425
426                 if (msf->ms_type == MSF_TYPE_UBUF || msf->ms_type == MSF_TYPE_KBUF)
427                         xio_release(msf->ms_xio);
428
429                 msf->ms_type = MSF_TYPE_UNKNOWN;
430                 msf->ms_flags |= MSF_ONFREEQ;
431                 msf->ms_xio = &msf->ms_internal_xio;
432                 TAILQ_INSERT_TAIL(&msf_buf_freelist, msf, free_list);
433                 ++msf_buf_count;
434                 if (msf_buf_alloc_want > 0)
435                         wakeup_one(&msf_buf_freelist);
436         }
437         crit_exit();
438 }
439