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 #include <xen-tools/common-macros.h>
20 
21 #include "private.h"
22 
23 #define DBGPRINTF(_m...) \
24     xtl_log(xcall->logger, XTL_DEBUG, -1, "xencall:buffer", _m)
25 
26 pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
27 
cache_lock(xencall_handle * xcall)28 static void cache_lock(xencall_handle *xcall)
29 {
30     int saved_errno = errno;
31     if ( xcall->flags & XENCALL_OPENFLAG_NON_REENTRANT )
32         return;
33     pthread_mutex_lock(&cache_mutex);
34     /* Ignore pthread errors. */
35     errno = saved_errno;
36 }
37 
cache_unlock(xencall_handle * xcall)38 static void cache_unlock(xencall_handle *xcall)
39 {
40     int saved_errno = errno;
41     if ( xcall->flags & XENCALL_OPENFLAG_NON_REENTRANT )
42         return;
43     pthread_mutex_unlock(&cache_mutex);
44     /* Ignore pthread errors. */
45     errno = saved_errno;
46 }
47 
cache_alloc(xencall_handle * xcall,size_t nr_pages)48 static void *cache_alloc(xencall_handle *xcall, size_t nr_pages)
49 {
50     void *p = NULL;
51 
52     cache_lock(xcall);
53 
54     xcall->buffer_total_allocations++;
55     xcall->buffer_current_allocations++;
56     if ( xcall->buffer_current_allocations > xcall->buffer_maximum_allocations )
57         xcall->buffer_maximum_allocations = xcall->buffer_current_allocations;
58 
59     if ( nr_pages > 1 )
60     {
61         xcall->buffer_cache_toobig++;
62     }
63     else if ( xcall->buffer_cache_nr > 0 )
64     {
65         p = xcall->buffer_cache[--xcall->buffer_cache_nr];
66         xcall->buffer_cache_hits++;
67     }
68     else
69     {
70         xcall->buffer_cache_misses++;
71     }
72 
73     cache_unlock(xcall);
74 
75     return p;
76 }
77 
cache_free(xencall_handle * xcall,void * p,size_t nr_pages)78 static int cache_free(xencall_handle *xcall, void *p, size_t nr_pages)
79 {
80     int rc = 0;
81 
82     cache_lock(xcall);
83 
84     xcall->buffer_total_releases++;
85     xcall->buffer_current_allocations--;
86 
87     if ( nr_pages == 1 &&
88          xcall->buffer_cache_nr < BUFFER_CACHE_SIZE )
89     {
90         xcall->buffer_cache[xcall->buffer_cache_nr++] = p;
91         rc = 1;
92     }
93 
94     cache_unlock(xcall);
95 
96     return rc;
97 }
98 
buffer_release_cache(xencall_handle * xcall)99 void buffer_release_cache(xencall_handle *xcall)
100 {
101     void *p;
102 
103     cache_lock(xcall);
104 
105     DBGPRINTF("total allocations:%d total releases:%d",
106               xcall->buffer_total_allocations,
107               xcall->buffer_total_releases);
108     DBGPRINTF("current allocations:%d maximum allocations:%d",
109               xcall->buffer_current_allocations,
110               xcall->buffer_maximum_allocations);
111     DBGPRINTF("cache current size:%d",
112               xcall->buffer_cache_nr);
113     DBGPRINTF("cache hits:%d misses:%d toobig:%d",
114               xcall->buffer_cache_hits,
115               xcall->buffer_cache_misses,
116               xcall->buffer_cache_toobig);
117 
118     while ( xcall->buffer_cache_nr > 0 )
119     {
120         p = xcall->buffer_cache[--xcall->buffer_cache_nr];
121         osdep_free_pages(xcall, p, 1);
122     }
123 
124     cache_unlock(xcall);
125 }
126 
xencall_alloc_buffer_pages(xencall_handle * xcall,size_t nr_pages)127 void *xencall_alloc_buffer_pages(xencall_handle *xcall, size_t nr_pages)
128 {
129     void *p = cache_alloc(xcall, nr_pages);
130 
131     if ( !p )
132         p = osdep_alloc_pages(xcall, nr_pages);
133 
134     if (!p)
135         return NULL;
136 
137     memset(p, 0, nr_pages * PAGE_SIZE);
138 
139     return p;
140 }
141 
xencall_free_buffer_pages(xencall_handle * xcall,void * p,size_t nr_pages)142 void xencall_free_buffer_pages(xencall_handle *xcall, void *p, size_t nr_pages)
143 {
144     if ( p == NULL )
145         return;
146 
147     if ( !cache_free(xcall, p, nr_pages) )
148         osdep_free_pages(xcall, p, nr_pages);
149 }
150 
151 struct allocation_header {
152     int nr_pages;
153     int pad[3];
154 };
155 
xencall_alloc_buffer(xencall_handle * xcall,size_t size)156 void *xencall_alloc_buffer(xencall_handle *xcall, size_t size)
157 {
158     size_t actual_size = ROUNDUP(size + sizeof(struct allocation_header), PAGE_SHIFT);
159     int nr_pages = actual_size >> PAGE_SHIFT;
160     struct allocation_header *hdr;
161 
162     hdr = xencall_alloc_buffer_pages(xcall, nr_pages);
163     if ( hdr == NULL )
164         return NULL;
165 
166     hdr->nr_pages = nr_pages;
167 
168     return (void *)(hdr+1);
169 }
170 
xencall_free_buffer(xencall_handle * xcall,void * p)171 void xencall_free_buffer(xencall_handle *xcall, void *p)
172 {
173     struct allocation_header *hdr;
174 
175     if (p == NULL)
176         return;
177 
178     hdr = p;
179     --hdr;
180 
181     xencall_free_buffer_pages(xcall, hdr, hdr->nr_pages);
182 }
183 
184 /*
185  * Local variables:
186  * mode: C
187  * c-file-style: "BSD"
188  * c-basic-offset: 4
189  * tab-width: 4
190  * indent-tabs-mode: nil
191  * End:
192  */
193