1 /******************************************************************************
2 *
3 * Copyright (c) 2009 Citrix Systems, Inc. (Grzegorz Milos)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include <assert.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <sys/mman.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26
27 #include "memshr-priv.h"
28 #include "bidir-hash.h"
29 #include "shm.h"
30
31 #define MEMSHR_INFO_SHM_FILE "/memshr-info"
32 #define MEMSHR_INFO_MAGIC 0x15263748
33
34 #define FGPRT_HASH_SHM_FILE "/blktap-fgprts"
35 #define FGPRT_HASH_PAGES 10000
36
37 #define BLOCK_HASH_SHM_FILE "/blktap-blks"
38 #define BLOCK_HASH_PAGES 10000
39
40 typedef struct shm_area {
41 void* base_addr;
42 size_t size;
43 int fd;
44 } shm_area_t;
45
46 typedef struct {
47 struct shm_area shared_info_area;
48 struct shm_area fgprts_area;
49 struct shm_area blocks_area;
50 } private_shm_info_t;
51
52 private_shm_info_t shm_info;
53
54
55
shm_area_open(const char * file,size_t size,int unlink,shm_area_t * shma)56 static int shm_area_open(const char *file, size_t size, int unlink, shm_area_t *shma)
57 {
58 /* TODO: If blktapctrl can be restarted while system is running, this needs
59 * to be cleverer */
60 if(unlink) shm_unlink(file);
61
62 shma->size = size;
63 shma->fd = shm_open(file,
64 (O_CREAT | O_RDWR),
65 (S_IREAD | S_IWRITE));
66
67 if(shma->fd < 0) return -1;
68
69 if(ftruncate(shma->fd, size) < 0) return -2;
70
71 shma->base_addr = mmap(NULL,
72 size,
73 PROT_READ | PROT_WRITE,
74 MAP_SHARED,
75 shma->fd,
76 0);
77
78 if(shma->base_addr == MAP_FAILED) return -2;
79
80 return 0;
81 }
82
shm_area_close(shm_area_t * shma)83 static void shm_area_close(shm_area_t *shma)
84 {
85 munmap(shma->base_addr, shma->size);
86 close(shma->fd);
87 }
88
89
shm_shared_info_open(int unlink)90 shared_memshr_info_t * shm_shared_info_open(int unlink)
91 {
92 shared_memshr_info_t *shared_info;
93 pthread_mutexattr_t lock_attr;
94 int nr_pages, i;
95
96 nr_pages = (sizeof(shared_memshr_info_t) >> XC_PAGE_SHIFT) + 1;
97 if(shm_area_open(MEMSHR_INFO_SHM_FILE,
98 nr_pages * XC_PAGE_SIZE,
99 unlink,
100 &(shm_info.shared_info_area)) < 0)
101 {
102 DPRINTF("Failed to open shma for shared info.\n");
103 return NULL;
104 }
105 shared_info = (shared_memshr_info_t *)
106 shm_info.shared_info_area.base_addr;
107 if(unlink)
108 {
109 memset(shared_info, 0, sizeof(shared_memshr_info_t));
110 if(pthread_mutexattr_init(&lock_attr) ||
111 pthread_mutexattr_setpshared(&lock_attr, PTHREAD_PROCESS_SHARED) ||
112 pthread_mutex_init(&shared_info->lock, &lock_attr) ||
113 pthread_mutexattr_destroy(&lock_attr))
114 {
115 DPRINTF("Failed to init shared info lock.\n");
116 return NULL;
117 }
118 strcpy(shared_info->vbd_images[0].file, "list-head");
119 for(i=1; i<MAX_NR_VBD_IMAGES; i++)
120 {
121 shared_info->vbd_images[i].next = i;
122 shared_info->vbd_images[i].prev = i;
123 }
124 shared_info->magic = MEMSHR_INFO_MAGIC;
125 }
126 else
127 if(shared_info->magic != MEMSHR_INFO_MAGIC)
128 {
129 DPRINTF("Incorrect magic in shared info.\n");
130 return NULL;
131 }
132
133 return shared_info;
134 }
135
136
shm_fgprtshr_hash_open(int unlink)137 struct fgprtshr_hash * shm_fgprtshr_hash_open(int unlink)
138 {
139 struct fgprtshr_hash *h;
140 if(shm_area_open(FGPRT_HASH_SHM_FILE,
141 FGPRT_HASH_PAGES * XC_PAGE_SIZE,
142 unlink,
143 &(shm_info.fgprts_area)) < 0)
144 {
145 DPRINTF("Failed to init shma for fgprtshr_hash.\n");
146 return NULL;
147 }
148
149 if(unlink)
150 {
151 h = fgprtshr_shm_hash_init(
152 (unsigned long) shm_info.fgprts_area.base_addr,
153 FGPRT_HASH_PAGES * XC_PAGE_SIZE);
154 } else
155 {
156 h = fgprtshr_shm_hash_get(
157 (unsigned long) shm_info.fgprts_area.base_addr);
158 }
159
160 return h;
161 }
162
shm_blockshr_hash_open(int unlink)163 struct blockshr_hash * shm_blockshr_hash_open(int unlink)
164 {
165 struct blockshr_hash *h;
166 if(shm_area_open(BLOCK_HASH_SHM_FILE,
167 BLOCK_HASH_PAGES * XC_PAGE_SIZE,
168 unlink,
169 &(shm_info.blocks_area)) < 0)
170 {
171 DPRINTF("Failed to init shma for blockshr_hash.\n");
172 return NULL;
173 }
174
175 if(unlink)
176 {
177 h = blockshr_shm_hash_init(
178 (unsigned long) shm_info.blocks_area.base_addr,
179 BLOCK_HASH_PAGES * XC_PAGE_SIZE);
180 } else
181 {
182 h = blockshr_shm_hash_get(
183 (unsigned long) shm_info.blocks_area.base_addr);
184 }
185
186 return h;
187 }
188
shm_vbd_image_get(const char * file,vbd_image_info_t * vbd_imgs)189 uint16_t shm_vbd_image_get(const char* file, vbd_image_info_t *vbd_imgs)
190 {
191 vbd_image_info_t *img, *next_img;
192 int i, img_id;
193
194 /* Try to find the file in the existing list first */
195 img = vbd_imgs;
196 while(img->next != 0)
197 {
198 img = vbd_imgs + img->next;
199 if(strncmp(img->file, file, MAX_NAME_LEN) == 0)
200 {
201 img->ref_cnt++;
202 return (uint16_t)(img - vbd_imgs);
203 }
204 }
205
206 /* Couldn't find an existing entry. We need to add one. Find empty slot */
207 for(i=1; i<MAX_NR_VBD_IMAGES; i++)
208 {
209 img = vbd_imgs + i;
210 if((img->next == i) && (img->prev == i))
211 break;
212 }
213 /* No entries left! */
214 if(i == MAX_NR_VBD_IMAGES)
215 {
216 DPRINTF("No space in vbds table.\n");
217 return 0;
218 }
219 if(strlen(file) > MAX_NAME_LEN - 1)
220 {
221 DPRINTF("Filename: %s too long (>%d).\n", file, MAX_NAME_LEN - 1);
222 return 0;
223 }
224 /* Init the entry */
225 img_id = (img - vbd_imgs);
226 next_img = vbd_imgs + vbd_imgs[0].next;
227 strcpy(img->file, file);
228 img->ref_cnt = 1;
229 img->next = vbd_imgs[0].next;
230 img->prev = 0;
231 next_img->prev = img_id;
232 vbd_imgs[0].next = img_id;
233
234 return img_id;
235 }
236
237
shm_vbd_image_put(uint16_t memshr_id,vbd_image_info_t * vbd_imgs)238 void shm_vbd_image_put(uint16_t memshr_id, vbd_image_info_t *vbd_imgs)
239 {
240 vbd_image_info_t *img, *next_img, *prev_img;
241
242 img = vbd_imgs + memshr_id;
243 if(img->ref_cnt == 0)
244 {
245 DPRINTF("Incorrect image put.\n");
246 return;
247 }
248
249 img->ref_cnt--;
250
251 /* Remove from list if ref_cnt is zero */
252 if(img->ref_cnt == 0)
253 {
254 next_img = vbd_imgs + img->next;
255 prev_img = vbd_imgs + img->prev;
256 prev_img->next = img->next;
257 next_img->prev = img->prev;
258 img->next = img->prev = (img - vbd_imgs);
259 memset(img->file, 0, MAX_NAME_LEN);
260 }
261 }
262
263