xref: /linux/drivers/s390/cio/vfio_ccw_drv.c (revision ead5d1f4d877e92c051e1a1ade623d0d30e71619)
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/device.h>
1663f1934dSDong Jia Shi #include <linux/slab.h>
174e149e43SDong Jia Shi #include <linux/uuid.h>
184e149e43SDong Jia Shi #include <linux/mdev.h>
1963f1934dSDong Jia Shi 
2063f1934dSDong Jia Shi #include <asm/isc.h>
2163f1934dSDong Jia Shi 
22b7701dfbSFarhan Ali #include "chp.h"
234e149e43SDong Jia Shi #include "ioasm.h"
244e149e43SDong Jia Shi #include "css.h"
2563f1934dSDong Jia Shi #include "vfio_ccw_private.h"
2663f1934dSDong Jia Shi 
27e5f84dbaSDong Jia Shi struct workqueue_struct *vfio_ccw_work_q;
2852df7837SSebastian Ott static struct kmem_cache *vfio_ccw_io_region;
29d5afd5d1SCornelia Huck static struct kmem_cache *vfio_ccw_cmd_region;
3024c98674SFarhan Ali static struct kmem_cache *vfio_ccw_schib_region;
31d8cac29bSFarhan Ali static struct kmem_cache *vfio_ccw_crw_region;
32e5f84dbaSDong Jia Shi 
3360e05d1cSCornelia Huck debug_info_t *vfio_ccw_debug_msg_id;
3460e05d1cSCornelia Huck debug_info_t *vfio_ccw_debug_trace_id;
3560e05d1cSCornelia Huck 
3663f1934dSDong Jia Shi /*
3763f1934dSDong Jia Shi  * Helpers
3863f1934dSDong Jia Shi  */
3984cd8fc4SDong Jia Shi int vfio_ccw_sch_quiesce(struct subchannel *sch)
4063f1934dSDong Jia Shi {
4163f1934dSDong Jia Shi 	struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
4263f1934dSDong Jia Shi 	DECLARE_COMPLETION_ONSTACK(completion);
4363f1934dSDong Jia Shi 	int iretry, ret = 0;
4463f1934dSDong Jia Shi 
4563f1934dSDong Jia Shi 	spin_lock_irq(sch->lock);
4663f1934dSDong Jia Shi 	if (!sch->schib.pmcw.ena)
4763f1934dSDong Jia Shi 		goto out_unlock;
4863f1934dSDong Jia Shi 	ret = cio_disable_subchannel(sch);
4963f1934dSDong Jia Shi 	if (ret != -EBUSY)
5063f1934dSDong Jia Shi 		goto out_unlock;
5163f1934dSDong Jia Shi 
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);
7863f1934dSDong Jia Shi out_unlock:
79bbe37e4cSDong Jia Shi 	private->state = VFIO_CCW_STATE_NOT_OPER;
8063f1934dSDong Jia Shi 	spin_unlock_irq(sch->lock);
8163f1934dSDong Jia Shi 	return ret;
8263f1934dSDong Jia Shi }
8363f1934dSDong Jia Shi 
84e5f84dbaSDong Jia Shi static void vfio_ccw_sch_io_todo(struct work_struct *work)
85e5f84dbaSDong Jia Shi {
86e5f84dbaSDong Jia Shi 	struct vfio_ccw_private *private;
87e5f84dbaSDong Jia Shi 	struct irb *irb;
8850b7f1b7SCornelia Huck 	bool is_final;
894e149e43SDong Jia Shi 
90e5f84dbaSDong Jia Shi 	private = container_of(work, struct vfio_ccw_private, io_work);
91e5f84dbaSDong Jia Shi 	irb = &private->irb;
924e149e43SDong Jia Shi 
9350b7f1b7SCornelia Huck 	is_final = !(scsw_actl(&irb->scsw) &
9450b7f1b7SCornelia Huck 		     (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT));
95e5f84dbaSDong Jia Shi 	if (scsw_is_solicited(&irb->scsw)) {
96e5f84dbaSDong Jia Shi 		cp_update_scsw(&private->cp, &irb->scsw);
97f4c99394SFarhan Ali 		if (is_final && private->state == VFIO_CCW_STATE_CP_PENDING)
98e5f84dbaSDong Jia Shi 			cp_free(&private->cp);
99e5f84dbaSDong Jia Shi 	}
1004f766173SCornelia Huck 	mutex_lock(&private->io_mutex);
101c98e16b2SEric Farman 	memcpy(private->io_region->irb_area, irb, sizeof(*irb));
1024f766173SCornelia Huck 	mutex_unlock(&private->io_mutex);
103e5f84dbaSDong Jia Shi 
10450b7f1b7SCornelia Huck 	if (private->mdev && is_final)
105bbe37e4cSDong Jia Shi 		private->state = VFIO_CCW_STATE_IDLE;
1064e31d6aeSEric Farman 
1074e31d6aeSEric Farman 	if (private->io_trigger)
1084e31d6aeSEric Farman 		eventfd_signal(private->io_trigger, 1);
1094e149e43SDong Jia Shi }
1104e149e43SDong Jia Shi 
1113f02cb2fSFarhan Ali static void vfio_ccw_crw_todo(struct work_struct *work)
1123f02cb2fSFarhan Ali {
1133f02cb2fSFarhan Ali 	struct vfio_ccw_private *private;
1143f02cb2fSFarhan Ali 
1153f02cb2fSFarhan Ali 	private = container_of(work, struct vfio_ccw_private, crw_work);
1163f02cb2fSFarhan Ali 
1173f02cb2fSFarhan Ali 	if (!list_empty(&private->crw) && private->crw_trigger)
1183f02cb2fSFarhan Ali 		eventfd_signal(private->crw_trigger, 1);
1193f02cb2fSFarhan Ali }
1203f02cb2fSFarhan Ali 
12163f1934dSDong Jia Shi /*
12263f1934dSDong Jia Shi  * Css driver callbacks
12363f1934dSDong Jia Shi  */
12463f1934dSDong Jia Shi static void vfio_ccw_sch_irq(struct subchannel *sch)
12563f1934dSDong Jia Shi {
12663f1934dSDong Jia Shi 	struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
12763f1934dSDong Jia Shi 
12863f1934dSDong Jia Shi 	inc_irq_stat(IRQIO_CIO);
129bbe37e4cSDong Jia Shi 	vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_INTERRUPT);
13063f1934dSDong Jia Shi }
13163f1934dSDong Jia Shi 
1329a44ce6cSFarhan Ali static void vfio_ccw_free_regions(struct vfio_ccw_private *private)
1339a44ce6cSFarhan Ali {
134d8cac29bSFarhan Ali 	if (private->crw_region)
135d8cac29bSFarhan Ali 		kmem_cache_free(vfio_ccw_crw_region, private->crw_region);
13624c98674SFarhan Ali 	if (private->schib_region)
13724c98674SFarhan Ali 		kmem_cache_free(vfio_ccw_schib_region, private->schib_region);
1389a44ce6cSFarhan Ali 	if (private->cmd_region)
1399a44ce6cSFarhan Ali 		kmem_cache_free(vfio_ccw_cmd_region, private->cmd_region);
1409a44ce6cSFarhan Ali 	if (private->io_region)
1419a44ce6cSFarhan Ali 		kmem_cache_free(vfio_ccw_io_region, private->io_region);
1429a44ce6cSFarhan Ali }
1439a44ce6cSFarhan Ali 
14463f1934dSDong Jia Shi static int vfio_ccw_sch_probe(struct subchannel *sch)
14563f1934dSDong Jia Shi {
14663f1934dSDong Jia Shi 	struct pmcw *pmcw = &sch->schib.pmcw;
14763f1934dSDong Jia Shi 	struct vfio_ccw_private *private;
148d5afd5d1SCornelia Huck 	int ret = -ENOMEM;
14963f1934dSDong Jia Shi 
15063f1934dSDong Jia Shi 	if (pmcw->qf) {
15163f1934dSDong Jia Shi 		dev_warn(&sch->dev, "vfio: ccw: does not support QDIO: %s\n",
15263f1934dSDong Jia Shi 			 dev_name(&sch->dev));
15363f1934dSDong Jia Shi 		return -ENODEV;
15463f1934dSDong Jia Shi 	}
15563f1934dSDong Jia Shi 
15663f1934dSDong Jia Shi 	private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA);
15763f1934dSDong Jia Shi 	if (!private)
15863f1934dSDong Jia Shi 		return -ENOMEM;
159c98e16b2SEric Farman 
1601d897e47SEric Farman 	private->cp.guest_cp = kcalloc(CCWCHAIN_LEN_MAX, sizeof(struct ccw1),
1611d897e47SEric Farman 				       GFP_KERNEL);
1621d897e47SEric Farman 	if (!private->cp.guest_cp)
1631d897e47SEric Farman 		goto out_free;
1641d897e47SEric Farman 
165bf42daedSEric Farman 	private->io_region = kmem_cache_zalloc(vfio_ccw_io_region,
166c98e16b2SEric Farman 					       GFP_KERNEL | GFP_DMA);
167d5afd5d1SCornelia Huck 	if (!private->io_region)
168d5afd5d1SCornelia Huck 		goto out_free;
169d5afd5d1SCornelia Huck 
170d5afd5d1SCornelia Huck 	private->cmd_region = kmem_cache_zalloc(vfio_ccw_cmd_region,
171d5afd5d1SCornelia Huck 						GFP_KERNEL | GFP_DMA);
172d5afd5d1SCornelia Huck 	if (!private->cmd_region)
173d5afd5d1SCornelia Huck 		goto out_free;
174c98e16b2SEric Farman 
17524c98674SFarhan Ali 	private->schib_region = kmem_cache_zalloc(vfio_ccw_schib_region,
17624c98674SFarhan Ali 						  GFP_KERNEL | GFP_DMA);
17724c98674SFarhan Ali 
17824c98674SFarhan Ali 	if (!private->schib_region)
17924c98674SFarhan Ali 		goto out_free;
18024c98674SFarhan Ali 
181d8cac29bSFarhan Ali 	private->crw_region = kmem_cache_zalloc(vfio_ccw_crw_region,
182d8cac29bSFarhan Ali 						GFP_KERNEL | GFP_DMA);
183d8cac29bSFarhan Ali 
184d8cac29bSFarhan Ali 	if (!private->crw_region)
185d8cac29bSFarhan Ali 		goto out_free;
186d8cac29bSFarhan Ali 
18763f1934dSDong Jia Shi 	private->sch = sch;
18863f1934dSDong Jia Shi 	dev_set_drvdata(&sch->dev, private);
1894f766173SCornelia Huck 	mutex_init(&private->io_mutex);
19063f1934dSDong Jia Shi 
19163f1934dSDong Jia Shi 	spin_lock_irq(sch->lock);
192bbe37e4cSDong Jia Shi 	private->state = VFIO_CCW_STATE_NOT_OPER;
19363f1934dSDong Jia Shi 	sch->isc = VFIO_CCW_ISC;
19463f1934dSDong Jia Shi 	ret = cio_enable_subchannel(sch, (u32)(unsigned long)sch);
19563f1934dSDong Jia Shi 	spin_unlock_irq(sch->lock);
19663f1934dSDong Jia Shi 	if (ret)
19763f1934dSDong Jia Shi 		goto out_free;
19863f1934dSDong Jia Shi 
1993f02cb2fSFarhan Ali 	INIT_LIST_HEAD(&private->crw);
200e5f84dbaSDong Jia Shi 	INIT_WORK(&private->io_work, vfio_ccw_sch_io_todo);
2013f02cb2fSFarhan Ali 	INIT_WORK(&private->crw_work, vfio_ccw_crw_todo);
20284cd8fc4SDong Jia Shi 	atomic_set(&private->avail, 1);
203bbe37e4cSDong Jia Shi 	private->state = VFIO_CCW_STATE_STANDBY;
20484cd8fc4SDong Jia Shi 
20555e93ecdSPierre Morel 	ret = vfio_ccw_mdev_reg(sch);
20655e93ecdSPierre Morel 	if (ret)
20755e93ecdSPierre Morel 		goto out_disable;
20855e93ecdSPierre Morel 
2092bc55eaeSCornelia Huck 	if (dev_get_uevent_suppress(&sch->dev)) {
2102bc55eaeSCornelia Huck 		dev_set_uevent_suppress(&sch->dev, 0);
2112bc55eaeSCornelia Huck 		kobject_uevent(&sch->dev.kobj, KOBJ_ADD);
2122bc55eaeSCornelia Huck 	}
2132bc55eaeSCornelia Huck 
21460e05d1cSCornelia Huck 	VFIO_CCW_MSG_EVENT(4, "bound to subchannel %x.%x.%04x\n",
21560e05d1cSCornelia Huck 			   sch->schid.cssid, sch->schid.ssid,
21660e05d1cSCornelia Huck 			   sch->schid.sch_no);
21763f1934dSDong Jia Shi 	return 0;
21863f1934dSDong Jia Shi 
21963f1934dSDong Jia Shi out_disable:
22063f1934dSDong Jia Shi 	cio_disable_subchannel(sch);
22163f1934dSDong Jia Shi out_free:
22263f1934dSDong Jia Shi 	dev_set_drvdata(&sch->dev, NULL);
2239a44ce6cSFarhan Ali 	vfio_ccw_free_regions(private);
2241d897e47SEric Farman 	kfree(private->cp.guest_cp);
22563f1934dSDong Jia Shi 	kfree(private);
22663f1934dSDong Jia Shi 	return ret;
22763f1934dSDong Jia Shi }
22863f1934dSDong Jia Shi 
22963f1934dSDong Jia Shi static int vfio_ccw_sch_remove(struct subchannel *sch)
23063f1934dSDong Jia Shi {
23163f1934dSDong Jia Shi 	struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
2323f02cb2fSFarhan Ali 	struct vfio_ccw_crw *crw, *temp;
23363f1934dSDong Jia Shi 
23463f1934dSDong Jia Shi 	vfio_ccw_sch_quiesce(sch);
23563f1934dSDong Jia Shi 
2363f02cb2fSFarhan Ali 	list_for_each_entry_safe(crw, temp, &private->crw, next) {
2373f02cb2fSFarhan Ali 		list_del(&crw->next);
2383f02cb2fSFarhan Ali 		kfree(crw);
2393f02cb2fSFarhan Ali 	}
2403f02cb2fSFarhan Ali 
24184cd8fc4SDong Jia Shi 	vfio_ccw_mdev_unreg(sch);
24284cd8fc4SDong Jia Shi 
24363f1934dSDong Jia Shi 	dev_set_drvdata(&sch->dev, NULL);
24463f1934dSDong Jia Shi 
2459a44ce6cSFarhan Ali 	vfio_ccw_free_regions(private);
2461d897e47SEric Farman 	kfree(private->cp.guest_cp);
24763f1934dSDong Jia Shi 	kfree(private);
24863f1934dSDong Jia Shi 
24960e05d1cSCornelia Huck 	VFIO_CCW_MSG_EVENT(4, "unbound from subchannel %x.%x.%04x\n",
25060e05d1cSCornelia Huck 			   sch->schid.cssid, sch->schid.ssid,
25160e05d1cSCornelia Huck 			   sch->schid.sch_no);
25263f1934dSDong Jia Shi 	return 0;
25363f1934dSDong Jia Shi }
25463f1934dSDong Jia Shi 
25563f1934dSDong Jia Shi static void vfio_ccw_sch_shutdown(struct subchannel *sch)
25663f1934dSDong Jia Shi {
25763f1934dSDong Jia Shi 	vfio_ccw_sch_quiesce(sch);
25863f1934dSDong Jia Shi }
25963f1934dSDong Jia Shi 
26063f1934dSDong Jia Shi /**
26163f1934dSDong Jia Shi  * vfio_ccw_sch_event - process subchannel event
26263f1934dSDong Jia Shi  * @sch: subchannel
26363f1934dSDong Jia Shi  * @process: non-zero if function is called in process context
26463f1934dSDong Jia Shi  *
26563f1934dSDong Jia Shi  * An unspecified event occurred for this subchannel. Adjust data according
26663f1934dSDong Jia Shi  * to the current operational state of the subchannel. Return zero when the
26763f1934dSDong Jia Shi  * event has been handled sufficiently or -EAGAIN when this function should
26863f1934dSDong Jia Shi  * be called again in process context.
26963f1934dSDong Jia Shi  */
27063f1934dSDong Jia Shi static int vfio_ccw_sch_event(struct subchannel *sch, int process)
27163f1934dSDong Jia Shi {
272bbe37e4cSDong Jia Shi 	struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
27363f1934dSDong Jia Shi 	unsigned long flags;
2742c861d89SDong Jia Shi 	int rc = -EAGAIN;
27563f1934dSDong Jia Shi 
27663f1934dSDong Jia Shi 	spin_lock_irqsave(sch->lock, flags);
27763f1934dSDong Jia Shi 	if (!device_is_registered(&sch->dev))
27863f1934dSDong Jia Shi 		goto out_unlock;
27963f1934dSDong Jia Shi 
28063f1934dSDong Jia Shi 	if (work_pending(&sch->todo_work))
28163f1934dSDong Jia Shi 		goto out_unlock;
28263f1934dSDong Jia Shi 
28363f1934dSDong Jia Shi 	if (cio_update_schib(sch)) {
284bbe37e4cSDong Jia Shi 		vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_NOT_OPER);
2852c861d89SDong Jia Shi 		rc = 0;
28663f1934dSDong Jia Shi 		goto out_unlock;
28763f1934dSDong Jia Shi 	}
28863f1934dSDong Jia Shi 
289bbe37e4cSDong Jia Shi 	private = dev_get_drvdata(&sch->dev);
290bbe37e4cSDong Jia Shi 	if (private->state == VFIO_CCW_STATE_NOT_OPER) {
291bbe37e4cSDong Jia Shi 		private->state = private->mdev ? VFIO_CCW_STATE_IDLE :
292bbe37e4cSDong Jia Shi 				 VFIO_CCW_STATE_STANDBY;
293bbe37e4cSDong Jia Shi 	}
2942c861d89SDong Jia Shi 	rc = 0;
295bbe37e4cSDong Jia Shi 
29663f1934dSDong Jia Shi out_unlock:
29763f1934dSDong Jia Shi 	spin_unlock_irqrestore(sch->lock, flags);
29863f1934dSDong Jia Shi 
2992c861d89SDong Jia Shi 	return rc;
30063f1934dSDong Jia Shi }
30163f1934dSDong Jia Shi 
3023f02cb2fSFarhan Ali static void vfio_ccw_queue_crw(struct vfio_ccw_private *private,
3033f02cb2fSFarhan Ali 			       unsigned int rsc,
3043f02cb2fSFarhan Ali 			       unsigned int erc,
3053f02cb2fSFarhan Ali 			       unsigned int rsid)
3063f02cb2fSFarhan Ali {
3073f02cb2fSFarhan Ali 	struct vfio_ccw_crw *crw;
3083f02cb2fSFarhan Ali 
3093f02cb2fSFarhan Ali 	/*
3103f02cb2fSFarhan Ali 	 * If unable to allocate a CRW, just drop the event and
3113f02cb2fSFarhan Ali 	 * carry on.  The guest will either see a later one or
3123f02cb2fSFarhan Ali 	 * learn when it issues its own store subchannel.
3133f02cb2fSFarhan Ali 	 */
3143f02cb2fSFarhan Ali 	crw = kzalloc(sizeof(*crw), GFP_ATOMIC);
3153f02cb2fSFarhan Ali 	if (!crw)
3163f02cb2fSFarhan Ali 		return;
3173f02cb2fSFarhan Ali 
3183f02cb2fSFarhan Ali 	/*
3193f02cb2fSFarhan Ali 	 * Build the CRW based on the inputs given to us.
3203f02cb2fSFarhan Ali 	 */
3213f02cb2fSFarhan Ali 	crw->crw.rsc = rsc;
3223f02cb2fSFarhan Ali 	crw->crw.erc = erc;
3233f02cb2fSFarhan Ali 	crw->crw.rsid = rsid;
3243f02cb2fSFarhan Ali 
3253f02cb2fSFarhan Ali 	list_add_tail(&crw->next, &private->crw);
3263f02cb2fSFarhan Ali 	queue_work(vfio_ccw_work_q, &private->crw_work);
3273f02cb2fSFarhan Ali }
3283f02cb2fSFarhan Ali 
329b7701dfbSFarhan Ali static int vfio_ccw_chp_event(struct subchannel *sch,
330b7701dfbSFarhan Ali 			      struct chp_link *link, int event)
331b7701dfbSFarhan Ali {
332b7701dfbSFarhan Ali 	struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
333b7701dfbSFarhan Ali 	int mask = chp_ssd_get_mask(&sch->ssd_info, link);
334b7701dfbSFarhan Ali 	int retry = 255;
335b7701dfbSFarhan Ali 
336b7701dfbSFarhan Ali 	if (!private || !mask)
337b7701dfbSFarhan Ali 		return 0;
338b7701dfbSFarhan Ali 
339*b2dd9a44SEric Farman 	trace_vfio_ccw_chp_event(private->sch->schid, mask, event);
340b7701dfbSFarhan Ali 	VFIO_CCW_MSG_EVENT(2, "%pUl (%x.%x.%04x): mask=0x%x event=%d\n",
341b7701dfbSFarhan Ali 			   mdev_uuid(private->mdev), sch->schid.cssid,
342b7701dfbSFarhan Ali 			   sch->schid.ssid, sch->schid.sch_no,
343b7701dfbSFarhan Ali 			   mask, event);
344b7701dfbSFarhan Ali 
345b7701dfbSFarhan Ali 	if (cio_update_schib(sch))
346b7701dfbSFarhan Ali 		return -ENODEV;
347b7701dfbSFarhan Ali 
348b7701dfbSFarhan Ali 	switch (event) {
349b7701dfbSFarhan Ali 	case CHP_VARY_OFF:
350b7701dfbSFarhan Ali 		/* Path logically turned off */
351b7701dfbSFarhan Ali 		sch->opm &= ~mask;
352b7701dfbSFarhan Ali 		sch->lpm &= ~mask;
353b7701dfbSFarhan Ali 		if (sch->schib.pmcw.lpum & mask)
354b7701dfbSFarhan Ali 			cio_cancel_halt_clear(sch, &retry);
355b7701dfbSFarhan Ali 		break;
356b7701dfbSFarhan Ali 	case CHP_OFFLINE:
357b7701dfbSFarhan Ali 		/* Path is gone */
358b7701dfbSFarhan Ali 		if (sch->schib.pmcw.lpum & mask)
359b7701dfbSFarhan Ali 			cio_cancel_halt_clear(sch, &retry);
3603f02cb2fSFarhan Ali 		vfio_ccw_queue_crw(private, CRW_RSC_CPATH, CRW_ERC_PERRN,
3613f02cb2fSFarhan Ali 				   link->chpid.id);
362b7701dfbSFarhan Ali 		break;
363b7701dfbSFarhan Ali 	case CHP_VARY_ON:
364b7701dfbSFarhan Ali 		/* Path logically turned on */
365b7701dfbSFarhan Ali 		sch->opm |= mask;
366b7701dfbSFarhan Ali 		sch->lpm |= mask;
367b7701dfbSFarhan Ali 		break;
368b7701dfbSFarhan Ali 	case CHP_ONLINE:
369b7701dfbSFarhan Ali 		/* Path became available */
370b7701dfbSFarhan Ali 		sch->lpm |= mask & sch->opm;
3713f02cb2fSFarhan Ali 		vfio_ccw_queue_crw(private, CRW_RSC_CPATH, CRW_ERC_INIT,
3723f02cb2fSFarhan Ali 				   link->chpid.id);
373b7701dfbSFarhan Ali 		break;
374b7701dfbSFarhan Ali 	}
375b7701dfbSFarhan Ali 
376b7701dfbSFarhan Ali 	return 0;
377b7701dfbSFarhan Ali }
378b7701dfbSFarhan Ali 
37963f1934dSDong Jia Shi static struct css_device_id vfio_ccw_sch_ids[] = {
38063f1934dSDong Jia Shi 	{ .match_flags = 0x1, .type = SUBCHANNEL_TYPE_IO, },
38163f1934dSDong Jia Shi 	{ /* end of list */ },
38263f1934dSDong Jia Shi };
38363f1934dSDong Jia Shi MODULE_DEVICE_TABLE(css, vfio_ccw_sch_ids);
38463f1934dSDong Jia Shi 
38563f1934dSDong Jia Shi static struct css_driver vfio_ccw_sch_driver = {
38663f1934dSDong Jia Shi 	.drv = {
38763f1934dSDong Jia Shi 		.name = "vfio_ccw",
38863f1934dSDong Jia Shi 		.owner = THIS_MODULE,
38963f1934dSDong Jia Shi 	},
39063f1934dSDong Jia Shi 	.subchannel_type = vfio_ccw_sch_ids,
39163f1934dSDong Jia Shi 	.irq = vfio_ccw_sch_irq,
39263f1934dSDong Jia Shi 	.probe = vfio_ccw_sch_probe,
39363f1934dSDong Jia Shi 	.remove = vfio_ccw_sch_remove,
39463f1934dSDong Jia Shi 	.shutdown = vfio_ccw_sch_shutdown,
39563f1934dSDong Jia Shi 	.sch_event = vfio_ccw_sch_event,
396b7701dfbSFarhan Ali 	.chp_event = vfio_ccw_chp_event,
39763f1934dSDong Jia Shi };
39863f1934dSDong Jia Shi 
39960e05d1cSCornelia Huck static int __init vfio_ccw_debug_init(void)
40060e05d1cSCornelia Huck {
40160e05d1cSCornelia Huck 	vfio_ccw_debug_msg_id = debug_register("vfio_ccw_msg", 16, 1,
40260e05d1cSCornelia Huck 					       11 * sizeof(long));
40360e05d1cSCornelia Huck 	if (!vfio_ccw_debug_msg_id)
40460e05d1cSCornelia Huck 		goto out_unregister;
40560e05d1cSCornelia Huck 	debug_register_view(vfio_ccw_debug_msg_id, &debug_sprintf_view);
40660e05d1cSCornelia Huck 	debug_set_level(vfio_ccw_debug_msg_id, 2);
40760e05d1cSCornelia Huck 	vfio_ccw_debug_trace_id = debug_register("vfio_ccw_trace", 16, 1, 16);
40860e05d1cSCornelia Huck 	if (!vfio_ccw_debug_trace_id)
40960e05d1cSCornelia Huck 		goto out_unregister;
41060e05d1cSCornelia Huck 	debug_register_view(vfio_ccw_debug_trace_id, &debug_hex_ascii_view);
41160e05d1cSCornelia Huck 	debug_set_level(vfio_ccw_debug_trace_id, 2);
41260e05d1cSCornelia Huck 	return 0;
41360e05d1cSCornelia Huck 
41460e05d1cSCornelia Huck out_unregister:
41560e05d1cSCornelia Huck 	debug_unregister(vfio_ccw_debug_msg_id);
41660e05d1cSCornelia Huck 	debug_unregister(vfio_ccw_debug_trace_id);
41760e05d1cSCornelia Huck 	return -1;
41860e05d1cSCornelia Huck }
41960e05d1cSCornelia Huck 
42060e05d1cSCornelia Huck static void vfio_ccw_debug_exit(void)
42160e05d1cSCornelia Huck {
42260e05d1cSCornelia Huck 	debug_unregister(vfio_ccw_debug_msg_id);
42360e05d1cSCornelia Huck 	debug_unregister(vfio_ccw_debug_trace_id);
42460e05d1cSCornelia Huck }
42560e05d1cSCornelia Huck 
4269a44ce6cSFarhan Ali static void vfio_ccw_destroy_regions(void)
4279a44ce6cSFarhan Ali {
428d8cac29bSFarhan Ali 	kmem_cache_destroy(vfio_ccw_crw_region);
42924c98674SFarhan Ali 	kmem_cache_destroy(vfio_ccw_schib_region);
4309a44ce6cSFarhan Ali 	kmem_cache_destroy(vfio_ccw_cmd_region);
4319a44ce6cSFarhan Ali 	kmem_cache_destroy(vfio_ccw_io_region);
4329a44ce6cSFarhan Ali }
4339a44ce6cSFarhan Ali 
43463f1934dSDong Jia Shi static int __init vfio_ccw_sch_init(void)
43563f1934dSDong Jia Shi {
43660e05d1cSCornelia Huck 	int ret;
43760e05d1cSCornelia Huck 
43860e05d1cSCornelia Huck 	ret = vfio_ccw_debug_init();
43960e05d1cSCornelia Huck 	if (ret)
44060e05d1cSCornelia Huck 		return ret;
44163f1934dSDong Jia Shi 
442e5f84dbaSDong Jia Shi 	vfio_ccw_work_q = create_singlethread_workqueue("vfio-ccw");
44360e05d1cSCornelia Huck 	if (!vfio_ccw_work_q) {
44460e05d1cSCornelia Huck 		ret = -ENOMEM;
44560e05d1cSCornelia Huck 		goto out_err;
44660e05d1cSCornelia Huck 	}
447e5f84dbaSDong Jia Shi 
448bf42daedSEric Farman 	vfio_ccw_io_region = kmem_cache_create_usercopy("vfio_ccw_io_region",
449bf42daedSEric Farman 					sizeof(struct ccw_io_region), 0,
450bf42daedSEric Farman 					SLAB_ACCOUNT, 0,
451bf42daedSEric Farman 					sizeof(struct ccw_io_region), NULL);
452987ca7caSWei Yongjun 	if (!vfio_ccw_io_region) {
453987ca7caSWei Yongjun 		ret = -ENOMEM;
454d5afd5d1SCornelia Huck 		goto out_err;
455987ca7caSWei Yongjun 	}
456d5afd5d1SCornelia Huck 
457d5afd5d1SCornelia Huck 	vfio_ccw_cmd_region = kmem_cache_create_usercopy("vfio_ccw_cmd_region",
458d5afd5d1SCornelia Huck 					sizeof(struct ccw_cmd_region), 0,
459d5afd5d1SCornelia Huck 					SLAB_ACCOUNT, 0,
460d5afd5d1SCornelia Huck 					sizeof(struct ccw_cmd_region), NULL);
461987ca7caSWei Yongjun 	if (!vfio_ccw_cmd_region) {
462987ca7caSWei Yongjun 		ret = -ENOMEM;
463d5afd5d1SCornelia Huck 		goto out_err;
464987ca7caSWei Yongjun 	}
465bf42daedSEric Farman 
46624c98674SFarhan Ali 	vfio_ccw_schib_region = kmem_cache_create_usercopy("vfio_ccw_schib_region",
46724c98674SFarhan Ali 					sizeof(struct ccw_schib_region), 0,
46824c98674SFarhan Ali 					SLAB_ACCOUNT, 0,
46924c98674SFarhan Ali 					sizeof(struct ccw_schib_region), NULL);
47024c98674SFarhan Ali 
47124c98674SFarhan Ali 	if (!vfio_ccw_schib_region) {
47224c98674SFarhan Ali 		ret = -ENOMEM;
47324c98674SFarhan Ali 		goto out_err;
47424c98674SFarhan Ali 	}
47524c98674SFarhan Ali 
476d8cac29bSFarhan Ali 	vfio_ccw_crw_region = kmem_cache_create_usercopy("vfio_ccw_crw_region",
477d8cac29bSFarhan Ali 					sizeof(struct ccw_crw_region), 0,
478d8cac29bSFarhan Ali 					SLAB_ACCOUNT, 0,
479d8cac29bSFarhan Ali 					sizeof(struct ccw_crw_region), NULL);
480d8cac29bSFarhan Ali 
481d8cac29bSFarhan Ali 	if (!vfio_ccw_crw_region) {
482d8cac29bSFarhan Ali 		ret = -ENOMEM;
483d8cac29bSFarhan Ali 		goto out_err;
484d8cac29bSFarhan Ali 	}
485d8cac29bSFarhan Ali 
48663f1934dSDong Jia Shi 	isc_register(VFIO_CCW_ISC);
48763f1934dSDong Jia Shi 	ret = css_driver_register(&vfio_ccw_sch_driver);
488e5f84dbaSDong Jia Shi 	if (ret) {
48963f1934dSDong Jia Shi 		isc_unregister(VFIO_CCW_ISC);
490d5afd5d1SCornelia Huck 		goto out_err;
491e5f84dbaSDong Jia Shi 	}
49263f1934dSDong Jia Shi 
49363f1934dSDong Jia Shi 	return ret;
494d5afd5d1SCornelia Huck 
495d5afd5d1SCornelia Huck out_err:
4969a44ce6cSFarhan Ali 	vfio_ccw_destroy_regions();
497d5afd5d1SCornelia Huck 	destroy_workqueue(vfio_ccw_work_q);
49860e05d1cSCornelia Huck 	vfio_ccw_debug_exit();
499d5afd5d1SCornelia Huck 	return ret;
50063f1934dSDong Jia Shi }
50163f1934dSDong Jia Shi 
50263f1934dSDong Jia Shi static void __exit vfio_ccw_sch_exit(void)
50363f1934dSDong Jia Shi {
50463f1934dSDong Jia Shi 	css_driver_unregister(&vfio_ccw_sch_driver);
50563f1934dSDong Jia Shi 	isc_unregister(VFIO_CCW_ISC);
5069a44ce6cSFarhan Ali 	vfio_ccw_destroy_regions();
507e5f84dbaSDong Jia Shi 	destroy_workqueue(vfio_ccw_work_q);
50860e05d1cSCornelia Huck 	vfio_ccw_debug_exit();
50963f1934dSDong Jia Shi }
51063f1934dSDong Jia Shi module_init(vfio_ccw_sch_init);
51163f1934dSDong Jia Shi module_exit(vfio_ccw_sch_exit);
51263f1934dSDong Jia Shi 
51363f1934dSDong Jia Shi MODULE_LICENSE("GPL v2");
514