diff options
author | Christoph Hellwig <hch@lst.de> | 2017-02-05 12:15:19 -0500 |
---|---|---|
committer | Michael S. Tsirkin <mst@redhat.com> | 2017-02-27 13:54:03 -0500 |
commit | 07ec51480b5eb1233f8c1b0f5d7a7c8d1247c507 (patch) | |
tree | ddf6e2c0d8259437966396771dd09828c147dde7 | |
parent | 5c34d002dcc7a6dd665a19d098b4f4cd5501ba1a (diff) |
virtio_pci: use shared interrupts for virtqueues
This lets IRQ layer handle dispatching IRQs to separate handlers for the
case where we don't have per-VQ MSI-X vectors, and allows us to greatly
simplify the code based on the assumption that we always have interrupt
vector 0 (legacy INTx or config interrupt for MSI-X) available, and
any other interrupt is request/freed throught the VQ, even if the
actual interrupt line might be shared in some cases.
This allows removing a great deal of variables keeping track of the
interrupt state in struct virtio_pci_device, as we can now simply walk the
list of VQs and deal with per-VQ interrupt handlers there, and only treat
vector 0 special.
Additionally clean up the VQ allocation code to properly unwind on error
instead of having a single global cleanup label, which is error prone,
and in this case also leads to more code.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
-rw-r--r-- | drivers/virtio/virtio_pci_common.c | 235 | ||||
-rw-r--r-- | drivers/virtio/virtio_pci_common.h | 16 |
2 files changed, 106 insertions, 145 deletions
diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c index a33767318cbf..274dc1ff09c0 100644 --- a/drivers/virtio/virtio_pci_common.c +++ b/drivers/virtio/virtio_pci_common.c | |||
@@ -33,10 +33,8 @@ void vp_synchronize_vectors(struct virtio_device *vdev) | |||
33 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); | 33 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); |
34 | int i; | 34 | int i; |
35 | 35 | ||
36 | if (vp_dev->intx_enabled) | 36 | synchronize_irq(pci_irq_vector(vp_dev->pci_dev, 0)); |
37 | synchronize_irq(vp_dev->pci_dev->irq); | 37 | for (i = 1; i < vp_dev->msix_vectors; i++) |
38 | |||
39 | for (i = 0; i < vp_dev->msix_vectors; ++i) | ||
40 | synchronize_irq(pci_irq_vector(vp_dev->pci_dev, i)); | 38 | synchronize_irq(pci_irq_vector(vp_dev->pci_dev, i)); |
41 | } | 39 | } |
42 | 40 | ||
@@ -99,77 +97,10 @@ static irqreturn_t vp_interrupt(int irq, void *opaque) | |||
99 | return vp_vring_interrupt(irq, opaque); | 97 | return vp_vring_interrupt(irq, opaque); |
100 | } | 98 | } |
101 | 99 | ||
102 | static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors, | 100 | static void vp_remove_vqs(struct virtio_device *vdev) |
103 | bool per_vq_vectors) | ||
104 | { | ||
105 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); | ||
106 | const char *name = dev_name(&vp_dev->vdev.dev); | ||
107 | unsigned i, v; | ||
108 | int err = -ENOMEM; | ||
109 | |||
110 | vp_dev->msix_vectors = nvectors; | ||
111 | |||
112 | vp_dev->msix_names = kmalloc(nvectors * sizeof *vp_dev->msix_names, | ||
113 | GFP_KERNEL); | ||
114 | if (!vp_dev->msix_names) | ||
115 | goto error; | ||
116 | vp_dev->msix_affinity_masks | ||
117 | = kzalloc(nvectors * sizeof *vp_dev->msix_affinity_masks, | ||
118 | GFP_KERNEL); | ||
119 | if (!vp_dev->msix_affinity_masks) | ||
120 | goto error; | ||
121 | for (i = 0; i < nvectors; ++i) | ||
122 | if (!alloc_cpumask_var(&vp_dev->msix_affinity_masks[i], | ||
123 | GFP_KERNEL)) | ||
124 | goto error; | ||
125 | |||
126 | err = pci_alloc_irq_vectors(vp_dev->pci_dev, nvectors, nvectors, | ||
127 | PCI_IRQ_MSIX); | ||
128 | if (err < 0) | ||
129 | goto error; | ||
130 | vp_dev->msix_enabled = 1; | ||
131 | |||
132 | /* Set the vector used for configuration */ | ||
133 | v = vp_dev->msix_used_vectors; | ||
134 | snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names, | ||
135 | "%s-config", name); | ||
136 | err = request_irq(pci_irq_vector(vp_dev->pci_dev, v), | ||
137 | vp_config_changed, 0, vp_dev->msix_names[v], | ||
138 | vp_dev); | ||
139 | if (err) | ||
140 | goto error; | ||
141 | ++vp_dev->msix_used_vectors; | ||
142 | |||
143 | v = vp_dev->config_vector(vp_dev, v); | ||
144 | /* Verify we had enough resources to assign the vector */ | ||
145 | if (v == VIRTIO_MSI_NO_VECTOR) { | ||
146 | err = -EBUSY; | ||
147 | goto error; | ||
148 | } | ||
149 | |||
150 | if (!per_vq_vectors) { | ||
151 | /* Shared vector for all VQs */ | ||
152 | v = vp_dev->msix_used_vectors; | ||
153 | snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names, | ||
154 | "%s-virtqueues", name); | ||
155 | err = request_irq(pci_irq_vector(vp_dev->pci_dev, v), | ||
156 | vp_vring_interrupt, 0, vp_dev->msix_names[v], | ||
157 | vp_dev); | ||
158 | if (err) | ||
159 | goto error; | ||
160 | ++vp_dev->msix_used_vectors; | ||
161 | } | ||
162 | return 0; | ||
163 | error: | ||
164 | return err; | ||
165 | } | ||
166 | |||
167 | /* the config->del_vqs() implementation */ | ||
168 | void vp_del_vqs(struct virtio_device *vdev) | ||
169 | { | 101 | { |
170 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); | 102 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); |
171 | struct virtqueue *vq, *n; | 103 | struct virtqueue *vq, *n; |
172 | int i; | ||
173 | 104 | ||
174 | list_for_each_entry_safe(vq, n, &vdev->vqs, list) { | 105 | list_for_each_entry_safe(vq, n, &vdev->vqs, list) { |
175 | if (vp_dev->msix_vector_map) { | 106 | if (vp_dev->msix_vector_map) { |
@@ -181,35 +112,33 @@ void vp_del_vqs(struct virtio_device *vdev) | |||
181 | } | 112 | } |
182 | vp_dev->del_vq(vq); | 113 | vp_dev->del_vq(vq); |
183 | } | 114 | } |
115 | } | ||
184 | 116 | ||
185 | if (vp_dev->intx_enabled) { | 117 | /* the config->del_vqs() implementation */ |
186 | free_irq(vp_dev->pci_dev->irq, vp_dev); | 118 | void vp_del_vqs(struct virtio_device *vdev) |
187 | vp_dev->intx_enabled = 0; | 119 | { |
188 | } | 120 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); |
121 | int i; | ||
189 | 122 | ||
190 | for (i = 0; i < vp_dev->msix_used_vectors; ++i) | 123 | if (WARN_ON_ONCE(list_empty_careful(&vdev->vqs))) |
191 | free_irq(pci_irq_vector(vp_dev->pci_dev, i), vp_dev); | 124 | return; |
192 | 125 | ||
193 | for (i = 0; i < vp_dev->msix_vectors; i++) | 126 | vp_remove_vqs(vdev); |
194 | if (vp_dev->msix_affinity_masks[i]) | ||
195 | free_cpumask_var(vp_dev->msix_affinity_masks[i]); | ||
196 | 127 | ||
197 | if (vp_dev->msix_enabled) { | 128 | if (vp_dev->msix_enabled) { |
129 | for (i = 0; i < vp_dev->msix_vectors; i++) | ||
130 | free_cpumask_var(vp_dev->msix_affinity_masks[i]); | ||
131 | |||
198 | /* Disable the vector used for configuration */ | 132 | /* Disable the vector used for configuration */ |
199 | vp_dev->config_vector(vp_dev, VIRTIO_MSI_NO_VECTOR); | 133 | vp_dev->config_vector(vp_dev, VIRTIO_MSI_NO_VECTOR); |
200 | 134 | ||
201 | pci_free_irq_vectors(vp_dev->pci_dev); | 135 | kfree(vp_dev->msix_affinity_masks); |
202 | vp_dev->msix_enabled = 0; | 136 | kfree(vp_dev->msix_names); |
137 | kfree(vp_dev->msix_vector_map); | ||
203 | } | 138 | } |
204 | 139 | ||
205 | vp_dev->msix_vectors = 0; | 140 | free_irq(pci_irq_vector(vp_dev->pci_dev, 0), vp_dev); |
206 | vp_dev->msix_used_vectors = 0; | 141 | pci_free_irq_vectors(vp_dev->pci_dev); |
207 | kfree(vp_dev->msix_names); | ||
208 | vp_dev->msix_names = NULL; | ||
209 | kfree(vp_dev->msix_affinity_masks); | ||
210 | vp_dev->msix_affinity_masks = NULL; | ||
211 | kfree(vp_dev->msix_vector_map); | ||
212 | vp_dev->msix_vector_map = NULL; | ||
213 | } | 142 | } |
214 | 143 | ||
215 | static int vp_find_vqs_msix(struct virtio_device *vdev, unsigned nvqs, | 144 | static int vp_find_vqs_msix(struct virtio_device *vdev, unsigned nvqs, |
@@ -219,79 +148,122 @@ static int vp_find_vqs_msix(struct virtio_device *vdev, unsigned nvqs, | |||
219 | bool per_vq_vectors) | 148 | bool per_vq_vectors) |
220 | { | 149 | { |
221 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); | 150 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); |
151 | const char *name = dev_name(&vp_dev->vdev.dev); | ||
152 | int i, err = -ENOMEM, allocated_vectors, nvectors; | ||
222 | u16 msix_vec; | 153 | u16 msix_vec; |
223 | int i, err, nvectors, allocated_vectors; | 154 | |
155 | nvectors = 1; | ||
156 | for (i = 0; i < nvqs; i++) | ||
157 | if (callbacks[i]) | ||
158 | nvectors++; | ||
224 | 159 | ||
225 | if (per_vq_vectors) { | 160 | if (per_vq_vectors) { |
226 | /* Best option: one for change interrupt, one per vq. */ | 161 | err = pci_alloc_irq_vectors(vp_dev->pci_dev, nvectors, nvectors, |
227 | nvectors = 1; | 162 | PCI_IRQ_MSIX); |
228 | for (i = 0; i < nvqs; ++i) | ||
229 | if (callbacks[i]) | ||
230 | ++nvectors; | ||
231 | } else { | 163 | } else { |
232 | /* Second best: one for change, shared for all vqs. */ | 164 | err = pci_alloc_irq_vectors(vp_dev->pci_dev, 2, 2, |
233 | nvectors = 2; | 165 | PCI_IRQ_MSIX); |
234 | } | 166 | } |
167 | if (err < 0) | ||
168 | return err; | ||
235 | 169 | ||
236 | err = vp_request_msix_vectors(vdev, nvectors, per_vq_vectors); | 170 | vp_dev->msix_vectors = nvectors; |
171 | vp_dev->msix_names = kmalloc_array(nvectors, | ||
172 | sizeof(*vp_dev->msix_names), GFP_KERNEL); | ||
173 | if (!vp_dev->msix_names) | ||
174 | goto out_free_irq_vectors; | ||
175 | |||
176 | vp_dev->msix_affinity_masks = kcalloc(nvectors, | ||
177 | sizeof(*vp_dev->msix_affinity_masks), GFP_KERNEL); | ||
178 | if (!vp_dev->msix_affinity_masks) | ||
179 | goto out_free_msix_names; | ||
180 | |||
181 | for (i = 0; i < nvectors; ++i) { | ||
182 | if (!alloc_cpumask_var(&vp_dev->msix_affinity_masks[i], | ||
183 | GFP_KERNEL)) | ||
184 | goto out_free_msix_affinity_masks; | ||
185 | } | ||
186 | |||
187 | /* Set the vector used for configuration */ | ||
188 | snprintf(vp_dev->msix_names[0], sizeof(*vp_dev->msix_names), | ||
189 | "%s-config", name); | ||
190 | err = request_irq(pci_irq_vector(vp_dev->pci_dev, 0), vp_config_changed, | ||
191 | 0, vp_dev->msix_names[0], vp_dev); | ||
237 | if (err) | 192 | if (err) |
238 | goto error_find; | 193 | goto out_free_irq_vectors; |
239 | 194 | ||
240 | if (per_vq_vectors) { | 195 | /* Verify we had enough resources to assign the vector */ |
241 | vp_dev->msix_vector_map = kmalloc_array(nvqs, | 196 | if (vp_dev->config_vector(vp_dev, 0) == VIRTIO_MSI_NO_VECTOR) { |
242 | sizeof(*vp_dev->msix_vector_map), GFP_KERNEL); | 197 | err = -EBUSY; |
243 | if (!vp_dev->msix_vector_map) | 198 | goto out_free_config_irq; |
244 | goto error_find; | ||
245 | } | 199 | } |
246 | 200 | ||
247 | allocated_vectors = vp_dev->msix_used_vectors; | 201 | vp_dev->msix_vector_map = kmalloc_array(nvqs, |
202 | sizeof(*vp_dev->msix_vector_map), GFP_KERNEL); | ||
203 | if (!vp_dev->msix_vector_map) | ||
204 | goto out_disable_config_irq; | ||
205 | |||
206 | allocated_vectors = 1; /* vector 0 is the config interrupt */ | ||
248 | for (i = 0; i < nvqs; ++i) { | 207 | for (i = 0; i < nvqs; ++i) { |
249 | if (!names[i]) { | 208 | if (!names[i]) { |
250 | vqs[i] = NULL; | 209 | vqs[i] = NULL; |
251 | continue; | 210 | continue; |
252 | } | 211 | } |
253 | 212 | ||
254 | if (!callbacks[i]) | 213 | if (callbacks[i]) |
255 | msix_vec = VIRTIO_MSI_NO_VECTOR; | 214 | msix_vec = allocated_vectors; |
256 | else if (per_vq_vectors) | ||
257 | msix_vec = allocated_vectors++; | ||
258 | else | 215 | else |
259 | msix_vec = VP_MSIX_VQ_VECTOR; | 216 | msix_vec = VIRTIO_MSI_NO_VECTOR; |
217 | |||
260 | vqs[i] = vp_dev->setup_vq(vp_dev, i, callbacks[i], names[i], | 218 | vqs[i] = vp_dev->setup_vq(vp_dev, i, callbacks[i], names[i], |
261 | msix_vec); | 219 | msix_vec); |
262 | if (IS_ERR(vqs[i])) { | 220 | if (IS_ERR(vqs[i])) { |
263 | err = PTR_ERR(vqs[i]); | 221 | err = PTR_ERR(vqs[i]); |
264 | goto error_find; | 222 | goto out_remove_vqs; |
265 | } | 223 | } |
266 | 224 | ||
267 | if (!per_vq_vectors) | ||
268 | continue; | ||
269 | |||
270 | if (msix_vec == VIRTIO_MSI_NO_VECTOR) { | 225 | if (msix_vec == VIRTIO_MSI_NO_VECTOR) { |
271 | vp_dev->msix_vector_map[i] = VIRTIO_MSI_NO_VECTOR; | 226 | vp_dev->msix_vector_map[i] = VIRTIO_MSI_NO_VECTOR; |
272 | continue; | 227 | continue; |
273 | } | 228 | } |
274 | 229 | ||
275 | /* allocate per-vq irq if available and necessary */ | 230 | snprintf(vp_dev->msix_names[i + 1], |
276 | snprintf(vp_dev->msix_names[msix_vec], | 231 | sizeof(*vp_dev->msix_names), "%s-%s", |
277 | sizeof *vp_dev->msix_names, | ||
278 | "%s-%s", | ||
279 | dev_name(&vp_dev->vdev.dev), names[i]); | 232 | dev_name(&vp_dev->vdev.dev), names[i]); |
280 | err = request_irq(pci_irq_vector(vp_dev->pci_dev, msix_vec), | 233 | err = request_irq(pci_irq_vector(vp_dev->pci_dev, msix_vec), |
281 | vring_interrupt, 0, | 234 | vring_interrupt, IRQF_SHARED, |
282 | vp_dev->msix_names[msix_vec], | 235 | vp_dev->msix_names[i + 1], vqs[i]); |
283 | vqs[i]); | ||
284 | if (err) { | 236 | if (err) { |
285 | /* don't free this irq on error */ | 237 | /* don't free this irq on error */ |
286 | vp_dev->msix_vector_map[i] = VIRTIO_MSI_NO_VECTOR; | 238 | vp_dev->msix_vector_map[i] = VIRTIO_MSI_NO_VECTOR; |
287 | goto error_find; | 239 | goto out_remove_vqs; |
288 | } | 240 | } |
289 | vp_dev->msix_vector_map[i] = msix_vec; | 241 | vp_dev->msix_vector_map[i] = msix_vec; |
242 | |||
243 | if (per_vq_vectors) | ||
244 | allocated_vectors++; | ||
290 | } | 245 | } |
246 | |||
247 | vp_dev->msix_enabled = 1; | ||
291 | return 0; | 248 | return 0; |
292 | 249 | ||
293 | error_find: | 250 | out_remove_vqs: |
294 | vp_del_vqs(vdev); | 251 | vp_remove_vqs(vdev); |
252 | kfree(vp_dev->msix_vector_map); | ||
253 | out_disable_config_irq: | ||
254 | vp_dev->config_vector(vp_dev, VIRTIO_MSI_NO_VECTOR); | ||
255 | out_free_config_irq: | ||
256 | free_irq(pci_irq_vector(vp_dev->pci_dev, 0), vp_dev); | ||
257 | out_free_msix_affinity_masks: | ||
258 | for (i = 0; i < nvectors; i++) { | ||
259 | if (vp_dev->msix_affinity_masks[i]) | ||
260 | free_cpumask_var(vp_dev->msix_affinity_masks[i]); | ||
261 | } | ||
262 | kfree(vp_dev->msix_affinity_masks); | ||
263 | out_free_msix_names: | ||
264 | kfree(vp_dev->msix_names); | ||
265 | out_free_irq_vectors: | ||
266 | pci_free_irq_vectors(vp_dev->pci_dev); | ||
295 | return err; | 267 | return err; |
296 | } | 268 | } |
297 | 269 | ||
@@ -305,9 +277,8 @@ static int vp_find_vqs_intx(struct virtio_device *vdev, unsigned nvqs, | |||
305 | err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, IRQF_SHARED, | 277 | err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, IRQF_SHARED, |
306 | dev_name(&vdev->dev), vp_dev); | 278 | dev_name(&vdev->dev), vp_dev); |
307 | if (err) | 279 | if (err) |
308 | goto out_del_vqs; | 280 | return err; |
309 | 281 | ||
310 | vp_dev->intx_enabled = 1; | ||
311 | for (i = 0; i < nvqs; ++i) { | 282 | for (i = 0; i < nvqs; ++i) { |
312 | if (!names[i]) { | 283 | if (!names[i]) { |
313 | vqs[i] = NULL; | 284 | vqs[i] = NULL; |
@@ -317,13 +288,15 @@ static int vp_find_vqs_intx(struct virtio_device *vdev, unsigned nvqs, | |||
317 | VIRTIO_MSI_NO_VECTOR); | 288 | VIRTIO_MSI_NO_VECTOR); |
318 | if (IS_ERR(vqs[i])) { | 289 | if (IS_ERR(vqs[i])) { |
319 | err = PTR_ERR(vqs[i]); | 290 | err = PTR_ERR(vqs[i]); |
320 | goto out_del_vqs; | 291 | goto out_remove_vqs; |
321 | } | 292 | } |
322 | } | 293 | } |
323 | 294 | ||
324 | return 0; | 295 | return 0; |
325 | out_del_vqs: | 296 | |
326 | vp_del_vqs(vdev); | 297 | out_remove_vqs: |
298 | vp_remove_vqs(vdev); | ||
299 | free_irq(pci_irq_vector(vp_dev->pci_dev, 0), vp_dev); | ||
327 | return err; | 300 | return err; |
328 | } | 301 | } |
329 | 302 | ||
diff --git a/drivers/virtio/virtio_pci_common.h b/drivers/virtio/virtio_pci_common.h index 2038887bdf23..85593867e712 100644 --- a/drivers/virtio/virtio_pci_common.h +++ b/drivers/virtio/virtio_pci_common.h | |||
@@ -66,16 +66,12 @@ struct virtio_pci_device { | |||
66 | 66 | ||
67 | /* MSI-X support */ | 67 | /* MSI-X support */ |
68 | int msix_enabled; | 68 | int msix_enabled; |
69 | int intx_enabled; | ||
70 | cpumask_var_t *msix_affinity_masks; | 69 | cpumask_var_t *msix_affinity_masks; |
71 | /* Name strings for interrupts. This size should be enough, | 70 | /* Name strings for interrupts. This size should be enough, |
72 | * and I'm too lazy to allocate each name separately. */ | 71 | * and I'm too lazy to allocate each name separately. */ |
73 | char (*msix_names)[256]; | 72 | char (*msix_names)[256]; |
74 | /* Number of available vectors */ | 73 | /* Total Number of MSI-X vectors (including per-VQ ones). */ |
75 | unsigned msix_vectors; | 74 | int msix_vectors; |
76 | /* Vectors allocated, excluding per-vq vectors if any */ | ||
77 | unsigned msix_used_vectors; | ||
78 | |||
79 | /* Map of per-VQ MSI-X vectors, may be NULL */ | 75 | /* Map of per-VQ MSI-X vectors, may be NULL */ |
80 | unsigned *msix_vector_map; | 76 | unsigned *msix_vector_map; |
81 | 77 | ||
@@ -89,14 +85,6 @@ struct virtio_pci_device { | |||
89 | u16 (*config_vector)(struct virtio_pci_device *vp_dev, u16 vector); | 85 | u16 (*config_vector)(struct virtio_pci_device *vp_dev, u16 vector); |
90 | }; | 86 | }; |
91 | 87 | ||
92 | /* Constants for MSI-X */ | ||
93 | /* Use first vector for configuration changes, second and the rest for | ||
94 | * virtqueues Thus, we need at least 2 vectors for MSI. */ | ||
95 | enum { | ||
96 | VP_MSIX_CONFIG_VECTOR = 0, | ||
97 | VP_MSIX_VQ_VECTOR = 1, | ||
98 | }; | ||
99 | |||
100 | /* Convert a generic virtio device to our structure */ | 88 | /* Convert a generic virtio device to our structure */ |
101 | static struct virtio_pci_device *to_vp_device(struct virtio_device *vdev) | 89 | static struct virtio_pci_device *to_vp_device(struct virtio_device *vdev) |
102 | { | 90 | { |