1 | /* $NetBSD: rf_cvscan.c,v 1.16 2011/08/31 18:31:02 plunky Exp $ */ |
2 | /* |
3 | * Copyright (c) 1995 Carnegie-Mellon University. |
4 | * All rights reserved. |
5 | * |
6 | * Author: Mark Holland |
7 | * |
8 | * Permission to use, copy, modify and distribute this software and |
9 | * its documentation is hereby granted, provided that both the copyright |
10 | * notice and this permission notice appear in all copies of the |
11 | * software, derivative works or modified versions, and any portions |
12 | * thereof, and that both notices appear in supporting documentation. |
13 | * |
14 | * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" |
15 | * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND |
16 | * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. |
17 | * |
18 | * Carnegie Mellon requests users of this software to return to |
19 | * |
20 | * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU |
21 | * School of Computer Science |
22 | * Carnegie Mellon University |
23 | * Pittsburgh PA 15213-3890 |
24 | * |
25 | * any improvements or extensions that they make and grant Carnegie the |
26 | * rights to redistribute these changes. |
27 | */ |
28 | |
29 | /******************************************************************************* |
30 | * |
31 | * cvscan.c -- prioritized cvscan disk queueing code. |
32 | * |
33 | * Nov 9, 1994, adapted from raidSim version (MCH) |
34 | * |
35 | ******************************************************************************/ |
36 | |
37 | #include <sys/cdefs.h> |
38 | __KERNEL_RCSID(0, "$NetBSD: rf_cvscan.c,v 1.16 2011/08/31 18:31:02 plunky Exp $" ); |
39 | |
40 | #include <dev/raidframe/raidframevar.h> |
41 | #include "rf_alloclist.h" |
42 | #include "rf_stripelocks.h" |
43 | #include "rf_layout.h" |
44 | #include "rf_diskqueue.h" |
45 | #include "rf_cvscan.h" |
46 | #include "rf_debugMem.h" |
47 | #include "rf_general.h" |
48 | |
49 | #define DO_CHECK_STATE(_hdr_) CheckCvscanState((_hdr_)) |
50 | |
51 | #define pri_ok(p) ( ((p) == RF_IO_NORMAL_PRIORITY) || ((p) == RF_IO_LOW_PRIORITY)) |
52 | |
53 | static void |
54 | CheckCvscanState(RF_CvscanHeader_t *hdr) |
55 | { |
56 | long i, key; |
57 | RF_DiskQueueData_t *tmp; |
58 | |
59 | if (hdr->left != NULL) |
60 | RF_ASSERT(hdr->left->sectorOffset < hdr->cur_block); |
61 | for (key = hdr->cur_block, i = 0, tmp = hdr->left; |
62 | tmp != NULL; |
63 | key = tmp->sectorOffset, i++, tmp = tmp->next) |
64 | RF_ASSERT(tmp->sectorOffset <= key |
65 | && tmp->priority == hdr->nxt_priority && pri_ok(tmp->priority)); |
66 | RF_ASSERT(i == hdr->left_cnt); |
67 | |
68 | for (key = hdr->cur_block, i = 0, tmp = hdr->right; |
69 | tmp != NULL; |
70 | key = tmp->sectorOffset, i++, tmp = tmp->next) { |
71 | RF_ASSERT(key <= tmp->sectorOffset); |
72 | RF_ASSERT(tmp->priority == hdr->nxt_priority); |
73 | RF_ASSERT(pri_ok(tmp->priority)); |
74 | } |
75 | RF_ASSERT(i == hdr->right_cnt); |
76 | |
77 | for (key = hdr->nxt_priority - 1, tmp = hdr->burner; |
78 | tmp != NULL; |
79 | key = tmp->priority, tmp = tmp->next) { |
80 | RF_ASSERT(tmp); |
81 | RF_ASSERT(hdr); |
82 | RF_ASSERT(pri_ok(tmp->priority)); |
83 | RF_ASSERT(key >= tmp->priority); |
84 | RF_ASSERT(tmp->priority < hdr->nxt_priority); |
85 | } |
86 | } |
87 | |
88 | |
89 | |
90 | static void |
91 | PriorityInsert(RF_DiskQueueData_t **list_ptr, RF_DiskQueueData_t *req) |
92 | { |
93 | /* * insert block pointed to by req in to list whose first * entry is |
94 | * pointed to by the pointer that list_ptr points to * ie., list_ptr |
95 | * is a grandparent of the first entry */ |
96 | |
97 | for (; (*list_ptr) != NULL && (*list_ptr)->priority > req->priority; |
98 | list_ptr = &((*list_ptr)->next)) { |
99 | } |
100 | req->next = (*list_ptr); |
101 | (*list_ptr) = req; |
102 | } |
103 | |
104 | |
105 | |
106 | static void |
107 | ReqInsert(RF_DiskQueueData_t **list_ptr, RF_DiskQueueData_t *req, RF_CvscanArmDir_t order) |
108 | { |
109 | /* * insert block pointed to by req in to list whose first * entry is |
110 | * pointed to by the pointer that list_ptr points to * ie., list_ptr |
111 | * is a grandparent of the first entry */ |
112 | |
113 | for (; (*list_ptr) != NULL && |
114 | ((order == rf_cvscan_RIGHT && (*list_ptr)->sectorOffset <= req->sectorOffset) |
115 | || (order == rf_cvscan_LEFT && (*list_ptr)->sectorOffset > req->sectorOffset)); |
116 | list_ptr = &((*list_ptr)->next)) { |
117 | } |
118 | req->next = (*list_ptr); |
119 | (*list_ptr) = req; |
120 | } |
121 | |
122 | |
123 | |
124 | static RF_DiskQueueData_t * |
125 | ReqDequeue(RF_DiskQueueData_t **list_ptr) |
126 | { |
127 | RF_DiskQueueData_t *ret = (*list_ptr); |
128 | if ((*list_ptr) != NULL) { |
129 | (*list_ptr) = (*list_ptr)->next; |
130 | } |
131 | return (ret); |
132 | } |
133 | |
134 | |
135 | |
136 | static void |
137 | ReBalance(RF_CvscanHeader_t *hdr) |
138 | { |
139 | /* DO_CHECK_STATE(hdr); */ |
140 | while (hdr->right != NULL |
141 | && hdr->right->sectorOffset < hdr->cur_block) { |
142 | hdr->right_cnt--; |
143 | hdr->left_cnt++; |
144 | ReqInsert(&hdr->left, ReqDequeue(&hdr->right), rf_cvscan_LEFT); |
145 | } |
146 | /* DO_CHECK_STATE(hdr); */ |
147 | } |
148 | |
149 | |
150 | |
151 | static void |
152 | Transfer(RF_DiskQueueData_t **to_list_ptr, RF_DiskQueueData_t **from_list_ptr) |
153 | { |
154 | RF_DiskQueueData_t *gp; |
155 | for (gp = (*from_list_ptr); gp != NULL;) { |
156 | RF_DiskQueueData_t *p = gp->next; |
157 | PriorityInsert(to_list_ptr, gp); |
158 | gp = p; |
159 | } |
160 | (*from_list_ptr) = NULL; |
161 | } |
162 | |
163 | |
164 | |
165 | static void |
166 | RealEnqueue(RF_CvscanHeader_t *hdr, RF_DiskQueueData_t *req) |
167 | { |
168 | RF_ASSERT(req->priority == RF_IO_NORMAL_PRIORITY || req->priority == RF_IO_LOW_PRIORITY); |
169 | |
170 | DO_CHECK_STATE(hdr); |
171 | if (hdr->left_cnt == 0 && hdr->right_cnt == 0) { |
172 | hdr->nxt_priority = req->priority; |
173 | } |
174 | if (req->priority > hdr->nxt_priority) { |
175 | /* |
176 | ** dump all other outstanding requests on the back burner |
177 | */ |
178 | Transfer(&hdr->burner, &hdr->left); |
179 | Transfer(&hdr->burner, &hdr->right); |
180 | hdr->left_cnt = 0; |
181 | hdr->right_cnt = 0; |
182 | hdr->nxt_priority = req->priority; |
183 | } |
184 | if (req->priority < hdr->nxt_priority) { |
185 | /* |
186 | ** yet another low priority task! |
187 | */ |
188 | PriorityInsert(&hdr->burner, req); |
189 | } else { |
190 | if (req->sectorOffset < hdr->cur_block) { |
191 | /* this request is to the left of the current arms */ |
192 | ReqInsert(&hdr->left, req, rf_cvscan_LEFT); |
193 | hdr->left_cnt++; |
194 | } else { |
195 | /* this request is to the right of the current arms */ |
196 | ReqInsert(&hdr->right, req, rf_cvscan_RIGHT); |
197 | hdr->right_cnt++; |
198 | } |
199 | } |
200 | DO_CHECK_STATE(hdr); |
201 | } |
202 | |
203 | |
204 | |
205 | void |
206 | rf_CvscanEnqueue(void *q_in, RF_DiskQueueData_t * elem, int priority) |
207 | { |
208 | RF_CvscanHeader_t *hdr = (RF_CvscanHeader_t *) q_in; |
209 | RealEnqueue(hdr, elem /* req */ ); |
210 | } |
211 | |
212 | |
213 | |
214 | RF_DiskQueueData_t * |
215 | rf_CvscanDequeue(void *q_in) |
216 | { |
217 | RF_CvscanHeader_t *hdr = (RF_CvscanHeader_t *) q_in; |
218 | long range, i, sum_dist_left, sum_dist_right; |
219 | RF_DiskQueueData_t *ret; |
220 | RF_DiskQueueData_t *tmp; |
221 | |
222 | DO_CHECK_STATE(hdr); |
223 | |
224 | if (hdr->left_cnt == 0 && hdr->right_cnt == 0) |
225 | return (NULL); |
226 | |
227 | range = RF_MIN(hdr->range_for_avg, RF_MIN(hdr->left_cnt, hdr->right_cnt)); |
228 | for (i = 0, tmp = hdr->left, sum_dist_left = |
229 | ((hdr->direction == rf_cvscan_RIGHT) ? range * hdr->change_penalty : 0); |
230 | tmp != NULL && i < range; |
231 | tmp = tmp->next, i++) { |
232 | sum_dist_left += hdr->cur_block - tmp->sectorOffset; |
233 | } |
234 | for (i = 0, tmp = hdr->right, sum_dist_right = |
235 | ((hdr->direction == rf_cvscan_LEFT) ? range * hdr->change_penalty : 0); |
236 | tmp != NULL && i < range; |
237 | tmp = tmp->next, i++) { |
238 | sum_dist_right += tmp->sectorOffset - hdr->cur_block; |
239 | } |
240 | |
241 | if (hdr->right_cnt == 0 || sum_dist_left < sum_dist_right) { |
242 | hdr->direction = rf_cvscan_LEFT; |
243 | hdr->cur_block = hdr->left->sectorOffset + hdr->left->numSector; |
244 | hdr->left_cnt = RF_MAX(hdr->left_cnt - 1, 0); |
245 | tmp = hdr->left; |
246 | ret = (ReqDequeue(&hdr->left)) /*->parent*/ ; |
247 | } else { |
248 | hdr->direction = rf_cvscan_RIGHT; |
249 | hdr->cur_block = hdr->right->sectorOffset + hdr->right->numSector; |
250 | hdr->right_cnt = RF_MAX(hdr->right_cnt - 1, 0); |
251 | tmp = hdr->right; |
252 | ret = (ReqDequeue(&hdr->right)) /*->parent*/ ; |
253 | } |
254 | ReBalance(hdr); |
255 | |
256 | if (hdr->left_cnt == 0 && hdr->right_cnt == 0 |
257 | && hdr->burner != NULL) { |
258 | /* |
259 | ** restore low priority requests for next dequeue |
260 | */ |
261 | RF_DiskQueueData_t *burner = hdr->burner; |
262 | hdr->nxt_priority = burner->priority; |
263 | while (burner != NULL |
264 | && burner->priority == hdr->nxt_priority) { |
265 | RF_DiskQueueData_t *next = burner->next; |
266 | RealEnqueue(hdr, burner); |
267 | burner = next; |
268 | } |
269 | hdr->burner = burner; |
270 | } |
271 | DO_CHECK_STATE(hdr); |
272 | return (ret); |
273 | } |
274 | |
275 | |
276 | |
277 | RF_DiskQueueData_t * |
278 | rf_CvscanPeek(void *q_in) |
279 | { |
280 | RF_CvscanHeader_t *hdr = (RF_CvscanHeader_t *) q_in; |
281 | long range, i, sum_dist_left, sum_dist_right; |
282 | RF_DiskQueueData_t *tmp, *headElement; |
283 | |
284 | DO_CHECK_STATE(hdr); |
285 | |
286 | if (hdr->left_cnt == 0 && hdr->right_cnt == 0) |
287 | headElement = NULL; |
288 | else { |
289 | range = RF_MIN(hdr->range_for_avg, RF_MIN(hdr->left_cnt, hdr->right_cnt)); |
290 | for (i = 0, tmp = hdr->left, sum_dist_left = |
291 | ((hdr->direction == rf_cvscan_RIGHT) ? range * hdr->change_penalty : 0); |
292 | tmp != NULL && i < range; |
293 | tmp = tmp->next, i++) { |
294 | sum_dist_left += hdr->cur_block - tmp->sectorOffset; |
295 | } |
296 | for (i = 0, tmp = hdr->right, sum_dist_right = |
297 | ((hdr->direction == rf_cvscan_LEFT) ? range * hdr->change_penalty : 0); |
298 | tmp != NULL && i < range; |
299 | tmp = tmp->next, i++) { |
300 | sum_dist_right += tmp->sectorOffset - hdr->cur_block; |
301 | } |
302 | |
303 | if (hdr->right_cnt == 0 || sum_dist_left < sum_dist_right) |
304 | headElement = hdr->left; |
305 | else |
306 | headElement = hdr->right; |
307 | } |
308 | return (headElement); |
309 | } |
310 | |
311 | |
312 | |
313 | /* |
314 | ** CVSCAN( 1, 0 ) is Shortest Seek Time First (SSTF) |
315 | ** lowest average response time |
316 | ** CVSCAN( 1, infinity ) is SCAN |
317 | ** lowest response time standard deviation |
318 | */ |
319 | |
320 | void * |
321 | rf_CvscanCreate(RF_SectorCount_t sectPerDisk, |
322 | RF_AllocListElem_t *clList, |
323 | RF_ShutdownList_t **listp) |
324 | { |
325 | RF_CvscanHeader_t *hdr; |
326 | long range = 2; /* Currently no mechanism to change these */ |
327 | long penalty = sectPerDisk / 5; |
328 | |
329 | RF_MallocAndAdd(hdr, sizeof(RF_CvscanHeader_t), (RF_CvscanHeader_t *), clList); |
330 | memset((char *) hdr, 0, sizeof(RF_CvscanHeader_t)); |
331 | hdr->range_for_avg = RF_MAX(range, 1); |
332 | hdr->change_penalty = RF_MAX(penalty, 0); |
333 | hdr->direction = rf_cvscan_RIGHT; |
334 | hdr->cur_block = 0; |
335 | hdr->left_cnt = hdr->right_cnt = 0; |
336 | hdr->left = hdr->right = NULL; |
337 | hdr->burner = NULL; |
338 | DO_CHECK_STATE(hdr); |
339 | |
340 | return ((void *) hdr); |
341 | } |
342 | |
343 | |
344 | #if defined(__NetBSD__) && defined(_KERNEL) |
345 | /* PrintCvscanQueue is not used, so we ignore it... */ |
346 | #else |
347 | static void |
348 | PrintCvscanQueue(RF_CvscanHeader_t *hdr) |
349 | { |
350 | RF_DiskQueueData_t *tmp; |
351 | |
352 | printf("CVSCAN(%d,%d) at %d going %s\n" , |
353 | (int) hdr->range_for_avg, |
354 | (int) hdr->change_penalty, |
355 | (int) hdr->cur_block, |
356 | (hdr->direction == rf_cvscan_LEFT) ? "LEFT" : "RIGHT" ); |
357 | printf("\tLeft(%d): " , hdr->left_cnt); |
358 | for (tmp = hdr->left; tmp != NULL; tmp = tmp->next) |
359 | printf("(%d,%ld,%d) " , |
360 | (int) tmp->sectorOffset, |
361 | (long) (tmp->sectorOffset + tmp->numSector), |
362 | tmp->priority); |
363 | printf("\n" ); |
364 | printf("\tRight(%d): " , hdr->right_cnt); |
365 | for (tmp = hdr->right; tmp != NULL; tmp = tmp->next) |
366 | printf("(%d,%ld,%d) " , |
367 | (int) tmp->sectorOffset, |
368 | (long) (tmp->sectorOffset + tmp->numSector), |
369 | tmp->priority); |
370 | printf("\n" ); |
371 | printf("\tBurner: " ); |
372 | for (tmp = hdr->burner; tmp != NULL; tmp = tmp->next) |
373 | printf("(%d,%ld,%d) " , |
374 | (int) tmp->sectorOffset, |
375 | (long) (tmp->sectorOffset + tmp->numSector), |
376 | tmp->priority); |
377 | printf("\n" ); |
378 | } |
379 | #endif |
380 | |
381 | |
382 | /* promotes reconstruction accesses for the given stripeID to normal priority. |
383 | * returns 1 if an access was found and zero otherwise. Normally, we should |
384 | * only have one or zero entries in the burner queue, so execution time should |
385 | * be short. |
386 | */ |
387 | int |
388 | rf_CvscanPromote(void *q_in, RF_StripeNum_t parityStripeID, |
389 | RF_ReconUnitNum_t which_ru) |
390 | { |
391 | RF_CvscanHeader_t *hdr = (RF_CvscanHeader_t *) q_in; |
392 | RF_DiskQueueData_t *trailer = NULL, *tmp = hdr->burner, *tlist = NULL; |
393 | int retval = 0; |
394 | |
395 | DO_CHECK_STATE(hdr); |
396 | while (tmp) { /* handle entries at the front of the list */ |
397 | if (tmp->parityStripeID == parityStripeID && tmp->which_ru == which_ru) { |
398 | hdr->burner = tmp->next; |
399 | tmp->priority = RF_IO_NORMAL_PRIORITY; |
400 | tmp->next = tlist; |
401 | tlist = tmp; |
402 | tmp = hdr->burner; |
403 | } else |
404 | break; |
405 | } |
406 | if (tmp) { |
407 | trailer = tmp; |
408 | tmp = tmp->next; |
409 | } |
410 | while (tmp) { /* handle entries on the rest of the list */ |
411 | if (tmp->parityStripeID == parityStripeID && tmp->which_ru == which_ru) { |
412 | trailer->next = tmp->next; |
413 | tmp->priority = RF_IO_NORMAL_PRIORITY; |
414 | tmp->next = tlist; |
415 | tlist = tmp; /* insert on a temp queue */ |
416 | tmp = trailer->next; |
417 | } else { |
418 | trailer = tmp; |
419 | tmp = tmp->next; |
420 | } |
421 | } |
422 | while (tlist) { |
423 | retval++; |
424 | tmp = tlist->next; |
425 | RealEnqueue(hdr, tlist); |
426 | tlist = tmp; |
427 | } |
428 | RF_ASSERT(retval == 0 || retval == 1); |
429 | DO_CHECK_STATE((RF_CvscanHeader_t *) q_in); |
430 | return (retval); |
431 | } |
432 | |