xref: /openbmc/linux/drivers/s390/cio/vfio_ccw_drv.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
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