1 /*	$OpenBSD: rf_memchunk.c,v 1.4 2002/12/16 07:01:04 tdeval Exp $	*/
2 /*	$NetBSD: rf_memchunk.c,v 1.4 1999/08/13 03:41:56 oster Exp $	*/
3 /*
4  * Copyright (c) 1995 Carnegie-Mellon University.
5  * All rights reserved.
6  *
7  * Author: Mark Holland
8  *
9  * Permission to use, copy, modify and distribute this software and
10  * its documentation is hereby granted, provided that both the copyright
11  * notice and this permission notice appear in all copies of the
12  * software, derivative works or modified versions, and any portions
13  * thereof, and that both notices appear in supporting documentation.
14  *
15  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
16  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
17  * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
18  *
19  * Carnegie Mellon requests users of this software to return to
20  *
21  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
22  *  School of Computer Science
23  *  Carnegie Mellon University
24  *  Pittsburgh PA 15213-3890
25  *
26  * any improvements or extensions that they make and grant Carnegie the
27  * rights to redistribute these changes.
28  */
29 
30 /*********************************************************************************
31  * rf_memchunk.c
32  *
33  * experimental code.  I've found that the malloc and free calls in the DAG
34  * creation code are very expensive.  Since for any given workload the DAGs
35  * created for different accesses are likely to be similar to each other, the
36  * amount of memory used for any given DAG data structure is likely to be one
37  * of a small number of values.  For example, in UNIX, all reads and writes will
38  * be less than 8k and will not span stripe unit boundaries.  Thus in the absence
39  * of failure, the only DAGs that will ever get created are single-node reads
40  * and single-stripe-unit atomic read-modify-writes.  So, I'm very likely to
41  * be continually asking for chunks of memory equal to the sizes of these two
42  * DAGs.
43  *
44  * This leads to the idea of holding on to these chunks of memory when the DAG is
45  * freed and then, when a new DAG is created, trying to find such a chunk before
46  * calling malloc.
47  *
48  * the "chunk list" is a list of lists.  Each header node contains a size value
49  * and a pointer to a list of chunk descriptors, each of which holds a pointer
50  * to a chunk of memory of the indicated size.
51  *
52  * There is currently no way to purge memory out of the chunk list.  My
53  * initial thought on this is to have a low-priority thread that wakes up every
54  * 1 or 2 seconds, purges all the chunks with low reuse counts, and sets all
55  * the reuse counts to zero.
56  *
57  * This whole idea may be bad, since malloc may be able to do this more efficiently.
58  * It's worth a try, though, and it can be turned off by setting useMemChunks to 0.
59  *
60  ********************************************************************************/
61 
62 #include "rf_types.h"
63 #include "rf_threadstuff.h"
64 #include "rf_debugMem.h"
65 #include "rf_memchunk.h"
66 #include "rf_general.h"
67 #include "rf_options.h"
68 #include "rf_shutdown.h"
69 
70 typedef struct RF_ChunkHdr_s RF_ChunkHdr_t;
71 struct RF_ChunkHdr_s {
72 	int     size;
73 	RF_ChunkDesc_t *list;
74 	RF_ChunkHdr_t *next;
75 };
76 
77 static RF_ChunkHdr_t *chunklist, *chunk_hdr_free_list;
78 static RF_ChunkDesc_t *chunk_desc_free_list;
79 RF_DECLARE_STATIC_MUTEX(chunkmutex);
80 void rf_ShutdownMemChunk(void *);
81 RF_ChunkDesc_t *rf_NewMemChunk(int, char *);
82 
83 
rf_ShutdownMemChunk(ignored)84 void rf_ShutdownMemChunk(ignored)
85 	void   *ignored;
86 {
87 	RF_ChunkDesc_t *pt, *p;
88 	RF_ChunkHdr_t *hdr, *ht;
89 
90 	if (rf_memChunkDebug)
91 		printf("Chunklist:\n");
92 	for (hdr = chunklist; hdr;) {
93 		for (p = hdr->list; p;) {
94 			if (rf_memChunkDebug)
95 				printf("Size %d reuse count %d\n", p->size, p->reuse_count);
96 			pt = p;
97 			p = p->next;
98 			RF_Free(pt->buf, pt->size);
99 			RF_Free(pt, sizeof(*pt));
100 		}
101 		ht = hdr;
102 		hdr = hdr->next;
103 		RF_Free(ht, sizeof(*ht));
104 	}
105 
106 	rf_mutex_destroy(&chunkmutex);
107 }
108 
109 int
rf_ConfigureMemChunk(listp)110 rf_ConfigureMemChunk(listp)
111 	RF_ShutdownList_t **listp;
112 {
113 	int     rc;
114 
115 	chunklist = NULL;
116 	chunk_hdr_free_list = NULL;
117 	chunk_desc_free_list = NULL;
118 	rc = rf_mutex_init(&chunkmutex);
119 	if (rc) {
120 		RF_ERRORMSG3("Unable to init mutex file %s line %d rc=%d\n", __FILE__,
121 		    __LINE__, rc);
122 	}
123 	rc = rf_ShutdownCreate(listp, rf_ShutdownMemChunk, NULL);
124 	if (rc) {
125 		RF_ERRORMSG3("Unable to add to shutdown list file %s line %d rc=%d\n", __FILE__,
126 		    __LINE__, rc);
127 		rf_mutex_destroy(&chunkmutex);
128 	}
129 	return (rc);
130 }
131 /* called to get a chunk descriptor for a newly-allocated chunk of memory
132  * MUTEX MUST BE LOCKED
133  *
134  * free list is not currently used
135  */
136 RF_ChunkDesc_t *
rf_NewMemChunk(size,buf)137 rf_NewMemChunk(size, buf)
138 	int     size;
139 	char   *buf;
140 {
141 	RF_ChunkDesc_t *p;
142 
143 	if (chunk_desc_free_list) {
144 		p = chunk_desc_free_list;
145 		chunk_desc_free_list = p->next;
146 	} else
147 		RF_Malloc(p, sizeof(RF_ChunkDesc_t), (RF_ChunkDesc_t *));
148 	p->size = size;
149 	p->buf = buf;
150 	p->next = NULL;
151 	p->reuse_count = 0;
152 	return (p);
153 }
154 /* looks for a chunk of memory of acceptable size.  If none, allocates one and returns
155  * a chunk descriptor for it, but does not install anything in the list.  This is done
156  * when the chunk is released.
157  */
158 RF_ChunkDesc_t *
rf_GetMemChunk(size)159 rf_GetMemChunk(size)
160 	int     size;
161 {
162 	RF_ChunkHdr_t *hdr = chunklist;
163 	RF_ChunkDesc_t *p = NULL;
164 	char   *buf;
165 
166 	RF_LOCK_MUTEX(chunkmutex);
167 	for (hdr = chunklist; hdr; hdr = hdr->next)
168 		if (hdr->size >= size) {
169 			p = hdr->list;
170 			if (p) {
171 				hdr->list = p->next;
172 				p->next = NULL;
173 				p->reuse_count++;
174 			}
175 			break;
176 		}
177 	if (!p) {
178 		RF_Malloc(buf, size, (char *));
179 		p = rf_NewMemChunk(size, buf);
180 	}
181 	RF_UNLOCK_MUTEX(chunkmutex);
182 	(void) bzero(p->buf, size);
183 	return (p);
184 }
185 
186 void
rf_ReleaseMemChunk(chunk)187 rf_ReleaseMemChunk(chunk)
188 	RF_ChunkDesc_t *chunk;
189 {
190 	RF_ChunkHdr_t *hdr, *ht = NULL, *new;
191 
192 	RF_LOCK_MUTEX(chunkmutex);
193 	for (hdr = chunklist; hdr && hdr->size < chunk->size; ht = hdr, hdr = hdr->next);
194 	if (hdr && hdr->size == chunk->size) {
195 		chunk->next = hdr->list;
196 		hdr->list = chunk;
197 	} else {
198 		RF_Malloc(new, sizeof(RF_ChunkHdr_t), (RF_ChunkHdr_t *));
199 		new->size = chunk->size;
200 		new->list = chunk;
201 		chunk->next = NULL;
202 		if (ht) {
203 			new->next = ht->next;
204 			ht->next = new;
205 		} else {
206 			new->next = hdr;
207 			chunklist = new;
208 		}
209 	}
210 	RF_UNLOCK_MUTEX(chunkmutex);
211 }
212