xref: /dpdk/drivers/bus/vmbus/vmbus_common_uio.c (revision aedd054c)
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