cachelib.c revision 194093
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: head/usr.sbin/nscd/cachelib.c 194093 2009-06-13 00:43:56Z des $");
30
31#include <sys/time.h>
32
33#include <assert.h>
34#include <stdlib.h>
35#include <string.h>
36
37#include "cachelib.h"
38#include "debug.h"
39
40#define INITIAL_ENTRIES_CAPACITY 32
41#define ENTRIES_CAPACITY_STEP 32
42
43#define STRING_SIMPLE_HASH_BODY(in_var, var, a, M)		\
44	for ((var) = 0; *(in_var) != '\0'; ++(in_var))		\
45		(var) = ((a)*(var) + *(in_var)) % (M)
46
47#define STRING_SIMPLE_MP2_HASH_BODY(in_var, var, a, M)		\
48	for ((var) = 0; *(in_var) != 0; ++(in_var))		\
49		(var) = ((a)*(var) + *(in_var)) & (M - 1)
50
51static int cache_elemsize_common_continue_func(struct cache_common_entry_ *,
52	struct cache_policy_item_ *);
53static int cache_lifetime_common_continue_func(struct cache_common_entry_ *,
54	struct cache_policy_item_ *);
55static void clear_cache_entry(struct cache_entry_ *);
56static void destroy_cache_entry(struct cache_entry_ *);
57static void destroy_cache_mp_read_session(struct cache_mp_read_session_ *);
58static void destroy_cache_mp_write_session(struct cache_mp_write_session_ *);
59static int entries_bsearch_cmp_func(const void *, const void *);
60static int entries_qsort_cmp_func(const void *, const void *);
61static struct cache_entry_ ** find_cache_entry_p(struct cache_ *,
62	const char *);
63static void flush_cache_entry(struct cache_entry_ *);
64static void flush_cache_policy(struct cache_common_entry_ *,
65	struct cache_policy_ *, struct cache_policy_ *,
66		int (*)(struct cache_common_entry_ *,
67		struct cache_policy_item_ *));
68static int ht_items_cmp_func(const void *, const void *);
69static int ht_items_fixed_size_left_cmp_func(const void *, const void *);
70static hashtable_index_t ht_item_hash_func(const void *, size_t);
71
72/*
73 * Hashing and comparing routines, that are used with the hash tables
74 */
75static int
76ht_items_cmp_func(const void *p1, const void *p2)
77{
78    	struct cache_ht_item_data_ *hp1, *hp2;
79	size_t min_size;
80	int result;
81
82	hp1 = (struct cache_ht_item_data_ *)p1;
83	hp2 = (struct cache_ht_item_data_ *)p2;
84
85	assert(hp1->key != NULL);
86	assert(hp2->key != NULL);
87
88	if (hp1->key_size != hp2->key_size) {
89		min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size :
90			hp2->key_size;
91		result = memcmp(hp1->key, hp2->key, min_size);
92
93		if (result == 0)
94			return ((hp1->key_size < hp2->key_size) ? -1 : 1);
95		else
96			return (result);
97	} else
98		return (memcmp(hp1->key, hp2->key, hp1->key_size));
99}
100
101static int
102ht_items_fixed_size_left_cmp_func(const void *p1, const void *p2)
103{
104    	struct cache_ht_item_data_ *hp1, *hp2;
105	size_t min_size;
106	int result;
107
108	hp1 = (struct cache_ht_item_data_ *)p1;
109	hp2 = (struct cache_ht_item_data_ *)p2;
110
111	assert(hp1->key != NULL);
112	assert(hp2->key != NULL);
113
114	if (hp1->key_size != hp2->key_size) {
115		min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size :
116			hp2->key_size;
117		result = memcmp(hp1->key, hp2->key, min_size);
118
119		if (result == 0)
120			if (min_size == hp1->key_size)
121			    return (0);
122			else
123			    return ((hp1->key_size < hp2->key_size) ? -1 : 1);
124		else
125			return (result);
126	} else
127		return (memcmp(hp1->key, hp2->key, hp1->key_size));
128}
129
130static hashtable_index_t
131ht_item_hash_func(const void *p, size_t cache_entries_size)
132{
133    	struct cache_ht_item_data_ *hp;
134	size_t i;
135
136	hashtable_index_t retval;
137
138	hp = (struct cache_ht_item_data_ *)p;
139	assert(hp->key != NULL);
140
141	retval = 0;
142	for (i = 0; i < hp->key_size; ++i)
143	    retval = (127 * retval + (unsigned char)hp->key[i]) %
144		cache_entries_size;
145
146	return retval;
147}
148
149HASHTABLE_GENERATE(cache_ht_, cache_ht_item_, struct cache_ht_item_data_, data,
150	ht_item_hash_func, ht_items_cmp_func);
151
152/*
153 * Routines to sort and search the entries by name
154 */
155static int
156entries_bsearch_cmp_func(const void *key, const void *ent)
157{
158
159	assert(key != NULL);
160	assert(ent != NULL);
161
162	return (strcmp((char const *)key,
163		(*(struct cache_entry_ const **)ent)->name));
164}
165
166static int
167entries_qsort_cmp_func(const void *e1, const void *e2)
168{
169
170	assert(e1 != NULL);
171	assert(e2 != NULL);
172
173	return (strcmp((*(struct cache_entry_ const **)e1)->name,
174		(*(struct cache_entry_ const **)e2)->name));
175}
176
177static struct cache_entry_ **
178find_cache_entry_p(struct cache_ *the_cache, const char *entry_name)
179{
180
181	return ((struct cache_entry_ **)(bsearch(entry_name, the_cache->entries,
182		the_cache->entries_size, sizeof(struct cache_entry_ *),
183		entries_bsearch_cmp_func)));
184}
185
186static void
187destroy_cache_mp_write_session(struct cache_mp_write_session_ *ws)
188{
189
190	struct cache_mp_data_item_	*data_item;
191
192	TRACE_IN(destroy_cache_mp_write_session);
193	assert(ws != NULL);
194	while (!TAILQ_EMPTY(&ws->items)) {
195		data_item = TAILQ_FIRST(&ws->items);
196		TAILQ_REMOVE(&ws->items, data_item, entries);
197		free(data_item->value);
198		free(data_item);
199	}
200
201	free(ws);
202	TRACE_OUT(destroy_cache_mp_write_session);
203}
204
205static void
206destroy_cache_mp_read_session(struct cache_mp_read_session_ *rs)
207{
208
209	TRACE_IN(destroy_cache_mp_read_session);
210	assert(rs != NULL);
211	free(rs);
212	TRACE_OUT(destroy_cache_mp_read_session);
213}
214
215static void
216destroy_cache_entry(struct cache_entry_ *entry)
217{
218	struct cache_common_entry_	*common_entry;
219	struct cache_mp_entry_		*mp_entry;
220	struct cache_mp_read_session_	*rs;
221	struct cache_mp_write_session_	*ws;
222	struct cache_ht_item_ *ht_item;
223	struct cache_ht_item_data_ *ht_item_data;
224
225	TRACE_IN(destroy_cache_entry);
226	assert(entry != NULL);
227
228	if (entry->params->entry_type == CET_COMMON) {
229		common_entry = (struct cache_common_entry_ *)entry;
230
231		HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
232			HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data)
233			{
234				free(ht_item_data->key);
235				free(ht_item_data->value);
236			}
237			HASHTABLE_ENTRY_CLEAR(ht_item, data);
238		}
239
240		HASHTABLE_DESTROY(&(common_entry->items), data);
241
242		/* FIFO policy is always first */
243		destroy_cache_fifo_policy(common_entry->policies[0]);
244		switch (common_entry->common_params.policy) {
245		case CPT_LRU:
246			destroy_cache_lru_policy(common_entry->policies[1]);
247			break;
248		case CPT_LFU:
249			destroy_cache_lfu_policy(common_entry->policies[1]);
250			break;
251		default:
252		break;
253		}
254		free(common_entry->policies);
255	} else {
256		mp_entry = (struct cache_mp_entry_ *)entry;
257
258		while (!TAILQ_EMPTY(&mp_entry->ws_head)) {
259			ws = TAILQ_FIRST(&mp_entry->ws_head);
260			TAILQ_REMOVE(&mp_entry->ws_head, ws, entries);
261			destroy_cache_mp_write_session(ws);
262		}
263
264		while (!TAILQ_EMPTY(&mp_entry->rs_head)) {
265			rs = TAILQ_FIRST(&mp_entry->rs_head);
266			TAILQ_REMOVE(&mp_entry->rs_head, rs, entries);
267			destroy_cache_mp_read_session(rs);
268		}
269
270		if (mp_entry->completed_write_session != NULL)
271			destroy_cache_mp_write_session(
272				mp_entry->completed_write_session);
273
274		if (mp_entry->pending_write_session != NULL)
275			destroy_cache_mp_write_session(
276				mp_entry->pending_write_session);
277	}
278
279	free(entry->name);
280	free(entry);
281	TRACE_OUT(destroy_cache_entry);
282}
283
284static void
285clear_cache_entry(struct cache_entry_ *entry)
286{
287	struct cache_mp_entry_		*mp_entry;
288	struct cache_common_entry_	*common_entry;
289	struct cache_ht_item_ *ht_item;
290	struct cache_ht_item_data_ *ht_item_data;
291	struct cache_policy_ *policy;
292	struct cache_policy_item_ *item, *next_item;
293	size_t entry_size;
294	int i;
295
296	if (entry->params->entry_type == CET_COMMON) {
297		common_entry = (struct cache_common_entry_ *)entry;
298
299		entry_size = 0;
300		HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
301			HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data)
302			{
303				free(ht_item_data->key);
304				free(ht_item_data->value);
305			}
306			entry_size += HASHTABLE_ENTRY_SIZE(ht_item, data);
307			HASHTABLE_ENTRY_CLEAR(ht_item, data);
308		}
309
310		common_entry->items_size -= entry_size;
311		for (i = 0; i < common_entry->policies_size; ++i) {
312			policy = common_entry->policies[i];
313
314			next_item = NULL;
315			item = policy->get_first_item_func(policy);
316			while (item != NULL) {
317				next_item = policy->get_next_item_func(policy,
318			    		item);
319				policy->remove_item_func(policy, item);
320				policy->destroy_item_func(item);
321				item = next_item;
322			}
323		}
324	} else {
325		mp_entry = (struct cache_mp_entry_ *)entry;
326
327		if (mp_entry->rs_size == 0) {
328			if (mp_entry->completed_write_session != NULL) {
329				destroy_cache_mp_write_session(
330					mp_entry->completed_write_session);
331				mp_entry->completed_write_session = NULL;
332			}
333
334			memset(&mp_entry->creation_time, 0,
335				sizeof(struct timeval));
336			memset(&mp_entry->last_request_time, 0,
337				sizeof(struct timeval));
338		}
339	}
340}
341
342/*
343 * When passed to the flush_cache_policy, ensures that all old elements are
344 * deleted.
345 */
346static int
347cache_lifetime_common_continue_func(struct cache_common_entry_ *entry,
348	struct cache_policy_item_ *item)
349{
350
351	return ((item->last_request_time.tv_sec - item->creation_time.tv_sec >
352		entry->common_params.max_lifetime.tv_sec) ? 1: 0);
353}
354
355/*
356 * When passed to the flush_cache_policy, ensures that all elements, that
357 * exceed the size limit, are deleted.
358 */
359static int
360cache_elemsize_common_continue_func(struct cache_common_entry_ *entry,
361	struct cache_policy_item_ *item)
362{
363
364	return ((entry->items_size > entry->common_params.satisf_elemsize) ? 1
365    		: 0);
366}
367
368/*
369 * Removes the elements from the cache entry, while the continue_func returns 1.
370 */
371static void
372flush_cache_policy(struct cache_common_entry_ *entry,
373	struct cache_policy_ *policy,
374	struct cache_policy_ *connected_policy,
375	int (*continue_func)(struct cache_common_entry_ *,
376		struct cache_policy_item_ *))
377{
378	struct cache_policy_item_ *item, *next_item, *connected_item;
379	struct cache_ht_item_ *ht_item;
380	struct cache_ht_item_data_ *ht_item_data, ht_key;
381	hashtable_index_t hash;
382
383	assert(policy != NULL);
384
385	next_item = NULL;
386	item = policy->get_first_item_func(policy);
387	while ((item != NULL) && (continue_func(entry, item) == 1)) {
388		next_item = policy->get_next_item_func(policy, item);
389
390		connected_item = item->connected_item;
391		policy->remove_item_func(policy, item);
392
393		memset(&ht_key, 0, sizeof(struct cache_ht_item_data_));
394		ht_key.key = item->key;
395		ht_key.key_size = item->key_size;
396
397		hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &entry->items,
398			&ht_key);
399		assert(hash >= 0);
400		assert(hash < HASHTABLE_ENTRIES_COUNT(&entry->items));
401
402		ht_item = HASHTABLE_GET_ENTRY(&(entry->items), hash);
403		ht_item_data = HASHTABLE_ENTRY_FIND(cache_ht_, ht_item,
404			&ht_key);
405		assert(ht_item_data != NULL);
406		free(ht_item_data->key);
407		free(ht_item_data->value);
408		HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item, ht_item_data);
409		--entry->items_size;
410
411		policy->destroy_item_func(item);
412
413		if (connected_item != NULL) {
414			connected_policy->remove_item_func(connected_policy,
415				connected_item);
416			connected_policy->destroy_item_func(connected_item);
417		}
418
419		item = next_item;
420	}
421}
422
423static void
424flush_cache_entry(struct cache_entry_ *entry)
425{
426	struct cache_mp_entry_		*mp_entry;
427	struct cache_common_entry_	*common_entry;
428	struct cache_policy_ *policy, *connected_policy;
429
430	connected_policy = NULL;
431	if (entry->params->entry_type == CET_COMMON) {
432		common_entry = (struct cache_common_entry_ *)entry;
433		if ((common_entry->common_params.max_lifetime.tv_sec != 0) ||
434		    (common_entry->common_params.max_lifetime.tv_usec != 0)) {
435
436			policy = common_entry->policies[0];
437			if (common_entry->policies_size > 1)
438				connected_policy = common_entry->policies[1];
439
440			flush_cache_policy(common_entry, policy,
441				connected_policy,
442				cache_lifetime_common_continue_func);
443		}
444
445
446		if ((common_entry->common_params.max_elemsize != 0) &&
447			common_entry->items_size >
448			common_entry->common_params.max_elemsize) {
449
450			if (common_entry->policies_size > 1) {
451				policy = common_entry->policies[1];
452				connected_policy = common_entry->policies[0];
453			} else {
454				policy = common_entry->policies[0];
455				connected_policy = NULL;
456			}
457
458			flush_cache_policy(common_entry, policy,
459				connected_policy,
460				cache_elemsize_common_continue_func);
461		}
462	} else {
463		mp_entry = (struct cache_mp_entry_ *)entry;
464
465		if ((mp_entry->mp_params.max_lifetime.tv_sec != 0)
466			|| (mp_entry->mp_params.max_lifetime.tv_usec != 0)) {
467
468			if (mp_entry->last_request_time.tv_sec -
469				mp_entry->last_request_time.tv_sec >
470				mp_entry->mp_params.max_lifetime.tv_sec)
471				clear_cache_entry(entry);
472		}
473	}
474}
475
476struct cache_ *
477init_cache(struct cache_params const *params)
478{
479	struct cache_ *retval;
480
481	TRACE_IN(init_cache);
482	assert(params != NULL);
483
484	retval = (struct cache_ *)calloc(1, sizeof(struct cache_));
485	assert(retval != NULL);
486
487	assert(params != NULL);
488	memcpy(&retval->params, params, sizeof(struct cache_params));
489
490	retval->entries = (struct cache_entry_ **)calloc(1,
491		sizeof(struct cache_entry_ *) * INITIAL_ENTRIES_CAPACITY);
492	assert(retval->entries != NULL);
493
494	retval->entries_capacity = INITIAL_ENTRIES_CAPACITY;
495	retval->entries_size = 0;
496
497	TRACE_OUT(init_cache);
498	return (retval);
499}
500
501void
502destroy_cache(struct cache_ *the_cache)
503{
504
505	TRACE_IN(destroy_cache);
506	assert(the_cache != NULL);
507
508	if (the_cache->entries != NULL) {
509		size_t i;
510		for (i = 0; i < the_cache->entries_size; ++i)
511			destroy_cache_entry(the_cache->entries[i]);
512
513		free(the_cache->entries);
514	}
515
516	free(the_cache);
517	TRACE_OUT(destroy_cache);
518}
519
520int
521register_cache_entry(struct cache_ *the_cache,
522	struct cache_entry_params const *params)
523{
524	int policies_size;
525	size_t entry_name_size;
526	struct cache_common_entry_	*new_common_entry;
527	struct cache_mp_entry_		*new_mp_entry;
528
529	TRACE_IN(register_cache_entry);
530	assert(the_cache != NULL);
531
532	if (find_cache_entry(the_cache, params->entry_name) != NULL) {
533		TRACE_OUT(register_cache_entry);
534		return (-1);
535	}
536
537	if (the_cache->entries_size == the_cache->entries_capacity) {
538		struct cache_entry_ **new_entries;
539		size_t	new_capacity;
540
541		new_capacity = the_cache->entries_capacity +
542			ENTRIES_CAPACITY_STEP;
543		new_entries = (struct cache_entry_ **)calloc(1,
544			sizeof(struct cache_entry_ *) * new_capacity);
545		assert(new_entries != NULL);
546
547		memcpy(new_entries, the_cache->entries,
548			sizeof(struct cache_entry_ *)
549			* the_cache->entries_size);
550
551		free(the_cache->entries);
552		the_cache->entries = new_entries;
553	}
554
555	entry_name_size = strlen(params->entry_name) + 1;
556	switch (params->entry_type)
557	{
558	case CET_COMMON:
559		new_common_entry = (struct cache_common_entry_ *)calloc(1,
560			sizeof(struct cache_common_entry_));
561		assert(new_common_entry != NULL);
562
563		memcpy(&new_common_entry->common_params, params,
564			sizeof(struct common_cache_entry_params));
565		new_common_entry->params =
566		  (struct cache_entry_params *)&new_common_entry->common_params;
567
568		new_common_entry->common_params.entry_name = (char *)calloc(1,
569			entry_name_size);
570		assert(new_common_entry->common_params.entry_name != NULL);
571		strlcpy(new_common_entry->common_params.entry_name,
572			params->entry_name, entry_name_size);
573		new_common_entry->name =
574			new_common_entry->common_params.entry_name;
575
576		HASHTABLE_INIT(&(new_common_entry->items),
577			struct cache_ht_item_data_, data,
578			new_common_entry->common_params.cache_entries_size);
579
580		if (new_common_entry->common_params.policy == CPT_FIFO)
581			policies_size = 1;
582		else
583			policies_size = 2;
584
585		new_common_entry->policies = (struct cache_policy_ **)calloc(1,
586			sizeof(struct cache_policy_ *) * policies_size);
587		assert(new_common_entry->policies != NULL);
588
589		new_common_entry->policies_size = policies_size;
590		new_common_entry->policies[0] = init_cache_fifo_policy();
591
592		if (policies_size > 1) {
593			switch (new_common_entry->common_params.policy) {
594			case CPT_LRU:
595				new_common_entry->policies[1] =
596					init_cache_lru_policy();
597			break;
598			case CPT_LFU:
599				new_common_entry->policies[1] =
600					init_cache_lfu_policy();
601			break;
602			default:
603			break;
604			}
605		}
606
607		new_common_entry->get_time_func =
608			the_cache->params.get_time_func;
609		the_cache->entries[the_cache->entries_size++] =
610			(struct cache_entry_ *)new_common_entry;
611		break;
612	case CET_MULTIPART:
613		new_mp_entry = (struct cache_mp_entry_ *)calloc(1,
614			sizeof(struct cache_mp_entry_));
615		assert(new_mp_entry != NULL);
616
617		memcpy(&new_mp_entry->mp_params, params,
618			sizeof(struct mp_cache_entry_params));
619		new_mp_entry->params =
620			(struct cache_entry_params *)&new_mp_entry->mp_params;
621
622		new_mp_entry->mp_params.entry_name = (char *)calloc(1,
623			entry_name_size);
624		assert(new_mp_entry->mp_params.entry_name != NULL);
625		strlcpy(new_mp_entry->mp_params.entry_name, params->entry_name,
626			entry_name_size);
627		new_mp_entry->name = new_mp_entry->mp_params.entry_name;
628
629		TAILQ_INIT(&new_mp_entry->ws_head);
630		TAILQ_INIT(&new_mp_entry->rs_head);
631
632		new_mp_entry->get_time_func = the_cache->params.get_time_func;
633		the_cache->entries[the_cache->entries_size++] =
634			(struct cache_entry_ *)new_mp_entry;
635		break;
636	}
637
638
639	qsort(the_cache->entries, the_cache->entries_size,
640		sizeof(struct cache_entry_ *), entries_qsort_cmp_func);
641
642	TRACE_OUT(register_cache_entry);
643	return (0);
644}
645
646int
647unregister_cache_entry(struct cache_ *the_cache, const char *entry_name)
648{
649	struct cache_entry_ **del_ent;
650
651	TRACE_IN(unregister_cache_entry);
652	assert(the_cache != NULL);
653
654	del_ent = find_cache_entry_p(the_cache, entry_name);
655	if (del_ent != NULL) {
656		destroy_cache_entry(*del_ent);
657		--the_cache->entries_size;
658
659		memmove(del_ent, del_ent + 1,
660			(&(the_cache->entries[--the_cache->entries_size]) -
661	    		del_ent) * sizeof(struct cache_entry_ *));
662
663		TRACE_OUT(unregister_cache_entry);
664		return (0);
665	} else {
666		TRACE_OUT(unregister_cache_entry);
667		return (-1);
668	}
669}
670
671struct cache_entry_ *
672find_cache_entry(struct cache_ *the_cache, const char *entry_name)
673{
674	struct cache_entry_ **result;
675
676	TRACE_IN(find_cache_entry);
677	result = find_cache_entry_p(the_cache, entry_name);
678
679	if (result == NULL) {
680		TRACE_OUT(find_cache_entry);
681		return (NULL);
682	} else {
683		TRACE_OUT(find_cache_entry);
684		return (*result);
685	}
686}
687
688/*
689 * Tries to read the element with the specified key from the cache. If the
690 * value_size is too small, it will be filled with the proper number, and
691 * the user will need to call cache_read again with the value buffer, that
692 * is large enough.
693 * Function returns 0 on success, -1 on error, and -2 if the value_size is too
694 * small.
695 */
696int
697cache_read(struct cache_entry_ *entry, const char *key, size_t key_size,
698	char *value, size_t *value_size)
699{
700	struct cache_common_entry_	*common_entry;
701	struct cache_ht_item_data_	item_data, *find_res;
702	struct cache_ht_item_		*item;
703	hashtable_index_t	hash;
704	struct cache_policy_item_ *connected_item;
705
706	TRACE_IN(cache_read);
707	assert(entry != NULL);
708	assert(key != NULL);
709	assert(value_size != NULL);
710	assert(entry->params->entry_type == CET_COMMON);
711
712	common_entry = (struct cache_common_entry_ *)entry;
713
714	memset(&item_data, 0, sizeof(struct cache_ht_item_data_));
715	/* can't avoid the cast here */
716	item_data.key = (char *)key;
717	item_data.key_size = key_size;
718
719	hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items,
720		&item_data);
721	assert(hash >= 0);
722	assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items));
723
724	item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash);
725	find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data);
726	if (find_res == NULL) {
727		TRACE_OUT(cache_read);
728		return (-1);
729	}
730
731	if ((common_entry->common_params.max_lifetime.tv_sec != 0) ||
732		(common_entry->common_params.max_lifetime.tv_usec != 0)) {
733
734		if (find_res->fifo_policy_item->last_request_time.tv_sec -
735			find_res->fifo_policy_item->creation_time.tv_sec >
736			common_entry->common_params.max_lifetime.tv_sec) {
737
738			free(find_res->key);
739			free(find_res->value);
740
741			connected_item =
742			    find_res->fifo_policy_item->connected_item;
743			if (connected_item != NULL) {
744				common_entry->policies[1]->remove_item_func(
745					common_entry->policies[1],
746			    		connected_item);
747				common_entry->policies[1]->destroy_item_func(
748					connected_item);
749			}
750
751			common_entry->policies[0]->remove_item_func(
752				common_entry->policies[0],
753					find_res->fifo_policy_item);
754			common_entry->policies[0]->destroy_item_func(
755				find_res->fifo_policy_item);
756
757			HASHTABLE_ENTRY_REMOVE(cache_ht_, item, find_res);
758			--common_entry->items_size;
759		}
760	}
761
762	if ((*value_size < find_res->value_size) || (value == NULL)) {
763		*value_size = find_res->value_size;
764		TRACE_OUT(cache_read);
765		return (-2);
766	}
767
768	*value_size = find_res->value_size;
769	memcpy(value, find_res->value, find_res->value_size);
770
771	++find_res->fifo_policy_item->request_count;
772	common_entry->get_time_func(
773		&find_res->fifo_policy_item->last_request_time);
774	common_entry->policies[0]->update_item_func(common_entry->policies[0],
775		find_res->fifo_policy_item);
776
777	if (find_res->fifo_policy_item->connected_item != NULL) {
778		connected_item = find_res->fifo_policy_item->connected_item;
779		memcpy(&connected_item->last_request_time,
780			&find_res->fifo_policy_item->last_request_time,
781			sizeof(struct timeval));
782		connected_item->request_count =
783			find_res->fifo_policy_item->request_count;
784
785		common_entry->policies[1]->update_item_func(
786			common_entry->policies[1], connected_item);
787	}
788
789	TRACE_OUT(cache_read);
790	return (0);
791}
792
793/*
794 * Writes the value with the specified key into the cache entry.
795 * Functions returns 0 on success, and -1 on error.
796 */
797int
798cache_write(struct cache_entry_ *entry, const char *key, size_t key_size,
799    	char const *value, size_t value_size)
800{
801	struct cache_common_entry_	*common_entry;
802	struct cache_ht_item_data_	item_data, *find_res;
803	struct cache_ht_item_		*item;
804	hashtable_index_t	hash;
805
806	struct cache_policy_		*policy, *connected_policy;
807	struct cache_policy_item_	*policy_item;
808	struct cache_policy_item_	*connected_policy_item;
809
810	TRACE_IN(cache_write);
811	assert(entry != NULL);
812	assert(key != NULL);
813	assert(value != NULL);
814	assert(entry->params->entry_type == CET_COMMON);
815
816	common_entry = (struct cache_common_entry_ *)entry;
817
818	memset(&item_data, 0, sizeof(struct cache_ht_item_data_));
819	/* can't avoid the cast here */
820	item_data.key = (char *)key;
821	item_data.key_size = key_size;
822
823	hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items,
824		&item_data);
825	assert(hash >= 0);
826	assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items));
827
828	item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash);
829	find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data);
830	if (find_res != NULL) {
831		TRACE_OUT(cache_write);
832		return (-1);
833	}
834
835	item_data.key = (char *)malloc(key_size);
836	memcpy(item_data.key, key, key_size);
837
838	item_data.value = (char *)malloc(value_size);
839	assert(item_data.value != NULL);
840
841	memcpy(item_data.value, value, value_size);
842	item_data.value_size = value_size;
843
844	policy_item = common_entry->policies[0]->create_item_func();
845	policy_item->key = item_data.key;
846	policy_item->key_size = item_data.key_size;
847	common_entry->get_time_func(&policy_item->creation_time);
848
849	if (common_entry->policies_size > 1) {
850		connected_policy_item =
851			common_entry->policies[1]->create_item_func();
852		memcpy(&connected_policy_item->creation_time,
853			&policy_item->creation_time,
854			sizeof(struct timeval));
855		connected_policy_item->key = policy_item->key;
856		connected_policy_item->key_size = policy_item->key_size;
857
858		connected_policy_item->connected_item = policy_item;
859		policy_item->connected_item = connected_policy_item;
860	}
861
862	item_data.fifo_policy_item = policy_item;
863
864	common_entry->policies[0]->add_item_func(common_entry->policies[0],
865		policy_item);
866	if (common_entry->policies_size > 1)
867		common_entry->policies[1]->add_item_func(
868			common_entry->policies[1], connected_policy_item);
869
870	HASHTABLE_ENTRY_STORE(cache_ht_, item, &item_data);
871	++common_entry->items_size;
872
873	if ((common_entry->common_params.max_elemsize != 0) &&
874		(common_entry->items_size >
875		common_entry->common_params.max_elemsize)) {
876		if (common_entry->policies_size > 1) {
877			policy = common_entry->policies[1];
878			connected_policy = common_entry->policies[0];
879		} else {
880			policy = common_entry->policies[0];
881			connected_policy = NULL;
882		}
883
884		flush_cache_policy(common_entry, policy, connected_policy,
885			cache_elemsize_common_continue_func);
886	}
887
888	TRACE_OUT(cache_write);
889	return (0);
890}
891
892/*
893 * Initializes the write session for the specified multipart entry. This
894 * session then should be filled with data either committed or abandoned by
895 * using close_cache_mp_write_session or abandon_cache_mp_write_session
896 * respectively.
897 * Returns NULL on errors (when there are too many opened write sessions for
898 * the entry).
899 */
900struct cache_mp_write_session_ *
901open_cache_mp_write_session(struct cache_entry_ *entry)
902{
903	struct cache_mp_entry_	*mp_entry;
904	struct cache_mp_write_session_	*retval;
905
906	TRACE_IN(open_cache_mp_write_session);
907	assert(entry != NULL);
908	assert(entry->params->entry_type == CET_MULTIPART);
909	mp_entry = (struct cache_mp_entry_ *)entry;
910
911	if ((mp_entry->mp_params.max_sessions > 0) &&
912		(mp_entry->ws_size == mp_entry->mp_params.max_sessions)) {
913		TRACE_OUT(open_cache_mp_write_session);
914		return (NULL);
915	}
916
917	retval = (struct cache_mp_write_session_ *)calloc(1,
918		sizeof(struct cache_mp_write_session_));
919	assert(retval != NULL);
920
921	TAILQ_INIT(&retval->items);
922	retval->parent_entry = mp_entry;
923
924	TAILQ_INSERT_HEAD(&mp_entry->ws_head, retval, entries);
925	++mp_entry->ws_size;
926
927	TRACE_OUT(open_cache_mp_write_session);
928	return (retval);
929}
930
931/*
932 * Writes data to the specified session. Return 0 on success and -1 on errors
933 * (when write session size limit is exceeded).
934 */
935int
936cache_mp_write(struct cache_mp_write_session_ *ws, char *data,
937	size_t data_size)
938{
939	struct cache_mp_data_item_	*new_item;
940
941	TRACE_IN(cache_mp_write);
942	assert(ws != NULL);
943	assert(ws->parent_entry != NULL);
944	assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
945
946	if ((ws->parent_entry->mp_params.max_elemsize > 0) &&
947		(ws->parent_entry->mp_params.max_elemsize == ws->items_size)) {
948		TRACE_OUT(cache_mp_write);
949		return (-1);
950	}
951
952	new_item = (struct cache_mp_data_item_ *)calloc(1,
953		sizeof(struct cache_mp_data_item_));
954	assert(new_item != NULL);
955
956	new_item->value = (char *)malloc(data_size);
957	assert(new_item->value != NULL);
958	memcpy(new_item->value, data, data_size);
959	new_item->value_size = data_size;
960
961	TAILQ_INSERT_TAIL(&ws->items, new_item, entries);
962	++ws->items_size;
963
964	TRACE_OUT(cache_mp_write);
965	return (0);
966}
967
968/*
969 * Abandons the write session and frees all the connected resources.
970 */
971void
972abandon_cache_mp_write_session(struct cache_mp_write_session_ *ws)
973{
974
975	TRACE_IN(abandon_cache_mp_write_session);
976	assert(ws != NULL);
977	assert(ws->parent_entry != NULL);
978	assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
979
980	TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries);
981	--ws->parent_entry->ws_size;
982
983	destroy_cache_mp_write_session(ws);
984	TRACE_OUT(abandon_cache_mp_write_session);
985}
986
987/*
988 * Commits the session to the entry, for which it was created.
989 */
990void
991close_cache_mp_write_session(struct cache_mp_write_session_ *ws)
992{
993
994	TRACE_IN(close_cache_mp_write_session);
995	assert(ws != NULL);
996	assert(ws->parent_entry != NULL);
997	assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
998
999	TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries);
1000	--ws->parent_entry->ws_size;
1001
1002	if (ws->parent_entry->completed_write_session == NULL) {
1003		/*
1004		 * If there is no completed session yet, this will be the one
1005		 */
1006		ws->parent_entry->get_time_func(
1007	    		&ws->parent_entry->creation_time);
1008		ws->parent_entry->completed_write_session = ws;
1009	} else {
1010		/*
1011		 * If there is a completed session, then we'll save our session
1012		 * as a pending session. If there is already a pending session,
1013		 * it would be destroyed.
1014		 */
1015		if (ws->parent_entry->pending_write_session != NULL)
1016			destroy_cache_mp_write_session(
1017				ws->parent_entry->pending_write_session);
1018
1019		ws->parent_entry->pending_write_session = ws;
1020	}
1021	TRACE_OUT(close_cache_mp_write_session);
1022}
1023
1024/*
1025 * Opens read session for the specified entry. Returns NULL on errors (when
1026 * there are no data in the entry, or the data are obsolete).
1027 */
1028struct cache_mp_read_session_ *
1029open_cache_mp_read_session(struct cache_entry_ *entry)
1030{
1031	struct cache_mp_entry_			*mp_entry;
1032	struct cache_mp_read_session_	*retval;
1033
1034	TRACE_IN(open_cache_mp_read_session);
1035	assert(entry != NULL);
1036	assert(entry->params->entry_type == CET_MULTIPART);
1037	mp_entry = (struct cache_mp_entry_ *)entry;
1038
1039	if (mp_entry->completed_write_session == NULL) {
1040		TRACE_OUT(open_cache_mp_read_session);
1041		return (NULL);
1042	}
1043
1044	if ((mp_entry->mp_params.max_lifetime.tv_sec != 0)
1045		|| (mp_entry->mp_params.max_lifetime.tv_usec != 0)) {
1046		if (mp_entry->last_request_time.tv_sec -
1047			mp_entry->last_request_time.tv_sec >
1048			mp_entry->mp_params.max_lifetime.tv_sec) {
1049			flush_cache_entry(entry);
1050			TRACE_OUT(open_cache_mp_read_session);
1051			return (NULL);
1052		}
1053	}
1054
1055	retval = (struct cache_mp_read_session_ *)calloc(1,
1056		sizeof(struct cache_mp_read_session_));
1057	assert(retval != NULL);
1058
1059	retval->parent_entry = mp_entry;
1060	retval->current_item = TAILQ_FIRST(
1061		&mp_entry->completed_write_session->items);
1062
1063	TAILQ_INSERT_HEAD(&mp_entry->rs_head, retval, entries);
1064	++mp_entry->rs_size;
1065
1066	mp_entry->get_time_func(&mp_entry->last_request_time);
1067	TRACE_OUT(open_cache_mp_read_session);
1068	return (retval);
1069}
1070
1071/*
1072 * Reads the data from the read session - step by step.
1073 * Returns 0 on success, -1 on error (when there are no more data), and -2 if
1074 * the data_size is too small.  In the last case, data_size would be filled
1075 * the proper value.
1076 */
1077int
1078cache_mp_read(struct cache_mp_read_session_ *rs, char *data, size_t *data_size)
1079{
1080
1081	TRACE_IN(cache_mp_read);
1082	assert(rs != NULL);
1083
1084	if (rs->current_item == NULL) {
1085		TRACE_OUT(cache_mp_read);
1086		return (-1);
1087	}
1088
1089	if (rs->current_item->value_size > *data_size) {
1090		*data_size = rs->current_item->value_size;
1091		if (data == NULL) {
1092			TRACE_OUT(cache_mp_read);
1093			return (0);
1094		}
1095
1096		TRACE_OUT(cache_mp_read);
1097		return (-2);
1098	}
1099
1100	*data_size = rs->current_item->value_size;
1101	memcpy(data, rs->current_item->value, rs->current_item->value_size);
1102	rs->current_item = TAILQ_NEXT(rs->current_item, entries);
1103
1104	TRACE_OUT(cache_mp_read);
1105	return (0);
1106}
1107
1108/*
1109 * Closes the read session. If there are no more read sessions and there is
1110 * a pending write session, it will be committed and old
1111 * completed_write_session will be destroyed.
1112 */
1113void
1114close_cache_mp_read_session(struct cache_mp_read_session_ *rs)
1115{
1116
1117	TRACE_IN(close_cache_mp_read_session);
1118	assert(rs != NULL);
1119	assert(rs->parent_entry != NULL);
1120
1121	TAILQ_REMOVE(&rs->parent_entry->rs_head, rs, entries);
1122	--rs->parent_entry->rs_size;
1123
1124	if ((rs->parent_entry->rs_size == 0) &&
1125		(rs->parent_entry->pending_write_session != NULL)) {
1126		destroy_cache_mp_write_session(
1127			rs->parent_entry->completed_write_session);
1128		rs->parent_entry->completed_write_session =
1129			rs->parent_entry->pending_write_session;
1130		rs->parent_entry->pending_write_session = NULL;
1131	}
1132
1133	destroy_cache_mp_read_session(rs);
1134	TRACE_OUT(close_cache_mp_read_session);
1135}
1136
1137int
1138transform_cache_entry(struct cache_entry_ *entry,
1139	enum cache_transformation_t transformation)
1140{
1141
1142	TRACE_IN(transform_cache_entry);
1143	switch (transformation) {
1144	case CTT_CLEAR:
1145		clear_cache_entry(entry);
1146		TRACE_OUT(transform_cache_entry);
1147		return (0);
1148	case CTT_FLUSH:
1149		flush_cache_entry(entry);
1150		TRACE_OUT(transform_cache_entry);
1151		return (0);
1152	default:
1153		TRACE_OUT(transform_cache_entry);
1154		return (-1);
1155	}
1156}
1157
1158int
1159transform_cache_entry_part(struct cache_entry_ *entry,
1160	enum cache_transformation_t transformation, const char *key_part,
1161	size_t key_part_size, enum part_position_t part_position)
1162{
1163	struct cache_common_entry_ *common_entry;
1164	struct cache_ht_item_ *ht_item;
1165	struct cache_ht_item_data_ *ht_item_data, ht_key;
1166
1167	struct cache_policy_item_ *item, *connected_item;
1168
1169	TRACE_IN(transform_cache_entry_part);
1170	if (entry->params->entry_type != CET_COMMON) {
1171		TRACE_OUT(transform_cache_entry_part);
1172		return (-1);
1173	}
1174
1175	if (transformation != CTT_CLEAR) {
1176		TRACE_OUT(transform_cache_entry_part);
1177		return (-1);
1178	}
1179
1180	memset(&ht_key, 0, sizeof(struct cache_ht_item_data_));
1181	ht_key.key = (char *)key_part;	/* can't avoid casting here */
1182	ht_key.key_size = key_part_size;
1183
1184	common_entry = (struct cache_common_entry_ *)entry;
1185	HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
1186		do {
1187			ht_item_data = HASHTABLE_ENTRY_FIND_SPECIAL(cache_ht_,
1188				ht_item, &ht_key,
1189				ht_items_fixed_size_left_cmp_func);
1190
1191			if (ht_item_data != NULL) {
1192			    item = ht_item_data->fifo_policy_item;
1193			    connected_item = item->connected_item;
1194
1195			    common_entry->policies[0]->remove_item_func(
1196				common_entry->policies[0],
1197				item);
1198
1199			    free(ht_item_data->key);
1200			    free(ht_item_data->value);
1201			    HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item,
1202				ht_item_data);
1203			    --common_entry->items_size;
1204
1205			    common_entry->policies[0]->destroy_item_func(
1206				item);
1207			    if (common_entry->policies_size == 2) {
1208				common_entry->policies[1]->remove_item_func(
1209				    common_entry->policies[1],
1210				    connected_item);
1211				common_entry->policies[1]->destroy_item_func(
1212				    connected_item);
1213			    }
1214			}
1215		} while (ht_item_data != NULL);
1216	}
1217
1218	TRACE_OUT(transform_cache_entry_part);
1219	return (0);
1220}
1221