a6545b758e366d7d9b1d89b83ffc169e597b8915
[dpdk.git] / drivers / bus / vmbus / vmbus_common_uio.c
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 static int
27 vmbus_uio_map_secondary(struct rte_vmbus_device *dev)
28 {
29         int fd, i;
30         struct mapped_vmbus_resource *uio_res;
31         struct mapped_vmbus_res_list *uio_res_list
32                 = RTE_TAILQ_CAST(vmbus_tailq.head, mapped_vmbus_res_list);
33
34         TAILQ_FOREACH(uio_res, uio_res_list, next) {
35
36                 /* skip this element if it doesn't match our UUID */
37                 if (rte_uuid_compare(uio_res->id, dev->device_id) != 0)
38                         continue;
39
40                 /* open /dev/uioX */
41                 fd = open(uio_res->path, O_RDWR);
42                 if (fd < 0) {
43                         VMBUS_LOG(ERR, "Cannot open %s: %s",
44                                   uio_res->path, strerror(errno));
45                         return -1;
46                 }
47
48                 for (i = 0; i != uio_res->nb_maps; i++) {
49                         void *mapaddr;
50                         off_t offset = i * PAGE_SIZE;
51
52                         mapaddr = vmbus_map_resource(uio_res->maps[i].addr,
53                                                      fd, offset,
54                                                      uio_res->maps[i].size, 0);
55
56                         if (mapaddr == uio_res->maps[i].addr)
57                                 continue;
58
59                         VMBUS_LOG(ERR,
60                                   "Cannot mmap device resource file %s to address: %p",
61                                   uio_res->path, uio_res->maps[i].addr);
62
63                         if (mapaddr != MAP_FAILED)
64                                 /* unmap addr wrongly mapped */
65                                 vmbus_unmap_resource(mapaddr,
66                                                      (size_t)uio_res->maps[i].size);
67
68                         /* unmap addrs correctly mapped */
69                         while (--i >= 0)
70                                 vmbus_unmap_resource(uio_res->maps[i].addr,
71                                                      (size_t)uio_res->maps[i].size);
72
73                         close(fd);
74                         return -1;
75                 }
76
77                 /* fd is not needed in slave process, close it */
78                 close(fd);
79                 return 0;
80         }
81
82         VMBUS_LOG(ERR,  "Cannot find resource for device");
83         return 1;
84 }
85
86 static int
87 vmbus_uio_map_primary(struct rte_vmbus_device *dev)
88 {
89         int i, ret;
90         struct mapped_vmbus_resource *uio_res = NULL;
91         struct mapped_vmbus_res_list *uio_res_list =
92                 RTE_TAILQ_CAST(vmbus_tailq.head, mapped_vmbus_res_list);
93
94         /* allocate uio resource */
95         ret = vmbus_uio_alloc_resource(dev, &uio_res);
96         if (ret)
97                 return ret;
98
99         /* Map the resources */
100         for (i = 0; i < VMBUS_MAX_RESOURCE; i++) {
101                 /* stop at empty BAR */
102                 if (dev->resource[i].len == 0)
103                         break;
104
105                 ret = vmbus_uio_map_resource_by_index(dev, i, uio_res, 0);
106                 if (ret)
107                         goto error;
108         }
109
110         uio_res->nb_maps = i;
111
112         TAILQ_INSERT_TAIL(uio_res_list, uio_res, next);
113
114         return 0;
115 error:
116         while (--i >= 0) {
117                 vmbus_unmap_resource(uio_res->maps[i].addr,
118                                 (size_t)uio_res->maps[i].size);
119         }
120         vmbus_uio_free_resource(dev, uio_res);
121         return -1;
122 }
123
124
125 struct mapped_vmbus_resource *
126 vmbus_uio_find_resource(const struct rte_vmbus_device *dev)
127 {
128         struct mapped_vmbus_resource *uio_res;
129         struct mapped_vmbus_res_list *uio_res_list =
130                         RTE_TAILQ_CAST(vmbus_tailq.head, mapped_vmbus_res_list);
131
132         if (dev == NULL)
133                 return NULL;
134
135         TAILQ_FOREACH(uio_res, uio_res_list, next) {
136                 /* skip this element if it doesn't match our VMBUS address */
137                 if (rte_uuid_compare(uio_res->id, dev->device_id) == 0)
138                         return uio_res;
139         }
140         return NULL;
141 }
142
143 /* map the VMBUS resource of a VMBUS device in virtual memory */
144 int
145 vmbus_uio_map_resource(struct rte_vmbus_device *dev)
146 {
147         struct mapped_vmbus_resource *uio_res;
148         int ret;
149
150         /* TODO: handle rescind */
151         dev->intr_handle.fd = -1;
152         dev->intr_handle.uio_cfg_fd = -1;
153         dev->intr_handle.type = RTE_INTR_HANDLE_UNKNOWN;
154
155         /* secondary processes - use already recorded details */
156         if (rte_eal_process_type() != RTE_PROC_PRIMARY)
157                 ret = vmbus_uio_map_secondary(dev);
158         else
159                 ret = vmbus_uio_map_primary(dev);
160
161         if (ret != 0)
162                 return ret;
163
164         uio_res = vmbus_uio_find_resource(dev);
165         if (!uio_res) {
166                 VMBUS_LOG(ERR, "can not find resources!");
167                 return -EIO;
168         }
169
170         if (uio_res->nb_maps <= HV_MON_PAGE_MAP) {
171                 VMBUS_LOG(ERR, "VMBUS: only %u resources found!",
172                         uio_res->nb_maps);
173                 return -EINVAL;
174         }
175
176         dev->int_page = (uint32_t *)((char *)uio_res->maps[HV_INT_PAGE_MAP].addr
177                                      + (PAGE_SIZE >> 1));
178         dev->monitor_page = uio_res->maps[HV_MON_PAGE_MAP].addr;
179         return 0;
180 }
181
182 static void
183 vmbus_uio_unmap(struct mapped_vmbus_resource *uio_res)
184 {
185         int i;
186
187         if (uio_res == NULL)
188                 return;
189
190         for (i = 0; i != uio_res->nb_maps; i++) {
191                 vmbus_unmap_resource(uio_res->maps[i].addr,
192                                      (size_t)uio_res->maps[i].size);
193         }
194 }
195
196 /* unmap the VMBUS resource of a VMBUS device in virtual memory */
197 void
198 vmbus_uio_unmap_resource(struct rte_vmbus_device *dev)
199 {
200         struct mapped_vmbus_resource *uio_res;
201         struct mapped_vmbus_res_list *uio_res_list =
202                         RTE_TAILQ_CAST(vmbus_tailq.head, mapped_vmbus_res_list);
203
204         if (dev == NULL)
205                 return;
206
207         /* find an entry for the device */
208         uio_res = vmbus_uio_find_resource(dev);
209         if (uio_res == NULL)
210                 return;
211
212         /* secondary processes - just free maps */
213         if (rte_eal_process_type() != RTE_PROC_PRIMARY)
214                 return vmbus_uio_unmap(uio_res);
215
216         TAILQ_REMOVE(uio_res_list, uio_res, next);
217
218         /* unmap all resources */
219         vmbus_uio_unmap(uio_res);
220
221         /* free uio resource */
222         rte_free(uio_res);
223
224         /* close fd if in primary process */
225         close(dev->intr_handle.fd);
226         if (dev->intr_handle.uio_cfg_fd >= 0) {
227                 close(dev->intr_handle.uio_cfg_fd);
228                 dev->intr_handle.uio_cfg_fd = -1;
229         }
230
231         dev->intr_handle.fd = -1;
232         dev->intr_handle.type = RTE_INTR_HANDLE_UNKNOWN;
233 }