rte_xen_lib.c revision 809f0800
1/*-
2 *   BSD LICENSE
3 *
4 *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
5 *   All rights reserved.
6 *
7 *   Redistribution and use in source and binary forms, with or without
8 *   modification, are permitted provided that the following conditions
9 *   are met:
10 *
11 *     * Redistributions of source code must retain the above copyright
12 *       notice, this list of conditions and the following disclaimer.
13 *     * Redistributions in binary form must reproduce the above copyright
14 *       notice, this list of conditions and the following disclaimer in
15 *       the documentation and/or other materials provided with the
16 *       distribution.
17 *     * Neither the name of Intel Corporation nor the names of its
18 *       contributors may be used to endorse or promote products derived
19 *       from this software without specific prior written permission.
20 *
21 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include <stdint.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <unistd.h>
38#include <string.h>
39#include <sys/types.h>
40#include <fcntl.h>
41#include <sys/mman.h>
42#include <sys/ioctl.h>
43#include <xen/xen-compat.h>
44#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00040200
45#include <xs.h>
46#else
47#include <xenstore.h>
48#endif
49#include <xen/sys/gntalloc.h>
50
51#include <rte_common.h>
52#include <rte_string_fns.h>
53#include <rte_malloc.h>
54
55#include "rte_xen_lib.h"
56
57/*
58 * The grant node format in xenstore for vring/mpool is:
59 * 0_rx_vring_gref = "gref1#, gref2#, gref3#"
60 * 0_mempool_gref  = "gref1#, gref2#, gref3#"
61 * each gref# is a grant reference for a shared page.
62 * In each shared page, we store the grant_node_item items.
63 */
64struct grant_node_item {
65	uint32_t gref;
66	uint32_t pfn;
67} __attribute__((packed));
68
69/* fd for xen_gntalloc driver, used to allocate grant pages*/
70int gntalloc_fd = -1;
71
72/* xenstore path for local domain, now it is '/local/domain/domid/' */
73static char *dompath = NULL;
74/* handle to xenstore read/write operations */
75static struct xs_handle *xs = NULL;
76/* flag to indicate if xenstore cleanup is required */
77static bool is_xenstore_cleaned_up;
78
79/*
80 * Reserve a virtual address space.
81 * On success, returns the pointer. On failure, returns NULL.
82 */
83void *
84get_xen_virtual(size_t size, size_t page_sz)
85{
86	void *addr;
87	uintptr_t aligned_addr;
88
89	addr = mmap(NULL, size + page_sz, PROT_READ, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
90	if (addr == MAP_FAILED) {
91		RTE_LOG(ERR, PMD, "failed get a virtual area\n");
92		return NULL;
93	}
94
95	aligned_addr = RTE_ALIGN_CEIL((uintptr_t)addr, page_sz);
96	addr = (void *)(aligned_addr);
97
98	return addr;
99}
100
101/*
102 * Get the physical address for virtual memory starting at va.
103 */
104int
105get_phys_map(void *va, phys_addr_t pa[], uint32_t pg_num, uint32_t pg_sz)
106{
107	int32_t fd, rc = 0;
108	uint32_t i, nb;
109	off_t ofs;
110
111	ofs = (uintptr_t)va / pg_sz * sizeof(*pa);
112	nb = pg_num * sizeof(*pa);
113
114	if ((fd = open(PAGEMAP_FNAME, O_RDONLY)) < 0 ||
115			(rc = pread(fd, pa, nb, ofs)) < 0 ||
116			(rc -= nb) != 0) {
117		RTE_LOG(ERR, PMD, "%s: failed read of %u bytes from \'%s\' "
118			"at offset %lu, error code: %d\n",
119			__func__, nb, PAGEMAP_FNAME, (unsigned long)ofs, errno);
120		rc = ENOENT;
121	}
122
123	close(fd);
124	for (i = 0; i != pg_num; i++)
125		pa[i] = (pa[i] & PAGEMAP_PFN_MASK) * pg_sz;
126
127	return rc;
128}
129
130int
131gntalloc_open(void)
132{
133	gntalloc_fd = open(XEN_GNTALLOC_FNAME, O_RDWR);
134	return (gntalloc_fd != -1) ? 0 : -1;
135}
136
137void
138gntalloc_close(void)
139{
140	if (gntalloc_fd != -1)
141		close(gntalloc_fd);
142	gntalloc_fd = -1;
143}
144
145void *
146gntalloc(size_t size, uint32_t *gref, uint64_t *start_index)
147{
148	int page_size = getpagesize();
149	uint32_t i, pg_num;
150	void *va;
151	int rv;
152	struct ioctl_gntalloc_alloc_gref *arg;
153	struct ioctl_gntalloc_dealloc_gref arg_d;
154
155	if (size % page_size) {
156		RTE_LOG(ERR, PMD, "%s: %zu isn't multiple of page size\n",
157			__func__, size);
158		return NULL;
159	}
160
161	pg_num = size / page_size;
162	arg = malloc(sizeof(*arg) + (pg_num - 1) * sizeof(uint32_t));
163	if (arg == NULL)
164		return NULL;
165	arg->domid = DOM0_DOMID;
166	arg->flags = GNTALLOC_FLAG_WRITABLE;
167	arg->count = pg_num;
168
169	rv = ioctl(gntalloc_fd, IOCTL_GNTALLOC_ALLOC_GREF, arg);
170	if (rv) {
171		RTE_LOG(ERR, PMD, "%s: ioctl error\n", __func__);
172		free(arg);
173		return NULL;
174	}
175
176	va = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, gntalloc_fd, arg->index);
177	if (va == MAP_FAILED) {
178		RTE_LOG(ERR, PMD, "%s: mmap failed\n", __func__);
179		arg_d.count = pg_num;
180		arg_d.index = arg->index;
181		ioctl(gntalloc_fd, IOCTL_GNTALLOC_DEALLOC_GREF, arg_d);
182		free(arg);
183		return NULL;
184	}
185
186	if (gref) {
187		for (i = 0; i < pg_num; i++) {
188			gref[i] = arg->gref_ids[i];
189		}
190	}
191	if (start_index)
192		*start_index = arg->index;
193
194	free(arg);
195
196	return va;
197}
198
199int
200grefwatch_from_alloc(uint32_t *gref, void **pptr)
201{
202	int rv;
203	void *ptr;
204	int pg_size = getpagesize();
205	struct ioctl_gntalloc_alloc_gref arg = {
206		.domid = DOM0_DOMID,
207		.flags = GNTALLOC_FLAG_WRITABLE,
208		.count = 1
209	};
210	struct ioctl_gntalloc_dealloc_gref arg_d;
211	struct ioctl_gntalloc_unmap_notify notify = {
212		.action = UNMAP_NOTIFY_CLEAR_BYTE
213	};
214
215	rv = ioctl(gntalloc_fd, IOCTL_GNTALLOC_ALLOC_GREF, &arg);
216	if (rv) {
217		RTE_LOG(ERR, PMD, "%s: ioctl error\n", __func__);
218		return -1;
219	}
220
221	ptr = (void *)mmap(NULL, pg_size, PROT_READ|PROT_WRITE, MAP_SHARED, gntalloc_fd, arg.index);
222	arg_d.index = arg.index;
223	arg_d.count = 1;
224	if (ptr == MAP_FAILED) {
225		RTE_LOG(ERR, PMD, "%s: mmap failed\n", __func__);
226		ioctl(gntalloc_fd, IOCTL_GNTALLOC_DEALLOC_GREF, &arg_d);
227		return -1;
228	}
229	if (pptr)
230		*pptr = ptr;
231	if (gref)
232		*gref = arg.gref_ids[0];
233
234	notify.index = arg.index;
235	rv = ioctl(gntalloc_fd, IOCTL_GNTALLOC_SET_UNMAP_NOTIFY, &notify);
236	if (rv) {
237		RTE_LOG(ERR, PMD, "%s: unmap notify failed\n", __func__);
238		munmap(ptr, pg_size);
239		ioctl(gntalloc_fd, IOCTL_GNTALLOC_DEALLOC_GREF, &arg_d);
240		return -1;
241	}
242
243	return 0;
244}
245
246void
247gntfree(void *va, size_t sz, uint64_t start_index)
248{
249	struct ioctl_gntalloc_dealloc_gref arg_d;
250
251	if (va && sz) {
252		munmap(va, sz);
253		arg_d.count = sz / getpagesize();
254		arg_d.index = start_index;
255		ioctl(gntalloc_fd, IOCTL_GNTALLOC_DEALLOC_GREF, &arg_d);
256	}
257}
258
259static int
260xenstore_cleanup(void)
261{
262	char store_path[PATH_MAX] = {0};
263
264	if (snprintf(store_path, sizeof(store_path),
265		"%s%s", dompath, DPDK_XENSTORE_NODE) == -1)
266		return -1;
267
268	if (xs_rm(xs, XBT_NULL, store_path) == false) {
269		RTE_LOG(ERR, PMD, "%s: failed cleanup node\n", __func__);
270		return -1;
271	}
272
273	return 0;
274}
275
276int
277xenstore_init(void)
278{
279	unsigned int len, domid;
280	char *buf;
281	char *end;
282
283	xs = xs_domain_open();
284	if (xs == NULL) {
285		RTE_LOG(ERR, PMD,"%s: xs_domain_open failed\n", __func__);
286		return -1;
287	}
288	buf = xs_read(xs, XBT_NULL, "domid", &len);
289	if (buf == NULL) {
290		RTE_LOG(ERR, PMD, "%s: failed read domid\n", __func__);
291		return -1;
292	}
293	errno = 0;
294	domid = strtoul(buf, &end, 0);
295	if (errno != 0 || end == NULL || end == buf ||  domid == 0)
296		return -1;
297
298	RTE_LOG(INFO, PMD, "retrieved dom ID = %d\n", domid);
299
300	dompath = xs_get_domain_path(xs, domid);
301	if (dompath == NULL)
302		return -1;
303
304	xs_transaction_start(xs); /* When to stop transaction */
305
306	if (is_xenstore_cleaned_up == 0) {
307		if (xenstore_cleanup())
308			return -1;
309		is_xenstore_cleaned_up = 1;
310	}
311
312	return 0;
313}
314
315int
316xenstore_uninit(void)
317{
318	xs_close(xs);
319
320	if (is_xenstore_cleaned_up == 0) {
321		if (xenstore_cleanup())
322			return -1;
323		is_xenstore_cleaned_up = 1;
324	}
325	free(dompath);
326	dompath = NULL;
327
328	return 0;
329}
330
331int
332xenstore_write(const char *key_str, const char *val_str)
333{
334	char grant_path[PATH_MAX];
335	int rv, len;
336
337	if (xs == NULL) {
338		RTE_LOG(ERR, PMD, "%s: xenstore init failed\n", __func__);
339		return -1;
340	}
341	rv = snprintf(grant_path, sizeof(grant_path), "%s%s", dompath, key_str);
342	if (rv == -1) {
343		RTE_LOG(ERR, PMD, "%s: snprintf %s %s failed\n",
344			__func__, dompath, key_str);
345		return -1;
346	}
347	len = strnlen(val_str, PATH_MAX);
348
349	if (xs_write(xs, XBT_NULL, grant_path, val_str, len) == false) {
350		RTE_LOG(ERR, PMD, "%s: xs_write failed\n", __func__);
351		return -1;
352	}
353
354	return 0;
355}
356
357int
358grant_node_create(uint32_t pg_num, uint32_t *gref_arr, phys_addr_t *pa_arr, char *val_str, size_t str_size)
359{
360	uint64_t start_index;
361	int pg_size;
362	uint32_t pg_shift;
363	void *ptr = NULL;
364	uint32_t count, entries_per_pg;
365	uint32_t i, j = 0, k = 0;
366	uint32_t *gref_tmp;
367	int first = 1;
368	char tmp_str[PATH_MAX] = {0};
369	int rv = -1;
370
371	pg_size = getpagesize();
372	if (rte_is_power_of_2(pg_size) == 0) {
373		return -1;
374	}
375	pg_shift = rte_bsf32(pg_size);
376	if (pg_size % sizeof(struct grant_node_item)) {
377		RTE_LOG(ERR, PMD, "pg_size isn't a multiple of grant node item\n");
378		return -1;
379	}
380
381	entries_per_pg = pg_size / sizeof(struct grant_node_item);
382	count  = (pg_num +  entries_per_pg - 1 ) / entries_per_pg;
383	gref_tmp = malloc(count * sizeof(uint32_t));
384	if (gref_tmp == NULL)
385		return -1;
386	ptr = gntalloc(pg_size * count, gref_tmp, &start_index);
387	if (ptr == NULL) {
388		RTE_LOG(ERR, PMD, "%s: gntalloc error of %d pages\n", __func__, count);
389		free(gref_tmp);
390		return -1;
391	}
392
393	while (j < pg_num) {
394		if (first) {
395			rv = snprintf(val_str, str_size, "%u", gref_tmp[k]);
396			first = 0;
397		} else {
398			snprintf(tmp_str, PATH_MAX, "%s", val_str);
399			rv = snprintf(val_str, str_size, "%s,%u", tmp_str, gref_tmp[k]);
400		}
401		k++;
402		if (rv == -1)
403			break;
404
405		for (i = 0; i < entries_per_pg && j < pg_num ; i++) {
406			((struct grant_node_item *)ptr)->gref = gref_arr[j];
407			((struct grant_node_item *)ptr)->pfn =  pa_arr[j] >> pg_shift;
408			ptr = RTE_PTR_ADD(ptr, sizeof(struct grant_node_item));
409			j++;
410		}
411	}
412	if (rv == -1) {
413		gntfree(ptr, pg_size * count, start_index);
414	} else
415		rv = 0;
416	free(gref_tmp);
417	return rv;
418}
419
420
421int
422grant_gntalloc_mbuf_pool(struct rte_mempool *mpool, uint32_t pg_num, uint32_t *gref_arr, phys_addr_t *pa_arr, int mempool_idx)
423{
424	char key_str[PATH_MAX] = {0};
425	char val_str[PATH_MAX] = {0};
426	void *mempool_obj_va;
427
428	if (grant_node_create(pg_num, gref_arr, pa_arr, val_str, sizeof(val_str))) {
429		return -1;
430	}
431
432	if (snprintf(key_str, sizeof(key_str),
433		DPDK_XENSTORE_PATH"%d"MEMPOOL_XENSTORE_STR, mempool_idx) == -1)
434		return -1;
435	if (xenstore_write(key_str, val_str) == -1)
436		return -1;
437
438	if (snprintf(key_str, sizeof(key_str),
439		DPDK_XENSTORE_PATH"%d"MEMPOOL_VA_XENSTORE_STR, mempool_idx) == -1)
440		return -1;
441	if (mpool->nb_mem_chunks != 1) {
442		RTE_LOG(ERR, PMD,
443			"mempool with more than 1 chunk is not supported\n");
444		return -1;
445	}
446	mempool_obj_va = STAILQ_FIRST(&mpool->mem_list)->addr;
447	if (snprintf(val_str, sizeof(val_str), "%"PRIxPTR,
448			(uintptr_t)mempool_obj_va) == -1)
449		return -1;
450	if (xenstore_write(key_str, val_str) == -1)
451		return -1;
452
453	return 0;
454}
455