1 /*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation;
5 * version 2.1 of the License.
6 *
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; If not, see <http://www.gnu.org/licenses/>.
14 */
15
16 #include <errno.h>
17 #include <string.h>
18 #include <pthread.h>
19
20 #include "private.h"
21
22 #define DBGPRINTF(_m...) \
23 xtl_log(xcall->logger, XTL_DEBUG, -1, "xencall:buffer", _m)
24
25 #define ROUNDUP(_x,_w) (((unsigned long)(_x)+(1UL<<(_w))-1) & ~((1UL<<(_w))-1))
26
27 pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
28
cache_lock(xencall_handle * xcall)29 static void cache_lock(xencall_handle *xcall)
30 {
31 int saved_errno = errno;
32 if ( xcall->flags & XENCALL_OPENFLAG_NON_REENTRANT )
33 return;
34 pthread_mutex_lock(&cache_mutex);
35 /* Ignore pthread errors. */
36 errno = saved_errno;
37 }
38
cache_unlock(xencall_handle * xcall)39 static void cache_unlock(xencall_handle *xcall)
40 {
41 int saved_errno = errno;
42 if ( xcall->flags & XENCALL_OPENFLAG_NON_REENTRANT )
43 return;
44 pthread_mutex_unlock(&cache_mutex);
45 /* Ignore pthread errors. */
46 errno = saved_errno;
47 }
48
cache_alloc(xencall_handle * xcall,size_t nr_pages)49 static void *cache_alloc(xencall_handle *xcall, size_t nr_pages)
50 {
51 void *p = NULL;
52
53 cache_lock(xcall);
54
55 xcall->buffer_total_allocations++;
56 xcall->buffer_current_allocations++;
57 if ( xcall->buffer_current_allocations > xcall->buffer_maximum_allocations )
58 xcall->buffer_maximum_allocations = xcall->buffer_current_allocations;
59
60 if ( nr_pages > 1 )
61 {
62 xcall->buffer_cache_toobig++;
63 }
64 else if ( xcall->buffer_cache_nr > 0 )
65 {
66 p = xcall->buffer_cache[--xcall->buffer_cache_nr];
67 xcall->buffer_cache_hits++;
68 }
69 else
70 {
71 xcall->buffer_cache_misses++;
72 }
73
74 cache_unlock(xcall);
75
76 return p;
77 }
78
cache_free(xencall_handle * xcall,void * p,size_t nr_pages)79 static int cache_free(xencall_handle *xcall, void *p, size_t nr_pages)
80 {
81 int rc = 0;
82
83 cache_lock(xcall);
84
85 xcall->buffer_total_releases++;
86 xcall->buffer_current_allocations--;
87
88 if ( nr_pages == 1 &&
89 xcall->buffer_cache_nr < BUFFER_CACHE_SIZE )
90 {
91 xcall->buffer_cache[xcall->buffer_cache_nr++] = p;
92 rc = 1;
93 }
94
95 cache_unlock(xcall);
96
97 return rc;
98 }
99
buffer_release_cache(xencall_handle * xcall)100 void buffer_release_cache(xencall_handle *xcall)
101 {
102 void *p;
103
104 cache_lock(xcall);
105
106 DBGPRINTF("total allocations:%d total releases:%d",
107 xcall->buffer_total_allocations,
108 xcall->buffer_total_releases);
109 DBGPRINTF("current allocations:%d maximum allocations:%d",
110 xcall->buffer_current_allocations,
111 xcall->buffer_maximum_allocations);
112 DBGPRINTF("cache current size:%d",
113 xcall->buffer_cache_nr);
114 DBGPRINTF("cache hits:%d misses:%d toobig:%d",
115 xcall->buffer_cache_hits,
116 xcall->buffer_cache_misses,
117 xcall->buffer_cache_toobig);
118
119 while ( xcall->buffer_cache_nr > 0 )
120 {
121 p = xcall->buffer_cache[--xcall->buffer_cache_nr];
122 osdep_free_pages(xcall, p, 1);
123 }
124
125 cache_unlock(xcall);
126 }
127
xencall_alloc_buffer_pages(xencall_handle * xcall,size_t nr_pages)128 void *xencall_alloc_buffer_pages(xencall_handle *xcall, size_t nr_pages)
129 {
130 void *p = cache_alloc(xcall, nr_pages);
131
132 if ( !p )
133 p = osdep_alloc_pages(xcall, nr_pages);
134
135 if (!p)
136 return NULL;
137
138 memset(p, 0, nr_pages * PAGE_SIZE);
139
140 return p;
141 }
142
xencall_free_buffer_pages(xencall_handle * xcall,void * p,size_t nr_pages)143 void xencall_free_buffer_pages(xencall_handle *xcall, void *p, size_t nr_pages)
144 {
145 if ( p == NULL )
146 return;
147
148 if ( !cache_free(xcall, p, nr_pages) )
149 osdep_free_pages(xcall, p, nr_pages);
150 }
151
152 struct allocation_header {
153 int nr_pages;
154 int pad[3];
155 };
156
xencall_alloc_buffer(xencall_handle * xcall,size_t size)157 void *xencall_alloc_buffer(xencall_handle *xcall, size_t size)
158 {
159 size_t actual_size = ROUNDUP(size + sizeof(struct allocation_header), PAGE_SHIFT);
160 int nr_pages = actual_size >> PAGE_SHIFT;
161 struct allocation_header *hdr;
162
163 hdr = xencall_alloc_buffer_pages(xcall, nr_pages);
164 if ( hdr == NULL )
165 return NULL;
166
167 hdr->nr_pages = nr_pages;
168
169 return (void *)(hdr+1);
170 }
171
xencall_free_buffer(xencall_handle * xcall,void * p)172 void xencall_free_buffer(xencall_handle *xcall, void *p)
173 {
174 struct allocation_header *hdr;
175
176 if (p == NULL)
177 return;
178
179 hdr = p;
180 --hdr;
181
182 xencall_free_buffer_pages(xcall, hdr, hdr->nr_pages);
183 }
184
185 /*
186 * Local variables:
187 * mode: C
188 * c-file-style: "BSD"
189 * c-basic-offset: 4
190 * tab-width: 4
191 * indent-tabs-mode: nil
192 * End:
193 */
194