blob: a25367ebaa89595971f859e56f4142434e12e4e7 [file] [log] [blame]
Dong Jia Shi63f19342017-03-17 04:17:31 +01001/*
2 * VFIO based Physical Subchannel device driver
3 *
4 * Copyright IBM Corp. 2017
5 *
6 * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
7 * Xiao Feng Ren <renxiaof@linux.vnet.ibm.com>
8 */
9
10#include <linux/module.h>
11#include <linux/init.h>
12#include <linux/device.h>
13#include <linux/slab.h>
Dong Jia Shi4e149e42017-03-17 04:17:35 +010014#include <linux/uuid.h>
15#include <linux/mdev.h>
Dong Jia Shi63f19342017-03-17 04:17:31 +010016
17#include <asm/isc.h>
18
Dong Jia Shi4e149e42017-03-17 04:17:35 +010019#include "ioasm.h"
20#include "css.h"
Dong Jia Shi63f19342017-03-17 04:17:31 +010021#include "vfio_ccw_private.h"
22
Dong Jia Shie5f84db2017-03-17 04:17:39 +010023struct workqueue_struct *vfio_ccw_work_q;
24
Dong Jia Shi63f19342017-03-17 04:17:31 +010025/*
26 * Helpers
27 */
Dong Jia Shi84cd8fc2017-03-17 04:17:33 +010028int vfio_ccw_sch_quiesce(struct subchannel *sch)
Dong Jia Shi63f19342017-03-17 04:17:31 +010029{
30 struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
31 DECLARE_COMPLETION_ONSTACK(completion);
32 int iretry, ret = 0;
33
34 spin_lock_irq(sch->lock);
35 if (!sch->schib.pmcw.ena)
36 goto out_unlock;
37 ret = cio_disable_subchannel(sch);
38 if (ret != -EBUSY)
39 goto out_unlock;
40
41 do {
42 iretry = 255;
43
44 ret = cio_cancel_halt_clear(sch, &iretry);
45 while (ret == -EBUSY) {
46 /*
47 * Flush all I/O and wait for
48 * cancel/halt/clear completion.
49 */
50 private->completion = &completion;
51 spin_unlock_irq(sch->lock);
52
53 wait_for_completion_timeout(&completion, 3*HZ);
54
55 spin_lock_irq(sch->lock);
56 private->completion = NULL;
Dong Jia Shie5f84db2017-03-17 04:17:39 +010057 flush_workqueue(vfio_ccw_work_q);
Dong Jia Shi63f19342017-03-17 04:17:31 +010058 ret = cio_cancel_halt_clear(sch, &iretry);
59 };
60
61 ret = cio_disable_subchannel(sch);
62 } while (ret == -EBUSY);
Dong Jia Shi63f19342017-03-17 04:17:31 +010063out_unlock:
Dong Jia Shibbe37e42017-03-17 04:17:40 +010064 private->state = VFIO_CCW_STATE_NOT_OPER;
Dong Jia Shi63f19342017-03-17 04:17:31 +010065 spin_unlock_irq(sch->lock);
66 return ret;
67}
68
Dong Jia Shie5f84db2017-03-17 04:17:39 +010069static void vfio_ccw_sch_io_todo(struct work_struct *work)
70{
71 struct vfio_ccw_private *private;
72 struct subchannel *sch;
73 struct irb *irb;
Dong Jia Shi4e149e42017-03-17 04:17:35 +010074
Dong Jia Shie5f84db2017-03-17 04:17:39 +010075 private = container_of(work, struct vfio_ccw_private, io_work);
76 irb = &private->irb;
77 sch = private->sch;
Dong Jia Shi4e149e42017-03-17 04:17:35 +010078
Dong Jia Shie5f84db2017-03-17 04:17:39 +010079 if (scsw_is_solicited(&irb->scsw)) {
80 cp_update_scsw(&private->cp, &irb->scsw);
81 cp_free(&private->cp);
82 }
83 memcpy(private->io_region.irb_area, irb, sizeof(*irb));
84
85 if (private->io_trigger)
86 eventfd_signal(private->io_trigger, 1);
Dong Jia Shi4e149e42017-03-17 04:17:35 +010087
Dong Jia Shibbe37e42017-03-17 04:17:40 +010088 if (private->mdev)
89 private->state = VFIO_CCW_STATE_IDLE;
Dong Jia Shi4e149e42017-03-17 04:17:35 +010090}
91
Dong Jia Shi63f19342017-03-17 04:17:31 +010092/*
Dong Jia Shi63f19342017-03-17 04:17:31 +010093 * Css driver callbacks
94 */
95static void vfio_ccw_sch_irq(struct subchannel *sch)
96{
97 struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
98
99 inc_irq_stat(IRQIO_CIO);
Dong Jia Shibbe37e42017-03-17 04:17:40 +0100100 vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_INTERRUPT);
Dong Jia Shi63f19342017-03-17 04:17:31 +0100101}
102
103static int vfio_ccw_sch_probe(struct subchannel *sch)
104{
105 struct pmcw *pmcw = &sch->schib.pmcw;
106 struct vfio_ccw_private *private;
107 int ret;
108
109 if (pmcw->qf) {
110 dev_warn(&sch->dev, "vfio: ccw: does not support QDIO: %s\n",
111 dev_name(&sch->dev));
112 return -ENODEV;
113 }
114
115 private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA);
116 if (!private)
117 return -ENOMEM;
118 private->sch = sch;
119 dev_set_drvdata(&sch->dev, private);
120
121 spin_lock_irq(sch->lock);
Dong Jia Shibbe37e42017-03-17 04:17:40 +0100122 private->state = VFIO_CCW_STATE_NOT_OPER;
Dong Jia Shi63f19342017-03-17 04:17:31 +0100123 sch->isc = VFIO_CCW_ISC;
124 ret = cio_enable_subchannel(sch, (u32)(unsigned long)sch);
125 spin_unlock_irq(sch->lock);
126 if (ret)
127 goto out_free;
128
Dong Jia Shi84cd8fc2017-03-17 04:17:33 +0100129 ret = vfio_ccw_mdev_reg(sch);
130 if (ret)
Sebastian Ott36f62372017-05-15 15:49:07 +0200131 goto out_disable;
Dong Jia Shi84cd8fc2017-03-17 04:17:33 +0100132
Dong Jia Shie5f84db2017-03-17 04:17:39 +0100133 INIT_WORK(&private->io_work, vfio_ccw_sch_io_todo);
Dong Jia Shi84cd8fc2017-03-17 04:17:33 +0100134 atomic_set(&private->avail, 1);
Dong Jia Shibbe37e42017-03-17 04:17:40 +0100135 private->state = VFIO_CCW_STATE_STANDBY;
Dong Jia Shi84cd8fc2017-03-17 04:17:33 +0100136
Dong Jia Shi63f19342017-03-17 04:17:31 +0100137 return 0;
138
139out_disable:
140 cio_disable_subchannel(sch);
141out_free:
142 dev_set_drvdata(&sch->dev, NULL);
143 kfree(private);
144 return ret;
145}
146
147static int vfio_ccw_sch_remove(struct subchannel *sch)
148{
149 struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
150
151 vfio_ccw_sch_quiesce(sch);
152
Dong Jia Shi84cd8fc2017-03-17 04:17:33 +0100153 vfio_ccw_mdev_unreg(sch);
154
Dong Jia Shi63f19342017-03-17 04:17:31 +0100155 dev_set_drvdata(&sch->dev, NULL);
156
157 kfree(private);
158
159 return 0;
160}
161
162static void vfio_ccw_sch_shutdown(struct subchannel *sch)
163{
164 vfio_ccw_sch_quiesce(sch);
165}
166
167/**
168 * vfio_ccw_sch_event - process subchannel event
169 * @sch: subchannel
170 * @process: non-zero if function is called in process context
171 *
172 * An unspecified event occurred for this subchannel. Adjust data according
173 * to the current operational state of the subchannel. Return zero when the
174 * event has been handled sufficiently or -EAGAIN when this function should
175 * be called again in process context.
176 */
177static int vfio_ccw_sch_event(struct subchannel *sch, int process)
178{
Dong Jia Shibbe37e42017-03-17 04:17:40 +0100179 struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
Dong Jia Shi63f19342017-03-17 04:17:31 +0100180 unsigned long flags;
181
182 spin_lock_irqsave(sch->lock, flags);
183 if (!device_is_registered(&sch->dev))
184 goto out_unlock;
185
186 if (work_pending(&sch->todo_work))
187 goto out_unlock;
188
189 if (cio_update_schib(sch)) {
Dong Jia Shibbe37e42017-03-17 04:17:40 +0100190 vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_NOT_OPER);
Dong Jia Shi63f19342017-03-17 04:17:31 +0100191 goto out_unlock;
192 }
193
Dong Jia Shibbe37e42017-03-17 04:17:40 +0100194 private = dev_get_drvdata(&sch->dev);
195 if (private->state == VFIO_CCW_STATE_NOT_OPER) {
196 private->state = private->mdev ? VFIO_CCW_STATE_IDLE :
197 VFIO_CCW_STATE_STANDBY;
198 }
199
Dong Jia Shi63f19342017-03-17 04:17:31 +0100200out_unlock:
201 spin_unlock_irqrestore(sch->lock, flags);
202
203 return 0;
204}
205
206static struct css_device_id vfio_ccw_sch_ids[] = {
207 { .match_flags = 0x1, .type = SUBCHANNEL_TYPE_IO, },
208 { /* end of list */ },
209};
210MODULE_DEVICE_TABLE(css, vfio_ccw_sch_ids);
211
212static struct css_driver vfio_ccw_sch_driver = {
213 .drv = {
214 .name = "vfio_ccw",
215 .owner = THIS_MODULE,
216 },
217 .subchannel_type = vfio_ccw_sch_ids,
218 .irq = vfio_ccw_sch_irq,
219 .probe = vfio_ccw_sch_probe,
220 .remove = vfio_ccw_sch_remove,
221 .shutdown = vfio_ccw_sch_shutdown,
222 .sch_event = vfio_ccw_sch_event,
223};
224
225static int __init vfio_ccw_sch_init(void)
226{
227 int ret;
228
Dong Jia Shie5f84db2017-03-17 04:17:39 +0100229 vfio_ccw_work_q = create_singlethread_workqueue("vfio-ccw");
230 if (!vfio_ccw_work_q)
231 return -ENOMEM;
232
Dong Jia Shi63f19342017-03-17 04:17:31 +0100233 isc_register(VFIO_CCW_ISC);
234 ret = css_driver_register(&vfio_ccw_sch_driver);
Dong Jia Shie5f84db2017-03-17 04:17:39 +0100235 if (ret) {
Dong Jia Shi63f19342017-03-17 04:17:31 +0100236 isc_unregister(VFIO_CCW_ISC);
Dong Jia Shie5f84db2017-03-17 04:17:39 +0100237 destroy_workqueue(vfio_ccw_work_q);
238 }
Dong Jia Shi63f19342017-03-17 04:17:31 +0100239
240 return ret;
241}
242
243static void __exit vfio_ccw_sch_exit(void)
244{
245 css_driver_unregister(&vfio_ccw_sch_driver);
246 isc_unregister(VFIO_CCW_ISC);
Dong Jia Shie5f84db2017-03-17 04:17:39 +0100247 destroy_workqueue(vfio_ccw_work_q);
Dong Jia Shi63f19342017-03-17 04:17:31 +0100248}
249module_init(vfio_ccw_sch_init);
250module_exit(vfio_ccw_sch_exit);
251
252MODULE_LICENSE("GPL v2");