xref: /freebsd/usr.sbin/nscd/cacheplcs.c (revision 195ebc7e9e4b129de810833791a19dfb4349d6a9)
1 /*-
2  * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  */
27 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 #include <assert.h>
32 #include <string.h>
33 #include "cacheplcs.h"
34 #include "debug.h"
35 
36 static void cache_fifo_policy_update_item(struct cache_policy_ *,
37 	struct cache_policy_item_ *);
38 static void cache_lfu_policy_add_item(struct cache_policy_ *,
39 	struct cache_policy_item_ *);
40 static struct cache_policy_item_ * cache_lfu_policy_create_item(void);
41 static void cache_lfu_policy_destroy_item(struct cache_policy_item_ *);
42 static struct cache_policy_item_ *cache_lfu_policy_get_first_item(
43 	struct cache_policy_ *);
44 static struct cache_policy_item_ *cache_lfu_policy_get_last_item(
45 	struct cache_policy_ *);
46 static struct cache_policy_item_ *cache_lfu_policy_get_next_item(
47 	struct cache_policy_ *, struct cache_policy_item_ *);
48 static struct cache_policy_item_ *cache_lfu_policy_get_prev_item(
49 	struct cache_policy_ *, struct cache_policy_item_ *);
50 static void cache_lfu_policy_remove_item(struct cache_policy_ *,
51 	struct cache_policy_item_ *);
52 static void cache_lfu_policy_update_item(struct cache_policy_ *,
53 	struct cache_policy_item_ *);
54 static void cache_lru_policy_update_item(struct cache_policy_ *,
55 	struct cache_policy_item_ *);
56 static void cache_queue_policy_add_item(struct cache_policy_ *,
57 	struct cache_policy_item_ *);
58 static struct cache_policy_item_ * cache_queue_policy_create_item();
59 static void cache_queue_policy_destroy_item(struct cache_policy_item_ *);
60 static struct cache_policy_item_ *cache_queue_policy_get_first_item(
61 	struct cache_policy_ *);
62 static struct cache_policy_item_ *cache_queue_policy_get_last_item(
63 	struct cache_policy_ *);
64 static struct cache_policy_item_ *cache_queue_policy_get_next_item(
65 	struct cache_policy_ *, struct cache_policy_item_ *);
66 static struct cache_policy_item_ *cache_queue_policy_get_prev_item(
67 	struct cache_policy_ *, struct cache_policy_item_ *);
68 static void cache_queue_policy_remove_item(struct cache_policy_ *,
69 	struct cache_policy_item_ *);
70 static void destroy_cache_queue_policy(struct cache_queue_policy_ *);
71 static struct cache_queue_policy_ *init_cache_queue_policy(void);
72 
73 /*
74  * All cache_queue_policy_XXX functions below will be used to fill
75  * the cache_queue_policy structure. They implement the most functionality of
76  * LRU and FIFO policies. LRU and FIFO policies are actually the
77  * cache_queue_policy_ with cache_update_item function changed.
78  */
79 static struct cache_policy_item_ *
80 cache_queue_policy_create_item()
81 {
82 	struct cache_queue_policy_item_ *retval;
83 
84 	TRACE_IN(cache_queue_policy_create_item);
85 	retval = (struct cache_queue_policy_item_ *)calloc(1,
86 		sizeof(struct cache_queue_policy_item_));
87 	assert(retval != NULL);
88 
89 	TRACE_OUT(cache_queue_policy_create_item);
90 	return ((struct cache_policy_item_ *)retval);
91 }
92 
93 static void
94 cache_queue_policy_destroy_item(struct cache_policy_item_ *item)
95 {
96 
97 	TRACE_IN(cache_queue_policy_destroy_item);
98 	assert(item != NULL);
99 	free(item);
100 	TRACE_OUT(cache_queue_policy_destroy_item);
101 }
102 
103 static void
104 cache_queue_policy_add_item(struct cache_policy_ *policy,
105 	struct cache_policy_item_ *item)
106 {
107 	struct cache_queue_policy_ *queue_policy;
108 	struct cache_queue_policy_item_ *queue_item;
109 
110 	TRACE_IN(cache_queue_policy_add_item);
111 	queue_policy = (struct cache_queue_policy_ *)policy;
112 	queue_item = (struct cache_queue_policy_item_ *)item;
113 	TAILQ_INSERT_TAIL(&queue_policy->head, queue_item, entries);
114 	TRACE_OUT(cache_queue_policy_add_item);
115 }
116 
117 static void
118 cache_queue_policy_remove_item(struct cache_policy_ *policy,
119 	struct cache_policy_item_ *item)
120 {
121 	struct cache_queue_policy_ *queue_policy;
122 	struct cache_queue_policy_item_	*queue_item;
123 
124 	TRACE_IN(cache_queue_policy_remove_item);
125 	queue_policy = (struct cache_queue_policy_ *)policy;
126 	queue_item = (struct cache_queue_policy_item_ *)item;
127 	TAILQ_REMOVE(&queue_policy->head, queue_item, entries);
128 	TRACE_OUT(cache_queue_policy_remove_item);
129 }
130 
131 static struct cache_policy_item_ *
132 cache_queue_policy_get_first_item(struct cache_policy_ *policy)
133 {
134 	struct cache_queue_policy_ *queue_policy;
135 
136 	TRACE_IN(cache_queue_policy_get_first_item);
137 	queue_policy = (struct cache_queue_policy_ *)policy;
138 	TRACE_OUT(cache_queue_policy_get_first_item);
139 	return ((struct cache_policy_item_ *)TAILQ_FIRST(&queue_policy->head));
140 }
141 
142 static struct cache_policy_item_ *
143 cache_queue_policy_get_last_item(struct cache_policy_ *policy)
144 {
145 	struct cache_queue_policy_ *queue_policy;
146 
147 	TRACE_IN(cache_queue_policy_get_last_item);
148 	queue_policy = (struct cache_queue_policy_ *)policy;
149 	TRACE_OUT(cache_queue_policy_get_last_item);
150 	return ((struct cache_policy_item_ *)TAILQ_LAST(&queue_policy->head,
151 		cache_queue_policy_head_));
152 }
153 
154 static struct cache_policy_item_ *
155 cache_queue_policy_get_next_item(struct cache_policy_ *policy,
156 	struct cache_policy_item_ *item)
157 {
158 	struct cache_queue_policy_ *queue_policy;
159 	struct cache_queue_policy_item_	*queue_item;
160 
161 	TRACE_IN(cache_queue_policy_get_next_item);
162 	queue_policy = (struct cache_queue_policy_ *)policy;
163 	queue_item = (struct cache_queue_policy_item_ *)item;
164 
165 	TRACE_OUT(cache_queue_policy_get_next_item);
166 	return ((struct cache_policy_item_ *)TAILQ_NEXT(queue_item, entries));
167 }
168 
169 static struct cache_policy_item_ *
170 cache_queue_policy_get_prev_item(struct cache_policy_ *policy,
171 	struct cache_policy_item_ *item)
172 {
173 	struct cache_queue_policy_ *queue_policy;
174 	struct cache_queue_policy_item_	*queue_item;
175 
176 	TRACE_IN(cache_queue_policy_get_prev_item);
177 	queue_policy = (struct cache_queue_policy_ *)policy;
178 	queue_item = (struct cache_queue_policy_item_ *)item;
179 
180 	TRACE_OUT(cache_queue_policy_get_prev_item);
181 	return ((struct cache_policy_item_ *)TAILQ_PREV(queue_item,
182 		cache_queue_policy_head_, entries));
183 }
184 
185 /*
186  * Initializes cache_queue_policy_ by filling the structure with the functions
187  * pointers, defined above
188  */
189 static struct cache_queue_policy_ *
190 init_cache_queue_policy(void)
191 {
192 	struct cache_queue_policy_	*retval;
193 
194 	TRACE_IN(init_cache_queue_policy);
195 	retval = (struct cache_queue_policy_ *)calloc(1,
196 		sizeof(struct cache_queue_policy_));
197 	assert(retval != NULL);
198 
199 	retval->parent_data.create_item_func = cache_queue_policy_create_item;
200 	retval->parent_data.destroy_item_func = cache_queue_policy_destroy_item;
201 
202 	retval->parent_data.add_item_func = cache_queue_policy_add_item;
203 	retval->parent_data.remove_item_func = cache_queue_policy_remove_item;
204 
205 	retval->parent_data.get_first_item_func =
206 		cache_queue_policy_get_first_item;
207 	retval->parent_data.get_last_item_func =
208 		cache_queue_policy_get_last_item;
209 	retval->parent_data.get_next_item_func =
210 		cache_queue_policy_get_next_item;
211 	retval->parent_data.get_prev_item_func =
212 		cache_queue_policy_get_prev_item;
213 
214 	TAILQ_INIT(&retval->head);
215 	TRACE_OUT(init_cache_queue_policy);
216 	return (retval);
217 }
218 
219 static void
220 destroy_cache_queue_policy(struct cache_queue_policy_ *queue_policy)
221 {
222 	struct cache_queue_policy_item_	*queue_item;
223 
224 	TRACE_IN(destroy_cache_queue_policy);
225 	while (!TAILQ_EMPTY(&queue_policy->head)) {
226 		queue_item = TAILQ_FIRST(&queue_policy->head);
227 		TAILQ_REMOVE(&queue_policy->head, queue_item, entries);
228 		cache_queue_policy_destroy_item(
229 			(struct cache_policy_item_ *)queue_item);
230 	}
231 	free(queue_policy);
232 	TRACE_OUT(destroy_cache_queue_policy);
233 }
234 
235 /*
236  * Makes cache_queue_policy_ behave like FIFO policy - we don't do anything,
237  * when the cache element is updated. So it always stays in its initial
238  * position in the queue - that is exactly the FIFO functionality.
239  */
240 static void
241 cache_fifo_policy_update_item(struct cache_policy_ *policy,
242 	struct cache_policy_item_ *item)
243 {
244 
245 	TRACE_IN(cache_fifo_policy_update_item);
246 	/* policy and item arguments are ignored */
247 	TRACE_OUT(cache_fifo_policy_update_item);
248 }
249 
250 struct cache_policy_ *
251 init_cache_fifo_policy()
252 {
253 	struct cache_queue_policy_ *retval;
254 
255 	TRACE_IN(init_cache_fifo_policy);
256 	retval = init_cache_queue_policy();
257 	retval->parent_data.update_item_func = cache_fifo_policy_update_item;
258 
259 	TRACE_OUT(init_cache_fifo_policy);
260 	return ((struct cache_policy_ *)retval);
261 }
262 
263 void
264 destroy_cache_fifo_policy(struct cache_policy_ *policy)
265 {
266 	struct cache_queue_policy_	*queue_policy;
267 
268 	TRACE_IN(destroy_cache_fifo_policy);
269 	queue_policy = (struct cache_queue_policy_ *)policy;
270 	destroy_cache_queue_policy(queue_policy);
271 	TRACE_OUT(destroy_cache_fifo_policy);
272 }
273 
274 /*
275  * Makes cache_queue_policy_ behave like LRU policy. On each update, cache
276  * element is moved to the end of the queue - so it would be deleted in last
277  * turn. That is exactly the LRU policy functionality.
278  */
279 static void
280 cache_lru_policy_update_item(struct cache_policy_ *policy,
281 	struct cache_policy_item_ *item)
282 {
283 	struct cache_queue_policy_ *queue_policy;
284 	struct cache_queue_policy_item_ *queue_item;
285 
286 	TRACE_IN(cache_lru_policy_update_item);
287 	queue_policy = (struct cache_queue_policy_ *)policy;
288 	queue_item = (struct cache_queue_policy_item_ *)item;
289 
290 	TAILQ_REMOVE(&queue_policy->head, queue_item, entries);
291 	TAILQ_INSERT_TAIL(&queue_policy->head, queue_item, entries);
292 	TRACE_OUT(cache_lru_policy_update_item);
293 }
294 
295 struct cache_policy_ *
296 init_cache_lru_policy()
297 {
298 	struct cache_queue_policy_ *retval;
299 
300 	TRACE_IN(init_cache_lru_policy);
301 	retval = init_cache_queue_policy();
302 	retval->parent_data.update_item_func = cache_lru_policy_update_item;
303 
304 	TRACE_OUT(init_cache_lru_policy);
305 	return ((struct cache_policy_ *)retval);
306 }
307 
308 void
309 destroy_cache_lru_policy(struct cache_policy_ *policy)
310 {
311 	struct cache_queue_policy_	*queue_policy;
312 
313 	TRACE_IN(destroy_cache_lru_policy);
314 	queue_policy = (struct cache_queue_policy_ *)policy;
315 	destroy_cache_queue_policy(queue_policy);
316 	TRACE_OUT(destroy_cache_lru_policy);
317 }
318 
319 /*
320  * LFU (least frequently used) policy implementation differs much from the
321  * LRU and FIFO (both based on cache_queue_policy_). Almost all cache_policy_
322  * functions are implemented specifically for this policy. The idea of this
323  * policy is to represent frequency (real number) as the integer number and
324  * use it as the index in the array. Each array's element is
325  * the list of elements. For example, if we have the 100-elements
326  * array for this policy, the elements with frequency 0.1 (calls per-second)
327  * would be in 10th element of the array.
328  */
329 static struct cache_policy_item_ *
330 cache_lfu_policy_create_item(void)
331 {
332 	struct cache_lfu_policy_item_ *retval;
333 
334 	TRACE_IN(cache_lfu_policy_create_item);
335 	retval = (struct cache_lfu_policy_item_ *)calloc(1,
336 		sizeof(struct cache_lfu_policy_item_));
337 	assert(retval != NULL);
338 
339 	TRACE_OUT(cache_lfu_policy_create_item);
340 	return ((struct cache_policy_item_ *)retval);
341 }
342 
343 static void
344 cache_lfu_policy_destroy_item(struct cache_policy_item_ *item)
345 {
346 
347 	TRACE_IN(cache_lfu_policy_destroy_item);
348 	assert(item != NULL);
349 	free(item);
350 	TRACE_OUT(cache_lfu_policy_destroy_item);
351 }
352 
353 /*
354  * When placed in the LFU policy queue for the first time, the maximum
355  * frequency is assigned to the element
356  */
357 static void
358 cache_lfu_policy_add_item(struct cache_policy_ *policy,
359 	struct cache_policy_item_ *item)
360 {
361 	struct cache_lfu_policy_ *lfu_policy;
362 	struct cache_lfu_policy_item_ *lfu_item;
363 
364 	TRACE_IN(cache_lfu_policy_add_item);
365 	lfu_policy = (struct cache_lfu_policy_ *)policy;
366 	lfu_item = (struct cache_lfu_policy_item_ *)item;
367 
368 	lfu_item->frequency = CACHELIB_MAX_FREQUENCY - 1;
369 	TAILQ_INSERT_HEAD(&(lfu_policy->groups[CACHELIB_MAX_FREQUENCY - 1]),
370 		lfu_item, entries);
371 	TRACE_OUT(cache_lfu_policy_add_item);
372 }
373 
374 /*
375  * On each update the frequency of the element is recalculated and, if it
376  * changed, the element would be moved to the another place in the array.
377  */
378 static void
379 cache_lfu_policy_update_item(struct cache_policy_ *policy,
380 	struct cache_policy_item_ *item)
381 {
382 	struct cache_lfu_policy_ *lfu_policy;
383 	struct cache_lfu_policy_item_ *lfu_item;
384 	int index;
385 
386 	TRACE_IN(cache_lfu_policy_update_item);
387 	lfu_policy = (struct cache_lfu_policy_ *)policy;
388 	lfu_item = (struct cache_lfu_policy_item_ *)item;
389 
390 	/*
391 	 * We calculate the square of the request_count to avoid grouping of
392 	 * all elements at the start of the array (for example, if array size is
393 	 * 100 and most of its elements has frequency below the 0.01, they
394 	 * all would be grouped in the first array's position). Other
395 	 * techniques should be used here later to ensure, that elements are
396 	 * equally distributed  in the array and not grouped in its beginning.
397 	 */
398 	if (lfu_item->parent_data.last_request_time.tv_sec !=
399 		lfu_item->parent_data.creation_time.tv_sec) {
400 		index = ((double)lfu_item->parent_data.request_count *
401 			(double)lfu_item->parent_data.request_count /
402 			(lfu_item->parent_data.last_request_time.tv_sec -
403 			    lfu_item->parent_data.creation_time.tv_sec + 1)) *
404 			    CACHELIB_MAX_FREQUENCY;
405 		if (index >= CACHELIB_MAX_FREQUENCY)
406 			index = CACHELIB_MAX_FREQUENCY - 1;
407 	} else
408 		index = CACHELIB_MAX_FREQUENCY - 1;
409 
410 	TAILQ_REMOVE(&(lfu_policy->groups[lfu_item->frequency]), lfu_item,
411 		entries);
412 	lfu_item->frequency = index;
413 	TAILQ_INSERT_HEAD(&(lfu_policy->groups[index]), lfu_item, entries);
414 
415 	TRACE_OUT(cache_lfu_policy_update_item);
416 }
417 
418 static void
419 cache_lfu_policy_remove_item(struct cache_policy_ *policy,
420 	struct cache_policy_item_ *item)
421 {
422 	struct cache_lfu_policy_ *lfu_policy;
423 	struct cache_lfu_policy_item_ *lfu_item;
424 
425 	TRACE_IN(cache_lfu_policy_remove_item);
426 	lfu_policy = (struct cache_lfu_policy_ *)policy;
427 	lfu_item = (struct cache_lfu_policy_item_ *)item;
428 
429 	TAILQ_REMOVE(&(lfu_policy->groups[lfu_item->frequency]), lfu_item,
430 		entries);
431 	TRACE_OUT(cache_lfu_policy_remove_item);
432 }
433 
434 static struct cache_policy_item_ *
435 cache_lfu_policy_get_first_item(struct cache_policy_ *policy)
436 {
437 	struct cache_lfu_policy_ *lfu_policy;
438 	struct cache_lfu_policy_item_ *lfu_item;
439 	int i;
440 
441 	TRACE_IN(cache_lfu_policy_get_first_item);
442 	lfu_item = NULL;
443 	lfu_policy = (struct cache_lfu_policy_ *)policy;
444 	for (i = 0; i < CACHELIB_MAX_FREQUENCY; ++i)
445 		if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) {
446 			lfu_item = TAILQ_FIRST(&(lfu_policy->groups[i]));
447 			break;
448 		}
449 
450 	TRACE_OUT(cache_lfu_policy_get_first_item);
451 	return ((struct cache_policy_item_ *)lfu_item);
452 }
453 
454 static struct cache_policy_item_ *
455 cache_lfu_policy_get_last_item(struct cache_policy_ *policy)
456 {
457 	struct cache_lfu_policy_ *lfu_policy;
458 	struct cache_lfu_policy_item_ *lfu_item;
459 	int i;
460 
461 	TRACE_IN(cache_lfu_policy_get_last_item);
462 	lfu_item = NULL;
463 	lfu_policy = (struct cache_lfu_policy_ *)policy;
464 	for (i = CACHELIB_MAX_FREQUENCY - 1; i >= 0; --i)
465 		if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) {
466 			lfu_item = TAILQ_LAST(&(lfu_policy->groups[i]),
467 				cache_lfu_policy_group_);
468 			break;
469 		}
470 
471 	TRACE_OUT(cache_lfu_policy_get_last_item);
472 	return ((struct cache_policy_item_ *)lfu_item);
473 }
474 
475 static struct cache_policy_item_ *
476 cache_lfu_policy_get_next_item(struct cache_policy_ *policy,
477 	struct cache_policy_item_ *item)
478 {
479 	struct cache_lfu_policy_ *lfu_policy;
480 	struct cache_lfu_policy_item_ *lfu_item;
481 	int i;
482 
483 	TRACE_IN(cache_lfu_policy_get_next_item);
484 	lfu_policy = (struct cache_lfu_policy_ *)policy;
485 	lfu_item = TAILQ_NEXT((struct cache_lfu_policy_item_ *)item, entries);
486 	if (lfu_item == NULL)
487 	{
488 		for (i = ((struct cache_lfu_policy_item_ *)item)->frequency + 1;
489 			i < CACHELIB_MAX_FREQUENCY; ++i) {
490 			if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) {
491 			    lfu_item = TAILQ_FIRST(&(lfu_policy->groups[i]));
492 			    break;
493 			}
494 		}
495 	}
496 
497 	TRACE_OUT(cache_lfu_policy_get_next_item);
498 	return ((struct cache_policy_item_ *)lfu_item);
499 }
500 
501 static struct cache_policy_item_ *
502 cache_lfu_policy_get_prev_item(struct cache_policy_ *policy,
503 	struct cache_policy_item_ *item)
504 {
505 	struct cache_lfu_policy_ *lfu_policy;
506 	struct cache_lfu_policy_item_ *lfu_item;
507 	int i;
508 
509 	TRACE_IN(cache_lfu_policy_get_prev_item);
510 	lfu_policy = (struct cache_lfu_policy_ *)policy;
511 	lfu_item = TAILQ_PREV((struct cache_lfu_policy_item_ *)item,
512 		cache_lfu_policy_group_, entries);
513 	if (lfu_item == NULL)
514 	{
515 		for (i = ((struct cache_lfu_policy_item_ *)item)->frequency - 1;
516 			i >= 0; --i)
517 			if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) {
518 				lfu_item = TAILQ_LAST(&(lfu_policy->groups[i]),
519 					cache_lfu_policy_group_);
520 				break;
521 		}
522 	}
523 
524 	TRACE_OUT(cache_lfu_policy_get_prev_item);
525 	return ((struct cache_policy_item_ *)lfu_item);
526 }
527 
528 /*
529  * Initializes the cache_policy_ structure by filling it with appropriate
530  * functions pointers
531  */
532 struct cache_policy_ *
533 init_cache_lfu_policy()
534 {
535 	int i;
536 	struct cache_lfu_policy_ *retval;
537 
538 	TRACE_IN(init_cache_lfu_policy);
539 	retval = (struct cache_lfu_policy_ *)calloc(1,
540 		sizeof(struct cache_lfu_policy_));
541 	assert(retval != NULL);
542 
543 	retval->parent_data.create_item_func = cache_lfu_policy_create_item;
544 	retval->parent_data.destroy_item_func = cache_lfu_policy_destroy_item;
545 
546 	retval->parent_data.add_item_func = cache_lfu_policy_add_item;
547 	retval->parent_data.update_item_func = cache_lfu_policy_update_item;
548 	retval->parent_data.remove_item_func = cache_lfu_policy_remove_item;
549 
550 	retval->parent_data.get_first_item_func =
551 		cache_lfu_policy_get_first_item;
552 	retval->parent_data.get_last_item_func =
553 		cache_lfu_policy_get_last_item;
554 	retval->parent_data.get_next_item_func =
555 		cache_lfu_policy_get_next_item;
556 	retval->parent_data.get_prev_item_func =
557 		cache_lfu_policy_get_prev_item;
558 
559 	for (i = 0; i < CACHELIB_MAX_FREQUENCY; ++i)
560 		TAILQ_INIT(&(retval->groups[i]));
561 
562 	TRACE_OUT(init_cache_lfu_policy);
563 	return ((struct cache_policy_ *)retval);
564 }
565 
566 void
567 destroy_cache_lfu_policy(struct cache_policy_ *policy)
568 {
569 	int i;
570 	struct cache_lfu_policy_ *lfu_policy;
571 	struct cache_lfu_policy_item_ *lfu_item;
572 
573 	TRACE_IN(destroy_cache_lfu_policy);
574 	lfu_policy = (struct cache_lfu_policy_ *)policy;
575 	for (i = 0; i < CACHELIB_MAX_FREQUENCY; ++i) {
576 		while (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) {
577 			lfu_item = TAILQ_FIRST(&(lfu_policy->groups[i]));
578 			TAILQ_REMOVE(&(lfu_policy->groups[i]), lfu_item,
579 				entries);
580 			cache_lfu_policy_destroy_item(
581 				(struct cache_policy_item_ *)lfu_item);
582 		}
583 	}
584 	free(policy);
585 	TRACE_OUT(destroy_cache_lfu_policy);
586 }
587