]>
Commit | Line | Data |
---|---|---|
919ae3dd YS |
1 | /* |
2 | * QEMU paravirtual RDMA | |
3 | * | |
4 | * Copyright (C) 2018 Oracle | |
5 | * Copyright (C) 2018 Red Hat Inc | |
6 | * | |
7 | * Authors: | |
8 | * Yuval Shaia <yuval.shaia@oracle.com> | |
9 | * Marcel Apfelbaum <marcel@redhat.com> | |
10 | * | |
11 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
12 | * See the COPYING file in the top-level directory. | |
13 | * | |
14 | */ | |
15 | ||
0efc9511 MT |
16 | #include "qemu/osdep.h" |
17 | #include "qapi/error.h" | |
0b8fa32f | 18 | #include "qemu/module.h" |
0efc9511 MT |
19 | #include "hw/pci/pci.h" |
20 | #include "hw/pci/pci_ids.h" | |
21 | #include "hw/pci/msi.h" | |
22 | #include "hw/pci/msix.h" | |
0efc9511 | 23 | #include "hw/qdev-properties.h" |
ce35e229 | 24 | #include "hw/qdev-properties-system.h" |
0efc9511 | 25 | #include "cpu.h" |
919ae3dd | 26 | #include "trace.h" |
f4b2c02a YS |
27 | #include "monitor/monitor.h" |
28 | #include "hw/rdma/rdma.h" | |
919ae3dd YS |
29 | |
30 | #include "../rdma_rm.h" | |
31 | #include "../rdma_backend.h" | |
32 | #include "../rdma_utils.h" | |
33 | ||
34 | #include <infiniband/verbs.h> | |
35 | #include "pvrdma.h" | |
0efc9511 | 36 | #include "standard-headers/rdma/vmw_pvrdma-abi.h" |
54d31236 | 37 | #include "sysemu/runstate.h" |
0efc9511 | 38 | #include "standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h" |
919ae3dd YS |
39 | #include "pvrdma_qp_ops.h" |
40 | ||
41 | static Property pvrdma_dev_properties[] = { | |
2b05705d YS |
42 | DEFINE_PROP_STRING("netdev", PVRDMADev, backend_eth_device_name), |
43 | DEFINE_PROP_STRING("ibdev", PVRDMADev, backend_device_name), | |
44 | DEFINE_PROP_UINT8("ibport", PVRDMADev, backend_port_num, 1), | |
919ae3dd YS |
45 | DEFINE_PROP_UINT64("dev-caps-max-mr-size", PVRDMADev, dev_attr.max_mr_size, |
46 | MAX_MR_SIZE), | |
47 | DEFINE_PROP_INT32("dev-caps-max-qp", PVRDMADev, dev_attr.max_qp, MAX_QP), | |
919ae3dd YS |
48 | DEFINE_PROP_INT32("dev-caps-max-cq", PVRDMADev, dev_attr.max_cq, MAX_CQ), |
49 | DEFINE_PROP_INT32("dev-caps-max-mr", PVRDMADev, dev_attr.max_mr, MAX_MR), | |
50 | DEFINE_PROP_INT32("dev-caps-max-pd", PVRDMADev, dev_attr.max_pd, MAX_PD), | |
51 | DEFINE_PROP_INT32("dev-caps-qp-rd-atom", PVRDMADev, dev_attr.max_qp_rd_atom, | |
52 | MAX_QP_RD_ATOM), | |
53 | DEFINE_PROP_INT32("dev-caps-max-qp-init-rd-atom", PVRDMADev, | |
54 | dev_attr.max_qp_init_rd_atom, MAX_QP_INIT_RD_ATOM), | |
55 | DEFINE_PROP_INT32("dev-caps-max-ah", PVRDMADev, dev_attr.max_ah, MAX_AH), | |
355b7cf3 | 56 | DEFINE_PROP_INT32("dev-caps-max-srq", PVRDMADev, dev_attr.max_srq, MAX_SRQ), |
605ec166 | 57 | DEFINE_PROP_CHR("mad-chardev", PVRDMADev, mad_chr), |
919ae3dd YS |
58 | DEFINE_PROP_END_OF_LIST(), |
59 | }; | |
60 | ||
8dbbca5c | 61 | static void pvrdma_format_statistics(RdmaProvider *obj, GString *buf) |
f4b2c02a YS |
62 | { |
63 | PVRDMADev *dev = PVRDMA_DEV(obj); | |
64 | PCIDevice *pdev = PCI_DEVICE(dev); | |
65 | ||
8dbbca5c DB |
66 | g_string_append_printf(buf, "%s, %x.%x\n", |
67 | pdev->name, PCI_SLOT(pdev->devfn), | |
68 | PCI_FUNC(pdev->devfn)); | |
69 | g_string_append_printf(buf, "\tcommands : %" PRId64 "\n", | |
70 | dev->stats.commands); | |
71 | g_string_append_printf(buf, "\tregs_reads : %" PRId64 "\n", | |
72 | dev->stats.regs_reads); | |
73 | g_string_append_printf(buf, "\tregs_writes : %" PRId64 "\n", | |
74 | dev->stats.regs_writes); | |
75 | g_string_append_printf(buf, "\tuar_writes : %" PRId64 "\n", | |
76 | dev->stats.uar_writes); | |
77 | g_string_append_printf(buf, "\tinterrupts : %" PRId64 "\n", | |
78 | dev->stats.interrupts); | |
79 | rdma_format_device_counters(&dev->rdma_dev_res, buf); | |
f4b2c02a YS |
80 | } |
81 | ||
919ae3dd YS |
82 | static void free_dev_ring(PCIDevice *pci_dev, PvrdmaRing *ring, |
83 | void *ring_state) | |
84 | { | |
85 | pvrdma_ring_free(ring); | |
86 | rdma_pci_dma_unmap(pci_dev, ring_state, TARGET_PAGE_SIZE); | |
87 | } | |
88 | ||
3aa1b7af | 89 | static int init_dev_ring(PvrdmaRing *ring, PvrdmaRingState **ring_state, |
919ae3dd YS |
90 | const char *name, PCIDevice *pci_dev, |
91 | dma_addr_t dir_addr, uint32_t num_pages) | |
92 | { | |
93 | uint64_t *dir, *tbl; | |
94 | int rc = 0; | |
95 | ||
32e5703c MA |
96 | if (!num_pages) { |
97 | rdma_error_report("Ring pages count must be strictly positive"); | |
98 | return -EINVAL; | |
99 | } | |
100 | ||
919ae3dd YS |
101 | dir = rdma_pci_dma_map(pci_dev, dir_addr, TARGET_PAGE_SIZE); |
102 | if (!dir) { | |
4d71b38a | 103 | rdma_error_report("Failed to map to page directory (ring %s)", name); |
919ae3dd YS |
104 | rc = -ENOMEM; |
105 | goto out; | |
106 | } | |
107 | tbl = rdma_pci_dma_map(pci_dev, dir[0], TARGET_PAGE_SIZE); | |
108 | if (!tbl) { | |
4d71b38a | 109 | rdma_error_report("Failed to map to page table (ring %s)", name); |
919ae3dd YS |
110 | rc = -ENOMEM; |
111 | goto out_free_dir; | |
112 | } | |
113 | ||
114 | *ring_state = rdma_pci_dma_map(pci_dev, tbl[0], TARGET_PAGE_SIZE); | |
115 | if (!*ring_state) { | |
4d71b38a | 116 | rdma_error_report("Failed to map to ring state (ring %s)", name); |
919ae3dd YS |
117 | rc = -ENOMEM; |
118 | goto out_free_tbl; | |
119 | } | |
120 | /* RX ring is the second */ | |
197053e2 | 121 | (*ring_state)++; |
919ae3dd | 122 | rc = pvrdma_ring_init(ring, name, pci_dev, |
3aa1b7af | 123 | (PvrdmaRingState *)*ring_state, |
919ae3dd YS |
124 | (num_pages - 1) * TARGET_PAGE_SIZE / |
125 | sizeof(struct pvrdma_cqne), | |
126 | sizeof(struct pvrdma_cqne), | |
127 | (dma_addr_t *)&tbl[1], (dma_addr_t)num_pages - 1); | |
128 | if (rc) { | |
919ae3dd YS |
129 | rc = -ENOMEM; |
130 | goto out_free_ring_state; | |
131 | } | |
132 | ||
133 | goto out_free_tbl; | |
134 | ||
135 | out_free_ring_state: | |
136 | rdma_pci_dma_unmap(pci_dev, *ring_state, TARGET_PAGE_SIZE); | |
137 | ||
138 | out_free_tbl: | |
139 | rdma_pci_dma_unmap(pci_dev, tbl, TARGET_PAGE_SIZE); | |
140 | ||
141 | out_free_dir: | |
142 | rdma_pci_dma_unmap(pci_dev, dir, TARGET_PAGE_SIZE); | |
143 | ||
144 | out: | |
145 | return rc; | |
146 | } | |
147 | ||
148 | static void free_dsr(PVRDMADev *dev) | |
149 | { | |
150 | PCIDevice *pci_dev = PCI_DEVICE(dev); | |
151 | ||
152 | if (!dev->dsr_info.dsr) { | |
153 | return; | |
154 | } | |
155 | ||
156 | free_dev_ring(pci_dev, &dev->dsr_info.async, | |
157 | dev->dsr_info.async_ring_state); | |
158 | ||
159 | free_dev_ring(pci_dev, &dev->dsr_info.cq, dev->dsr_info.cq_ring_state); | |
160 | ||
161 | rdma_pci_dma_unmap(pci_dev, dev->dsr_info.req, | |
162 | sizeof(union pvrdma_cmd_req)); | |
163 | ||
164 | rdma_pci_dma_unmap(pci_dev, dev->dsr_info.rsp, | |
165 | sizeof(union pvrdma_cmd_resp)); | |
166 | ||
167 | rdma_pci_dma_unmap(pci_dev, dev->dsr_info.dsr, | |
168 | sizeof(struct pvrdma_device_shared_region)); | |
169 | ||
170 | dev->dsr_info.dsr = NULL; | |
171 | } | |
172 | ||
173 | static int load_dsr(PVRDMADev *dev) | |
174 | { | |
175 | int rc = 0; | |
176 | PCIDevice *pci_dev = PCI_DEVICE(dev); | |
177 | DSRInfo *dsr_info; | |
178 | struct pvrdma_device_shared_region *dsr; | |
179 | ||
180 | free_dsr(dev); | |
181 | ||
182 | /* Map to DSR */ | |
919ae3dd YS |
183 | dev->dsr_info.dsr = rdma_pci_dma_map(pci_dev, dev->dsr_info.dma, |
184 | sizeof(struct pvrdma_device_shared_region)); | |
185 | if (!dev->dsr_info.dsr) { | |
4d71b38a | 186 | rdma_error_report("Failed to map to DSR"); |
919ae3dd YS |
187 | rc = -ENOMEM; |
188 | goto out; | |
189 | } | |
190 | ||
191 | /* Shortcuts */ | |
192 | dsr_info = &dev->dsr_info; | |
193 | dsr = dsr_info->dsr; | |
194 | ||
195 | /* Map to command slot */ | |
919ae3dd YS |
196 | dsr_info->req = rdma_pci_dma_map(pci_dev, dsr->cmd_slot_dma, |
197 | sizeof(union pvrdma_cmd_req)); | |
198 | if (!dsr_info->req) { | |
4d71b38a | 199 | rdma_error_report("Failed to map to command slot address"); |
919ae3dd YS |
200 | rc = -ENOMEM; |
201 | goto out_free_dsr; | |
202 | } | |
203 | ||
204 | /* Map to response slot */ | |
919ae3dd YS |
205 | dsr_info->rsp = rdma_pci_dma_map(pci_dev, dsr->resp_slot_dma, |
206 | sizeof(union pvrdma_cmd_resp)); | |
207 | if (!dsr_info->rsp) { | |
4d71b38a | 208 | rdma_error_report("Failed to map to response slot address"); |
919ae3dd YS |
209 | rc = -ENOMEM; |
210 | goto out_free_req; | |
211 | } | |
212 | ||
213 | /* Map to CQ notification ring */ | |
214 | rc = init_dev_ring(&dsr_info->cq, &dsr_info->cq_ring_state, "dev_cq", | |
215 | pci_dev, dsr->cq_ring_pages.pdir_dma, | |
216 | dsr->cq_ring_pages.num_pages); | |
217 | if (rc) { | |
919ae3dd YS |
218 | rc = -ENOMEM; |
219 | goto out_free_rsp; | |
220 | } | |
221 | ||
222 | /* Map to event notification ring */ | |
223 | rc = init_dev_ring(&dsr_info->async, &dsr_info->async_ring_state, | |
224 | "dev_async", pci_dev, dsr->async_ring_pages.pdir_dma, | |
225 | dsr->async_ring_pages.num_pages); | |
226 | if (rc) { | |
919ae3dd YS |
227 | rc = -ENOMEM; |
228 | goto out_free_rsp; | |
229 | } | |
230 | ||
231 | goto out; | |
232 | ||
233 | out_free_rsp: | |
234 | rdma_pci_dma_unmap(pci_dev, dsr_info->rsp, sizeof(union pvrdma_cmd_resp)); | |
235 | ||
236 | out_free_req: | |
237 | rdma_pci_dma_unmap(pci_dev, dsr_info->req, sizeof(union pvrdma_cmd_req)); | |
238 | ||
239 | out_free_dsr: | |
240 | rdma_pci_dma_unmap(pci_dev, dsr_info->dsr, | |
241 | sizeof(struct pvrdma_device_shared_region)); | |
242 | dsr_info->dsr = NULL; | |
243 | ||
244 | out: | |
245 | return rc; | |
246 | } | |
247 | ||
248 | static void init_dsr_dev_caps(PVRDMADev *dev) | |
249 | { | |
250 | struct pvrdma_device_shared_region *dsr; | |
251 | ||
252 | if (dev->dsr_info.dsr == NULL) { | |
4d71b38a | 253 | rdma_error_report("Can't initialized DSR"); |
919ae3dd YS |
254 | return; |
255 | } | |
256 | ||
257 | dsr = dev->dsr_info.dsr; | |
919ae3dd | 258 | dsr->caps.fw_ver = PVRDMA_FW_VERSION; |
919ae3dd | 259 | dsr->caps.mode = PVRDMA_DEVICE_MODE_ROCE; |
919ae3dd | 260 | dsr->caps.gid_types |= PVRDMA_GID_TYPE_FLAG_ROCE_V1; |
919ae3dd | 261 | dsr->caps.max_uar = RDMA_BAR2_UAR_SIZE; |
919ae3dd YS |
262 | dsr->caps.max_mr_size = dev->dev_attr.max_mr_size; |
263 | dsr->caps.max_qp = dev->dev_attr.max_qp; | |
264 | dsr->caps.max_qp_wr = dev->dev_attr.max_qp_wr; | |
265 | dsr->caps.max_sge = dev->dev_attr.max_sge; | |
266 | dsr->caps.max_cq = dev->dev_attr.max_cq; | |
267 | dsr->caps.max_cqe = dev->dev_attr.max_cqe; | |
268 | dsr->caps.max_mr = dev->dev_attr.max_mr; | |
269 | dsr->caps.max_pd = dev->dev_attr.max_pd; | |
270 | dsr->caps.max_ah = dev->dev_attr.max_ah; | |
355b7cf3 KH |
271 | dsr->caps.max_srq = dev->dev_attr.max_srq; |
272 | dsr->caps.max_srq_wr = dev->dev_attr.max_srq_wr; | |
273 | dsr->caps.max_srq_sge = dev->dev_attr.max_srq_sge; | |
919ae3dd | 274 | dsr->caps.gid_tbl_len = MAX_GIDS; |
919ae3dd | 275 | dsr->caps.sys_image_guid = 0; |
028c3f93 | 276 | dsr->caps.node_guid = dev->node_guid; |
919ae3dd | 277 | dsr->caps.phys_port_cnt = MAX_PORTS; |
919ae3dd | 278 | dsr->caps.max_pkeys = MAX_PKEYS; |
919ae3dd YS |
279 | } |
280 | ||
75152227 YS |
281 | static void uninit_msix(PCIDevice *pdev, int used_vectors) |
282 | { | |
283 | PVRDMADev *dev = PVRDMA_DEV(pdev); | |
284 | int i; | |
285 | ||
286 | for (i = 0; i < used_vectors; i++) { | |
287 | msix_vector_unuse(pdev, i); | |
288 | } | |
289 | ||
290 | msix_uninit(pdev, &dev->msix, &dev->msix); | |
291 | } | |
292 | ||
4d71b38a | 293 | static int init_msix(PCIDevice *pdev) |
75152227 YS |
294 | { |
295 | PVRDMADev *dev = PVRDMA_DEV(pdev); | |
296 | int i; | |
297 | int rc; | |
298 | ||
299 | rc = msix_init(pdev, RDMA_MAX_INTRS, &dev->msix, RDMA_MSIX_BAR_IDX, | |
300 | RDMA_MSIX_TABLE, &dev->msix, RDMA_MSIX_BAR_IDX, | |
301 | RDMA_MSIX_PBA, 0, NULL); | |
302 | ||
303 | if (rc < 0) { | |
4d71b38a | 304 | rdma_error_report("Failed to initialize MSI-X"); |
75152227 YS |
305 | return rc; |
306 | } | |
307 | ||
308 | for (i = 0; i < RDMA_MAX_INTRS; i++) { | |
309 | rc = msix_vector_use(PCI_DEVICE(dev), i); | |
310 | if (rc < 0) { | |
4d71b38a | 311 | rdma_error_report("Fail mark MSI-X vector %d", i); |
75152227 YS |
312 | uninit_msix(pdev, i); |
313 | return rc; | |
314 | } | |
315 | } | |
316 | ||
317 | return 0; | |
318 | } | |
319 | ||
320 | static void pvrdma_fini(PCIDevice *pdev) | |
321 | { | |
322 | PVRDMADev *dev = PVRDMA_DEV(pdev); | |
323 | ||
b556c3ce YS |
324 | notifier_remove(&dev->shutdown_notifier); |
325 | ||
75152227 YS |
326 | pvrdma_qp_ops_fini(); |
327 | ||
ff30a446 YS |
328 | rdma_backend_stop(&dev->backend_dev); |
329 | ||
2b05705d YS |
330 | rdma_rm_fini(&dev->rdma_dev_res, &dev->backend_dev, |
331 | dev->backend_eth_device_name); | |
75152227 YS |
332 | |
333 | rdma_backend_fini(&dev->backend_dev); | |
334 | ||
335 | free_dsr(dev); | |
336 | ||
337 | if (msix_enabled(pdev)) { | |
338 | uninit_msix(pdev, RDMA_MAX_INTRS); | |
339 | } | |
ffa65d97 | 340 | |
4d71b38a YS |
341 | rdma_info_report("Device %s %x.%x is down", pdev->name, |
342 | PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); | |
75152227 YS |
343 | } |
344 | ||
345 | static void pvrdma_stop(PVRDMADev *dev) | |
346 | { | |
347 | rdma_backend_stop(&dev->backend_dev); | |
348 | } | |
349 | ||
350 | static void pvrdma_start(PVRDMADev *dev) | |
351 | { | |
352 | rdma_backend_start(&dev->backend_dev); | |
353 | } | |
354 | ||
919ae3dd YS |
355 | static void activate_device(PVRDMADev *dev) |
356 | { | |
75152227 | 357 | pvrdma_start(dev); |
919ae3dd | 358 | set_reg_val(dev, PVRDMA_REG_ERR, 0); |
919ae3dd YS |
359 | } |
360 | ||
361 | static int unquiesce_device(PVRDMADev *dev) | |
362 | { | |
919ae3dd YS |
363 | return 0; |
364 | } | |
365 | ||
f00c48ca | 366 | static void reset_device(PVRDMADev *dev) |
919ae3dd | 367 | { |
75152227 | 368 | pvrdma_stop(dev); |
919ae3dd YS |
369 | } |
370 | ||
4d71b38a | 371 | static uint64_t pvrdma_regs_read(void *opaque, hwaddr addr, unsigned size) |
919ae3dd YS |
372 | { |
373 | PVRDMADev *dev = opaque; | |
374 | uint32_t val; | |
375 | ||
c2dd117b YS |
376 | dev->stats.regs_reads++; |
377 | ||
919ae3dd | 378 | if (get_reg_val(dev, addr, &val)) { |
4d71b38a YS |
379 | rdma_error_report("Failed to read REG value from address 0x%x", |
380 | (uint32_t)addr); | |
919ae3dd YS |
381 | return -EINVAL; |
382 | } | |
383 | ||
384 | trace_pvrdma_regs_read(addr, val); | |
385 | ||
386 | return val; | |
387 | } | |
388 | ||
4d71b38a YS |
389 | static void pvrdma_regs_write(void *opaque, hwaddr addr, uint64_t val, |
390 | unsigned size) | |
919ae3dd YS |
391 | { |
392 | PVRDMADev *dev = opaque; | |
393 | ||
c2dd117b YS |
394 | dev->stats.regs_writes++; |
395 | ||
919ae3dd | 396 | if (set_reg_val(dev, addr, val)) { |
4d71b38a YS |
397 | rdma_error_report("Failed to set REG value, addr=0x%"PRIx64 ", val=0x%"PRIx64, |
398 | addr, val); | |
919ae3dd YS |
399 | return; |
400 | } | |
401 | ||
919ae3dd YS |
402 | switch (addr) { |
403 | case PVRDMA_REG_DSRLOW: | |
4d71b38a | 404 | trace_pvrdma_regs_write(addr, val, "DSRLOW", ""); |
919ae3dd YS |
405 | dev->dsr_info.dma = val; |
406 | break; | |
407 | case PVRDMA_REG_DSRHIGH: | |
4d71b38a | 408 | trace_pvrdma_regs_write(addr, val, "DSRHIGH", ""); |
919ae3dd YS |
409 | dev->dsr_info.dma |= val << 32; |
410 | load_dsr(dev); | |
411 | init_dsr_dev_caps(dev); | |
412 | break; | |
413 | case PVRDMA_REG_CTL: | |
414 | switch (val) { | |
415 | case PVRDMA_DEVICE_CTL_ACTIVATE: | |
4d71b38a | 416 | trace_pvrdma_regs_write(addr, val, "CTL", "ACTIVATE"); |
919ae3dd YS |
417 | activate_device(dev); |
418 | break; | |
419 | case PVRDMA_DEVICE_CTL_UNQUIESCE: | |
4d71b38a | 420 | trace_pvrdma_regs_write(addr, val, "CTL", "UNQUIESCE"); |
919ae3dd YS |
421 | unquiesce_device(dev); |
422 | break; | |
423 | case PVRDMA_DEVICE_CTL_RESET: | |
4d71b38a | 424 | trace_pvrdma_regs_write(addr, val, "CTL", "URESET"); |
919ae3dd YS |
425 | reset_device(dev); |
426 | break; | |
427 | } | |
67b32fe2 | 428 | break; |
919ae3dd | 429 | case PVRDMA_REG_IMR: |
4d71b38a | 430 | trace_pvrdma_regs_write(addr, val, "INTR_MASK", ""); |
919ae3dd YS |
431 | dev->interrupt_mask = val; |
432 | break; | |
433 | case PVRDMA_REG_REQUEST: | |
434 | if (val == 0) { | |
4d71b38a YS |
435 | trace_pvrdma_regs_write(addr, val, "REQUEST", ""); |
436 | pvrdma_exec_cmd(dev); | |
919ae3dd | 437 | } |
67b32fe2 | 438 | break; |
919ae3dd YS |
439 | default: |
440 | break; | |
441 | } | |
442 | } | |
443 | ||
444 | static const MemoryRegionOps regs_ops = { | |
4d71b38a YS |
445 | .read = pvrdma_regs_read, |
446 | .write = pvrdma_regs_write, | |
919ae3dd YS |
447 | .endianness = DEVICE_LITTLE_ENDIAN, |
448 | .impl = { | |
449 | .min_access_size = sizeof(uint32_t), | |
450 | .max_access_size = sizeof(uint32_t), | |
451 | }, | |
452 | }; | |
453 | ||
4d71b38a | 454 | static uint64_t pvrdma_uar_read(void *opaque, hwaddr addr, unsigned size) |
2aa86456 PP |
455 | { |
456 | return 0xffffffff; | |
457 | } | |
458 | ||
4d71b38a YS |
459 | static void pvrdma_uar_write(void *opaque, hwaddr addr, uint64_t val, |
460 | unsigned size) | |
919ae3dd YS |
461 | { |
462 | PVRDMADev *dev = opaque; | |
463 | ||
c2dd117b YS |
464 | dev->stats.uar_writes++; |
465 | ||
919ae3dd YS |
466 | switch (addr & 0xFFF) { /* Mask with 0xFFF as each UC gets page */ |
467 | case PVRDMA_UAR_QP_OFFSET: | |
919ae3dd | 468 | if (val & PVRDMA_UAR_QP_SEND) { |
4d71b38a YS |
469 | trace_pvrdma_uar_write(addr, val, "QP", "SEND", |
470 | val & PVRDMA_UAR_HANDLE_MASK, 0); | |
919ae3dd YS |
471 | pvrdma_qp_send(dev, val & PVRDMA_UAR_HANDLE_MASK); |
472 | } | |
473 | if (val & PVRDMA_UAR_QP_RECV) { | |
4d71b38a YS |
474 | trace_pvrdma_uar_write(addr, val, "QP", "RECV", |
475 | val & PVRDMA_UAR_HANDLE_MASK, 0); | |
919ae3dd YS |
476 | pvrdma_qp_recv(dev, val & PVRDMA_UAR_HANDLE_MASK); |
477 | } | |
478 | break; | |
479 | case PVRDMA_UAR_CQ_OFFSET: | |
919ae3dd | 480 | if (val & PVRDMA_UAR_CQ_ARM) { |
4d71b38a YS |
481 | trace_pvrdma_uar_write(addr, val, "CQ", "ARM", |
482 | val & PVRDMA_UAR_HANDLE_MASK, | |
483 | !!(val & PVRDMA_UAR_CQ_ARM_SOL)); | |
919ae3dd YS |
484 | rdma_rm_req_notify_cq(&dev->rdma_dev_res, |
485 | val & PVRDMA_UAR_HANDLE_MASK, | |
486 | !!(val & PVRDMA_UAR_CQ_ARM_SOL)); | |
487 | } | |
488 | if (val & PVRDMA_UAR_CQ_ARM_SOL) { | |
4d71b38a YS |
489 | trace_pvrdma_uar_write(addr, val, "CQ", "ARMSOL - not supported", 0, |
490 | 0); | |
919ae3dd YS |
491 | } |
492 | if (val & PVRDMA_UAR_CQ_POLL) { | |
4d71b38a YS |
493 | trace_pvrdma_uar_write(addr, val, "CQ", "POLL", |
494 | val & PVRDMA_UAR_HANDLE_MASK, 0); | |
919ae3dd YS |
495 | pvrdma_cq_poll(&dev->rdma_dev_res, val & PVRDMA_UAR_HANDLE_MASK); |
496 | } | |
497 | break; | |
355b7cf3 KH |
498 | case PVRDMA_UAR_SRQ_OFFSET: |
499 | if (val & PVRDMA_UAR_SRQ_RECV) { | |
500 | trace_pvrdma_uar_write(addr, val, "QP", "SRQ", | |
501 | val & PVRDMA_UAR_HANDLE_MASK, 0); | |
502 | pvrdma_srq_recv(dev, val & PVRDMA_UAR_HANDLE_MASK); | |
503 | } | |
504 | break; | |
919ae3dd | 505 | default: |
4d71b38a YS |
506 | rdma_error_report("Unsupported command, addr=0x%"PRIx64", val=0x%"PRIx64, |
507 | addr, val); | |
919ae3dd YS |
508 | break; |
509 | } | |
510 | } | |
511 | ||
512 | static const MemoryRegionOps uar_ops = { | |
4d71b38a YS |
513 | .read = pvrdma_uar_read, |
514 | .write = pvrdma_uar_write, | |
919ae3dd YS |
515 | .endianness = DEVICE_LITTLE_ENDIAN, |
516 | .impl = { | |
517 | .min_access_size = sizeof(uint32_t), | |
518 | .max_access_size = sizeof(uint32_t), | |
519 | }, | |
520 | }; | |
521 | ||
522 | static void init_pci_config(PCIDevice *pdev) | |
523 | { | |
524 | pdev->config[PCI_INTERRUPT_PIN] = 1; | |
525 | } | |
526 | ||
527 | static void init_bars(PCIDevice *pdev) | |
528 | { | |
529 | PVRDMADev *dev = PVRDMA_DEV(pdev); | |
530 | ||
531 | /* BAR 0 - MSI-X */ | |
532 | memory_region_init(&dev->msix, OBJECT(dev), "pvrdma-msix", | |
533 | RDMA_BAR0_MSIX_SIZE); | |
534 | pci_register_bar(pdev, RDMA_MSIX_BAR_IDX, PCI_BASE_ADDRESS_SPACE_MEMORY, | |
535 | &dev->msix); | |
536 | ||
537 | /* BAR 1 - Registers */ | |
538 | memset(&dev->regs_data, 0, sizeof(dev->regs_data)); | |
539 | memory_region_init_io(&dev->regs, OBJECT(dev), ®s_ops, dev, | |
35092917 | 540 | "pvrdma-regs", sizeof(dev->regs_data)); |
919ae3dd YS |
541 | pci_register_bar(pdev, RDMA_REG_BAR_IDX, PCI_BASE_ADDRESS_SPACE_MEMORY, |
542 | &dev->regs); | |
543 | ||
544 | /* BAR 2 - UAR */ | |
545 | memset(&dev->uar_data, 0, sizeof(dev->uar_data)); | |
546 | memory_region_init_io(&dev->uar, OBJECT(dev), &uar_ops, dev, "rdma-uar", | |
35092917 | 547 | sizeof(dev->uar_data)); |
919ae3dd YS |
548 | pci_register_bar(pdev, RDMA_UAR_BAR_IDX, PCI_BASE_ADDRESS_SPACE_MEMORY, |
549 | &dev->uar); | |
550 | } | |
551 | ||
552 | static void init_regs(PCIDevice *pdev) | |
553 | { | |
554 | PVRDMADev *dev = PVRDMA_DEV(pdev); | |
555 | ||
556 | set_reg_val(dev, PVRDMA_REG_VERSION, PVRDMA_HW_VERSION); | |
557 | set_reg_val(dev, PVRDMA_REG_ERR, 0xFFFF); | |
558 | } | |
559 | ||
919ae3dd YS |
560 | static void init_dev_caps(PVRDMADev *dev) |
561 | { | |
562 | size_t pg_tbl_bytes = TARGET_PAGE_SIZE * | |
563 | (TARGET_PAGE_SIZE / sizeof(uint64_t)); | |
564 | size_t wr_sz = MAX(sizeof(struct pvrdma_sq_wqe_hdr), | |
565 | sizeof(struct pvrdma_rq_wqe_hdr)); | |
566 | ||
567 | dev->dev_attr.max_qp_wr = pg_tbl_bytes / | |
ffef4775 YS |
568 | (wr_sz + sizeof(struct pvrdma_sge) * |
569 | dev->dev_attr.max_sge) - TARGET_PAGE_SIZE; | |
570 | /* First page is ring state ^^^^ */ | |
919ae3dd YS |
571 | |
572 | dev->dev_attr.max_cqe = pg_tbl_bytes / sizeof(struct pvrdma_cqe) - | |
573 | TARGET_PAGE_SIZE; /* First page is ring state */ | |
355b7cf3 KH |
574 | |
575 | dev->dev_attr.max_srq_wr = pg_tbl_bytes / | |
576 | ((sizeof(struct pvrdma_rq_wqe_hdr) + | |
577 | sizeof(struct pvrdma_sge)) * | |
578 | dev->dev_attr.max_sge) - TARGET_PAGE_SIZE; | |
919ae3dd YS |
579 | } |
580 | ||
581 | static int pvrdma_check_ram_shared(Object *obj, void *opaque) | |
582 | { | |
583 | bool *shared = opaque; | |
584 | ||
585 | if (object_dynamic_cast(obj, "memory-backend-ram")) { | |
586 | *shared = object_property_get_bool(obj, "share", NULL); | |
587 | } | |
588 | ||
589 | return 0; | |
590 | } | |
591 | ||
ffa65d97 YS |
592 | static void pvrdma_shutdown_notifier(Notifier *n, void *opaque) |
593 | { | |
594 | PVRDMADev *dev = container_of(n, PVRDMADev, shutdown_notifier); | |
595 | PCIDevice *pci_dev = PCI_DEVICE(dev); | |
596 | ||
597 | pvrdma_fini(pci_dev); | |
598 | } | |
599 | ||
919ae3dd YS |
600 | static void pvrdma_realize(PCIDevice *pdev, Error **errp) |
601 | { | |
cce64861 | 602 | int rc = 0; |
919ae3dd YS |
603 | PVRDMADev *dev = PVRDMA_DEV(pdev); |
604 | Object *memdev_root; | |
605 | bool ram_shared = false; | |
d961ead1 | 606 | PCIDevice *func0; |
919ae3dd | 607 | |
4d71b38a YS |
608 | rdma_info_report("Initializing device %s %x.%x", pdev->name, |
609 | PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); | |
919ae3dd | 610 | |
038adc2f | 611 | if (TARGET_PAGE_SIZE != qemu_real_host_page_size) { |
919ae3dd YS |
612 | error_setg(errp, "Target page size must be the same as host page size"); |
613 | return; | |
614 | } | |
615 | ||
d961ead1 YS |
616 | func0 = pci_get_function_0(pdev); |
617 | /* Break if not vmxnet3 device in slot 0 */ | |
db8b88bf | 618 | if (strcmp(object_get_typename(OBJECT(func0)), TYPE_VMXNET3)) { |
d961ead1 YS |
619 | error_setg(errp, "Device on %x.0 must be %s", PCI_SLOT(pdev->devfn), |
620 | TYPE_VMXNET3); | |
621 | return; | |
622 | } | |
623 | dev->func0 = VMXNET3(func0); | |
624 | ||
028c3f93 YS |
625 | addrconf_addr_eui48((unsigned char *)&dev->node_guid, |
626 | (const char *)&dev->func0->conf.macaddr.a); | |
627 | ||
919ae3dd YS |
628 | memdev_root = object_resolve_path("/objects", NULL); |
629 | if (memdev_root) { | |
630 | object_child_foreach(memdev_root, pvrdma_check_ram_shared, &ram_shared); | |
631 | } | |
632 | if (!ram_shared) { | |
633 | error_setg(errp, "Only shared memory backed ram is supported"); | |
634 | return; | |
635 | } | |
636 | ||
637 | dev->dsr_info.dsr = NULL; | |
638 | ||
639 | init_pci_config(pdev); | |
640 | ||
641 | init_bars(pdev); | |
642 | ||
643 | init_regs(pdev); | |
644 | ||
4d71b38a | 645 | rc = init_msix(pdev); |
919ae3dd YS |
646 | if (rc) { |
647 | goto out; | |
648 | } | |
649 | ||
430e440c | 650 | rc = rdma_backend_init(&dev->backend_dev, pdev, &dev->rdma_dev_res, |
919ae3dd | 651 | dev->backend_device_name, dev->backend_port_num, |
4d71b38a | 652 | &dev->dev_attr, &dev->mad_chr); |
919ae3dd YS |
653 | if (rc) { |
654 | goto out; | |
655 | } | |
656 | ||
ffef4775 YS |
657 | init_dev_caps(dev); |
658 | ||
4d71b38a | 659 | rc = rdma_rm_init(&dev->rdma_dev_res, &dev->dev_attr); |
919ae3dd YS |
660 | if (rc) { |
661 | goto out; | |
662 | } | |
663 | ||
919ae3dd YS |
664 | rc = pvrdma_qp_ops_init(); |
665 | if (rc) { | |
666 | goto out; | |
667 | } | |
668 | ||
c2dd117b YS |
669 | memset(&dev->stats, 0, sizeof(dev->stats)); |
670 | ||
ffa65d97 YS |
671 | dev->shutdown_notifier.notify = pvrdma_shutdown_notifier; |
672 | qemu_register_shutdown_notifier(&dev->shutdown_notifier); | |
673 | ||
68b89aee YS |
674 | #ifdef LEGACY_RDMA_REG_MR |
675 | rdma_info_report("Using legacy reg_mr"); | |
676 | #else | |
677 | rdma_info_report("Using iova reg_mr"); | |
678 | #endif | |
679 | ||
919ae3dd YS |
680 | out: |
681 | if (rc) { | |
cce64861 | 682 | pvrdma_fini(pdev); |
4d71b38a | 683 | error_append_hint(errp, "Device failed to load\n"); |
919ae3dd YS |
684 | } |
685 | } | |
686 | ||
919ae3dd YS |
687 | static void pvrdma_class_init(ObjectClass *klass, void *data) |
688 | { | |
689 | DeviceClass *dc = DEVICE_CLASS(klass); | |
690 | PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); | |
7b4433bb | 691 | RdmaProviderClass *ir = RDMA_PROVIDER_CLASS(klass); |
919ae3dd YS |
692 | |
693 | k->realize = pvrdma_realize; | |
919ae3dd YS |
694 | k->vendor_id = PCI_VENDOR_ID_VMWARE; |
695 | k->device_id = PCI_DEVICE_ID_VMWARE_PVRDMA; | |
696 | k->revision = 0x00; | |
697 | k->class_id = PCI_CLASS_NETWORK_OTHER; | |
698 | ||
699 | dc->desc = "RDMA Device"; | |
4f67d30b | 700 | device_class_set_props(dc, pvrdma_dev_properties); |
919ae3dd | 701 | set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); |
f4b2c02a | 702 | |
8dbbca5c | 703 | ir->format_statistics = pvrdma_format_statistics; |
919ae3dd YS |
704 | } |
705 | ||
706 | static const TypeInfo pvrdma_info = { | |
707 | .name = PVRDMA_HW_NAME, | |
708 | .parent = TYPE_PCI_DEVICE, | |
709 | .instance_size = sizeof(PVRDMADev), | |
710 | .class_init = pvrdma_class_init, | |
711 | .interfaces = (InterfaceInfo[]) { | |
712 | { INTERFACE_CONVENTIONAL_PCI_DEVICE }, | |
f4b2c02a | 713 | { INTERFACE_RDMA_PROVIDER }, |
919ae3dd YS |
714 | { } |
715 | } | |
716 | }; | |
717 | ||
718 | static void register_types(void) | |
719 | { | |
720 | type_register_static(&pvrdma_info); | |
721 | } | |
722 | ||
723 | type_init(register_types) |