xref: /dpdk/drivers/bus/vmbus/vmbus_common_uio.c (revision 68a03efeed657e6e05f281479b33b51102797e15)
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 };
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
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 			continue;	/* successful map */
74 
75 		if (mapaddr == MAP_FAILED)
76 			VMBUS_LOG(ERR,
77 				  "mmap resource %d in secondary failed", i);
78 		else {
79 			VMBUS_LOG(ERR,
80 				  "mmap resource %d address mismatch", i);
81 			vmbus_unmap_resource(mapaddr, uio_res->maps[i].size);
82 		}
83 
84 		close(fd);
85 		return -1;
86 	}
87 
88 	/* fd is not needed in secondary process, close it */
89 	close(fd);
90 
91 	dev->primary = uio_res->primary;
92 	if (!dev->primary) {
93 		VMBUS_LOG(ERR, "missing primary channel");
94 		return -1;
95 	}
96 
97 	STAILQ_FOREACH(chan, &dev->primary->subchannel_list, next) {
98 		if (vmbus_uio_map_secondary_subchan(dev, chan) != 0) {
99 			VMBUS_LOG(ERR, "cannot map secondary subchan");
100 			return -1;
101 		}
102 	}
103 	return 0;
104 }
105 
106 static int
107 vmbus_uio_map_primary(struct rte_vmbus_device *dev)
108 {
109 	int i, ret;
110 	struct mapped_vmbus_resource *uio_res = NULL;
111 	struct mapped_vmbus_res_list *uio_res_list =
112 		RTE_TAILQ_CAST(vmbus_tailq.head, mapped_vmbus_res_list);
113 
114 	/* allocate uio resource */
115 	ret = vmbus_uio_alloc_resource(dev, &uio_res);
116 	if (ret)
117 		return ret;
118 
119 	/* Map the resources */
120 	for (i = 0; i < VMBUS_MAX_RESOURCE; i++) {
121 		/* stop at empty BAR */
122 		if (dev->resource[i].len == 0)
123 			break;
124 
125 		ret = vmbus_uio_map_resource_by_index(dev, i, uio_res, 0);
126 		if (ret)
127 			goto error;
128 	}
129 
130 	uio_res->nb_maps = i;
131 
132 	TAILQ_INSERT_TAIL(uio_res_list, uio_res, next);
133 
134 	return 0;
135 error:
136 	while (--i >= 0) {
137 		vmbus_unmap_resource(uio_res->maps[i].addr,
138 				(size_t)uio_res->maps[i].size);
139 	}
140 	vmbus_uio_free_resource(dev, uio_res);
141 	return -1;
142 }
143 
144 /* map the VMBUS resource of a VMBUS device in virtual memory */
145 int
146 vmbus_uio_map_resource(struct rte_vmbus_device *dev)
147 {
148 	struct mapped_vmbus_resource *uio_res;
149 	int ret;
150 
151 	/* TODO: handle rescind */
152 	dev->intr_handle.fd = -1;
153 	dev->intr_handle.uio_cfg_fd = -1;
154 	dev->intr_handle.type = RTE_INTR_HANDLE_UNKNOWN;
155 
156 	/* secondary processes - use already recorded details */
157 	if (rte_eal_process_type() != RTE_PROC_PRIMARY)
158 		ret = vmbus_uio_map_secondary(dev);
159 	else
160 		ret = vmbus_uio_map_primary(dev);
161 
162 	if (ret != 0)
163 		return ret;
164 
165 	uio_res = vmbus_uio_find_resource(dev);
166 	if (!uio_res) {
167 		VMBUS_LOG(ERR, "can not find resources!");
168 		return -EIO;
169 	}
170 
171 	if (uio_res->nb_maps <= HV_MON_PAGE_MAP) {
172 		VMBUS_LOG(ERR, "VMBUS: only %u resources found!",
173 			uio_res->nb_maps);
174 		return -EINVAL;
175 	}
176 
177 	dev->int_page = (uint32_t *)((char *)uio_res->maps[HV_INT_PAGE_MAP].addr
178 				     + (rte_mem_page_size() >> 1));
179 	dev->monitor_page = uio_res->maps[HV_MON_PAGE_MAP].addr;
180 	return 0;
181 }
182 
183 static void
184 vmbus_uio_unmap(struct mapped_vmbus_resource *uio_res)
185 {
186 	int i;
187 
188 	if (uio_res == NULL)
189 		return;
190 
191 	for (i = 0; i != uio_res->nb_maps; i++) {
192 		vmbus_unmap_resource(uio_res->maps[i].addr,
193 				     (size_t)uio_res->maps[i].size);
194 	}
195 }
196 
197 /* unmap the VMBUS resource of a VMBUS device in virtual memory */
198 void
199 vmbus_uio_unmap_resource(struct rte_vmbus_device *dev)
200 {
201 	struct mapped_vmbus_resource *uio_res;
202 	struct mapped_vmbus_res_list *uio_res_list =
203 			RTE_TAILQ_CAST(vmbus_tailq.head, mapped_vmbus_res_list);
204 
205 	if (dev == NULL)
206 		return;
207 
208 	/* find an entry for the device */
209 	uio_res = vmbus_uio_find_resource(dev);
210 	if (uio_res == NULL)
211 		return;
212 
213 	/* secondary processes - just free maps */
214 	if (rte_eal_process_type() != RTE_PROC_PRIMARY)
215 		return vmbus_uio_unmap(uio_res);
216 
217 	TAILQ_REMOVE(uio_res_list, uio_res, next);
218 
219 	/* unmap all resources */
220 	vmbus_uio_unmap(uio_res);
221 
222 	/* free uio resource */
223 	rte_free(uio_res);
224 
225 	/* close fd if in primary process */
226 	close(dev->intr_handle.fd);
227 	if (dev->intr_handle.uio_cfg_fd >= 0) {
228 		close(dev->intr_handle.uio_cfg_fd);
229 		dev->intr_handle.uio_cfg_fd = -1;
230 	}
231 
232 	dev->intr_handle.fd = -1;
233 	dev->intr_handle.type = RTE_INTR_HANDLE_UNKNOWN;
234 }
235