1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2018, Microsoft Corporation.
3 * All Rights Reserved.
4 */
5
6 #include <fcntl.h>
7 #include <string.h>
8 #include <unistd.h>
9 #include <sys/types.h>
10 #include <sys/mman.h>
11
12 #include <rte_eal.h>
13 #include <rte_tailq.h>
14 #include <rte_log.h>
15 #include <rte_malloc.h>
16 #include <rte_bus_vmbus.h>
17
18 #include "private.h"
19
20 static struct rte_tailq_elem vmbus_tailq = {
21 .name = "VMBUS_RESOURCE_LIST",
22 };
EAL_REGISTER_TAILQ(vmbus_tailq)23 EAL_REGISTER_TAILQ(vmbus_tailq)
24
25 struct mapped_vmbus_resource *
26 vmbus_uio_find_resource(const struct rte_vmbus_device *dev)
27 {
28 struct mapped_vmbus_resource *uio_res;
29 struct mapped_vmbus_res_list *uio_res_list =
30 RTE_TAILQ_CAST(vmbus_tailq.head, mapped_vmbus_res_list);
31
32 if (dev == NULL)
33 return NULL;
34
35 TAILQ_FOREACH(uio_res, uio_res_list, next) {
36 if (rte_uuid_compare(uio_res->id, dev->device_id) == 0)
37 return uio_res;
38 }
39 return NULL;
40 }
41
42 static int
vmbus_uio_map_secondary(struct rte_vmbus_device * dev)43 vmbus_uio_map_secondary(struct rte_vmbus_device *dev)
44 {
45 struct mapped_vmbus_resource *uio_res;
46 struct vmbus_channel *chan;
47 int fd, i;
48
49 uio_res = vmbus_uio_find_resource(dev);
50 if (!uio_res) {
51 VMBUS_LOG(ERR, "Cannot find resource for device");
52 return -1;
53 }
54
55 /* open /dev/uioX */
56 fd = open(uio_res->path, O_RDWR);
57 if (fd < 0) {
58 VMBUS_LOG(ERR, "Cannot open %s: %s",
59 uio_res->path, strerror(errno));
60 return -1;
61 }
62
63 for (i = 0; i != uio_res->nb_maps; i++) {
64 void *mapaddr;
65 off_t offset = i * rte_mem_page_size();
66
67 mapaddr = vmbus_map_resource(uio_res->maps[i].addr,
68 fd, offset,
69 uio_res->maps[i].size, 0);
70
71 if (mapaddr == uio_res->maps[i].addr) {
72 dev->resource[i].addr = mapaddr;
73 continue; /* successful map */
74 }
75
76 if (mapaddr == MAP_FAILED)
77 VMBUS_LOG(ERR,
78 "mmap resource %d in secondary failed", i);
79 else {
80 VMBUS_LOG(ERR,
81 "mmap resource %d address mismatch", i);
82 vmbus_unmap_resource(mapaddr, uio_res->maps[i].size);
83 }
84
85 close(fd);
86 return -1;
87 }
88
89 /* fd is not needed in secondary process, close it */
90 close(fd);
91
92 /* Create and map primary channel */
93 if (vmbus_chan_create(dev, dev->relid, 0,
94 dev->monitor_id, &dev->primary)) {
95 VMBUS_LOG(ERR, "cannot create primary channel");
96 goto failed_primary;
97 }
98
99 /* Create and map sub channels */
100 for (i = 0; i < uio_res->nb_subchannels; i++) {
101 if (rte_vmbus_subchan_open(dev->primary, &chan)) {
102 VMBUS_LOG(ERR,
103 "failed to create subchannel at index %d", i);
104 goto failed_secondary;
105 }
106 }
107
108 return 0;
109
110 failed_secondary:
111 while (!STAILQ_EMPTY(&dev->primary->subchannel_list)) {
112 chan = STAILQ_FIRST(&dev->primary->subchannel_list);
113 vmbus_unmap_resource(chan->txbr.vbr, chan->txbr.dsize * 2);
114 rte_vmbus_chan_close(chan);
115 }
116 rte_vmbus_chan_close(dev->primary);
117
118 failed_primary:
119 for (i = 0; i != uio_res->nb_maps; i++) {
120 vmbus_unmap_resource(
121 uio_res->maps[i].addr, uio_res->maps[i].size);
122 }
123
124 return -1;
125 }
126
127 static int
vmbus_uio_map_primary(struct rte_vmbus_device * dev)128 vmbus_uio_map_primary(struct rte_vmbus_device *dev)
129 {
130 int i, ret;
131 struct mapped_vmbus_resource *uio_res = NULL;
132 struct mapped_vmbus_res_list *uio_res_list =
133 RTE_TAILQ_CAST(vmbus_tailq.head, mapped_vmbus_res_list);
134
135 /* allocate uio resource */
136 ret = vmbus_uio_alloc_resource(dev, &uio_res);
137 if (ret)
138 return ret;
139
140 /* Map the resources */
141 for (i = 0; i < VMBUS_MAX_RESOURCE; i++) {
142 /* stop at empty BAR */
143 if (dev->resource[i].len == 0)
144 break;
145
146 ret = vmbus_uio_map_resource_by_index(dev, i, uio_res, 0);
147 if (ret)
148 goto error;
149 }
150
151 uio_res->nb_maps = i;
152
153 TAILQ_INSERT_TAIL(uio_res_list, uio_res, next);
154
155 return 0;
156 error:
157 while (--i >= 0) {
158 vmbus_unmap_resource(uio_res->maps[i].addr,
159 (size_t)uio_res->maps[i].size);
160 }
161 vmbus_uio_free_resource(dev, uio_res);
162 return -1;
163 }
164
165 /* map the VMBUS resource of a VMBUS device in virtual memory */
166 int
vmbus_uio_map_resource(struct rte_vmbus_device * dev)167 vmbus_uio_map_resource(struct rte_vmbus_device *dev)
168 {
169 struct mapped_vmbus_resource *uio_res;
170 int ret;
171
172 /* TODO: handle rescind */
173 if (rte_intr_fd_set(dev->intr_handle, -1))
174 return -1;
175
176 if (rte_intr_dev_fd_set(dev->intr_handle, -1))
177 return -1;
178
179 if (rte_intr_type_set(dev->intr_handle, RTE_INTR_HANDLE_UNKNOWN))
180 return -1;
181
182 /* secondary processes - use already recorded details */
183 if (rte_eal_process_type() != RTE_PROC_PRIMARY)
184 ret = vmbus_uio_map_secondary(dev);
185 else
186 ret = vmbus_uio_map_primary(dev);
187
188 if (ret != 0)
189 return ret;
190
191 uio_res = vmbus_uio_find_resource(dev);
192 if (!uio_res) {
193 VMBUS_LOG(ERR, "can not find resources!");
194 return -EIO;
195 }
196
197 if (uio_res->nb_maps <= HV_MON_PAGE_MAP) {
198 VMBUS_LOG(ERR, "VMBUS: only %u resources found!",
199 uio_res->nb_maps);
200 return -EINVAL;
201 }
202
203 dev->int_page = (uint32_t *)((char *)uio_res->maps[HV_INT_PAGE_MAP].addr
204 + (rte_mem_page_size() >> 1));
205 dev->monitor_page = uio_res->maps[HV_MON_PAGE_MAP].addr;
206 return 0;
207 }
208
209 static void
vmbus_uio_unmap(struct mapped_vmbus_resource * uio_res)210 vmbus_uio_unmap(struct mapped_vmbus_resource *uio_res)
211 {
212 int i;
213
214 if (uio_res == NULL)
215 return;
216
217 for (i = 0; i < uio_res->nb_subchannels; i++) {
218 vmbus_unmap_resource(uio_res->subchannel_maps[i].addr,
219 uio_res->subchannel_maps[i].size);
220 }
221
222 for (i = 0; i != uio_res->nb_maps; i++) {
223 vmbus_unmap_resource(uio_res->maps[i].addr,
224 (size_t)uio_res->maps[i].size);
225 }
226 }
227
228 /* unmap the VMBUS resource of a VMBUS device in virtual memory */
229 void
vmbus_uio_unmap_resource(struct rte_vmbus_device * dev)230 vmbus_uio_unmap_resource(struct rte_vmbus_device *dev)
231 {
232 struct mapped_vmbus_resource *uio_res;
233 struct mapped_vmbus_res_list *uio_res_list =
234 RTE_TAILQ_CAST(vmbus_tailq.head, mapped_vmbus_res_list);
235
236 if (dev == NULL)
237 return;
238
239 /* find an entry for the device */
240 uio_res = vmbus_uio_find_resource(dev);
241 if (uio_res == NULL)
242 return;
243
244 /* secondary processes - just free maps */
245 if (rte_eal_process_type() != RTE_PROC_PRIMARY) {
246 vmbus_uio_unmap(uio_res);
247 rte_free(dev->primary);
248 return;
249 }
250
251 TAILQ_REMOVE(uio_res_list, uio_res, next);
252
253 /* unmap all resources */
254 vmbus_uio_unmap(uio_res);
255
256 /* free uio resource */
257 rte_free(uio_res);
258
259 /* close fd if in primary process */
260 if (rte_intr_fd_get(dev->intr_handle) >= 0)
261 close(rte_intr_fd_get(dev->intr_handle));
262
263 if (rte_intr_dev_fd_get(dev->intr_handle) >= 0) {
264 close(rte_intr_dev_fd_get(dev->intr_handle));
265 rte_intr_dev_fd_set(dev->intr_handle, -1);
266 }
267
268 rte_intr_fd_set(dev->intr_handle, -1);
269 rte_intr_type_set(dev->intr_handle, RTE_INTR_HANDLE_UNKNOWN);
270 }
271