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