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