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