aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2017-02-05 12:15:19 -0500
committerMichael S. Tsirkin <mst@redhat.com>2017-02-27 13:54:03 -0500
commit07ec51480b5eb1233f8c1b0f5d7a7c8d1247c507 (patch)
treeddf6e2c0d8259437966396771dd09828c147dde7
parent5c34d002dcc7a6dd665a19d098b4f4cd5501ba1a (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.c235
-rw-r--r--drivers/virtio/virtio_pci_common.h16
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
102static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors, 100static 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;
163error:
164 return err;
165}
166
167/* the config->del_vqs() implementation */
168void 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); 118void 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
215static int vp_find_vqs_msix(struct virtio_device *vdev, unsigned nvqs, 144static 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
293error_find: 250out_remove_vqs:
294 vp_del_vqs(vdev); 251 vp_remove_vqs(vdev);
252 kfree(vp_dev->msix_vector_map);
253out_disable_config_irq:
254 vp_dev->config_vector(vp_dev, VIRTIO_MSI_NO_VECTOR);
255out_free_config_irq:
256 free_irq(pci_irq_vector(vp_dev->pci_dev, 0), vp_dev);
257out_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);
263out_free_msix_names:
264 kfree(vp_dev->msix_names);
265out_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;
325out_del_vqs: 296
326 vp_del_vqs(vdev); 297out_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. */
95enum {
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 */
101static struct virtio_pci_device *to_vp_device(struct virtio_device *vdev) 89static struct virtio_pci_device *to_vp_device(struct virtio_device *vdev)
102{ 90{