vhost: copy host memory mapping to a new cuse file
[dpdk.git] / lib / librte_vhost / vhost_cuse / virtio-net-cdev.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2010-2014 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 <dirent.h>
36 #include <linux/vhost.h>
37 #include <linux/virtio_net.h>
38 #include <fuse/cuse_lowlevel.h>
39 #include <stddef.h>
40 #include <string.h>
41 #include <stdlib.h>
42 #include <sys/eventfd.h>
43 #include <sys/mman.h>
44 #include <sys/types.h>
45 #include <unistd.h>
46 #include <errno.h>
47
48 #include <rte_log.h>
49
50 #include "vhost-net.h"
51
52 /* Line size for reading maps file. */
53 static const uint32_t BUFSIZE = PATH_MAX;
54
55 /* Size of prot char array in procmap. */
56 #define PROT_SZ 5
57
58 /* Number of elements in procmap struct. */
59 #define PROCMAP_SZ 8
60
61 /* Structure containing information gathered from maps file. */
62 struct procmap {
63         uint64_t va_start;      /* Start virtual address in file. */
64         uint64_t len;           /* Size of file. */
65         uint64_t pgoff;         /* Not used. */
66         uint32_t maj;           /* Not used. */
67         uint32_t min;           /* Not used. */
68         uint32_t ino;           /* Not used. */
69         char prot[PROT_SZ];     /* Not used. */
70         char fname[PATH_MAX];   /* File name. */
71 };
72
73 /*
74  * Locate the file containing QEMU's memory space and
75  * map it to our address space.
76  */
77 static int
78 host_memory_map(struct virtio_net *dev, struct virtio_memory *mem,
79         pid_t pid, uint64_t addr)
80 {
81         struct dirent *dptr = NULL;
82         struct procmap procmap;
83         DIR *dp = NULL;
84         int fd;
85         int i;
86         char memfile[PATH_MAX];
87         char mapfile[PATH_MAX];
88         char procdir[PATH_MAX];
89         char resolved_path[PATH_MAX];
90         char *path = NULL;
91         FILE *fmap;
92         void *map;
93         uint8_t found = 0;
94         char line[BUFSIZE];
95         char dlm[] = "-   :   ";
96         char *str, *sp, *in[PROCMAP_SZ];
97         char *end = NULL;
98
99         /* Path where mem files are located. */
100         snprintf(procdir, PATH_MAX, "/proc/%u/fd/", pid);
101         /* Maps file used to locate mem file. */
102         snprintf(mapfile, PATH_MAX, "/proc/%u/maps", pid);
103
104         fmap = fopen(mapfile, "r");
105         if (fmap == NULL) {
106                 RTE_LOG(ERR, VHOST_CONFIG,
107                         "(%"PRIu64") Failed to open maps file for pid %d\n",
108                         dev->device_fh, pid);
109                 return -1;
110         }
111
112         /* Read through maps file until we find out base_address. */
113         while (fgets(line, BUFSIZE, fmap) != 0) {
114                 str = line;
115                 errno = 0;
116                 /* Split line into fields. */
117                 for (i = 0; i < PROCMAP_SZ; i++) {
118                         in[i] = strtok_r(str, &dlm[i], &sp);
119                         if ((in[i] == NULL) || (errno != 0)) {
120                                 fclose(fmap);
121                                 return -1;
122                         }
123                         str = NULL;
124                 }
125
126                 /* Convert/Copy each field as needed. */
127                 procmap.va_start = strtoull(in[0], &end, 16);
128                 if ((in[0] == '\0') || (end == NULL) || (*end != '\0') ||
129                         (errno != 0)) {
130                         fclose(fmap);
131                         return -1;
132                 }
133
134                 procmap.len = strtoull(in[1], &end, 16);
135                 if ((in[1] == '\0') || (end == NULL) || (*end != '\0') ||
136                         (errno != 0)) {
137                         fclose(fmap);
138                         return -1;
139                 }
140
141                 procmap.pgoff = strtoull(in[3], &end, 16);
142                 if ((in[3] == '\0') || (end == NULL) || (*end != '\0') ||
143                         (errno != 0)) {
144                         fclose(fmap);
145                         return -1;
146                 }
147
148                 procmap.maj = strtoul(in[4], &end, 16);
149                 if ((in[4] == '\0') || (end == NULL) || (*end != '\0') ||
150                         (errno != 0)) {
151                         fclose(fmap);
152                         return -1;
153                 }
154
155                 procmap.min = strtoul(in[5], &end, 16);
156                 if ((in[5] == '\0') || (end == NULL) || (*end != '\0') ||
157                         (errno != 0)) {
158                         fclose(fmap);
159                         return -1;
160                 }
161
162                 procmap.ino = strtoul(in[6], &end, 16);
163                 if ((in[6] == '\0') || (end == NULL) || (*end != '\0') ||
164                         (errno != 0)) {
165                         fclose(fmap);
166                         return -1;
167                 }
168
169                 memcpy(&procmap.prot, in[2], PROT_SZ);
170                 memcpy(&procmap.fname, in[7], PATH_MAX);
171
172                 if (procmap.va_start == addr) {
173                         procmap.len = procmap.len - procmap.va_start;
174                         found = 1;
175                         break;
176                 }
177         }
178         fclose(fmap);
179
180         if (!found) {
181                 RTE_LOG(ERR, VHOST_CONFIG,
182                         "(%"PRIu64") Failed to find memory file in pid %d maps file\n",
183                         dev->device_fh, pid);
184                 return -1;
185         }
186
187         /* Find the guest memory file among the process fds. */
188         dp = opendir(procdir);
189         if (dp == NULL) {
190                 RTE_LOG(ERR, VHOST_CONFIG,
191                         "(%"PRIu64") Cannot open pid %d process directory\n",
192                         dev->device_fh, pid);
193                 return -1;
194         }
195
196         found = 0;
197
198         /* Read the fd directory contents. */
199         while (NULL != (dptr = readdir(dp))) {
200                 snprintf(memfile, PATH_MAX, "/proc/%u/fd/%s",
201                                 pid, dptr->d_name);
202                 path = realpath(memfile, resolved_path);
203                 if ((path == NULL) && (strlen(resolved_path) == 0)) {
204                         RTE_LOG(ERR, VHOST_CONFIG,
205                                 "(%"PRIu64") Failed to resolve fd directory\n",
206                                 dev->device_fh);
207                         closedir(dp);
208                         return -1;
209                 }
210                 if (strncmp(resolved_path, procmap.fname,
211                         strnlen(procmap.fname, PATH_MAX)) == 0) {
212                         found = 1;
213                         break;
214                 }
215         }
216
217         closedir(dp);
218
219         if (found == 0) {
220                 RTE_LOG(ERR, VHOST_CONFIG,
221                         "(%"PRIu64") Failed to find memory file for pid %d\n",
222                         dev->device_fh, pid);
223                 return -1;
224         }
225         /* Open the shared memory file and map the memory into this process. */
226         fd = open(memfile, O_RDWR);
227
228         if (fd == -1) {
229                 RTE_LOG(ERR, VHOST_CONFIG,
230                         "(%"PRIu64") Failed to open %s for pid %d\n",
231                         dev->device_fh, memfile, pid);
232                 return -1;
233         }
234
235         map = mmap(0, (size_t)procmap.len, PROT_READ|PROT_WRITE,
236                 MAP_POPULATE|MAP_SHARED, fd, 0);
237         close(fd);
238
239         if (map == MAP_FAILED) {
240                 RTE_LOG(ERR, VHOST_CONFIG,
241                         "(%"PRIu64") Error mapping the file %s for pid %d\n",
242                         dev->device_fh, memfile, pid);
243                 return -1;
244         }
245
246         /* Store the memory address and size in the device data structure */
247         mem->mapped_address = (uint64_t)(uintptr_t)map;
248         mem->mapped_size = procmap.len;
249
250         LOG_DEBUG(VHOST_CONFIG,
251                 "(%"PRIu64") Mem File: %s->%s - Size: %llu - VA: %p\n",
252                 dev->device_fh,
253                 memfile, resolved_path,
254                 (unsigned long long)mem->mapped_size, map);
255
256         return 0;
257 }