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