1724117b7SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
263f1934dSDong Jia Shi /*
363f1934dSDong Jia Shi * VFIO based Physical Subchannel device driver
463f1934dSDong Jia Shi *
563f1934dSDong Jia Shi * Copyright IBM Corp. 2017
6d5afd5d1SCornelia Huck * Copyright Red Hat, Inc. 2019
763f1934dSDong Jia Shi *
863f1934dSDong Jia Shi * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
963f1934dSDong Jia Shi * Xiao Feng Ren <renxiaof@linux.vnet.ibm.com>
10d5afd5d1SCornelia Huck * Cornelia Huck <cohuck@redhat.com>
1163f1934dSDong Jia Shi */
1263f1934dSDong Jia Shi
1363f1934dSDong Jia Shi #include <linux/module.h>
1463f1934dSDong Jia Shi #include <linux/init.h>
1563f1934dSDong Jia Shi #include <linux/slab.h>
164e149e43SDong Jia Shi #include <linux/mdev.h>
1763f1934dSDong Jia Shi
1863f1934dSDong Jia Shi #include <asm/isc.h>
1963f1934dSDong Jia Shi
20b7701dfbSFarhan Ali #include "chp.h"
214e149e43SDong Jia Shi #include "ioasm.h"
224e149e43SDong Jia Shi #include "css.h"
2363f1934dSDong Jia Shi #include "vfio_ccw_private.h"
2463f1934dSDong Jia Shi
25e5f84dbaSDong Jia Shi struct workqueue_struct *vfio_ccw_work_q;
2606caaa27SEric Farman struct kmem_cache *vfio_ccw_io_region;
2706caaa27SEric Farman struct kmem_cache *vfio_ccw_cmd_region;
2806caaa27SEric Farman struct kmem_cache *vfio_ccw_schib_region;
2906caaa27SEric Farman struct kmem_cache *vfio_ccw_crw_region;
30e5f84dbaSDong Jia Shi
3160e05d1cSCornelia Huck debug_info_t *vfio_ccw_debug_msg_id;
3260e05d1cSCornelia Huck debug_info_t *vfio_ccw_debug_trace_id;
3360e05d1cSCornelia Huck
3463f1934dSDong Jia Shi /*
3563f1934dSDong Jia Shi * Helpers
3663f1934dSDong Jia Shi */
vfio_ccw_sch_quiesce(struct subchannel * sch)3784cd8fc4SDong Jia Shi int vfio_ccw_sch_quiesce(struct subchannel *sch)
3863f1934dSDong Jia Shi {
399e6f07cdSEric Farman struct vfio_ccw_parent *parent = dev_get_drvdata(&sch->dev);
409e6f07cdSEric Farman struct vfio_ccw_private *private = dev_get_drvdata(&parent->dev);
4163f1934dSDong Jia Shi DECLARE_COMPLETION_ONSTACK(completion);
4263f1934dSDong Jia Shi int iretry, ret = 0;
4363f1934dSDong Jia Shi
449e6f07cdSEric Farman /*
459e6f07cdSEric Farman * Probably an impossible situation, after being called through
469e6f07cdSEric Farman * FSM callbacks. But in the event it did, register a warning
479e6f07cdSEric Farman * and return as if things were fine.
489e6f07cdSEric Farman */
499e6f07cdSEric Farman if (WARN_ON(!private))
509e6f07cdSEric Farman return 0;
519e6f07cdSEric Farman
5263f1934dSDong Jia Shi iretry = 255;
53d1ffa760SFarhan Ali do {
5463f1934dSDong Jia Shi
5563f1934dSDong Jia Shi ret = cio_cancel_halt_clear(sch, &iretry);
56d1ffa760SFarhan Ali
57d1ffa760SFarhan Ali if (ret == -EIO) {
58d1ffa760SFarhan Ali pr_err("vfio_ccw: could not quiesce subchannel 0.%x.%04x!\n",
59d1ffa760SFarhan Ali sch->schid.ssid, sch->schid.sch_no);
60d1ffa760SFarhan Ali break;
61d1ffa760SFarhan Ali }
62d1ffa760SFarhan Ali
6363f1934dSDong Jia Shi /*
6463f1934dSDong Jia Shi * Flush all I/O and wait for
6563f1934dSDong Jia Shi * cancel/halt/clear completion.
6663f1934dSDong Jia Shi */
6763f1934dSDong Jia Shi private->completion = &completion;
6863f1934dSDong Jia Shi spin_unlock_irq(sch->lock);
6963f1934dSDong Jia Shi
70d1ffa760SFarhan Ali if (ret == -EBUSY)
7163f1934dSDong Jia Shi wait_for_completion_timeout(&completion, 3*HZ);
7263f1934dSDong Jia Shi
7363f1934dSDong Jia Shi private->completion = NULL;
74e5f84dbaSDong Jia Shi flush_workqueue(vfio_ccw_work_q);
75cea5dde4SFarhan Ali spin_lock_irq(sch->lock);
7663f1934dSDong Jia Shi ret = cio_disable_subchannel(sch);
7763f1934dSDong Jia Shi } while (ret == -EBUSY);
78f4b4ed44SEric Farman
7963f1934dSDong Jia Shi return ret;
8063f1934dSDong Jia Shi }
8163f1934dSDong Jia Shi
vfio_ccw_sch_io_todo(struct work_struct * work)8206caaa27SEric Farman void vfio_ccw_sch_io_todo(struct work_struct *work)
83e5f84dbaSDong Jia Shi {
84e5f84dbaSDong Jia Shi struct vfio_ccw_private *private;
85e5f84dbaSDong Jia Shi struct irb *irb;
8650b7f1b7SCornelia Huck bool is_final;
872af7a834SEric Farman bool cp_is_finished = false;
884e149e43SDong Jia Shi
89e5f84dbaSDong Jia Shi private = container_of(work, struct vfio_ccw_private, io_work);
90e5f84dbaSDong Jia Shi irb = &private->irb;
914e149e43SDong Jia Shi
9250b7f1b7SCornelia Huck is_final = !(scsw_actl(&irb->scsw) &
9350b7f1b7SCornelia Huck (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT));
94e5f84dbaSDong Jia Shi if (scsw_is_solicited(&irb->scsw)) {
95e5f84dbaSDong Jia Shi cp_update_scsw(&private->cp, &irb->scsw);
962af7a834SEric Farman if (is_final && private->state == VFIO_CCW_STATE_CP_PENDING) {
97e5f84dbaSDong Jia Shi cp_free(&private->cp);
982af7a834SEric Farman cp_is_finished = true;
992af7a834SEric Farman }
100e5f84dbaSDong Jia Shi }
1014f766173SCornelia Huck mutex_lock(&private->io_mutex);
102c98e16b2SEric Farman memcpy(private->io_region->irb_area, irb, sizeof(*irb));
1034f766173SCornelia Huck mutex_unlock(&private->io_mutex);
104e5f84dbaSDong Jia Shi
1052af7a834SEric Farman /*
1062af7a834SEric Farman * Reset to IDLE only if processing of a channel program
1072af7a834SEric Farman * has finished. Do not overwrite a possible processing
108f6c876d6SEric Farman * state if the interrupt was unsolicited, or if the final
109f6c876d6SEric Farman * interrupt was for HSCH or CSCH.
1102af7a834SEric Farman */
111f6c876d6SEric Farman if (cp_is_finished)
112bbe37e4cSDong Jia Shi private->state = VFIO_CCW_STATE_IDLE;
1134e31d6aeSEric Farman
1144e31d6aeSEric Farman if (private->io_trigger)
1154e31d6aeSEric Farman eventfd_signal(private->io_trigger, 1);
1164e149e43SDong Jia Shi }
1174e149e43SDong Jia Shi
vfio_ccw_crw_todo(struct work_struct * work)11806caaa27SEric Farman void vfio_ccw_crw_todo(struct work_struct *work)
1193f02cb2fSFarhan Ali {
1203f02cb2fSFarhan Ali struct vfio_ccw_private *private;
1213f02cb2fSFarhan Ali
1223f02cb2fSFarhan Ali private = container_of(work, struct vfio_ccw_private, crw_work);
1233f02cb2fSFarhan Ali
1243f02cb2fSFarhan Ali if (!list_empty(&private->crw) && private->crw_trigger)
1253f02cb2fSFarhan Ali eventfd_signal(private->crw_trigger, 1);
1263f02cb2fSFarhan Ali }
1273f02cb2fSFarhan Ali
12863f1934dSDong Jia Shi /*
12963f1934dSDong Jia Shi * Css driver callbacks
13063f1934dSDong Jia Shi */
vfio_ccw_sch_irq(struct subchannel * sch)13163f1934dSDong Jia Shi static void vfio_ccw_sch_irq(struct subchannel *sch)
13263f1934dSDong Jia Shi {
1339e6f07cdSEric Farman struct vfio_ccw_parent *parent = dev_get_drvdata(&sch->dev);
1349e6f07cdSEric Farman struct vfio_ccw_private *private = dev_get_drvdata(&parent->dev);
1359e6f07cdSEric Farman
1369e6f07cdSEric Farman /*
1379e6f07cdSEric Farman * The subchannel should still be disabled at this point,
1389e6f07cdSEric Farman * so an interrupt would be quite surprising. As with an
1399e6f07cdSEric Farman * interrupt while the FSM is closed, let's attempt to
1409e6f07cdSEric Farman * disable the subchannel again.
1419e6f07cdSEric Farman */
1429e6f07cdSEric Farman if (!private) {
1439e6f07cdSEric Farman VFIO_CCW_MSG_EVENT(2, "sch %x.%x.%04x: unexpected interrupt\n",
1449e6f07cdSEric Farman sch->schid.cssid, sch->schid.ssid,
1459e6f07cdSEric Farman sch->schid.sch_no);
1469e6f07cdSEric Farman
1479e6f07cdSEric Farman cio_disable_subchannel(sch);
1489e6f07cdSEric Farman return;
1499e6f07cdSEric Farman }
15063f1934dSDong Jia Shi
15163f1934dSDong Jia Shi inc_irq_stat(IRQIO_CIO);
152bbe37e4cSDong Jia Shi vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_INTERRUPT);
15363f1934dSDong Jia Shi }
15463f1934dSDong Jia Shi
vfio_ccw_free_parent(struct device * dev)1559e6f07cdSEric Farman static void vfio_ccw_free_parent(struct device *dev)
1569e6f07cdSEric Farman {
1579e6f07cdSEric Farman struct vfio_ccw_parent *parent = container_of(dev, struct vfio_ccw_parent, dev);
1589e6f07cdSEric Farman
1599e6f07cdSEric Farman kfree(parent);
1609e6f07cdSEric Farman }
1619e6f07cdSEric Farman
vfio_ccw_sch_probe(struct subchannel * sch)16263f1934dSDong Jia Shi static int vfio_ccw_sch_probe(struct subchannel *sch)
16363f1934dSDong Jia Shi {
16463f1934dSDong Jia Shi struct pmcw *pmcw = &sch->schib.pmcw;
1659e6f07cdSEric Farman struct vfio_ccw_parent *parent;
166d5afd5d1SCornelia Huck int ret = -ENOMEM;
16763f1934dSDong Jia Shi
16863f1934dSDong Jia Shi if (pmcw->qf) {
16963f1934dSDong Jia Shi dev_warn(&sch->dev, "vfio: ccw: does not support QDIO: %s\n",
17063f1934dSDong Jia Shi dev_name(&sch->dev));
17163f1934dSDong Jia Shi return -ENODEV;
17263f1934dSDong Jia Shi }
17363f1934dSDong Jia Shi
174*d933e5f4SGustavo A. R. Silva parent = kzalloc(struct_size(parent, mdev_types, 1), GFP_KERNEL);
1759e6f07cdSEric Farman if (!parent)
1769e6f07cdSEric Farman return -ENOMEM;
177c98e16b2SEric Farman
1789e6f07cdSEric Farman dev_set_name(&parent->dev, "parent");
1799e6f07cdSEric Farman parent->dev.parent = &sch->dev;
1809e6f07cdSEric Farman parent->dev.release = &vfio_ccw_free_parent;
1819e6f07cdSEric Farman ret = device_register(&parent->dev);
18255e93ecdSPierre Morel if (ret)
183204b394aSEric Farman goto out_free;
18455e93ecdSPierre Morel
1859e6f07cdSEric Farman dev_set_drvdata(&sch->dev, parent);
1869e6f07cdSEric Farman
1879e6f07cdSEric Farman parent->mdev_type.sysfs_name = "io";
1889e6f07cdSEric Farman parent->mdev_type.pretty_name = "I/O subchannel (Non-QDIO)";
1899e6f07cdSEric Farman parent->mdev_types[0] = &parent->mdev_type;
1909e6f07cdSEric Farman ret = mdev_register_parent(&parent->parent, &sch->dev,
1919e6f07cdSEric Farman &vfio_ccw_mdev_driver,
1929e6f07cdSEric Farman parent->mdev_types, 1);
1939e6f07cdSEric Farman if (ret)
1949e6f07cdSEric Farman goto out_unreg;
1959e6f07cdSEric Farman
19660e05d1cSCornelia Huck VFIO_CCW_MSG_EVENT(4, "bound to subchannel %x.%x.%04x\n",
19760e05d1cSCornelia Huck sch->schid.cssid, sch->schid.ssid,
19860e05d1cSCornelia Huck sch->schid.sch_no);
19963f1934dSDong Jia Shi return 0;
20063f1934dSDong Jia Shi
2019e6f07cdSEric Farman out_unreg:
202ce389573SAlex Williamson device_del(&parent->dev);
20363f1934dSDong Jia Shi out_free:
204ce389573SAlex Williamson put_device(&parent->dev);
20563f1934dSDong Jia Shi dev_set_drvdata(&sch->dev, NULL);
20663f1934dSDong Jia Shi return ret;
20763f1934dSDong Jia Shi }
20863f1934dSDong Jia Shi
vfio_ccw_sch_remove(struct subchannel * sch)209a7bdb9a9SUwe Kleine-König static void vfio_ccw_sch_remove(struct subchannel *sch)
21063f1934dSDong Jia Shi {
2119e6f07cdSEric Farman struct vfio_ccw_parent *parent = dev_get_drvdata(&sch->dev);
21263f1934dSDong Jia Shi
2139e6f07cdSEric Farman mdev_unregister_parent(&parent->parent);
21484cd8fc4SDong Jia Shi
2159e6f07cdSEric Farman device_unregister(&parent->dev);
21663f1934dSDong Jia Shi dev_set_drvdata(&sch->dev, NULL);
21763f1934dSDong Jia Shi
21860e05d1cSCornelia Huck VFIO_CCW_MSG_EVENT(4, "unbound from subchannel %x.%x.%04x\n",
21960e05d1cSCornelia Huck sch->schid.cssid, sch->schid.ssid,
22060e05d1cSCornelia Huck sch->schid.sch_no);
22163f1934dSDong Jia Shi }
22263f1934dSDong Jia Shi
vfio_ccw_sch_shutdown(struct subchannel * sch)22363f1934dSDong Jia Shi static void vfio_ccw_sch_shutdown(struct subchannel *sch)
22463f1934dSDong Jia Shi {
2259e6f07cdSEric Farman struct vfio_ccw_parent *parent = dev_get_drvdata(&sch->dev);
2269e6f07cdSEric Farman struct vfio_ccw_private *private = dev_get_drvdata(&parent->dev);
2279e6f07cdSEric Farman
2281c06bb87SEric Farman if (!private)
2299e6f07cdSEric Farman return;
230f4b4ed44SEric Farman
231f4b4ed44SEric Farman vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_CLOSE);
232204b394aSEric Farman vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_NOT_OPER);
23363f1934dSDong Jia Shi }
23463f1934dSDong Jia Shi
23563f1934dSDong Jia Shi /**
23663f1934dSDong Jia Shi * vfio_ccw_sch_event - process subchannel event
23763f1934dSDong Jia Shi * @sch: subchannel
23863f1934dSDong Jia Shi * @process: non-zero if function is called in process context
23963f1934dSDong Jia Shi *
24063f1934dSDong Jia Shi * An unspecified event occurred for this subchannel. Adjust data according
24163f1934dSDong Jia Shi * to the current operational state of the subchannel. Return zero when the
24263f1934dSDong Jia Shi * event has been handled sufficiently or -EAGAIN when this function should
24363f1934dSDong Jia Shi * be called again in process context.
24463f1934dSDong Jia Shi */
vfio_ccw_sch_event(struct subchannel * sch,int process)24563f1934dSDong Jia Shi static int vfio_ccw_sch_event(struct subchannel *sch, int process)
24663f1934dSDong Jia Shi {
2479e6f07cdSEric Farman struct vfio_ccw_parent *parent = dev_get_drvdata(&sch->dev);
2489e6f07cdSEric Farman struct vfio_ccw_private *private = dev_get_drvdata(&parent->dev);
24963f1934dSDong Jia Shi unsigned long flags;
2502c861d89SDong Jia Shi int rc = -EAGAIN;
25163f1934dSDong Jia Shi
25263f1934dSDong Jia Shi spin_lock_irqsave(sch->lock, flags);
25363f1934dSDong Jia Shi if (!device_is_registered(&sch->dev))
25463f1934dSDong Jia Shi goto out_unlock;
25563f1934dSDong Jia Shi
25663f1934dSDong Jia Shi if (work_pending(&sch->todo_work))
25763f1934dSDong Jia Shi goto out_unlock;
25863f1934dSDong Jia Shi
2592c861d89SDong Jia Shi rc = 0;
26063f1934dSDong Jia Shi
2619e6f07cdSEric Farman if (cio_update_schib(sch)) {
2629e6f07cdSEric Farman if (private)
263cffcc109SEric Farman vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_NOT_OPER);
2649e6f07cdSEric Farman }
265bbe37e4cSDong Jia Shi
26663f1934dSDong Jia Shi out_unlock:
26763f1934dSDong Jia Shi spin_unlock_irqrestore(sch->lock, flags);
26863f1934dSDong Jia Shi
2692c861d89SDong Jia Shi return rc;
27063f1934dSDong Jia Shi }
27163f1934dSDong Jia Shi
vfio_ccw_queue_crw(struct vfio_ccw_private * private,unsigned int rsc,unsigned int erc,unsigned int rsid)2723f02cb2fSFarhan Ali static void vfio_ccw_queue_crw(struct vfio_ccw_private *private,
2733f02cb2fSFarhan Ali unsigned int rsc,
2743f02cb2fSFarhan Ali unsigned int erc,
2753f02cb2fSFarhan Ali unsigned int rsid)
2763f02cb2fSFarhan Ali {
2773f02cb2fSFarhan Ali struct vfio_ccw_crw *crw;
2783f02cb2fSFarhan Ali
2793f02cb2fSFarhan Ali /*
2803f02cb2fSFarhan Ali * If unable to allocate a CRW, just drop the event and
2813f02cb2fSFarhan Ali * carry on. The guest will either see a later one or
2823f02cb2fSFarhan Ali * learn when it issues its own store subchannel.
2833f02cb2fSFarhan Ali */
2843f02cb2fSFarhan Ali crw = kzalloc(sizeof(*crw), GFP_ATOMIC);
2853f02cb2fSFarhan Ali if (!crw)
2863f02cb2fSFarhan Ali return;
2873f02cb2fSFarhan Ali
2883f02cb2fSFarhan Ali /*
2893f02cb2fSFarhan Ali * Build the CRW based on the inputs given to us.
2903f02cb2fSFarhan Ali */
2913f02cb2fSFarhan Ali crw->crw.rsc = rsc;
2923f02cb2fSFarhan Ali crw->crw.erc = erc;
2933f02cb2fSFarhan Ali crw->crw.rsid = rsid;
2943f02cb2fSFarhan Ali
2953f02cb2fSFarhan Ali list_add_tail(&crw->next, &private->crw);
2963f02cb2fSFarhan Ali queue_work(vfio_ccw_work_q, &private->crw_work);
2973f02cb2fSFarhan Ali }
2983f02cb2fSFarhan Ali
vfio_ccw_chp_event(struct subchannel * sch,struct chp_link * link,int event)299b7701dfbSFarhan Ali static int vfio_ccw_chp_event(struct subchannel *sch,
300b7701dfbSFarhan Ali struct chp_link *link, int event)
301b7701dfbSFarhan Ali {
3029e6f07cdSEric Farman struct vfio_ccw_parent *parent = dev_get_drvdata(&sch->dev);
3039e6f07cdSEric Farman struct vfio_ccw_private *private = dev_get_drvdata(&parent->dev);
304b7701dfbSFarhan Ali int mask = chp_ssd_get_mask(&sch->ssd_info, link);
305b7701dfbSFarhan Ali int retry = 255;
306b7701dfbSFarhan Ali
307b7701dfbSFarhan Ali if (!private || !mask)
308b7701dfbSFarhan Ali return 0;
309b7701dfbSFarhan Ali
310008a011dSEric Farman trace_vfio_ccw_chp_event(sch->schid, mask, event);
3113566ee1dSMichael Kawano VFIO_CCW_MSG_EVENT(2, "sch %x.%x.%04x: mask=0x%x event=%d\n",
3123566ee1dSMichael Kawano sch->schid.cssid,
313b7701dfbSFarhan Ali sch->schid.ssid, sch->schid.sch_no,
314b7701dfbSFarhan Ali mask, event);
315b7701dfbSFarhan Ali
316b7701dfbSFarhan Ali if (cio_update_schib(sch))
317b7701dfbSFarhan Ali return -ENODEV;
318b7701dfbSFarhan Ali
319b7701dfbSFarhan Ali switch (event) {
320b7701dfbSFarhan Ali case CHP_VARY_OFF:
321b7701dfbSFarhan Ali /* Path logically turned off */
322b7701dfbSFarhan Ali sch->opm &= ~mask;
323b7701dfbSFarhan Ali sch->lpm &= ~mask;
324b7701dfbSFarhan Ali if (sch->schib.pmcw.lpum & mask)
325b7701dfbSFarhan Ali cio_cancel_halt_clear(sch, &retry);
326b7701dfbSFarhan Ali break;
327b7701dfbSFarhan Ali case CHP_OFFLINE:
328b7701dfbSFarhan Ali /* Path is gone */
329b7701dfbSFarhan Ali if (sch->schib.pmcw.lpum & mask)
330b7701dfbSFarhan Ali cio_cancel_halt_clear(sch, &retry);
3313f02cb2fSFarhan Ali vfio_ccw_queue_crw(private, CRW_RSC_CPATH, CRW_ERC_PERRN,
3323f02cb2fSFarhan Ali link->chpid.id);
333b7701dfbSFarhan Ali break;
334b7701dfbSFarhan Ali case CHP_VARY_ON:
335b7701dfbSFarhan Ali /* Path logically turned on */
336b7701dfbSFarhan Ali sch->opm |= mask;
337b7701dfbSFarhan Ali sch->lpm |= mask;
338b7701dfbSFarhan Ali break;
339b7701dfbSFarhan Ali case CHP_ONLINE:
340b7701dfbSFarhan Ali /* Path became available */
341b7701dfbSFarhan Ali sch->lpm |= mask & sch->opm;
3423f02cb2fSFarhan Ali vfio_ccw_queue_crw(private, CRW_RSC_CPATH, CRW_ERC_INIT,
3433f02cb2fSFarhan Ali link->chpid.id);
344b7701dfbSFarhan Ali break;
345b7701dfbSFarhan Ali }
346b7701dfbSFarhan Ali
347b7701dfbSFarhan Ali return 0;
348b7701dfbSFarhan Ali }
349b7701dfbSFarhan Ali
35063f1934dSDong Jia Shi static struct css_device_id vfio_ccw_sch_ids[] = {
35163f1934dSDong Jia Shi { .match_flags = 0x1, .type = SUBCHANNEL_TYPE_IO, },
35263f1934dSDong Jia Shi { /* end of list */ },
35363f1934dSDong Jia Shi };
35463f1934dSDong Jia Shi MODULE_DEVICE_TABLE(css, vfio_ccw_sch_ids);
35563f1934dSDong Jia Shi
35663f1934dSDong Jia Shi static struct css_driver vfio_ccw_sch_driver = {
35763f1934dSDong Jia Shi .drv = {
35863f1934dSDong Jia Shi .name = "vfio_ccw",
35963f1934dSDong Jia Shi .owner = THIS_MODULE,
36063f1934dSDong Jia Shi },
36163f1934dSDong Jia Shi .subchannel_type = vfio_ccw_sch_ids,
36263f1934dSDong Jia Shi .irq = vfio_ccw_sch_irq,
36363f1934dSDong Jia Shi .probe = vfio_ccw_sch_probe,
36463f1934dSDong Jia Shi .remove = vfio_ccw_sch_remove,
36563f1934dSDong Jia Shi .shutdown = vfio_ccw_sch_shutdown,
36663f1934dSDong Jia Shi .sch_event = vfio_ccw_sch_event,
367b7701dfbSFarhan Ali .chp_event = vfio_ccw_chp_event,
36863f1934dSDong Jia Shi };
36963f1934dSDong Jia Shi
vfio_ccw_debug_init(void)37060e05d1cSCornelia Huck static int __init vfio_ccw_debug_init(void)
37160e05d1cSCornelia Huck {
37260e05d1cSCornelia Huck vfio_ccw_debug_msg_id = debug_register("vfio_ccw_msg", 16, 1,
37360e05d1cSCornelia Huck 11 * sizeof(long));
37460e05d1cSCornelia Huck if (!vfio_ccw_debug_msg_id)
37560e05d1cSCornelia Huck goto out_unregister;
37660e05d1cSCornelia Huck debug_register_view(vfio_ccw_debug_msg_id, &debug_sprintf_view);
37760e05d1cSCornelia Huck debug_set_level(vfio_ccw_debug_msg_id, 2);
37860e05d1cSCornelia Huck vfio_ccw_debug_trace_id = debug_register("vfio_ccw_trace", 16, 1, 16);
37960e05d1cSCornelia Huck if (!vfio_ccw_debug_trace_id)
38060e05d1cSCornelia Huck goto out_unregister;
38160e05d1cSCornelia Huck debug_register_view(vfio_ccw_debug_trace_id, &debug_hex_ascii_view);
38260e05d1cSCornelia Huck debug_set_level(vfio_ccw_debug_trace_id, 2);
38360e05d1cSCornelia Huck return 0;
38460e05d1cSCornelia Huck
38560e05d1cSCornelia Huck out_unregister:
38660e05d1cSCornelia Huck debug_unregister(vfio_ccw_debug_msg_id);
38760e05d1cSCornelia Huck debug_unregister(vfio_ccw_debug_trace_id);
38860e05d1cSCornelia Huck return -1;
38960e05d1cSCornelia Huck }
39060e05d1cSCornelia Huck
vfio_ccw_debug_exit(void)39160e05d1cSCornelia Huck static void vfio_ccw_debug_exit(void)
39260e05d1cSCornelia Huck {
39360e05d1cSCornelia Huck debug_unregister(vfio_ccw_debug_msg_id);
39460e05d1cSCornelia Huck debug_unregister(vfio_ccw_debug_trace_id);
39560e05d1cSCornelia Huck }
39660e05d1cSCornelia Huck
vfio_ccw_destroy_regions(void)3979a44ce6cSFarhan Ali static void vfio_ccw_destroy_regions(void)
3989a44ce6cSFarhan Ali {
399d8cac29bSFarhan Ali kmem_cache_destroy(vfio_ccw_crw_region);
40024c98674SFarhan Ali kmem_cache_destroy(vfio_ccw_schib_region);
4019a44ce6cSFarhan Ali kmem_cache_destroy(vfio_ccw_cmd_region);
4029a44ce6cSFarhan Ali kmem_cache_destroy(vfio_ccw_io_region);
4039a44ce6cSFarhan Ali }
4049a44ce6cSFarhan Ali
vfio_ccw_sch_init(void)40563f1934dSDong Jia Shi static int __init vfio_ccw_sch_init(void)
40663f1934dSDong Jia Shi {
40760e05d1cSCornelia Huck int ret;
40860e05d1cSCornelia Huck
40960e05d1cSCornelia Huck ret = vfio_ccw_debug_init();
41060e05d1cSCornelia Huck if (ret)
41160e05d1cSCornelia Huck return ret;
41263f1934dSDong Jia Shi
413e5f84dbaSDong Jia Shi vfio_ccw_work_q = create_singlethread_workqueue("vfio-ccw");
41460e05d1cSCornelia Huck if (!vfio_ccw_work_q) {
41560e05d1cSCornelia Huck ret = -ENOMEM;
4163bf1311fSJason Gunthorpe goto out_regions;
41760e05d1cSCornelia Huck }
418e5f84dbaSDong Jia Shi
419bf42daedSEric Farman vfio_ccw_io_region = kmem_cache_create_usercopy("vfio_ccw_io_region",
420bf42daedSEric Farman sizeof(struct ccw_io_region), 0,
421bf42daedSEric Farman SLAB_ACCOUNT, 0,
422bf42daedSEric Farman sizeof(struct ccw_io_region), NULL);
423987ca7caSWei Yongjun if (!vfio_ccw_io_region) {
424987ca7caSWei Yongjun ret = -ENOMEM;
4253bf1311fSJason Gunthorpe goto out_regions;
426987ca7caSWei Yongjun }
427d5afd5d1SCornelia Huck
428d5afd5d1SCornelia Huck vfio_ccw_cmd_region = kmem_cache_create_usercopy("vfio_ccw_cmd_region",
429d5afd5d1SCornelia Huck sizeof(struct ccw_cmd_region), 0,
430d5afd5d1SCornelia Huck SLAB_ACCOUNT, 0,
431d5afd5d1SCornelia Huck sizeof(struct ccw_cmd_region), NULL);
432987ca7caSWei Yongjun if (!vfio_ccw_cmd_region) {
433987ca7caSWei Yongjun ret = -ENOMEM;
4343bf1311fSJason Gunthorpe goto out_regions;
435987ca7caSWei Yongjun }
436bf42daedSEric Farman
43724c98674SFarhan Ali vfio_ccw_schib_region = kmem_cache_create_usercopy("vfio_ccw_schib_region",
43824c98674SFarhan Ali sizeof(struct ccw_schib_region), 0,
43924c98674SFarhan Ali SLAB_ACCOUNT, 0,
44024c98674SFarhan Ali sizeof(struct ccw_schib_region), NULL);
44124c98674SFarhan Ali
44224c98674SFarhan Ali if (!vfio_ccw_schib_region) {
44324c98674SFarhan Ali ret = -ENOMEM;
4443bf1311fSJason Gunthorpe goto out_regions;
44524c98674SFarhan Ali }
44624c98674SFarhan Ali
447d8cac29bSFarhan Ali vfio_ccw_crw_region = kmem_cache_create_usercopy("vfio_ccw_crw_region",
448d8cac29bSFarhan Ali sizeof(struct ccw_crw_region), 0,
449d8cac29bSFarhan Ali SLAB_ACCOUNT, 0,
450d8cac29bSFarhan Ali sizeof(struct ccw_crw_region), NULL);
451d8cac29bSFarhan Ali
452d8cac29bSFarhan Ali if (!vfio_ccw_crw_region) {
453d8cac29bSFarhan Ali ret = -ENOMEM;
4543bf1311fSJason Gunthorpe goto out_regions;
455d8cac29bSFarhan Ali }
456d8cac29bSFarhan Ali
4573bf1311fSJason Gunthorpe ret = mdev_register_driver(&vfio_ccw_mdev_driver);
4583bf1311fSJason Gunthorpe if (ret)
4593bf1311fSJason Gunthorpe goto out_regions;
4603bf1311fSJason Gunthorpe
46163f1934dSDong Jia Shi isc_register(VFIO_CCW_ISC);
46263f1934dSDong Jia Shi ret = css_driver_register(&vfio_ccw_sch_driver);
463e5f84dbaSDong Jia Shi if (ret) {
46463f1934dSDong Jia Shi isc_unregister(VFIO_CCW_ISC);
4653bf1311fSJason Gunthorpe goto out_driver;
466e5f84dbaSDong Jia Shi }
46763f1934dSDong Jia Shi
46863f1934dSDong Jia Shi return ret;
469d5afd5d1SCornelia Huck
4703bf1311fSJason Gunthorpe out_driver:
4713bf1311fSJason Gunthorpe mdev_unregister_driver(&vfio_ccw_mdev_driver);
4723bf1311fSJason Gunthorpe out_regions:
4739a44ce6cSFarhan Ali vfio_ccw_destroy_regions();
474d5afd5d1SCornelia Huck destroy_workqueue(vfio_ccw_work_q);
47560e05d1cSCornelia Huck vfio_ccw_debug_exit();
476d5afd5d1SCornelia Huck return ret;
47763f1934dSDong Jia Shi }
47863f1934dSDong Jia Shi
vfio_ccw_sch_exit(void)47963f1934dSDong Jia Shi static void __exit vfio_ccw_sch_exit(void)
48063f1934dSDong Jia Shi {
48163f1934dSDong Jia Shi css_driver_unregister(&vfio_ccw_sch_driver);
4823bf1311fSJason Gunthorpe mdev_unregister_driver(&vfio_ccw_mdev_driver);
48363f1934dSDong Jia Shi isc_unregister(VFIO_CCW_ISC);
4849a44ce6cSFarhan Ali vfio_ccw_destroy_regions();
485e5f84dbaSDong Jia Shi destroy_workqueue(vfio_ccw_work_q);
48660e05d1cSCornelia Huck vfio_ccw_debug_exit();
48763f1934dSDong Jia Shi }
48863f1934dSDong Jia Shi module_init(vfio_ccw_sch_init);
48963f1934dSDong Jia Shi module_exit(vfio_ccw_sch_exit);
49063f1934dSDong Jia Shi
49163f1934dSDong Jia Shi MODULE_LICENSE("GPL v2");
492