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