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 <string.h>
19 #include <inttypes.h>
20 
21 #include "memshr.h"
22 #include "memshr-priv.h"
23 #include "bidir-hash.h"
24 #include "shm.h"
25 #include "bidir-daemon.h"
26 
27 typedef struct {
28     int     enabled;
29     domid_t domid;
30     xc_interface *xc_handle;
31 } memshr_vbd_info_t;
32 
33 memshr_vbd_info_t vbd_info = {0, DOMID_INVALID};
34 
35 
36 typedef struct {
37     struct shared_memshr_info *shared_info;
38     struct fgprtshr_hash      *fgprts;
39     struct blockshr_hash      *blks;
40 } private_memshr_info_t;
41 
42 private_memshr_info_t memshr;
43 
44 #define SHARED_INFO  (memshr.shared_info)
45 
memshr_set_domid(int domid)46 void memshr_set_domid(int domid)
47 {
48     vbd_info.domid = domid;
49 }
50 
memshr_daemon_initialize(void)51 void memshr_daemon_initialize(void)
52 {
53     void *shm_base_addr;
54     struct fgprtshr_hash *h;
55 
56     memset(&memshr, 0, sizeof(private_memshr_info_t));
57 
58     if((SHARED_INFO = shm_shared_info_open(1)) == NULL)
59     {
60         DPRINTF("Failed to init shared info.\n");
61         return;
62     }
63 
64     if((memshr.fgprts = shm_fgprtshr_hash_open(1)) == NULL)
65     {
66         DPRINTF("Failed to init fgprtshr hash.\n");
67         return;
68     }
69     memshr.shared_info->fgprtshr_hash_inited = 1;
70 
71     if((memshr.blks = shm_blockshr_hash_open(1)) == NULL)
72     {
73         DPRINTF("Failed to init blockshr hash.\n");
74         return;
75     }
76     memshr.shared_info->blockshr_hash_inited = 1;
77 
78     bidir_daemon_initialize(memshr.blks);
79 }
80 
81 
memshr_vbd_initialize(void)82 void memshr_vbd_initialize(void)
83 {
84     xc_interface *xc_handle;
85 
86     memset(&memshr, 0, sizeof(private_memshr_info_t));
87 
88     if((SHARED_INFO = shm_shared_info_open(0)) == NULL)
89     {
90         DPRINTF("Failed to open shared info.\n");
91         return;
92     }
93 
94     if(!SHARED_INFO->fgprtshr_hash_inited)
95     {
96         DPRINTF("fgprtshr hash not inited.\n");
97         return;
98     }
99 
100     if((memshr.fgprts = shm_fgprtshr_hash_open(0)) == NULL)
101     {
102         DPRINTF("Failed to open fgprtshr_hash.\n");
103         return;
104     }
105 
106     if((memshr.blks = shm_blockshr_hash_open(0)) == NULL)
107     {
108         DPRINTF("Failed to open blockshr_hash.\n");
109         return;
110     }
111 
112     if(vbd_info.domid == DOMID_INVALID)
113         return;
114 
115     if((xc_handle = xc_interface_open(0,0,0)) == 0)
116     {
117         DPRINTF("Failed to open XC interface.\n");
118         return;
119     }
120 
121     vbd_info.xc_handle = xc_handle;
122     vbd_info.enabled = 1;
123 }
124 
memshr_vbd_image_get(const char * file)125 uint16_t memshr_vbd_image_get(const char* file)
126 {
127     uint16_t id;
128 
129     if(pthread_mutex_lock(&SHARED_INFO->lock)) goto error_out;
130     id = shm_vbd_image_get(file, SHARED_INFO->vbd_images);
131     if(pthread_mutex_unlock(&SHARED_INFO->lock)) goto error_out;
132 
133     return id;
134 error_out:
135     return 0;
136 }
137 
memshr_vbd_image_put(uint16_t memshr_id)138 void memshr_vbd_image_put(uint16_t memshr_id)
139 {
140     if(pthread_mutex_lock(&SHARED_INFO->lock)) return;
141     shm_vbd_image_put(memshr_id, SHARED_INFO->vbd_images);
142     if(pthread_mutex_unlock(&SHARED_INFO->lock)) return;
143 }
144 
memshr_vbd_issue_ro_request(char * buf,grant_ref_t gref,uint16_t file_id,uint64_t sec,int secs,share_tuple_t * hnd)145 int memshr_vbd_issue_ro_request(char *buf,
146                                 grant_ref_t gref,
147                                 uint16_t file_id,
148                                 uint64_t sec,
149                                 int secs,
150                                 share_tuple_t *hnd)
151 {
152     vbdblk_t blk;
153     share_tuple_t source_st, client_st;
154     uint64_t c_hnd;
155     int ret;
156 
157     *hnd = (share_tuple_t){ 0, 0, 0 };
158     if(!vbd_info.enabled)
159         return -1;
160 
161     if(secs != 8)
162         return -2;
163 
164     /* Nominate the granted page for sharing */
165     ret = xc_memshr_nominate_gref(vbd_info.xc_handle,
166                                   vbd_info.domid,
167                                   gref,
168                                   &c_hnd);
169     /* If page couldn't be made sharable, we cannot do anything about it */
170     if(ret != 0)
171         return -3;
172 
173     client_st = (share_tuple_t){ vbd_info.domid, gref, c_hnd };
174     *hnd = client_st;
175 
176     /* Check if we've read matching disk block previously */
177     blk.sec     = sec;
178     blk.disk_id = file_id;
179     if(blockshr_block_lookup(memshr.blks, blk, &source_st) > 0)
180     {
181         ret = xc_memshr_share_grefs(vbd_info.xc_handle, source_st.domain, source_st.frame,
182                                     source_st.handle, vbd_info.domid, gref, c_hnd);
183         if(!ret) return 0;
184         /* Handles failed to be shared => at least one of them must be invalid,
185            remove the relevant ones from the map */
186         switch(ret)
187         {
188             case XENMEM_SHARING_OP_S_HANDLE_INVALID:
189                 ret = blockshr_shrhnd_remove(memshr.blks, source_st, NULL);
190                 if(ret) DPRINTF("Could not rm invl s_hnd: %u %"PRId64" %"PRId64"\n",
191                                     source_st.domain, source_st.frame, source_st.handle);
192                 break;
193             case XENMEM_SHARING_OP_C_HANDLE_INVALID:
194                 ret = blockshr_shrhnd_remove(memshr.blks, client_st, NULL);
195                 if(ret) DPRINTF("Could not rm invl c_hnd: %u %"PRId64" %"PRId64"\n",
196                                     client_st.domain, client_st.frame, client_st.handle);
197                 break;
198             default:
199                 break;
200         }
201         return -5;
202     }
203 
204     return -4;
205 }
206 
memshr_vbd_complete_ro_request(share_tuple_t hnd,uint16_t file_id,uint64_t sec,int secs)207 void memshr_vbd_complete_ro_request(share_tuple_t hnd,
208                                     uint16_t file_id,
209                                     uint64_t sec,
210                                     int secs)
211 {
212     vbdblk_t blk;
213 
214     if(!vbd_info.enabled)
215         return;
216 
217     if(secs != 8)
218         return;
219 
220     blk.sec     = sec;
221     blk.disk_id = file_id;
222     if(blockshr_insert(memshr.blks, blk, hnd) < 0)
223         DPRINTF("Could not insert block hint into hash.\n");
224 }
225