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