2 * Copyright (c) 1998 David Greenman. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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
25 * $DragonFly: src/sys/kern/kern_sfbuf.c,v 1.7 2004/05/13 19:46:18 dillon Exp $
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>
39 #include <vm/vm_extern.h>
40 #include <vm/vm_kern.h>
41 #include <vm/vm_page.h>
43 #include <sys/thread2.h>
45 static void sf_buf_init(void *arg);
46 SYSINIT(sock_sf, SI_SUB_MBUF, SI_ORDER_ANY, sf_buf_init, NULL)
48 LIST_HEAD(sf_buf_list, sf_buf);
50 SYSCTL_DECL(_kern_ipc);
51 SYSCTL_INT(_kern_ipc, OID_AUTO, nsfbufs, CTLFLAG_RD, &nsfbufs, 0,
52 "Maximum number of sf_bufs available to the system");
55 * A hash table of active sendfile(2) buffers
57 static struct sf_buf_list *sf_buf_hashtable;
58 static u_long sf_buf_hashmask;
60 static TAILQ_HEAD(, sf_buf) sf_buf_freelist;
61 static u_int sf_buf_alloc_want;
63 static vm_offset_t sf_base;
64 static struct sf_buf *sf_bufs;
66 static int sfbuf_quick = 1;
67 SYSCTL_INT(_debug, OID_AUTO, sfbuf_quick, CTLFLAG_RW, &sfbuf_quick, 0, "");
71 sf_buf_hash(vm_page_t m)
75 hv = ((int)m / sizeof(vm_page_t)) + ((int)m >> 12);
76 return(hv & sf_buf_hashmask);
80 * Allocate a pool of sf_bufs (sendfile(2) or "super-fast" if you prefer. :-))
83 sf_buf_init(void *arg)
87 sf_buf_hashtable = hashinit(nsfbufs, M_TEMP, &sf_buf_hashmask);
88 TAILQ_INIT(&sf_buf_freelist);
89 sf_base = kmem_alloc_nofault(kernel_map, nsfbufs * PAGE_SIZE);
90 sf_bufs = malloc(nsfbufs * sizeof(struct sf_buf), M_TEMP,
92 for (i = 0; i < nsfbufs; i++) {
93 sf_bufs[i].kva = sf_base + i * PAGE_SIZE;
94 sf_bufs[i].flags |= SFBA_ONFREEQ;
95 TAILQ_INSERT_TAIL(&sf_buf_freelist, &sf_bufs[i], free_entry);
100 * Get an sf_buf from the freelist. Will block if none are available.
103 sf_buf_alloc(struct vm_page *m, int flags)
105 struct sf_buf_list *hash_chain;
113 hash_chain = &sf_buf_hashtable[sf_buf_hash(m)];
114 LIST_FOREACH(sf, hash_chain, list_entry) {
119 * We must invalidate the TLB entry based on whether
120 * it need only be valid on the local cpu (SFBA_QUICK),
121 * or on all cpus. This is conditionalized and in
122 * most cases no system-wide invalidation should be
125 * Note: we do not remove the entry from the freelist
126 * on the 0->1 transition.
129 if ((flags & SFBA_QUICK) && sfbuf_quick) {
130 if ((sf->cpumask & gd->gd_cpumask) == 0) {
131 pmap_kenter_sync_quick(sf->kva);
132 sf->cpumask |= gd->gd_cpumask;
135 if (sf->cpumask != (cpumask_t)-1) {
136 pmap_kenter_sync(sf->kva);
137 sf->cpumask = (cpumask_t)-1;
140 goto done; /* found existing mapping */
145 * Didn't find old mapping. Get a buffer off the freelist. We
146 * may have to remove and skip buffers with non-zero ref counts
147 * that were lazily allocated.
150 if ((sf = TAILQ_FIRST(&sf_buf_freelist)) == NULL) {
151 pflags = (flags & SFBA_PCATCH) ? PCATCH : 0;
153 error = tsleep(&sf_buf_freelist, pflags, "sfbufa", 0);
159 * We may have to do delayed removals for referenced
160 * sf_buf's here in addition to locating a sf_buf
161 * to reuse. The sf_bufs must be removed.
163 * We are finished when we find an sf_buf with a
164 * refcnt of 0. We theoretically do not have to
165 * remove it from the freelist but it's a good idea
166 * to do so to preserve LRU operation for the
167 * (1) never before seen before case and (2)
168 * accidently recycled due to prior cached uses not
169 * removing the buffer case.
171 KKASSERT(sf->flags & SFBA_ONFREEQ);
172 TAILQ_REMOVE(&sf_buf_freelist, sf, free_entry);
173 sf->flags &= ~SFBA_ONFREEQ;
178 if (sf->m != NULL) /* remove previous mapping from hash table */
179 LIST_REMOVE(sf, list_entry);
180 LIST_INSERT_HEAD(hash_chain, sf, list_entry);
183 if ((flags & SFBA_QUICK) && sfbuf_quick) {
184 pmap_kenter_quick(sf->kva, sf->m->phys_addr);
185 sf->cpumask = gd->gd_cpumask;
187 pmap_kenter(sf->kva, sf->m->phys_addr);
188 sf->cpumask = (cpumask_t)-1;
195 #define dtosf(x) (&sf_bufs[((uintptr_t)(x) - (uintptr_t)sf_base) >> PAGE_SHIFT])
198 sf_buf_tosf(caddr_t addr)
206 * Add a reference to a buffer (currently unused)
209 sf_buf_ref(struct sf_buf *sf)
212 panic("sf_buf_ref: referencing a free sf_buf");
221 * Lose a reference to an sf_buf. When none left, detach mapped page
222 * and release resources back to the system. Note that the sfbuf's
223 * removal from the freelist is delayed, so it may in fact already be
224 * on the free list. This is the optimal (and most likely) scenario.
226 * Must be called at splimp.
229 sf_buf_free(struct sf_buf *sf)
232 panic("sf_buf_free: freeing free sf_buf");
235 if (sf->refcnt == 0 && (sf->flags & SFBA_ONFREEQ) == 0) {
236 KKASSERT(sf->aux1 == 0 && sf->aux2 == 0);
237 TAILQ_INSERT_TAIL(&sf_buf_freelist, sf, free_entry);
238 sf->flags |= SFBA_ONFREEQ;
239 if (sf_buf_alloc_want > 0)
240 wakeup_one(&sf_buf_freelist);