Add a prototype for isab_attach(), which is used by the ACPI-5 ISA
[dragonfly.git] / sys / kern / kern_msfbuf.c
1 /*
2  * Copyright (c) 2004 The DragonFly Project.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to The DragonFly Project
6  * by Hiten Pandya <hmp@backplane.com> and Matthew Dillon
7  * <dillon@backplane.com>.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  * 3. Neither the name of The DragonFly Project nor the names of its
20  *    contributors may be used to endorse or promote products derived
21  *    from this software without specific, prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
27  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * The MSF_BUF API was augmented from the SFBUF API:
37  *      Copyright (c) 1998 David Greenman.  All rights reserved.
38  *      src/sys/kern/kern_sfbuf.c,v 1.7 2004/05/13 19:46:18 dillon
39  *
40  * $DragonFly: src/sys/kern/kern_msfbuf.c,v 1.5 2004/07/07 23:11:33 hmp Exp $
41  */
42 /*
43  * MSFBUFs cache linear multi-page ephermal mappings and operate similar
44  * to SFBUFs.  MSFBUFs use XIO's internally to hold the page list and can
45  * be considered to be a KVA wrapper around an XIO.
46  *
47  * Like the SFBUF subsystem, the locking and validation of the page array
48  * is the responsibility of the caller.  Also like the SFBUF subsystem,
49  * MSFBUFs are SMP-friendly, cache the mappings, and will avoid unnecessary
50  * page invalidations when possible.
51  *
52  * MSFBUFs are primarily designed to be used in subsystems that manipulate
53  * XIOs.  The DEV and BUF subsystems are a good example.
54  *
55  * TODO LIST:
56  *      - Implement the FREEQ optimization that exists in the SFBUF code.
57  *      - Allow allocation (aka mapping) based on an XIO instead of a pglist.
58  *      - Overload XIOs representitive of smaller chunks of memory onto the
59  *        same KVA space to efficiently cache smaller mappings (filesystem
60  *        blocks / buffer cache related).
61  */
62
63 #include <sys/param.h>
64 #include <sys/systm.h>
65 #include <sys/globaldata.h>
66 #include <sys/kernel.h>
67 #include <sys/malloc.h>
68 #include <sys/queue.h>
69 #include <sys/sfbuf.h>
70 #include <sys/sysctl.h>
71 #include <sys/thread.h>
72 #include <sys/thread2.h>
73 #include <sys/xio.h>
74 #include <sys/msfbuf.h>
75
76 #include <vm/vm.h>
77 #include <vm/vm_extern.h>
78 #include <vm/vm_kern.h>
79 #include <vm/vm_page.h>
80 #include <vm/pmap.h>
81
82 MALLOC_DECLARE(M_MSFBUF);
83 MALLOC_DEFINE(M_MSFBUF, "MSFBUF", "direct-copy buffers");
84
85 /* lists and queues associated with msf_bufs */
86 LIST_HEAD(msf_buf_list, msf_buf);
87
88 TAILQ_HEAD(, msf_buf) msf_buf_freelist;
89
90 /* hash table for tracking msf_bufs */
91 static struct msf_buf_list *msf_buf_hashtable;
92 static u_long msf_buf_hashmask;
93
94 /* indicate shortage of available msf_bufs */
95 static u_int msf_buf_alloc_want;
96
97 /* base of the msf_buf map */
98 static vm_offset_t msf_base;
99 static struct msf_buf *msf_bufs;
100
101 static int num_msf_bufs = 256; /* magic value */
102 SYSCTL_INT(_kern_ipc, OID_AUTO, msfbufs, CTLFLAG_RD, &num_msf_bufs,
103         0, "number of direct-copy buffers available");
104
105 static void
106 msf_buf_init(void *__dummy)
107 {
108         int i;
109         
110         msf_buf_alloc_want = 0;
111         TUNABLE_INT_FETCH("kern.ipc.msfbufs", &num_msf_bufs);
112
113         msf_buf_hashtable = hashinit(num_msf_bufs, M_TEMP, &msf_buf_hashmask);
114         TAILQ_INIT(&msf_buf_freelist);
115
116         msf_base = kmem_alloc_nofault(kernel_map,
117                                         num_msf_bufs * XIO_INTERNAL_SIZE);
118
119         /* 
120          * Use contig. memory for the maps, so it is quicker to access
121          * linear maps, and they can also be passed to device
122          * buffers (in the future).
123          */
124         msf_bufs = malloc(num_msf_bufs * sizeof(struct msf_buf), M_MSFBUF,
125                         M_WAITOK|M_ZERO);
126
127         /* Initialize the free list with necessary information. */
128         for (i = 0; i < num_msf_bufs; i++) {
129                 msf_bufs[i].m_kva = msf_base + i * XIO_INTERNAL_SIZE;
130                 msf_bufs[i].m_flags |= SFBA_ONFREEQ;
131                 xio_init(&msf_bufs[i].m_xio);
132                 TAILQ_INSERT_TAIL(&msf_buf_freelist, &msf_bufs[i], free_list);
133         }
134 }
135 SYSINIT(msf_buf, SI_SUB_MBUF, SI_ORDER_ANY, msf_buf_init, NULL);
136
137 /*
138  * Hash the base page of an MSF's array of pages.
139  */
140 static __inline
141 int
142 msf_buf_hash(vm_page_t base_m)
143 {
144     int hv;
145
146     hv = ((int)base_m / sizeof(vm_page_t)) + ((int)base_m >> 12);
147     return(hv & msf_buf_hashmask);
148 }
149
150 /*
151  * Get an msf_buf from the freelist; if none are available
152  * than it will block.
153  *
154  * If SFBA_PCATCH was specified in 'flags' than the sleep is
155  * block is interruptable by signals etc; this flag is normally
156  * use for system calls.
157  *
158  */
159 struct msf_buf *
160 msf_buf_alloc(vm_page_t *pg_ary, int npages, int flags)
161 {
162         struct msf_buf_list *hash_chain;
163         struct msf_buf *msf;
164         globaldata_t gd;
165         int error, pflags;
166         int i;
167
168         KKASSERT(npages != 0 && npages <= XIO_INTERNAL_SIZE);
169
170         gd = mycpu;
171         crit_enter();
172         hash_chain = &msf_buf_hashtable[msf_buf_hash(*pg_ary)];
173         LIST_FOREACH(msf, hash_chain, active_list) {
174                 if (msf->m_xio.xio_npages == npages) {
175                         for (i = npages - 1; i >= 0; --i) {
176                                 if (msf->m_xio.xio_pages[i] != pg_ary[i])
177                                         break;
178                         }
179                         if (i >= 0)
180                                 continue;
181                         /*
182                          * found existing mapping
183                          */
184                         if (msf->m_flags & SFBA_ONFREEQ) {
185                             TAILQ_REMOVE(&msf_buf_freelist, msf, free_list);
186                             msf->m_flags &= ~SFBA_ONFREEQ;
187                         }
188
189                         goto done;
190                 }
191         }
192
193         /*
194          * Didn't find old mapping.  Get a buffer off the freelist.  We
195          * may have to remove and skip buffers with non-zero ref counts 
196          * that were lazily allocated.
197          *
198          * If the freelist is empty, we block until something becomes
199          * available.  This usually happens pretty quickly because sf_bufs
200          * and msf_bufs are supposed to be temporary mappings.
201          */
202         while ((msf = TAILQ_FIRST(&msf_buf_freelist)) == NULL) {
203                 pflags = (flags & SFBA_PCATCH) ? PCATCH : 0;
204                 ++msf_buf_alloc_want;
205                 error = tsleep(&msf_buf_freelist, pflags, "msfbuf", 0);
206                 --msf_buf_alloc_want;
207                 if (error)
208                         goto done2;
209         }
210         
211         /*
212          * We are finished when we find an msf_buf with ref. count of
213          * 0.  Theoretically, we do not have to remove it from the
214          * freelist but it's a good idea to do so to preserve LRU
215          * operation for the (1) never seen before case and
216          * (2) accidently recycled due to prior cached uses not removing
217          * the buffer case.
218          */
219         KKASSERT(msf->m_flags & SFBA_ONFREEQ);
220         TAILQ_REMOVE(&msf_buf_freelist, msf, free_list);
221         msf->m_flags &= ~SFBA_ONFREEQ;
222
223         /* Remove previous mapping from hash table and overwrite new one */
224         if (msf->m_xio.xio_pages[0] != NULL)
225                 LIST_REMOVE(msf, active_list);
226          
227         LIST_INSERT_HEAD(hash_chain, msf, active_list);
228
229         for (i = 0; i < npages; ++i)
230                 msf->m_xio.xio_pages[i] = pg_ary[i];
231
232         msf->m_xio.xio_npages = npages;
233         msf->m_xio.xio_bytes = npages * PAGE_SIZE;
234
235         /*
236          * Successful MSF setup, bump the ref count and enter the pages.
237          */
238 done:
239         ++msf->m_refcnt;
240         if ((flags & SFBA_QUICK)) {
241                 pmap_qenter2(msf->m_kva, msf->m_xio.xio_pages, 
242                             msf->m_xio.xio_npages, &msf->m_cpumask);
243         } else {
244                 pmap_qenter(msf->m_kva, msf->m_xio.xio_pages,
245                             msf->m_xio.xio_npages);
246                 msf->m_cpumask = (cpumask_t)-1;
247         }
248 done2:
249         crit_exit();
250         return (msf);
251 }
252
253 #if 0
254 /*
255  * Add a reference to a buffer (currently unused)
256  */
257 void
258 msf_buf_ref(struct msf_buf *msf)
259 {
260         if (msf->m_refcnt == 0)
261                 panic("msf_buf_ref: referencing a free msf_buf");
262         crit_enter();
263         ++msf->m_refcnt;
264         crit_exit();
265 }
266 #endif
267
268 /*
269  * Lose a reference to an msf_buf. When none left, detach mapped page
270  * and release resources back to the system.  Note that the sfbuf's
271  * removal from the freelist is delayed, so it may in fact already be
272  * on the free list.  This is the optimal (and most likely) scenario.
273  *
274  * Must be called at splimp.
275  */
276 void
277 msf_buf_free(struct msf_buf *msf)
278 {
279         crit_enter();
280         KKASSERT(msf->m_refcnt > 0);
281
282         msf->m_refcnt--;
283         if (msf->m_refcnt == 0) {
284                 KKASSERT((msf->m_flags & SFBA_ONFREEQ) == 0);
285                 TAILQ_INSERT_TAIL(&msf_buf_freelist, msf, free_list);
286                 msf->m_flags |= SFBA_ONFREEQ;
287                 if (msf_buf_alloc_want > 0)
288                         wakeup_one(&msf_buf_freelist);
289         }
290         crit_exit();
291 }