1 // SPDX-License-Identifier: GPL-2.0
3 * Finite state machine for vfio-ccw device handling
5 * Copyright IBM Corp. 2017
7 * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
10 #include <linux/vfio.h>
11 #include <linux/mdev.h>
14 #include "vfio_ccw_private.h"
16 static int fsm_io_helper(struct vfio_ccw_private
*private)
18 struct subchannel
*sch
;
26 spin_lock_irqsave(sch
->lock
, flags
);
27 private->state
= VFIO_CCW_STATE_BUSY
;
28 spin_unlock_irqrestore(sch
->lock
, flags
);
30 orb
= cp_get_orb(&private->cp
, (u32
)(addr_t
)sch
, sch
->lpm
);
32 /* Issue "Start Subchannel" */
33 ccode
= ssch(sch
->schid
, orb
);
38 * Initialize device status information
40 sch
->schib
.scsw
.cmd
.actl
|= SCSW_ACTL_START_PEND
;
42 case 1: /* Status pending */
45 case 3: /* Device/path not operational */
53 if (cio_update_schib(sch
))
56 return sch
->lpm
? -EACCES
: -ENODEV
;
63 static void fsm_notoper(struct vfio_ccw_private
*private,
64 enum vfio_ccw_event event
)
66 struct subchannel
*sch
= private->sch
;
70 * Probably we should send the machine check to the guest.
72 css_sched_sch_todo(sch
, SCH_TODO_UNREG
);
73 private->state
= VFIO_CCW_STATE_NOT_OPER
;
77 * No operation action.
79 static void fsm_nop(struct vfio_ccw_private
*private,
80 enum vfio_ccw_event event
)
84 static void fsm_io_error(struct vfio_ccw_private
*private,
85 enum vfio_ccw_event event
)
87 pr_err("vfio-ccw: FSM: I/O request from state:%d\n", private->state
);
88 private->io_region
.ret_code
= -EIO
;
91 static void fsm_io_busy(struct vfio_ccw_private
*private,
92 enum vfio_ccw_event event
)
94 private->io_region
.ret_code
= -EBUSY
;
97 static void fsm_disabled_irq(struct vfio_ccw_private
*private,
98 enum vfio_ccw_event event
)
100 struct subchannel
*sch
= private->sch
;
103 * An interrupt in a disabled state means a previous disable was not
104 * successful - should not happen, but we try to disable again.
106 cio_disable_subchannel(sch
);
110 * Deal with the ccw command request from the userspace.
112 static void fsm_io_request(struct vfio_ccw_private
*private,
113 enum vfio_ccw_event event
)
116 union scsw
*scsw
= &private->scsw
;
117 struct ccw_io_region
*io_region
= &private->io_region
;
118 struct mdev_device
*mdev
= private->mdev
;
120 private->state
= VFIO_CCW_STATE_BOXED
;
122 memcpy(scsw
, io_region
->scsw_area
, sizeof(*scsw
));
124 if (scsw
->cmd
.fctl
& SCSW_FCTL_START_FUNC
) {
125 orb
= (union orb
*)io_region
->orb_area
;
127 io_region
->ret_code
= cp_init(&private->cp
, mdev_dev(mdev
),
129 if (io_region
->ret_code
)
132 io_region
->ret_code
= cp_prefetch(&private->cp
);
133 if (io_region
->ret_code
) {
134 cp_free(&private->cp
);
138 /* Start channel program and wait for I/O interrupt. */
139 io_region
->ret_code
= fsm_io_helper(private);
140 if (io_region
->ret_code
) {
141 cp_free(&private->cp
);
145 } else if (scsw
->cmd
.fctl
& SCSW_FCTL_HALT_FUNC
) {
146 /* XXX: Handle halt. */
147 io_region
->ret_code
= -EOPNOTSUPP
;
149 } else if (scsw
->cmd
.fctl
& SCSW_FCTL_CLEAR_FUNC
) {
150 /* XXX: Handle clear. */
151 io_region
->ret_code
= -EOPNOTSUPP
;
156 private->state
= VFIO_CCW_STATE_IDLE
;
160 * Got an interrupt for a normal io (state busy).
162 static void fsm_irq(struct vfio_ccw_private
*private,
163 enum vfio_ccw_event event
)
165 struct irb
*irb
= this_cpu_ptr(&cio_irb
);
167 memcpy(&private->irb
, irb
, sizeof(*irb
));
169 queue_work(vfio_ccw_work_q
, &private->io_work
);
171 if (private->completion
)
172 complete(private->completion
);
176 * Device statemachine
178 fsm_func_t
*vfio_ccw_jumptable
[NR_VFIO_CCW_STATES
][NR_VFIO_CCW_EVENTS
] = {
179 [VFIO_CCW_STATE_NOT_OPER
] = {
180 [VFIO_CCW_EVENT_NOT_OPER
] = fsm_nop
,
181 [VFIO_CCW_EVENT_IO_REQ
] = fsm_io_error
,
182 [VFIO_CCW_EVENT_INTERRUPT
] = fsm_disabled_irq
,
184 [VFIO_CCW_STATE_STANDBY
] = {
185 [VFIO_CCW_EVENT_NOT_OPER
] = fsm_notoper
,
186 [VFIO_CCW_EVENT_IO_REQ
] = fsm_io_error
,
187 [VFIO_CCW_EVENT_INTERRUPT
] = fsm_irq
,
189 [VFIO_CCW_STATE_IDLE
] = {
190 [VFIO_CCW_EVENT_NOT_OPER
] = fsm_notoper
,
191 [VFIO_CCW_EVENT_IO_REQ
] = fsm_io_request
,
192 [VFIO_CCW_EVENT_INTERRUPT
] = fsm_irq
,
194 [VFIO_CCW_STATE_BOXED
] = {
195 [VFIO_CCW_EVENT_NOT_OPER
] = fsm_notoper
,
196 [VFIO_CCW_EVENT_IO_REQ
] = fsm_io_busy
,
197 [VFIO_CCW_EVENT_INTERRUPT
] = fsm_irq
,
199 [VFIO_CCW_STATE_BUSY
] = {
200 [VFIO_CCW_EVENT_NOT_OPER
] = fsm_notoper
,
201 [VFIO_CCW_EVENT_IO_REQ
] = fsm_io_busy
,
202 [VFIO_CCW_EVENT_INTERRUPT
] = fsm_irq
,