]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/vm/vm_pageq.c
This commit was generated by cvs2svn to compensate for changes in r160157,
[FreeBSD/FreeBSD.git] / sys / vm / vm_pageq.c
1 /*-
2  * Copyright (c) 1998 Matthew Dillon.  All Rights Reserved.
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions
5  * are met:
6  * 1. Redistributions of source code must retain the above copyright
7  *    notice, this list of conditions and the following disclaimer.
8  * 2. Redistributions in binary form must reproduce the above copyright
9  *    notice, this list of conditions and the following disclaimer in the
10  *    documentation and/or other materials provided with the distribution.
11  * 4. Neither the name of the University nor the names of its contributors
12  *    may be used to endorse or promote products derived from this software
13  *    without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
21  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include "opt_vmpage.h"
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/linker_set.h>
36 #include <sys/lock.h>
37 #include <sys/malloc.h>
38 #include <sys/mutex.h>
39 #include <sys/sysctl.h>
40 #include <sys/proc.h>
41 #include <sys/vmmeter.h>
42 #include <sys/vnode.h>
43
44 #include <vm/vm.h>
45 #include <vm/vm_param.h>
46 #include <vm/vm_kern.h>
47 #include <vm/vm_object.h>
48 #include <vm/vm_page.h>
49 #include <vm/vm_pageout.h>
50 #include <vm/vm_pager.h>
51 #include <vm/vm_extern.h>
52
53 static void vm_coloring_init(void);
54 void setPQL2(int *const size, int *const ways);
55
56 struct vpgqueues vm_page_queues[PQ_MAXCOUNT];
57 struct pq_coloring page_queue_coloring;
58
59 static int pq_cachesize = 0;    /* size of the cache in KB */
60 static int pq_cachenways = 0;   /* associativity of the cache */
61
62 SYSCTL_DECL(_vm_stats);
63 SYSCTL_NODE(_vm_stats, OID_AUTO, pagequeue, CTLFLAG_RW, 0, "VM meter stats");
64 SYSCTL_INT(_vm_stats_pagequeue, OID_AUTO, page_colors, CTLFLAG_RD,
65     &(PQ_NUMCOLORS), 0, "Number of colors in the page queue");
66 SYSCTL_INT(_vm_stats_pagequeue, OID_AUTO, cachesize, CTLFLAG_RD,
67     &pq_cachesize, 0, "Size of the processor cache in KB");
68 SYSCTL_INT(_vm_stats_pagequeue, OID_AUTO, cachenways, CTLFLAG_RD,
69     &pq_cachenways, 0, "Associativity of the processor cache");
70 SYSCTL_INT(_vm_stats_pagequeue, OID_AUTO, prime1, CTLFLAG_RD,
71     &(PQ_PRIME1), 0, "Cache tuning value");
72 SYSCTL_INT(_vm_stats_pagequeue, OID_AUTO, prime2, CTLFLAG_RD,
73     &(PQ_PRIME2), 0, "Cache tuning value");
74
75 static void
76 vm_coloring_init(void)
77 {
78 #ifdef PQ_NOOPT
79         PQ_NUMCOLORS = PQ_PRIME1 = PQ_PRIME2 = 1;
80 #else
81
82         setPQL2(&pq_cachesize, &pq_cachenways);
83
84         CTASSERT(PAGE_SIZE/1024 > 0);
85
86         if (pq_cachesize > 0 && pq_cachenways > 0)
87                 PQ_NUMCOLORS = pq_cachesize / (PAGE_SIZE/1024) / \
88                     pq_cachenways;
89         else
90                 PQ_NUMCOLORS = 32;
91
92         if (PQ_MAXCOLORS < PQ_NUMCOLORS) {
93                 printf("VM-PQ color limit (PQ_MAXCOLORS=%u) exceeded (%u), see vm_page.h", PQ_MAXCOLORS, PQ_NUMCOLORS);
94                 PQ_NUMCOLORS = PQ_MAXCOLORS;
95         }
96
97         if (PQ_NUMCOLORS >= 128) {
98                 PQ_PRIME1 = 31;
99                 PQ_PRIME2 = 23;
100         } else if (PQ_NUMCOLORS >= 64) {
101                 PQ_PRIME1 = 13;
102                 PQ_PRIME2 = 7;
103         } else if (PQ_NUMCOLORS >= 32) {
104                 PQ_PRIME1 = 9;
105                 PQ_PRIME2 = 5;
106         } else if (PQ_NUMCOLORS >= 16) {
107                 PQ_PRIME1 = 5;
108                 PQ_PRIME2 = 3;
109         } else
110                 PQ_NUMCOLORS = PQ_PRIME1 = PQ_PRIME2 = 1;
111 #endif
112
113         /*
114          * PQ_CACHE represents a
115          * PQ_NUMCOLORS consecutive queue.
116          */
117         PQ_COLORMASK = PQ_NUMCOLORS - 1;
118         PQ_INACTIVE  = 1 + PQ_NUMCOLORS;
119         PQ_ACTIVE    = 2 + PQ_NUMCOLORS;
120         PQ_CACHE     = 3 + PQ_NUMCOLORS;
121         PQ_HOLD      = 3 + 2 * PQ_NUMCOLORS;
122         PQ_COUNT     = 4 + 2 * PQ_NUMCOLORS;
123         PQ_MAXLENGTH = PQ_NUMCOLORS / 3 + PQ_PRIME1;
124
125 #if 0
126         /* XXX: is it possible to allocate vm_page_queues[PQ_COUNT] here? */
127 #error XXX: vm_page_queues = malloc(PQ_COUNT * sizeof(struct vpgqueues));
128 #endif
129
130         if (bootverbose)
131                 if (PQ_NUMCOLORS > 1)
132                     printf("Using %d colors for the VM-PQ tuning (%d, %d)\n",
133                     PQ_NUMCOLORS, pq_cachesize, pq_cachenways);
134 }
135
136 void
137 vm_pageq_init(void)
138 {
139         int i;
140
141         vm_coloring_init();
142
143         for (i = 0; i < PQ_NUMCOLORS; ++i) {
144                 vm_page_queues[PQ_FREE+i].cnt = &cnt.v_free_count;
145         }
146         for (i = 0; i < PQ_NUMCOLORS; ++i) {
147                 vm_page_queues[PQ_CACHE + i].cnt = &cnt.v_cache_count;
148         }
149         vm_page_queues[PQ_INACTIVE].cnt = &cnt.v_inactive_count;
150         vm_page_queues[PQ_ACTIVE].cnt = &cnt.v_active_count;
151         vm_page_queues[PQ_HOLD].cnt = &cnt.v_active_count;
152
153         for (i = 0; i < PQ_COUNT; i++) {
154                 TAILQ_INIT(&vm_page_queues[i].pl);
155         }
156 }
157
158 void
159 vm_pageq_requeue(vm_page_t m)
160 {
161         int queue = VM_PAGE_GETQUEUE(m);
162         struct vpgqueues *vpq;
163
164         if (queue != PQ_NONE) {
165                 vpq = &vm_page_queues[queue];
166                 TAILQ_REMOVE(&vpq->pl, m, pageq);
167                 TAILQ_INSERT_TAIL(&vpq->pl, m, pageq);
168         }
169 }
170
171 /*
172  *      vm_pageq_enqueue:
173  */
174 void
175 vm_pageq_enqueue(int queue, vm_page_t m)
176 {
177         struct vpgqueues *vpq;
178
179         vpq = &vm_page_queues[queue];
180         VM_PAGE_SETQUEUE2(m, queue);
181         TAILQ_INSERT_TAIL(&vpq->pl, m, pageq);
182         ++*vpq->cnt;
183         ++vpq->lcnt;
184 }
185
186 /*
187  *      vm_add_new_page:
188  *
189  *      Add a new page to the freelist for use by the system.
190  */
191 vm_page_t
192 vm_pageq_add_new_page(vm_paddr_t pa)
193 {
194         vm_page_t m;
195
196         atomic_add_int(&cnt.v_page_count, 1);
197         m = PHYS_TO_VM_PAGE(pa);
198         m->phys_addr = pa;
199         m->flags = 0;
200         m->pc = (pa >> PAGE_SHIFT) & PQ_COLORMASK;
201         pmap_page_init(m);
202         mtx_lock_spin(&vm_page_queue_free_mtx);
203         vm_pageq_enqueue(m->pc + PQ_FREE, m);
204         mtx_unlock_spin(&vm_page_queue_free_mtx);
205         return (m);
206 }
207
208 /*
209  * vm_pageq_remove_nowakeup:
210  *
211  *      vm_page_unqueue() without any wakeup
212  *
213  *      The queue containing the given page must be locked.
214  *      This routine may not block.
215  */
216 void
217 vm_pageq_remove_nowakeup(vm_page_t m)
218 {
219         int queue = VM_PAGE_GETQUEUE(m);
220         struct vpgqueues *pq;
221
222         if (queue != PQ_NONE) {
223                 pq = &vm_page_queues[queue];
224                 VM_PAGE_SETQUEUE2(m, PQ_NONE);
225                 TAILQ_REMOVE(&pq->pl, m, pageq);
226                 (*pq->cnt)--;
227                 pq->lcnt--;
228         }
229 }
230
231 /*
232  * vm_pageq_remove:
233  *
234  *      Remove a page from its queue.
235  *
236  *      The queue containing the given page must be locked.
237  *      This routine may not block.
238  */
239 void
240 vm_pageq_remove(vm_page_t m)
241 {
242         int queue = VM_PAGE_GETQUEUE(m);
243         struct vpgqueues *pq;
244
245         if (queue != PQ_NONE) {
246                 VM_PAGE_SETQUEUE2(m, PQ_NONE);
247                 pq = &vm_page_queues[queue];
248                 TAILQ_REMOVE(&pq->pl, m, pageq);
249                 (*pq->cnt)--;
250                 pq->lcnt--;
251                 if (VM_PAGE_RESOLVEQUEUE(m, queue) == PQ_CACHE) {
252                         if (vm_paging_needed())
253                                 pagedaemon_wakeup();
254                 }
255         }
256 }
257
258 #ifndef PQ_NOOPT
259
260 /*
261  *      vm_pageq_find:
262  *
263  *      Find a page on the specified queue with color optimization.
264  *
265  *      The page coloring optimization attempts to locate a page
266  *      that does not overload other nearby pages in the object in
267  *      the cpu's L2 cache.  We need this optimization because cpu
268  *      caches tend to be physical caches, while object spaces tend 
269  *      to be virtual.
270  *
271  *      The specified queue must be locked.
272  *      This routine may not block.
273  *
274  *      This routine may only be called from the vm_pageq_find()
275  *      function in this file.
276  */
277 static inline vm_page_t
278 _vm_pageq_find(int basequeue, int index)
279 {
280         int i;
281         vm_page_t m = NULL;
282         struct vpgqueues *pq;
283
284         pq = &vm_page_queues[basequeue];
285
286         /*
287          * Note that for the first loop, index+i and index-i wind up at the
288          * same place.  Even though this is not totally optimal, we've already
289          * blown it by missing the cache case so we do not care.
290          */
291         for (i = PQ_NUMCOLORS / 2; i > 0; --i) {
292                 if ((m = TAILQ_FIRST(&pq[(index + i) & PQ_COLORMASK].pl)) \
293                     != NULL)
294                         break;
295
296                 if ((m = TAILQ_FIRST(&pq[(index - i) & PQ_COLORMASK].pl)) \
297                     != NULL)
298                         break;
299         }
300         return (m);
301 }
302 #endif /* PQ_NOOPT */
303
304 vm_page_t
305 vm_pageq_find(int basequeue, int index, boolean_t prefer_zero)
306 {
307         vm_page_t m;
308
309 #ifndef PQ_NOOPT
310         if (PQ_NUMCOLORS > 1) {
311                 if (prefer_zero) {
312                         m = TAILQ_LAST(&vm_page_queues[basequeue+index].pl, \
313                             pglist);
314                 } else {
315                         m = TAILQ_FIRST(&vm_page_queues[basequeue+index].pl);
316                 }
317                 if (m == NULL) {
318                         m = _vm_pageq_find(basequeue, index);
319                 }
320         } else {
321 #endif
322                 if (prefer_zero) {
323                         m = TAILQ_LAST(&vm_page_queues[basequeue].pl, pglist);
324                 } else {
325                         m = TAILQ_FIRST(&vm_page_queues[basequeue].pl);
326                 }
327 #ifndef PQ_NOOPT
328         }
329 #endif
330         return (m);
331 }
332