1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
6 #include <linux/vfio.h>
7 #include <linux/slab.h>
8 #include <linux/types.h>
9 #include <linux/eventfd.h>
10 #include <linux/msi.h>
11 #include <linux/interrupt.h>
13 #include "linux/cdx/cdx_bus.h"
16 static irqreturn_t
vfio_cdx_msihandler(int irq_no
, void *arg
)
18 struct eventfd_ctx
*trigger
= arg
;
20 eventfd_signal(trigger
);
24 static int vfio_cdx_msi_enable(struct vfio_cdx_device
*vdev
, int nvec
)
26 struct cdx_device
*cdx_dev
= to_cdx_device(vdev
->vdev
.dev
);
27 struct device
*dev
= vdev
->vdev
.dev
;
30 vdev
->cdx_irqs
= kcalloc(nvec
, sizeof(struct vfio_cdx_irq
), GFP_KERNEL
);
34 ret
= cdx_enable_msi(cdx_dev
);
36 kfree(vdev
->cdx_irqs
);
40 /* Allocate cdx MSIs */
41 ret
= msi_domain_alloc_irqs(dev
, MSI_DEFAULT_DOMAIN
, nvec
);
43 cdx_disable_msi(cdx_dev
);
44 kfree(vdev
->cdx_irqs
);
48 for (msi_idx
= 0; msi_idx
< nvec
; msi_idx
++)
49 vdev
->cdx_irqs
[msi_idx
].irq_no
= msi_get_virq(dev
, msi_idx
);
51 vdev
->msi_count
= nvec
;
57 static int vfio_cdx_msi_set_vector_signal(struct vfio_cdx_device
*vdev
,
60 struct eventfd_ctx
*trigger
;
63 if (vector
< 0 || vector
>= vdev
->msi_count
)
66 irq_no
= vdev
->cdx_irqs
[vector
].irq_no
;
68 if (vdev
->cdx_irqs
[vector
].trigger
) {
69 free_irq(irq_no
, vdev
->cdx_irqs
[vector
].trigger
);
70 kfree(vdev
->cdx_irqs
[vector
].name
);
71 eventfd_ctx_put(vdev
->cdx_irqs
[vector
].trigger
);
72 vdev
->cdx_irqs
[vector
].trigger
= NULL
;
78 vdev
->cdx_irqs
[vector
].name
= kasprintf(GFP_KERNEL
, "vfio-msi[%d](%s)",
79 vector
, dev_name(vdev
->vdev
.dev
));
80 if (!vdev
->cdx_irqs
[vector
].name
)
83 trigger
= eventfd_ctx_fdget(fd
);
84 if (IS_ERR(trigger
)) {
85 kfree(vdev
->cdx_irqs
[vector
].name
);
86 return PTR_ERR(trigger
);
89 ret
= request_irq(irq_no
, vfio_cdx_msihandler
, 0,
90 vdev
->cdx_irqs
[vector
].name
, trigger
);
92 kfree(vdev
->cdx_irqs
[vector
].name
);
93 eventfd_ctx_put(trigger
);
97 vdev
->cdx_irqs
[vector
].trigger
= trigger
;
102 static int vfio_cdx_msi_set_block(struct vfio_cdx_device
*vdev
,
103 unsigned int start
, unsigned int count
,
108 if (start
>= vdev
->msi_count
|| start
+ count
> vdev
->msi_count
)
111 for (i
= 0, j
= start
; i
< count
&& !ret
; i
++, j
++) {
112 int fd
= fds
? fds
[i
] : -1;
114 ret
= vfio_cdx_msi_set_vector_signal(vdev
, j
, fd
);
118 for (--j
; j
>= (int)start
; j
--)
119 vfio_cdx_msi_set_vector_signal(vdev
, j
, -1);
125 static void vfio_cdx_msi_disable(struct vfio_cdx_device
*vdev
)
127 struct cdx_device
*cdx_dev
= to_cdx_device(vdev
->vdev
.dev
);
128 struct device
*dev
= vdev
->vdev
.dev
;
130 vfio_cdx_msi_set_block(vdev
, 0, vdev
->msi_count
, NULL
);
132 if (!vdev
->config_msi
)
135 msi_domain_free_irqs_all(dev
, MSI_DEFAULT_DOMAIN
);
136 cdx_disable_msi(cdx_dev
);
137 kfree(vdev
->cdx_irqs
);
139 vdev
->cdx_irqs
= NULL
;
141 vdev
->config_msi
= 0;
144 static int vfio_cdx_set_msi_trigger(struct vfio_cdx_device
*vdev
,
145 unsigned int index
, unsigned int start
,
146 unsigned int count
, u32 flags
,
149 struct cdx_device
*cdx_dev
= to_cdx_device(vdev
->vdev
.dev
);
152 if (start
+ count
> cdx_dev
->num_msi
)
155 if (!count
&& (flags
& VFIO_IRQ_SET_DATA_NONE
)) {
156 vfio_cdx_msi_disable(vdev
);
160 if (flags
& VFIO_IRQ_SET_DATA_EVENTFD
) {
164 if (vdev
->config_msi
)
165 return vfio_cdx_msi_set_block(vdev
, start
, count
,
167 ret
= vfio_cdx_msi_enable(vdev
, cdx_dev
->num_msi
);
171 ret
= vfio_cdx_msi_set_block(vdev
, start
, count
, fds
);
173 vfio_cdx_msi_disable(vdev
);
178 for (i
= start
; i
< start
+ count
; i
++) {
179 if (!vdev
->cdx_irqs
[i
].trigger
)
181 if (flags
& VFIO_IRQ_SET_DATA_NONE
) {
182 eventfd_signal(vdev
->cdx_irqs
[i
].trigger
);
183 } else if (flags
& VFIO_IRQ_SET_DATA_BOOL
) {
186 if (bools
[i
- start
])
187 eventfd_signal(vdev
->cdx_irqs
[i
].trigger
);
194 int vfio_cdx_set_irqs_ioctl(struct vfio_cdx_device
*vdev
,
195 u32 flags
, unsigned int index
,
196 unsigned int start
, unsigned int count
,
199 if (flags
& VFIO_IRQ_SET_ACTION_TRIGGER
)
200 return vfio_cdx_set_msi_trigger(vdev
, index
, start
,
206 /* Free All IRQs for the given device */
207 void vfio_cdx_irqs_cleanup(struct vfio_cdx_device
*vdev
)
210 * Device does not support any interrupt or the interrupts
211 * were not configured
216 vfio_cdx_set_msi_trigger(vdev
, 0, 0, 0, VFIO_IRQ_SET_DATA_NONE
, NULL
);