c99dee975f7d19b48e39082bfe0aceffd51c38c5
[dragonfly.git] / sys / vm / vm_zeroidle.c
1 /*
2  * (MPSAFE)
3  *
4  * Copyright (c) 1994 John Dyson
5  * Copyright (c) 2001 Matt Dillon
6  * Copyright (c) 2010 The DragonFly Project
7  *
8  * All Rights Reserved.
9  *
10  * This code is derived from software contributed to The DragonFly Project
11  * by Venkatesh Srinivas <me@endeavour.zapto.org>
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
26  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
27  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
29  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
31  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
33  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36  *
37  *      from: @(#)vm_machdep.c  7.3 (Berkeley) 5/13/91
38  *      Utah $Hdr: vm_machdep.c 1.16.1.1 89/06/23$
39  * from FreeBSD: .../i386/vm_machdep.c,v 1.165 2001/07/04 23:27:04 dillon
40  */
41
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/kernel.h>
45 #include <sys/proc.h>
46 #include <sys/vmmeter.h>
47 #include <sys/sched.h>
48 #include <sys/sysctl.h>
49 #include <sys/thread.h>
50 #include <sys/kthread.h>
51 #include <sys/unistd.h>
52 #include <vm/vm.h>
53 #include <vm/vm_page.h>
54 #include <cpu/lwbuf.h>
55
56 #include <sys/thread2.h>
57 #include <sys/mplock2.h>
58
59 /*
60  * Implement the pre-zeroed page mechanism.
61  */
62 #define ZIDLE_LO(v)     ((v) * 2 / 3)
63 #define ZIDLE_HI(v)     ((v) * 4 / 5)
64
65 /* Number of bytes to zero between reschedule checks */
66 #define IDLEZERO_RUN    (64)
67
68 /* Maximum number of pages per second to zero */
69 #define NPAGES_RUN      (20000)
70
71 static int idlezero_enable = 1;
72 TUNABLE_INT("vm.idlezero_enable", &idlezero_enable);
73 SYSCTL_INT(_vm, OID_AUTO, idlezero_enable, CTLFLAG_RW, &idlezero_enable, 0,
74            "Allow the kernel to use idle CPU cycles to zero pages");
75 static int idlezero_rate = NPAGES_RUN;
76 SYSCTL_INT(_vm, OID_AUTO, idlezero_rate, CTLFLAG_RW, &idlezero_rate, 0,
77            "Maximum pages per second to zero");
78 static int idlezero_nocache = -1;
79 SYSCTL_INT(_vm, OID_AUTO, idlezero_nocache, CTLFLAG_RW, &idlezero_nocache, 0,
80            "Maximum pages per second to zero");
81
82 static int idlezero_count = 0;
83 SYSCTL_INT(_vm, OID_AUTO, idlezero_count, CTLFLAG_RD, &idlezero_count, 0,
84            "The number of physical pages prezeroed at idle time");
85
86 enum zeroidle_state {
87         STATE_IDLE,
88         STATE_GET_PAGE,
89         STATE_ZERO_PAGE,
90         STATE_RELEASE_PAGE
91 };
92
93 #define DEFAULT_SLEEP_TIME      (hz / 10)
94 #define LONG_SLEEP_TIME         (hz * 10)
95
96 static int zero_state;
97
98 /*
99  * Attempt to maintain approximately 1/2 of our free pages in a
100  * PG_ZERO'd state. Add some hysteresis to (attempt to) avoid
101  * generally zeroing a page when the system is near steady-state.
102  * Otherwise we might get 'flutter' during disk I/O / IPC or
103  * fast sleeps. We also do not want to be continuously zeroing
104  * pages because doing so may flush our L1 and L2 caches too much.
105  *
106  * Returns non-zero if pages should be zerod.
107  */
108 static int
109 vm_page_zero_check(void)
110 {
111         if (idlezero_enable == 0)
112                 return (0);
113         if (zero_state == 0) {
114                 /*
115                  * Wait for the count to fall to LO before starting
116                  * to zero pages.
117                  */
118                 if (vm_page_zero_count <= ZIDLE_LO(vmstats.v_free_count))
119                         zero_state = 1;
120         } else {
121                 /*
122                  * Once we are zeroing pages wait for the count to
123                  * increase to HI before we stop zeroing pages.
124                  */
125                 if (vm_page_zero_count >= ZIDLE_HI(vmstats.v_free_count))
126                         zero_state = 0;
127         }
128         return (zero_state);
129 }
130
131 /*
132  * vm_pagezero should sleep for a longer time when idlezero is disabled or
133  * when there is an excess of zeroed pages.
134  */
135 static int
136 vm_page_zero_time(void)
137 {
138         if (idlezero_enable == 0)
139                 return (LONG_SLEEP_TIME);
140         if (vm_page_zero_count >= ZIDLE_HI(vmstats.v_free_count))
141                 return (LONG_SLEEP_TIME);
142         return (DEFAULT_SLEEP_TIME);
143 }
144
145 /*
146  * MPSAFE thread
147  */
148 static void
149 vm_pagezero(void __unused *arg)
150 {
151         vm_page_t m = NULL;
152         struct lwbuf *buf = NULL;
153         enum zeroidle_state state = STATE_IDLE;
154         char *pg = NULL;
155         int npages = 0;
156         int sleep_time; 
157         int i = 0;
158
159         /*
160          * Adjust thread parameters before entering our loop.  The thread
161          * is started with the MP lock held and with normal kernel thread
162          * priority.
163          *
164          * Also put us on the last cpu for now.
165          *
166          * For now leave the MP lock held, the VM routines cannot be called
167          * with it released until tokenization is finished.
168          */
169         lwkt_setpri_self(TDPRI_IDLE_WORK);
170         lwkt_setcpu_self(globaldata_find(ncpus - 1));
171         sleep_time = DEFAULT_SLEEP_TIME;
172
173         /*
174          * Loop forever
175          */
176         for (;;) {
177                 switch(state) {
178                 case STATE_IDLE:
179                         /*
180                          * Wait for work.
181                          */
182                         tsleep(&zero_state, 0, "pgzero", sleep_time);
183                         if (vm_page_zero_check())
184                                 npages = idlezero_rate / 10;
185                         sleep_time = vm_page_zero_time();
186                         if (npages)
187                                 state = STATE_GET_PAGE; /* Fallthrough */
188                         break;
189                 case STATE_GET_PAGE:
190                         /*
191                          * Acquire page to zero
192                          */
193                         if (--npages == 0) {
194                                 state = STATE_IDLE;
195                         } else {
196                                 m = vm_page_free_fromq_fast();
197                                 if (m == NULL) {
198                                         state = STATE_IDLE;
199                                 } else {
200                                         state = STATE_ZERO_PAGE;
201                                         buf = lwbuf_alloc(m);
202                                         pg = (char *)lwbuf_kva(buf);
203                                         i = 0;
204                                 }
205                         }
206                         break;
207                 case STATE_ZERO_PAGE:
208                         /*
209                          * Zero-out the page
210                          */
211                         while (i < PAGE_SIZE) {
212                                 if (idlezero_nocache == 1)
213                                         bzeront(&pg[i], IDLEZERO_RUN);
214                                 else
215                                         bzero(&pg[i], IDLEZERO_RUN);
216                                 i += IDLEZERO_RUN;
217                                 lwkt_yield();
218                         }
219                         state = STATE_RELEASE_PAGE;
220                         break;
221                 case STATE_RELEASE_PAGE:
222                         lwbuf_free(buf);
223                         vm_page_flag_set(m, PG_ZERO);
224                         vm_page_free_toq(m);
225                         state = STATE_GET_PAGE;
226                         ++idlezero_count;
227                         break;
228                 }
229                 lwkt_yield();
230         }
231 }
232
233 static void
234 pagezero_start(void __unused *arg)
235 {
236         int error;
237         struct thread *td;
238
239         if (idlezero_nocache < 0 && (cpu_mi_feature & CPU_MI_BZERONT))
240                 idlezero_nocache = 1;
241
242         error = kthread_create(vm_pagezero, NULL, &td, "pagezero");
243         if (error)
244                 panic("pagezero_start: error %d\n", error);
245 }
246
247 SYSINIT(pagezero, SI_SUB_KTHREAD_VM, SI_ORDER_ANY, pagezero_start, NULL);