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 #include "vchan.h"
44
45 #ifndef PAGE_SHIFT
46 #define PAGE_SHIFT 12
47 #endif
48
49 #ifndef PAGE_SIZE
50 #define PAGE_SIZE 4096
51 #endif
52
53
rd_prod(struct libxenvchan * ctrl)54 static inline uint32_t rd_prod(struct libxenvchan *ctrl)
55 {
56 return ctrl->read.shr->prod;
57 }
58
_rd_cons(struct libxenvchan * ctrl)59 static inline uint32_t* _rd_cons(struct libxenvchan *ctrl)
60 {
61 return &ctrl->read.shr->cons;
62 }
63 #define rd_cons(x) (*_rd_cons(x))
64
_wr_prod(struct libxenvchan * ctrl)65 static inline uint32_t* _wr_prod(struct libxenvchan *ctrl)
66 {
67 return &ctrl->write.shr->prod;
68 }
69 #define wr_prod(x) (*_wr_prod(x))
70
wr_cons(struct libxenvchan * ctrl)71 static inline uint32_t wr_cons(struct libxenvchan *ctrl)
72 {
73 return ctrl->write.shr->cons;
74 }
75
rd_ring(struct libxenvchan * ctrl)76 static inline const void* rd_ring(struct libxenvchan *ctrl)
77 {
78 return ctrl->read.buffer;
79 }
80
wr_ring(struct libxenvchan * ctrl)81 static inline void* wr_ring(struct libxenvchan *ctrl)
82 {
83 return ctrl->write.buffer;
84 }
85
wr_ring_size(struct libxenvchan * ctrl)86 static inline uint32_t wr_ring_size(struct libxenvchan *ctrl)
87 {
88 return (1 << ctrl->write.order);
89 }
90
rd_ring_size(struct libxenvchan * ctrl)91 static inline uint32_t rd_ring_size(struct libxenvchan *ctrl)
92 {
93 return (1 << ctrl->read.order);
94 }
95
request_notify(struct libxenvchan * ctrl,uint8_t bit)96 static inline void request_notify(struct libxenvchan *ctrl, uint8_t bit)
97 {
98 uint8_t *notify = ctrl->is_server ? &ctrl->ring->cli_notify : &ctrl->ring->srv_notify;
99 __sync_or_and_fetch(notify, bit);
100 xen_mb(); /* post the request /before/ caller re-reads any indexes */
101 }
102
send_notify(struct libxenvchan * ctrl,uint8_t bit)103 static inline int send_notify(struct libxenvchan *ctrl, uint8_t bit)
104 {
105 uint8_t *notify, prev;
106 xen_mb(); /* caller updates indexes /before/ we decode to notify */
107 notify = ctrl->is_server ? &ctrl->ring->srv_notify : &ctrl->ring->cli_notify;
108 prev = __sync_fetch_and_and(notify, ~bit);
109 if (prev & bit)
110 return xenevtchn_notify(ctrl->event, ctrl->event_port);
111 else
112 return 0;
113 }
114
115 /*
116 * Get the amount of buffer space available, and do nothing about
117 * notifications.
118 */
raw_get_data_ready(struct libxenvchan * ctrl)119 static inline int raw_get_data_ready(struct libxenvchan *ctrl)
120 {
121 uint32_t ready = rd_prod(ctrl) - rd_cons(ctrl);
122 xen_mb(); /* Ensure 'ready' is read only once. */
123 if (ready > rd_ring_size(ctrl))
124 /* We have no way to return errors. Locking up the ring is
125 * better than the alternatives. */
126 return 0;
127 return ready;
128 }
129
130 /**
131 * Get the amount of buffer space available and enable notifications if needed.
132 */
fast_get_data_ready(struct libxenvchan * ctrl,size_t request)133 static inline int fast_get_data_ready(struct libxenvchan *ctrl, size_t request)
134 {
135 int ready = raw_get_data_ready(ctrl);
136 if (ready >= request)
137 return ready;
138 /* We plan to consume all data; please tell us if you send more */
139 request_notify(ctrl, VCHAN_NOTIFY_WRITE);
140 /*
141 * If the writer moved rd_prod after our read but before request, we
142 * will not get notified even though the actual amount of data ready is
143 * above request. Reread rd_prod to cover this case.
144 */
145 return raw_get_data_ready(ctrl);
146 }
147
libxenvchan_data_ready(struct libxenvchan * ctrl)148 int libxenvchan_data_ready(struct libxenvchan *ctrl)
149 {
150 /* Since this value is being used outside libxenvchan, request notification
151 * when it changes
152 */
153 request_notify(ctrl, VCHAN_NOTIFY_WRITE);
154 return raw_get_data_ready(ctrl);
155 }
156
157 /**
158 * Get the amount of buffer space available, and do nothing
159 * about notifications
160 */
raw_get_buffer_space(struct libxenvchan * ctrl)161 static inline int raw_get_buffer_space(struct libxenvchan *ctrl)
162 {
163 uint32_t ready = wr_ring_size(ctrl) - (wr_prod(ctrl) - wr_cons(ctrl));
164 xen_mb(); /* Ensure 'ready' is read only once. */
165 if (ready > wr_ring_size(ctrl))
166 /* We have no way to return errors. Locking up the ring is
167 * better than the alternatives. */
168 return 0;
169 return ready;
170 }
171
172 /**
173 * Get the amount of buffer space available and enable notifications if needed.
174 */
fast_get_buffer_space(struct libxenvchan * ctrl,size_t request)175 static inline int fast_get_buffer_space(struct libxenvchan *ctrl, size_t request)
176 {
177 int ready = raw_get_buffer_space(ctrl);
178 if (ready >= request)
179 return ready;
180 /* We plan to fill the buffer; please tell us when you've read it */
181 request_notify(ctrl, VCHAN_NOTIFY_READ);
182 /*
183 * If the reader moved wr_cons after our read but before request, we
184 * will not get notified even though the actual amount of buffer space
185 * is above request. Reread wr_cons to cover this case.
186 */
187 return raw_get_buffer_space(ctrl);
188 }
189
libxenvchan_buffer_space(struct libxenvchan * ctrl)190 int libxenvchan_buffer_space(struct libxenvchan *ctrl)
191 {
192 /* Since this value is being used outside libxenvchan, request notification
193 * when it changes
194 */
195 request_notify(ctrl, VCHAN_NOTIFY_READ);
196 return raw_get_buffer_space(ctrl);
197 }
198
libxenvchan_wait(struct libxenvchan * ctrl)199 int libxenvchan_wait(struct libxenvchan *ctrl)
200 {
201 int ret = xenevtchn_pending(ctrl->event);
202 if (ret < 0)
203 return -1;
204 xenevtchn_unmask(ctrl->event, ret);
205 return 0;
206 }
207
208 /**
209 * returns -1 on error, or size on success
210 *
211 * caller must have checked that enough space is available
212 */
do_send(struct libxenvchan * ctrl,const void * data,size_t size)213 static int do_send(struct libxenvchan *ctrl, const void *data, size_t size)
214 {
215 int real_idx = wr_prod(ctrl) & (wr_ring_size(ctrl) - 1);
216 int avail_contig = wr_ring_size(ctrl) - real_idx;
217 if (avail_contig > size)
218 avail_contig = size;
219 xen_mb(); /* read indexes /then/ write data */
220 memcpy(wr_ring(ctrl) + real_idx, data, avail_contig);
221 if (avail_contig < size)
222 {
223 // we rolled across the end of the ring
224 memcpy(wr_ring(ctrl), data + avail_contig, size - avail_contig);
225 }
226 xen_wmb(); /* write data /then/ notify */
227 wr_prod(ctrl) += size;
228 if (send_notify(ctrl, VCHAN_NOTIFY_WRITE))
229 return -1;
230 return size;
231 }
232
233 /**
234 * returns 0 if no buffer space is available, -1 on error, or size on success
235 */
libxenvchan_send(struct libxenvchan * ctrl,const void * data,size_t size)236 int libxenvchan_send(struct libxenvchan *ctrl, const void *data, size_t size)
237 {
238 int avail;
239 while (1) {
240 if (!libxenvchan_is_open(ctrl))
241 return -1;
242 avail = fast_get_buffer_space(ctrl, size);
243 if (size <= avail)
244 return do_send(ctrl, data, size);
245 if (!ctrl->blocking)
246 return 0;
247 if (size > wr_ring_size(ctrl))
248 return -1;
249 if (libxenvchan_wait(ctrl))
250 return -1;
251 }
252 }
253
libxenvchan_write(struct libxenvchan * ctrl,const void * data,size_t size)254 int libxenvchan_write(struct libxenvchan *ctrl, const void *data, size_t size)
255 {
256 int avail;
257 if (!libxenvchan_is_open(ctrl))
258 return -1;
259 if (ctrl->blocking) {
260 size_t pos = 0;
261 while (1) {
262 avail = fast_get_buffer_space(ctrl, size - pos);
263 if (pos + avail > size)
264 avail = size - pos;
265 if (avail)
266 pos += do_send(ctrl, data + pos, avail);
267 if (pos == size)
268 return pos;
269 if (libxenvchan_wait(ctrl))
270 return -1;
271 if (!libxenvchan_is_open(ctrl))
272 return -1;
273 }
274 } else {
275 avail = fast_get_buffer_space(ctrl, size);
276 if (size > avail)
277 size = avail;
278 if (size == 0)
279 return 0;
280 return do_send(ctrl, data, size);
281 }
282 }
283
284 /**
285 * returns -1 on error, or size on success
286 *
287 * caller must have checked that enough data is available
288 */
do_recv(struct libxenvchan * ctrl,void * data,size_t size)289 static int do_recv(struct libxenvchan *ctrl, void *data, size_t size)
290 {
291 int real_idx = rd_cons(ctrl) & (rd_ring_size(ctrl) - 1);
292 int avail_contig = rd_ring_size(ctrl) - real_idx;
293 if (avail_contig > size)
294 avail_contig = size;
295 xen_rmb(); /* data read must happen /after/ rd_cons read */
296 memcpy(data, rd_ring(ctrl) + real_idx, avail_contig);
297 if (avail_contig < size)
298 {
299 // we rolled across the end of the ring
300 memcpy(data + avail_contig, rd_ring(ctrl), size - avail_contig);
301 }
302 xen_mb(); /* consume /then/ notify */
303 rd_cons(ctrl) += size;
304 if (send_notify(ctrl, VCHAN_NOTIFY_READ))
305 return -1;
306 return size;
307 }
308
309 /**
310 * reads exactly size bytes from the vchan.
311 * returns 0 if insufficient data is available, -1 on error, or size on success
312 */
libxenvchan_recv(struct libxenvchan * ctrl,void * data,size_t size)313 int libxenvchan_recv(struct libxenvchan *ctrl, void *data, size_t size)
314 {
315 while (1) {
316 int avail = fast_get_data_ready(ctrl, size);
317 if (size <= avail)
318 return do_recv(ctrl, data, size);
319 if (!libxenvchan_is_open(ctrl))
320 return -1;
321 if (!ctrl->blocking)
322 return 0;
323 if (size > rd_ring_size(ctrl))
324 return -1;
325 if (libxenvchan_wait(ctrl))
326 return -1;
327 }
328 }
329
libxenvchan_read(struct libxenvchan * ctrl,void * data,size_t size)330 int libxenvchan_read(struct libxenvchan *ctrl, void *data, size_t size)
331 {
332 while (1) {
333 int avail = fast_get_data_ready(ctrl, size);
334 if (avail && size > avail)
335 size = avail;
336 if (avail)
337 return do_recv(ctrl, data, size);
338 if (!libxenvchan_is_open(ctrl))
339 return -1;
340 if (!ctrl->blocking)
341 return 0;
342 if (libxenvchan_wait(ctrl))
343 return -1;
344 }
345 }
346
libxenvchan_is_open(struct libxenvchan * ctrl)347 int libxenvchan_is_open(struct libxenvchan* ctrl)
348 {
349 if (ctrl->is_server)
350 return ctrl->server_persist ? 1 : ctrl->ring->cli_live;
351 else
352 return ctrl->ring->srv_live;
353 }
354
libxenvchan_fd_for_select(struct libxenvchan * ctrl)355 int libxenvchan_fd_for_select(struct libxenvchan *ctrl)
356 {
357 return xenevtchn_fd(ctrl->event);
358 }
359
libxenvchan_close(struct libxenvchan * ctrl)360 void libxenvchan_close(struct libxenvchan *ctrl)
361 {
362 if (!ctrl)
363 return;
364 if (ctrl->read.order >= PAGE_SHIFT)
365 munmap(ctrl->read.buffer, 1 << ctrl->read.order);
366 if (ctrl->write.order >= PAGE_SHIFT)
367 munmap(ctrl->write.buffer, 1 << ctrl->write.order);
368 if (ctrl->ring) {
369 if (ctrl->is_server) {
370 ctrl->ring->srv_live = 0;
371 xengntshr_unshare(ctrl->gntshr, ctrl->ring, 1);
372 } else {
373 ctrl->ring->cli_live = 0;
374 xengnttab_unmap(ctrl->gnttab, ctrl->ring, 1);
375 }
376 }
377 if (ctrl->event) {
378 if (ctrl->ring)
379 xenevtchn_notify(ctrl->event, ctrl->event_port);
380 xenevtchn_close(ctrl->event);
381 }
382 if (ctrl->is_server) {
383 if (ctrl->gntshr)
384 xengntshr_close(ctrl->gntshr);
385 } else {
386 if (ctrl->gnttab)
387 xengnttab_close(ctrl->gnttab);
388 }
389 if (ctrl->is_server)
390 close_xs_srv(ctrl);
391 free(ctrl);
392 }
393