18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Finite state machine for vfio-ccw device handling 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2017 68c2ecf20Sopenharmony_ci * Copyright Red Hat, Inc. 2019 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com> 98c2ecf20Sopenharmony_ci * Cornelia Huck <cohuck@redhat.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/vfio.h> 138c2ecf20Sopenharmony_ci#include <linux/mdev.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "ioasm.h" 168c2ecf20Sopenharmony_ci#include "vfio_ccw_private.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic int fsm_io_helper(struct vfio_ccw_private *private) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci struct subchannel *sch; 218c2ecf20Sopenharmony_ci union orb *orb; 228c2ecf20Sopenharmony_ci int ccode; 238c2ecf20Sopenharmony_ci __u8 lpm; 248c2ecf20Sopenharmony_ci unsigned long flags; 258c2ecf20Sopenharmony_ci int ret; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci sch = private->sch; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci spin_lock_irqsave(sch->lock, flags); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci orb = cp_get_orb(&private->cp, (u32)(addr_t)sch, sch->lpm); 328c2ecf20Sopenharmony_ci if (!orb) { 338c2ecf20Sopenharmony_ci ret = -EIO; 348c2ecf20Sopenharmony_ci goto out; 358c2ecf20Sopenharmony_ci } 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci VFIO_CCW_TRACE_EVENT(5, "stIO"); 388c2ecf20Sopenharmony_ci VFIO_CCW_TRACE_EVENT(5, dev_name(&sch->dev)); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci /* Issue "Start Subchannel" */ 418c2ecf20Sopenharmony_ci ccode = ssch(sch->schid, orb); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci VFIO_CCW_HEX_EVENT(5, &ccode, sizeof(ccode)); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci switch (ccode) { 468c2ecf20Sopenharmony_ci case 0: 478c2ecf20Sopenharmony_ci /* 488c2ecf20Sopenharmony_ci * Initialize device status information 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND; 518c2ecf20Sopenharmony_ci ret = 0; 528c2ecf20Sopenharmony_ci private->state = VFIO_CCW_STATE_CP_PENDING; 538c2ecf20Sopenharmony_ci break; 548c2ecf20Sopenharmony_ci case 1: /* Status pending */ 558c2ecf20Sopenharmony_ci case 2: /* Busy */ 568c2ecf20Sopenharmony_ci ret = -EBUSY; 578c2ecf20Sopenharmony_ci break; 588c2ecf20Sopenharmony_ci case 3: /* Device/path not operational */ 598c2ecf20Sopenharmony_ci { 608c2ecf20Sopenharmony_ci lpm = orb->cmd.lpm; 618c2ecf20Sopenharmony_ci if (lpm != 0) 628c2ecf20Sopenharmony_ci sch->lpm &= ~lpm; 638c2ecf20Sopenharmony_ci else 648c2ecf20Sopenharmony_ci sch->lpm = 0; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (cio_update_schib(sch)) 678c2ecf20Sopenharmony_ci ret = -ENODEV; 688c2ecf20Sopenharmony_ci else 698c2ecf20Sopenharmony_ci ret = sch->lpm ? -EACCES : -ENODEV; 708c2ecf20Sopenharmony_ci break; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci default: 738c2ecf20Sopenharmony_ci ret = ccode; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ciout: 768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(sch->lock, flags); 778c2ecf20Sopenharmony_ci return ret; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic int fsm_do_halt(struct vfio_ccw_private *private) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct subchannel *sch; 838c2ecf20Sopenharmony_ci unsigned long flags; 848c2ecf20Sopenharmony_ci int ccode; 858c2ecf20Sopenharmony_ci int ret; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci sch = private->sch; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci spin_lock_irqsave(sch->lock, flags); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci VFIO_CCW_TRACE_EVENT(2, "haltIO"); 928c2ecf20Sopenharmony_ci VFIO_CCW_TRACE_EVENT(2, dev_name(&sch->dev)); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* Issue "Halt Subchannel" */ 958c2ecf20Sopenharmony_ci ccode = hsch(sch->schid); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci VFIO_CCW_HEX_EVENT(2, &ccode, sizeof(ccode)); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci switch (ccode) { 1008c2ecf20Sopenharmony_ci case 0: 1018c2ecf20Sopenharmony_ci /* 1028c2ecf20Sopenharmony_ci * Initialize device status information 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_ci sch->schib.scsw.cmd.actl |= SCSW_ACTL_HALT_PEND; 1058c2ecf20Sopenharmony_ci ret = 0; 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci case 1: /* Status pending */ 1088c2ecf20Sopenharmony_ci case 2: /* Busy */ 1098c2ecf20Sopenharmony_ci ret = -EBUSY; 1108c2ecf20Sopenharmony_ci break; 1118c2ecf20Sopenharmony_ci case 3: /* Device not operational */ 1128c2ecf20Sopenharmony_ci ret = -ENODEV; 1138c2ecf20Sopenharmony_ci break; 1148c2ecf20Sopenharmony_ci default: 1158c2ecf20Sopenharmony_ci ret = ccode; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(sch->lock, flags); 1188c2ecf20Sopenharmony_ci return ret; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int fsm_do_clear(struct vfio_ccw_private *private) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct subchannel *sch; 1248c2ecf20Sopenharmony_ci unsigned long flags; 1258c2ecf20Sopenharmony_ci int ccode; 1268c2ecf20Sopenharmony_ci int ret; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci sch = private->sch; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci spin_lock_irqsave(sch->lock, flags); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci VFIO_CCW_TRACE_EVENT(2, "clearIO"); 1338c2ecf20Sopenharmony_ci VFIO_CCW_TRACE_EVENT(2, dev_name(&sch->dev)); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* Issue "Clear Subchannel" */ 1368c2ecf20Sopenharmony_ci ccode = csch(sch->schid); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci VFIO_CCW_HEX_EVENT(2, &ccode, sizeof(ccode)); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci switch (ccode) { 1418c2ecf20Sopenharmony_ci case 0: 1428c2ecf20Sopenharmony_ci /* 1438c2ecf20Sopenharmony_ci * Initialize device status information 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_ci sch->schib.scsw.cmd.actl = SCSW_ACTL_CLEAR_PEND; 1468c2ecf20Sopenharmony_ci /* TODO: check what else we might need to clear */ 1478c2ecf20Sopenharmony_ci ret = 0; 1488c2ecf20Sopenharmony_ci break; 1498c2ecf20Sopenharmony_ci case 3: /* Device not operational */ 1508c2ecf20Sopenharmony_ci ret = -ENODEV; 1518c2ecf20Sopenharmony_ci break; 1528c2ecf20Sopenharmony_ci default: 1538c2ecf20Sopenharmony_ci ret = ccode; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(sch->lock, flags); 1568c2ecf20Sopenharmony_ci return ret; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic void fsm_notoper(struct vfio_ccw_private *private, 1608c2ecf20Sopenharmony_ci enum vfio_ccw_event event) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct subchannel *sch = private->sch; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci VFIO_CCW_TRACE_EVENT(2, "notoper"); 1658c2ecf20Sopenharmony_ci VFIO_CCW_TRACE_EVENT(2, dev_name(&sch->dev)); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* 1688c2ecf20Sopenharmony_ci * TODO: 1698c2ecf20Sopenharmony_ci * Probably we should send the machine check to the guest. 1708c2ecf20Sopenharmony_ci */ 1718c2ecf20Sopenharmony_ci css_sched_sch_todo(sch, SCH_TODO_UNREG); 1728c2ecf20Sopenharmony_ci private->state = VFIO_CCW_STATE_NOT_OPER; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/* 1768c2ecf20Sopenharmony_ci * No operation action. 1778c2ecf20Sopenharmony_ci */ 1788c2ecf20Sopenharmony_cistatic void fsm_nop(struct vfio_ccw_private *private, 1798c2ecf20Sopenharmony_ci enum vfio_ccw_event event) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic void fsm_io_error(struct vfio_ccw_private *private, 1848c2ecf20Sopenharmony_ci enum vfio_ccw_event event) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci pr_err("vfio-ccw: FSM: I/O request from state:%d\n", private->state); 1878c2ecf20Sopenharmony_ci private->io_region->ret_code = -EIO; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic void fsm_io_busy(struct vfio_ccw_private *private, 1918c2ecf20Sopenharmony_ci enum vfio_ccw_event event) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci private->io_region->ret_code = -EBUSY; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic void fsm_io_retry(struct vfio_ccw_private *private, 1978c2ecf20Sopenharmony_ci enum vfio_ccw_event event) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci private->io_region->ret_code = -EAGAIN; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic void fsm_async_error(struct vfio_ccw_private *private, 2038c2ecf20Sopenharmony_ci enum vfio_ccw_event event) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci struct ccw_cmd_region *cmd_region = private->cmd_region; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci pr_err("vfio-ccw: FSM: %s request from state:%d\n", 2088c2ecf20Sopenharmony_ci cmd_region->command == VFIO_CCW_ASYNC_CMD_HSCH ? "halt" : 2098c2ecf20Sopenharmony_ci cmd_region->command == VFIO_CCW_ASYNC_CMD_CSCH ? "clear" : 2108c2ecf20Sopenharmony_ci "<unknown>", private->state); 2118c2ecf20Sopenharmony_ci cmd_region->ret_code = -EIO; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic void fsm_async_retry(struct vfio_ccw_private *private, 2158c2ecf20Sopenharmony_ci enum vfio_ccw_event event) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci private->cmd_region->ret_code = -EAGAIN; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic void fsm_disabled_irq(struct vfio_ccw_private *private, 2218c2ecf20Sopenharmony_ci enum vfio_ccw_event event) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct subchannel *sch = private->sch; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* 2268c2ecf20Sopenharmony_ci * An interrupt in a disabled state means a previous disable was not 2278c2ecf20Sopenharmony_ci * successful - should not happen, but we try to disable again. 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_ci cio_disable_subchannel(sch); 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ciinline struct subchannel_id get_schid(struct vfio_ccw_private *p) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci return p->sch->schid; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci/* 2378c2ecf20Sopenharmony_ci * Deal with the ccw command request from the userspace. 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_cistatic void fsm_io_request(struct vfio_ccw_private *private, 2408c2ecf20Sopenharmony_ci enum vfio_ccw_event event) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci union orb *orb; 2438c2ecf20Sopenharmony_ci union scsw *scsw = &private->scsw; 2448c2ecf20Sopenharmony_ci struct ccw_io_region *io_region = private->io_region; 2458c2ecf20Sopenharmony_ci struct mdev_device *mdev = private->mdev; 2468c2ecf20Sopenharmony_ci char *errstr = "request"; 2478c2ecf20Sopenharmony_ci struct subchannel_id schid = get_schid(private); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci private->state = VFIO_CCW_STATE_CP_PROCESSING; 2508c2ecf20Sopenharmony_ci memcpy(scsw, io_region->scsw_area, sizeof(*scsw)); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) { 2538c2ecf20Sopenharmony_ci orb = (union orb *)io_region->orb_area; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* Don't try to build a cp if transport mode is specified. */ 2568c2ecf20Sopenharmony_ci if (orb->tm.b) { 2578c2ecf20Sopenharmony_ci io_region->ret_code = -EOPNOTSUPP; 2588c2ecf20Sopenharmony_ci VFIO_CCW_MSG_EVENT(2, 2598c2ecf20Sopenharmony_ci "%pUl (%x.%x.%04x): transport mode\n", 2608c2ecf20Sopenharmony_ci mdev_uuid(mdev), schid.cssid, 2618c2ecf20Sopenharmony_ci schid.ssid, schid.sch_no); 2628c2ecf20Sopenharmony_ci errstr = "transport mode"; 2638c2ecf20Sopenharmony_ci goto err_out; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci io_region->ret_code = cp_init(&private->cp, mdev_dev(mdev), 2668c2ecf20Sopenharmony_ci orb); 2678c2ecf20Sopenharmony_ci if (io_region->ret_code) { 2688c2ecf20Sopenharmony_ci VFIO_CCW_MSG_EVENT(2, 2698c2ecf20Sopenharmony_ci "%pUl (%x.%x.%04x): cp_init=%d\n", 2708c2ecf20Sopenharmony_ci mdev_uuid(mdev), schid.cssid, 2718c2ecf20Sopenharmony_ci schid.ssid, schid.sch_no, 2728c2ecf20Sopenharmony_ci io_region->ret_code); 2738c2ecf20Sopenharmony_ci errstr = "cp init"; 2748c2ecf20Sopenharmony_ci goto err_out; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci io_region->ret_code = cp_prefetch(&private->cp); 2788c2ecf20Sopenharmony_ci if (io_region->ret_code) { 2798c2ecf20Sopenharmony_ci VFIO_CCW_MSG_EVENT(2, 2808c2ecf20Sopenharmony_ci "%pUl (%x.%x.%04x): cp_prefetch=%d\n", 2818c2ecf20Sopenharmony_ci mdev_uuid(mdev), schid.cssid, 2828c2ecf20Sopenharmony_ci schid.ssid, schid.sch_no, 2838c2ecf20Sopenharmony_ci io_region->ret_code); 2848c2ecf20Sopenharmony_ci errstr = "cp prefetch"; 2858c2ecf20Sopenharmony_ci cp_free(&private->cp); 2868c2ecf20Sopenharmony_ci goto err_out; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* Start channel program and wait for I/O interrupt. */ 2908c2ecf20Sopenharmony_ci io_region->ret_code = fsm_io_helper(private); 2918c2ecf20Sopenharmony_ci if (io_region->ret_code) { 2928c2ecf20Sopenharmony_ci VFIO_CCW_MSG_EVENT(2, 2938c2ecf20Sopenharmony_ci "%pUl (%x.%x.%04x): fsm_io_helper=%d\n", 2948c2ecf20Sopenharmony_ci mdev_uuid(mdev), schid.cssid, 2958c2ecf20Sopenharmony_ci schid.ssid, schid.sch_no, 2968c2ecf20Sopenharmony_ci io_region->ret_code); 2978c2ecf20Sopenharmony_ci errstr = "cp fsm_io_helper"; 2988c2ecf20Sopenharmony_ci cp_free(&private->cp); 2998c2ecf20Sopenharmony_ci goto err_out; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci return; 3028c2ecf20Sopenharmony_ci } else if (scsw->cmd.fctl & SCSW_FCTL_HALT_FUNC) { 3038c2ecf20Sopenharmony_ci VFIO_CCW_MSG_EVENT(2, 3048c2ecf20Sopenharmony_ci "%pUl (%x.%x.%04x): halt on io_region\n", 3058c2ecf20Sopenharmony_ci mdev_uuid(mdev), schid.cssid, 3068c2ecf20Sopenharmony_ci schid.ssid, schid.sch_no); 3078c2ecf20Sopenharmony_ci /* halt is handled via the async cmd region */ 3088c2ecf20Sopenharmony_ci io_region->ret_code = -EOPNOTSUPP; 3098c2ecf20Sopenharmony_ci goto err_out; 3108c2ecf20Sopenharmony_ci } else if (scsw->cmd.fctl & SCSW_FCTL_CLEAR_FUNC) { 3118c2ecf20Sopenharmony_ci VFIO_CCW_MSG_EVENT(2, 3128c2ecf20Sopenharmony_ci "%pUl (%x.%x.%04x): clear on io_region\n", 3138c2ecf20Sopenharmony_ci mdev_uuid(mdev), schid.cssid, 3148c2ecf20Sopenharmony_ci schid.ssid, schid.sch_no); 3158c2ecf20Sopenharmony_ci /* clear is handled via the async cmd region */ 3168c2ecf20Sopenharmony_ci io_region->ret_code = -EOPNOTSUPP; 3178c2ecf20Sopenharmony_ci goto err_out; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cierr_out: 3218c2ecf20Sopenharmony_ci private->state = VFIO_CCW_STATE_IDLE; 3228c2ecf20Sopenharmony_ci trace_vfio_ccw_fsm_io_request(scsw->cmd.fctl, schid, 3238c2ecf20Sopenharmony_ci io_region->ret_code, errstr); 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci/* 3278c2ecf20Sopenharmony_ci * Deal with an async request from userspace. 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_cistatic void fsm_async_request(struct vfio_ccw_private *private, 3308c2ecf20Sopenharmony_ci enum vfio_ccw_event event) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci struct ccw_cmd_region *cmd_region = private->cmd_region; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci switch (cmd_region->command) { 3358c2ecf20Sopenharmony_ci case VFIO_CCW_ASYNC_CMD_HSCH: 3368c2ecf20Sopenharmony_ci cmd_region->ret_code = fsm_do_halt(private); 3378c2ecf20Sopenharmony_ci break; 3388c2ecf20Sopenharmony_ci case VFIO_CCW_ASYNC_CMD_CSCH: 3398c2ecf20Sopenharmony_ci cmd_region->ret_code = fsm_do_clear(private); 3408c2ecf20Sopenharmony_ci break; 3418c2ecf20Sopenharmony_ci default: 3428c2ecf20Sopenharmony_ci /* should not happen? */ 3438c2ecf20Sopenharmony_ci cmd_region->ret_code = -EINVAL; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci trace_vfio_ccw_fsm_async_request(get_schid(private), 3478c2ecf20Sopenharmony_ci cmd_region->command, 3488c2ecf20Sopenharmony_ci cmd_region->ret_code); 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci/* 3528c2ecf20Sopenharmony_ci * Got an interrupt for a normal io (state busy). 3538c2ecf20Sopenharmony_ci */ 3548c2ecf20Sopenharmony_cistatic void fsm_irq(struct vfio_ccw_private *private, 3558c2ecf20Sopenharmony_ci enum vfio_ccw_event event) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct irb *irb = this_cpu_ptr(&cio_irb); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci VFIO_CCW_TRACE_EVENT(6, "IRQ"); 3608c2ecf20Sopenharmony_ci VFIO_CCW_TRACE_EVENT(6, dev_name(&private->sch->dev)); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci memcpy(&private->irb, irb, sizeof(*irb)); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci queue_work(vfio_ccw_work_q, &private->io_work); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (private->completion) 3678c2ecf20Sopenharmony_ci complete(private->completion); 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci/* 3718c2ecf20Sopenharmony_ci * Device statemachine 3728c2ecf20Sopenharmony_ci */ 3738c2ecf20Sopenharmony_cifsm_func_t *vfio_ccw_jumptable[NR_VFIO_CCW_STATES][NR_VFIO_CCW_EVENTS] = { 3748c2ecf20Sopenharmony_ci [VFIO_CCW_STATE_NOT_OPER] = { 3758c2ecf20Sopenharmony_ci [VFIO_CCW_EVENT_NOT_OPER] = fsm_nop, 3768c2ecf20Sopenharmony_ci [VFIO_CCW_EVENT_IO_REQ] = fsm_io_error, 3778c2ecf20Sopenharmony_ci [VFIO_CCW_EVENT_ASYNC_REQ] = fsm_async_error, 3788c2ecf20Sopenharmony_ci [VFIO_CCW_EVENT_INTERRUPT] = fsm_disabled_irq, 3798c2ecf20Sopenharmony_ci }, 3808c2ecf20Sopenharmony_ci [VFIO_CCW_STATE_STANDBY] = { 3818c2ecf20Sopenharmony_ci [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper, 3828c2ecf20Sopenharmony_ci [VFIO_CCW_EVENT_IO_REQ] = fsm_io_error, 3838c2ecf20Sopenharmony_ci [VFIO_CCW_EVENT_ASYNC_REQ] = fsm_async_error, 3848c2ecf20Sopenharmony_ci [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq, 3858c2ecf20Sopenharmony_ci }, 3868c2ecf20Sopenharmony_ci [VFIO_CCW_STATE_IDLE] = { 3878c2ecf20Sopenharmony_ci [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper, 3888c2ecf20Sopenharmony_ci [VFIO_CCW_EVENT_IO_REQ] = fsm_io_request, 3898c2ecf20Sopenharmony_ci [VFIO_CCW_EVENT_ASYNC_REQ] = fsm_async_request, 3908c2ecf20Sopenharmony_ci [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq, 3918c2ecf20Sopenharmony_ci }, 3928c2ecf20Sopenharmony_ci [VFIO_CCW_STATE_CP_PROCESSING] = { 3938c2ecf20Sopenharmony_ci [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper, 3948c2ecf20Sopenharmony_ci [VFIO_CCW_EVENT_IO_REQ] = fsm_io_retry, 3958c2ecf20Sopenharmony_ci [VFIO_CCW_EVENT_ASYNC_REQ] = fsm_async_retry, 3968c2ecf20Sopenharmony_ci [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq, 3978c2ecf20Sopenharmony_ci }, 3988c2ecf20Sopenharmony_ci [VFIO_CCW_STATE_CP_PENDING] = { 3998c2ecf20Sopenharmony_ci [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper, 4008c2ecf20Sopenharmony_ci [VFIO_CCW_EVENT_IO_REQ] = fsm_io_busy, 4018c2ecf20Sopenharmony_ci [VFIO_CCW_EVENT_ASYNC_REQ] = fsm_async_request, 4028c2ecf20Sopenharmony_ci [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq, 4038c2ecf20Sopenharmony_ci }, 4048c2ecf20Sopenharmony_ci}; 405