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