1 /**
2 * @file
3 * @section AUTHORS
4 *
5 * Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
6 *
7 * Authors:
8 * Rafal Wojtczuk <rafal@invisiblethingslab.com>
9 * Daniel De Graaf <dgdegra@tycho.nsa.gov>
10 *
11 * @section LICENSE
12 *
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
17 *
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
22 *
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; If not, see <http://www.gnu.org/licenses/>.
25 *
26 * @section DESCRIPTION
27 *
28 * This file contains the communications interface built on the ring buffer.
29 */
30
31 #include <sys/types.h>
32 #include <sys/mman.h>
33 #include <sys/ioctl.h>
34 #include <sys/uio.h>
35 #include <stdlib.h>
36 #include <stdint.h>
37 #include <string.h>
38 #include <unistd.h>
39
40 #include <xenctrl.h>
41 #include <libxenvchan.h>
42
43 #ifndef PAGE_SHIFT
44 #define PAGE_SHIFT 12
45 #endif
46
47 #ifndef PAGE_SIZE
48 #define PAGE_SIZE 4096
49 #endif
50
51
rd_prod(struct libxenvchan * ctrl)52 static inline uint32_t rd_prod(struct libxenvchan *ctrl)
53 {
54 return ctrl->read.shr->prod;
55 }
56
_rd_cons(struct libxenvchan * ctrl)57 static inline uint32_t* _rd_cons(struct libxenvchan *ctrl)
58 {
59 return &ctrl->read.shr->cons;
60 }
61 #define rd_cons(x) (*_rd_cons(x))
62
_wr_prod(struct libxenvchan * ctrl)63 static inline uint32_t* _wr_prod(struct libxenvchan *ctrl)
64 {
65 return &ctrl->write.shr->prod;
66 }
67 #define wr_prod(x) (*_wr_prod(x))
68
wr_cons(struct libxenvchan * ctrl)69 static inline uint32_t wr_cons(struct libxenvchan *ctrl)
70 {
71 return ctrl->write.shr->cons;
72 }
73
rd_ring(struct libxenvchan * ctrl)74 static inline const void* rd_ring(struct libxenvchan *ctrl)
75 {
76 return ctrl->read.buffer;
77 }
78
wr_ring(struct libxenvchan * ctrl)79 static inline void* wr_ring(struct libxenvchan *ctrl)
80 {
81 return ctrl->write.buffer;
82 }
83
wr_ring_size(struct libxenvchan * ctrl)84 static inline uint32_t wr_ring_size(struct libxenvchan *ctrl)
85 {
86 return (1 << ctrl->write.order);
87 }
88
rd_ring_size(struct libxenvchan * ctrl)89 static inline uint32_t rd_ring_size(struct libxenvchan *ctrl)
90 {
91 return (1 << ctrl->read.order);
92 }
93
request_notify(struct libxenvchan * ctrl,uint8_t bit)94 static inline void request_notify(struct libxenvchan *ctrl, uint8_t bit)
95 {
96 uint8_t *notify = ctrl->is_server ? &ctrl->ring->cli_notify : &ctrl->ring->srv_notify;
97 __sync_or_and_fetch(notify, bit);
98 xen_mb(); /* post the request /before/ caller re-reads any indexes */
99 }
100
send_notify(struct libxenvchan * ctrl,uint8_t bit)101 static inline int send_notify(struct libxenvchan *ctrl, uint8_t bit)
102 {
103 uint8_t *notify, prev;
104 xen_mb(); /* caller updates indexes /before/ we decode to notify */
105 notify = ctrl->is_server ? &ctrl->ring->srv_notify : &ctrl->ring->cli_notify;
106 prev = __sync_fetch_and_and(notify, ~bit);
107 if (prev & bit)
108 return xenevtchn_notify(ctrl->event, ctrl->event_port);
109 else
110 return 0;
111 }
112
113 /*
114 * Get the amount of buffer space available, and do nothing about
115 * notifications.
116 */
raw_get_data_ready(struct libxenvchan * ctrl)117 static inline int raw_get_data_ready(struct libxenvchan *ctrl)
118 {
119 uint32_t ready = rd_prod(ctrl) - rd_cons(ctrl);
120 xen_mb(); /* Ensure 'ready' is read only once. */
121 if (ready > rd_ring_size(ctrl))
122 /* We have no way to return errors. Locking up the ring is
123 * better than the alternatives. */
124 return 0;
125 return ready;
126 }
127
128 /**
129 * Get the amount of buffer space available and enable notifications if needed.
130 */
fast_get_data_ready(struct libxenvchan * ctrl,size_t request)131 static inline int fast_get_data_ready(struct libxenvchan *ctrl, size_t request)
132 {
133 int ready = raw_get_data_ready(ctrl);
134 if (ready >= request)
135 return ready;
136 /* We plan to consume all data; please tell us if you send more */
137 request_notify(ctrl, VCHAN_NOTIFY_WRITE);
138 /*
139 * If the writer moved rd_prod after our read but before request, we
140 * will not get notified even though the actual amount of data ready is
141 * above request. Reread rd_prod to cover this case.
142 */
143 return raw_get_data_ready(ctrl);
144 }
145
libxenvchan_data_ready(struct libxenvchan * ctrl)146 int libxenvchan_data_ready(struct libxenvchan *ctrl)
147 {
148 /* Since this value is being used outside libxenvchan, request notification
149 * when it changes
150 */
151 request_notify(ctrl, VCHAN_NOTIFY_WRITE);
152 return raw_get_data_ready(ctrl);
153 }
154
155 /**
156 * Get the amount of buffer space available, and do nothing
157 * about notifications
158 */
raw_get_buffer_space(struct libxenvchan * ctrl)159 static inline int raw_get_buffer_space(struct libxenvchan *ctrl)
160 {
161 uint32_t ready = wr_ring_size(ctrl) - (wr_prod(ctrl) - wr_cons(ctrl));
162 xen_mb(); /* Ensure 'ready' is read only once. */
163 if (ready > wr_ring_size(ctrl))
164 /* We have no way to return errors. Locking up the ring is
165 * better than the alternatives. */
166 return 0;
167 return ready;
168 }
169
170 /**
171 * Get the amount of buffer space available and enable notifications if needed.
172 */
fast_get_buffer_space(struct libxenvchan * ctrl,size_t request)173 static inline int fast_get_buffer_space(struct libxenvchan *ctrl, size_t request)
174 {
175 int ready = raw_get_buffer_space(ctrl);
176 if (ready >= request)
177 return ready;
178 /* We plan to fill the buffer; please tell us when you've read it */
179 request_notify(ctrl, VCHAN_NOTIFY_READ);
180 /*
181 * If the reader moved wr_cons after our read but before request, we
182 * will not get notified even though the actual amount of buffer space
183 * is above request. Reread wr_cons to cover this case.
184 */
185 return raw_get_buffer_space(ctrl);
186 }
187
libxenvchan_buffer_space(struct libxenvchan * ctrl)188 int libxenvchan_buffer_space(struct libxenvchan *ctrl)
189 {
190 /* Since this value is being used outside libxenvchan, request notification
191 * when it changes
192 */
193 request_notify(ctrl, VCHAN_NOTIFY_READ);
194 return raw_get_buffer_space(ctrl);
195 }
196
libxenvchan_wait(struct libxenvchan * ctrl)197 int libxenvchan_wait(struct libxenvchan *ctrl)
198 {
199 int ret = xenevtchn_pending(ctrl->event);
200 if (ret < 0)
201 return -1;
202 xenevtchn_unmask(ctrl->event, ret);
203 return 0;
204 }
205
206 /**
207 * returns -1 on error, or size on success
208 *
209 * caller must have checked that enough space is available
210 */
do_send(struct libxenvchan * ctrl,const void * data,size_t size)211 static int do_send(struct libxenvchan *ctrl, const void *data, size_t size)
212 {
213 int real_idx = wr_prod(ctrl) & (wr_ring_size(ctrl) - 1);
214 int avail_contig = wr_ring_size(ctrl) - real_idx;
215 if (avail_contig > size)
216 avail_contig = size;
217 xen_mb(); /* read indexes /then/ write data */
218 memcpy(wr_ring(ctrl) + real_idx, data, avail_contig);
219 if (avail_contig < size)
220 {
221 // we rolled across the end of the ring
222 memcpy(wr_ring(ctrl), data + avail_contig, size - avail_contig);
223 }
224 xen_wmb(); /* write data /then/ notify */
225 wr_prod(ctrl) += size;
226 if (send_notify(ctrl, VCHAN_NOTIFY_WRITE))
227 return -1;
228 return size;
229 }
230
231 /**
232 * returns 0 if no buffer space is available, -1 on error, or size on success
233 */
libxenvchan_send(struct libxenvchan * ctrl,const void * data,size_t size)234 int libxenvchan_send(struct libxenvchan *ctrl, const void *data, size_t size)
235 {
236 int avail;
237 while (1) {
238 if (!libxenvchan_is_open(ctrl))
239 return -1;
240 avail = fast_get_buffer_space(ctrl, size);
241 if (size <= avail)
242 return do_send(ctrl, data, size);
243 if (!ctrl->blocking)
244 return 0;
245 if (size > wr_ring_size(ctrl))
246 return -1;
247 if (libxenvchan_wait(ctrl))
248 return -1;
249 }
250 }
251
libxenvchan_write(struct libxenvchan * ctrl,const void * data,size_t size)252 int libxenvchan_write(struct libxenvchan *ctrl, const void *data, size_t size)
253 {
254 int avail;
255 if (!libxenvchan_is_open(ctrl))
256 return -1;
257 if (ctrl->blocking) {
258 size_t pos = 0;
259 while (1) {
260 avail = fast_get_buffer_space(ctrl, size - pos);
261 if (pos + avail > size)
262 avail = size - pos;
263 if (avail)
264 pos += do_send(ctrl, data + pos, avail);
265 if (pos == size)
266 return pos;
267 if (libxenvchan_wait(ctrl))
268 return -1;
269 if (!libxenvchan_is_open(ctrl))
270 return -1;
271 }
272 } else {
273 avail = fast_get_buffer_space(ctrl, size);
274 if (size > avail)
275 size = avail;
276 if (size == 0)
277 return 0;
278 return do_send(ctrl, data, size);
279 }
280 }
281
282 /**
283 * returns -1 on error, or size on success
284 *
285 * caller must have checked that enough data is available
286 */
do_recv(struct libxenvchan * ctrl,void * data,size_t size)287 static int do_recv(struct libxenvchan *ctrl, void *data, size_t size)
288 {
289 int real_idx = rd_cons(ctrl) & (rd_ring_size(ctrl) - 1);
290 int avail_contig = rd_ring_size(ctrl) - real_idx;
291 if (avail_contig > size)
292 avail_contig = size;
293 xen_rmb(); /* data read must happen /after/ rd_cons read */
294 memcpy(data, rd_ring(ctrl) + real_idx, avail_contig);
295 if (avail_contig < size)
296 {
297 // we rolled across the end of the ring
298 memcpy(data + avail_contig, rd_ring(ctrl), size - avail_contig);
299 }
300 xen_mb(); /* consume /then/ notify */
301 rd_cons(ctrl) += size;
302 if (send_notify(ctrl, VCHAN_NOTIFY_READ))
303 return -1;
304 return size;
305 }
306
307 /**
308 * reads exactly size bytes from the vchan.
309 * returns 0 if insufficient data is available, -1 on error, or size on success
310 */
libxenvchan_recv(struct libxenvchan * ctrl,void * data,size_t size)311 int libxenvchan_recv(struct libxenvchan *ctrl, void *data, size_t size)
312 {
313 while (1) {
314 int avail = fast_get_data_ready(ctrl, size);
315 if (size <= avail)
316 return do_recv(ctrl, data, size);
317 if (!libxenvchan_is_open(ctrl))
318 return -1;
319 if (!ctrl->blocking)
320 return 0;
321 if (size > rd_ring_size(ctrl))
322 return -1;
323 if (libxenvchan_wait(ctrl))
324 return -1;
325 }
326 }
327
libxenvchan_read(struct libxenvchan * ctrl,void * data,size_t size)328 int libxenvchan_read(struct libxenvchan *ctrl, void *data, size_t size)
329 {
330 while (1) {
331 int avail = fast_get_data_ready(ctrl, size);
332 if (avail && size > avail)
333 size = avail;
334 if (avail)
335 return do_recv(ctrl, data, size);
336 if (!libxenvchan_is_open(ctrl))
337 return -1;
338 if (!ctrl->blocking)
339 return 0;
340 if (libxenvchan_wait(ctrl))
341 return -1;
342 }
343 }
344
libxenvchan_is_open(struct libxenvchan * ctrl)345 int libxenvchan_is_open(struct libxenvchan* ctrl)
346 {
347 if (ctrl->is_server)
348 return ctrl->server_persist ? 1 : ctrl->ring->cli_live;
349 else
350 return ctrl->ring->srv_live;
351 }
352
libxenvchan_fd_for_select(struct libxenvchan * ctrl)353 int libxenvchan_fd_for_select(struct libxenvchan *ctrl)
354 {
355 return xenevtchn_fd(ctrl->event);
356 }
357
libxenvchan_close(struct libxenvchan * ctrl)358 void libxenvchan_close(struct libxenvchan *ctrl)
359 {
360 if (!ctrl)
361 return;
362 if (ctrl->read.order >= PAGE_SHIFT)
363 munmap(ctrl->read.buffer, 1 << ctrl->read.order);
364 if (ctrl->write.order >= PAGE_SHIFT)
365 munmap(ctrl->write.buffer, 1 << ctrl->write.order);
366 if (ctrl->ring) {
367 if (ctrl->is_server) {
368 ctrl->ring->srv_live = 0;
369 xengntshr_unshare(ctrl->gntshr, ctrl->ring, 1);
370 } else {
371 ctrl->ring->cli_live = 0;
372 xengnttab_unmap(ctrl->gnttab, ctrl->ring, 1);
373 }
374 }
375 if (ctrl->event) {
376 if (ctrl->ring)
377 xenevtchn_notify(ctrl->event, ctrl->event_port);
378 xenevtchn_close(ctrl->event);
379 }
380 if (ctrl->is_server) {
381 if (ctrl->gntshr)
382 xengntshr_close(ctrl->gntshr);
383 } else {
384 if (ctrl->gnttab)
385 xengnttab_close(ctrl->gnttab);
386 }
387 free(ctrl);
388 }
389