aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCornelia Huck <cornelia.huck@de.ibm.com>2013-02-06 04:23:39 -0500
committerChristian Borntraeger <borntraeger@de.ibm.com>2014-03-04 04:41:04 -0500
commit96b14536d935848cffd904f583f67c66169002d8 (patch)
tree87d6be1ed35f1d12f0da33cc88a533bc0fd1afd9
parent84ec96a6150477b9509664557bc6ad4eaa21f72a (diff)
virtio-ccw: virtio-ccw adapter interrupt support.
Implement the new CCW_CMD_SET_IND_ADAPTER command and try to enable adapter interrupts for every device on the first startup. If the host does not support adapter interrupts, fall back to normal I/O interrupts. virtio-ccw adapter interrupts use the same isc as normal I/O subchannels and share a summary indicator for all devices sharing the same indicator area. Indicator bits for the individual virtqueues may be contained in the same indicator area for different devices. Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com> Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
-rw-r--r--arch/s390/include/asm/irq.h1
-rw-r--r--arch/s390/kernel/irq.c1
-rw-r--r--drivers/s390/kvm/virtio_ccw.c278
3 files changed, 270 insertions, 10 deletions
diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h
index 5f8bcc5fe423..35f0faab5361 100644
--- a/arch/s390/include/asm/irq.h
+++ b/arch/s390/include/asm/irq.h
@@ -53,6 +53,7 @@ enum interruption_class {
53 IRQIO_PCI, 53 IRQIO_PCI,
54 IRQIO_MSI, 54 IRQIO_MSI,
55 IRQIO_VIR, 55 IRQIO_VIR,
56 IRQIO_VAI,
56 NMI_NMI, 57 NMI_NMI,
57 CPU_RST, 58 CPU_RST,
58 NR_ARCH_IRQS 59 NR_ARCH_IRQS
diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c
index bb27a262c44a..c288ef7e47b4 100644
--- a/arch/s390/kernel/irq.c
+++ b/arch/s390/kernel/irq.c
@@ -84,6 +84,7 @@ static const struct irq_class irqclass_sub_desc[NR_ARCH_IRQS] = {
84 [IRQIO_PCI] = {.name = "PCI", .desc = "[I/O] PCI Interrupt" }, 84 [IRQIO_PCI] = {.name = "PCI", .desc = "[I/O] PCI Interrupt" },
85 [IRQIO_MSI] = {.name = "MSI", .desc = "[I/O] MSI Interrupt" }, 85 [IRQIO_MSI] = {.name = "MSI", .desc = "[I/O] MSI Interrupt" },
86 [IRQIO_VIR] = {.name = "VIR", .desc = "[I/O] Virtual I/O Devices"}, 86 [IRQIO_VIR] = {.name = "VIR", .desc = "[I/O] Virtual I/O Devices"},
87 [IRQIO_VAI] = {.name = "VAI", .desc = "[I/O] Virtual I/O Devices AI"},
87 [NMI_NMI] = {.name = "NMI", .desc = "[NMI] Machine Check"}, 88 [NMI_NMI] = {.name = "NMI", .desc = "[NMI] Machine Check"},
88 [CPU_RST] = {.name = "RST", .desc = "[CPU] CPU Restart"}, 89 [CPU_RST] = {.name = "RST", .desc = "[CPU] CPU Restart"},
89}; 90};
diff --git a/drivers/s390/kvm/virtio_ccw.c b/drivers/s390/kvm/virtio_ccw.c
index 413c6304d511..6a2b5fdcd552 100644
--- a/drivers/s390/kvm/virtio_ccw.c
+++ b/drivers/s390/kvm/virtio_ccw.c
@@ -1,7 +1,7 @@
1/* 1/*
2 * ccw based virtio transport 2 * ccw based virtio transport
3 * 3 *
4 * Copyright IBM Corp. 2012 4 * Copyright IBM Corp. 2012, 2014
5 * 5 *
6 * This program is free software; you can redistribute it and/or modify 6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License (version 2 only) 7 * it under the terms of the GNU General Public License (version 2 only)
@@ -32,6 +32,8 @@
32#include <asm/cio.h> 32#include <asm/cio.h>
33#include <asm/ccwdev.h> 33#include <asm/ccwdev.h>
34#include <asm/virtio-ccw.h> 34#include <asm/virtio-ccw.h>
35#include <asm/isc.h>
36#include <asm/airq.h>
35 37
36/* 38/*
37 * virtio related functions 39 * virtio related functions
@@ -58,6 +60,8 @@ struct virtio_ccw_device {
58 unsigned long indicators; 60 unsigned long indicators;
59 unsigned long indicators2; 61 unsigned long indicators2;
60 struct vq_config_block *config_block; 62 struct vq_config_block *config_block;
63 bool is_thinint;
64 void *airq_info;
61}; 65};
62 66
63struct vq_info_block { 67struct vq_info_block {
@@ -72,15 +76,38 @@ struct virtio_feature_desc {
72 __u8 index; 76 __u8 index;
73} __packed; 77} __packed;
74 78
79struct virtio_thinint_area {
80 unsigned long summary_indicator;
81 unsigned long indicator;
82 u64 bit_nr;
83 u8 isc;
84} __packed;
85
75struct virtio_ccw_vq_info { 86struct virtio_ccw_vq_info {
76 struct virtqueue *vq; 87 struct virtqueue *vq;
77 int num; 88 int num;
78 void *queue; 89 void *queue;
79 struct vq_info_block *info_block; 90 struct vq_info_block *info_block;
91 int bit_nr;
80 struct list_head node; 92 struct list_head node;
81 long cookie; 93 long cookie;
82}; 94};
83 95
96#define VIRTIO_AIRQ_ISC IO_SCH_ISC /* inherit from subchannel */
97
98#define VIRTIO_IV_BITS (L1_CACHE_BYTES * 8)
99#define MAX_AIRQ_AREAS 20
100
101static int virtio_ccw_use_airq = 1;
102
103struct airq_info {
104 rwlock_t lock;
105 u8 summary_indicator;
106 struct airq_struct airq;
107 struct airq_iv *aiv;
108};
109static struct airq_info *airq_areas[MAX_AIRQ_AREAS];
110
84#define CCW_CMD_SET_VQ 0x13 111#define CCW_CMD_SET_VQ 0x13
85#define CCW_CMD_VDEV_RESET 0x33 112#define CCW_CMD_VDEV_RESET 0x33
86#define CCW_CMD_SET_IND 0x43 113#define CCW_CMD_SET_IND 0x43
@@ -91,6 +118,7 @@ struct virtio_ccw_vq_info {
91#define CCW_CMD_WRITE_CONF 0x21 118#define CCW_CMD_WRITE_CONF 0x21
92#define CCW_CMD_WRITE_STATUS 0x31 119#define CCW_CMD_WRITE_STATUS 0x31
93#define CCW_CMD_READ_VQ_CONF 0x32 120#define CCW_CMD_READ_VQ_CONF 0x32
121#define CCW_CMD_SET_IND_ADAPTER 0x73
94 122
95#define VIRTIO_CCW_DOING_SET_VQ 0x00010000 123#define VIRTIO_CCW_DOING_SET_VQ 0x00010000
96#define VIRTIO_CCW_DOING_RESET 0x00040000 124#define VIRTIO_CCW_DOING_RESET 0x00040000
@@ -102,6 +130,7 @@ struct virtio_ccw_vq_info {
102#define VIRTIO_CCW_DOING_SET_IND 0x01000000 130#define VIRTIO_CCW_DOING_SET_IND 0x01000000
103#define VIRTIO_CCW_DOING_READ_VQ_CONF 0x02000000 131#define VIRTIO_CCW_DOING_READ_VQ_CONF 0x02000000
104#define VIRTIO_CCW_DOING_SET_CONF_IND 0x04000000 132#define VIRTIO_CCW_DOING_SET_CONF_IND 0x04000000
133#define VIRTIO_CCW_DOING_SET_IND_ADAPTER 0x08000000
105#define VIRTIO_CCW_INTPARM_MASK 0xffff0000 134#define VIRTIO_CCW_INTPARM_MASK 0xffff0000
106 135
107static struct virtio_ccw_device *to_vc_device(struct virtio_device *vdev) 136static struct virtio_ccw_device *to_vc_device(struct virtio_device *vdev)
@@ -109,6 +138,125 @@ static struct virtio_ccw_device *to_vc_device(struct virtio_device *vdev)
109 return container_of(vdev, struct virtio_ccw_device, vdev); 138 return container_of(vdev, struct virtio_ccw_device, vdev);
110} 139}
111 140
141static void drop_airq_indicator(struct virtqueue *vq, struct airq_info *info)
142{
143 unsigned long i, flags;
144
145 write_lock_irqsave(&info->lock, flags);
146 for (i = 0; i < airq_iv_end(info->aiv); i++) {
147 if (vq == (void *)airq_iv_get_ptr(info->aiv, i)) {
148 airq_iv_free_bit(info->aiv, i);
149 airq_iv_set_ptr(info->aiv, i, 0);
150 break;
151 }
152 }
153 write_unlock_irqrestore(&info->lock, flags);
154}
155
156static void virtio_airq_handler(struct airq_struct *airq)
157{
158 struct airq_info *info = container_of(airq, struct airq_info, airq);
159 unsigned long ai;
160
161 inc_irq_stat(IRQIO_VAI);
162 read_lock(&info->lock);
163 /* Walk through indicators field, summary indicator active. */
164 for (ai = 0;;) {
165 ai = airq_iv_scan(info->aiv, ai, airq_iv_end(info->aiv));
166 if (ai == -1UL)
167 break;
168 vring_interrupt(0, (void *)airq_iv_get_ptr(info->aiv, ai));
169 }
170 info->summary_indicator = 0;
171 smp_wmb();
172 /* Walk through indicators field, summary indicator not active. */
173 for (ai = 0;;) {
174 ai = airq_iv_scan(info->aiv, ai, airq_iv_end(info->aiv));
175 if (ai == -1UL)
176 break;
177 vring_interrupt(0, (void *)airq_iv_get_ptr(info->aiv, ai));
178 }
179 read_unlock(&info->lock);
180}
181
182static struct airq_info *new_airq_info(void)
183{
184 struct airq_info *info;
185 int rc;
186
187 info = kzalloc(sizeof(*info), GFP_KERNEL);
188 if (!info)
189 return NULL;
190 rwlock_init(&info->lock);
191 info->aiv = airq_iv_create(VIRTIO_IV_BITS, AIRQ_IV_ALLOC | AIRQ_IV_PTR);
192 if (!info->aiv) {
193 kfree(info);
194 return NULL;
195 }
196 info->airq.handler = virtio_airq_handler;
197 info->airq.lsi_ptr = &info->summary_indicator;
198 info->airq.lsi_mask = 0xff;
199 info->airq.isc = VIRTIO_AIRQ_ISC;
200 rc = register_adapter_interrupt(&info->airq);
201 if (rc) {
202 airq_iv_release(info->aiv);
203 kfree(info);
204 return NULL;
205 }
206 return info;
207}
208
209static void destroy_airq_info(struct airq_info *info)
210{
211 if (!info)
212 return;
213
214 unregister_adapter_interrupt(&info->airq);
215 airq_iv_release(info->aiv);
216 kfree(info);
217}
218
219static unsigned long get_airq_indicator(struct virtqueue *vqs[], int nvqs,
220 u64 *first, void **airq_info)
221{
222 int i, j;
223 struct airq_info *info;
224 unsigned long indicator_addr = 0;
225 unsigned long bit, flags;
226
227 for (i = 0; i < MAX_AIRQ_AREAS && !indicator_addr; i++) {
228 if (!airq_areas[i])
229 airq_areas[i] = new_airq_info();
230 info = airq_areas[i];
231 if (!info)
232 return 0;
233 write_lock_irqsave(&info->lock, flags);
234 bit = airq_iv_alloc(info->aiv, nvqs);
235 if (bit == -1UL) {
236 /* Not enough vacancies. */
237 write_unlock_irqrestore(&info->lock, flags);
238 continue;
239 }
240 *first = bit;
241 *airq_info = info;
242 indicator_addr = (unsigned long)info->aiv->vector;
243 for (j = 0; j < nvqs; j++) {
244 airq_iv_set_ptr(info->aiv, bit + j,
245 (unsigned long)vqs[j]);
246 }
247 write_unlock_irqrestore(&info->lock, flags);
248 }
249 return indicator_addr;
250}
251
252static void virtio_ccw_drop_indicators(struct virtio_ccw_device *vcdev)
253{
254 struct virtio_ccw_vq_info *info;
255
256 list_for_each_entry(info, &vcdev->virtqueues, node)
257 drop_airq_indicator(info->vq, vcdev->airq_info);
258}
259
112static int doing_io(struct virtio_ccw_device *vcdev, __u32 flag) 260static int doing_io(struct virtio_ccw_device *vcdev, __u32 flag)
113{ 261{
114 unsigned long flags; 262 unsigned long flags;
@@ -145,6 +293,51 @@ static int ccw_io_helper(struct virtio_ccw_device *vcdev,
145 return ret ? ret : vcdev->err; 293 return ret ? ret : vcdev->err;
146} 294}
147 295
296static void virtio_ccw_drop_indicator(struct virtio_ccw_device *vcdev,
297 struct ccw1 *ccw)
298{
299 int ret;
300 unsigned long *indicatorp = NULL;
301 struct virtio_thinint_area *thinint_area = NULL;
302 struct airq_info *airq_info = vcdev->airq_info;
303
304 if (vcdev->is_thinint) {
305 thinint_area = kzalloc(sizeof(*thinint_area),
306 GFP_DMA | GFP_KERNEL);
307 if (!thinint_area)
308 return;
309 thinint_area->summary_indicator =
310 (unsigned long) &airq_info->summary_indicator;
311 thinint_area->isc = VIRTIO_AIRQ_ISC;
312 ccw->cmd_code = CCW_CMD_SET_IND_ADAPTER;
313 ccw->count = sizeof(*thinint_area);
314 ccw->cda = (__u32)(unsigned long) thinint_area;
315 } else {
316 indicatorp = kmalloc(sizeof(&vcdev->indicators),
317 GFP_DMA | GFP_KERNEL);
318 if (!indicatorp)
319 return;
320 *indicatorp = 0;
321 ccw->cmd_code = CCW_CMD_SET_IND;
322 ccw->count = sizeof(vcdev->indicators);
323 ccw->cda = (__u32)(unsigned long) indicatorp;
324 }
325 /* Deregister indicators from host. */
326 vcdev->indicators = 0;
327 ccw->flags = 0;
328 ret = ccw_io_helper(vcdev, ccw,
329 vcdev->is_thinint ?
330 VIRTIO_CCW_DOING_SET_IND_ADAPTER :
331 VIRTIO_CCW_DOING_SET_IND);
332 if (ret && (ret != -ENODEV))
333 dev_info(&vcdev->cdev->dev,
334 "Failed to deregister indicators (%d)\n", ret);
335 else if (vcdev->is_thinint)
336 virtio_ccw_drop_indicators(vcdev);
337 kfree(indicatorp);
338 kfree(thinint_area);
339}
340
148static inline long do_kvm_notify(struct subchannel_id schid, 341static inline long do_kvm_notify(struct subchannel_id schid,
149 unsigned long queue_index, 342 unsigned long queue_index,
150 long cookie) 343 long cookie)
@@ -232,11 +425,13 @@ static void virtio_ccw_del_vqs(struct virtio_device *vdev)
232{ 425{
233 struct virtqueue *vq, *n; 426 struct virtqueue *vq, *n;
234 struct ccw1 *ccw; 427 struct ccw1 *ccw;
428 struct virtio_ccw_device *vcdev = to_vc_device(vdev);
235 429
236 ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); 430 ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL);
237 if (!ccw) 431 if (!ccw)
238 return; 432 return;
239 433
434 virtio_ccw_drop_indicator(vcdev, ccw);
240 435
241 list_for_each_entry_safe(vq, n, &vdev->vqs, list) 436 list_for_each_entry_safe(vq, n, &vdev->vqs, list)
242 virtio_ccw_del_vq(vq, ccw); 437 virtio_ccw_del_vq(vq, ccw);
@@ -326,6 +521,54 @@ out_err:
326 return ERR_PTR(err); 521 return ERR_PTR(err);
327} 522}
328 523
524static int virtio_ccw_register_adapter_ind(struct virtio_ccw_device *vcdev,
525 struct virtqueue *vqs[], int nvqs,
526 struct ccw1 *ccw)
527{
528 int ret;
529 struct virtio_thinint_area *thinint_area = NULL;
530 struct airq_info *info;
531
532 thinint_area = kzalloc(sizeof(*thinint_area), GFP_DMA | GFP_KERNEL);
533 if (!thinint_area) {
534 ret = -ENOMEM;
535 goto out;
536 }
537 /* Try to get an indicator. */
538 thinint_area->indicator = get_airq_indicator(vqs, nvqs,
539 &thinint_area->bit_nr,
540 &vcdev->airq_info);
541 if (!thinint_area->indicator) {
542 ret = -ENOSPC;
543 goto out;
544 }
545 info = vcdev->airq_info;
546 thinint_area->summary_indicator =
547 (unsigned long) &info->summary_indicator;
548 thinint_area->isc = VIRTIO_AIRQ_ISC;
549 ccw->cmd_code = CCW_CMD_SET_IND_ADAPTER;
550 ccw->flags = CCW_FLAG_SLI;
551 ccw->count = sizeof(*thinint_area);
552 ccw->cda = (__u32)(unsigned long)thinint_area;
553 ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_IND_ADAPTER);
554 if (ret) {
555 if (ret == -EOPNOTSUPP) {
556 /*
557 * The host does not support adapter interrupts
558 * for virtio-ccw, stop trying.
559 */
560 virtio_ccw_use_airq = 0;
561 pr_info("Adapter interrupts unsupported on host\n");
562 } else
563 dev_warn(&vcdev->cdev->dev,
564 "enabling adapter interrupts = %d\n", ret);
565 virtio_ccw_drop_indicators(vcdev);
566 }
567out:
568 kfree(thinint_area);
569 return ret;
570}
571
329static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs, 572static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs,
330 struct virtqueue *vqs[], 573 struct virtqueue *vqs[],
331 vq_callback_t *callbacks[], 574 vq_callback_t *callbacks[],
@@ -355,15 +598,23 @@ static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs,
355 if (!indicatorp) 598 if (!indicatorp)
356 goto out; 599 goto out;
357 *indicatorp = (unsigned long) &vcdev->indicators; 600 *indicatorp = (unsigned long) &vcdev->indicators;
358 /* Register queue indicators with host. */ 601 if (vcdev->is_thinint) {
359 vcdev->indicators = 0; 602 ret = virtio_ccw_register_adapter_ind(vcdev, vqs, nvqs, ccw);
360 ccw->cmd_code = CCW_CMD_SET_IND; 603 if (ret)
361 ccw->flags = 0; 604 /* no error, just fall back to legacy interrupts */
362 ccw->count = sizeof(vcdev->indicators); 605 vcdev->is_thinint = 0;
363 ccw->cda = (__u32)(unsigned long) indicatorp; 606 }
364 ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_IND); 607 if (!vcdev->is_thinint) {
365 if (ret) 608 /* Register queue indicators with host. */
366 goto out; 609 vcdev->indicators = 0;
610 ccw->cmd_code = CCW_CMD_SET_IND;
611 ccw->flags = 0;
612 ccw->count = sizeof(vcdev->indicators);
613 ccw->cda = (__u32)(unsigned long) indicatorp;
614 ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_IND);
615 if (ret)
616 goto out;
617 }
367 /* Register indicators2 with host for config changes */ 618 /* Register indicators2 with host for config changes */
368 *indicatorp = (unsigned long) &vcdev->indicators2; 619 *indicatorp = (unsigned long) &vcdev->indicators2;
369 vcdev->indicators2 = 0; 620 vcdev->indicators2 = 0;
@@ -665,6 +916,7 @@ static void virtio_ccw_int_handler(struct ccw_device *cdev,
665 case VIRTIO_CCW_DOING_SET_CONF_IND: 916 case VIRTIO_CCW_DOING_SET_CONF_IND:
666 case VIRTIO_CCW_DOING_RESET: 917 case VIRTIO_CCW_DOING_RESET:
667 case VIRTIO_CCW_DOING_READ_VQ_CONF: 918 case VIRTIO_CCW_DOING_READ_VQ_CONF:
919 case VIRTIO_CCW_DOING_SET_IND_ADAPTER:
668 vcdev->curr_io &= ~activity; 920 vcdev->curr_io &= ~activity;
669 wake_up(&vcdev->wait_q); 921 wake_up(&vcdev->wait_q);
670 break; 922 break;
@@ -795,6 +1047,8 @@ static int virtio_ccw_online(struct ccw_device *cdev)
795 goto out_free; 1047 goto out_free;
796 } 1048 }
797 1049
1050 vcdev->is_thinint = virtio_ccw_use_airq; /* at least try */
1051
798 vcdev->vdev.dev.parent = &cdev->dev; 1052 vcdev->vdev.dev.parent = &cdev->dev;
799 vcdev->vdev.dev.release = virtio_ccw_release_dev; 1053 vcdev->vdev.dev.release = virtio_ccw_release_dev;
800 vcdev->vdev.config = &virtio_ccw_config_ops; 1054 vcdev->vdev.config = &virtio_ccw_config_ops;
@@ -956,6 +1210,10 @@ module_init(virtio_ccw_init);
956 1210
957static void __exit virtio_ccw_exit(void) 1211static void __exit virtio_ccw_exit(void)
958{ 1212{
1213 int i;
1214
959 ccw_driver_unregister(&virtio_ccw_driver); 1215 ccw_driver_unregister(&virtio_ccw_driver);
1216 for (i = 0; i < MAX_AIRQ_AREAS; i++)
1217 destroy_airq_info(airq_areas[i]);
960} 1218}
961module_exit(virtio_ccw_exit); 1219module_exit(virtio_ccw_exit);