Enhance the pmap_kenter*() API and friends, separating out entries which
[dragonfly.git] / sys / kern / kern_sfbuf.c
1 /*
2  * Copyright (c) 1998 David Greenman.  All rights reserved. 
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  * $DragonFly: src/sys/kern/kern_sfbuf.c,v 1.3 2004/04/01 17:58:02 dillon Exp $
26  */
27
28 #include <sys/param.h>
29 #include <sys/types.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/malloc.h>
33 #include <sys/queue.h>
34 #include <sys/sfbuf.h>
35 #include <sys/globaldata.h>
36 #include <sys/thread.h>
37 #include <sys/sysctl.h>
38 #include <vm/vm.h>
39 #include <vm/vm_extern.h>
40 #include <vm/vm_kern.h>
41 #include <vm/vm_page.h>
42 #include <vm/pmap.h>
43 #include <sys/thread2.h>
44
45 static void sf_buf_init(void *arg);
46 SYSINIT(sock_sf, SI_SUB_MBUF, SI_ORDER_ANY, sf_buf_init, NULL)
47
48 LIST_HEAD(sf_buf_list, sf_buf);
49
50 /*
51  * A hash table of active sendfile(2) buffers
52  */
53 static struct sf_buf_list *sf_buf_hashtable;
54 static u_long sf_buf_hashmask;
55
56 #define SF_BUF_HASH(m)  (((m) - vm_page_array) & sf_buf_hashmask)
57
58 static TAILQ_HEAD(, sf_buf) sf_buf_freelist;
59 static u_int sf_buf_alloc_want;
60
61 static vm_offset_t sf_base;
62 static struct sf_buf *sf_bufs;
63
64 static int sfbuf_quick = 1;
65 SYSCTL_INT(_debug, OID_AUTO, sfbuf_quick, CTLFLAG_RW, &sfbuf_quick, 0, "");
66
67 /*
68  * Allocate a pool of sf_bufs (sendfile(2) or "super-fast" if you prefer. :-))
69  */
70 static void
71 sf_buf_init(void *arg)
72 {
73         int i;
74
75         sf_buf_hashtable = hashinit(nsfbufs, M_TEMP, &sf_buf_hashmask);
76         TAILQ_INIT(&sf_buf_freelist);
77         sf_base = kmem_alloc_nofault(kernel_map, nsfbufs * PAGE_SIZE);
78         sf_bufs = malloc(nsfbufs * sizeof(struct sf_buf), M_TEMP,
79             M_NOWAIT | M_ZERO);
80         for (i = 0; i < nsfbufs; i++) {
81                 sf_bufs[i].kva = sf_base + i * PAGE_SIZE;
82                 sf_bufs[i].flags |= SFBA_ONFREEQ;
83                 TAILQ_INSERT_TAIL(&sf_buf_freelist, &sf_bufs[i], free_entry);
84         }
85 }
86
87 /*
88  * Get an sf_buf from the freelist. Will block if none are available.
89  */
90 struct sf_buf *
91 sf_buf_alloc(struct vm_page *m, int flags)
92 {
93         struct sf_buf_list *hash_chain;
94         struct sf_buf *sf;
95         globaldata_t gd;
96         int error;
97         int pflags;
98
99         gd = mycpu;
100         crit_enter();
101         hash_chain = &sf_buf_hashtable[SF_BUF_HASH(m)];
102         LIST_FOREACH(sf, hash_chain, list_entry) {
103                 if (sf->m == m) {
104                         /*
105                          * cache hit
106                          *
107                          * We must invalidate the TLB entry based on whether
108                          * it need only be valid on the local cpu (SFBA_QUICK),
109                          * or on all cpus.  This is conditionalized and in
110                          * most cases no system-wide invalidation should be
111                          * needed.
112                          *
113                          * Note: we do not remove the entry from the freelist
114                          * on the 0->1 transition. 
115                          */
116                         ++sf->refcnt;
117                         if ((flags & SFBA_QUICK) && sfbuf_quick) {
118                                 if ((sf->cpumask & gd->gd_cpumask) == 0) {
119                                         pmap_kenter_sync_quick(sf->kva);
120                                         sf->cpumask |= gd->gd_cpumask;
121                                 }
122                         } else {
123                                 if (sf->cpumask != (cpumask_t)-1) {
124                                         pmap_kenter_sync(sf->kva);
125                                         sf->cpumask = (cpumask_t)-1;
126                                 }
127                         }
128                         goto done;      /* found existing mapping */
129                 }
130         }
131
132         /*
133          * Didn't find old mapping.  Get a buffer off the freelist.  We
134          * may have to remove and skip buffers with non-zero ref counts 
135          * that were lazily allocated.
136          */
137         for (;;) {
138                 if ((sf = TAILQ_FIRST(&sf_buf_freelist)) == NULL) {
139                         pflags = (flags & SFBA_PCATCH) ? PCATCH : 0;
140                         ++sf_buf_alloc_want;
141                         error = tsleep(&sf_buf_freelist, pflags, "sfbufa", 0);
142                         --sf_buf_alloc_want;
143                         if (error)
144                                 goto done;
145                 } else {
146                         /*
147                          * We may have to do delayed removals for referenced
148                          * sf_buf's here in addition to locating a sf_buf
149                          * to reuse.  The sf_bufs must be removed.
150                          *
151                          * We are finished when we find an sf_buf with a
152                          * refcnt of 0.  We theoretically do not have to
153                          * remove it from the freelist but it's a good idea
154                          * to do so to preserve LRU operation for the
155                          * (1) never before seen before case and (2) 
156                          * accidently recycled due to prior cached uses not
157                          * removing the buffer case.
158                          */
159                         KKASSERT(sf->flags & SFBA_ONFREEQ);
160                         TAILQ_REMOVE(&sf_buf_freelist, sf, free_entry);
161                         sf->flags &= ~SFBA_ONFREEQ;
162                         if (sf->refcnt == 0)
163                                 break;
164                 }
165         }
166         if (sf->m != NULL)      /* remove previous mapping from hash table */
167                 LIST_REMOVE(sf, list_entry);
168         LIST_INSERT_HEAD(hash_chain, sf, list_entry);
169         sf->refcnt = 1;
170         sf->m = m;
171         if ((flags & SFBA_QUICK) && sfbuf_quick) {
172                 pmap_kenter_quick(sf->kva, sf->m->phys_addr);
173                 sf->cpumask = gd->gd_cpumask;
174         } else {
175                 pmap_kenter(sf->kva, sf->m->phys_addr);
176                 sf->cpumask = (cpumask_t)-1;
177         }
178 done:
179         crit_exit();
180         return (sf);
181 }
182
183 #define dtosf(x)        (&sf_bufs[((uintptr_t)(x) - (uintptr_t)sf_base) >> PAGE_SHIFT])
184
185 struct sf_buf *
186 sf_buf_tosf(caddr_t addr)
187 {
188         return(dtosf(addr));
189 }
190
191 void
192 sf_buf_ref(struct sf_buf *sf)
193 {
194         if (sf->refcnt == 0)
195                 panic("sf_buf_ref: referencing a free sf_buf");
196         sf->refcnt++;
197 }
198
199 /*
200  * Lose a reference to an sf_buf. When none left, detach mapped page
201  * and release resources back to the system.  Note that the sfbuf's
202  * removal from the freelist is delayed, so it may in fact already be
203  * on the free list.  This is the optimal (and most likely) scenario.
204  *
205  * Must be called at splimp.
206  */
207 void
208 sf_buf_free(struct sf_buf *sf)
209 {
210         if (sf->refcnt == 0)
211                 panic("sf_buf_free: freeing free sf_buf");
212         sf->refcnt--;
213         if (sf->refcnt == 0 && (sf->flags & SFBA_ONFREEQ) == 0) {
214                 KKASSERT(sf->aux1 == 0 && sf->aux2 == 0);
215                 TAILQ_INSERT_TAIL(&sf_buf_freelist, sf, free_entry);
216                 sf->flags |= SFBA_ONFREEQ;
217                 if (sf_buf_alloc_want > 0)
218                         wakeup_one(&sf_buf_freelist);
219         }
220 }
221