kernel/dsched: Add a version parameter to the DSCHED_POLICY_MODULE macro.
[dragonfly.git] / sys / kern / dsched / as / as.c
CommitLineData
e8e9bef2
BP
1#include <sys/param.h>
2#include <sys/systm.h>
3#include <sys/kernel.h>
4#include <sys/proc.h>
5#include <sys/sysctl.h>
6#include <sys/buf.h>
7#include <sys/conf.h>
8#include <sys/diskslice.h>
9#include <sys/disk.h>
10#include <sys/malloc.h>
11#include <machine/md_var.h>
12#include <sys/ctype.h>
13#include <sys/syslog.h>
14#include <sys/device.h>
15#include <sys/msgport.h>
16#include <sys/msgport2.h>
17#include <sys/buf2.h>
18#include <sys/dsched.h>
19#include <sys/fcntl.h>
20#include <machine/varargs.h>
21
22/*
23 * A simple anticipatory scheduler
24 */
25struct as_disk_ctx{
26 struct dsched_disk_ctx head;
27 TAILQ_HEAD(, bio) as_queue_rd;
28 TAILQ_HEAD(, bio) as_queue_wr;
29 /*
30 * TODO: lockmgr may be too heavy here,
31 * use spinlock instead!
32 */
33 struct lock as_queue_rd_lock;
34 struct lock as_queue_wr_lock;
35 int queue_rd_size;
36 int queue_wr_size;
37
38 struct callout as_callout;
39
40 int as_blockall;
41 pid_t as_blockon;
42};
43
44struct dsched_as_stats{
45 int32_t unused;
46}as_stats;
47
48static dsched_prepare_t as_prepare;
49static dsched_teardown_t as_teardown;
50static dsched_cancel_t as_cancel;
51static dsched_queue_t as_queue;
52static dsched_polling_func_t as_dequeue;
53
54static struct dsched_policy dsched_as_policy = {
55 .name ="as",
56/*
57 * field need_request_polling
58 * is removed from struct dsched_policy
59 */
60 //.need_request_polling = 1,
61 .prepare = as_prepare,
62 .teardown = as_teardown,
63 .cancel_all = as_cancel,
64 .bio_queue = as_queue,
65 .polling_func = as_dequeue
66};
67
68static int dsched_as_version_maj = 1;
69static int dsched_as_version_min = 0;
70
71static int
72as_prepare(struct dsched_disk_ctx *diskctx)
73{
74 struct as_disk_ctx *as_diskctx = (struct as_disk_ctx *)diskctx;
75 TAILQ_INIT(&as_diskctx->as_queue_wr);
76 as_diskctx->queue_wr_size = 0;
77 TAILQ_INIT(&as_diskctx->as_queue_rd);
78 as_diskctx->queue_rd_size = 0;
79 lockinit(&as_diskctx->as_queue_rd_lock, "as_queue_rd", 0, LK_CANRECURSE);
80 lockinit(&as_diskctx->as_queue_wr_lock, "as_queue_wr", 0, LK_CANRECURSE);
81 callout_init(&as_diskctx->as_callout);
82 as_diskctx->as_blockall = 0;
83 as_diskctx->as_blockon = NO_PID;
84 return 0;
85}
86
87static void
88as_teardown(struct dsched_disk_ctx *diskctx)
89{
90
91
92}
93
94static void
95as_cancel(struct dsched_disk_ctx *diskctx)
96{
97 struct as_disk_ctx *as_diskctx = (struct as_disk_ctx *)diskctx;
98 struct bio *bio, *bio2;
99 struct dsched_thread_io *tdio;
100 DSCHED_DISK_CTX_LOCK(&as_diskctx->head);
101 lockmgr(&as_diskctx->as_queue_rd_lock, LK_EXCLUSIVE);
102 TAILQ_FOREACH_MUTABLE(bio, &as_diskctx->as_queue_rd, link, bio2){
103 TAILQ_REMOVE(&as_diskctx->as_queue_rd, bio, link);
104 tdio = dsched_get_bio_tdio(bio);
105 dsched_cancel_bio(bio);
106 dsched_thread_io_unref(tdio);
107 }
108 lockmgr(&as_diskctx->as_queue_rd_lock, LK_RELEASE);
109
110 lockmgr(&as_diskctx->as_queue_wr_lock, LK_EXCLUSIVE);
111 TAILQ_FOREACH_MUTABLE(bio, &as_diskctx->as_queue_wr, link, bio2){
112 TAILQ_REMOVE(&as_diskctx->as_queue_wr, bio, link);
113 tdio = dsched_get_bio_tdio(bio);
114 dsched_cancel_bio(bio);
115 dsched_thread_io_unref(tdio);
116 }
117 lockmgr(&as_diskctx->as_queue_wr_lock, LK_RELEASE);
118
119 DSCHED_DISK_CTX_UNLOCK(&as_diskctx->head);
120}
121
122static void
123as_timeout(void *p)
124{
125 pid_t last_blockon;
126 struct as_disk_ctx *as_diskctx = (struct as_disk_ctx *)p;
127 DSCHED_DISK_CTX_LOCK(&as_diskctx->head);
128 as_diskctx->as_blockall = 0;
129 last_blockon = as_diskctx->as_blockon;
130 as_diskctx->as_blockon = NO_PID;
131 DSCHED_DISK_CTX_UNLOCK(&as_diskctx->head);
132 //dsched_debug(0, "dsched: as, timeout %d\n", last_blockon);
133 as_dequeue((struct dsched_disk_ctx *)as_diskctx);
134}
135
136static int
137as_queue(struct dsched_disk_ctx *diskctx, struct dsched_thread_io *tdio,
138 struct bio *bio)
139{
140 struct as_disk_ctx *as_diskctx = (struct as_disk_ctx *)diskctx;
141 //if (tdio->p && (uint32_t)tdio->p != ~0)
142 // dsched_debug(0, "dsched: user process bio from %d\n", tdio->p->p_pid);
143 /*save tdio for each bio*/
144 dsched_set_bio_priv(bio, tdio);
145 dsched_set_bio_tdio(bio, tdio);
146 /* will be unreferenced in bio_done function */
147 dsched_thread_io_ref(tdio);
148 DSCHED_DISK_CTX_LOCK(&as_diskctx->head);
149 /* blocking for as,
150 * if current bio is from as_blockon, insert it at head
151 */
152 if (bio->bio_buf->b_cmd == BUF_CMD_READ){
153 lockmgr(&as_diskctx->as_queue_rd_lock, LK_EXCLUSIVE);
154 if (as_diskctx->as_blockall && tdio->p && as_diskctx->as_blockon == tdio->p->p_pid)
155 TAILQ_INSERT_HEAD(&as_diskctx->as_queue_rd, bio, link);
156 else
157 TAILQ_INSERT_TAIL(&as_diskctx->as_queue_rd, bio, link);
158 atomic_add_int(&as_diskctx->queue_rd_size, 1);
159 lockmgr(&as_diskctx->as_queue_rd_lock, LK_RELEASE);
160 } else {
161 lockmgr(&as_diskctx->as_queue_wr_lock, LK_EXCLUSIVE);
162 TAILQ_INSERT_TAIL(&as_diskctx->as_queue_wr, bio, link);
163 atomic_add_int(&as_diskctx->queue_wr_size, 1);
164 lockmgr(&as_diskctx->as_queue_wr_lock, LK_RELEASE);
165 }
166 DSCHED_DISK_CTX_UNLOCK(&as_diskctx->head);
167 as_dequeue(diskctx);
168 return 0;
169
170}
171static void
172as_dequeue(struct dsched_disk_ctx *diskctx)
173{
174 int free_slots = 0;
175 struct as_disk_ctx *as_diskctx = (struct as_disk_ctx *)diskctx;
176 struct bio *bio;
177 struct dsched_thread_io *tdio;
178 /*Lock the diskctx for the whole dispatching process,
179 * to ensure atomic change to current_tab_queue_depth*/
180
181 DSCHED_DISK_CTX_LOCK(&as_diskctx->head);
182 /* if blocking all dispatching for anticipatory scheduling
183 * return directly
184 */
185 if (as_diskctx->as_blockall){
186 // dsched_debug(0, "dsched: as, dequeue blocked! %d\n", as_diskctx->as_blockon);
187 goto rtn;
188 }
189 free_slots = as_diskctx->head.max_tag_queue_depth - as_diskctx->head.current_tag_queue_depth;
190 KKASSERT(free_slots>=0 && free_slots <=64);
191 lockmgr(&as_diskctx->as_queue_rd_lock, LK_EXCLUSIVE);
192 while (free_slots > 0){
193 if (TAILQ_EMPTY(&as_diskctx->as_queue_rd))
194 break;
195 bio = TAILQ_FIRST(&as_diskctx->as_queue_rd);
196 tdio = dsched_get_bio_priv(bio);
197 //kernel thread
b3fc94f8 198 if (!tdio->p) {
e8e9bef2
BP
199 TAILQ_REMOVE(&as_diskctx->as_queue_rd, bio, link);
200 dsched_strategy_request_polling(as_diskctx->head.dp, bio, diskctx);
201 } else {
202 //user process, continue
203// dsched_debug(0, "dsched: as, user process bio\n");
204 if (as_diskctx->as_blockon == NO_PID || as_diskctx->as_blockon == tdio->p->p_pid){
205 as_diskctx->as_blockon = tdio->p->p_pid;
206 TAILQ_REMOVE(&as_diskctx->as_queue_rd, bio, link);
207 dsched_strategy_request_polling(as_diskctx->head.dp, bio, diskctx);
208 } else {
209 //user process, before switching, as!
210 as_diskctx->as_blockall = 1;
211// dsched_debug(0, "dsched: as, block on %d\n", as_diskctx->as_blockon);
212 callout_reset(&as_diskctx->as_callout, 10, as_timeout, as_diskctx);
213 break;
214 }
215
216 }
217 free_slots --;
218 }
219 lockmgr(&as_diskctx->as_queue_rd_lock, LK_RELEASE);
220 lockmgr(&as_diskctx->as_queue_wr_lock, LK_EXCLUSIVE);
221 while (free_slots > 0){
222 if (TAILQ_EMPTY(&as_diskctx->as_queue_wr))
223 break;
224 bio = TAILQ_FIRST(&as_diskctx->as_queue_wr);
225 TAILQ_REMOVE(&as_diskctx->as_queue_wr, bio, link);
226 dsched_strategy_request_polling(as_diskctx->head.dp, bio, diskctx);
227 free_slots --;
228 }
229 lockmgr(&as_diskctx->as_queue_wr_lock, LK_RELEASE);
230
231rtn:
232 DSCHED_DISK_CTX_UNLOCK(&as_diskctx->head);
233}
234
235static int
236do_asstats(SYSCTL_HANDLER_ARGS)
237{
238 return (sysctl_handle_opaque(oidp, &as_stats, sizeof(struct dsched_as_stats), req));
239}
240
241static int
242as_mod_handler(module_t mod, int type, void *unused)
243{
244 static struct sysctl_ctx_list sysctl_ctx;
245 static struct sysctl_oid *oid;
246 static char version[16];
247 int error;
248
249 ksnprintf(version, sizeof(version), "%d.%d",
250 dsched_as_version_maj, dsched_as_version_min);
251
252 switch (type) {
253 case MOD_LOAD:
254 bzero(&as_stats, sizeof(struct dsched_as_stats));
255 if ((error = dsched_register(&dsched_as_policy)))
256 return (error);
257
258 sysctl_ctx_init(&sysctl_ctx);
259 oid = SYSCTL_ADD_NODE(&sysctl_ctx,
260 SYSCTL_STATIC_CHILDREN(_dsched),
261 OID_AUTO,
262 "as",
263 CTLFLAG_RD, 0, "");
264
265 SYSCTL_ADD_PROC(&sysctl_ctx, SYSCTL_CHILDREN(oid),
266 OID_AUTO, "stats", CTLTYPE_OPAQUE|CTLFLAG_RD,
267 0, 0, do_asstats, "S,dsched_as_stats", "as statistics");
268
269 SYSCTL_ADD_STRING(&sysctl_ctx, SYSCTL_CHILDREN(oid),
270 OID_AUTO, "version", CTLFLAG_RD, version, 0, "as version");
271
272 kprintf("AS scheduler policy version %d.%d loaded\n",
273 dsched_as_version_maj, dsched_as_version_min);
274 break;
275
276 case MOD_UNLOAD:
277 if ((error = dsched_unregister(&dsched_as_policy)))
278 return (error);
279 sysctl_ctx_free(&sysctl_ctx);
280 kprintf("AS scheduler policy unloaded\n");
281 break;
282
283 default:
284 break;
285 }
286
287 return 0;
288
289}
1f509c0d 290DSCHED_POLICY_MODULE(dsched_as, as_mod_handler, 1);