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