]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | */ | |
64 | struct 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*/ | |
70 | int gntalloc_fd = -1; | |
71 | ||
72 | /* xenstore path for local domain, now it is '/local/domain/domid/' */ | |
73 | static char *dompath = NULL; | |
74 | /* handle to xenstore read/write operations */ | |
75 | static struct xs_handle *xs = NULL; | |
76 | /* flag to indicate if xenstore cleanup is required */ | |
77 | static bool is_xenstore_cleaned_up; | |
78 | ||
79 | /* | |
80 | * Reserve a virtual address space. | |
81 | * On success, returns the pointer. On failure, returns NULL. | |
82 | */ | |
83 | void * | |
84 | get_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 | */ | |
104 | int | |
105 | get_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 | ||
130 | int | |
131 | gntalloc_open(void) | |
132 | { | |
133 | gntalloc_fd = open(XEN_GNTALLOC_FNAME, O_RDWR); | |
134 | return (gntalloc_fd != -1) ? 0 : -1; | |
135 | } | |
136 | ||
137 | void | |
138 | gntalloc_close(void) | |
139 | { | |
140 | if (gntalloc_fd != -1) | |
141 | close(gntalloc_fd); | |
142 | gntalloc_fd = -1; | |
143 | } | |
144 | ||
145 | void * | |
146 | gntalloc(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 | ||
199 | int | |
200 | grefwatch_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, ¬ify); | |
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 | ||
246 | void | |
247 | gntfree(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 | ||
259 | static int | |
260 | xenstore_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 | ||
276 | int | |
277 | xenstore_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 | ||
315 | int | |
316 | xenstore_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 | ||
331 | int | |
332 | xenstore_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 | ||
357 | int | |
358 | grant_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 | ||
421 | int | |
422 | grant_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 | } |